diff options
Diffstat (limited to 'drivers')
251 files changed, 15394 insertions, 43207 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index a2aea53a75e..14cf9077bb2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -51,7 +51,6 @@ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ obj-y += firewire/ -obj-y += ieee1394/ obj-$(CONFIG_UIO) += uio/ obj-y += cdrom/ obj-y += auxdisplay/ @@ -92,6 +91,7 @@ obj-$(CONFIG_EISA) += eisa/ obj-y += lguest/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ +obj-$(CONFIG_DMA_ENGINE) += dma/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-$(CONFIG_NEW_LEDS) += leds/ @@ -104,7 +104,6 @@ obj-$(CONFIG_ARCH_SHMOBILE) += sh/ ifndef CONFIG_ARCH_USES_GETTIMEOFFSET obj-y += clocksource/ endif -obj-$(CONFIG_DMA_ENGINE) += dma/ obj-$(CONFIG_DCA) += dca/ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 3966e62ad01..f051cfff18a 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -147,6 +147,7 @@ static void platform_device_release(struct device *dev) struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev); + of_device_node_put(&pa->pdev.dev); kfree(pa->pdev.dev.platform_data); kfree(pa->pdev.resource); kfree(pa); diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 6e968cd4893..829161edae5 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1225,7 +1225,8 @@ ace_of_probe(struct platform_device *op, const struct of_device_id *match) bus_width = ACE_BUS_WIDTH_8; /* Call the bus-independant setup code */ - return ace_alloc(&op->dev, id ? *id : 0, physaddr, irq, bus_width); + return ace_alloc(&op->dev, id ? be32_to_cpup(id) : 0, + physaddr, irq, bus_width); } static int __devexit ace_of_remove(struct platform_device *op) diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 7b01bc609de..c3425bb3a1f 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -1303,13 +1303,11 @@ static int __init hvc_iucv_init(void) if (rc) { pr_err("Registering IUCV handlers failed with error code=%d\n", rc); - goto out_error_iucv; + goto out_error_hvc; } return 0; -out_error_iucv: - iucv_unregister(&hvc_iucv_handler, 0); out_error_hvc: for (i = 0; i < hvc_iucv_devices; i++) if (hvc_iucv_table[i]) diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index a7ca75212bf..e95d7876ca6 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -175,8 +175,7 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier); */ struct getset_keycode_data { - unsigned int scancode; - unsigned int keycode; + struct input_keymap_entry ke; int error; }; @@ -184,32 +183,50 @@ static int getkeycode_helper(struct input_handle *handle, void *data) { struct getset_keycode_data *d = data; - d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode); + d->error = input_get_keycode(handle->dev, &d->ke); return d->error == 0; /* stop as soon as we successfully get one */ } int getkeycode(unsigned int scancode) { - struct getset_keycode_data d = { scancode, 0, -ENODEV }; + struct getset_keycode_data d = { + .ke = { + .flags = 0, + .len = sizeof(scancode), + .keycode = 0, + }, + .error = -ENODEV, + }; + + memcpy(d.ke.scancode, &scancode, sizeof(scancode)); input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper); - return d.error ?: d.keycode; + return d.error ?: d.ke.keycode; } static int setkeycode_helper(struct input_handle *handle, void *data) { struct getset_keycode_data *d = data; - d->error = input_set_keycode(handle->dev, d->scancode, d->keycode); + d->error = input_set_keycode(handle->dev, &d->ke); return d->error == 0; /* stop as soon as we successfully set one */ } int setkeycode(unsigned int scancode, unsigned int keycode) { - struct getset_keycode_data d = { scancode, keycode, -ENODEV }; + struct getset_keycode_data d = { + .ke = { + .flags = 0, + .len = sizeof(scancode), + .keycode = keycode, + }, + .error = -ENODEV, + }; + + memcpy(d.ke.scancode, &scancode, sizeof(scancode)); input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper); diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index f3019f53e87..eaa5d3efa79 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -566,10 +566,16 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] = static bool sysrq_down; static int sysrq_alt_use; static int sysrq_alt; +static DEFINE_SPINLOCK(sysrq_event_lock); static bool sysrq_filter(struct input_handle *handle, unsigned int type, unsigned int code, int value) { + bool suppress; + + /* We are called with interrupts disabled, just take the lock */ + spin_lock(&sysrq_event_lock); + if (type != EV_KEY) goto out; @@ -601,7 +607,10 @@ static bool sysrq_filter(struct input_handle *handle, unsigned int type, } out: - return sysrq_down; + suppress = sysrq_down; + spin_unlock(&sysrq_event_lock); + + return suppress; } static int sysrq_connect(struct input_handler *handler, @@ -652,8 +661,8 @@ static void sysrq_disconnect(struct input_handle *handle) } /* - * We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all - * keyboards have SysRq ikey predefined and so user may add it to keymap + * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all + * keyboards have SysRq key predefined and so user may add it to keymap * later, but we expect all such keyboards to have left alt. */ static const struct input_device_id sysrq_ids[] = { diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 717305d3044..a4461165228 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -308,7 +308,7 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id) * isr before we end up here. */ if (p->flags & FLAG_CLOCKSOURCE) - p->total_cycles += p->match_value; + p->total_cycles += p->match_value + 1; if (!(p->flags & FLAG_REPROGRAM)) p->next_match_value = p->max_match_value; @@ -403,7 +403,7 @@ static cycle_t sh_cmt_clocksource_read(struct clocksource *cs) raw = sh_cmt_get_counter(p, &has_wrapped); if (unlikely(has_wrapped)) - raw += p->match_value; + raw += p->match_value + 1; spin_unlock_irqrestore(&p->lock, flags); return value + raw; @@ -445,7 +445,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, /* clk_get_rate() needs an enabled clock */ clk_enable(p->clk); - p->rate = clk_get_rate(p->clk) / (p->width == 16) ? 512 : 8; + p->rate = clk_get_rate(p->clk) / ((p->width == 16) ? 512 : 8); clk_disable(p->clk); /* TODO: calculate good shift from rate and counter bit width */ @@ -478,7 +478,7 @@ static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic) ced->min_delta_ns = clockevent_delta2ns(0x1f, ced); if (periodic) - sh_cmt_set_next(p, (p->rate + HZ/2) / HZ); + sh_cmt_set_next(p, ((p->rate + HZ/2) / HZ) - 1); else sh_cmt_set_next(p, p->max_match_value); } @@ -523,9 +523,9 @@ static int sh_cmt_clock_event_next(unsigned long delta, BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT); if (likely(p->flags & FLAG_IRQCONTEXT)) - p->next_match_value = delta; + p->next_match_value = delta - 1; else - sh_cmt_set_next(p, delta); + sh_cmt_set_next(p, delta - 1); return 0; } diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index fcf3ea28340..40a222e19b2 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -3,9 +3,6 @@ menu "IEEE 1394 (FireWire) support" # firewire-core does not depend on PCI but is # not useful without PCI controller driver -comment "You can enable one or both FireWire driver stacks." -comment "The newer stack is recommended." - config FIREWIRE tristate "FireWire driver stack" select CRC_ITU_T @@ -64,8 +61,6 @@ config FIREWIRE_NET To compile this driver as a module, say M here: The module will be called firewire-net. -source "drivers/ieee1394/Kconfig" - config FIREWIRE_NOSY tristate "Nosy - a FireWire traffic sniffer for PCILynx cards" depends on PCI diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile index 3c6a7fb20aa..e3870d5c43d 100644 --- a/drivers/firewire/Makefile +++ b/drivers/firewire/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o obj-$(CONFIG_FIREWIRE_NOSY) += nosy.o +obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o diff --git a/drivers/ieee1394/init_ohci1394_dma.c b/drivers/firewire/init_ohci1394_dma.c index ddaab6eb8ac..a9a347adb35 100644 --- a/drivers/ieee1394/init_ohci1394_dma.c +++ b/drivers/firewire/init_ohci1394_dma.c @@ -32,23 +32,41 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <linux/interrupt.h> /* for ohci1394.h */ #include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> #include <linux/pci.h> /* for PCI defines */ -#include <linux/init_ohci1394_dma.h> +#include <linux/string.h> + #include <asm/pci-direct.h> /* for direct PCI config space access */ #include <asm/fixmap.h> -#include "ieee1394_types.h" -#include "ohci1394.h" +#include <linux/init_ohci1394_dma.h> +#include "ohci.h" int __initdata init_ohci1394_dma_early; +struct ohci { + void __iomem *registers; +}; + +static inline void reg_write(const struct ohci *ohci, int offset, u32 data) +{ + writel(data, ohci->registers + offset); +} + +static inline u32 reg_read(const struct ohci *ohci, int offset) +{ + return readl(ohci->registers + offset); +} + +#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */ + /* Reads a PHY register of an OHCI-1394 controller */ -static inline u8 __init get_phy_reg(struct ti_ohci *ohci, u8 addr) +static inline u8 __init get_phy_reg(struct ohci *ohci, u8 addr) { int i; - quadlet_t r; + u32 r; reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000); @@ -63,22 +81,22 @@ static inline u8 __init get_phy_reg(struct ti_ohci *ohci, u8 addr) } /* Writes to a PHY register of an OHCI-1394 controller */ -static inline void __init set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data) +static inline void __init set_phy_reg(struct ohci *ohci, u8 addr, u8 data) { int i; reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000); for (i = 0; i < OHCI_LOOP_COUNT; i++) { - u32 r = reg_read(ohci, OHCI1394_PhyControl); - if (!(r & 0x00004000)) + if (!(reg_read(ohci, OHCI1394_PhyControl) & 0x00004000)) break; mdelay(1); } } /* Resets an OHCI-1394 controller (for sane state before initialization) */ -static inline void __init init_ohci1394_soft_reset(struct ti_ohci *ohci) { +static inline void __init init_ohci1394_soft_reset(struct ohci *ohci) +{ int i; reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); @@ -91,10 +109,14 @@ static inline void __init init_ohci1394_soft_reset(struct ti_ohci *ohci) { } } +#define OHCI1394_MAX_AT_REQ_RETRIES 0xf +#define OHCI1394_MAX_AT_RESP_RETRIES 0x2 +#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 + /* Basic OHCI-1394 register and port inititalization */ -static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci) +static inline void __init init_ohci1394_initialize(struct ohci *ohci) { - quadlet_t bus_options; + u32 bus_options; int num_ports, i; /* Put some defaults to these undefined bus options */ @@ -116,7 +138,7 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci) /* enable phys */ reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_RcvPhyPkt); + OHCI1394_LinkControl_rcvPhyPkt); /* Don't accept phy packets into AR request context */ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); @@ -128,7 +150,7 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci) reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); /* Accept asyncronous transfer requests from all nodes for now */ - reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000); + reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); /* Specify asyncronous transfer retries */ reg_write(ohci, OHCI1394_ATRetries, @@ -137,7 +159,8 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci) (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); /* We don't want hardware swapping */ - reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap); + reg_write(ohci, OHCI1394_HCControlClear, + OHCI1394_HCControl_noByteSwapData); /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable); @@ -164,11 +187,11 @@ static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci) * has to be enabled after each bus reset when needed. We resort * to polling here because on early boot, we have no interrupts. */ -static inline void __init init_ohci1394_wait_for_busresets(struct ti_ohci *ohci) +static inline void __init init_ohci1394_wait_for_busresets(struct ohci *ohci) { int i, events; - for (i=0; i < 9; i++) { + for (i = 0; i < 9; i++) { mdelay(200); events = reg_read(ohci, OHCI1394_IntEventSet); if (events & OHCI1394_busReset) @@ -182,18 +205,18 @@ static inline void __init init_ohci1394_wait_for_busresets(struct ti_ohci *ohci) * This enables remote DMA access over IEEE1394 from every host for the low * 4GB of address space. DMA accesses above 4GB are not available currently. */ -static inline void __init init_ohci1394_enable_physical_dma(struct ti_ohci *hci) +static inline void __init init_ohci1394_enable_physical_dma(struct ohci *ohci) { - reg_write(hci, OHCI1394_PhyReqFilterHiSet, 0xffffffff); - reg_write(hci, OHCI1394_PhyReqFilterLoSet, 0xffffffff); - reg_write(hci, OHCI1394_PhyUpperBound, 0xffff0000); + reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 0xffffffff); + reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 0xffffffff); + reg_write(ohci, OHCI1394_PhyUpperBound, 0xffff0000); } /** * init_ohci1394_reset_and_init_dma - init controller and enable DMA * This initializes the given controller and enables physical DMA engine in it. */ -static inline void __init init_ohci1394_reset_and_init_dma(struct ti_ohci *ohci) +static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci) { /* Start off with a soft reset, clears everything to a sane state. */ init_ohci1394_soft_reset(ohci); @@ -225,7 +248,7 @@ static inline void __init init_ohci1394_reset_and_init_dma(struct ti_ohci *ohci) static inline void __init init_ohci1394_controller(int num, int slot, int func) { unsigned long ohci_base; - struct ti_ohci ohci; + struct ohci ohci; printk(KERN_INFO "init_ohci1394_dma: initializing OHCI-1394" " at %02x:%02x.%x\n", num, slot, func); @@ -235,7 +258,7 @@ static inline void __init init_ohci1394_controller(int num, int slot, int func) set_fixmap_nocache(FIX_OHCI1394_BASE, ohci_base); - ohci.registers = (void *)fix_to_virt(FIX_OHCI1394_BASE); + ohci.registers = (void __iomem *)fix_to_virt(FIX_OHCI1394_BASE); init_ohci1394_reset_and_init_dma(&ohci); } @@ -247,6 +270,7 @@ static inline void __init init_ohci1394_controller(int num, int slot, int func) void __init init_ohci1394_dma_on_all_controllers(void) { int num, slot, func; + u32 class; if (!early_pci_allowed()) return; @@ -255,9 +279,9 @@ void __init init_ohci1394_dma_on_all_controllers(void) for (num = 0; num < 32; num++) { for (slot = 0; slot < 32; slot++) { for (func = 0; func < 8; func++) { - u32 class = read_pci_config(num,slot,func, + class = read_pci_config(num, slot, func, PCI_CLASS_REVISION); - if ((class == 0xffffffff)) + if (class == 0xffffffff) continue; /* No device at this func */ if (class>>8 != PCI_CLASS_SERIAL_FIREWIRE_OHCI) diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c index 709690995d0..846fbd5e31b 100644 --- a/drivers/gpio/xilinx_gpio.c +++ b/drivers/gpio/xilinx_gpio.c @@ -171,13 +171,13 @@ static int __devinit xgpio_of_probe(struct device_node *np) /* Update GPIO state shadow register with default value */ tree_info = of_get_property(np, "xlnx,dout-default", NULL); if (tree_info) - chip->gpio_state = *tree_info; + chip->gpio_state = be32_to_cpup(tree_info); /* Update GPIO direction shadow register with default value */ chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ tree_info = of_get_property(np, "xlnx,tri-default", NULL); if (tree_info) - chip->gpio_dir = *tree_info; + chip->gpio_dir = be32_to_cpup(tree_info); /* Check device node and parent device node for device width */ chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ @@ -186,7 +186,7 @@ static int __devinit xgpio_of_probe(struct device_node *np) tree_info = of_get_property(np->parent, "xlnx,gpio-width", NULL); if (tree_info) - chip->mmchip.gc.ngpio = *tree_info; + chip->mmchip.gc.ngpio = be32_to_cpup(tree_info); spin_lock_init(&chip->gpio_lock); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7832b6e2478..515345b11ac 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1780,6 +1780,11 @@ static bool hid_ignore(struct hid_device *hdev) hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST) return true; break; + case USB_VENDOR_ID_HANWANG: + if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST && + hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST) + return true; + break; } if (hdev->type == HID_TYPE_USBMOUSE && diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3ee999d3300..3341baa86a3 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -299,6 +299,10 @@ #define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003 #define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008 +#define USB_VENDOR_ID_HANWANG 0x0b57 +#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000 +#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff + #define USB_VENDOR_ID_HAPP 0x078b #define USB_DEVICE_ID_UGCI_DRIVING 0x0010 #define USB_DEVICE_ID_UGCI_FLYING 0x0020 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 834ef47b76d..bb0b3659437 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -68,39 +68,52 @@ static const struct { #define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ &max, EV_KEY, (c)) -static inline int match_scancode(unsigned int code, unsigned int scancode) +static bool match_scancode(struct hid_usage *usage, + unsigned int cur_idx, unsigned int scancode) { - if (scancode == 0) - return 1; - - return (code & (HID_USAGE_PAGE | HID_USAGE)) == scancode; + return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode; } -static inline int match_keycode(unsigned int code, unsigned int keycode) +static bool match_keycode(struct hid_usage *usage, + unsigned int cur_idx, unsigned int keycode) { - if (keycode == 0) - return 1; + /* + * We should exclude unmapped usages when doing lookup by keycode. + */ + return (usage->type == EV_KEY && usage->code == keycode); +} - return code == keycode; +static bool match_index(struct hid_usage *usage, + unsigned int cur_idx, unsigned int idx) +{ + return cur_idx == idx; } +typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage, + unsigned int cur_idx, unsigned int val); + static struct hid_usage *hidinput_find_key(struct hid_device *hid, - unsigned int scancode, - unsigned int keycode) + hid_usage_cmp_t match, + unsigned int value, + unsigned int *usage_idx) { - int i, j, k; + unsigned int i, j, k, cur_idx = 0; struct hid_report *report; struct hid_usage *usage; for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { list_for_each_entry(report, &hid->report_enum[k].report_list, list) { for (i = 0; i < report->maxfield; i++) { - for ( j = 0; j < report->field[i]->maxusage; j++) { + for (j = 0; j < report->field[i]->maxusage; j++) { usage = report->field[i]->usage + j; - if (usage->type == EV_KEY && - match_scancode(usage->hid, scancode) && - match_keycode(usage->code, keycode)) - return usage; + if (usage->type == EV_KEY || usage->type == 0) { + if (match(usage, cur_idx, value)) { + if (usage_idx) + *usage_idx = cur_idx; + return usage; + } + cur_idx++; + } } } } @@ -108,39 +121,68 @@ static struct hid_usage *hidinput_find_key(struct hid_device *hid, return NULL; } +static struct hid_usage *hidinput_locate_usage(struct hid_device *hid, + const struct input_keymap_entry *ke, + unsigned int *index) +{ + struct hid_usage *usage; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + usage = hidinput_find_key(hid, match_index, ke->index, index); + else if (input_scancode_to_scalar(ke, &scancode) == 0) + usage = hidinput_find_key(hid, match_scancode, scancode, index); + else + usage = NULL; + + return usage; +} + static int hidinput_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) + struct input_keymap_entry *ke) { struct hid_device *hid = input_get_drvdata(dev); struct hid_usage *usage; + unsigned int scancode, index; - usage = hidinput_find_key(hid, scancode, 0); + usage = hidinput_locate_usage(hid, ke, &index); if (usage) { - *keycode = usage->code; + ke->keycode = usage->type == EV_KEY ? + usage->code : KEY_RESERVED; + ke->index = index; + scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE); + ke->len = sizeof(scancode); + memcpy(ke->scancode, &scancode, sizeof(scancode)); return 0; } + return -EINVAL; } static int hidinput_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { struct hid_device *hid = input_get_drvdata(dev); struct hid_usage *usage; - int old_keycode; - usage = hidinput_find_key(hid, scancode, 0); + usage = hidinput_locate_usage(hid, ke, NULL); if (usage) { - old_keycode = usage->code; - usage->code = keycode; + *old_keycode = usage->type == EV_KEY ? + usage->code : KEY_RESERVED; + usage->code = ke->keycode; - clear_bit(old_keycode, dev->keybit); + clear_bit(*old_keycode, dev->keybit); set_bit(usage->code, dev->keybit); - dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); - /* Set the keybit for the old keycode if the old keycode is used - * by another key */ - if (hidinput_find_key (hid, 0, old_keycode)) - set_bit(old_keycode, dev->keybit); + dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", + usage->code, usage->hid); + + /* + * Set the keybit for the old keycode if the old keycode is used + * by another key + */ + if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL)) + set_bit(*old_keycode, dev->keybit); return 0; } @@ -835,8 +877,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) hid->ll_driver->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; - input_dev->setkeycode = hidinput_setkeycode; - input_dev->getkeycode = hidinput_getkeycode; + input_dev->setkeycode_new = hidinput_setkeycode; + input_dev->getkeycode_new = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 97499d00615..c357c835eb1 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -399,6 +399,15 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_GPIO_FAN + tristate "GPIO fan" + depends on GENERIC_GPIO + help + If you say yes here you get support for fans connected to GPIO lines. + + This driver can also be built as a module. If so, the module + will be called gpio-fan. + config SENSORS_CORETEMP tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 && PCI && EXPERIMENTAL @@ -654,6 +663,17 @@ config SENSORS_LTC4245 This driver can also be built as a module. If so, the module will be called ltc4245. +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4261. + config SENSORS_LM95241 tristate "National Semiconductor LM95241 sensor chip" depends on I2C @@ -1088,26 +1108,6 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. -config SENSORS_HDAPS - tristate "IBM Hard Drive Active Protection System (hdaps)" - depends on INPUT && X86 - select INPUT_POLLDEV - default n - help - This driver provides support for the IBM Hard Drive Active Protection - System (hdaps), which provides an accelerometer and other misc. data. - ThinkPads starting with the R50, T41, and X40 are supported. The - accelerometer data is readable via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - If your ThinkPad is not recognized by the driver, please update to latest - BIOS. This is especially the case for some R52 ThinkPads. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of hdaps. - config SENSORS_LIS3_SPI tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" depends on !ACPI && SPI_MASTER && INPUT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e3c2484f6c5..d30f0f6870e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -51,8 +51,8 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o -obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o @@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index a23b17a78ac..42de98d73ff 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -280,11 +279,9 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, case 0x1a: dev_warn(dev, "TjMax is assumed as 100 C!\n"); return 100000; - break; case 0x17: case 0x1c: /* Atom CPUs */ return adjust_tjmax(c, id, dev); - break; default: dev_warn(dev, "CPU (model=0x%x) is not supported yet," " using default TjMax of 100C.\n", c->x86_model); @@ -292,6 +289,15 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, } } +static void __devinit get_ucode_rev_on_cpu(void *edx) +{ + u32 eax; + + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + sync_core(); + rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); +} + static int __devinit coretemp_probe(struct platform_device *pdev) { struct coretemp_data *data; @@ -327,8 +333,15 @@ static int __devinit coretemp_probe(struct platform_device *pdev) if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) { /* check for microcode update */ - rdmsr_on_cpu(data->id, MSR_IA32_UCODE_REV, &eax, &edx); - if (edx < 0x39) { + err = smp_call_function_single(data->id, get_ucode_rev_on_cpu, + &edx, 1); + if (err) { + dev_err(&pdev->dev, + "Cannot determine microcode revision of " + "CPU#%u (%d)!\n", data->id, err); + err = -ENODEV; + goto exit_free; + } else if (edx < 0x39) { err = -ENODEV; dev_err(&pdev->dev, "Errata AE18 not fixed, update BIOS or " @@ -490,7 +503,7 @@ exit: return err; } -static void coretemp_device_remove(unsigned int cpu) +static void __cpuinit coretemp_device_remove(unsigned int cpu) { struct pdev_entry *p; unsigned int i; @@ -569,9 +582,8 @@ exit: static void __exit coretemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&coretemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c new file mode 100644 index 00000000000..aa701a18370 --- /dev/null +++ b/drivers/hwmon/gpio-fan.c @@ -0,0 +1,558 @@ +/* + * gpio-fan.c - Hwmon driver for fans connected to GPIO lines. + * + * Copyright (C) 2010 LaCie + * + * Author: Simon Guinot <sguinot@lacie.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/hwmon.h> +#include <linux/gpio.h> +#include <linux/gpio-fan.h> + +struct gpio_fan_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex lock; /* lock GPIOs operations. */ + int num_ctrl; + unsigned *ctrl; + int num_speed; + struct gpio_fan_speed *speed; + int speed_index; +#ifdef CONFIG_PM + int resume_speed; +#endif + bool pwm_enable; + struct gpio_fan_alarm *alarm; + struct work_struct alarm_work; +}; + +/* + * Alarm GPIO. + */ + +static void fan_alarm_notify(struct work_struct *ws) +{ + struct gpio_fan_data *fan_data = + container_of(ws, struct gpio_fan_data, alarm_work); + + sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm"); + kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE); +} + +static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) +{ + struct gpio_fan_data *fan_data = dev_id; + + schedule_work(&fan_data->alarm_work); + + return IRQ_NONE; +} + +static ssize_t show_fan_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + struct gpio_fan_alarm *alarm = fan_data->alarm; + int value = gpio_get_value(alarm->gpio); + + if (alarm->active_low) + value = !value; + + return sprintf(buf, "%d\n", value); +} + +static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); + +static int fan_alarm_init(struct gpio_fan_data *fan_data, + struct gpio_fan_alarm *alarm) +{ + int err; + int alarm_irq; + struct platform_device *pdev = fan_data->pdev; + + fan_data->alarm = alarm; + + err = gpio_request(alarm->gpio, "GPIO fan alarm"); + if (err) + return err; + + err = gpio_direction_input(alarm->gpio); + if (err) + goto err_free_gpio; + + err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); + if (err) + goto err_free_gpio; + + /* + * If the alarm GPIO don't support interrupts, just leave + * without initializing the fail notification support. + */ + alarm_irq = gpio_to_irq(alarm->gpio); + if (alarm_irq < 0) + return 0; + + INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); + set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); + err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, + "GPIO fan alarm", fan_data); + if (err) + goto err_free_sysfs; + + return 0; + +err_free_sysfs: + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); +err_free_gpio: + gpio_free(alarm->gpio); + + return err; +} + +static void fan_alarm_free(struct gpio_fan_data *fan_data) +{ + struct platform_device *pdev = fan_data->pdev; + int alarm_irq = gpio_to_irq(fan_data->alarm->gpio); + + if (alarm_irq >= 0) + free_irq(alarm_irq, fan_data); + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); + gpio_free(fan_data->alarm->gpio); +} + +/* + * Control GPIOs. + */ + +/* Must be called with fan_data->lock held, except during initialization. */ +static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) +{ + int i; + + for (i = 0; i < fan_data->num_ctrl; i++) + gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); +} + +static int __get_fan_ctrl(struct gpio_fan_data *fan_data) +{ + int i; + int ctrl_val = 0; + + for (i = 0; i < fan_data->num_ctrl; i++) { + int value; + + value = gpio_get_value(fan_data->ctrl[i]); + ctrl_val |= (value << i); + } + return ctrl_val; +} + +/* Must be called with fan_data->lock held, except during initialization. */ +static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index) +{ + if (fan_data->speed_index == speed_index) + return; + + __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val); + fan_data->speed_index = speed_index; +} + +static int get_fan_speed_index(struct gpio_fan_data *fan_data) +{ + int ctrl_val = __get_fan_ctrl(fan_data); + int i; + + for (i = 0; i < fan_data->num_speed; i++) + if (fan_data->speed[i].ctrl_val == ctrl_val) + return i; + + dev_warn(&fan_data->pdev->dev, + "missing speed array entry for GPIO value 0x%x\n", ctrl_val); + + return -EINVAL; +} + +static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) +{ + struct gpio_fan_speed *speed = fan_data->speed; + int i; + + for (i = 0; i < fan_data->num_speed; i++) + if (speed[i].rpm >= rpm) + return i; + + return fan_data->num_speed - 1; +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1); + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long pwm; + int speed_index; + int ret = count; + + if (strict_strtoul(buf, 10, &pwm) || pwm > 255) + return -EINVAL; + + mutex_lock(&fan_data->lock); + + if (!fan_data->pwm_enable) { + ret = -EPERM; + goto exit_unlock; + } + + speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255); + set_fan_speed(fan_data, speed_index); + +exit_unlock: + mutex_unlock(&fan_data->lock); + + return ret; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->pwm_enable); +} + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + if (fan_data->pwm_enable == val) + return count; + + mutex_lock(&fan_data->lock); + + fan_data->pwm_enable = val; + + /* Disable manual control mode: set fan at full speed. */ + if (val == 0) + set_fan_speed(fan_data, fan_data->num_speed - 1); + + mutex_unlock(&fan_data->lock); + + return count; +} + +static ssize_t show_pwm_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t show_rpm_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->speed[0].rpm); +} + +static ssize_t show_rpm_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", + fan_data->speed[fan_data->num_speed - 1].rpm); +} + +static ssize_t show_rpm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm); +} + +static ssize_t set_rpm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long rpm; + int ret = count; + + if (strict_strtoul(buf, 10, &rpm)) + return -EINVAL; + + mutex_lock(&fan_data->lock); + + if (!fan_data->pwm_enable) { + ret = -EPERM; + goto exit_unlock; + } + + set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm)); + +exit_unlock: + mutex_unlock(&fan_data->lock); + + return ret; +} + +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable); +static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL); +static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL); +static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); +static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); +static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); + +static struct attribute *gpio_fan_ctrl_attributes[] = { + &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1_mode.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_max.attr, + NULL +}; + +static const struct attribute_group gpio_fan_ctrl_group = { + .attrs = gpio_fan_ctrl_attributes, +}; + +static int fan_ctrl_init(struct gpio_fan_data *fan_data, + struct gpio_fan_platform_data *pdata) +{ + struct platform_device *pdev = fan_data->pdev; + int num_ctrl = pdata->num_ctrl; + unsigned *ctrl = pdata->ctrl; + int i, err; + + for (i = 0; i < num_ctrl; i++) { + err = gpio_request(ctrl[i], "GPIO fan control"); + if (err) + goto err_free_gpio; + + err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + if (err) { + gpio_free(ctrl[i]); + goto err_free_gpio; + } + } + + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + if (err) + goto err_free_gpio; + + fan_data->num_ctrl = num_ctrl; + fan_data->ctrl = ctrl; + fan_data->num_speed = pdata->num_speed; + fan_data->speed = pdata->speed; + fan_data->pwm_enable = true; /* Enable manual fan speed control. */ + fan_data->speed_index = get_fan_speed_index(fan_data); + if (fan_data->speed_index < 0) { + err = -ENODEV; + goto err_free_gpio; + } + + return 0; + +err_free_gpio: + for (i = i - 1; i >= 0; i--) + gpio_free(ctrl[i]); + + return err; +} + +static void fan_ctrl_free(struct gpio_fan_data *fan_data) +{ + struct platform_device *pdev = fan_data->pdev; + int i; + + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + for (i = 0; i < fan_data->num_ctrl; i++) + gpio_free(fan_data->ctrl[i]); +} + +/* + * Platform driver. + */ + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "gpio-fan\n"); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static int __devinit gpio_fan_probe(struct platform_device *pdev) +{ + int err; + struct gpio_fan_data *fan_data; + struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL); + if (!fan_data) + return -ENOMEM; + + fan_data->pdev = pdev; + platform_set_drvdata(pdev, fan_data); + mutex_init(&fan_data->lock); + + /* Configure alarm GPIO if available. */ + if (pdata->alarm) { + err = fan_alarm_init(fan_data, pdata->alarm); + if (err) + goto err_free_data; + } + + /* Configure control GPIOs if available. */ + if (pdata->ctrl && pdata->num_ctrl > 0) { + if (!pdata->speed || pdata->num_speed <= 1) { + err = -EINVAL; + goto err_free_alarm; + } + err = fan_ctrl_init(fan_data, pdata); + if (err) + goto err_free_alarm; + } + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto err_free_ctrl; + + /* Make this driver part of hwmon class. */ + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(fan_data->hwmon_dev)) { + err = PTR_ERR(fan_data->hwmon_dev); + goto err_remove_name; + } + + dev_info(&pdev->dev, "GPIO fan initialized\n"); + + return 0; + +err_remove_name: + device_remove_file(&pdev->dev, &dev_attr_name); +err_free_ctrl: + if (fan_data->ctrl) + fan_ctrl_free(fan_data); +err_free_alarm: + if (fan_data->alarm) + fan_alarm_free(fan_data); +err_free_data: + platform_set_drvdata(pdev, NULL); + kfree(fan_data); + + return err; +} + +static int __devexit gpio_fan_remove(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + hwmon_device_unregister(fan_data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_name); + if (fan_data->alarm) + fan_alarm_free(fan_data); + if (fan_data->ctrl) + fan_ctrl_free(fan_data); + kfree(fan_data); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (fan_data->ctrl) { + fan_data->resume_speed = fan_data->speed_index; + set_fan_speed(fan_data, 0); + } + + return 0; +} + +static int gpio_fan_resume(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, fan_data->resume_speed); + + return 0; +} +#else +#define gpio_fan_suspend NULL +#define gpio_fan_resume NULL +#endif + +static struct platform_driver gpio_fan_driver = { + .probe = gpio_fan_probe, + .remove = __devexit_p(gpio_fan_remove), + .suspend = gpio_fan_suspend, + .resume = gpio_fan_resume, + .driver = { + .name = "gpio-fan", + }, +}; + +static int __init gpio_fan_init(void) +{ + return platform_driver_register(&gpio_fan_driver); +} + +static void __exit gpio_fan_exit(void) +{ + platform_driver_unregister(&gpio_fan_driver); +} + +module_init(gpio_fan_init); +module_exit(gpio_fan_exit); + +MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); +MODULE_DESCRIPTION("GPIO FAN driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-fan"); diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 36e95753223..a56a78412fc 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -146,7 +146,7 @@ int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) { - lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); return 1; @@ -154,16 +154,19 @@ static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). * If the value is negative, the opposite of the hw value is used. */ -static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; -static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; -static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; +#define DEFINE_CONV(name, x, y, z) \ + static union axis_conversion lis3lv02d_axis_##name = \ + { .as_array = { x, y, z } } +DEFINE_CONV(normal, 1, 2, 3); +DEFINE_CONV(y_inverted, 1, -2, 3); +DEFINE_CONV(x_inverted, -1, 2, 3); +DEFINE_CONV(z_inverted, 1, 2, -3); +DEFINE_CONV(xy_swap, 2, 1, 3); +DEFINE_CONV(xy_rotated_left, -2, 1, 3); +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); +DEFINE_CONV(xy_rotated_right, 2, -1, 3); +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ .ident = _ident, \ @@ -222,7 +225,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), - AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd), + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" @@ -299,7 +302,10 @@ static int lis3lv02d_add(struct acpi_device *device) lis3lv02d_enum_resources(device); /* If possible use a "standard" axes order */ - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { + printk(KERN_INFO DRIVER_NAME ": Using custom axes %d,%d,%d\n", + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " "using default axes configuration\n"); lis3_dev.ac = lis3lv02d_axis_normal; diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index fc591ae5310..0cee73a6124 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -31,9 +31,11 @@ #include <linux/delay.h> #include <linux/wait.h> #include <linux/poll.h> +#include <linux/slab.h> #include <linux/freezer.h> #include <linux/uaccess.h> #include <linux/miscdevice.h> +#include <linux/pm_runtime.h> #include <asm/atomic.h> #include "lis3lv02d.h" @@ -43,6 +45,16 @@ #define MDPS_POLL_INTERVAL 50 #define MDPS_POLL_MIN 0 #define MDPS_POLL_MAX 2000 + +#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ + +#define SELFTEST_OK 0 +#define SELFTEST_FAIL -1 +#define SELFTEST_IRQ -2 + +#define IRQ_LINE0 0 +#define IRQ_LINE1 1 + /* * The sensor can also generate interrupts (DRDY) but it's pretty pointless * because they are generated even if the data do not change. So it's better @@ -66,8 +78,10 @@ #define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024) #define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY) -#define LIS3_DEFAULT_FUZZ 3 -#define LIS3_DEFAULT_FLAT 3 +#define LIS3_DEFAULT_FUZZ_12B 3 +#define LIS3_DEFAULT_FLAT_12B 3 +#define LIS3_DEFAULT_FUZZ_8B 1 +#define LIS3_DEFAULT_FLAT_8B 1 struct lis3lv02d lis3_dev = { .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), @@ -75,6 +89,30 @@ struct lis3lv02d lis3_dev = { EXPORT_SYMBOL_GPL(lis3_dev); +/* just like param_set_int() but does sanity-check so that it won't point + * over the axis array size + */ +static int param_set_axis(const char *val, const struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + if (!ret) { + int val = *(int *)kp->arg; + if (val < 0) + val = -val; + if (!val || val > 3) + return -EINVAL; + } + return ret; +} + +static struct kernel_param_ops param_ops_axis = { + .set = param_set_axis, + .get = param_get_int, +}; + +module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644); +MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions"); + static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) { s8 lo; @@ -123,9 +161,24 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) int position[3]; int i; - position[0] = lis3->read_data(lis3, OUTX); - position[1] = lis3->read_data(lis3, OUTY); - position[2] = lis3->read_data(lis3, OUTZ); + if (lis3->blkread) { + if (lis3_dev.whoami == WAI_12B) { + u16 data[3]; + lis3->blkread(lis3, OUTX_L, 6, (u8 *)data); + for (i = 0; i < 3; i++) + position[i] = (s16)le16_to_cpu(data[i]); + } else { + u8 data[5]; + /* Data: x, dummy, y, dummy, z */ + lis3->blkread(lis3, OUTX, 5, data); + for (i = 0; i < 3; i++) + position[i] = (s8)data[i * 2]; + } + } else { + position[0] = lis3->read_data(lis3, OUTX); + position[1] = lis3->read_data(lis3, OUTY); + position[2] = lis3->read_data(lis3, OUTZ); + } for (i = 0; i < 3; i++) position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; @@ -138,6 +191,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) /* conversion btw sampling rate and the register values */ static int lis3_12_rates[4] = {40, 160, 640, 2560}; static int lis3_8_rates[2] = {100, 400}; +static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000}; /* ODR is Output Data Rate */ static int lis3lv02d_get_odr(void) @@ -156,6 +210,9 @@ static int lis3lv02d_set_odr(int rate) u8 ctrl; int i, len, shift; + if (!rate) + return -EINVAL; + lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); ctrl &= ~lis3_dev.odr_mask; len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ @@ -172,19 +229,42 @@ static int lis3lv02d_set_odr(int rate) static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) { - u8 reg; + u8 ctlreg, reg; s16 x, y, z; u8 selftest; int ret; + u8 ctrl_reg_data; + unsigned char irq_cfg; mutex_lock(&lis3->mutex); - if (lis3_dev.whoami == WAI_12B) - selftest = CTRL1_ST; - else - selftest = CTRL1_STP; - lis3->read(lis3, CTRL_REG1, ®); - lis3->write(lis3, CTRL_REG1, (reg | selftest)); + irq_cfg = lis3->irq_cfg; + if (lis3_dev.whoami == WAI_8B) { + lis3->data_ready_count[IRQ_LINE0] = 0; + lis3->data_ready_count[IRQ_LINE1] = 0; + + /* Change interrupt cfg to data ready for selftest */ + atomic_inc(&lis3_dev.wake_thread); + lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY; + lis3->read(lis3, CTRL_REG3, &ctrl_reg_data); + lis3->write(lis3, CTRL_REG3, (ctrl_reg_data & + ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) | + (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY)); + } + + if (lis3_dev.whoami == WAI_3DC) { + ctlreg = CTRL_REG4; + selftest = CTRL4_ST0; + } else { + ctlreg = CTRL_REG1; + if (lis3_dev.whoami == WAI_12B) + selftest = CTRL1_ST; + else + selftest = CTRL1_STP; + } + + lis3->read(lis3, ctlreg, ®); + lis3->write(lis3, ctlreg, (reg | selftest)); msleep(lis3->pwron_delay / lis3lv02d_get_odr()); /* Read directly to avoid axis remap */ @@ -193,7 +273,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) z = lis3->read_data(lis3, OUTZ); /* back to normal settings */ - lis3->write(lis3, CTRL_REG1, reg); + lis3->write(lis3, ctlreg, reg); msleep(lis3->pwron_delay / lis3lv02d_get_odr()); results[0] = x - lis3->read_data(lis3, OUTX); @@ -201,13 +281,33 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) results[2] = z - lis3->read_data(lis3, OUTZ); ret = 0; + + if (lis3_dev.whoami == WAI_8B) { + /* Restore original interrupt configuration */ + atomic_dec(&lis3_dev.wake_thread); + lis3->write(lis3, CTRL_REG3, ctrl_reg_data); + lis3->irq_cfg = irq_cfg; + + if ((irq_cfg & LIS3_IRQ1_MASK) && + lis3->data_ready_count[IRQ_LINE0] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + + if ((irq_cfg & LIS3_IRQ2_MASK) && + lis3->data_ready_count[IRQ_LINE1] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + } + if (lis3->pdata) { int i; for (i = 0; i < 3; i++) { /* Check against selftest acceptance limits */ if ((results[i] < lis3->pdata->st_min_limits[i]) || (results[i] > lis3->pdata->st_max_limits[i])) { - ret = -EIO; + ret = SELFTEST_FAIL; goto fail; } } @@ -219,10 +319,46 @@ fail: return ret; } +/* + * Order of registers in the list affects to order of the restore process. + * Perhaps it is a good idea to set interrupt enable register as a last one + * after all other configurations + */ +static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, + FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, + CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, + CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, + CTRL_REG1, CTRL_REG2, CTRL_REG3}; + +static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, + FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, + DD_THSE_L, DD_THSE_H, + CTRL_REG1, CTRL_REG3, CTRL_REG2}; + +static inline void lis3_context_save(struct lis3lv02d *lis3) +{ + int i; + for (i = 0; i < lis3->regs_size; i++) + lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); + lis3->regs_stored = true; +} + +static inline void lis3_context_restore(struct lis3lv02d *lis3) +{ + int i; + if (lis3->regs_stored) + for (i = 0; i < lis3->regs_size; i++) + lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); +} + void lis3lv02d_poweroff(struct lis3lv02d *lis3) { + if (lis3->reg_ctrl) + lis3_context_save(lis3); /* disable X,Y,Z axis and power down */ lis3->write(lis3, CTRL_REG1, 0x00); + if (lis3->reg_ctrl) + lis3->reg_ctrl(lis3, LIS3_REG_OFF); } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); @@ -232,19 +368,24 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3) lis3->init(lis3); - /* LIS3 power on delay is quite long */ - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - /* * Common configuration * BDU: (12 bits sensors only) LSB and MSB values are not updated until * both have been read. So the value read will always be correct. + * Set BOOT bit to refresh factory tuning values. */ - if (lis3->whoami == WAI_12B) { - lis3->read(lis3, CTRL_REG2, ®); - reg |= CTRL2_BDU; - lis3->write(lis3, CTRL_REG2, reg); - } + lis3->read(lis3, CTRL_REG2, ®); + if (lis3->whoami == WAI_12B) + reg |= CTRL2_BDU | CTRL2_BOOT; + else + reg |= CTRL2_BOOT_8B; + lis3->write(lis3, CTRL_REG2, reg); + + /* LIS3 power on delay is quite long */ + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); + + if (lis3->reg_ctrl) + lis3_context_restore(lis3); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -262,6 +403,27 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) mutex_unlock(&lis3_dev.mutex); } +static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) +{ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + + if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev) + atomic_set(&lis3_dev.wake_thread, 1); + /* + * Update coordinates for the case where poll interval is 0 and + * the chip in running purely under interrupt control + */ + lis3lv02d_joystick_poll(pidev); +} + +static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) +{ + atomic_set(&lis3_dev.wake_thread, 0); + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); +} + static irqreturn_t lis302dl_interrupt(int irq, void *dummy) { if (!test_bit(0, &lis3_dev.misc_opened)) @@ -277,8 +439,7 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) wake_up_interruptible(&lis3_dev.misc_wait); kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); out: - if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev && - lis3_dev.idev->input->users) + if (atomic_read(&lis3_dev.wake_thread)) return IRQ_WAKE_THREAD; return IRQ_HANDLED; } @@ -309,44 +470,41 @@ static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) mutex_unlock(&lis3->mutex); } -static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3) +static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index) { - u8 wu1_src; - u8 wu2_src; - - lis3->read(lis3, FF_WU_SRC_1, &wu1_src); - lis3->read(lis3, FF_WU_SRC_2, &wu2_src); + int dummy; - wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0; - wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0; - - /* joystick poll is internally protected by the lis3->mutex. */ - if (wu1_src || wu2_src) - lis3lv02d_joystick_poll(lis3_dev.idev); + /* Dummy read to ack interrupt */ + lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy); + lis3->data_ready_count[index]++; } static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) { - struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK; - if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK) + if (irq_cfg == LIS3_IRQ1_CLICK) lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE0); else - lis302dl_interrupt_handle_ff_wu(lis3); + lis3lv02d_joystick_poll(lis3->idev); return IRQ_HANDLED; } static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) { - struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK; - if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK) + if (irq_cfg == LIS3_IRQ2_CLICK) lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE1); else - lis302dl_interrupt_handle_ff_wu(lis3); + lis3lv02d_joystick_poll(lis3->idev); return IRQ_HANDLED; } @@ -356,6 +514,9 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file) if (test_and_set_bit(0, &lis3_dev.misc_opened)) return -EBUSY; /* already open */ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + atomic_set(&lis3_dev.count, 0); return 0; } @@ -364,6 +525,8 @@ static int lis3lv02d_misc_release(struct inode *inode, struct file *file) { fasync_helper(-1, file, 0, &lis3_dev.async_queue); clear_bit(0, &lis3_dev.misc_opened); /* release the device */ + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); return 0; } @@ -460,6 +623,8 @@ int lis3lv02d_joystick_enable(void) return -ENOMEM; lis3_dev.idev->poll = lis3lv02d_joystick_poll; + lis3_dev.idev->open = lis3lv02d_joystick_open; + lis3_dev.idev->close = lis3lv02d_joystick_close; lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; @@ -473,8 +638,16 @@ int lis3lv02d_joystick_enable(void) set_bit(EV_ABS, input_dev->evbit); max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY; - fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY; - flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY; + if (lis3_dev.whoami == WAI_12B) { + fuzz = LIS3_DEFAULT_FUZZ_12B; + flat = LIS3_DEFAULT_FLAT_12B; + } else { + fuzz = LIS3_DEFAULT_FUZZ_8B; + flat = LIS3_DEFAULT_FLAT_8B; + } + fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY; + flat = (flat * lis3_dev.scale) / LIS3_ACCURACY; + input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); @@ -512,14 +685,47 @@ void lis3lv02d_joystick_disable(void) EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); /* Sysfs stuff */ +static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) +{ + /* + * SYSFS functions are fast visitors so put-call + * immediately after the get-call. However, keep + * chip running for a while and schedule delayed + * suspend. This way periodic sysfs calls doesn't + * suffer from relatively long power up time. + */ + + if (lis3->pm_dev) { + pm_runtime_get_sync(lis3->pm_dev); + pm_runtime_put_noidle(lis3->pm_dev); + pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); + } +} + static ssize_t lis3lv02d_selftest_show(struct device *dev, struct device_attribute *attr, char *buf) { - int result; s16 values[3]; - result = lis3lv02d_selftest(&lis3_dev, values); - return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL", + static const char ok[] = "OK"; + static const char fail[] = "FAIL"; + static const char irq[] = "FAIL_IRQ"; + const char *res; + + lis3lv02d_sysfs_poweron(&lis3_dev); + switch (lis3lv02d_selftest(&lis3_dev, values)) { + case SELFTEST_FAIL: + res = fail; + break; + case SELFTEST_IRQ: + res = irq; + break; + case SELFTEST_OK: + default: + res = ok; + break; + } + return sprintf(buf, "%s %d %d %d\n", res, values[0], values[1], values[2]); } @@ -528,6 +734,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, { int x, y, z; + lis3lv02d_sysfs_poweron(&lis3_dev); mutex_lock(&lis3_dev.mutex); lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); mutex_unlock(&lis3_dev.mutex); @@ -537,6 +744,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, static ssize_t lis3lv02d_rate_show(struct device *dev, struct device_attribute *attr, char *buf) { + lis3lv02d_sysfs_poweron(&lis3_dev); return sprintf(buf, "%d\n", lis3lv02d_get_odr()); } @@ -549,6 +757,7 @@ static ssize_t lis3lv02d_rate_set(struct device *dev, if (strict_strtoul(buf, 0, &rate)) return -EINVAL; + lis3lv02d_sysfs_poweron(&lis3_dev); if (lis3lv02d_set_odr(rate)) return -EINVAL; @@ -585,6 +794,18 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3) { sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); platform_device_unregister(lis3->pdev); + if (lis3->pm_dev) { + /* Barrier after the sysfs remove */ + pm_runtime_barrier(lis3->pm_dev); + + /* SYSFS may have left chip running. Turn off if necessary */ + if (!pm_runtime_suspended(lis3->pm_dev)) + lis3lv02d_poweroff(&lis3_dev); + + pm_runtime_disable(lis3->pm_dev); + pm_runtime_set_suspended(lis3->pm_dev); + } + kfree(lis3->reg_cache); return 0; } EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); @@ -616,16 +837,16 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, if (p->wakeup_flags) { dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); - /* default to 2.5ms for now */ - dev->write(dev, FF_WU_DURATION_1, 1); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1); ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/ } if (p->wakeup_flags2) { dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2); dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f); - /* default to 2.5ms for now */ - dev->write(dev, FF_WU_DURATION_2, 1); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1); ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/ } /* Configure hipass filters */ @@ -635,8 +856,8 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, err = request_threaded_irq(p->irq2, NULL, lis302dl_interrupt_thread2_8b, - IRQF_TRIGGER_RISING | - IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + (p->irq_flags2 & IRQF_TRIGGER_MASK), DRIVER_NAME, &lis3_dev); if (err < 0) printk(KERN_ERR DRIVER_NAME @@ -652,6 +873,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) { int err; irq_handler_t thread_fn; + int irq_flags = 0; dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); @@ -664,6 +886,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) dev->odrs = lis3_12_rates; dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; dev->scale = LIS3_SENSITIVITY_12B; + dev->regs = lis3_wai12_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); break; case WAI_8B: printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n"); @@ -673,6 +897,17 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) dev->odrs = lis3_8_rates; dev->odr_mask = CTRL1_DR; dev->scale = LIS3_SENSITIVITY_8B; + dev->regs = lis3_wai8_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); + break; + case WAI_3DC: + printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n"); + dev->read_data = lis3lv02d_read_8; + dev->mdps_max_val = 128; + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; + dev->odrs = lis3_3dc_rates; + dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3; + dev->scale = LIS3_SENSITIVITY_8B; break; default: printk(KERN_ERR DRIVER_NAME @@ -680,11 +915,25 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) return -EINVAL; } + dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), + sizeof(lis3_wai12_regs)), GFP_KERNEL); + + if (dev->reg_cache == NULL) { + printk(KERN_ERR DRIVER_NAME "out of memory\n"); + return -ENOMEM; + } + mutex_init(&dev->mutex); + atomic_set(&dev->wake_thread, 0); lis3lv02d_add_fs(dev); lis3lv02d_poweron(dev); + if (dev->pm_dev) { + pm_runtime_set_active(dev->pm_dev); + pm_runtime_enable(dev->pm_dev); + } + if (lis3lv02d_joystick_enable()) printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); @@ -696,8 +945,14 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) if (dev->whoami == WAI_8B) lis3lv02d_8b_configure(dev, p); + irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK; + + dev->irq_cfg = p->irq_cfg; if (p->irq_cfg) dev->write(dev, CTRL_REG3, p->irq_cfg); + + if (p->default_rate) + lis3lv02d_set_odr(p->default_rate); } /* bail if we did not get an IRQ from the bus layer */ @@ -725,7 +980,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) err = request_threaded_irq(dev->irq, lis302dl_interrupt, thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + irq_flags, DRIVER_NAME, &lis3_dev); if (err < 0) { diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 854091380e3..a1939589eb2 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -20,6 +20,7 @@ */ #include <linux/platform_device.h> #include <linux/input-polldev.h> +#include <linux/regulator/consumer.h> /* * This driver tries to support the "digital" accelerometer chips from @@ -45,6 +46,7 @@ enum lis3_reg { CTRL_REG1 = 0x20, CTRL_REG2 = 0x21, CTRL_REG3 = 0x22, + CTRL_REG4 = 0x23, HP_FILTER_RESET = 0x23, STATUS_REG = 0x27, OUTX_L = 0x28, @@ -93,6 +95,7 @@ enum lis3lv02d_reg { }; enum lis3_who_am_i { + WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */ WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ @@ -118,6 +121,13 @@ enum lis3lv02d_ctrl1_8b { CTRL1_DR = 0x80, }; +enum lis3lv02d_ctrl1_3dc { + CTRL1_ODR0 = 0x10, + CTRL1_ODR1 = 0x20, + CTRL1_ODR2 = 0x40, + CTRL1_ODR3 = 0x80, +}; + enum lis3lv02d_ctrl2 { CTRL2_DAS = 0x01, CTRL2_SIM = 0x02, @@ -129,9 +139,18 @@ enum lis3lv02d_ctrl2 { CTRL2_FS = 0x80, /* Full Scale selection */ }; +enum lis3lv02d_ctrl4_3dc { + CTRL4_SIM = 0x01, + CTRL4_ST0 = 0x02, + CTRL4_ST1 = 0x04, + CTRL4_FS0 = 0x10, + CTRL4_FS1 = 0x20, +}; + enum lis302d_ctrl2 { HP_FF_WU2 = 0x08, HP_FF_WU1 = 0x04, + CTRL2_BOOT_8B = 0x40, }; enum lis3lv02d_ctrl3 { @@ -206,19 +225,33 @@ enum lis3lv02d_click_src_8b { CLICK_IA = 0x40, }; -struct axis_conversion { - s8 x; - s8 y; - s8 z; +enum lis3lv02d_reg_state { + LIS3_REG_OFF = 0x00, + LIS3_REG_ON = 0x01, +}; + +union axis_conversion { + struct { + int x, y, z; + }; + int as_array[3]; + }; struct lis3lv02d { void *bus_priv; /* used by the bus layer only */ + struct device *pm_dev; /* for pm_runtime purposes */ int (*init) (struct lis3lv02d *lis3); int (*write) (struct lis3lv02d *lis3, int reg, u8 val); int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); + int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret); + int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); int *odrs; /* Supported output data rates */ + u8 *regs; /* Regs to store / restore */ + int regs_size; + u8 *reg_cache; + bool regs_stored; u8 odr_mask; /* ODR bit mask */ u8 whoami; /* indicates measurement precision */ s16 (*read_data) (struct lis3lv02d *lis3, int reg); @@ -231,14 +264,18 @@ struct lis3lv02d { struct input_polled_dev *idev; /* input device */ struct platform_device *pdev; /* platform device */ + struct regulator_bulk_data regulators[2]; atomic_t count; /* interrupt count after last read */ - struct axis_conversion ac; /* hw -> logical axis */ + union axis_conversion ac; /* hw -> logical axis */ int mapped_btns[3]; u32 irq; /* IRQ number */ struct fasync_struct *async_queue; /* queue for the misc device */ wait_queue_head_t misc_wait; /* Wait queue for the misc device */ unsigned long misc_opened; /* bit0: whether the device is open */ + int data_ready_count[2]; + atomic_t wake_thread; + unsigned char irq_cfg; struct lis3lv02d_platform_data *pdata; /* for passing board config */ struct mutex mutex; /* Serialize poll and selftest */ diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c index 8e5933b72d1..9f4bae07f71 100644 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ b/drivers/hwmon/lis3lv02d_i2c.c @@ -29,10 +29,30 @@ #include <linux/init.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> #include "lis3lv02d.h" #define DRV_NAME "lis3lv02d_i2c" +static const char reg_vdd[] = "Vdd"; +static const char reg_vdd_io[] = "Vdd_IO"; + +static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) +{ + int ret; + if (state == LIS3_REG_OFF) { + ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + } else { + ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + /* Chip needs time to wakeup. Not mentioned in datasheet */ + usleep_range(10000, 20000); + } + return ret; +} + static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) { struct i2c_client *c = lis3->bus_priv; @@ -46,24 +66,38 @@ static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) return 0; } +static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, + u8 *v) +{ + struct i2c_client *c = lis3->bus_priv; + reg |= (1 << 7); /* 7th bit enables address auto incrementation */ + return i2c_smbus_read_i2c_block_data(c, reg, len, v); +} + static int lis3_i2c_init(struct lis3lv02d *lis3) { u8 reg; int ret; + if (lis3->reg_ctrl) + lis3_reg_ctrl(lis3, LIS3_REG_ON); + + lis3->read(lis3, WHO_AM_I, ®); + if (reg != lis3->whoami) + printk(KERN_ERR "lis3: power on failure\n"); + /* power up the device */ ret = lis3->read(lis3, CTRL_REG1, ®); if (ret < 0) return ret; - reg |= CTRL1_PD0; + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; return lis3->write(lis3, CTRL_REG1, reg); } /* Default axis mapping but it can be overwritten by platform data */ -static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X, - LIS3_DEV_Y, - LIS3_DEV_Z }; +static union axis_conversion lis3lv02d_axis_map = + { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -72,6 +106,15 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, struct lis3lv02d_platform_data *pdata = client->dev.platform_data; if (pdata) { + /* Regulator control is optional */ + if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) + lis3_dev.reg_ctrl = lis3_reg_ctrl; + + if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK))) + lis3_dev.blkread = lis3_i2c_blockread; + if (pdata->axis_x) lis3lv02d_axis_map.x = pdata->axis_x; @@ -88,6 +131,16 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, goto fail; } + if (lis3_dev.reg_ctrl) { + lis3_dev.regulators[0].supply = reg_vdd; + lis3_dev.regulators[1].supply = reg_vdd_io; + ret = regulator_bulk_get(&client->dev, + ARRAY_SIZE(lis3_dev.regulators), + lis3_dev.regulators); + if (ret < 0) + goto fail; + } + lis3_dev.pdata = pdata; lis3_dev.bus_priv = client; lis3_dev.init = lis3_i2c_init; @@ -95,10 +148,24 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, lis3_dev.write = lis3_i2c_write; lis3_dev.irq = client->irq; lis3_dev.ac = lis3lv02d_axis_map; + lis3_dev.pm_dev = &client->dev; i2c_set_clientdata(client, &lis3_dev); + + /* Provide power over the init call */ + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); + ret = lis3lv02d_init_device(&lis3_dev); + + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); + + if (ret == 0) + return 0; fail: + if (pdata && pdata->release_resources) + pdata->release_resources(); return ret; } @@ -111,14 +178,18 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) pdata->release_resources(); lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(lis3); + lis3lv02d_remove_fs(&lis3_dev); - return lis3lv02d_remove_fs(&lis3_dev); + if (lis3_dev.reg_ctrl) + regulator_bulk_free(ARRAY_SIZE(lis3->regulators), + lis3_dev.regulators); + return 0; } #ifdef CONFIG_PM -static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +static int lis3lv02d_i2c_suspend(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); if (!lis3->pdata || !lis3->pdata->wakeup_flags) @@ -126,18 +197,21 @@ static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int lis3lv02d_i2c_resume(struct i2c_client *client) +static int lis3lv02d_i2c_resume(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); - if (!lis3->pdata || !lis3->pdata->wakeup_flags) + /* + * pm_runtime documentation says that devices should always + * be powered on at resume. Pm_runtime turns them off after system + * wide resume is complete. + */ + if (!lis3->pdata || !lis3->pdata->wakeup_flags || + pm_runtime_suspended(dev)) lis3lv02d_poweron(lis3); - return 0; -} -static void lis3lv02d_i2c_shutdown(struct i2c_client *client) -{ - lis3lv02d_i2c_suspend(client, PMSG_SUSPEND); + return 0; } #else #define lis3lv02d_i2c_suspend NULL @@ -145,6 +219,24 @@ static void lis3lv02d_i2c_shutdown(struct i2c_client *client) #define lis3lv02d_i2c_shutdown NULL #endif +static int lis3_i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweroff(lis3); + return 0; +} + +static int lis3_i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweron(lis3); + return 0; +} + static const struct i2c_device_id lis3lv02d_id[] = { {"lis3lv02d", 0 }, {} @@ -152,14 +244,20 @@ static const struct i2c_device_id lis3lv02d_id[] = { MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); +static const struct dev_pm_ops lis3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, + lis3lv02d_i2c_resume) + SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, + lis3_i2c_runtime_resume, + NULL) +}; + static struct i2c_driver lis3lv02d_i2c_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .pm = &lis3_pm_ops, }, - .suspend = lis3lv02d_i2c_suspend, - .shutdown = lis3lv02d_i2c_shutdown, - .resume = lis3lv02d_i2c_resume, .probe = lis3lv02d_i2c_probe, .remove = __devexit_p(lis3lv02d_i2c_remove), .id_table = lis3lv02d_id, diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c index b9be5e3a22b..2549de1de4e 100644 --- a/drivers/hwmon/lis3lv02d_spi.c +++ b/drivers/hwmon/lis3lv02d_spi.c @@ -50,11 +50,12 @@ static int lis3_spi_init(struct lis3lv02d *lis3) if (ret < 0) return ret; - reg |= CTRL1_PD0; + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; return lis3->write(lis3, CTRL_REG1, reg); } -static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 }; +static union axis_conversion lis3lv02d_axis_normal = + { .as_array = { 1, 2, 3 } }; static int __devinit lis302dl_spi_probe(struct spi_device *spi) { diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c new file mode 100644 index 00000000000..26762617867 --- /dev/null +++ b/drivers/hwmon/ltc4261.c @@ -0,0 +1,315 @@ +/* + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller + * + * Copyright (C) 2010 Ericsson AB. + * + * Derived from: + * + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller + * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> + * + * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +/* chip registers */ +#define LTC4261_STATUS 0x00 /* readonly */ +#define LTC4261_FAULT 0x01 +#define LTC4261_ALERT 0x02 +#define LTC4261_CONTROL 0x03 +#define LTC4261_SENSE_H 0x04 +#define LTC4261_SENSE_L 0x05 +#define LTC4261_ADIN2_H 0x06 +#define LTC4261_ADIN2_L 0x07 +#define LTC4261_ADIN_H 0x08 +#define LTC4261_ADIN_L 0x09 + +/* + * Fault register bits + */ +#define FAULT_OV (1<<0) +#define FAULT_UV (1<<1) +#define FAULT_OC (1<<2) + +struct ltc4261_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[10]; +}; + +static struct ltc4261_data *ltc4261_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = i2c_get_clientdata(client); + struct ltc4261_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) { + int i; + + /* Read registers -- 0x00 to 0x09 */ + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + int val; + + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) { + dev_dbg(dev, + "Failed to read ADC value: error %d", + val); + ret = ERR_PTR(val); + goto abort; + } + data->regs[i] = val; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* Return the voltage from the given register in mV or mA */ +static int ltc4261_get_value(struct ltc4261_data *data, u8 reg) +{ + u32 val; + + val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6); + + switch (reg) { + case LTC4261_ADIN_H: + case LTC4261_ADIN2_H: + /* 2.5mV resolution. Convert to mV. */ + val = val * 25 / 10; + break; + case LTC4261_SENSE_H: + /* + * 62.5uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = val * 625 / 10; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ltc4261_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ltc4261_data *data = ltc4261_update_device(dev); + int value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = ltc4261_get_value(data, attr->index); + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4261_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = ltc4261_update_device(dev); + u8 fault; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fault = data->regs[LTC4261_FAULT] & attr->index; + if (fault) /* Clear reported faults in chip register */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault); + + return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); +} + +/* + * These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4261_VALUE(name, ltc4261_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_value, NULL, ltc4261_cmd_idx) + +#define LTC4261_BOOL(name, mask) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_bool, NULL, (mask)) + +/* + * Input voltages. + */ +LTC4261_VALUE(in1_input, LTC4261_ADIN_H); +LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); + +/* + * Voltage alarms. The chip has only one set of voltage alarm status bits, + * triggered by input voltage alarms. In many designs, those alarms are + * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin + * to the OV pin. ADIN2 is, however, not available on all chip variants. + * To ensure that the alarm condition is reported to the user, report it + * with both voltage sensors. + */ +LTC4261_BOOL(in1_min_alarm, FAULT_UV); +LTC4261_BOOL(in1_max_alarm, FAULT_OV); +LTC4261_BOOL(in2_min_alarm, FAULT_UV); +LTC4261_BOOL(in2_max_alarm, FAULT_OV); + +/* Currents (via sense resistor) */ +LTC4261_VALUE(curr1_input, LTC4261_SENSE_H); + +/* Overcurrent alarm */ +LTC4261_BOOL(curr1_max_alarm, FAULT_OC); + +static struct attribute *ltc4261_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4261_group = { + .attrs = ltc4261_attributes, +}; + +static int ltc4261_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ltc4261_data *data; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { + dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n", + adapter->id, client->addr, LTC4261_STATUS); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Clear faults */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4261_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4261_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4261_remove(struct i2c_client *client) +{ + struct ltc4261_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4261_group); + + kfree(data); + + return 0; +} + +static const struct i2c_device_id ltc4261_id[] = { + {"ltc4261", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ltc4261_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4261_driver = { + .driver = { + .name = "ltc4261", + }, + .probe = ltc4261_probe, + .remove = ltc4261_remove, + .id_table = ltc4261_id, +}; + +static int __init ltc4261_init(void) +{ + return i2c_add_driver(<c4261_driver); +} + +static void __exit ltc4261_exit(void) +{ + i2c_del_driver(<c4261_driver); +} + +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_DESCRIPTION("LTC4261 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4261_init); +module_exit(ltc4261_exit); diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c index f11903936c8..0798210590b 100644 --- a/drivers/hwmon/pkgtemp.c +++ b/drivers/hwmon/pkgtemp.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -35,6 +34,7 @@ #include <linux/cpu.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/smp.h> #define DRVNAME "pkgtemp" @@ -339,8 +339,7 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU -static void pkgtemp_device_remove(unsigned int cpu) +static void __cpuinit pkgtemp_device_remove(unsigned int cpu) { struct pdev_entry *p; unsigned int i; @@ -387,12 +386,10 @@ static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block pkgtemp_cpu_notifier __refdata = { .notifier_call = pkgtemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ static int __init pkgtemp_init(void) { int i, err = -ENODEV; - struct pdev_entry *p, *n; /* quick check if we run Intel */ if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL) @@ -402,31 +399,23 @@ static int __init pkgtemp_init(void) if (err) goto exit; - for_each_online_cpu(i) { - err = pkgtemp_device_add(i); - if (err) - goto exit_devices_unreg; - } + for_each_online_cpu(i) + pkgtemp_device_add(i); + +#ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { err = -ENODEV; goto exit_driver_unreg; } +#endif -#ifdef CONFIG_HOTPLUG_CPU register_hotcpu_notifier(&pkgtemp_cpu_notifier); -#endif return 0; -exit_devices_unreg: - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); +#ifndef CONFIG_HOTPLUG_CPU exit_driver_unreg: platform_driver_unregister(&pkgtemp_driver); +#endif exit: return err; } @@ -434,9 +423,8 @@ exit: static void __exit pkgtemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&pkgtemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index ffb793af680..ec7fad747ad 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -22,10 +22,8 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/jiffies.h> #include <linux/hwmon.h> #include <linux/sysfs.h> #include <linux/hwmon-sysfs.h> @@ -237,8 +235,7 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU -static void via_cputemp_device_remove(unsigned int cpu) +static void __cpuinit via_cputemp_device_remove(unsigned int cpu) { struct pdev_entry *p, *n; mutex_lock(&pdev_list_mutex); @@ -272,7 +269,6 @@ static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block via_cputemp_cpu_notifier __refdata = { .notifier_call = via_cputemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ static int __init via_cputemp_init(void) { @@ -313,9 +309,7 @@ static int __init via_cputemp_init(void) goto exit_driver_unreg; } -#ifdef CONFIG_HOTPLUG_CPU register_hotcpu_notifier(&via_cputemp_cpu_notifier); -#endif return 0; exit_devices_unreg: @@ -335,9 +329,8 @@ exit: static void __exit via_cputemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index 4f93da31d3a..3cad8fecc3d 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -101,12 +101,12 @@ struct cami2c { static inline void OUT32(struct cami2c *cam, int reg, unsigned long val) { - ctrl_outl(val, (unsigned long)cam->iobase + reg); + __raw_writel(val, (unsigned long)cam->iobase + reg); } static inline unsigned long IN32(struct cami2c *cam, int reg) { - return ctrl_inl((unsigned long)cam->iobase + reg); + return __raw_readl((unsigned long)cam->iobase + reg); } static irqreturn_t sh7760_i2c_irq(int irq, void *ptr) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 598c49acaeb..2707f5e1715 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -538,15 +538,17 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) { struct resource *res; int ret = -ENXIO; - int q, m; - int k = 0; - int n = 0; + int n, k = 0; while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) { for (n = res->start; hook && n <= res->end; n++) { if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED, - dev_name(&dev->dev), dev)) + dev_name(&dev->dev), dev)) { + for (n--; n >= res->start; n--) + free_irq(n, dev); + goto rollback; + } } k++; } @@ -554,16 +556,17 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) if (hook) return k > 0 ? 0 : -ENOENT; - k--; ret = 0; rollback: - for (q = k; k >= 0; k--) { - for (m = n; m >= res->start; m--) - free_irq(m, dev); + k--; + + while (k >= 0) { + res = platform_get_resource(dev, IORESOURCE_IRQ, k); + for (n = res->start; n <= res->end; n++) + free_irq(n, dev); - res = platform_get_resource(dev, IORESOURCE_IRQ, k - 1); - m = res->end; + k--; } return ret; diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig deleted file mode 100644 index e02096cf7d9..00000000000 --- a/drivers/ieee1394/Kconfig +++ /dev/null @@ -1,182 +0,0 @@ -config IEEE1394 - tristate "Legacy alternative FireWire driver stack" - depends on PCI || BROKEN - help - IEEE 1394 describes a high performance serial bus, which is also - known as FireWire(tm) or i.Link(tm) and is used for connecting all - sorts of devices (most notably digital video cameras) to your - computer. - - If you have FireWire hardware and want to use it, say Y here. This - is the core support only, you will also need to select a driver for - your IEEE 1394 adapter. - - To compile this driver as a module, say M here: the module will be - called ieee1394. - - NOTE: - ieee1394 is superseded by the newer firewire-core driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_OHCI1394 - tristate "OHCI-1394 controllers" - depends on PCI && IEEE1394 - help - Enable this driver if you have an IEEE 1394 controller based on the - OHCI-1394 specification. The current driver is only tested with OHCI - chipsets made by Texas Instruments and NEC. Most third-party vendors - use one of these chipsets. It should work with any OHCI-1394 - compliant card, however. - - To compile this driver as a module, say M here: the module will be - called ohci1394. - - NOTE: - ohci1394 is superseded by the newer firewire-ohci driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - - If you want to install firewire-ohci and ohci1394 together, you - should configure them only as modules and blacklist the driver(s) - which you don't want to have auto-loaded. Add either - - blacklist ohci1394 - blacklist video1394 - blacklist dv1394 - or - blacklist firewire-ohci - - to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf - depending on your distribution. - -comment "PCILynx controller requires I2C" - depends on IEEE1394 && I2C=n - -config IEEE1394_PCILYNX - tristate "PCILynx controller" - depends on PCI && IEEE1394 && I2C - select I2C_ALGOBIT - help - Say Y here if you have an IEEE-1394 controller with the Texas - Instruments PCILynx chip. Note: this driver is written for revision - 2 of this chip and may not work with revision 0. - - To compile this driver as a module, say M here: the module will be - called pcilynx. - - Only some old and now very rare PCI and CardBus cards and - PowerMacs G3 B&W contain the PCILynx controller. Therefore - almost everybody can say N here. - -comment "SBP-2 support (for storage devices) requires SCSI" - depends on IEEE1394 && SCSI=n - -config IEEE1394_SBP2 - tristate "Storage devices (SBP-2 protocol)" - depends on IEEE1394 && SCSI - help - This option enables you to use SBP-2 devices connected to an IEEE - 1394 bus. SBP-2 devices include storage devices like harddisks and - DVD drives, also some other FireWire devices like scanners. - - You should also enable support for disks, CD-ROMs, etc. in the SCSI - configuration section. - - To compile this driver as a module, say M here: the module will be - called sbp2. - - NOTE: - sbp2 is superseded by the newer firewire-sbp2 driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_SBP2_PHYS_DMA - bool "Enable replacement for physical DMA in SBP2" - depends on IEEE1394_SBP2 && VIRT_TO_BUS && EXPERIMENTAL - help - This builds sbp2 for use with non-OHCI host adapters which do not - support physical DMA or for when ohci1394 is run with phys_dma=0. - Physical DMA is data movement without assistance of the drivers' - interrupt handlers. This option includes the interrupt handlers - that are required in absence of this hardware feature. - - This option is buggy and currently broken on some architectures. - If unsure, say N. - -config IEEE1394_ETH1394_ROM_ENTRY - depends on IEEE1394 - bool - default n - -config IEEE1394_ETH1394 - tristate "IP networking over 1394 (experimental)" - depends on IEEE1394 && EXPERIMENTAL && INET - select IEEE1394_ETH1394_ROM_ENTRY - help - This driver implements a functional majority of RFC 2734: IPv4 over - 1394. It will provide IP connectivity with implementations of RFC - 2734 found on other operating systems. It will not communicate with - older versions of this driver found in stock kernels prior to 2.6.3. - This driver is still considered experimental. It does not yet support - MCAP, therefore multicast support is significantly limited. - - The module is called eth1394 although it does not emulate Ethernet. - - NOTE: - eth1394 is superseded by the newer firewire-net driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_RAWIO - tristate "raw1394 userspace interface" - depends on IEEE1394 - help - This option adds support for the raw1394 device file which enables - direct communication of user programs with IEEE 1394 devices - (isochronous and asynchronous). Almost all application programs - which access FireWire require this option. - - To compile this driver as a module, say M here: the module will be - called raw1394. - - NOTE: - raw1394 is superseded by the newer firewire-core driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_VIDEO1394 - tristate "video1394 userspace interface" - depends on IEEE1394 && IEEE1394_OHCI1394 - help - This option adds support for the video1394 device files which enable - isochronous communication of user programs with IEEE 1394 devices, - especially video capture or export. This interface is used by all - libdc1394 based programs and by several other programs, in addition to - the raw1394 interface. It is generally not required for DV capture. - - To compile this driver as a module, say M here: the module will be - called video1394. - - NOTE: - video1394 is superseded by the newer firewire-core driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_DV1394 - tristate "dv1394 userspace interface (deprecated)" - depends on IEEE1394 && IEEE1394_OHCI1394 - help - The dv1394 driver is unsupported and may be removed from Linux in a - future release. Its functionality is now provided by either - raw1394 or firewire-core together with libraries such as libiec61883. - -config IEEE1394_VERBOSEDEBUG - bool "Excessive debugging output" - depends on IEEE1394 - help - If you say Y here, you will get very verbose debugging logs from the - ieee1394 drivers, including sent and received packet headers. This - will quickly result in large amounts of data sent to the system log. - - Say Y if you really need the debugging output. Everyone else says N. diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile deleted file mode 100644 index 1f8153b5750..00000000000 --- a/drivers/ieee1394/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Makefile for the Linux IEEE 1394 implementation -# - -ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ - highlevel.o csr.o nodemgr.o dma.o iso.o \ - csr1212.o config_roms.o - -obj-$(CONFIG_IEEE1394) += ieee1394.o -obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o -obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o -obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o -obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o -obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o -obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o -obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o - -obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c deleted file mode 100644 index 1b981207fa7..00000000000 --- a/drivers/ieee1394/config_roms.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * ConfigROM entries - * - * Copyright (C) 2004 Ben Collins - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include <linux/types.h> - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "csr.h" -#include "config_roms.h" - -struct hpsb_config_rom_entry { - const char *name; - - /* Base initialization, called at module load */ - int (*init)(void); - - /* Cleanup called at module exit */ - void (*cleanup)(void); - - /* The flag added to host->config_roms */ - unsigned int flag; -}; - -/* The default host entry. This must succeed. */ -int hpsb_default_host_entry(struct hpsb_host *host) -{ - struct csr1212_keyval *root; - struct csr1212_keyval *vend_id = NULL; - struct csr1212_keyval *text = NULL; - char csr_name[128]; - int ret; - - sprintf(csr_name, "Linux - %s", host->driver->name); - root = host->csr.rom->root_kv; - - vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR, host->csr.guid_hi >> 8); - text = csr1212_new_string_descriptor_leaf(csr_name); - - if (!vend_id || !text) { - if (vend_id) - csr1212_release_keyval(vend_id); - if (text) - csr1212_release_keyval(text); - csr1212_destroy_csr(host->csr.rom); - return -ENOMEM; - } - - csr1212_associate_keyval(vend_id, text); - csr1212_release_keyval(text); - ret = csr1212_attach_keyval_to_directory(root, vend_id); - csr1212_release_keyval(vend_id); - if (ret != CSR1212_SUCCESS) { - csr1212_destroy_csr(host->csr.rom); - return -ENOMEM; - } - - host->update_config_rom = 1; - - return 0; -} - - -#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY -#include "eth1394.h" - -static struct csr1212_keyval *ip1394_ud; - -static int config_rom_ip1394_init(void) -{ - struct csr1212_keyval *spec_id = NULL; - struct csr1212_keyval *spec_desc = NULL; - struct csr1212_keyval *ver = NULL; - struct csr1212_keyval *ver_desc = NULL; - int ret = -ENOMEM; - - ip1394_ud = csr1212_new_directory(CSR1212_KV_ID_UNIT); - - spec_id = csr1212_new_immediate(CSR1212_KV_ID_SPECIFIER_ID, - ETHER1394_GASP_SPECIFIER_ID); - spec_desc = csr1212_new_string_descriptor_leaf("IANA"); - ver = csr1212_new_immediate(CSR1212_KV_ID_VERSION, - ETHER1394_GASP_VERSION); - ver_desc = csr1212_new_string_descriptor_leaf("IPv4"); - - if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc) - goto ip1394_fail; - - csr1212_associate_keyval(spec_id, spec_desc); - csr1212_associate_keyval(ver, ver_desc); - if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) - == CSR1212_SUCCESS && - csr1212_attach_keyval_to_directory(ip1394_ud, ver) - == CSR1212_SUCCESS) - ret = 0; - -ip1394_fail: - if (ret && ip1394_ud) { - csr1212_release_keyval(ip1394_ud); - ip1394_ud = NULL; - } - - if (spec_id) - csr1212_release_keyval(spec_id); - if (spec_desc) - csr1212_release_keyval(spec_desc); - if (ver) - csr1212_release_keyval(ver); - if (ver_desc) - csr1212_release_keyval(ver_desc); - - return ret; -} - -static void config_rom_ip1394_cleanup(void) -{ - if (ip1394_ud) { - csr1212_release_keyval(ip1394_ud); - ip1394_ud = NULL; - } -} - -int hpsb_config_rom_ip1394_add(struct hpsb_host *host) -{ - if (!ip1394_ud) - return -ENODEV; - - if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv, - ip1394_ud) != CSR1212_SUCCESS) - return -ENOMEM; - - host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394; - host->update_config_rom = 1; - return 0; -} -EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add); - -void hpsb_config_rom_ip1394_remove(struct hpsb_host *host) -{ - csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud); - host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394; - host->update_config_rom = 1; -} -EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove); - -static struct hpsb_config_rom_entry ip1394_entry = { - .name = "ip1394", - .init = config_rom_ip1394_init, - .cleanup = config_rom_ip1394_cleanup, - .flag = HPSB_CONFIG_ROM_ENTRY_IP1394, -}; - -#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */ - -static struct hpsb_config_rom_entry *const config_rom_entries[] = { -#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY - &ip1394_entry, -#endif -}; - -/* Initialize all config roms */ -int hpsb_init_config_roms(void) -{ - int i, error = 0; - - for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) - if (config_rom_entries[i]->init()) { - HPSB_ERR("Failed to initialize config rom entry `%s'", - config_rom_entries[i]->name); - error = -1; - } - - return error; -} - -/* Cleanup all config roms */ -void hpsb_cleanup_config_roms(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) - config_rom_entries[i]->cleanup(); -} diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h deleted file mode 100644 index 1f5cd1f16c4..00000000000 --- a/drivers/ieee1394/config_roms.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _IEEE1394_CONFIG_ROMS_H -#define _IEEE1394_CONFIG_ROMS_H - -struct hpsb_host; - -int hpsb_default_host_entry(struct hpsb_host *host); -int hpsb_init_config_roms(void); -void hpsb_cleanup_config_roms(void); - -/* List of flags to check if a host contains a certain extra config rom - * entry. Available in the host->config_roms member. */ -#define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001 - -#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY -int hpsb_config_rom_ip1394_add(struct hpsb_host *host); -void hpsb_config_rom_ip1394_remove(struct hpsb_host *host); -#endif - -#endif /* _IEEE1394_CONFIG_ROMS_H */ diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c deleted file mode 100644 index d696f69ebce..00000000000 --- a/drivers/ieee1394/csr.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * CSR implementation, iso/bus manager implementation. - * - * Copyright (C) 1999 Andreas E. Bombe - * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at> - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * configuration ROM manipulation - * - */ - -#include <linux/jiffies.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/param.h> -#include <linux/spinlock.h> -#include <linux/string.h> - -#include "csr1212.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394.h" -#include "highlevel.h" -#include "ieee1394_core.h" - -/* Module Parameters */ -/* this module parameter can be used to disable mapping of the FCP registers */ - -static int fcp = 1; -module_param(fcp, int, 0444); -MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0)."); - -static struct csr1212_keyval *node_cap = NULL; - -static void add_host(struct hpsb_host *host); -static void remove_host(struct hpsb_host *host); -static void host_reset(struct hpsb_host *host); -static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl); -static int write_fcp(struct hpsb_host *host, int nodeid, int dest, - quadlet_t *data, u64 addr, size_t length, u16 flags); -static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf, - u64 addr, size_t length, u16 flags); -static int write_regs(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags); -static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl); -static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl); -static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl); -static u64 allocate_addr_range(u64 size, u32 alignment, void *__host); -static void release_addr_range(u64 addr, void *__host); - -static struct hpsb_highlevel csr_highlevel = { - .name = "standard registers", - .add_host = add_host, - .remove_host = remove_host, - .host_reset = host_reset, -}; - -static const struct hpsb_address_ops map_ops = { - .read = read_maps, -}; - -static const struct hpsb_address_ops fcp_ops = { - .write = write_fcp, -}; - -static const struct hpsb_address_ops reg_ops = { - .read = read_regs, - .write = write_regs, - .lock = lock_regs, - .lock64 = lock64_regs, -}; - -static const struct hpsb_address_ops config_rom_ops = { - .read = read_config_rom, -}; - -struct csr1212_bus_ops csr_bus_ops = { - .allocate_addr_range = allocate_addr_range, - .release_addr = release_addr_range, -}; - - -static u16 csr_crc16(unsigned *data, int length) -{ - int check=0, i; - int shift, sum, next=0; - - for (i = length; i; i--) { - for (next = check, shift = 28; shift >= 0; shift -= 4 ) { - sum = ((next >> 12) ^ (be32_to_cpu(*data) >> shift)) & 0xf; - next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); - } - check = next & 0xffff; - data++; - } - - return check; -} - -static void host_reset(struct hpsb_host *host) -{ - host->csr.state &= 0x300; - - host->csr.bus_manager_id = 0x3f; - host->csr.bandwidth_available = 4915; - host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */ - host->csr.channels_available_lo = ~0; - host->csr.broadcast_channel = 0x80000000 | 31; - - if (host->is_irm) { - if (host->driver->hw_csr_reg) { - host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0); - } - } - - host->csr.node_ids = host->node_id << 16; - - if (!host->is_root) { - /* clear cmstr bit */ - host->csr.state &= ~0x100; - } - - be32_add_cpu(&host->csr.topology_map[1], 1); - host->csr.topology_map[2] = cpu_to_be32(host->node_count << 16 - | host->selfid_count); - host->csr.topology_map[0] = - cpu_to_be32((host->selfid_count + 2) << 16 - | csr_crc16(host->csr.topology_map + 1, - host->selfid_count + 2)); - - be32_add_cpu(&host->csr.speed_map[1], 1); - host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16 - | csr_crc16(host->csr.speed_map+1, - 0x3f1)); -} - -/* - * HI == seconds (bits 0:2) - * LO == fractions of a second in units of 125usec (bits 19:31) - * - * Convert SPLIT_TIMEOUT to jiffies. - * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms. - */ -static inline void calculate_expire(struct csr_control *csr) -{ - unsigned int usecs = (csr->split_timeout_hi & 7) * 1000000 + - (csr->split_timeout_lo >> 19) * 125; - - csr->expire = usecs_to_jiffies(usecs > 100000 ? usecs : 100000); - HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ); -} - - -static void add_host(struct hpsb_host *host) -{ - struct csr1212_keyval *root; - quadlet_t bus_info[CSR_BUS_INFO_SIZE]; - - hpsb_register_addrspace(&csr_highlevel, host, ®_ops, - CSR_REGISTER_BASE, - CSR_REGISTER_BASE + CSR_CONFIG_ROM); - hpsb_register_addrspace(&csr_highlevel, host, &config_rom_ops, - CSR_REGISTER_BASE + CSR_CONFIG_ROM, - CSR_REGISTER_BASE + CSR_CONFIG_ROM_END); - if (fcp) { - hpsb_register_addrspace(&csr_highlevel, host, &fcp_ops, - CSR_REGISTER_BASE + CSR_FCP_COMMAND, - CSR_REGISTER_BASE + CSR_FCP_END); - } - hpsb_register_addrspace(&csr_highlevel, host, &map_ops, - CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP, - CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END); - hpsb_register_addrspace(&csr_highlevel, host, &map_ops, - CSR_REGISTER_BASE + CSR_SPEED_MAP, - CSR_REGISTER_BASE + CSR_SPEED_MAP_END); - - spin_lock_init(&host->csr.lock); - - host->csr.state = 0; - host->csr.node_ids = 0; - host->csr.split_timeout_hi = 0; - host->csr.split_timeout_lo = 800 << 19; - calculate_expire(&host->csr); - host->csr.cycle_time = 0; - host->csr.bus_time = 0; - host->csr.bus_manager_id = 0x3f; - host->csr.bandwidth_available = 4915; - host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */ - host->csr.channels_available_lo = ~0; - host->csr.broadcast_channel = 0x80000000 | 31; - - if (host->is_irm) { - if (host->driver->hw_csr_reg) { - host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0); - } - } - - if (host->csr.max_rec >= 9) - host->csr.max_rom = 2; - else if (host->csr.max_rec >= 5) - host->csr.max_rom = 1; - else - host->csr.max_rom = 0; - - host->csr.generation = 2; - - bus_info[1] = IEEE1394_BUSID_MAGIC; - bus_info[2] = cpu_to_be32((hpsb_disable_irm ? 0 : 1 << CSR_IRMC_SHIFT) | - (1 << CSR_CMC_SHIFT) | - (1 << CSR_ISC_SHIFT) | - (0 << CSR_BMC_SHIFT) | - (0 << CSR_PMC_SHIFT) | - (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) | - (host->csr.max_rec << CSR_MAX_REC_SHIFT) | - (host->csr.max_rom << CSR_MAX_ROM_SHIFT) | - (host->csr.generation << CSR_GENERATION_SHIFT) | - host->csr.lnk_spd); - - bus_info[3] = cpu_to_be32(host->csr.guid_hi); - bus_info[4] = cpu_to_be32(host->csr.guid_lo); - - /* The hardware copy of the bus info block will be set later when a - * bus reset is issued. */ - - csr1212_init_local_csr(host->csr.rom, bus_info, host->csr.max_rom); - - root = host->csr.rom->root_kv; - - if(csr1212_attach_keyval_to_directory(root, node_cap) != CSR1212_SUCCESS) { - HPSB_ERR("Failed to attach Node Capabilities to root directory"); - } - - host->update_config_rom = 1; -} - -static void remove_host(struct hpsb_host *host) -{ - quadlet_t bus_info[CSR_BUS_INFO_SIZE]; - - bus_info[1] = IEEE1394_BUSID_MAGIC; - bus_info[2] = cpu_to_be32((0 << CSR_IRMC_SHIFT) | - (0 << CSR_CMC_SHIFT) | - (0 << CSR_ISC_SHIFT) | - (0 << CSR_BMC_SHIFT) | - (0 << CSR_PMC_SHIFT) | - (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) | - (host->csr.max_rec << CSR_MAX_REC_SHIFT) | - (0 << CSR_MAX_ROM_SHIFT) | - (0 << CSR_GENERATION_SHIFT) | - host->csr.lnk_spd); - - bus_info[3] = cpu_to_be32(host->csr.guid_hi); - bus_info[4] = cpu_to_be32(host->csr.guid_lo); - - csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, node_cap); - - csr1212_init_local_csr(host->csr.rom, bus_info, 0); - host->update_config_rom = 1; -} - - -int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, - size_t buffersize, unsigned char rom_version) -{ - unsigned long flags; - int ret; - - HPSB_NOTICE("hpsb_update_config_rom() is deprecated"); - - spin_lock_irqsave(&host->csr.lock, flags); - if (rom_version != host->csr.generation) - ret = -1; - else if (buffersize > host->csr.rom->cache_head->size) - ret = -2; - else { - /* Just overwrite the generated ConfigROM image with new data, - * it can be regenerated later. */ - memcpy(host->csr.rom->cache_head->data, new_rom, buffersize); - host->csr.rom->cache_head->len = buffersize; - - if (host->driver->set_hw_config_rom) - host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data); - /* Increment the generation number to keep some sort of sync - * with the newer ConfigROM manipulation method. */ - host->csr.generation++; - if (host->csr.generation > 0xf || host->csr.generation < 2) - host->csr.generation = 2; - ret=0; - } - spin_unlock_irqrestore(&host->csr.lock, flags); - return ret; -} - - -/* Read topology / speed maps and configuration ROM */ -static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl) -{ - unsigned long flags; - int csraddr = addr - CSR_REGISTER_BASE; - const char *src; - - spin_lock_irqsave(&host->csr.lock, flags); - - if (csraddr < CSR_SPEED_MAP) { - src = ((char *)host->csr.topology_map) + csraddr - - CSR_TOPOLOGY_MAP; - } else { - src = ((char *)host->csr.speed_map) + csraddr - CSR_SPEED_MAP; - } - - memcpy(buffer, src, length); - spin_unlock_irqrestore(&host->csr.lock, flags); - return RCODE_COMPLETE; -} - - -#define out if (--length == 0) break - -static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf, - u64 addr, size_t length, u16 flags) -{ - int csraddr = addr - CSR_REGISTER_BASE; - int oldcycle; - quadlet_t ret; - - if ((csraddr | length) & 0x3) - return RCODE_TYPE_ERROR; - - length /= 4; - - switch (csraddr) { - case CSR_STATE_CLEAR: - *(buf++) = cpu_to_be32(host->csr.state); - out; - case CSR_STATE_SET: - *(buf++) = cpu_to_be32(host->csr.state); - out; - case CSR_NODE_IDS: - *(buf++) = cpu_to_be32(host->csr.node_ids); - out; - - case CSR_RESET_START: - return RCODE_TYPE_ERROR; - - /* address gap - handled by default below */ - - case CSR_SPLIT_TIMEOUT_HI: - *(buf++) = cpu_to_be32(host->csr.split_timeout_hi); - out; - case CSR_SPLIT_TIMEOUT_LO: - *(buf++) = cpu_to_be32(host->csr.split_timeout_lo); - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_CYCLE_TIME: - oldcycle = host->csr.cycle_time; - host->csr.cycle_time = - host->driver->devctl(host, GET_CYCLE_COUNTER, 0); - - if (oldcycle > host->csr.cycle_time) { - /* cycle time wrapped around */ - host->csr.bus_time += 1 << 7; - } - *(buf++) = cpu_to_be32(host->csr.cycle_time); - out; - case CSR_BUS_TIME: - oldcycle = host->csr.cycle_time; - host->csr.cycle_time = - host->driver->devctl(host, GET_CYCLE_COUNTER, 0); - - if (oldcycle > host->csr.cycle_time) { - /* cycle time wrapped around */ - host->csr.bus_time += (1 << 7); - } - *(buf++) = cpu_to_be32(host->csr.bus_time - | (host->csr.cycle_time >> 25)); - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUSY_TIMEOUT: - /* not yet implemented */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUS_MANAGER_ID: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 0, 0, 0); - else - ret = host->csr.bus_manager_id; - - *(buf++) = cpu_to_be32(ret); - out; - case CSR_BANDWIDTH_AVAILABLE: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 1, 0, 0); - else - ret = host->csr.bandwidth_available; - - *(buf++) = cpu_to_be32(ret); - out; - case CSR_CHANNELS_AVAILABLE_HI: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 2, 0, 0); - else - ret = host->csr.channels_available_hi; - - *(buf++) = cpu_to_be32(ret); - out; - case CSR_CHANNELS_AVAILABLE_LO: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 3, 0, 0); - else - ret = host->csr.channels_available_lo; - - *(buf++) = cpu_to_be32(ret); - out; - - case CSR_BROADCAST_CHANNEL: - *(buf++) = cpu_to_be32(host->csr.broadcast_channel); - out; - - /* address gap to end - fall through to default */ - default: - return RCODE_ADDRESS_ERROR; - } - - return RCODE_COMPLETE; -} - -static int write_regs(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags) -{ - int csraddr = addr - CSR_REGISTER_BASE; - - if ((csraddr | length) & 0x3) - return RCODE_TYPE_ERROR; - - length /= 4; - - switch (csraddr) { - case CSR_STATE_CLEAR: - /* FIXME FIXME FIXME */ - printk("doh, someone wants to mess with state clear\n"); - out; - case CSR_STATE_SET: - printk("doh, someone wants to mess with state set\n"); - out; - - case CSR_NODE_IDS: - host->csr.node_ids &= NODE_MASK << 16; - host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16); - host->node_id = host->csr.node_ids >> 16; - host->driver->devctl(host, SET_BUS_ID, host->node_id >> 6); - out; - - case CSR_RESET_START: - /* FIXME - perform command reset */ - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_SPLIT_TIMEOUT_HI: - host->csr.split_timeout_hi = - be32_to_cpu(*(data++)) & 0x00000007; - calculate_expire(&host->csr); - out; - case CSR_SPLIT_TIMEOUT_LO: - host->csr.split_timeout_lo = - be32_to_cpu(*(data++)) & 0xfff80000; - calculate_expire(&host->csr); - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_CYCLE_TIME: - /* should only be set by cycle start packet, automatically */ - host->csr.cycle_time = be32_to_cpu(*data); - host->driver->devctl(host, SET_CYCLE_COUNTER, - be32_to_cpu(*(data++))); - out; - case CSR_BUS_TIME: - host->csr.bus_time = be32_to_cpu(*(data++)) & 0xffffff80; - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUSY_TIMEOUT: - /* not yet implemented */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUS_MANAGER_ID: - case CSR_BANDWIDTH_AVAILABLE: - case CSR_CHANNELS_AVAILABLE_HI: - case CSR_CHANNELS_AVAILABLE_LO: - /* these are not writable, only lockable */ - return RCODE_TYPE_ERROR; - - case CSR_BROADCAST_CHANNEL: - /* only the valid bit can be written */ - host->csr.broadcast_channel = (host->csr.broadcast_channel & ~0x40000000) - | (be32_to_cpu(*data) & 0x40000000); - out; - - /* address gap to end - fall through */ - default: - return RCODE_ADDRESS_ERROR; - } - - return RCODE_COMPLETE; -} - -#undef out - - -static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl) -{ - int csraddr = addr - CSR_REGISTER_BASE; - unsigned long flags; - quadlet_t *regptr = NULL; - - if (csraddr & 0x3) - return RCODE_TYPE_ERROR; - - if (csraddr < CSR_BUS_MANAGER_ID || csraddr > CSR_CHANNELS_AVAILABLE_LO - || extcode != EXTCODE_COMPARE_SWAP) - goto unsupported_lockreq; - - data = be32_to_cpu(data); - arg = be32_to_cpu(arg); - - /* Is somebody releasing the broadcast_channel on us? */ - if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x1)) { - /* Note: this is may not be the right way to handle - * the problem, so we should look into the proper way - * eventually. */ - HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release " - "broadcast channel 31. Ignoring.", - NODE_BUS_ARGS(host, nodeid)); - - data &= ~0x1; /* keep broadcast channel allocated */ - } - - if (host->driver->hw_csr_reg) { - quadlet_t old; - - old = host->driver-> - hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, - data, arg); - - *store = cpu_to_be32(old); - return RCODE_COMPLETE; - } - - spin_lock_irqsave(&host->csr.lock, flags); - - switch (csraddr) { - case CSR_BUS_MANAGER_ID: - regptr = &host->csr.bus_manager_id; - *store = cpu_to_be32(*regptr); - if (*regptr == arg) - *regptr = data; - break; - - case CSR_BANDWIDTH_AVAILABLE: - { - quadlet_t bandwidth; - quadlet_t old; - quadlet_t new; - - regptr = &host->csr.bandwidth_available; - old = *regptr; - - /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */ - if (arg > 0x1fff) { - *store = cpu_to_be32(old); /* change nothing */ - break; - } - data &= 0x1fff; - if (arg >= data) { - /* allocate bandwidth */ - bandwidth = arg - data; - if (old >= bandwidth) { - new = old - bandwidth; - *store = cpu_to_be32(arg); - *regptr = new; - } else { - *store = cpu_to_be32(old); - } - } else { - /* deallocate bandwidth */ - bandwidth = data - arg; - if (old + bandwidth < 0x2000) { - new = old + bandwidth; - *store = cpu_to_be32(arg); - *regptr = new; - } else { - *store = cpu_to_be32(old); - } - } - break; - } - - case CSR_CHANNELS_AVAILABLE_HI: - { - /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */ - quadlet_t affected_channels = arg ^ data; - - regptr = &host->csr.channels_available_hi; - - if ((arg & affected_channels) == (*regptr & affected_channels)) { - *regptr ^= affected_channels; - *store = cpu_to_be32(arg); - } else { - *store = cpu_to_be32(*regptr); - } - - break; - } - - case CSR_CHANNELS_AVAILABLE_LO: - { - /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */ - quadlet_t affected_channels = arg ^ data; - - regptr = &host->csr.channels_available_lo; - - if ((arg & affected_channels) == (*regptr & affected_channels)) { - *regptr ^= affected_channels; - *store = cpu_to_be32(arg); - } else { - *store = cpu_to_be32(*regptr); - } - break; - } - } - - spin_unlock_irqrestore(&host->csr.lock, flags); - - return RCODE_COMPLETE; - - unsupported_lockreq: - switch (csraddr) { - case CSR_STATE_CLEAR: - case CSR_STATE_SET: - case CSR_RESET_START: - case CSR_NODE_IDS: - case CSR_SPLIT_TIMEOUT_HI: - case CSR_SPLIT_TIMEOUT_LO: - case CSR_CYCLE_TIME: - case CSR_BUS_TIME: - case CSR_BROADCAST_CHANNEL: - return RCODE_TYPE_ERROR; - - case CSR_BUSY_TIMEOUT: - /* not yet implemented - fall through */ - default: - return RCODE_ADDRESS_ERROR; - } -} - -static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl) -{ - int csraddr = addr - CSR_REGISTER_BASE; - unsigned long flags; - - data = be64_to_cpu(data); - arg = be64_to_cpu(arg); - - if (csraddr & 0x3) - return RCODE_TYPE_ERROR; - - if (csraddr != CSR_CHANNELS_AVAILABLE - || extcode != EXTCODE_COMPARE_SWAP) - goto unsupported_lock64req; - - /* Is somebody releasing the broadcast_channel on us? */ - if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x100000000ULL)) { - /* Note: this is may not be the right way to handle - * the problem, so we should look into the proper way - * eventually. */ - HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release " - "broadcast channel 31. Ignoring.", - NODE_BUS_ARGS(host, nodeid)); - - data &= ~0x100000000ULL; /* keep broadcast channel allocated */ - } - - if (host->driver->hw_csr_reg) { - quadlet_t data_hi, data_lo; - quadlet_t arg_hi, arg_lo; - quadlet_t old_hi, old_lo; - - data_hi = data >> 32; - data_lo = data & 0xFFFFFFFF; - arg_hi = arg >> 32; - arg_lo = arg & 0xFFFFFFFF; - - old_hi = host->driver->hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, - data_hi, arg_hi); - - old_lo = host->driver->hw_csr_reg(host, ((csraddr + 4) - CSR_BUS_MANAGER_ID) >> 2, - data_lo, arg_lo); - - *store = cpu_to_be64(((octlet_t)old_hi << 32) | old_lo); - } else { - octlet_t old; - octlet_t affected_channels = arg ^ data; - - spin_lock_irqsave(&host->csr.lock, flags); - - old = ((octlet_t)host->csr.channels_available_hi << 32) | host->csr.channels_available_lo; - - if ((arg & affected_channels) == (old & affected_channels)) { - host->csr.channels_available_hi ^= (affected_channels >> 32); - host->csr.channels_available_lo ^= (affected_channels & 0xffffffff); - *store = cpu_to_be64(arg); - } else { - *store = cpu_to_be64(old); - } - - spin_unlock_irqrestore(&host->csr.lock, flags); - } - - /* Is somebody erroneously releasing the broadcast_channel on us? */ - if (host->csr.channels_available_hi & 0x1) - host->csr.channels_available_hi &= ~0x1; - - return RCODE_COMPLETE; - - unsupported_lock64req: - switch (csraddr) { - case CSR_STATE_CLEAR: - case CSR_STATE_SET: - case CSR_RESET_START: - case CSR_NODE_IDS: - case CSR_SPLIT_TIMEOUT_HI: - case CSR_SPLIT_TIMEOUT_LO: - case CSR_CYCLE_TIME: - case CSR_BUS_TIME: - case CSR_BUS_MANAGER_ID: - case CSR_BROADCAST_CHANNEL: - case CSR_BUSY_TIMEOUT: - case CSR_BANDWIDTH_AVAILABLE: - return RCODE_TYPE_ERROR; - - default: - return RCODE_ADDRESS_ERROR; - } -} - -static int write_fcp(struct hpsb_host *host, int nodeid, int dest, - quadlet_t *data, u64 addr, size_t length, u16 flags) -{ - int csraddr = addr - CSR_REGISTER_BASE; - - if (length > 512) - return RCODE_TYPE_ERROR; - - switch (csraddr) { - case CSR_FCP_COMMAND: - highlevel_fcp_request(host, nodeid, 0, (u8 *)data, length); - break; - case CSR_FCP_RESPONSE: - highlevel_fcp_request(host, nodeid, 1, (u8 *)data, length); - break; - default: - return RCODE_TYPE_ERROR; - } - - return RCODE_COMPLETE; -} - -static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl) -{ - u32 offset = addr - CSR1212_REGISTER_SPACE_BASE; - - if (csr1212_read(host->csr.rom, offset, buffer, length) == CSR1212_SUCCESS) - return RCODE_COMPLETE; - else - return RCODE_ADDRESS_ERROR; -} - -static u64 allocate_addr_range(u64 size, u32 alignment, void *__host) -{ - struct hpsb_host *host = (struct hpsb_host*)__host; - - return hpsb_allocate_and_register_addrspace(&csr_highlevel, - host, - &config_rom_ops, - size, alignment, - CSR1212_UNITS_SPACE_BASE, - CSR1212_UNITS_SPACE_END); -} - -static void release_addr_range(u64 addr, void *__host) -{ - struct hpsb_host *host = (struct hpsb_host*)__host; - hpsb_unregister_addrspace(&csr_highlevel, host, addr); -} - - -int init_csr(void) -{ - node_cap = csr1212_new_immediate(CSR1212_KV_ID_NODE_CAPABILITIES, 0x0083c0); - if (!node_cap) { - HPSB_ERR("Failed to allocate memory for Node Capabilties ConfigROM entry!"); - return -ENOMEM; - } - - hpsb_register_highlevel(&csr_highlevel); - - return 0; -} - -void cleanup_csr(void) -{ - if (node_cap) - csr1212_release_keyval(node_cap); - hpsb_unregister_highlevel(&csr_highlevel); -} diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h deleted file mode 100644 index 90fb3f2192c..00000000000 --- a/drivers/ieee1394/csr.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef _IEEE1394_CSR_H -#define _IEEE1394_CSR_H - -#include <linux/spinlock_types.h> - -#include "csr1212.h" -#include "ieee1394_types.h" - -#define CSR_REGISTER_BASE 0xfffff0000000ULL - -/* register offsets relative to CSR_REGISTER_BASE */ -#define CSR_STATE_CLEAR 0x0 -#define CSR_STATE_SET 0x4 -#define CSR_NODE_IDS 0x8 -#define CSR_RESET_START 0xc -#define CSR_SPLIT_TIMEOUT_HI 0x18 -#define CSR_SPLIT_TIMEOUT_LO 0x1c -#define CSR_CYCLE_TIME 0x200 -#define CSR_BUS_TIME 0x204 -#define CSR_BUSY_TIMEOUT 0x210 -#define CSR_BUS_MANAGER_ID 0x21c -#define CSR_BANDWIDTH_AVAILABLE 0x220 -#define CSR_CHANNELS_AVAILABLE 0x224 -#define CSR_CHANNELS_AVAILABLE_HI 0x224 -#define CSR_CHANNELS_AVAILABLE_LO 0x228 -#define CSR_BROADCAST_CHANNEL 0x234 -#define CSR_CONFIG_ROM 0x400 -#define CSR_CONFIG_ROM_END 0x800 -#define CSR_FCP_COMMAND 0xB00 -#define CSR_FCP_RESPONSE 0xD00 -#define CSR_FCP_END 0xF00 -#define CSR_TOPOLOGY_MAP 0x1000 -#define CSR_TOPOLOGY_MAP_END 0x1400 -#define CSR_SPEED_MAP 0x2000 -#define CSR_SPEED_MAP_END 0x3000 - -/* IEEE 1394 bus specific Configuration ROM Key IDs */ -#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30) - -/* IEEE 1394 Bus Information Block specifics */ -#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t)) - -#define CSR_IRMC_SHIFT 31 -#define CSR_CMC_SHIFT 30 -#define CSR_ISC_SHIFT 29 -#define CSR_BMC_SHIFT 28 -#define CSR_PMC_SHIFT 27 -#define CSR_CYC_CLK_ACC_SHIFT 16 -#define CSR_MAX_REC_SHIFT 12 -#define CSR_MAX_ROM_SHIFT 8 -#define CSR_GENERATION_SHIFT 4 - -static inline void csr_set_bus_info_generation(struct csr1212_csr *csr, u8 gen) -{ - csr->bus_info_data[2] &= ~cpu_to_be32(0xf << CSR_GENERATION_SHIFT); - csr->bus_info_data[2] |= cpu_to_be32((u32)gen << CSR_GENERATION_SHIFT); -} - -struct csr_control { - spinlock_t lock; - - quadlet_t state; - quadlet_t node_ids; - quadlet_t split_timeout_hi, split_timeout_lo; - unsigned long expire; /* Calculated from split_timeout */ - quadlet_t cycle_time; - quadlet_t bus_time; - quadlet_t bus_manager_id; - quadlet_t bandwidth_available; - quadlet_t channels_available_hi, channels_available_lo; - quadlet_t broadcast_channel; - - /* Bus Info */ - quadlet_t guid_hi, guid_lo; - u8 cyc_clk_acc; - u8 max_rec; - u8 max_rom; - u8 generation; /* Only use values between 0x2 and 0xf */ - u8 lnk_spd; - - unsigned long gen_timestamp[16]; - - struct csr1212_csr *rom; - - quadlet_t topology_map[256]; - quadlet_t speed_map[1024]; -}; - -extern struct csr1212_bus_ops csr_bus_ops; - -int init_csr(void); -void cleanup_csr(void); - -/* hpsb_update_config_rom() is deprecated */ -struct hpsb_host; -int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, - size_t size, unsigned char rom_version); - -#endif /* _IEEE1394_CSR_H */ diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c deleted file mode 100644 index e76cac64c53..00000000000 --- a/drivers/ieee1394/csr1212.c +++ /dev/null @@ -1,1467 +0,0 @@ -/* - * csr1212.c -- IEEE 1212 Control and Status Register support for Linux - * - * Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za> - * Steve Kinneberg <kinnebergsteve@acmsystems.com> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -/* TODO List: - * - Verify interface consistency: i.e., public functions that take a size - * parameter expect size to be in bytes. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/kmemcheck.h> -#include <linux/string.h> -#include <asm/bug.h> -#include <asm/byteorder.h> - -#include "csr1212.h" - - -/* Permitted key type for each key id */ -#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE) -#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) -#define __D (1 << CSR1212_KV_TYPE_DIRECTORY) -#define __L (1 << CSR1212_KV_TYPE_LEAF) -static const u8 csr1212_key_id_type_map[0x30] = { - __C, /* used by Apple iSight */ - __D | __L, /* Descriptor */ - __I | __D | __L, /* Bus_Dependent_Info */ - __I | __D | __L, /* Vendor */ - __I, /* Hardware_Version */ - 0, 0, /* Reserved */ - __D | __L | __I, /* Module */ - __I, 0, 0, 0, /* used by Apple iSight, Reserved */ - __I, /* Node_Capabilities */ - __L, /* EUI_64 */ - 0, 0, 0, /* Reserved */ - __D, /* Unit */ - __I, /* Specifier_ID */ - __I, /* Version */ - __I | __C | __D | __L, /* Dependent_Info */ - __L, /* Unit_Location */ - 0, /* Reserved */ - __I, /* Model */ - __D, /* Instance */ - __L, /* Keyword */ - __D, /* Feature */ - __L, /* Extended_ROM */ - __I, /* Extended_Key_Specifier_ID */ - __I, /* Extended_Key */ - __I | __C | __D | __L, /* Extended_Data */ - __L, /* Modifiable_Descriptor */ - __I, /* Directory_ID */ - __I, /* Revision */ -}; -#undef __I -#undef __C -#undef __D -#undef __L - - -#define quads_to_bytes(_q) ((_q) * sizeof(u32)) -#define bytes_to_quads(_b) DIV_ROUND_UP(_b, sizeof(u32)) - -static void free_keyval(struct csr1212_keyval *kv) -{ - if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && - (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) - CSR1212_FREE(kv->value.leaf.data); - - CSR1212_FREE(kv); -} - -static u16 csr1212_crc16(const u32 *buffer, size_t length) -{ - int shift; - u32 data; - u16 sum, crc = 0; - - for (; length; length--) { - data = be32_to_cpu(*buffer); - buffer++; - for (shift = 28; shift >= 0; shift -= 4 ) { - sum = ((crc >> 12) ^ (data >> shift)) & 0xf; - crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); - } - crc &= 0xffff; - } - - return cpu_to_be16(crc); -} - -/* Microsoft computes the CRC with the bytes in reverse order. */ -static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) -{ - int shift; - u32 data; - u16 sum, crc = 0; - - for (; length; length--) { - data = le32_to_cpu(*buffer); - buffer++; - for (shift = 28; shift >= 0; shift -= 4 ) { - sum = ((crc >> 12) ^ (data >> shift)) & 0xf; - crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); - } - crc &= 0xffff; - } - - return cpu_to_be16(crc); -} - -static struct csr1212_dentry * -csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) -{ - struct csr1212_dentry *pos; - - for (pos = dir->value.directory.dentries_head; - pos != NULL; pos = pos->next) - if (pos->kv == kv) - return pos; - return NULL; -} - -static struct csr1212_keyval * -csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset) -{ - struct csr1212_keyval *kv; - - for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) - if (kv->offset == offset) - return kv; - return NULL; -} - - -/* Creation Routines */ - -struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, - size_t bus_info_size, void *private) -{ - struct csr1212_csr *csr; - - csr = CSR1212_MALLOC(sizeof(*csr)); - if (!csr) - return NULL; - - csr->cache_head = - csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET, - CSR1212_CONFIG_ROM_SPACE_SIZE); - if (!csr->cache_head) { - CSR1212_FREE(csr); - return NULL; - } - - /* The keyval key id is not used for the root node, but a valid key id - * that can be used for a directory needs to be passed to - * csr1212_new_directory(). */ - csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR); - if (!csr->root_kv) { - CSR1212_FREE(csr->cache_head); - CSR1212_FREE(csr); - return NULL; - } - - csr->bus_info_data = csr->cache_head->data; - csr->bus_info_len = bus_info_size; - csr->crc_len = bus_info_size; - csr->ops = ops; - csr->private = private; - csr->cache_tail = csr->cache_head; - - return csr; -} - -void csr1212_init_local_csr(struct csr1212_csr *csr, - const u32 *bus_info_data, int max_rom) -{ - static const int mr_map[] = { 4, 64, 1024, 0 }; - - BUG_ON(max_rom & ~0x3); - csr->max_rom = mr_map[max_rom]; - memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); -} - -static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) -{ - struct csr1212_keyval *kv; - - if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0)) - return NULL; - - kv = CSR1212_MALLOC(sizeof(*kv)); - if (!kv) - return NULL; - - atomic_set(&kv->refcnt, 1); - kv->key.type = type; - kv->key.id = key; - kv->associate = NULL; - kv->next = NULL; - kv->prev = NULL; - kv->offset = 0; - kv->valid = 0; - return kv; -} - -struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); - if (!kv) - return NULL; - - kv->value.immediate = value; - kv->valid = 1; - return kv; -} - -static struct csr1212_keyval * -csr1212_new_leaf(u8 key, const void *data, size_t data_len) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); - if (!kv) - return NULL; - - if (data_len > 0) { - kv->value.leaf.data = CSR1212_MALLOC(data_len); - if (!kv->value.leaf.data) { - CSR1212_FREE(kv); - return NULL; - } - - if (data) - memcpy(kv->value.leaf.data, data, data_len); - } else { - kv->value.leaf.data = NULL; - } - - kv->value.leaf.len = bytes_to_quads(data_len); - kv->offset = 0; - kv->valid = 1; - - return kv; -} - -static struct csr1212_keyval * -csr1212_new_csr_offset(u8 key, u32 csr_offset) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); - if (!kv) - return NULL; - - kv->value.csr_offset = csr_offset; - - kv->offset = 0; - kv->valid = 1; - return kv; -} - -struct csr1212_keyval *csr1212_new_directory(u8 key) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); - if (!kv) - return NULL; - - kv->value.directory.len = 0; - kv->offset = 0; - kv->value.directory.dentries_head = NULL; - kv->value.directory.dentries_tail = NULL; - kv->valid = 1; - return kv; -} - -void csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate) -{ - BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR || - (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && - associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && - associate->key.id < 0x30) || - (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) || - (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) || - (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) || - (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)); - - if (kv->associate) - csr1212_release_keyval(kv->associate); - - csr1212_keep_keyval(associate); - kv->associate = associate; -} - -static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv, - bool keep_keyval) -{ - struct csr1212_dentry *dentry; - - BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY); - - dentry = CSR1212_MALLOC(sizeof(*dentry)); - if (!dentry) - return -ENOMEM; - - if (keep_keyval) - csr1212_keep_keyval(kv); - dentry->kv = kv; - - dentry->next = NULL; - dentry->prev = dir->value.directory.dentries_tail; - - if (!dir->value.directory.dentries_head) - dir->value.directory.dentries_head = dentry; - - if (dir->value.directory.dentries_tail) - dir->value.directory.dentries_tail->next = dentry; - dir->value.directory.dentries_tail = dentry; - - return CSR1212_SUCCESS; -} - -int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) -{ - return __csr1212_attach_keyval_to_directory(dir, kv, true); -} - -#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ - (&((kv)->value.leaf.data[1])) - -#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ - ((kv)->value.leaf.data[0] = \ - cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ - ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) -#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ - ((kv)->value.leaf.data[0] = \ - cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ - CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ - ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) - -static struct csr1212_keyval * -csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id, - const void *data, size_t data_len) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL, - data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD); - if (!kv) - return NULL; - - kmemcheck_annotate_variable(kv->value.leaf.data[0]); - CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); - CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); - - if (data) - memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); - - return kv; -} - -/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */ -static int csr1212_check_minimal_ascii(const char *s) -{ - static const char minimal_ascii_table[] = { - /* 1 2 4 8 16 32 64 128 */ - 128, /* --, --, --, --, --, --, --, 07, */ - 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */ - 0, /* --, --, --, --, --, --, --, --, */ - 0, /* --, --, --, --, --, --, --, --, */ - 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */ - 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */ - 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */ - 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */ - 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */ - 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */ - 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */ - 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */ - 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */ - 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */ - 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */ - 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */ - }; - int i, j; - - for (; *s; s++) { - i = *s >> 3; /* i = *s / 8; */ - j = 1 << (*s & 3); /* j = 1 << (*s % 8); */ - - if (i >= ARRAY_SIZE(minimal_ascii_table) || - !(minimal_ascii_table[i] & j)) - return -EINVAL; - } - return 0; -} - -/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */ -struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) -{ - struct csr1212_keyval *kv; - u32 *text; - size_t str_len, quads; - - if (!s || !*s || csr1212_check_minimal_ascii(s)) - return NULL; - - str_len = strlen(s); - quads = bytes_to_quads(str_len); - kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) + - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); - if (!kv) - return NULL; - - kv->value.leaf.data[1] = 0; /* width, character_set, language */ - text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); - text[quads - 1] = 0; /* padding */ - memcpy(text, s, str_len); - - return kv; -} - - -/* Destruction Routines */ - -void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) -{ - struct csr1212_dentry *dentry; - - if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) - return; - - dentry = csr1212_find_keyval(dir, kv); - - if (!dentry) - return; - - if (dentry->prev) - dentry->prev->next = dentry->next; - if (dentry->next) - dentry->next->prev = dentry->prev; - if (dir->value.directory.dentries_head == dentry) - dir->value.directory.dentries_head = dentry->next; - if (dir->value.directory.dentries_tail == dentry) - dir->value.directory.dentries_tail = dentry->prev; - - CSR1212_FREE(dentry); - - csr1212_release_keyval(kv); -} - -/* This function is used to free the memory taken by a keyval. If the given - * keyval is a directory type, then any keyvals contained in that directory - * will be destroyed as well if noone holds a reference on them. By means of - * list manipulation, this routine will descend a directory structure in a - * non-recursive manner. */ -void csr1212_release_keyval(struct csr1212_keyval *kv) -{ - struct csr1212_keyval *k, *a; - struct csr1212_dentry dentry; - struct csr1212_dentry *head, *tail; - - if (!atomic_dec_and_test(&kv->refcnt)) - return; - - dentry.kv = kv; - dentry.next = NULL; - dentry.prev = NULL; - - head = &dentry; - tail = head; - - while (head) { - k = head->kv; - - while (k) { - /* must not dec_and_test kv->refcnt again */ - if (k != kv && !atomic_dec_and_test(&k->refcnt)) - break; - - a = k->associate; - - if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { - /* If the current entry is a directory, move all - * the entries to the destruction list. */ - if (k->value.directory.dentries_head) { - tail->next = - k->value.directory.dentries_head; - k->value.directory.dentries_head->prev = - tail; - tail = k->value.directory.dentries_tail; - } - } - free_keyval(k); - k = a; - } - - head = head->next; - if (head) { - if (head->prev && head->prev != &dentry) - CSR1212_FREE(head->prev); - head->prev = NULL; - } else if (tail != &dentry) { - CSR1212_FREE(tail); - } - } -} - -void csr1212_destroy_csr(struct csr1212_csr *csr) -{ - struct csr1212_csr_rom_cache *c, *oc; - struct csr1212_cache_region *cr, *ocr; - - csr1212_release_keyval(csr->root_kv); - - c = csr->cache_head; - while (c) { - oc = c; - cr = c->filled_head; - while (cr) { - ocr = cr; - cr = cr->next; - CSR1212_FREE(ocr); - } - c = c->next; - CSR1212_FREE(oc); - } - - CSR1212_FREE(csr); -} - - -/* CSR Image Creation */ - -static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) -{ - struct csr1212_csr_rom_cache *cache; - u64 csr_addr; - - BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range || - !csr->ops->release_addr || csr->max_rom < 1); - - /* ROM size must be a multiple of csr->max_rom */ - romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); - - csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, - csr->private); - if (csr_addr == CSR1212_INVALID_ADDR_SPACE) - return -ENOMEM; - - if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { - /* Invalid address returned from allocate_addr_range(). */ - csr->ops->release_addr(csr_addr, csr->private); - return -ENOMEM; - } - - cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, - romsize); - if (!cache) { - csr->ops->release_addr(csr_addr, csr->private); - return -ENOMEM; - } - - cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, - CSR1212_KV_ID_EXTENDED_ROM); - if (!cache->ext_rom) { - csr->ops->release_addr(csr_addr, csr->private); - CSR1212_FREE(cache); - return -ENOMEM; - } - - if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != - CSR1212_SUCCESS) { - csr1212_release_keyval(cache->ext_rom); - csr->ops->release_addr(csr_addr, csr->private); - CSR1212_FREE(cache); - return -ENOMEM; - } - cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; - cache->ext_rom->value.leaf.len = -1; - cache->ext_rom->value.leaf.data = cache->data; - - /* Add cache to tail of cache list */ - cache->prev = csr->cache_tail; - csr->cache_tail->next = cache; - csr->cache_tail = cache; - return CSR1212_SUCCESS; -} - -static void csr1212_remove_cache(struct csr1212_csr *csr, - struct csr1212_csr_rom_cache *cache) -{ - if (csr->cache_head == cache) - csr->cache_head = cache->next; - if (csr->cache_tail == cache) - csr->cache_tail = cache->prev; - - if (cache->prev) - cache->prev->next = cache->next; - if (cache->next) - cache->next->prev = cache->prev; - - if (cache->ext_rom) { - csr1212_detach_keyval_from_directory(csr->root_kv, - cache->ext_rom); - csr1212_release_keyval(cache->ext_rom); - } - - CSR1212_FREE(cache); -} - -static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, - struct csr1212_keyval **layout_tail) -{ - struct csr1212_dentry *dentry; - struct csr1212_keyval *dkv; - struct csr1212_keyval *last_extkey_spec = NULL; - struct csr1212_keyval *last_extkey = NULL; - int num_entries = 0; - - for (dentry = dir->value.directory.dentries_head; dentry; - dentry = dentry->next) { - for (dkv = dentry->kv; dkv; dkv = dkv->associate) { - /* Special Case: Extended Key Specifier_ID */ - if (dkv->key.id == - CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) - last_extkey_spec = dkv; - else if (dkv->value.immediate != - last_extkey_spec->value.immediate) - last_extkey_spec = dkv; - else - continue; - /* Special Case: Extended Key */ - } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) - last_extkey = dkv; - else if (dkv->value.immediate != - last_extkey->value.immediate) - last_extkey = dkv; - else - continue; - } - - num_entries += 1; - - switch (dkv->key.type) { - default: - case CSR1212_KV_TYPE_IMMEDIATE: - case CSR1212_KV_TYPE_CSR_OFFSET: - break; - case CSR1212_KV_TYPE_LEAF: - case CSR1212_KV_TYPE_DIRECTORY: - /* Remove from list */ - if (dkv->prev && (dkv->prev->next == dkv)) - dkv->prev->next = dkv->next; - if (dkv->next && (dkv->next->prev == dkv)) - dkv->next->prev = dkv->prev; - //if (dkv == *layout_tail) - // *layout_tail = dkv->prev; - - /* Special case: Extended ROM leafs */ - if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { - dkv->value.leaf.len = -1; - /* Don't add Extended ROM leafs in the - * layout list, they are handled - * differently. */ - break; - } - - /* Add to tail of list */ - dkv->next = NULL; - dkv->prev = *layout_tail; - (*layout_tail)->next = dkv; - *layout_tail = dkv; - break; - } - } - } - return num_entries; -} - -static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) -{ - struct csr1212_keyval *ltail = kv; - size_t agg_size = 0; - - while (kv) { - switch (kv->key.type) { - case CSR1212_KV_TYPE_LEAF: - /* Add 1 quadlet for crc/len field */ - agg_size += kv->value.leaf.len + 1; - break; - - case CSR1212_KV_TYPE_DIRECTORY: - kv->value.directory.len = - csr1212_generate_layout_subdir(kv, <ail); - /* Add 1 quadlet for crc/len field */ - agg_size += kv->value.directory.len + 1; - break; - } - kv = kv->next; - } - return quads_to_bytes(agg_size); -} - -static struct csr1212_keyval * -csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, int start_pos) -{ - struct csr1212_keyval *kv = start_kv; - struct csr1212_keyval *okv = start_kv; - int pos = start_pos; - int kv_len = 0, okv_len = 0; - - cache->layout_head = kv; - - while (kv && pos < cache->size) { - /* Special case: Extended ROM leafs */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - kv->offset = cache->offset + pos; - - switch (kv->key.type) { - case CSR1212_KV_TYPE_LEAF: - kv_len = kv->value.leaf.len; - break; - - case CSR1212_KV_TYPE_DIRECTORY: - kv_len = kv->value.directory.len; - break; - - default: - /* Should never get here */ - WARN_ON(1); - break; - } - - pos += quads_to_bytes(kv_len + 1); - - if (pos <= cache->size) { - okv = kv; - okv_len = kv_len; - kv = kv->next; - } - } - - cache->layout_tail = okv; - cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1); - - return kv; -} - -#define CSR1212_KV_KEY_SHIFT 24 -#define CSR1212_KV_KEY_TYPE_SHIFT 6 -#define CSR1212_KV_KEY_ID_MASK 0x3f -#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */ - -static void -csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer) -{ - struct csr1212_dentry *dentry; - struct csr1212_keyval *last_extkey_spec = NULL; - struct csr1212_keyval *last_extkey = NULL; - int index = 0; - - for (dentry = dir->value.directory.dentries_head; - dentry; - dentry = dentry->next) { - struct csr1212_keyval *a; - - for (a = dentry->kv; a; a = a->associate) { - u32 value = 0; - - /* Special Case: Extended Key Specifier_ID */ - if (a->key.id == - CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) - last_extkey_spec = a; - else if (a->value.immediate != - last_extkey_spec->value.immediate) - last_extkey_spec = a; - else - continue; - - /* Special Case: Extended Key */ - } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) - last_extkey = a; - else if (a->value.immediate != - last_extkey->value.immediate) - last_extkey = a; - else - continue; - } - - switch (a->key.type) { - case CSR1212_KV_TYPE_IMMEDIATE: - value = a->value.immediate; - break; - case CSR1212_KV_TYPE_CSR_OFFSET: - value = a->value.csr_offset; - break; - case CSR1212_KV_TYPE_LEAF: - value = a->offset; - value -= dir->offset + quads_to_bytes(1+index); - value = bytes_to_quads(value); - break; - case CSR1212_KV_TYPE_DIRECTORY: - value = a->offset; - value -= dir->offset + quads_to_bytes(1+index); - value = bytes_to_quads(value); - break; - default: - /* Should never get here */ - WARN_ON(1); - break; - } - - value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << - CSR1212_KV_KEY_SHIFT; - value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << - (CSR1212_KV_KEY_SHIFT + - CSR1212_KV_KEY_TYPE_SHIFT); - data_buffer[index] = cpu_to_be32(value); - index++; - } - } -} - -struct csr1212_keyval_img { - u16 length; - u16 crc; - - /* Must be last */ - u32 data[0]; /* older gcc can't handle [] which is standard */ -}; - -static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) -{ - struct csr1212_keyval *kv, *nkv; - struct csr1212_keyval_img *kvi; - - for (kv = cache->layout_head; - kv != cache->layout_tail->next; - kv = nkv) { - kvi = (struct csr1212_keyval_img *)(cache->data + - bytes_to_quads(kv->offset - cache->offset)); - switch (kv->key.type) { - default: - case CSR1212_KV_TYPE_IMMEDIATE: - case CSR1212_KV_TYPE_CSR_OFFSET: - /* Should never get here */ - WARN_ON(1); - break; - - case CSR1212_KV_TYPE_LEAF: - /* Don't copy over Extended ROM areas, they are - * already filled out! */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - memcpy(kvi->data, kv->value.leaf.data, - quads_to_bytes(kv->value.leaf.len)); - - kvi->length = cpu_to_be16(kv->value.leaf.len); - kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); - break; - - case CSR1212_KV_TYPE_DIRECTORY: - csr1212_generate_tree_subdir(kv, kvi->data); - - kvi->length = cpu_to_be16(kv->value.directory.len); - kvi->crc = csr1212_crc16(kvi->data, - kv->value.directory.len); - break; - } - - nkv = kv->next; - if (kv->prev) - kv->prev->next = NULL; - if (kv->next) - kv->next->prev = NULL; - kv->prev = NULL; - kv->next = NULL; - } -} - -/* This size is arbitrarily chosen. - * The struct overhead is subtracted for more economic allocations. */ -#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache)) - -int csr1212_generate_csr_image(struct csr1212_csr *csr) -{ - struct csr1212_bus_info_block_img *bi; - struct csr1212_csr_rom_cache *cache; - struct csr1212_keyval *kv; - size_t agg_size; - int ret; - int init_offset; - - BUG_ON(!csr); - - cache = csr->cache_head; - - bi = (struct csr1212_bus_info_block_img*)cache->data; - - bi->length = bytes_to_quads(csr->bus_info_len) - 1; - bi->crc_length = bi->length; - bi->crc = csr1212_crc16(bi->data, bi->crc_length); - - csr->root_kv->next = NULL; - csr->root_kv->prev = NULL; - - agg_size = csr1212_generate_layout_order(csr->root_kv); - - init_offset = csr->bus_info_len; - - for (kv = csr->root_kv, cache = csr->cache_head; - kv; - cache = cache->next) { - if (!cache) { - /* Estimate approximate number of additional cache - * regions needed (it assumes that the cache holding - * the first 1K Config ROM space always exists). */ - int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - - (2 * sizeof(u32))) + 1; - - /* Add additional cache regions, extras will be - * removed later */ - for (; est_c; est_c--) { - ret = csr1212_append_new_cache(csr, - CSR1212_EXTENDED_ROM_SIZE); - if (ret != CSR1212_SUCCESS) - return ret; - } - /* Need to re-layout for additional cache regions */ - agg_size = csr1212_generate_layout_order(csr->root_kv); - kv = csr->root_kv; - cache = csr->cache_head; - init_offset = csr->bus_info_len; - } - kv = csr1212_generate_positions(cache, kv, init_offset); - agg_size -= cache->len; - init_offset = sizeof(u32); - } - - /* Remove unused, excess cache regions */ - while (cache) { - struct csr1212_csr_rom_cache *oc = cache; - - cache = cache->next; - csr1212_remove_cache(csr, oc); - } - - /* Go through the list backward so that when done, the correct CRC - * will be calculated for the Extended ROM areas. */ - for (cache = csr->cache_tail; cache; cache = cache->prev) { - /* Only Extended ROM caches should have this set. */ - if (cache->ext_rom) { - int leaf_size; - - /* Make sure the Extended ROM leaf is a multiple of - * max_rom in size. */ - BUG_ON(csr->max_rom < 1); - leaf_size = (cache->len + (csr->max_rom - 1)) & - ~(csr->max_rom - 1); - - /* Zero out the unused ROM region */ - memset(cache->data + bytes_to_quads(cache->len), 0x00, - leaf_size - cache->len); - - /* Subtract leaf header */ - leaf_size -= sizeof(u32); - - /* Update the Extended ROM leaf length */ - cache->ext_rom->value.leaf.len = - bytes_to_quads(leaf_size); - } else { - /* Zero out the unused ROM region */ - memset(cache->data + bytes_to_quads(cache->len), 0x00, - cache->size - cache->len); - } - - /* Copy the data into the cache buffer */ - csr1212_fill_cache(cache); - - if (cache != csr->cache_head) { - /* Set the length and CRC of the extended ROM. */ - struct csr1212_keyval_img *kvi = - (struct csr1212_keyval_img*)cache->data; - u16 len = bytes_to_quads(cache->len) - 1; - - kvi->length = cpu_to_be16(len); - kvi->crc = csr1212_crc16(kvi->data, len); - } - } - - return CSR1212_SUCCESS; -} - -int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len) -{ - struct csr1212_csr_rom_cache *cache; - - for (cache = csr->cache_head; cache; cache = cache->next) - if (offset >= cache->offset && - (offset + len) <= (cache->offset + cache->size)) { - memcpy(buffer, &cache->data[ - bytes_to_quads(offset - cache->offset)], - len); - return CSR1212_SUCCESS; - } - - return -ENOENT; -} - -/* - * Apparently there are many different wrong implementations of the CRC - * algorithm. We don't fail, we just warn... approximately once per GUID. - */ -static void -csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid) -{ - static u64 last_bad_eui64; - u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]); - - if (csr1212_crc16(buffer, length) == crc || - csr1212_msft_crc16(buffer, length) == crc || - eui64 == last_bad_eui64) - return; - - printk(KERN_DEBUG "ieee1394: config ROM CRC error\n"); - last_bad_eui64 = eui64; -} - -/* Parse a chunk of data as a Config ROM */ - -static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) -{ - struct csr1212_bus_info_block_img *bi; - struct csr1212_cache_region *cr; - int i; - int ret; - - for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) { - ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); - if (ret != CSR1212_SUCCESS) - return ret; - - /* check ROM header's info_length */ - if (i == 0 && - be32_to_cpu(csr->cache_head->data[0]) >> 24 != - bytes_to_quads(csr->bus_info_len) - 1) - return -EINVAL; - } - - bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; - csr->crc_len = quads_to_bytes(bi->crc_length); - - /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that - * is not always the case, so read the rest of the crc area 1 quadlet at - * a time. */ - for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) { - ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); - if (ret != CSR1212_SUCCESS) - return ret; - } - - csr1212_check_crc(bi->data, bi->crc_length, bi->crc, - &csr->bus_info_data[3]); - - cr = CSR1212_MALLOC(sizeof(*cr)); - if (!cr) - return -ENOMEM; - - cr->next = NULL; - cr->prev = NULL; - cr->offset_start = 0; - cr->offset_end = csr->crc_len + 4; - - csr->cache_head->filled_head = cr; - csr->cache_head->filled_tail = cr; - - return CSR1212_SUCCESS; -} - -#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT) -#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT) -#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK) -#define CSR1212_KV_VAL_MASK 0xffffff -#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK) - -static int -csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) -{ - int ret = CSR1212_SUCCESS; - struct csr1212_keyval *k = NULL; - u32 offset; - bool keep_keyval = true; - - switch (CSR1212_KV_KEY_TYPE(ki)) { - case CSR1212_KV_TYPE_IMMEDIATE: - k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), - CSR1212_KV_VAL(ki)); - if (!k) { - ret = -ENOMEM; - goto out; - } - /* Don't keep local reference when parsing. */ - keep_keyval = false; - break; - - case CSR1212_KV_TYPE_CSR_OFFSET: - k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), - CSR1212_KV_VAL(ki)); - if (!k) { - ret = -ENOMEM; - goto out; - } - /* Don't keep local reference when parsing. */ - keep_keyval = false; - break; - - default: - /* Compute the offset from 0xffff f000 0000. */ - offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos; - if (offset == kv_pos) { - /* Uh-oh. Can't have a relative offset of 0 for Leaves - * or Directories. The Config ROM image is most likely - * messed up, so we'll just abort here. */ - ret = -EIO; - goto out; - } - - k = csr1212_find_keyval_offset(dir, offset); - - if (k) - break; /* Found it. */ - - if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) - k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); - else - k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); - - if (!k) { - ret = -ENOMEM; - goto out; - } - /* Don't keep local reference when parsing. */ - keep_keyval = false; - /* Contents not read yet so it's not valid. */ - k->valid = 0; - k->offset = offset; - - k->prev = dir; - k->next = dir->next; - dir->next->prev = k; - dir->next = k; - } - ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval); -out: - if (ret != CSR1212_SUCCESS && k != NULL) - free_keyval(k); - return ret; -} - -int csr1212_parse_keyval(struct csr1212_keyval *kv, - struct csr1212_csr_rom_cache *cache) -{ - struct csr1212_keyval_img *kvi; - int i; - int ret = CSR1212_SUCCESS; - int kvi_len; - - kvi = (struct csr1212_keyval_img*) - &cache->data[bytes_to_quads(kv->offset - cache->offset)]; - kvi_len = be16_to_cpu(kvi->length); - - /* GUID is wrong in here in case of extended ROM. We don't care. */ - csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]); - - switch (kv->key.type) { - case CSR1212_KV_TYPE_DIRECTORY: - for (i = 0; i < kvi_len; i++) { - u32 ki = kvi->data[i]; - - /* Some devices put null entries in their unit - * directories. If we come across such an entry, - * then skip it. */ - if (ki == 0x0) - continue; - ret = csr1212_parse_dir_entry(kv, ki, - kv->offset + quads_to_bytes(i + 1)); - } - kv->value.directory.len = kvi_len; - break; - - case CSR1212_KV_TYPE_LEAF: - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { - size_t size = quads_to_bytes(kvi_len); - - kv->value.leaf.data = CSR1212_MALLOC(size); - if (!kv->value.leaf.data) { - ret = -ENOMEM; - goto out; - } - - kv->value.leaf.len = kvi_len; - memcpy(kv->value.leaf.data, kvi->data, size); - } - break; - } - - kv->valid = 1; -out: - return ret; -} - -static int -csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) -{ - struct csr1212_cache_region *cr, *ncr, *newcr = NULL; - struct csr1212_keyval_img *kvi = NULL; - struct csr1212_csr_rom_cache *cache; - int cache_index; - u64 addr; - u32 *cache_ptr; - u16 kv_len = 0; - - BUG_ON(!csr || !kv || csr->max_rom < 1); - - /* First find which cache the data should be in (or go in if not read - * yet). */ - for (cache = csr->cache_head; cache; cache = cache->next) - if (kv->offset >= cache->offset && - kv->offset < (cache->offset + cache->size)) - break; - - if (!cache) { - u32 q, cache_size; - - /* Only create a new cache for Extended ROM leaves. */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - return -EINVAL; - - if (csr->ops->bus_read(csr, - CSR1212_REGISTER_SPACE_BASE + kv->offset, - &q, csr->private)) - return -EIO; - - kv->value.leaf.len = be32_to_cpu(q) >> 16; - - cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + - (csr->max_rom - 1)) & ~(csr->max_rom - 1); - - cache = csr1212_rom_cache_malloc(kv->offset, cache_size); - if (!cache) - return -ENOMEM; - - kv->value.leaf.data = &cache->data[1]; - csr->cache_tail->next = cache; - cache->prev = csr->cache_tail; - cache->next = NULL; - csr->cache_tail = cache; - cache->filled_head = - CSR1212_MALLOC(sizeof(*cache->filled_head)); - if (!cache->filled_head) - return -ENOMEM; - - cache->filled_head->offset_start = 0; - cache->filled_head->offset_end = sizeof(u32); - cache->filled_tail = cache->filled_head; - cache->filled_head->next = NULL; - cache->filled_head->prev = NULL; - cache->data[0] = q; - - /* Don't read the entire extended ROM now. Pieces of it will - * be read when entries inside it are read. */ - return csr1212_parse_keyval(kv, cache); - } - - cache_index = kv->offset - cache->offset; - - /* Now seach read portions of the cache to see if it is there. */ - for (cr = cache->filled_head; cr; cr = cr->next) { - if (cache_index < cr->offset_start) { - newcr = CSR1212_MALLOC(sizeof(*newcr)); - if (!newcr) - return -ENOMEM; - - newcr->offset_start = cache_index & ~(csr->max_rom - 1); - newcr->offset_end = newcr->offset_start; - newcr->next = cr; - newcr->prev = cr->prev; - cr->prev = newcr; - cr = newcr; - break; - } else if ((cache_index >= cr->offset_start) && - (cache_index < cr->offset_end)) { - kvi = (struct csr1212_keyval_img*) - (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); - break; - } else if (cache_index == cr->offset_end) { - break; - } - } - - if (!cr) { - cr = cache->filled_tail; - newcr = CSR1212_MALLOC(sizeof(*newcr)); - if (!newcr) - return -ENOMEM; - - newcr->offset_start = cache_index & ~(csr->max_rom - 1); - newcr->offset_end = newcr->offset_start; - newcr->prev = cr; - newcr->next = cr->next; - cr->next = newcr; - cr = newcr; - cache->filled_tail = newcr; - } - - while(!kvi || cr->offset_end < cache_index + kv_len) { - cache_ptr = &cache->data[bytes_to_quads(cr->offset_end & - ~(csr->max_rom - 1))]; - - addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset + - cr->offset_end) & ~(csr->max_rom - 1); - - if (csr->ops->bus_read(csr, addr, cache_ptr, csr->private)) - return -EIO; - - cr->offset_end += csr->max_rom - (cr->offset_end & - (csr->max_rom - 1)); - - if (!kvi && (cr->offset_end > cache_index)) { - kvi = (struct csr1212_keyval_img*) - (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); - } - - if ((kv_len + (kv->offset - cache->offset)) > cache->size) { - /* The Leaf or Directory claims its length extends - * beyond the ConfigROM image region and thus beyond the - * end of our cache region. Therefore, we abort now - * rather than seg faulting later. */ - return -EIO; - } - - ncr = cr->next; - - if (ncr && (cr->offset_end >= ncr->offset_start)) { - /* consolidate region entries */ - ncr->offset_start = cr->offset_start; - - if (cr->prev) - cr->prev->next = cr->next; - ncr->prev = cr->prev; - if (cache->filled_head == cr) - cache->filled_head = ncr; - CSR1212_FREE(cr); - cr = ncr; - } - } - - return csr1212_parse_keyval(kv, cache); -} - -struct csr1212_keyval * -csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) -{ - if (!kv) - return NULL; - if (!kv->valid) - if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) - return NULL; - return kv; -} - -int csr1212_parse_csr(struct csr1212_csr *csr) -{ - struct csr1212_dentry *dentry; - int ret; - - BUG_ON(!csr || !csr->ops || !csr->ops->bus_read); - - ret = csr1212_parse_bus_info_block(csr); - if (ret != CSR1212_SUCCESS) - return ret; - - /* - * There has been a buggy firmware with bus_info_block.max_rom > 0 - * spotted which actually only supported quadlet read requests to the - * config ROM. Therefore read everything quadlet by quadlet regardless - * of what the bus info block says. - */ - csr->max_rom = 4; - - csr->cache_head->layout_head = csr->root_kv; - csr->cache_head->layout_tail = csr->root_kv; - - csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) + - csr->bus_info_len; - - csr->root_kv->valid = 0; - csr->root_kv->next = csr->root_kv; - csr->root_kv->prev = csr->root_kv; - ret = csr1212_read_keyval(csr, csr->root_kv); - if (ret != CSR1212_SUCCESS) - return ret; - - /* Scan through the Root directory finding all extended ROM regions - * and make cache regions for them */ - for (dentry = csr->root_kv->value.directory.dentries_head; - dentry; dentry = dentry->next) { - if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM && - !dentry->kv->valid) { - ret = csr1212_read_keyval(csr, dentry->kv); - if (ret != CSR1212_SUCCESS) - return ret; - } - } - - return CSR1212_SUCCESS; -} diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h deleted file mode 100644 index a892d922dbc..00000000000 --- a/drivers/ieee1394/csr1212.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * csr1212.h -- IEEE 1212 Control and Status Register support for Linux - * - * Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za> - * Steve Kinneberg <kinnebergsteve@acmsystems.com> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __CSR1212_H__ -#define __CSR1212_H__ - -#include <linux/types.h> -#include <linux/slab.h> -#include <asm/atomic.h> - -#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) -#define CSR1212_FREE(ptr) kfree(ptr) - -#define CSR1212_SUCCESS (0) - - -/* CSR 1212 key types */ -#define CSR1212_KV_TYPE_IMMEDIATE 0 -#define CSR1212_KV_TYPE_CSR_OFFSET 1 -#define CSR1212_KV_TYPE_LEAF 2 -#define CSR1212_KV_TYPE_DIRECTORY 3 - - -/* CSR 1212 key ids */ -#define CSR1212_KV_ID_DESCRIPTOR 0x01 -#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02 -#define CSR1212_KV_ID_VENDOR 0x03 -#define CSR1212_KV_ID_HARDWARE_VERSION 0x04 -#define CSR1212_KV_ID_MODULE 0x07 -#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C -#define CSR1212_KV_ID_EUI_64 0x0D -#define CSR1212_KV_ID_UNIT 0x11 -#define CSR1212_KV_ID_SPECIFIER_ID 0x12 -#define CSR1212_KV_ID_VERSION 0x13 -#define CSR1212_KV_ID_DEPENDENT_INFO 0x14 -#define CSR1212_KV_ID_UNIT_LOCATION 0x15 -#define CSR1212_KV_ID_MODEL 0x17 -#define CSR1212_KV_ID_INSTANCE 0x18 -#define CSR1212_KV_ID_KEYWORD 0x19 -#define CSR1212_KV_ID_FEATURE 0x1A -#define CSR1212_KV_ID_EXTENDED_ROM 0x1B -#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C -#define CSR1212_KV_ID_EXTENDED_KEY 0x1D -#define CSR1212_KV_ID_EXTENDED_DATA 0x1E -#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F -#define CSR1212_KV_ID_DIRECTORY_ID 0x20 -#define CSR1212_KV_ID_REVISION 0x21 - - -/* IEEE 1212 Address space map */ -#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL) -#define CSR1212_ALL_SPACE_SIZE (1ULL << 48) -#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE) - -#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL) -#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20))) -#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE) - -#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL) -#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20)) -#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE) - -#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL) -#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20)) -#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE) - -#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL) -#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512) -#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE) -#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL) -#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512) -#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE) -#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL) -#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024) -#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE) -#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL) -#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048) -#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) -#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_INVALID_ADDR_SPACE -1 - - -/* Config ROM image structures */ -struct csr1212_bus_info_block_img { - u8 length; - u8 crc_length; - u16 crc; - - /* Must be last */ - u32 data[0]; /* older gcc can't handle [] which is standard */ -}; - -struct csr1212_leaf { - int len; - u32 *data; -}; - -struct csr1212_dentry { - struct csr1212_dentry *next, *prev; - struct csr1212_keyval *kv; -}; - -struct csr1212_directory { - int len; - struct csr1212_dentry *dentries_head, *dentries_tail; -}; - -struct csr1212_keyval { - struct { - u8 type; - u8 id; - } key; - union { - u32 immediate; - u32 csr_offset; - struct csr1212_leaf leaf; - struct csr1212_directory directory; - } value; - struct csr1212_keyval *associate; - atomic_t refcnt; - - /* used in generating and/or parsing CSR image */ - struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ - u32 offset; /* position in CSR from 0xffff f000 0000 */ - u8 valid; /* flag indicating keyval has valid data*/ -}; - - -struct csr1212_cache_region { - struct csr1212_cache_region *next, *prev; - u32 offset_start; /* inclusive */ - u32 offset_end; /* exclusive */ -}; - -struct csr1212_csr_rom_cache { - struct csr1212_csr_rom_cache *next, *prev; - struct csr1212_cache_region *filled_head, *filled_tail; - struct csr1212_keyval *layout_head, *layout_tail; - size_t size; - u32 offset; - struct csr1212_keyval *ext_rom; - size_t len; - - /* Must be last */ - u32 data[0]; /* older gcc can't handle [] which is standard */ -}; - -struct csr1212_csr { - size_t bus_info_len; /* bus info block length in bytes */ - size_t crc_len; /* crc length in bytes */ - __be32 *bus_info_data; /* bus info data incl bus name and EUI */ - - void *private; /* private, bus specific data */ - struct csr1212_bus_ops *ops; - - struct csr1212_keyval *root_kv; - - int max_rom; /* max bytes readable in Config ROM region */ - - /* Items below used for image parsing and generation */ - struct csr1212_csr_rom_cache *cache_head, *cache_tail; -}; - -struct csr1212_bus_ops { - /* This function is used by csr1212 to read additional information - * from remote nodes when parsing a Config ROM (i.e., read Config ROM - * entries located in the Units Space. Must return 0 on success - * anything else indicates an error. */ - int (*bus_read) (struct csr1212_csr *csr, u64 addr, - void *buffer, void *private); - - /* This function is used by csr1212 to allocate a region in units space - * in the event that Config ROM entries don't all fit in the predefined - * 1K region. The void *private parameter is private member of struct - * csr1212_csr. */ - u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private); - - /* This function is used by csr1212 to release a region in units space - * that is no longer needed. */ - void (*release_addr) (u64 addr, void *private); -}; - - -/* Descriptor Leaf manipulation macros */ -#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 -#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff -#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) - -#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ - (be32_to_cpu((kv)->value.leaf.data[0]) >> \ - CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) -#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ - (be32_to_cpu((kv)->value.leaf.data[0]) & \ - CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) - - -/* Text Descriptor Leaf manipulation macros */ -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */ -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) - -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ - (be32_to_cpu((kv)->value.leaf.data[1]) >> \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ - ((be32_to_cpu((kv)->value.leaf.data[1]) >> \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ - (be32_to_cpu((kv)->value.leaf.data[1]) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ - (&((kv)->value.leaf.data[2])) - - -/* The following 2 function are for creating new Configuration ROM trees. The - * first function is used for both creating local trees and parsing remote - * trees. The second function adds pertinent information to local Configuration - * ROM trees - namely data for the bus information block. */ -extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, - size_t bus_info_size, - void *private); -extern void csr1212_init_local_csr(struct csr1212_csr *csr, - const u32 *bus_info_data, int max_rom); - - -/* Destroy a Configuration ROM tree and release all memory taken by the tree. */ -extern void csr1212_destroy_csr(struct csr1212_csr *csr); - - -/* The following set of functions are fore creating new keyvals for placement in - * a Configuration ROM tree. Code that creates new keyvals with these functions - * must release those keyvals with csr1212_release_keyval() when they are no - * longer needed. */ -extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value); -extern struct csr1212_keyval *csr1212_new_directory(u8 key); -extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); - - -/* The following function manages association between keyvals. Typically, - * Descriptor Leaves and Directories will be associated with another keyval and - * it is desirable for the Descriptor keyval to be place immediately after the - * keyval that it is associated with. - * Take care with subsequent ROM modifications: There is no function to remove - * previously specified associations. - */ -extern void csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate); - - -/* The following functions manage the association of a keyval and directories. - * A keyval may be attached to more than one directory. */ -extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv); -extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv); - - -/* Creates a complete Configuration ROM image in the list of caches available - * via csr->cache_head. */ -extern int csr1212_generate_csr_image(struct csr1212_csr *csr); - - -/* This is a convience function for reading a block of data out of one of the - * caches in the csr->cache_head list. */ -extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, - u32 len); - - -/* The following functions are in place for parsing Configuration ROM images. - * csr1212_parse_keyval() is used should there be a need to directly parse a - * Configuration ROM directly. */ -extern int csr1212_parse_keyval(struct csr1212_keyval *kv, - struct csr1212_csr_rom_cache *cache); -extern int csr1212_parse_csr(struct csr1212_csr *csr); - - -/* This function allocates a new cache which may be used for either parsing or - * generating sub-sets of Configuration ROM images. */ -static inline struct csr1212_csr_rom_cache * -csr1212_rom_cache_malloc(u32 offset, size_t size) -{ - struct csr1212_csr_rom_cache *cache; - - cache = CSR1212_MALLOC(sizeof(*cache) + size); - if (!cache) - return NULL; - - cache->next = NULL; - cache->prev = NULL; - cache->filled_head = NULL; - cache->filled_tail = NULL; - cache->layout_head = NULL; - cache->layout_tail = NULL; - cache->offset = offset; - cache->size = size; - cache->ext_rom = NULL; - - return cache; -} - - -/* This function ensures that a keyval contains data when referencing a keyval - * created by parsing a Configuration ROM. */ -extern struct csr1212_keyval * -csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); - - -/* This function increments the reference count for a keyval should there be a - * need for code to retain a keyval that has been parsed. */ -static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) -{ - atomic_inc(&kv->refcnt); - smp_mb__after_atomic_inc(); -} - - -/* This function decrements a keyval's reference count and will destroy the - * keyval when there are no more users of the keyval. This should be called by - * any code that calls csr1212_keep_keyval() or any of the keyval creation - * routines csr1212_new_*(). */ -extern void csr1212_release_keyval(struct csr1212_keyval *kv); - - -/* - * This macro allows for looping over the keyval entries in a directory and it - * ensures that keyvals from remote ConfigROMs are parsed properly. - * - * struct csr1212_csr *_csr points to the CSR associated with dir. - * struct csr1212_keyval *_kv points to the current keyval (loop index). - * struct csr1212_keyval *_dir points to the directory to be looped. - * struct csr1212_dentry *_pos is used internally for indexing. - * - * kv will be NULL upon exit of the loop. - */ -#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ - for (csr1212_get_keyval((_csr), (_dir)), \ - _pos = (_dir)->value.directory.dentries_head, \ - _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\ - (_kv) && (_pos); \ - (_kv->associate == NULL) ? \ - ((_pos = _pos->next), (_kv = (_pos) ? \ - csr1212_get_keyval((_csr), _pos->kv) : \ - NULL)) : \ - (_kv = csr1212_get_keyval((_csr), _kv->associate))) - -#endif /* __CSR1212_H__ */ diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c deleted file mode 100644 index d178699b194..00000000000 --- a/drivers/ieee1394/dma.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * DMA region bookkeeping routines - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/vmalloc.h> -#include <linux/scatterlist.h> - -#include "dma.h" - -/* dma_prog_region */ - -void dma_prog_region_init(struct dma_prog_region *prog) -{ - prog->kvirt = NULL; - prog->dev = NULL; - prog->n_pages = 0; - prog->bus_addr = 0; -} - -int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, - struct pci_dev *dev) -{ - /* round up to page size */ - n_bytes = PAGE_ALIGN(n_bytes); - - prog->n_pages = n_bytes >> PAGE_SHIFT; - - prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr); - if (!prog->kvirt) { - printk(KERN_ERR - "dma_prog_region_alloc: pci_alloc_consistent() failed\n"); - dma_prog_region_free(prog); - return -ENOMEM; - } - - prog->dev = dev; - - return 0; -} - -void dma_prog_region_free(struct dma_prog_region *prog) -{ - if (prog->kvirt) { - pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT, - prog->kvirt, prog->bus_addr); - } - - prog->kvirt = NULL; - prog->dev = NULL; - prog->n_pages = 0; - prog->bus_addr = 0; -} - -/* dma_region */ - -/** - * dma_region_init - clear out all fields but do not allocate anything - */ -void dma_region_init(struct dma_region *dma) -{ - dma->kvirt = NULL; - dma->dev = NULL; - dma->n_pages = 0; - dma->n_dma_pages = 0; - dma->sglist = NULL; -} - -/** - * dma_region_alloc - allocate the buffer and map it to the IOMMU - */ -int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, - struct pci_dev *dev, int direction) -{ - unsigned int i; - - /* round up to page size */ - n_bytes = PAGE_ALIGN(n_bytes); - - dma->n_pages = n_bytes >> PAGE_SHIFT; - - dma->kvirt = vmalloc_32(n_bytes); - if (!dma->kvirt) { - printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n"); - goto err; - } - - /* Clear the ram out, no junk to the user */ - memset(dma->kvirt, 0, n_bytes); - - /* allocate scatter/gather list */ - dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist)); - if (!dma->sglist) { - printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed\n"); - goto err; - } - - sg_init_table(dma->sglist, dma->n_pages); - - /* fill scatter/gather list with pages */ - for (i = 0; i < dma->n_pages; i++) { - unsigned long va = - (unsigned long)dma->kvirt + (i << PAGE_SHIFT); - - sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va), - PAGE_SIZE, 0); - } - - /* map sglist to the IOMMU */ - dma->n_dma_pages = - pci_map_sg(dev, dma->sglist, dma->n_pages, direction); - - if (dma->n_dma_pages == 0) { - printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n"); - goto err; - } - - dma->dev = dev; - dma->direction = direction; - - return 0; - - err: - dma_region_free(dma); - return -ENOMEM; -} - -/** - * dma_region_free - unmap and free the buffer - */ -void dma_region_free(struct dma_region *dma) -{ - if (dma->n_dma_pages) { - pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages, - dma->direction); - dma->n_dma_pages = 0; - dma->dev = NULL; - } - - vfree(dma->sglist); - dma->sglist = NULL; - - vfree(dma->kvirt); - dma->kvirt = NULL; - dma->n_pages = 0; -} - -/* find the scatterlist index and remaining offset corresponding to a - given offset from the beginning of the buffer */ -static inline int dma_region_find(struct dma_region *dma, unsigned long offset, - unsigned int start, unsigned long *rem) -{ - int i; - unsigned long off = offset; - - for (i = start; i < dma->n_dma_pages; i++) { - if (off < sg_dma_len(&dma->sglist[i])) { - *rem = off; - break; - } - - off -= sg_dma_len(&dma->sglist[i]); - } - - BUG_ON(i >= dma->n_dma_pages); - - return i; -} - -/** - * dma_region_offset_to_bus - get bus address of an offset within a DMA region - * - * Returns the DMA bus address of the byte with the given @offset relative to - * the beginning of the @dma. - */ -dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, - unsigned long offset) -{ - unsigned long rem = 0; - - struct scatterlist *sg = - &dma->sglist[dma_region_find(dma, offset, 0, &rem)]; - return sg_dma_address(sg) + rem; -} - -/** - * dma_region_sync_for_cpu - sync the CPU's view of the buffer - */ -void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, - unsigned long len) -{ - int first, last; - unsigned long rem = 0; - - if (!len) - len = 1; - - first = dma_region_find(dma, offset, 0, &rem); - last = dma_region_find(dma, rem + len - 1, first, &rem); - - pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1, - dma->direction); -} - -/** - * dma_region_sync_for_device - sync the IO bus' view of the buffer - */ -void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, - unsigned long len) -{ - int first, last; - unsigned long rem = 0; - - if (!len) - len = 1; - - first = dma_region_find(dma, offset, 0, &rem); - last = dma_region_find(dma, rem + len - 1, first, &rem); - - pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first], - last - first + 1, dma->direction); -} - -#ifdef CONFIG_MMU - -static int dma_region_pagefault(struct vm_area_struct *vma, - struct vm_fault *vmf) -{ - struct dma_region *dma = (struct dma_region *)vma->vm_private_data; - - if (!dma->kvirt) - return VM_FAULT_SIGBUS; - - if (vmf->pgoff >= dma->n_pages) - return VM_FAULT_SIGBUS; - - vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT)); - get_page(vmf->page); - return 0; -} - -static const struct vm_operations_struct dma_region_vm_ops = { - .fault = dma_region_pagefault, -}; - -/** - * dma_region_mmap - map the buffer into a user space process - */ -int dma_region_mmap(struct dma_region *dma, struct file *file, - struct vm_area_struct *vma) -{ - unsigned long size; - - if (!dma->kvirt) - return -EINVAL; - - /* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */ - if (vma->vm_pgoff != 0) - return -EINVAL; - - /* check the length */ - size = vma->vm_end - vma->vm_start; - if (size > (dma->n_pages << PAGE_SHIFT)) - return -EINVAL; - - vma->vm_ops = &dma_region_vm_ops; - vma->vm_private_data = dma; - vma->vm_file = file; - vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP; - - return 0; -} - -#else /* CONFIG_MMU */ - -int dma_region_mmap(struct dma_region *dma, struct file *file, - struct vm_area_struct *vma) -{ - return -EINVAL; -} - -#endif /* CONFIG_MMU */ diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h deleted file mode 100644 index 467373cab8e..00000000000 --- a/drivers/ieee1394/dma.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * DMA region bookkeeping routines - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#ifndef IEEE1394_DMA_H -#define IEEE1394_DMA_H - -#include <asm/types.h> - -struct file; -struct pci_dev; -struct scatterlist; -struct vm_area_struct; - -/** - * struct dma_prog_region - small contiguous DMA buffer - * @kvirt: kernel virtual address - * @dev: PCI device - * @n_pages: number of kernel pages - * @bus_addr: base bus address - * - * a small, physically contiguous DMA buffer with random-access, synchronous - * usage characteristics - */ -struct dma_prog_region { - unsigned char *kvirt; - struct pci_dev *dev; - unsigned int n_pages; - dma_addr_t bus_addr; -}; - -/* clear out all fields but do not allocate any memory */ -void dma_prog_region_init(struct dma_prog_region *prog); -int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, - struct pci_dev *dev); -void dma_prog_region_free(struct dma_prog_region *prog); - -static inline dma_addr_t dma_prog_region_offset_to_bus( - struct dma_prog_region *prog, unsigned long offset) -{ - return prog->bus_addr + offset; -} - -/** - * struct dma_region - large non-contiguous DMA buffer - * @virt: kernel virtual address - * @dev: PCI device - * @n_pages: number of kernel pages - * @n_dma_pages: number of IOMMU pages - * @sglist: IOMMU mapping - * @direction: PCI_DMA_TODEVICE, etc. - * - * a large, non-physically-contiguous DMA buffer with streaming, asynchronous - * usage characteristics - */ -struct dma_region { - unsigned char *kvirt; - struct pci_dev *dev; - unsigned int n_pages; - unsigned int n_dma_pages; - struct scatterlist *sglist; - int direction; -}; - -void dma_region_init(struct dma_region *dma); -int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, - struct pci_dev *dev, int direction); -void dma_region_free(struct dma_region *dma); -void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, - unsigned long len); -void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, - unsigned long len); -int dma_region_mmap(struct dma_region *dma, struct file *file, - struct vm_area_struct *vma); -dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, - unsigned long offset); - -/** - * dma_region_i - macro to index into a DMA region (or dma_prog_region) - */ -#define dma_region_i(_dma, _type, _index) \ - ( ((_type*) ((_dma)->kvirt)) + (_index) ) - -#endif /* IEEE1394_DMA_H */ diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h deleted file mode 100644 index 18b92cbf4a9..00000000000 --- a/drivers/ieee1394/dv1394-private.h +++ /dev/null @@ -1,587 +0,0 @@ -/* - * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips - * Copyright (C)2001 Daniel Maas <dmaas@dcine.com> - * receive by Dan Dennedy <dan@dennedy.org> - * - * based on: - * video1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * Peter Schlaile <udbz@rz.uni-karlsruhe.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _DV_1394_PRIVATE_H -#define _DV_1394_PRIVATE_H - -#include "ieee1394.h" -#include "ohci1394.h" -#include "dma.h" - -/* data structures private to the dv1394 driver */ -/* none of this is exposed to user-space */ - - -/* - the 8-byte CIP (Common Isochronous Packet) header that precedes - each packet of DV data. - - See the IEC 61883 standard. -*/ - -struct CIP_header { unsigned char b[8]; }; - -static inline void fill_cip_header(struct CIP_header *cip, - unsigned char source_node_id, - unsigned long counter, - enum pal_or_ntsc format, - unsigned long timestamp) -{ - cip->b[0] = source_node_id; - cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */ - cip->b[2] = 0x00; - cip->b[3] = counter; - - cip->b[4] = 0x80; /* const */ - - switch(format) { - case DV1394_PAL: - cip->b[5] = 0x80; - break; - case DV1394_NTSC: - cip->b[5] = 0x00; - break; - } - - cip->b[6] = timestamp >> 8; - cip->b[7] = timestamp & 0xFF; -} - - - -/* - DMA commands used to program the OHCI's DMA engine - - See the Texas Instruments OHCI 1394 chipset documentation. -*/ - -struct output_more_immediate { __le32 q[8]; }; -struct output_more { __le32 q[4]; }; -struct output_last { __le32 q[4]; }; -struct input_more { __le32 q[4]; }; -struct input_last { __le32 q[4]; }; - -/* outputs */ - -static inline void fill_output_more_immediate(struct output_more_immediate *omi, - unsigned char tag, - unsigned char channel, - unsigned char sync_tag, - unsigned int payload_size) -{ - omi->q[0] = cpu_to_le32(0x02000000 | 8); /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */ - omi->q[1] = cpu_to_le32(0); - omi->q[2] = cpu_to_le32(0); - omi->q[3] = cpu_to_le32(0); - - /* IT packet header */ - omi->q[4] = cpu_to_le32( (0x0 << 16) /* IEEE1394_SPEED_100 */ - | (tag << 14) - | (channel << 8) - | (TCODE_ISO_DATA << 4) - | (sync_tag) ); - - /* reserved field; mimic behavior of my Sony DSR-40 */ - omi->q[5] = cpu_to_le32((payload_size << 16) | (0x7F << 8) | 0xA0); - - omi->q[6] = cpu_to_le32(0); - omi->q[7] = cpu_to_le32(0); -} - -static inline void fill_output_more(struct output_more *om, - unsigned int data_size, - unsigned long data_phys_addr) -{ - om->q[0] = cpu_to_le32(data_size); - om->q[1] = cpu_to_le32(data_phys_addr); - om->q[2] = cpu_to_le32(0); - om->q[3] = cpu_to_le32(0); -} - -static inline void fill_output_last(struct output_last *ol, - int want_timestamp, - int want_interrupt, - unsigned int data_size, - unsigned long data_phys_addr) -{ - u32 temp = 0; - temp |= 1 << 28; /* OUTPUT_LAST */ - - if (want_timestamp) /* controller will update timestamp at DMA time */ - temp |= 1 << 27; - - if (want_interrupt) - temp |= 3 << 20; - - temp |= 3 << 18; /* must take branch */ - temp |= data_size; - - ol->q[0] = cpu_to_le32(temp); - ol->q[1] = cpu_to_le32(data_phys_addr); - ol->q[2] = cpu_to_le32(0); - ol->q[3] = cpu_to_le32(0); -} - -/* inputs */ - -static inline void fill_input_more(struct input_more *im, - int want_interrupt, - unsigned int data_size, - unsigned long data_phys_addr) -{ - u32 temp = 2 << 28; /* INPUT_MORE */ - temp |= 8 << 24; /* s = 1, update xferStatus and resCount */ - if (want_interrupt) - temp |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */ - temp |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */ - /* disable wait on sync field, not used in DV :-( */ - temp |= data_size; - - im->q[0] = cpu_to_le32(temp); - im->q[1] = cpu_to_le32(data_phys_addr); - im->q[2] = cpu_to_le32(0); /* branchAddress and Z not use in packet-per-buffer mode */ - im->q[3] = cpu_to_le32(0); /* xferStatus & resCount, resCount must be initialize to data_size */ -} - -static inline void fill_input_last(struct input_last *il, - int want_interrupt, - unsigned int data_size, - unsigned long data_phys_addr) -{ - u32 temp = 3 << 28; /* INPUT_LAST */ - temp |= 8 << 24; /* s = 1, update xferStatus and resCount */ - if (want_interrupt) - temp |= 3 << 20; /* enable interrupts */ - temp |= 0xC << 16; /* enable branch to address */ - /* disable wait on sync field, not used in DV :-( */ - temp |= data_size; - - il->q[0] = cpu_to_le32(temp); - il->q[1] = cpu_to_le32(data_phys_addr); - il->q[2] = cpu_to_le32(1); /* branchAddress (filled in later) and Z = 1 descriptor in next block */ - il->q[3] = cpu_to_le32(data_size); /* xferStatus & resCount, resCount must be initialize to data_size */ -} - - - -/* - A "DMA descriptor block" consists of several contiguous DMA commands. - struct DMA_descriptor_block encapsulates all of the commands necessary - to send one packet of DV data. - - There are three different types of these blocks: - - 1) command to send an empty packet (CIP header only, no DV data): - - OUTPUT_MORE-Immediate <-- contains the iso header in-line - OUTPUT_LAST <-- points to the CIP header - - 2) command to send a full packet when the DV data payload does NOT - cross a page boundary: - - OUTPUT_MORE-Immediate <-- contains the iso header in-line - OUTPUT_MORE <-- points to the CIP header - OUTPUT_LAST <-- points to entire DV data payload - - 3) command to send a full packet when the DV payload DOES cross - a page boundary: - - OUTPUT_MORE-Immediate <-- contains the iso header in-line - OUTPUT_MORE <-- points to the CIP header - OUTPUT_MORE <-- points to first part of DV data payload - OUTPUT_LAST <-- points to second part of DV data payload - - This struct describes all three block types using unions. - - !!! It is vital that an even number of these descriptor blocks fit on one - page of memory, since a block cannot cross a page boundary !!! - - */ - -struct DMA_descriptor_block { - - union { - struct { - /* iso header, common to all output block types */ - struct output_more_immediate omi; - - union { - /* empty packet */ - struct { - struct output_last ol; /* CIP header */ - } empty; - - /* full packet */ - struct { - struct output_more om; /* CIP header */ - - union { - /* payload does not cross page boundary */ - struct { - struct output_last ol; /* data payload */ - } nocross; - - /* payload crosses page boundary */ - struct { - struct output_more om; /* data payload */ - struct output_last ol; /* data payload */ - } cross; - } u; - - } full; - } u; - } out; - - struct { - struct input_last il; - } in; - - } u; - - /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0 - by padding out to 128 bytes */ - u32 __pad__[12]; -}; - - -/* struct frame contains all data associated with one frame in the - ringbuffer these are allocated when the DMA context is initialized - do_dv1394_init(). They are re-used after the card finishes - transmitting the frame. */ - -struct video_card; /* forward declaration */ - -struct frame { - - /* points to the struct video_card that owns this frame */ - struct video_card *video; - - /* index of this frame in video_card->frames[] */ - unsigned int frame_num; - - /* FRAME_CLEAR - DMA program not set up, waiting for data - FRAME_READY - DMA program written, ready to transmit - - Changes to these should be locked against the interrupt - */ - enum { - FRAME_CLEAR = 0, - FRAME_READY - } state; - - /* whether this frame has been DMA'ed already; used only from - the IRQ handler to determine whether the frame can be reset */ - int done; - - - /* kernel virtual pointer to the start of this frame's data in - the user ringbuffer. Use only for CPU access; to get the DMA - bus address you must go through the video->user_dma mapping */ - unsigned long data; - - /* Max # of packets per frame */ -#define MAX_PACKETS 500 - - - /* a PAGE_SIZE memory pool for allocating CIP headers - !header_pool must be aligned to PAGE_SIZE! */ - struct CIP_header *header_pool; - dma_addr_t header_pool_dma; - - - /* a physically contiguous memory pool for allocating DMA - descriptor blocks; usually around 64KB in size - !descriptor_pool must be aligned to PAGE_SIZE! */ - struct DMA_descriptor_block *descriptor_pool; - dma_addr_t descriptor_pool_dma; - unsigned long descriptor_pool_size; - - - /* # of packets allocated for this frame */ - unsigned int n_packets; - - - /* below are several pointers (kernel virtual addresses, not - DMA bus addresses) to parts of the DMA program. These are - set each time the DMA program is written in - frame_prepare(). They are used later on, e.g. from the - interrupt handler, to check the status of the frame */ - - /* points to status/timestamp field of first DMA packet */ - /* (we'll check it later to monitor timestamp accuracy) */ - __le32 *frame_begin_timestamp; - - /* the timestamp we assigned to the first packet in the frame */ - u32 assigned_timestamp; - - /* pointer to the first packet's CIP header (where the timestamp goes) */ - struct CIP_header *cip_syt1; - - /* pointer to the second packet's CIP header - (only set if the first packet was empty) */ - struct CIP_header *cip_syt2; - - /* in order to figure out what caused an interrupt, - store pointers to the status fields of the two packets - that can cause interrupts. We'll check these from the - interrupt handler. - */ - __le32 *mid_frame_timestamp; - __le32 *frame_end_timestamp; - - /* branch address field of final packet. This is effectively - the "tail" in the chain of DMA descriptor blocks. - We will fill it with the address of the first DMA descriptor - block in the subsequent frame, once it is ready. - */ - __le32 *frame_end_branch; - - /* the number of descriptors in the first descriptor block - of the frame. Needed to start DMA */ - int first_n_descriptors; -}; - - -struct packet { - __le16 timestamp; - u16 invalid; - u16 iso_header; - __le16 data_length; - u32 cip_h1; - u32 cip_h2; - unsigned char data[480]; - unsigned char padding[16]; /* force struct size =512 for page alignment */ -}; - - -/* allocate/free a frame */ -static struct frame* frame_new(unsigned int frame_num, struct video_card *video); -static void frame_delete(struct frame *f); - -/* reset f so that it can be used again */ -static void frame_reset(struct frame *f); - -/* struct video_card contains all data associated with one instance - of the dv1394 driver -*/ -enum modes { - MODE_RECEIVE, - MODE_TRANSMIT -}; - -struct video_card { - - /* ohci card to which this instance corresponds */ - struct ti_ohci *ohci; - - /* OHCI card id; the link between the VFS inode and a specific video_card - (essentially the device minor number) */ - int id; - - /* entry in dv1394_cards */ - struct list_head list; - - /* OHCI card IT DMA context number, -1 if not in use */ - int ohci_it_ctx; - struct ohci1394_iso_tasklet it_tasklet; - - /* register offsets for current IT DMA context, 0 if not in use */ - u32 ohci_IsoXmitContextControlSet; - u32 ohci_IsoXmitContextControlClear; - u32 ohci_IsoXmitCommandPtr; - - /* OHCI card IR DMA context number, -1 if not in use */ - struct ohci1394_iso_tasklet ir_tasklet; - int ohci_ir_ctx; - - /* register offsets for current IR DMA context, 0 if not in use */ - u32 ohci_IsoRcvContextControlSet; - u32 ohci_IsoRcvContextControlClear; - u32 ohci_IsoRcvCommandPtr; - u32 ohci_IsoRcvContextMatch; - - - /* CONCURRENCY CONTROL */ - - /* there are THREE levels of locking associated with video_card. */ - - /* - 1) the 'open' flag - this prevents more than one process from - opening the device. (the driver currently assumes only one opener). - This is a regular int, but use test_and_set_bit() (on bit zero) - for atomicity. - */ - unsigned long open; - - /* - 2) the spinlock - this provides mutual exclusion between the interrupt - handler and process-context operations. Generally you must take the - spinlock under the following conditions: - 1) DMA (and hence the interrupt handler) may be running - AND - 2) you need to operate on the video_card, especially active_frame - - It is OK to play with video_card without taking the spinlock if - you are certain that DMA is not running. Even if DMA is running, - it is OK to *read* active_frame with the lock, then drop it - immediately. This is safe because the interrupt handler will never - advance active_frame onto a frame that is not READY (and the spinlock - must be held while marking a frame READY). - - spinlock is also used to protect ohci_it_ctx and ohci_ir_ctx, - which can be accessed from both process and interrupt context - */ - spinlock_t spinlock; - - /* flag to prevent spurious interrupts (which OHCI seems to - generate a lot :) from accessing the struct */ - int dma_running; - - /* - 3) the sleeping mutex 'mtx' - this is used from process context only, - to serialize various operations on the video_card. Even though only one - open() is allowed, we still need to prevent multiple threads of execution - from entering calls like read, write, ioctl, etc. - - I honestly can't think of a good reason to use dv1394 from several threads - at once, but we need to serialize anyway to prevent oopses =). - - NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock! - */ - struct mutex mtx; - - /* people waiting for buffer space, please form a line here... */ - wait_queue_head_t waitq; - - /* support asynchronous I/O signals (SIGIO) */ - struct fasync_struct *fasync; - - /* the large, non-contiguous (rvmalloc()) ringbuffer for DV - data, exposed to user-space via mmap() */ - unsigned long dv_buf_size; - struct dma_region dv_buf; - - /* next byte in the ringbuffer that a write() call will fill */ - size_t write_off; - - struct frame *frames[DV1394_MAX_FRAMES]; - - /* n_frames also serves as an indicator that this struct video_card is - initialized and ready to run DMA buffers */ - - int n_frames; - - /* this is the frame that is currently "owned" by the OHCI DMA controller - (set to -1 iff DMA is not running) - - ! must lock against the interrupt handler when accessing it ! - - RULES: - - Only the interrupt handler may change active_frame if DMA - is running; if not, process may change it - - If the next frame is READY, the interrupt handler will advance - active_frame when the current frame is finished. - - If the next frame is CLEAR, the interrupt handler will re-transmit - the current frame, and the dropped_frames counter will be incremented. - - The interrupt handler will NEVER advance active_frame to a - frame that is not READY. - */ - int active_frame; - int first_run; - - /* the same locking rules apply to these three fields also: */ - - /* altered ONLY from process context. Must check first_clear_frame->state; - if it's READY, that means the ringbuffer is full with READY frames; - if it's CLEAR, that means one or more ringbuffer frames are CLEAR */ - unsigned int first_clear_frame; - - /* altered both by process and interrupt */ - unsigned int n_clear_frames; - - /* only altered by the interrupt */ - unsigned int dropped_frames; - - - - /* the CIP accumulator and continuity counter are properties - of the DMA stream as a whole (not a single frame), so they - are stored here in the video_card */ - - unsigned long cip_accum; - unsigned long cip_n, cip_d; - unsigned int syt_offset; - unsigned int continuity_counter; - - enum pal_or_ntsc pal_or_ntsc; - - /* redundant, but simplifies the code somewhat */ - unsigned int frame_size; /* in bytes */ - - /* the isochronous channel to use, -1 if video card is inactive */ - int channel; - - - /* physically contiguous packet ringbuffer for receive */ - struct dma_region packet_buf; - unsigned long packet_buf_size; - - unsigned int current_packet; - int first_frame; /* received first start frame marker? */ - enum modes mode; -}; - -/* - if the video_card is not initialized, then the ONLY fields that are valid are: - ohci - open - n_frames -*/ - -static inline int video_card_initialized(struct video_card *v) -{ - return v->n_frames > 0; -} - -static int do_dv1394_init(struct video_card *video, struct dv1394_init *init); -static int do_dv1394_init_default(struct video_card *video); -static void do_dv1394_shutdown(struct video_card *video, int free_user_buf); - - -/* NTSC empty packet rate accurate to within 0.01%, - calibrated against a Sony DSR-40 DVCAM deck */ - -#define CIP_N_NTSC 68000000 -#define CIP_D_NTSC 1068000000 - -#define CIP_N_PAL 1 -#define CIP_D_PAL 16 - -#endif /* _DV_1394_PRIVATE_H */ - diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c deleted file mode 100644 index c5a031b79d0..00000000000 --- a/drivers/ieee1394/dv1394.c +++ /dev/null @@ -1,2584 +0,0 @@ -/* - * dv1394.c - DV input/output over IEEE 1394 on OHCI chips - * Copyright (C)2001 Daniel Maas <dmaas@dcine.com> - * receive by Dan Dennedy <dan@dennedy.org> - * - * based on: - * video1394.c - video driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - OVERVIEW - - I designed dv1394 as a "pipe" that you can use to shoot DV onto a - FireWire bus. In transmission mode, dv1394 does the following: - - 1. accepts contiguous frames of DV data from user-space, via write() - or mmap() (see dv1394.h for the complete API) - 2. wraps IEC 61883 packets around the DV data, inserting - empty synchronization packets as necessary - 3. assigns accurate SYT timestamps to the outgoing packets - 4. shoots them out using the OHCI card's IT DMA engine - - Thanks to Dan Dennedy, we now have a receive mode that does the following: - - 1. accepts raw IEC 61883 packets from the OHCI card - 2. re-assembles the DV data payloads into contiguous frames, - discarding empty packets - 3. sends the DV data to user-space via read() or mmap() -*/ - -/* - TODO: - - - tunable frame-drop behavior: either loop last frame, or halt transmission - - - use a scatter/gather buffer for DMA programs (f->descriptor_pool) - so that we don't rely on allocating 64KB of contiguous kernel memory - via pci_alloc_consistent() - - DONE: - - during reception, better handling of dropped frames and continuity errors - - during reception, prevent DMA from bypassing the irq tasklets - - reduce irq rate during reception (1/250 packets). - - add many more internal buffers during reception with scatter/gather dma. - - add dbc (continuity) checking on receive, increment status.dropped_frames - if not continuous. - - restart IT DMA after a bus reset - - safely obtain and release ISO Tx channels in cooperation with OHCI driver - - map received DIF blocks to their proper location in DV frame (ensure - recovery if dropped packet) - - handle bus resets gracefully (OHCI card seems to take care of this itself(!)) - - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings - - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk - - added wmb() and mb() to places where PCI read/write ordering needs to be enforced - - set video->id correctly - - store video_cards in an array indexed by OHCI card ID, rather than a list - - implement DMA context allocation to cooperate with other users of the OHCI - - fix all XXX showstoppers - - disable IR/IT DMA interrupts on shutdown - - flush pci writes to the card by issuing a read - - character device dispatching - - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!) - - keep all video_cards in a list (for open() via chardev), set file->private_data = video - - dv1394_poll should indicate POLLIN when receiving buffers are available - - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal - - expose xmit and recv as separate devices (not exclusive) - - expose NTSC and PAL as separate devices (can be overridden) - -*/ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/mutex.h> -#include <linux/bitops.h> -#include <asm/byteorder.h> -#include <asm/atomic.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/delay.h> -#include <asm/pgtable.h> -#include <asm/page.h> -#include <linux/sched.h> -#include <linux/types.h> -#include <linux/vmalloc.h> -#include <linux/string.h> -#include <linux/compat.h> -#include <linux/cdev.h> - -#include "dv1394.h" -#include "dv1394-private.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_types.h" -#include "nodemgr.h" -#include "ohci1394.h" - -/* DEBUG LEVELS: - 0 - no debugging messages - 1 - some debugging messages, but none during DMA frame transmission - 2 - lots of messages, including during DMA frame transmission - (will cause underflows if your machine is too slow!) -*/ - -#define DV1394_DEBUG_LEVEL 0 - -/* for debugging use ONLY: allow more than one open() of the device */ -/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */ - -#if DV1394_DEBUG_LEVEL >= 2 -#define irq_printk( args... ) printk( args ) -#else -#define irq_printk( args... ) do {} while (0) -#endif - -#if DV1394_DEBUG_LEVEL >= 1 -#define debug_printk( args... ) printk( args) -#else -#define debug_printk( args... ) do {} while (0) -#endif - -/* issue a dummy PCI read to force the preceding write - to be posted to the PCI bus immediately */ - -static inline void flush_pci_write(struct ti_ohci *ohci) -{ - mb(); - reg_read(ohci, OHCI1394_IsochronousCycleTimer); -} - -static void it_tasklet_func(unsigned long data); -static void ir_tasklet_func(unsigned long data); - -#ifdef CONFIG_COMPAT -static long dv1394_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); -#endif - -/* GLOBAL DATA */ - -/* list of all video_cards */ -static LIST_HEAD(dv1394_cards); -static DEFINE_SPINLOCK(dv1394_cards_lock); - -/* translate from a struct file* to the corresponding struct video_card* */ - -static inline struct video_card* file_to_video_card(struct file *file) -{ - return file->private_data; -} - -/*** FRAME METHODS *********************************************************/ - -static void frame_reset(struct frame *f) -{ - f->state = FRAME_CLEAR; - f->done = 0; - f->n_packets = 0; - f->frame_begin_timestamp = NULL; - f->assigned_timestamp = 0; - f->cip_syt1 = NULL; - f->cip_syt2 = NULL; - f->mid_frame_timestamp = NULL; - f->frame_end_timestamp = NULL; - f->frame_end_branch = NULL; -} - -static struct frame* frame_new(unsigned int frame_num, struct video_card *video) -{ - struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL); - if (!f) - return NULL; - - f->video = video; - f->frame_num = frame_num; - - f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma); - if (!f->header_pool) { - printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n"); - kfree(f); - return NULL; - } - - debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", - (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE); - - f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block); - /* make it an even # of pages */ - f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE); - - f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev, - f->descriptor_pool_size, - &f->descriptor_pool_dma); - if (!f->descriptor_pool) { - pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); - kfree(f); - return NULL; - } - - debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", - (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size); - - f->data = 0; - frame_reset(f); - - return f; -} - -static void frame_delete(struct frame *f) -{ - pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); - pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma); - kfree(f); -} - - - - -/* - frame_prepare() - build the DMA program for transmitting - - Frame_prepare() must be called OUTSIDE the video->spinlock. - However, frame_prepare() must still be serialized, so - it should be called WITH the video->mtx taken. - */ - -static void frame_prepare(struct video_card *video, unsigned int this_frame) -{ - struct frame *f = video->frames[this_frame]; - int last_frame; - - struct DMA_descriptor_block *block; - dma_addr_t block_dma; - struct CIP_header *cip; - dma_addr_t cip_dma; - - unsigned int n_descriptors, full_packets, packets_per_frame, payload_size; - - /* these flags denote packets that need special attention */ - int empty_packet, first_packet, last_packet, mid_packet; - - __le32 *branch_address, *last_branch_address = NULL; - unsigned long data_p; - int first_packet_empty = 0; - u32 cycleTimer, ct_sec, ct_cyc, ct_off; - unsigned long irq_flags; - - irq_printk("frame_prepare( %d ) ---------------------\n", this_frame); - - full_packets = 0; - - - - if (video->pal_or_ntsc == DV1394_PAL) - packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME; - else - packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME; - - while ( full_packets < packets_per_frame ) { - empty_packet = first_packet = last_packet = mid_packet = 0; - - data_p = f->data + full_packets * 480; - - /************************************************/ - /* allocate a descriptor block and a CIP header */ - /************************************************/ - - /* note: these should NOT cross a page boundary (DMA restriction) */ - - if (f->n_packets >= MAX_PACKETS) { - printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n"); - return; - } - - /* the block surely won't cross a page boundary, - since an even number of descriptor_blocks fit on a page */ - block = &(f->descriptor_pool[f->n_packets]); - - /* DMA address of the block = offset of block relative - to the kernel base address of the descriptor pool - + DMA base address of the descriptor pool */ - block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; - - - /* the whole CIP pool fits on one page, so no worries about boundaries */ - if ( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool) - > PAGE_SIZE) { - printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n"); - return; - } - - cip = &(f->header_pool[f->n_packets]); - - /* DMA address of the CIP header = offset of cip - relative to kernel base address of the header pool - + DMA base address of the header pool */ - cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma; - - /* is this an empty packet? */ - - if (video->cip_accum > (video->cip_d - video->cip_n)) { - empty_packet = 1; - payload_size = 8; - video->cip_accum -= (video->cip_d - video->cip_n); - } else { - payload_size = 488; - video->cip_accum += video->cip_n; - } - - /* there are three important packets each frame: - - the first packet in the frame - we ask the card to record the timestamp when - this packet is actually sent, so we can monitor - how accurate our timestamps are. Also, the first - packet serves as a semaphore to let us know that - it's OK to free the *previous* frame's DMA buffer - - the last packet in the frame - this packet is used to detect buffer underflows. - if this is the last ready frame, the last DMA block - will have a branch back to the beginning of the frame - (so that the card will re-send the frame on underflow). - if this branch gets taken, we know that at least one - frame has been dropped. When the next frame is ready, - the branch is pointed to its first packet, and the - semaphore is disabled. - - a "mid" packet slightly before the end of the frame - this packet should trigger - an interrupt so we can go and assign a timestamp to the first packet - in the next frame. We don't use the very last packet in the frame - for this purpose, because that would leave very little time to set - the timestamp before DMA starts on the next frame. - */ - - if (f->n_packets == 0) { - first_packet = 1; - } else if ( full_packets == (packets_per_frame-1) ) { - last_packet = 1; - } else if (f->n_packets == packets_per_frame) { - mid_packet = 1; - } - - - /********************/ - /* setup CIP header */ - /********************/ - - /* the timestamp will be written later from the - mid-frame interrupt handler. For now we just - store the address of the CIP header(s) that - need a timestamp. */ - - /* first packet in the frame needs a timestamp */ - if (first_packet) { - f->cip_syt1 = cip; - if (empty_packet) - first_packet_empty = 1; - - } else if (first_packet_empty && (f->n_packets == 1) ) { - /* if the first packet was empty, the second - packet's CIP header also needs a timestamp */ - f->cip_syt2 = cip; - } - - fill_cip_header(cip, - /* the node ID number of the OHCI card */ - reg_read(video->ohci, OHCI1394_NodeID) & 0x3F, - video->continuity_counter, - video->pal_or_ntsc, - 0xFFFF /* the timestamp is filled in later */); - - /* advance counter, only for full packets */ - if ( ! empty_packet ) - video->continuity_counter++; - - /******************************/ - /* setup DMA descriptor block */ - /******************************/ - - /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */ - fill_output_more_immediate( &(block->u.out.omi), 1, video->channel, 0, payload_size); - - if (empty_packet) { - /* second descriptor - OUTPUT_LAST for CIP header */ - fill_output_last( &(block->u.out.u.empty.ol), - - /* want completion status on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* want interrupts on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - sizeof(struct CIP_header), /* data size */ - cip_dma); - - if (first_packet) - f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]); - else if (mid_packet) - f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]); - else if (last_packet) { - f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]); - f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]); - } - - branch_address = &(block->u.out.u.empty.ol.q[2]); - n_descriptors = 3; - if (first_packet) - f->first_n_descriptors = n_descriptors; - - } else { /* full packet */ - - /* second descriptor - OUTPUT_MORE for CIP header */ - fill_output_more( &(block->u.out.u.full.om), - sizeof(struct CIP_header), /* data size */ - cip_dma); - - - /* third (and possibly fourth) descriptor - for DV data */ - /* the 480-byte payload can cross a page boundary; if so, - we need to split it into two DMA descriptors */ - - /* does the 480-byte data payload cross a page boundary? */ - if ( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) { - - /* page boundary crossed */ - - fill_output_more( &(block->u.out.u.full.u.cross.om), - /* data size - how much of data_p fits on the first page */ - PAGE_SIZE - (data_p % PAGE_SIZE), - - /* DMA address of data_p */ - dma_region_offset_to_bus(&video->dv_buf, - data_p - (unsigned long) video->dv_buf.kvirt)); - - fill_output_last( &(block->u.out.u.full.u.cross.ol), - - /* want completion status on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* want interrupt on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* data size - remaining portion of data_p */ - 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)), - - /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */ - dma_region_offset_to_bus(&video->dv_buf, - data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) video->dv_buf.kvirt)); - - if (first_packet) - f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); - else if (mid_packet) - f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); - else if (last_packet) { - f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); - f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]); - } - - branch_address = &(block->u.out.u.full.u.cross.ol.q[2]); - - n_descriptors = 5; - if (first_packet) - f->first_n_descriptors = n_descriptors; - - full_packets++; - - } else { - /* fits on one page */ - - fill_output_last( &(block->u.out.u.full.u.nocross.ol), - - /* want completion status on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* want interrupt on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - 480, /* data size (480 bytes of DV data) */ - - - /* DMA address of data_p */ - dma_region_offset_to_bus(&video->dv_buf, - data_p - (unsigned long) video->dv_buf.kvirt)); - - if (first_packet) - f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); - else if (mid_packet) - f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); - else if (last_packet) { - f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); - f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]); - } - - branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]); - - n_descriptors = 4; - if (first_packet) - f->first_n_descriptors = n_descriptors; - - full_packets++; - } - } - - /* link this descriptor block into the DMA program by filling in - the branch address of the previous block */ - - /* note: we are not linked into the active DMA chain yet */ - - if (last_branch_address) { - *(last_branch_address) = cpu_to_le32(block_dma | n_descriptors); - } - - last_branch_address = branch_address; - - - f->n_packets++; - - } - - /* when we first assemble a new frame, set the final branch - to loop back up to the top */ - *(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors); - - /* make the latest version of this frame visible to the PCI card */ - dma_region_sync_for_device(&video->dv_buf, f->data - (unsigned long) video->dv_buf.kvirt, video->frame_size); - - /* lock against DMA interrupt */ - spin_lock_irqsave(&video->spinlock, irq_flags); - - f->state = FRAME_READY; - - video->n_clear_frames--; - - last_frame = video->first_clear_frame - 1; - if (last_frame == -1) - last_frame = video->n_frames-1; - - video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; - - irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n", - this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame); - - irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n", - (unsigned long) f->frame_begin_timestamp, - (unsigned long) f->mid_frame_timestamp, - (unsigned long) f->frame_end_timestamp, - (unsigned long) f->frame_end_branch); - - if (video->active_frame != -1) { - - /* if DMA is already active, we are almost done */ - /* just link us onto the active DMA chain */ - if (video->frames[last_frame]->frame_end_branch) { - u32 temp; - - /* point the previous frame's tail to this frame's head */ - *(video->frames[last_frame]->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors); - - /* this write MUST precede the next one, or we could silently drop frames */ - wmb(); - - /* disable the want_status semaphore on the last packet */ - temp = le32_to_cpu(*(video->frames[last_frame]->frame_end_branch - 2)); - temp &= 0xF7CFFFFF; - *(video->frames[last_frame]->frame_end_branch - 2) = cpu_to_le32(temp); - - /* flush these writes to memory ASAP */ - flush_pci_write(video->ohci); - - /* NOTE: - ideally the writes should be "atomic": if - the OHCI card reads the want_status flag in - between them, we'll falsely report a - dropped frame. Hopefully this window is too - small to really matter, and the consequence - is rather harmless. */ - - - irq_printk(" new frame %d linked onto DMA chain\n", this_frame); - - } else { - printk(KERN_ERR "dv1394: last frame not ready???\n"); - } - - } else { - - u32 transmit_sec, transmit_cyc; - u32 ts_cyc; - - /* DMA is stopped, so this is the very first frame */ - video->active_frame = this_frame; - - /* set CommandPtr to address and size of first descriptor block */ - reg_write(video->ohci, video->ohci_IsoXmitCommandPtr, - video->frames[video->active_frame]->descriptor_pool_dma | - f->first_n_descriptors); - - /* assign a timestamp based on the current cycle time... - We'll tell the card to begin DMA 100 cycles from now, - and assign a timestamp 103 cycles from now */ - - cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer); - - ct_sec = cycleTimer >> 25; - ct_cyc = (cycleTimer >> 12) & 0x1FFF; - ct_off = cycleTimer & 0xFFF; - - transmit_sec = ct_sec; - transmit_cyc = ct_cyc + 100; - - transmit_sec += transmit_cyc/8000; - transmit_cyc %= 8000; - - ts_cyc = transmit_cyc + 3; - ts_cyc %= 8000; - - f->assigned_timestamp = (ts_cyc&0xF) << 12; - - /* now actually write the timestamp into the appropriate CIP headers */ - if (f->cip_syt1) { - f->cip_syt1->b[6] = f->assigned_timestamp >> 8; - f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF; - } - if (f->cip_syt2) { - f->cip_syt2->b[6] = f->assigned_timestamp >> 8; - f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF; - } - - /* --- start DMA --- */ - - /* clear all bits in ContextControl register */ - - reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF); - wmb(); - - /* the OHCI card has the ability to start ISO transmission on a - particular cycle (start-on-cycle). This way we can ensure that - the first DV frame will have an accurate timestamp. - - However, start-on-cycle only appears to work if the OHCI card - is cycle master! Since the consequences of messing up the first - timestamp are minimal*, just disable start-on-cycle for now. - - * my DV deck drops the first few frames before it "locks in;" - so the first frame having an incorrect timestamp is inconsequential. - */ - -#if 0 - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, - (1 << 31) /* enable start-on-cycle */ - | ( (transmit_sec & 0x3) << 29) - | (transmit_cyc << 16)); - wmb(); -#endif - - video->dma_running = 1; - - /* set the 'run' bit */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000); - flush_pci_write(video->ohci); - - /* --- DMA should be running now --- */ - - debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n", - (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF, - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); - - debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n", - ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF); - -#if DV1394_DEBUG_LEVEL >= 2 - { - /* check if DMA is really running */ - int i = 0; - while (i < 20) { - mb(); - mdelay(1); - if (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) { - printk("DMA ACTIVE after %d msec\n", i); - break; - } - i++; - } - - printk("set = %08x, cmdPtr = %08x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) - ); - - if ( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { - printk("DMA did NOT go active after 20ms, event = %x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F); - } else - printk("DMA is RUNNING!\n"); - } -#endif - - } - - - spin_unlock_irqrestore(&video->spinlock, irq_flags); -} - - - -/*** RECEIVE FUNCTIONS *****************************************************/ - -/* - frame method put_packet - - map and copy the packet data to its location in the frame - based upon DIF section and sequence -*/ - -static void inline -frame_put_packet (struct frame *f, struct packet *p) -{ - int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */ - int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */ - int dif_block = p->data[2]; - - /* sanity check */ - if (dif_sequence > 11 || dif_block > 149) return; - - switch (section_type) { - case 0: /* 1 Header block */ - memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480); - break; - - case 1: /* 2 Subcode blocks */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480); - break; - - case 2: /* 3 VAUX blocks */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480); - break; - - case 3: /* 9 Audio blocks interleaved with video */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480); - break; - - case 4: /* 135 Video blocks interleaved with audio */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480); - break; - - default: /* we can not handle any other data */ - break; - } -} - - -static void start_dma_receive(struct video_card *video) -{ - if (video->first_run == 1) { - video->first_run = 0; - - /* start DMA once all of the frames are READY */ - video->n_clear_frames = 0; - video->first_clear_frame = -1; - video->current_packet = 0; - video->active_frame = 0; - - /* reset iso recv control register */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF); - wmb(); - - /* clear bufferFill, set isochHeader and speed (0=100) */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000); - - /* match on all tags, listen on channel */ - reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel); - - /* address and first descriptor block + Z=1 */ - reg_write(video->ohci, video->ohci_IsoRcvCommandPtr, - video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */ - wmb(); - - video->dma_running = 1; - - /* run */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000); - flush_pci_write(video->ohci); - - debug_printk("dv1394: DMA started\n"); - -#if DV1394_DEBUG_LEVEL >= 2 - { - int i; - - for (i = 0; i < 1000; ++i) { - mdelay(1); - if (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) { - printk("DMA ACTIVE after %d msec\n", i); - break; - } - } - if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { - printk("DEAD, event = %x\n", - reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); - } else - printk("RUNNING!\n"); - } -#endif - } else if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { - debug_printk("DEAD, event = %x\n", - reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); - - /* wake */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); - } -} - - -/* - receive_packets() - build the DMA program for receiving -*/ - -static void receive_packets(struct video_card *video) -{ - struct DMA_descriptor_block *block = NULL; - dma_addr_t block_dma = 0; - struct packet *data = NULL; - dma_addr_t data_dma = 0; - __le32 *last_branch_address = NULL; - unsigned long irq_flags; - int want_interrupt = 0; - struct frame *f = NULL; - int i, j; - - spin_lock_irqsave(&video->spinlock, irq_flags); - - for (j = 0; j < video->n_frames; j++) { - - /* connect frames */ - if (j > 0 && f != NULL && f->frame_end_branch != NULL) - *(f->frame_end_branch) = cpu_to_le32(video->frames[j]->descriptor_pool_dma | 1); /* set Z=1 */ - - f = video->frames[j]; - - for (i = 0; i < MAX_PACKETS; i++) { - /* locate a descriptor block and packet from the buffer */ - block = &(f->descriptor_pool[i]); - block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; - - data = ((struct packet*)video->packet_buf.kvirt) + f->frame_num * MAX_PACKETS + i; - data_dma = dma_region_offset_to_bus( &video->packet_buf, - ((unsigned long) data - (unsigned long) video->packet_buf.kvirt) ); - - /* setup DMA descriptor block */ - want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1)); - fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma); - - /* link descriptors */ - last_branch_address = f->frame_end_branch; - - if (last_branch_address != NULL) - *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */ - - f->frame_end_branch = &(block->u.in.il.q[2]); - } - - } /* next j */ - - spin_unlock_irqrestore(&video->spinlock, irq_flags); - -} - - - -/*** MANAGEMENT FUNCTIONS **************************************************/ - -static int do_dv1394_init(struct video_card *video, struct dv1394_init *init) -{ - unsigned long flags, new_buf_size; - int i; - u64 chan_mask; - int retval = -EINVAL; - - debug_printk("dv1394: initialising %d\n", video->id); - if (init->api_version != DV1394_API_VERSION) - return -EINVAL; - - /* first sanitize all the parameters */ - if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) ) - return -EINVAL; - - if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) ) - return -EINVAL; - - if ( (init->syt_offset == 0) || (init->syt_offset > 50) ) - /* default SYT offset is 3 cycles */ - init->syt_offset = 3; - - if (init->channel > 63) - init->channel = 63; - - chan_mask = (u64)1 << init->channel; - - /* calculate what size DMA buffer is needed */ - if (init->format == DV1394_NTSC) - new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames; - else - new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames; - - /* round up to PAGE_SIZE */ - if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE); - - /* don't allow the user to allocate the DMA buffer more than once */ - if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) { - printk("dv1394: re-sizing the DMA buffer is not allowed\n"); - return -EINVAL; - } - - /* shutdown the card if it's currently active */ - /* (the card should not be reset if the parameters are screwy) */ - - do_dv1394_shutdown(video, 0); - - /* try to claim the ISO channel */ - spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); - if (video->ohci->ISO_channel_usage & chan_mask) { - spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); - retval = -EBUSY; - goto err; - } - video->ohci->ISO_channel_usage |= chan_mask; - spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); - - video->channel = init->channel; - - /* initialize misc. fields of video */ - video->n_frames = init->n_frames; - video->pal_or_ntsc = init->format; - - video->cip_accum = 0; - video->continuity_counter = 0; - - video->active_frame = -1; - video->first_clear_frame = 0; - video->n_clear_frames = video->n_frames; - video->dropped_frames = 0; - - video->write_off = 0; - - video->first_run = 1; - video->current_packet = -1; - video->first_frame = 0; - - if (video->pal_or_ntsc == DV1394_NTSC) { - video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC; - video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC; - video->frame_size = DV1394_NTSC_FRAME_SIZE; - } else { - video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL; - video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL; - video->frame_size = DV1394_PAL_FRAME_SIZE; - } - - video->syt_offset = init->syt_offset; - - /* find and claim DMA contexts on the OHCI card */ - - if (video->ohci_it_ctx == -1) { - ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT, - it_tasklet_func, (unsigned long) video); - - if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) { - printk(KERN_ERR "dv1394: could not find an available IT DMA context\n"); - retval = -EBUSY; - goto err; - } - - video->ohci_it_ctx = video->it_tasklet.context; - debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx); - } - - if (video->ohci_ir_ctx == -1) { - ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE, - ir_tasklet_func, (unsigned long) video); - - if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) { - printk(KERN_ERR "dv1394: could not find an available IR DMA context\n"); - retval = -EBUSY; - goto err; - } - video->ohci_ir_ctx = video->ir_tasklet.context; - debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx); - } - - /* allocate struct frames */ - for (i = 0; i < init->n_frames; i++) { - video->frames[i] = frame_new(i, video); - - if (!video->frames[i]) { - printk(KERN_ERR "dv1394: Cannot allocate frame structs\n"); - retval = -ENOMEM; - goto err; - } - } - - if (!video->dv_buf.kvirt) { - /* allocate the ringbuffer */ - retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE); - if (retval) - goto err; - - video->dv_buf_size = new_buf_size; - - debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n", - video->n_frames, video->dv_buf.n_pages, - video->dv_buf.n_dma_pages, video->dv_buf_size); - } - - /* set up the frame->data pointers */ - for (i = 0; i < video->n_frames; i++) - video->frames[i]->data = (unsigned long) video->dv_buf.kvirt + i * video->frame_size; - - if (!video->packet_buf.kvirt) { - /* allocate packet buffer */ - video->packet_buf_size = sizeof(struct packet) * video->n_frames * MAX_PACKETS; - if (video->packet_buf_size % PAGE_SIZE) - video->packet_buf_size += PAGE_SIZE - (video->packet_buf_size % PAGE_SIZE); - - retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size, - video->ohci->dev, PCI_DMA_FROMDEVICE); - if (retval) - goto err; - - debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n", - video->n_frames*MAX_PACKETS, video->packet_buf.n_pages, - video->packet_buf.n_dma_pages, video->packet_buf_size); - } - - /* set up register offsets for IT context */ - /* IT DMA context registers are spaced 16 bytes apart */ - video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx; - video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx; - video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx; - - /* enable interrupts for IT context */ - reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx)); - debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx); - - /* set up register offsets for IR context */ - /* IR DMA context registers are spaced 32 bytes apart */ - video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx; - video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx; - video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx; - video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx; - - /* enable interrupts for IR context */ - reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) ); - debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx); - - return 0; - -err: - do_dv1394_shutdown(video, 1); - return retval; -} - -/* if the user doesn't bother to call ioctl(INIT) before starting - mmap() or read()/write(), just give him some default values */ - -static int do_dv1394_init_default(struct video_card *video) -{ - struct dv1394_init init; - - init.api_version = DV1394_API_VERSION; - init.n_frames = DV1394_MAX_FRAMES / 4; - init.channel = video->channel; - init.format = video->pal_or_ntsc; - init.cip_n = video->cip_n; - init.cip_d = video->cip_d; - init.syt_offset = video->syt_offset; - - return do_dv1394_init(video, &init); -} - -/* do NOT call from interrupt context */ -static void stop_dma(struct video_card *video) -{ - unsigned long flags; - int i; - - /* no interrupts */ - spin_lock_irqsave(&video->spinlock, flags); - - video->dma_running = 0; - - if ( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) ) - goto out; - - /* stop DMA if in progress */ - if ( (video->active_frame != -1) || - (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || - (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { - - /* clear the .run bits */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); - reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); - flush_pci_write(video->ohci); - - video->active_frame = -1; - video->first_run = 1; - - /* wait until DMA really stops */ - i = 0; - while (i < 1000) { - - /* wait 0.1 millisecond */ - udelay(100); - - if ( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || - (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { - /* still active */ - debug_printk("dv1394: stop_dma: DMA not stopped yet\n" ); - mb(); - } else { - debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10); - break; - } - - i++; - } - - if (i == 1000) { - printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10); - } - } - else - debug_printk("dv1394: stop_dma: already stopped.\n"); - -out: - spin_unlock_irqrestore(&video->spinlock, flags); -} - - - -static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf) -{ - int i; - - debug_printk("dv1394: shutdown...\n"); - - /* stop DMA if in progress */ - stop_dma(video); - - /* release the DMA contexts */ - if (video->ohci_it_ctx != -1) { - video->ohci_IsoXmitContextControlSet = 0; - video->ohci_IsoXmitContextControlClear = 0; - video->ohci_IsoXmitCommandPtr = 0; - - /* disable interrupts for IT context */ - reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx)); - - /* remove tasklet */ - ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet); - debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx); - video->ohci_it_ctx = -1; - } - - if (video->ohci_ir_ctx != -1) { - video->ohci_IsoRcvContextControlSet = 0; - video->ohci_IsoRcvContextControlClear = 0; - video->ohci_IsoRcvCommandPtr = 0; - video->ohci_IsoRcvContextMatch = 0; - - /* disable interrupts for IR context */ - reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx)); - - /* remove tasklet */ - ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet); - debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx); - video->ohci_ir_ctx = -1; - } - - /* release the ISO channel */ - if (video->channel != -1) { - u64 chan_mask; - unsigned long flags; - - chan_mask = (u64)1 << video->channel; - - spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); - video->ohci->ISO_channel_usage &= ~(chan_mask); - spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); - - video->channel = -1; - } - - /* free the frame structs */ - for (i = 0; i < DV1394_MAX_FRAMES; i++) { - if (video->frames[i]) - frame_delete(video->frames[i]); - video->frames[i] = NULL; - } - - video->n_frames = 0; - - /* we can't free the DMA buffer unless it is guaranteed that - no more user-space mappings exist */ - - if (free_dv_buf) { - dma_region_free(&video->dv_buf); - video->dv_buf_size = 0; - } - - /* free packet buffer */ - dma_region_free(&video->packet_buf); - video->packet_buf_size = 0; - - debug_printk("dv1394: shutdown OK\n"); -} - -/* - ********************************** - *** MMAP() THEORY OF OPERATION *** - ********************************** - - The ringbuffer cannot be re-allocated or freed while - a user program maintains a mapping of it. (note that a mapping - can persist even after the device fd is closed!) - - So, only let the user process allocate the DMA buffer once. - To resize or deallocate it, you must close the device file - and open it again. - - Previously Dan M. hacked out a scheme that allowed the DMA - buffer to change by forcefully unmapping it from the user's - address space. It was prone to error because it's very hard to - track all the places the buffer could have been mapped (we - would have had to walk the vma list of every process in the - system to be sure we found all the mappings!). Instead, we - force the user to choose one buffer size and stick with - it. This small sacrifice is worth the huge reduction in - error-prone code in dv1394. -*/ - -static int dv1394_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_card *video = file_to_video_card(file); - int retval = -EINVAL; - - /* - * We cannot use the blocking variant mutex_lock here because .mmap - * is called with mmap_sem held, while .ioctl, .read, .write acquire - * video->mtx and subsequently call copy_to/from_user which will - * grab mmap_sem in case of a page fault. - */ - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - - if ( ! video_card_initialized(video) ) { - retval = do_dv1394_init_default(video); - if (retval) - goto out; - } - - retval = dma_region_mmap(&video->dv_buf, file, vma); -out: - mutex_unlock(&video->mtx); - return retval; -} - -/*** DEVICE FILE INTERFACE *************************************************/ - -/* no need to serialize, multiple threads OK */ -static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_card *video = file_to_video_card(file); - unsigned int mask = 0; - unsigned long flags; - - poll_wait(file, &video->waitq, wait); - - spin_lock_irqsave(&video->spinlock, flags); - if ( video->n_frames == 0 ) { - - } else if ( video->active_frame == -1 ) { - /* nothing going on */ - mask |= POLLOUT; - } else { - /* any clear/ready buffers? */ - if (video->n_clear_frames >0) - mask |= POLLOUT | POLLIN; - } - spin_unlock_irqrestore(&video->spinlock, flags); - - return mask; -} - -static int dv1394_fasync(int fd, struct file *file, int on) -{ - /* I just copied this code verbatim from Alan Cox's mouse driver example - (Documentation/DocBook/) */ - - struct video_card *video = file_to_video_card(file); - - return fasync_helper(fd, file, on, &video->fasync); -} - -static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct video_card *video = file_to_video_card(file); - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - size_t cnt; - unsigned long flags; - int target_frame; - - /* serialize this to prevent multi-threaded mayhem */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&video->mtx)) - return -ERESTARTSYS; - } - - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) { - mutex_unlock(&video->mtx); - return ret; - } - } - - ret = 0; - add_wait_queue(&video->waitq, &wait); - - while (count > 0) { - - /* must set TASK_INTERRUPTIBLE *before* checking for free - buffers; otherwise we could miss a wakeup if the interrupt - fires between the check and the schedule() */ - - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - target_frame = video->first_clear_frame; - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (video->frames[target_frame]->state == FRAME_CLEAR) { - - /* how much room is left in the target frame buffer */ - cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); - - } else { - /* buffer is already used */ - cnt = 0; - } - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - /* no room left, gotta wait */ - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - - schedule(); - - continue; /* start over from 'while(count > 0)...' */ - } - - if (copy_from_user(video->dv_buf.kvirt + video->write_off, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - - video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); - - count -= cnt; - buffer += cnt; - ret += cnt; - - if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) - frame_prepare(video, target_frame); - } - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - mutex_unlock(&video->mtx); - return ret; -} - - -static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct video_card *video = file_to_video_card(file); - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - size_t cnt; - unsigned long flags; - int target_frame; - - /* serialize this to prevent multi-threaded mayhem */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&video->mtx)) - return -ERESTARTSYS; - } - - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) { - mutex_unlock(&video->mtx); - return ret; - } - video->continuity_counter = -1; - - receive_packets(video); - - start_dma_receive(video); - } - - ret = 0; - add_wait_queue(&video->waitq, &wait); - - while (count > 0) { - - /* must set TASK_INTERRUPTIBLE *before* checking for free - buffers; otherwise we could miss a wakeup if the interrupt - fires between the check and the schedule() */ - - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - target_frame = video->first_clear_frame; - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (target_frame >= 0 && - video->n_clear_frames > 0 && - video->frames[target_frame]->state == FRAME_CLEAR) { - - /* how much room is left in the target frame buffer */ - cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); - - } else { - /* buffer is already used */ - cnt = 0; - } - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - /* no room left, gotta wait */ - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - - schedule(); - - continue; /* start over from 'while(count > 0)...' */ - } - - if (copy_to_user(buffer, video->dv_buf.kvirt + video->write_off, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - - video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); - - count -= cnt; - buffer += cnt; - ret += cnt; - - if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) { - spin_lock_irqsave(&video->spinlock, flags); - video->n_clear_frames--; - video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; - spin_unlock_irqrestore(&video->spinlock, flags); - } - } - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - mutex_unlock(&video->mtx); - return ret; -} - - -/*** DEVICE IOCTL INTERFACE ************************************************/ - -static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video_card *video = file_to_video_card(file); - unsigned long flags; - int ret = -EINVAL; - void __user *argp = (void __user *)arg; - - DECLARE_WAITQUEUE(wait, current); - - /* serialize this to prevent multi-threaded mayhem */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&video->mtx)) - return -ERESTARTSYS; - } - - switch(cmd) - { - case DV1394_IOC_SUBMIT_FRAMES: { - unsigned int n_submit; - - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) - goto out; - } - - n_submit = (unsigned int) arg; - - if (n_submit > video->n_frames) { - ret = -EINVAL; - goto out; - } - - while (n_submit > 0) { - - add_wait_queue(&video->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - /* wait until video->first_clear_frame is really CLEAR */ - while (video->frames[video->first_clear_frame]->state != FRAME_CLEAR) { - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (signal_pending(current)) { - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - ret = -EINTR; - goto out; - } - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - } - spin_unlock_irqrestore(&video->spinlock, flags); - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - - frame_prepare(video, video->first_clear_frame); - - n_submit--; - } - - ret = 0; - break; - } - - case DV1394_IOC_WAIT_FRAMES: { - unsigned int n_wait; - - if ( !video_card_initialized(video) ) { - ret = -EINVAL; - goto out; - } - - n_wait = (unsigned int) arg; - - /* since we re-run the last frame on underflow, we will - never actually have n_frames clear frames; at most only - n_frames - 1 */ - - if (n_wait > (video->n_frames-1) ) { - ret = -EINVAL; - goto out; - } - - add_wait_queue(&video->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - while (video->n_clear_frames < n_wait) { - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (signal_pending(current)) { - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - ret = -EINTR; - goto out; - } - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - } - - spin_unlock_irqrestore(&video->spinlock, flags); - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - ret = 0; - break; - } - - case DV1394_IOC_RECEIVE_FRAMES: { - unsigned int n_recv; - - if ( !video_card_initialized(video) ) { - ret = -EINVAL; - goto out; - } - - n_recv = (unsigned int) arg; - - /* at least one frame must be active */ - if (n_recv > (video->n_frames-1) ) { - ret = -EINVAL; - goto out; - } - - spin_lock_irqsave(&video->spinlock, flags); - - /* release the clear frames */ - video->n_clear_frames -= n_recv; - - /* advance the clear frame cursor */ - video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames; - - /* reset dropped_frames */ - video->dropped_frames = 0; - - spin_unlock_irqrestore(&video->spinlock, flags); - - ret = 0; - break; - } - - case DV1394_IOC_START_RECEIVE: { - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) - goto out; - } - - video->continuity_counter = -1; - - receive_packets(video); - - start_dma_receive(video); - - ret = 0; - break; - } - - case DV1394_IOC_INIT: { - struct dv1394_init init; - if (!argp) { - ret = do_dv1394_init_default(video); - } else { - if (copy_from_user(&init, argp, sizeof(init))) { - ret = -EFAULT; - goto out; - } - ret = do_dv1394_init(video, &init); - } - break; - } - - case DV1394_IOC_SHUTDOWN: - do_dv1394_shutdown(video, 0); - ret = 0; - break; - - - case DV1394_IOC_GET_STATUS: { - struct dv1394_status status; - - if ( !video_card_initialized(video) ) { - ret = -EINVAL; - goto out; - } - - status.init.api_version = DV1394_API_VERSION; - status.init.channel = video->channel; - status.init.n_frames = video->n_frames; - status.init.format = video->pal_or_ntsc; - status.init.cip_n = video->cip_n; - status.init.cip_d = video->cip_d; - status.init.syt_offset = video->syt_offset; - - status.first_clear_frame = video->first_clear_frame; - - /* the rest of the fields need to be locked against the interrupt */ - spin_lock_irqsave(&video->spinlock, flags); - - status.active_frame = video->active_frame; - status.n_clear_frames = video->n_clear_frames; - - status.dropped_frames = video->dropped_frames; - - /* reset dropped_frames */ - video->dropped_frames = 0; - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (copy_to_user(argp, &status, sizeof(status))) { - ret = -EFAULT; - goto out; - } - - ret = 0; - break; - } - - default: - break; - } - - out: - mutex_unlock(&video->mtx); - return ret; -} - -/*** DEVICE FILE INTERFACE CONTINUED ***************************************/ - -static int dv1394_open(struct inode *inode, struct file *file) -{ - struct video_card *video = NULL; - - if (file->private_data) { - video = file->private_data; - - } else { - /* look up the card by ID */ - unsigned long flags; - int idx = ieee1394_file_to_instance(file); - - spin_lock_irqsave(&dv1394_cards_lock, flags); - if (!list_empty(&dv1394_cards)) { - struct video_card *p; - list_for_each_entry(p, &dv1394_cards, list) { - if ((p->id) == idx) { - video = p; - break; - } - } - } - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - if (!video) { - debug_printk("dv1394: OHCI card %d not found", idx); - return -ENODEV; - } - - file->private_data = (void*) video; - } - -#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN - - if ( test_and_set_bit(0, &video->open) ) { - /* video is already open by someone else */ - return -EBUSY; - } - -#endif - - printk(KERN_INFO "%s: NOTE, the dv1394 interface is unsupported " - "and will not be available in the new firewire driver stack. " - "Try libraw1394 based programs instead.\n", current->comm); - - return nonseekable_open(inode, file); -} - - -static int dv1394_release(struct inode *inode, struct file *file) -{ - struct video_card *video = file_to_video_card(file); - - /* OK to free the DMA buffer, no more mappings can exist */ - do_dv1394_shutdown(video, 1); - - /* give someone else a turn */ - clear_bit(0, &video->open); - - return 0; -} - - -/*** DEVICE DRIVER HANDLERS ************************************************/ - -static void it_tasklet_func(unsigned long data) -{ - int wake = 0; - struct video_card *video = (struct video_card*) data; - - spin_lock(&video->spinlock); - - if (!video->dma_running) - goto out; - - irq_printk("ContextControl = %08x, CommandPtr = %08x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) - ); - - - if ( (video->ohci_it_ctx != -1) && - (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { - - struct frame *f; - unsigned int frame, i; - - - if (video->active_frame == -1) - frame = 0; - else - frame = video->active_frame; - - /* check all the DMA-able frames */ - for (i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) { - - irq_printk("IRQ checking frame %d...", frame); - f = video->frames[frame]; - if (f->state != FRAME_READY) { - irq_printk("clear, skipping\n"); - /* we don't own this frame */ - continue; - } - - irq_printk("DMA\n"); - - /* check the frame begin semaphore to see if we can free the previous frame */ - if ( *(f->frame_begin_timestamp) ) { - int prev_frame; - struct frame *prev_f; - - - - /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */ - irq_printk(" BEGIN\n"); - - prev_frame = frame - 1; - if (prev_frame == -1) - prev_frame += video->n_frames; - prev_f = video->frames[prev_frame]; - - /* make sure we can actually garbage collect - this frame */ - if ( (prev_f->state == FRAME_READY) && - prev_f->done && (!f->done) ) - { - frame_reset(prev_f); - video->n_clear_frames++; - wake = 1; - video->active_frame = frame; - - irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame); - } else { - irq_printk(" BEGIN - can't free yet\n"); - } - - f->done = 1; - } - - - /* see if we need to set the timestamp for the next frame */ - if ( *(f->mid_frame_timestamp) ) { - struct frame *next_frame; - u32 begin_ts, ts_cyc, ts_off; - - *(f->mid_frame_timestamp) = 0; - - begin_ts = le32_to_cpu(*(f->frame_begin_timestamp)); - - irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n", - begin_ts & 0x1FFF, begin_ts & 0xF, - f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF); - - /* prepare next frame and assign timestamp */ - next_frame = video->frames[ (frame+1) % video->n_frames ]; - - if (next_frame->state == FRAME_READY) { - irq_printk(" MIDDLE - next frame is ready, good\n"); - } else { - debug_printk("dv1394: Underflow! At least one frame has been dropped.\n"); - next_frame = f; - } - - /* set the timestamp to the timestamp of the last frame sent, - plus the length of the last frame sent, plus the syt latency */ - ts_cyc = begin_ts & 0xF; - /* advance one frame, plus syt latency (typically 2-3) */ - ts_cyc += f->n_packets + video->syt_offset ; - - ts_off = 0; - - ts_cyc += ts_off/3072; - ts_off %= 3072; - - next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off; - if (next_frame->cip_syt1) { - next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8; - next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF; - } - if (next_frame->cip_syt2) { - next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8; - next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF; - } - - } - - /* see if the frame looped */ - if ( *(f->frame_end_timestamp) ) { - - *(f->frame_end_timestamp) = 0; - - debug_printk(" END - the frame looped at least once\n"); - - video->dropped_frames++; - } - - } /* for (each frame) */ - } - - if (wake) { - kill_fasync(&video->fasync, SIGIO, POLL_OUT); - - /* wake readers/writers/ioctl'ers */ - wake_up_interruptible(&video->waitq); - } - -out: - spin_unlock(&video->spinlock); -} - -static void ir_tasklet_func(unsigned long data) -{ - int wake = 0; - struct video_card *video = (struct video_card*) data; - - spin_lock(&video->spinlock); - - if (!video->dma_running) - goto out; - - if ( (video->ohci_ir_ctx != -1) && - (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) { - - int sof=0; /* start-of-frame flag */ - struct frame *f; - u16 packet_length; - int i, dbc=0; - struct DMA_descriptor_block *block = NULL; - u16 xferstatus; - - int next_i, prev_i; - struct DMA_descriptor_block *next = NULL; - dma_addr_t next_dma = 0; - struct DMA_descriptor_block *prev = NULL; - - /* loop over all descriptors in all frames */ - for (i = 0; i < video->n_frames*MAX_PACKETS; i++) { - struct packet *p = dma_region_i(&video->packet_buf, struct packet, video->current_packet); - - /* make sure we are seeing the latest changes to p */ - dma_region_sync_for_cpu(&video->packet_buf, - (unsigned long) p - (unsigned long) video->packet_buf.kvirt, - sizeof(struct packet)); - - packet_length = le16_to_cpu(p->data_length); - - /* get the descriptor based on packet_buffer cursor */ - f = video->frames[video->current_packet / MAX_PACKETS]; - block = &(f->descriptor_pool[video->current_packet % MAX_PACKETS]); - xferstatus = le32_to_cpu(block->u.in.il.q[3]) >> 16; - xferstatus &= 0x1F; - irq_printk("ir_tasklet_func: xferStatus/resCount [%d] = 0x%08x\n", i, le32_to_cpu(block->u.in.il.q[3]) ); - - /* get the current frame */ - f = video->frames[video->active_frame]; - - /* exclude empty packet */ - if (packet_length > 8 && xferstatus == 0x11) { - /* check for start of frame */ - /* DRD> Changed to check section type ([0]>>5==0) - and dif sequence ([1]>>4==0) */ - sof = ( (p->data[0] >> 5) == 0 && (p->data[1] >> 4) == 0); - - dbc = (int) (p->cip_h1 >> 24); - if ( video->continuity_counter != -1 && dbc > ((video->continuity_counter + 1) % 256) ) - { - printk(KERN_WARNING "dv1394: discontinuity detected, dropping all frames\n" ); - video->dropped_frames += video->n_clear_frames + 1; - video->first_frame = 0; - video->n_clear_frames = 0; - video->first_clear_frame = -1; - } - video->continuity_counter = dbc; - - if (!video->first_frame) { - if (sof) { - video->first_frame = 1; - } - - } else if (sof) { - /* close current frame */ - frame_reset(f); /* f->state = STATE_CLEAR */ - video->n_clear_frames++; - if (video->n_clear_frames > video->n_frames) { - video->dropped_frames++; - printk(KERN_WARNING "dv1394: dropped a frame during reception\n" ); - video->n_clear_frames = video->n_frames-1; - video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; - } - if (video->first_clear_frame == -1) - video->first_clear_frame = video->active_frame; - - /* get the next frame */ - video->active_frame = (video->active_frame + 1) % video->n_frames; - f = video->frames[video->active_frame]; - irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n", - video->active_frame, video->n_clear_frames, video->first_clear_frame); - } - if (video->first_frame) { - if (sof) { - /* open next frame */ - f->state = FRAME_READY; - } - - /* copy to buffer */ - if (f->n_packets > (video->frame_size / 480)) { - printk(KERN_ERR "frame buffer overflow during receive\n"); - } - - frame_put_packet(f, p); - - } /* first_frame */ - } - - /* stop, end of ready packets */ - else if (xferstatus == 0) { - break; - } - - /* reset xferStatus & resCount */ - block->u.in.il.q[3] = cpu_to_le32(512); - - /* terminate dma chain at this (next) packet */ - next_i = video->current_packet; - f = video->frames[next_i / MAX_PACKETS]; - next = &(f->descriptor_pool[next_i % MAX_PACKETS]); - next_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; - next->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */ - next->u.in.il.q[2] = cpu_to_le32(0); /* disable branch */ - - /* link previous to next */ - prev_i = (next_i == 0) ? (MAX_PACKETS * video->n_frames - 1) : (next_i - 1); - f = video->frames[prev_i / MAX_PACKETS]; - prev = &(f->descriptor_pool[prev_i % MAX_PACKETS]); - if (prev_i % (MAX_PACKETS/2)) { - prev->u.in.il.q[0] &= ~cpu_to_le32(3 << 20); /* no interrupt */ - } else { - prev->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */ - } - prev->u.in.il.q[2] = cpu_to_le32(next_dma | 1); /* set Z=1 */ - wmb(); - - /* wake up DMA in case it fell asleep */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); - - /* advance packet_buffer cursor */ - video->current_packet = (video->current_packet + 1) % (MAX_PACKETS * video->n_frames); - - } /* for all packets */ - - wake = 1; /* why the hell not? */ - - } /* receive interrupt */ - - if (wake) { - kill_fasync(&video->fasync, SIGIO, POLL_IN); - - /* wake readers/writers/ioctl'ers */ - wake_up_interruptible(&video->waitq); - } - -out: - spin_unlock(&video->spinlock); -} - -static struct cdev dv1394_cdev; -static const struct file_operations dv1394_fops= -{ - .owner = THIS_MODULE, - .poll = dv1394_poll, - .unlocked_ioctl = dv1394_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = dv1394_compat_ioctl, -#endif - .mmap = dv1394_mmap, - .open = dv1394_open, - .write = dv1394_write, - .read = dv1394_read, - .release = dv1394_release, - .fasync = dv1394_fasync, - .llseek = no_llseek, -}; - - -/*** HOTPLUG STUFF **********************************************************/ -/* - * Export information about protocols/devices supported by this driver. - */ -#ifdef MODULE -static const struct ieee1394_device_id dv1394_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = AVC_SW_VERSION_ENTRY & 0xffffff - }, - { } -}; - -MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table); -#endif /* MODULE */ - -static struct hpsb_protocol_driver dv1394_driver = { - .name = "dv1394", -}; - - -/*** IEEE1394 HPSB CALLBACKS ***********************************************/ - -static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes mode) -{ - struct video_card *video; - unsigned long flags; - int i; - - video = kzalloc(sizeof(*video), GFP_KERNEL); - if (!video) { - printk(KERN_ERR "dv1394: cannot allocate video_card\n"); - return -1; - } - - video->ohci = ohci; - /* lower 2 bits of id indicate which of four "plugs" - per host */ - video->id = ohci->host->id << 2; - if (format == DV1394_NTSC) - video->id |= mode; - else - video->id |= 2 + mode; - - video->ohci_it_ctx = -1; - video->ohci_ir_ctx = -1; - - video->ohci_IsoXmitContextControlSet = 0; - video->ohci_IsoXmitContextControlClear = 0; - video->ohci_IsoXmitCommandPtr = 0; - - video->ohci_IsoRcvContextControlSet = 0; - video->ohci_IsoRcvContextControlClear = 0; - video->ohci_IsoRcvCommandPtr = 0; - video->ohci_IsoRcvContextMatch = 0; - - video->n_frames = 0; /* flag that video is not initialized */ - video->channel = 63; /* default to broadcast channel */ - video->active_frame = -1; - - /* initialize the following */ - video->pal_or_ntsc = format; - video->cip_n = 0; /* 0 = use builtin default */ - video->cip_d = 0; - video->syt_offset = 0; - video->mode = mode; - - for (i = 0; i < DV1394_MAX_FRAMES; i++) - video->frames[i] = NULL; - - dma_region_init(&video->dv_buf); - video->dv_buf_size = 0; - dma_region_init(&video->packet_buf); - video->packet_buf_size = 0; - - clear_bit(0, &video->open); - spin_lock_init(&video->spinlock); - video->dma_running = 0; - mutex_init(&video->mtx); - init_waitqueue_head(&video->waitq); - video->fasync = NULL; - - spin_lock_irqsave(&dv1394_cards_lock, flags); - INIT_LIST_HEAD(&video->list); - list_add_tail(&video->list, &dv1394_cards); - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id); - return 0; -} - -static void dv1394_remove_host(struct hpsb_host *host) -{ - struct video_card *video, *tmp_video; - unsigned long flags; - int found_ohci_card = 0; - - do { - video = NULL; - spin_lock_irqsave(&dv1394_cards_lock, flags); - list_for_each_entry(tmp_video, &dv1394_cards, list) { - if ((tmp_video->id >> 2) == host->id) { - list_del(&tmp_video->list); - video = tmp_video; - found_ohci_card = 1; - break; - } - } - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - if (video) { - do_dv1394_shutdown(video, 1); - kfree(video); - } - } while (video); - - if (found_ohci_card) - device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_DV1394 * 16 + (host->id << 2))); -} - -static void dv1394_add_host(struct hpsb_host *host) -{ - struct ti_ohci *ohci; - int id = host->id; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - ohci = (struct ti_ohci *)host->hostdata; - - device_create(hpsb_protocol_class, NULL, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), - NULL, "dv1394-%d", id); - - dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE); - dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT); - dv1394_init(ohci, DV1394_PAL, MODE_RECEIVE); - dv1394_init(ohci, DV1394_PAL, MODE_TRANSMIT); -} - - -/* Bus reset handler. In the event of a bus reset, we may need to - re-start the DMA contexts - otherwise the user program would - end up waiting forever. -*/ - -static void dv1394_host_reset(struct hpsb_host *host) -{ - struct video_card *video = NULL, *tmp_vid; - unsigned long flags; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - /* find the corresponding video_cards */ - spin_lock_irqsave(&dv1394_cards_lock, flags); - list_for_each_entry(tmp_vid, &dv1394_cards, list) { - if ((tmp_vid->id >> 2) == host->id) { - video = tmp_vid; - break; - } - } - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - if (!video) - return; - - - spin_lock_irqsave(&video->spinlock, flags); - - if (!video->dma_running) - goto out; - - /* check IT context */ - if (video->ohci_it_ctx != -1) { - u32 ctx; - - ctx = reg_read(video->ohci, video->ohci_IsoXmitContextControlSet); - - /* if (RUN but not ACTIVE) */ - if ( (ctx & (1<<15)) && - !(ctx & (1<<10)) ) { - - debug_printk("dv1394: IT context stopped due to bus reset; waking it up\n"); - - /* to be safe, assume a frame has been dropped. User-space programs - should handle this condition like an underflow. */ - video->dropped_frames++; - - /* for some reason you must clear, then re-set the RUN bit to restart DMA */ - - /* clear RUN */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); - flush_pci_write(video->ohci); - - /* set RUN */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 15)); - flush_pci_write(video->ohci); - - /* set the WAKE bit (just in case; this isn't strictly necessary) */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 12)); - flush_pci_write(video->ohci); - - irq_printk("dv1394: AFTER IT restart ctx 0x%08x ptr 0x%08x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); - } - } - - /* check IR context */ - if (video->ohci_ir_ctx != -1) { - u32 ctx; - - ctx = reg_read(video->ohci, video->ohci_IsoRcvContextControlSet); - - /* if (RUN but not ACTIVE) */ - if ( (ctx & (1<<15)) && - !(ctx & (1<<10)) ) { - - debug_printk("dv1394: IR context stopped due to bus reset; waking it up\n"); - - /* to be safe, assume a frame has been dropped. User-space programs - should handle this condition like an overflow. */ - video->dropped_frames++; - - /* for some reason you must clear, then re-set the RUN bit to restart DMA */ - /* XXX this doesn't work for me, I can't get IR DMA to restart :[ */ - - /* clear RUN */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); - flush_pci_write(video->ohci); - - /* set RUN */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 15)); - flush_pci_write(video->ohci); - - /* set the WAKE bit (just in case; this isn't strictly necessary) */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); - flush_pci_write(video->ohci); - - irq_printk("dv1394: AFTER IR restart ctx 0x%08x ptr 0x%08x\n", - reg_read(video->ohci, video->ohci_IsoRcvContextControlSet), - reg_read(video->ohci, video->ohci_IsoRcvCommandPtr)); - } - } - -out: - spin_unlock_irqrestore(&video->spinlock, flags); - - /* wake readers/writers/ioctl'ers */ - wake_up_interruptible(&video->waitq); -} - -static struct hpsb_highlevel dv1394_highlevel = { - .name = "dv1394", - .add_host = dv1394_add_host, - .remove_host = dv1394_remove_host, - .host_reset = dv1394_host_reset, -}; - -#ifdef CONFIG_COMPAT - -#define DV1394_IOC32_INIT _IOW('#', 0x06, struct dv1394_init32) -#define DV1394_IOC32_GET_STATUS _IOR('#', 0x0c, struct dv1394_status32) - -struct dv1394_init32 { - u32 api_version; - u32 channel; - u32 n_frames; - u32 format; - u32 cip_n; - u32 cip_d; - u32 syt_offset; -}; - -struct dv1394_status32 { - struct dv1394_init32 init; - s32 active_frame; - u32 first_clear_frame; - u32 n_clear_frames; - u32 dropped_frames; -}; - -/* RED-PEN: this should use compat_alloc_userspace instead */ - -static int handle_dv1394_init(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct dv1394_init32 dv32; - struct dv1394_init dv; - mm_segment_t old_fs; - int ret; - - if (file->f_op->unlocked_ioctl != dv1394_ioctl) - return -EFAULT; - - if (copy_from_user(&dv32, (void __user *)arg, sizeof(dv32))) - return -EFAULT; - - dv.api_version = dv32.api_version; - dv.channel = dv32.channel; - dv.n_frames = dv32.n_frames; - dv.format = dv32.format; - dv.cip_n = (unsigned long)dv32.cip_n; - dv.cip_d = (unsigned long)dv32.cip_d; - dv.syt_offset = dv32.syt_offset; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = dv1394_ioctl(file, DV1394_IOC_INIT, (unsigned long)&dv); - set_fs(old_fs); - - return ret; -} - -static int handle_dv1394_get_status(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct dv1394_status32 dv32; - struct dv1394_status dv; - mm_segment_t old_fs; - int ret; - - if (file->f_op->unlocked_ioctl != dv1394_ioctl) - return -EFAULT; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = dv1394_ioctl(file, DV1394_IOC_GET_STATUS, (unsigned long)&dv); - set_fs(old_fs); - - if (!ret) { - dv32.init.api_version = dv.init.api_version; - dv32.init.channel = dv.init.channel; - dv32.init.n_frames = dv.init.n_frames; - dv32.init.format = dv.init.format; - dv32.init.cip_n = (u32)dv.init.cip_n; - dv32.init.cip_d = (u32)dv.init.cip_d; - dv32.init.syt_offset = dv.init.syt_offset; - dv32.active_frame = dv.active_frame; - dv32.first_clear_frame = dv.first_clear_frame; - dv32.n_clear_frames = dv.n_clear_frames; - dv32.dropped_frames = dv.dropped_frames; - - if (copy_to_user((struct dv1394_status32 __user *)arg, &dv32, sizeof(dv32))) - ret = -EFAULT; - } - - return ret; -} - - - -static long dv1394_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - switch (cmd) { - case DV1394_IOC_SHUTDOWN: - case DV1394_IOC_SUBMIT_FRAMES: - case DV1394_IOC_WAIT_FRAMES: - case DV1394_IOC_RECEIVE_FRAMES: - case DV1394_IOC_START_RECEIVE: - return dv1394_ioctl(file, cmd, arg); - - case DV1394_IOC32_INIT: - return handle_dv1394_init(file, cmd, arg); - case DV1394_IOC32_GET_STATUS: - return handle_dv1394_get_status(file, cmd, arg); - default: - return -ENOIOCTLCMD; - } -} - -#endif /* CONFIG_COMPAT */ - - -/*** KERNEL MODULE HANDLERS ************************************************/ - -MODULE_AUTHOR("Dan Maas <dmaas@dcine.com>, Dan Dennedy <dan@dennedy.org>"); -MODULE_DESCRIPTION("driver for DV input/output on OHCI board"); -MODULE_SUPPORTED_DEVICE("dv1394"); -MODULE_LICENSE("GPL"); - -static void __exit dv1394_exit_module(void) -{ - hpsb_unregister_protocol(&dv1394_driver); - hpsb_unregister_highlevel(&dv1394_highlevel); - cdev_del(&dv1394_cdev); -} - -static int __init dv1394_init_module(void) -{ - int ret; - - cdev_init(&dv1394_cdev, &dv1394_fops); - dv1394_cdev.owner = THIS_MODULE; - ret = cdev_add(&dv1394_cdev, IEEE1394_DV1394_DEV, 16); - if (ret) { - printk(KERN_ERR "dv1394: unable to register character device\n"); - return ret; - } - - hpsb_register_highlevel(&dv1394_highlevel); - - ret = hpsb_register_protocol(&dv1394_driver); - if (ret) { - printk(KERN_ERR "dv1394: failed to register protocol\n"); - hpsb_unregister_highlevel(&dv1394_highlevel); - cdev_del(&dv1394_cdev); - return ret; - } - - return 0; -} - -module_init(dv1394_init_module); -module_exit(dv1394_exit_module); diff --git a/drivers/ieee1394/dv1394.h b/drivers/ieee1394/dv1394.h deleted file mode 100644 index 5807f528981..00000000000 --- a/drivers/ieee1394/dv1394.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * dv1394.h - DV input/output over IEEE 1394 on OHCI chips - * Copyright (C)2001 Daniel Maas <dmaas@dcine.com> - * receive by Dan Dennedy <dan@dennedy.org> - * - * based on: - * video1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * Peter Schlaile <udbz@rz.uni-karlsruhe.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _DV_1394_H -#define _DV_1394_H - -/* This is the public user-space interface. Try not to break it. */ - -#define DV1394_API_VERSION 0x20011127 - -/* ******************** - ** ** - ** DV1394 API ** - ** ** - ******************** - - There are two methods of operating the DV1394 DV output device. - - 1) - - The simplest is an interface based on write(): simply write - full DV frames of data to the device, and they will be transmitted - as quickly as possible. The FD may be set for non-blocking I/O, - in which case you can use select() or poll() to wait for output - buffer space. - - To set the DV output parameters (e.g. whether you want NTSC or PAL - video), use the DV1394_INIT ioctl, passing in the parameters you - want in a struct dv1394_init. - - Example 1: - To play a raw .DV file: cat foo.DV > /dev/dv1394 - (cat will use write() internally) - - Example 2: - static struct dv1394_init init = { - 0x63, (broadcast channel) - 4, (four-frame ringbuffer) - DV1394_NTSC, (send NTSC video) - 0, 0 (default empty packet rate) - } - - ioctl(fd, DV1394_INIT, &init); - - while (1) { - read( <a raw DV file>, buf, DV1394_NTSC_FRAME_SIZE ); - write( <the dv1394 FD>, buf, DV1394_NTSC_FRAME_SIZE ); - } - - 2) - - For more control over buffering, and to avoid unnecessary copies - of the DV data, you can use the more sophisticated the mmap() interface. - First, call the DV1394_INIT ioctl to specify your parameters, - including the number of frames in the ringbuffer. Then, calling mmap() - on the dv1394 device will give you direct access to the ringbuffer - from which the DV card reads your frame data. - - The ringbuffer is simply one large, contiguous region of memory - containing two or more frames of packed DV data. Each frame of DV data - is 120000 bytes (NTSC) or 144000 bytes (PAL). - - Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES - ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl - or select()/poll() to wait until the frames are transmitted. Next, you'll - need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer - frames are clear (ready to be filled with new DV data). Finally, use - DV1394_SUBMIT_FRAMES again to send the new data to the DV output. - - - Example: here is what a four-frame ringbuffer might look like - during DV transmission: - - - frame 0 frame 1 frame 2 frame 3 - - *--------------------------------------* - | CLEAR | DV data | DV data | CLEAR | - *--------------------------------------* - <ACTIVE> - - transmission goes in this direction --->>> - - - The DV hardware is currently transmitting the data in frame 1. - Once frame 1 is finished, it will automatically transmit frame 2. - (if frame 2 finishes before frame 3 is submitted, the device - will continue to transmit frame 2, and will increase the dropped_frames - counter each time it repeats the transmission). - - - If you called DV1394_GET_STATUS at this instant, you would - receive the following values: - - n_frames = 4 - active_frame = 1 - first_clear_frame = 3 - n_clear_frames = 2 - - At this point, you should write new DV data into frame 3 and optionally - frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that - it may transmit the new frames. - - ERROR HANDLING - - An error (buffer underflow/overflow or a break in the DV stream due - to a 1394 bus reset) can be detected by checking the dropped_frames - field of struct dv1394_status (obtained through the - DV1394_GET_STATUS ioctl). - - The best way to recover from such an error is to re-initialize - dv1394, either by using the DV1394_INIT ioctl call, or closing the - file descriptor and opening it again. (note that you must unmap all - ringbuffer mappings when closing the file descriptor, or else - dv1394 will still be considered 'in use'). - - MAIN LOOP - - For maximum efficiency and robustness against bus errors, you are - advised to model the main loop of your application after the - following pseudo-code example: - - (checks of system call return values omitted for brevity; always - check return values in your code!) - - while ( frames left ) { - - struct pollfd *pfd = ...; - - pfd->fd = dv1394_fd; - pfd->revents = 0; - pfd->events = POLLOUT | POLLIN; (OUT for transmit, IN for receive) - - (add other sources of I/O here) - - poll(pfd, 1, -1); (or select(); add a timeout if you want) - - if (pfd->revents) { - struct dv1394_status status; - - ioctl(dv1394_fd, DV1394_GET_STATUS, &status); - - if (status.dropped_frames > 0) { - reset_dv1394(); - } else { - for (int i = 0; i < status.n_clear_frames; i++) { - copy_DV_frame(); - } - } - } - } - - where copy_DV_frame() reads or writes on the dv1394 file descriptor - (read/write mode) or copies data to/from the mmap ringbuffer and - then calls ioctl(DV1394_SUBMIT_FRAMES) to notify dv1394 that new - frames are availble (mmap mode). - - reset_dv1394() is called in the event of a buffer - underflow/overflow or a halt in the DV stream (e.g. due to a 1394 - bus reset). To guarantee recovery from the error, this function - should close the dv1394 file descriptor (and munmap() all - ringbuffer mappings, if you are using them), then re-open the - dv1394 device (and re-map the ringbuffer). - -*/ - - -/* maximum number of frames in the ringbuffer */ -#define DV1394_MAX_FRAMES 32 - -/* number of *full* isochronous packets per DV frame */ -#define DV1394_NTSC_PACKETS_PER_FRAME 250 -#define DV1394_PAL_PACKETS_PER_FRAME 300 - -/* size of one frame's worth of DV data, in bytes */ -#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME) -#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME) - - -/* ioctl() commands */ -#include "ieee1394-ioctl.h" - - -enum pal_or_ntsc { - DV1394_NTSC = 0, - DV1394_PAL -}; - - - - -/* this is the argument to DV1394_INIT */ -struct dv1394_init { - /* DV1394_API_VERSION */ - unsigned int api_version; - - /* isochronous transmission channel to use */ - unsigned int channel; - - /* number of frames in the ringbuffer. Must be at least 2 - and at most DV1394_MAX_FRAMES. */ - unsigned int n_frames; - - /* send/receive PAL or NTSC video format */ - enum pal_or_ntsc format; - - /* the following are used only for transmission */ - - /* set these to zero unless you want a - non-default empty packet rate (see below) */ - unsigned long cip_n; - unsigned long cip_d; - - /* set this to zero unless you want a - non-default SYT cycle offset (default = 3 cycles) */ - unsigned int syt_offset; -}; - -/* NOTE: you may only allocate the DV frame ringbuffer once each time - you open the dv1394 device. DV1394_INIT will fail if you call it a - second time with different 'n_frames' or 'format' arguments (which - would imply a different size for the ringbuffer). If you need a - different buffer size, simply close and re-open the device, then - initialize it with your new settings. */ - -/* Q: What are cip_n and cip_d? */ - -/* - A: DV video streams do not utilize 100% of the potential bandwidth offered - by IEEE 1394 (FireWire). To achieve the correct rate of data transmission, - DV devices must periodically insert empty packets into the 1394 data stream. - Typically there is one empty packet per 14-16 data-carrying packets. - - Some DV devices will accept a wide range of empty packet rates, while others - require a precise rate. If the dv1394 driver produces empty packets at - a rate that your device does not accept, you may see ugly patterns on the - DV output, or even no output at all. - - The default empty packet insertion rate seems to work for many people; if - your DV output is stable, you can simply ignore this discussion. However, - we have exposed the empty packet rate as a parameter to support devices that - do not work with the default rate. - - The decision to insert an empty packet is made with a numerator/denominator - algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D. - You can alter the empty packet rate by passing non-zero values for cip_n - and cip_d to the INIT ioctl. - - */ - - - -struct dv1394_status { - /* this embedded init struct returns the current dv1394 - parameters in use */ - struct dv1394_init init; - - /* the ringbuffer frame that is currently being - displayed. (-1 if the device is not transmitting anything) */ - int active_frame; - - /* index of the first buffer (ahead of active_frame) that - is ready to be filled with data */ - unsigned int first_clear_frame; - - /* how many buffers, including first_clear_buffer, are - ready to be filled with data */ - unsigned int n_clear_frames; - - /* how many times the DV stream has underflowed, overflowed, - or otherwise encountered an error, since the previous call - to DV1394_GET_STATUS */ - unsigned int dropped_frames; - - /* N.B. The dropped_frames counter is only a lower bound on the actual - number of dropped frames, with the special case that if dropped_frames - is zero, then it is guaranteed that NO frames have been dropped - since the last call to DV1394_GET_STATUS. - */ -}; - - -#endif /* _DV_1394_H */ diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c deleted file mode 100644 index 63403822330..00000000000 --- a/drivers/ieee1394/eth1394.c +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem - * - * Copyright (C) 2001-2003 Ben Collins <bcollins@debian.org> - * 2000 Bonin Franck <boninf@free.fr> - * 2003 Steve Kinneberg <kinnebergsteve@acmsystems.com> - * - * Mainly based on work by Emanuel Pirker and Andreas E. Bombe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * This driver intends to support RFC 2734, which describes a method for - * transporting IPv4 datagrams over IEEE-1394 serial busses. - * - * TODO: - * RFC 2734 related: - * - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2. - * - * Non-RFC 2734 related: - * - Handle fragmented skb's coming from the networking layer. - * - Move generic GASP reception to core 1394 code - * - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead - * - Stability improvements - * - Performance enhancements - * - Consider garbage collecting old partial datagrams after X amount of time - */ - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/workqueue.h> - -#include <linux/netdevice.h> -#include <linux/inetdevice.h> -#include <linux/if_arp.h> -#include <linux/if_ether.h> -#include <linux/ip.h> -#include <linux/in.h> -#include <linux/tcp.h> -#include <linux/skbuff.h> -#include <linux/bitops.h> -#include <asm/uaccess.h> -#include <asm/delay.h> -#include <asm/unaligned.h> -#include <net/arp.h> - -#include "config_roms.h" -#include "csr1212.h" -#include "eth1394.h" -#include "highlevel.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" -#include "iso.h" -#include "nodemgr.h" - -#define ETH1394_PRINT_G(level, fmt, args...) \ - printk(level "%s: " fmt, driver_name, ## args) - -#define ETH1394_PRINT(level, dev_name, fmt, args...) \ - printk(level "%s: %s: " fmt, driver_name, dev_name, ## args) - -struct fragment_info { - struct list_head list; - int offset; - int len; -}; - -struct partial_datagram { - struct list_head list; - u16 dgl; - u16 dg_size; - __be16 ether_type; - struct sk_buff *skb; - char *pbuf; - struct list_head frag_info; -}; - -struct pdg_list { - struct list_head list; /* partial datagram list per node */ - unsigned int sz; /* partial datagram list size per node */ - spinlock_t lock; /* partial datagram lock */ -}; - -struct eth1394_host_info { - struct hpsb_host *host; - struct net_device *dev; -}; - -struct eth1394_node_ref { - struct unit_directory *ud; - struct list_head list; -}; - -struct eth1394_node_info { - u16 maxpayload; /* max payload */ - u8 sspd; /* max speed */ - u64 fifo; /* FIFO address */ - struct pdg_list pdg; /* partial RX datagram lists */ - int dgl; /* outgoing datagram label */ -}; - -static const char driver_name[] = "eth1394"; - -static struct kmem_cache *packet_task_cache; - -static struct hpsb_highlevel eth1394_highlevel; - -/* Use common.lf to determine header len */ -static const int hdr_type_len[] = { - sizeof(struct eth1394_uf_hdr), - sizeof(struct eth1394_ff_hdr), - sizeof(struct eth1394_sf_hdr), - sizeof(struct eth1394_sf_hdr) -}; - -static const u16 eth1394_speedto_maxpayload[] = { -/* S100, S200, S400, S800, S1600, S3200 */ - 512, 1024, 2048, 4096, 4096, 4096 -}; - -MODULE_AUTHOR("Ben Collins (bcollins@debian.org)"); -MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)"); -MODULE_LICENSE("GPL"); - -/* - * The max_partial_datagrams parameter is the maximum number of fragmented - * datagrams per node that eth1394 will keep in memory. Providing an upper - * bound allows us to limit the amount of memory that partial datagrams - * consume in the event that some partial datagrams are never completed. - */ -static int max_partial_datagrams = 25; -module_param(max_partial_datagrams, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(max_partial_datagrams, - "Maximum number of partially received fragmented datagrams " - "(default = 25)."); - - -static int ether1394_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len); -static int ether1394_rebuild_header(struct sk_buff *skb); -static int ether1394_header_parse(const struct sk_buff *skb, - unsigned char *haddr); -static int ether1394_header_cache(const struct neighbour *neigh, - struct hh_cache *hh); -static void ether1394_header_cache_update(struct hh_cache *hh, - const struct net_device *dev, - const unsigned char *haddr); -static netdev_tx_t ether1394_tx(struct sk_buff *skb, - struct net_device *dev); -static void ether1394_iso(struct hpsb_iso *iso); - -static int ether1394_write(struct hpsb_host *host, int srcid, int destid, - quadlet_t *data, u64 addr, size_t len, u16 flags); -static void ether1394_add_host(struct hpsb_host *host); -static void ether1394_remove_host(struct hpsb_host *host); -static void ether1394_host_reset(struct hpsb_host *host); - -/* Function for incoming 1394 packets */ -static const struct hpsb_address_ops addr_ops = { - .write = ether1394_write, -}; - -/* Ieee1394 highlevel driver functions */ -static struct hpsb_highlevel eth1394_highlevel = { - .name = driver_name, - .add_host = ether1394_add_host, - .remove_host = ether1394_remove_host, - .host_reset = ether1394_host_reset, -}; - -static int ether1394_recv_init(struct eth1394_priv *priv) -{ - unsigned int iso_buf_size; - - /* FIXME: rawiso limits us to PAGE_SIZE */ - iso_buf_size = min((unsigned int)PAGE_SIZE, - 2 * (1U << (priv->host->csr.max_rec + 1))); - - priv->iso = hpsb_iso_recv_init(priv->host, - ETHER1394_GASP_BUFFERS * iso_buf_size, - ETHER1394_GASP_BUFFERS, - priv->broadcast_channel, - HPSB_ISO_DMA_PACKET_PER_BUFFER, - 1, ether1394_iso); - if (priv->iso == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n"); - priv->bc_state = ETHER1394_BC_ERROR; - return -EAGAIN; - } - - if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) - priv->bc_state = ETHER1394_BC_STOPPED; - else - priv->bc_state = ETHER1394_BC_RUNNING; - return 0; -} - -/* This is called after an "ifup" */ -static int ether1394_open(struct net_device *dev) -{ - struct eth1394_priv *priv = netdev_priv(dev); - int ret; - - if (priv->bc_state == ETHER1394_BC_ERROR) { - ret = ether1394_recv_init(priv); - if (ret) - return ret; - } - netif_start_queue(dev); - return 0; -} - -/* This is called after an "ifdown" */ -static int ether1394_stop(struct net_device *dev) -{ - /* flush priv->wake */ - flush_scheduled_work(); - - netif_stop_queue(dev); - return 0; -} - -/* FIXME: What to do if we timeout? I think a host reset is probably in order, - * so that's what we do. Should we increment the stat counters too? */ -static void ether1394_tx_timeout(struct net_device *dev) -{ - struct hpsb_host *host = - ((struct eth1394_priv *)netdev_priv(dev))->host; - - ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n"); - ether1394_host_reset(host); -} - -static inline int ether1394_max_mtu(struct hpsb_host* host) -{ - return (1 << (host->csr.max_rec + 1)) - - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD; -} - -static int ether1394_change_mtu(struct net_device *dev, int new_mtu) -{ - int max_mtu; - - if (new_mtu < 68) - return -EINVAL; - - max_mtu = ether1394_max_mtu( - ((struct eth1394_priv *)netdev_priv(dev))->host); - if (new_mtu > max_mtu) { - ETH1394_PRINT(KERN_INFO, dev->name, - "Local node constrains MTU to %d\n", max_mtu); - return -ERANGE; - } - - dev->mtu = new_mtu; - return 0; -} - -static void purge_partial_datagram(struct list_head *old) -{ - struct partial_datagram *pd; - struct list_head *lh, *n; - struct fragment_info *fi; - - pd = list_entry(old, struct partial_datagram, list); - - list_for_each_safe(lh, n, &pd->frag_info) { - fi = list_entry(lh, struct fragment_info, list); - list_del(lh); - kfree(fi); - } - list_del(old); - kfree_skb(pd->skb); - kfree(pd); -} - -/****************************************** - * 1394 bus activity functions - ******************************************/ - -static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl, - struct unit_directory *ud) -{ - struct eth1394_node_ref *node; - - list_for_each_entry(node, inl, list) - if (node->ud == ud) - return node; - - return NULL; -} - -static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl, - u64 guid) -{ - struct eth1394_node_ref *node; - - list_for_each_entry(node, inl, list) - if (node->ud->ne->guid == guid) - return node; - - return NULL; -} - -static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl, - nodeid_t nodeid) -{ - struct eth1394_node_ref *node; - - list_for_each_entry(node, inl, list) - if (node->ud->ne->nodeid == nodeid) - return node; - - return NULL; -} - -static int eth1394_new_node(struct eth1394_host_info *hi, - struct unit_directory *ud) -{ - struct eth1394_priv *priv; - struct eth1394_node_ref *new_node; - struct eth1394_node_info *node_info; - - new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); - if (!new_node) - return -ENOMEM; - - node_info = kmalloc(sizeof(*node_info), GFP_KERNEL); - if (!node_info) { - kfree(new_node); - return -ENOMEM; - } - - spin_lock_init(&node_info->pdg.lock); - INIT_LIST_HEAD(&node_info->pdg.list); - node_info->pdg.sz = 0; - node_info->fifo = CSR1212_INVALID_ADDR_SPACE; - - dev_set_drvdata(&ud->device, node_info); - new_node->ud = ud; - - priv = netdev_priv(hi->dev); - list_add_tail(&new_node->list, &priv->ip_node_list); - return 0; -} - -static int eth1394_probe(struct device *dev) -{ - struct unit_directory *ud; - struct eth1394_host_info *hi; - - ud = container_of(dev, struct unit_directory, device); - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - return eth1394_new_node(hi, ud); -} - -static int eth1394_remove(struct device *dev) -{ - struct unit_directory *ud; - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - struct eth1394_node_ref *old_node; - struct eth1394_node_info *node_info; - struct list_head *lh, *n; - unsigned long flags; - - ud = container_of(dev, struct unit_directory, device); - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - priv = netdev_priv(hi->dev); - - old_node = eth1394_find_node(&priv->ip_node_list, ud); - if (!old_node) - return 0; - - list_del(&old_node->list); - kfree(old_node); - - node_info = dev_get_drvdata(&ud->device); - - spin_lock_irqsave(&node_info->pdg.lock, flags); - /* The partial datagram list should be empty, but we'll just - * make sure anyway... */ - list_for_each_safe(lh, n, &node_info->pdg.list) - purge_partial_datagram(lh); - spin_unlock_irqrestore(&node_info->pdg.lock, flags); - - kfree(node_info); - dev_set_drvdata(&ud->device, NULL); - return 0; -} - -static int eth1394_update(struct unit_directory *ud) -{ - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - struct eth1394_node_ref *node; - - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - priv = netdev_priv(hi->dev); - node = eth1394_find_node(&priv->ip_node_list, ud); - if (node) - return 0; - - return eth1394_new_node(hi, ud); -} - -static const struct ieee1394_device_id eth1394_id_table[] = { - { - .match_flags = (IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION), - .specifier_id = ETHER1394_GASP_SPECIFIER_ID, - .version = ETHER1394_GASP_VERSION, - }, - {} -}; - -MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table); - -static struct hpsb_protocol_driver eth1394_proto_driver = { - .name = driver_name, - .id_table = eth1394_id_table, - .update = eth1394_update, - .driver = { - .probe = eth1394_probe, - .remove = eth1394_remove, - }, -}; - -static void ether1394_reset_priv(struct net_device *dev, int set_mtu) -{ - unsigned long flags; - int i; - struct eth1394_priv *priv = netdev_priv(dev); - struct hpsb_host *host = priv->host; - u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3])); - int max_speed = IEEE1394_SPEED_MAX; - - spin_lock_irqsave(&priv->lock, flags); - - memset(priv->ud_list, 0, sizeof(priv->ud_list)); - priv->bc_maxpayload = 512; - - /* Determine speed limit */ - /* FIXME: This is broken for nodes with link speed < PHY speed, - * and it is suboptimal for S200B...S800B hardware. - * The result of nodemgr's speed probe should be used somehow. */ - for (i = 0; i < host->node_count; i++) { - /* take care of S100B...S400B PHY ports */ - if (host->speed[i] == SELFID_SPEED_UNKNOWN) { - max_speed = IEEE1394_SPEED_100; - break; - } - if (max_speed > host->speed[i]) - max_speed = host->speed[i]; - } - priv->bc_sspd = max_speed; - - if (set_mtu) { - /* Use the RFC 2734 default 1500 octets or the maximum payload - * as initial MTU */ - dev->mtu = min(1500, ether1394_max_mtu(host)); - - /* Set our hardware address while we're at it */ - memcpy(dev->dev_addr, &guid, sizeof(u64)); - memset(dev->broadcast, 0xff, sizeof(u64)); - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static const struct header_ops ether1394_header_ops = { - .create = ether1394_header, - .rebuild = ether1394_rebuild_header, - .cache = ether1394_header_cache, - .cache_update = ether1394_header_cache_update, - .parse = ether1394_header_parse, -}; - -static const struct net_device_ops ether1394_netdev_ops = { - .ndo_open = ether1394_open, - .ndo_stop = ether1394_stop, - .ndo_start_xmit = ether1394_tx, - .ndo_tx_timeout = ether1394_tx_timeout, - .ndo_change_mtu = ether1394_change_mtu, -}; - -static void ether1394_init_dev(struct net_device *dev) -{ - - dev->header_ops = ðer1394_header_ops; - dev->netdev_ops = ðer1394_netdev_ops; - - dev->watchdog_timeo = ETHER1394_TIMEOUT; - dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->features = NETIF_F_HIGHDMA; - dev->addr_len = ETH1394_ALEN; - dev->hard_header_len = ETH1394_HLEN; - dev->type = ARPHRD_IEEE1394; - - /* FIXME: This value was copied from ether_setup(). Is it too much? */ - dev->tx_queue_len = 1000; -} - -/* - * Wake the queue up after commonly encountered transmit failure conditions are - * hopefully over. Currently only tlabel exhaustion is accounted for. - */ -static void ether1394_wake_queue(struct work_struct *work) -{ - struct eth1394_priv *priv; - struct hpsb_packet *packet; - - priv = container_of(work, struct eth1394_priv, wake); - packet = hpsb_alloc_packet(0); - - /* This is really bad, but unjam the queue anyway. */ - if (!packet) - goto out; - - packet->host = priv->host; - packet->node_id = priv->wake_node; - /* - * A transaction label is all we really want. If we get one, it almost - * always means we can get a lot more because the ieee1394 core recycled - * a whole batch of tlabels, at last. - */ - if (hpsb_get_tlabel(packet) == 0) - hpsb_free_tlabel(packet); - - hpsb_free_packet(packet); -out: - netif_wake_queue(priv->wake_dev); -} - -/* - * This function is called every time a card is found. It is generally called - * when the module is installed. This is where we add all of our ethernet - * devices. One for each host. - */ -static void ether1394_add_host(struct hpsb_host *host) -{ - struct eth1394_host_info *hi = NULL; - struct net_device *dev = NULL; - struct eth1394_priv *priv; - u64 fifo_addr; - - if (hpsb_config_rom_ip1394_add(host) != 0) { - ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n"); - return; - } - - fifo_addr = hpsb_allocate_and_register_addrspace( - ð1394_highlevel, host, &addr_ops, - ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN, - CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE); - if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) { - ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n"); - hpsb_config_rom_ip1394_remove(host); - return; - } - - dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev); - if (dev == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - goto out; - } - - SET_NETDEV_DEV(dev, &host->device); - - priv = netdev_priv(dev); - INIT_LIST_HEAD(&priv->ip_node_list); - spin_lock_init(&priv->lock); - priv->host = host; - priv->local_fifo = fifo_addr; - INIT_WORK(&priv->wake, ether1394_wake_queue); - priv->wake_dev = dev; - - hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); - if (hi == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - goto out; - } - - ether1394_reset_priv(dev, 1); - - if (register_netdev(dev)) { - ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n"); - goto out; - } - - ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n", - host->id); - - hi->host = host; - hi->dev = dev; - - /* Ignore validity in hopes that it will be set in the future. It'll - * be checked when the eth device is opened. */ - priv->broadcast_channel = host->csr.broadcast_channel & 0x3f; - - ether1394_recv_init(priv); - return; -out: - if (dev) - free_netdev(dev); - if (hi) - hpsb_destroy_hostinfo(ð1394_highlevel, host); - hpsb_unregister_addrspace(ð1394_highlevel, host, fifo_addr); - hpsb_config_rom_ip1394_remove(host); -} - -/* Remove a card from our list */ -static void ether1394_remove_host(struct hpsb_host *host) -{ - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - - hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (!hi) - return; - priv = netdev_priv(hi->dev); - hpsb_unregister_addrspace(ð1394_highlevel, host, priv->local_fifo); - hpsb_config_rom_ip1394_remove(host); - if (priv->iso) - hpsb_iso_shutdown(priv->iso); - unregister_netdev(hi->dev); - free_netdev(hi->dev); -} - -/* A bus reset happened */ -static void ether1394_host_reset(struct hpsb_host *host) -{ - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - struct net_device *dev; - struct list_head *lh, *n; - struct eth1394_node_ref *node; - struct eth1394_node_info *node_info; - unsigned long flags; - - hi = hpsb_get_hostinfo(ð1394_highlevel, host); - - /* This can happen for hosts that we don't use */ - if (!hi) - return; - - dev = hi->dev; - priv = netdev_priv(dev); - - /* Reset our private host data, but not our MTU */ - netif_stop_queue(dev); - ether1394_reset_priv(dev, 0); - - list_for_each_entry(node, &priv->ip_node_list, list) { - node_info = dev_get_drvdata(&node->ud->device); - - spin_lock_irqsave(&node_info->pdg.lock, flags); - - list_for_each_safe(lh, n, &node_info->pdg.list) - purge_partial_datagram(lh); - - INIT_LIST_HEAD(&(node_info->pdg.list)); - node_info->pdg.sz = 0; - - spin_unlock_irqrestore(&node_info->pdg.lock, flags); - } - - netif_wake_queue(dev); -} - -/****************************************** - * HW Header net device functions - ******************************************/ -/* These functions have been adapted from net/ethernet/eth.c */ - -/* Create a fake MAC header for an arbitrary protocol layer. - * saddr=NULL means use device source address - * daddr=NULL means leave destination address (eg unresolved arp). */ -static int ether1394_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len) -{ - struct eth1394hdr *eth = - (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); - - eth->h_proto = htons(type); - - if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - memset(eth->h_dest, 0, dev->addr_len); - return dev->hard_header_len; - } - - if (daddr) { - memcpy(eth->h_dest, daddr, dev->addr_len); - return dev->hard_header_len; - } - - return -dev->hard_header_len; -} - -/* Rebuild the faked MAC header. This is called after an ARP - * (or in future other address resolution) has completed on this - * sk_buff. We now let ARP fill in the other fields. - * - * This routine CANNOT use cached dst->neigh! - * Really, it is used only when dst->neigh is wrong. - */ -static int ether1394_rebuild_header(struct sk_buff *skb) -{ - struct eth1394hdr *eth = (struct eth1394hdr *)skb->data; - - if (eth->h_proto == htons(ETH_P_IP)) - return arp_find((unsigned char *)ð->h_dest, skb); - - ETH1394_PRINT(KERN_DEBUG, skb->dev->name, - "unable to resolve type %04x addresses\n", - ntohs(eth->h_proto)); - return 0; -} - -static int ether1394_header_parse(const struct sk_buff *skb, - unsigned char *haddr) -{ - memcpy(haddr, skb->dev->dev_addr, ETH1394_ALEN); - return ETH1394_ALEN; -} - -static int ether1394_header_cache(const struct neighbour *neigh, - struct hh_cache *hh) -{ - __be16 type = hh->hh_type; - struct net_device *dev = neigh->dev; - struct eth1394hdr *eth = - (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN); - - if (type == htons(ETH_P_802_3)) - return -1; - - eth->h_proto = type; - memcpy(eth->h_dest, neigh->ha, dev->addr_len); - - hh->hh_len = ETH1394_HLEN; - return 0; -} - -/* Called by Address Resolution module to notify changes in address. */ -static void ether1394_header_cache_update(struct hh_cache *hh, - const struct net_device *dev, - const unsigned char * haddr) -{ - memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len); -} - -/****************************************** - * Datagram reception code - ******************************************/ - -/* Copied from net/ethernet/eth.c */ -static __be16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - struct eth1394hdr *eth; - unsigned char *rawp; - - skb_reset_mac_header(skb); - skb_pull(skb, ETH1394_HLEN); - eth = eth1394_hdr(skb); - - if (*eth->h_dest & 1) { - if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0) - skb->pkt_type = PACKET_BROADCAST; -#if 0 - else - skb->pkt_type = PACKET_MULTICAST; -#endif - } else { - if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len)) - skb->pkt_type = PACKET_OTHERHOST; - } - - if (ntohs(eth->h_proto) >= 1536) - return eth->h_proto; - - rawp = skb->data; - - if (*(unsigned short *)rawp == 0xFFFF) - return htons(ETH_P_802_3); - - return htons(ETH_P_802_2); -} - -/* Parse an encapsulated IP1394 header into an ethernet frame packet. - * We also perform ARP translation here, if need be. */ -static __be16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev, - nodeid_t srcid, nodeid_t destid, - __be16 ether_type) -{ - struct eth1394_priv *priv = netdev_priv(dev); - __be64 dest_hw; - __be16 ret = 0; - - /* Setup our hw addresses. We use these to build the ethernet header. */ - if (destid == (LOCAL_BUS | ALL_NODES)) - dest_hw = ~cpu_to_be64(0); /* broadcast */ - else - dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 | - priv->host->csr.guid_lo); - - /* If this is an ARP packet, convert it. First, we want to make - * use of some of the fields, since they tell us a little bit - * about the sending machine. */ - if (ether_type == htons(ETH_P_ARP)) { - struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; - struct arphdr *arp = (struct arphdr *)skb->data; - unsigned char *arp_ptr = (unsigned char *)(arp + 1); - u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | - ntohl(arp1394->fifo_lo); - u8 max_rec = min(priv->host->csr.max_rec, - (u8)(arp1394->max_rec)); - int sspd = arp1394->sspd; - u16 maxpayload; - struct eth1394_node_ref *node; - struct eth1394_node_info *node_info; - __be64 guid; - - /* Sanity check. MacOSX seems to be sending us 131 in this - * field (atleast on my Panther G5). Not sure why. */ - if (sspd > 5 || sspd < 0) - sspd = 0; - - maxpayload = min(eth1394_speedto_maxpayload[sspd], - (u16)(1 << (max_rec + 1))); - - guid = get_unaligned(&arp1394->s_uniq_id); - node = eth1394_find_node_guid(&priv->ip_node_list, - be64_to_cpu(guid)); - if (!node) - return cpu_to_be16(0); - - node_info = dev_get_drvdata(&node->ud->device); - - /* Update our speed/payload/fifo_offset table */ - node_info->maxpayload = maxpayload; - node_info->sspd = sspd; - node_info->fifo = fifo_addr; - - /* Now that we're done with the 1394 specific stuff, we'll - * need to alter some of the data. Believe it or not, all - * that needs to be done is sender_IP_address needs to be - * moved, the destination hardware address get stuffed - * in and the hardware address length set to 8. - * - * IMPORTANT: The code below overwrites 1394 specific data - * needed above so keep the munging of the data for the - * higher level IP stack last. */ - - arp->ar_hln = 8; - arp_ptr += arp->ar_hln; /* skip over sender unique id */ - *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ - arp_ptr += arp->ar_pln; /* skip over sender IP addr */ - - if (arp->ar_op == htons(ARPOP_REQUEST)) - memset(arp_ptr, 0, sizeof(u64)); - else - memcpy(arp_ptr, dev->dev_addr, sizeof(u64)); - } - - /* Now add the ethernet header. */ - if (dev_hard_header(skb, dev, ntohs(ether_type), &dest_hw, NULL, - skb->len) >= 0) - ret = ether1394_type_trans(skb, dev); - - return ret; -} - -static int fragment_overlap(struct list_head *frag_list, int offset, int len) -{ - struct fragment_info *fi; - int end = offset + len; - - list_for_each_entry(fi, frag_list, list) - if (offset < fi->offset + fi->len && end > fi->offset) - return 1; - - return 0; -} - -static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) -{ - struct partial_datagram *pd; - - list_for_each_entry(pd, pdgl, list) - if (pd->dgl == dgl) - return &pd->list; - - return NULL; -} - -/* Assumes that new fragment does not overlap any existing fragments */ -static int new_fragment(struct list_head *frag_info, int offset, int len) -{ - struct list_head *lh; - struct fragment_info *fi, *fi2, *new; - - list_for_each(lh, frag_info) { - fi = list_entry(lh, struct fragment_info, list); - if (fi->offset + fi->len == offset) { - /* The new fragment can be tacked on to the end */ - fi->len += len; - /* Did the new fragment plug a hole? */ - fi2 = list_entry(lh->next, struct fragment_info, list); - if (fi->offset + fi->len == fi2->offset) { - /* glue fragments together */ - fi->len += fi2->len; - list_del(lh->next); - kfree(fi2); - } - return 0; - } else if (offset + len == fi->offset) { - /* The new fragment can be tacked on to the beginning */ - fi->offset = offset; - fi->len += len; - /* Did the new fragment plug a hole? */ - fi2 = list_entry(lh->prev, struct fragment_info, list); - if (fi2->offset + fi2->len == fi->offset) { - /* glue fragments together */ - fi2->len += fi->len; - list_del(lh); - kfree(fi); - } - return 0; - } else if (offset > fi->offset + fi->len) { - break; - } else if (offset + len < fi->offset) { - lh = lh->prev; - break; - } - } - - new = kmalloc(sizeof(*new), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - new->offset = offset; - new->len = len; - - list_add(&new->list, lh); - return 0; -} - -static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl, - int dgl, int dg_size, char *frag_buf, - int frag_off, int frag_len) -{ - struct partial_datagram *new; - - new = kmalloc(sizeof(*new), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - INIT_LIST_HEAD(&new->frag_info); - - if (new_fragment(&new->frag_info, frag_off, frag_len) < 0) { - kfree(new); - return -ENOMEM; - } - - new->dgl = dgl; - new->dg_size = dg_size; - - new->skb = dev_alloc_skb(dg_size + dev->hard_header_len + 15); - if (!new->skb) { - struct fragment_info *fi = list_entry(new->frag_info.next, - struct fragment_info, - list); - kfree(fi); - kfree(new); - return -ENOMEM; - } - - skb_reserve(new->skb, (dev->hard_header_len + 15) & ~15); - new->pbuf = skb_put(new->skb, dg_size); - memcpy(new->pbuf + frag_off, frag_buf, frag_len); - - list_add(&new->list, pdgl); - return 0; -} - -static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, - char *frag_buf, int frag_off, int frag_len) -{ - struct partial_datagram *pd = - list_entry(lh, struct partial_datagram, list); - - if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) - return -ENOMEM; - - memcpy(pd->pbuf + frag_off, frag_buf, frag_len); - - /* Move list entry to beginnig of list so that oldest partial - * datagrams percolate to the end of the list */ - list_move(lh, pdgl); - return 0; -} - -static int is_datagram_complete(struct list_head *lh, int dg_size) -{ - struct partial_datagram *pd; - struct fragment_info *fi; - - pd = list_entry(lh, struct partial_datagram, list); - fi = list_entry(pd->frag_info.next, struct fragment_info, list); - - return (fi->len == dg_size); -} - -/* Packet reception. We convert the IP1394 encapsulation header to an - * ethernet header, and fill it with some of our other fields. This is - * an incoming packet from the 1394 bus. */ -static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, - char *buf, int len) -{ - struct sk_buff *skb; - unsigned long flags; - struct eth1394_priv *priv = netdev_priv(dev); - union eth1394_hdr *hdr = (union eth1394_hdr *)buf; - __be16 ether_type = cpu_to_be16(0); /* initialized to clear warning */ - int hdr_len; - struct unit_directory *ud = priv->ud_list[NODEID_TO_NODE(srcid)]; - struct eth1394_node_info *node_info; - - if (!ud) { - struct eth1394_node_ref *node; - node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid); - if (unlikely(!node)) { - HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid " - "lookup failure: " NODE_BUS_FMT, - NODE_BUS_ARGS(priv->host, srcid)); - dev->stats.rx_dropped++; - return -1; - } - ud = node->ud; - - priv->ud_list[NODEID_TO_NODE(srcid)] = ud; - } - - node_info = dev_get_drvdata(&ud->device); - - /* First, did we receive a fragmented or unfragmented datagram? */ - hdr->words.word1 = ntohs(hdr->words.word1); - - hdr_len = hdr_type_len[hdr->common.lf]; - - if (hdr->common.lf == ETH1394_HDR_LF_UF) { - /* An unfragmented datagram has been received by the ieee1394 - * bus. Build an skbuff around it so we can pass it to the - * high level network layer. */ - - skb = dev_alloc_skb(len + dev->hard_header_len + 15); - if (unlikely(!skb)) { - ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - dev->stats.rx_dropped++; - return -1; - } - skb_reserve(skb, (dev->hard_header_len + 15) & ~15); - memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, - len - hdr_len); - ether_type = hdr->uf.ether_type; - } else { - /* A datagram fragment has been received, now the fun begins. */ - - struct list_head *pdgl, *lh; - struct partial_datagram *pd; - int fg_off; - int fg_len = len - hdr_len; - int dg_size; - int dgl; - int retval; - struct pdg_list *pdg = &(node_info->pdg); - - hdr->words.word3 = ntohs(hdr->words.word3); - /* The 4th header word is reserved so no need to do ntohs() */ - - if (hdr->common.lf == ETH1394_HDR_LF_FF) { - ether_type = hdr->ff.ether_type; - dgl = hdr->ff.dgl; - dg_size = hdr->ff.dg_size + 1; - fg_off = 0; - } else { - hdr->words.word2 = ntohs(hdr->words.word2); - dgl = hdr->sf.dgl; - dg_size = hdr->sf.dg_size + 1; - fg_off = hdr->sf.fg_off; - } - spin_lock_irqsave(&pdg->lock, flags); - - pdgl = &(pdg->list); - lh = find_partial_datagram(pdgl, dgl); - - if (lh == NULL) { - while (pdg->sz >= max_partial_datagrams) { - /* remove the oldest */ - purge_partial_datagram(pdgl->prev); - pdg->sz--; - } - - retval = new_partial_datagram(dev, pdgl, dgl, dg_size, - buf + hdr_len, fg_off, - fg_len); - if (retval < 0) { - spin_unlock_irqrestore(&pdg->lock, flags); - goto bad_proto; - } - pdg->sz++; - lh = find_partial_datagram(pdgl, dgl); - } else { - pd = list_entry(lh, struct partial_datagram, list); - - if (fragment_overlap(&pd->frag_info, fg_off, fg_len)) { - /* Overlapping fragments, obliterate old - * datagram and start new one. */ - purge_partial_datagram(lh); - retval = new_partial_datagram(dev, pdgl, dgl, - dg_size, - buf + hdr_len, - fg_off, fg_len); - if (retval < 0) { - pdg->sz--; - spin_unlock_irqrestore(&pdg->lock, flags); - goto bad_proto; - } - } else { - retval = update_partial_datagram(pdgl, lh, - buf + hdr_len, - fg_off, fg_len); - if (retval < 0) { - /* Couldn't save off fragment anyway - * so might as well obliterate the - * datagram now. */ - purge_partial_datagram(lh); - pdg->sz--; - spin_unlock_irqrestore(&pdg->lock, flags); - goto bad_proto; - } - } /* fragment overlap */ - } /* new datagram or add to existing one */ - - pd = list_entry(lh, struct partial_datagram, list); - - if (hdr->common.lf == ETH1394_HDR_LF_FF) - pd->ether_type = ether_type; - - if (is_datagram_complete(lh, dg_size)) { - ether_type = pd->ether_type; - pdg->sz--; - skb = skb_get(pd->skb); - purge_partial_datagram(lh); - spin_unlock_irqrestore(&pdg->lock, flags); - } else { - /* Datagram is not complete, we're done for the - * moment. */ - spin_unlock_irqrestore(&pdg->lock, flags); - return 0; - } - } /* unframgented datagram or fragmented one */ - - /* Write metadata, and then pass to the receive level */ - skb->dev = dev; - skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ - - /* Parse the encapsulation header. This actually does the job of - * converting to an ethernet frame header, aswell as arp - * conversion if needed. ARP conversion is easier in this - * direction, since we are using ethernet as our backend. */ - skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid, - ether_type); - - spin_lock_irqsave(&priv->lock, flags); - - if (!skb->protocol) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; - dev_kfree_skb_any(skb); - } else if (netif_rx(skb) == NET_RX_DROP) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; - } else { - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - } - - spin_unlock_irqrestore(&priv->lock, flags); - -bad_proto: - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - - return 0; -} - -static int ether1394_write(struct hpsb_host *host, int srcid, int destid, - quadlet_t *data, u64 addr, size_t len, u16 flags) -{ - struct eth1394_host_info *hi; - - hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (unlikely(!hi)) { - ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", - host->id); - return RCODE_ADDRESS_ERROR; - } - - if (ether1394_data_handler(hi->dev, srcid, destid, (char*)data, len)) - return RCODE_ADDRESS_ERROR; - else - return RCODE_COMPLETE; -} - -static void ether1394_iso(struct hpsb_iso *iso) -{ - __be32 *data; - char *buf; - struct eth1394_host_info *hi; - struct net_device *dev; - unsigned int len; - u32 specifier_id; - u16 source_id; - int i; - int nready; - - hi = hpsb_get_hostinfo(ð1394_highlevel, iso->host); - if (unlikely(!hi)) { - ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", - iso->host->id); - return; - } - - dev = hi->dev; - - nready = hpsb_iso_n_ready(iso); - for (i = 0; i < nready; i++) { - struct hpsb_iso_packet_info *info = - &iso->infos[(iso->first_packet + i) % iso->buf_packets]; - data = (__be32 *)(iso->data_buf.kvirt + info->offset); - - /* skip over GASP header */ - buf = (char *)data + 8; - len = info->len - 8; - - specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 | - (be32_to_cpu(data[1]) & 0xff000000) >> 24; - source_id = be32_to_cpu(data[0]) >> 16; - - if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) - || specifier_id != ETHER1394_GASP_SPECIFIER_ID) { - /* This packet is not for us */ - continue; - } - ether1394_data_handler(dev, source_id, LOCAL_BUS | ALL_NODES, - buf, len); - } - - hpsb_iso_recv_release_packets(iso, i); - -} - -/****************************************** - * Datagram transmission code - ******************************************/ - -/* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire - * arphdr) is the same format as the ip1394 header, so they overlap. The rest - * needs to be munged a bit. The remainder of the arphdr is formatted based - * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to - * judge. - * - * Now that the EUI is used for the hardware address all we need to do to make - * this work for 1394 is to insert 2 quadlets that contain max_rec size, - * speed, and unicast FIFO address information between the sender_unique_id - * and the IP addresses. - */ -static void ether1394_arp_to_1394arp(struct sk_buff *skb, - struct net_device *dev) -{ - struct eth1394_priv *priv = netdev_priv(dev); - struct arphdr *arp = (struct arphdr *)skb->data; - unsigned char *arp_ptr = (unsigned char *)(arp + 1); - struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; - - arp1394->hw_addr_len = 16; - arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN); - arp1394->max_rec = priv->host->csr.max_rec; - arp1394->sspd = priv->host->csr.lnk_spd; - arp1394->fifo_hi = htons(priv->local_fifo >> 32); - arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0); -} - -/* We need to encapsulate the standard header with our own. We use the - * ethernet header's proto for our own. */ -static unsigned int ether1394_encapsulate_prep(unsigned int max_payload, - __be16 proto, - union eth1394_hdr *hdr, - u16 dg_size, u16 dgl) -{ - unsigned int adj_max_payload = - max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; - - /* Does it all fit in one packet? */ - if (dg_size <= adj_max_payload) { - hdr->uf.lf = ETH1394_HDR_LF_UF; - hdr->uf.ether_type = proto; - } else { - hdr->ff.lf = ETH1394_HDR_LF_FF; - hdr->ff.ether_type = proto; - hdr->ff.dg_size = dg_size - 1; - hdr->ff.dgl = dgl; - adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF]; - } - return DIV_ROUND_UP(dg_size, adj_max_payload); -} - -static unsigned int ether1394_encapsulate(struct sk_buff *skb, - unsigned int max_payload, - union eth1394_hdr *hdr) -{ - union eth1394_hdr *bufhdr; - int ftype = hdr->common.lf; - int hdrsz = hdr_type_len[ftype]; - unsigned int adj_max_payload = max_payload - hdrsz; - - switch (ftype) { - case ETH1394_HDR_LF_UF: - bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); - bufhdr->words.word1 = htons(hdr->words.word1); - bufhdr->words.word2 = hdr->words.word2; - break; - - case ETH1394_HDR_LF_FF: - bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); - bufhdr->words.word1 = htons(hdr->words.word1); - bufhdr->words.word2 = hdr->words.word2; - bufhdr->words.word3 = htons(hdr->words.word3); - bufhdr->words.word4 = 0; - - /* Set frag type here for future interior fragments */ - hdr->common.lf = ETH1394_HDR_LF_IF; - hdr->sf.fg_off = 0; - break; - - default: - hdr->sf.fg_off += adj_max_payload; - bufhdr = (union eth1394_hdr *)skb_pull(skb, adj_max_payload); - if (max_payload >= skb->len) - hdr->common.lf = ETH1394_HDR_LF_LF; - bufhdr->words.word1 = htons(hdr->words.word1); - bufhdr->words.word2 = htons(hdr->words.word2); - bufhdr->words.word3 = htons(hdr->words.word3); - bufhdr->words.word4 = 0; - } - return min(max_payload, skb->len); -} - -static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) -{ - struct hpsb_packet *p; - - p = hpsb_alloc_packet(0); - if (p) { - p->host = host; - p->generation = get_hpsb_generation(host); - p->type = hpsb_async; - } - return p; -} - -static int ether1394_prep_write_packet(struct hpsb_packet *p, - struct hpsb_host *host, nodeid_t node, - u64 addr, void *data, int tx_len) -{ - p->node_id = node; - - if (hpsb_get_tlabel(p)) - return -EAGAIN; - - p->tcode = TCODE_WRITEB; - p->header_size = 16; - p->expect_response = 1; - p->header[0] = - p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4; - p->header[1] = host->node_id << 16 | addr >> 32; - p->header[2] = addr & 0xffffffff; - p->header[3] = tx_len << 16; - p->data_size = (tx_len + 3) & ~3; - p->data = data; - - return 0; -} - -static void ether1394_prep_gasp_packet(struct hpsb_packet *p, - struct eth1394_priv *priv, - struct sk_buff *skb, int length) -{ - p->header_size = 4; - p->tcode = TCODE_STREAM_DATA; - - p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 | - TCODE_STREAM_DATA << 4; - p->data_size = length; - p->data = (quadlet_t *)skb->data - 2; - p->data[0] = cpu_to_be32(priv->host->node_id << 16 | - ETHER1394_GASP_SPECIFIER_ID_HI); - p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 | - ETHER1394_GASP_VERSION); - - p->speed_code = priv->bc_sspd; - - /* prevent hpsb_send_packet() from overriding our speed code */ - p->node_id = LOCAL_BUS | ALL_NODES; -} - -static void ether1394_free_packet(struct hpsb_packet *packet) -{ - if (packet->tcode != TCODE_STREAM_DATA) - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); -} - -static void ether1394_complete_cb(void *__ptask); - -static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len) -{ - struct eth1394_priv *priv = ptask->priv; - struct hpsb_packet *packet = NULL; - - packet = ether1394_alloc_common_packet(priv->host); - if (!packet) - return -ENOMEM; - - if (ptask->tx_type == ETH1394_GASP) { - int length = tx_len + 2 * sizeof(quadlet_t); - - ether1394_prep_gasp_packet(packet, priv, ptask->skb, length); - } else if (ether1394_prep_write_packet(packet, priv->host, - ptask->dest_node, - ptask->addr, ptask->skb->data, - tx_len)) { - hpsb_free_packet(packet); - return -EAGAIN; - } - - ptask->packet = packet; - hpsb_set_packet_complete_task(ptask->packet, ether1394_complete_cb, - ptask); - - if (hpsb_send_packet(packet) < 0) { - ether1394_free_packet(packet); - return -EIO; - } - - return 0; -} - -/* Task function to be run when a datagram transmission is completed */ -static void ether1394_dg_complete(struct packet_task *ptask, int fail) -{ - struct sk_buff *skb = ptask->skb; - struct net_device *dev = skb->dev; - struct eth1394_priv *priv = netdev_priv(dev); - unsigned long flags; - - /* Statistics */ - spin_lock_irqsave(&priv->lock, flags); - if (fail) { - dev->stats.tx_dropped++; - dev->stats.tx_errors++; - } else { - dev->stats.tx_bytes += skb->len; - dev->stats.tx_packets++; - } - spin_unlock_irqrestore(&priv->lock, flags); - - dev_kfree_skb_any(skb); - kmem_cache_free(packet_task_cache, ptask); -} - -/* Callback for when a packet has been sent and the status of that packet is - * known */ -static void ether1394_complete_cb(void *__ptask) -{ - struct packet_task *ptask = (struct packet_task *)__ptask; - struct hpsb_packet *packet = ptask->packet; - int fail = 0; - - if (packet->tcode != TCODE_STREAM_DATA) - fail = hpsb_packet_success(packet); - - ether1394_free_packet(packet); - - ptask->outstanding_pkts--; - if (ptask->outstanding_pkts > 0 && !fail) { - int tx_len, err; - - /* Add the encapsulation header to the fragment */ - tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload, - &ptask->hdr); - err = ether1394_send_packet(ptask, tx_len); - if (err) { - if (err == -EAGAIN) - ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n"); - - ether1394_dg_complete(ptask, 1); - } - } else { - ether1394_dg_complete(ptask, fail); - } -} - -/* Transmit a packet (called by kernel) */ -static netdev_tx_t ether1394_tx(struct sk_buff *skb, - struct net_device *dev) -{ - struct eth1394hdr hdr_buf; - struct eth1394_priv *priv = netdev_priv(dev); - __be16 proto; - unsigned long flags; - nodeid_t dest_node; - eth1394_tx_type tx_type; - unsigned int tx_len; - unsigned int max_payload; - u16 dg_size; - u16 dgl; - struct packet_task *ptask; - struct eth1394_node_ref *node; - struct eth1394_node_info *node_info = NULL; - - ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC); - if (ptask == NULL) - goto fail; - - /* XXX Ignore this for now. Noticed that when MacOSX is the IRM, - * it does not set our validity bit. We need to compensate for - * that somewhere else, but not in eth1394. */ -#if 0 - if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) - goto fail; -#endif - - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - goto fail; - - /* Get rid of the fake eth1394 header, but first make a copy. - * We might need to rebuild the header on tx failure. */ - memcpy(&hdr_buf, skb->data, sizeof(hdr_buf)); - skb_pull(skb, ETH1394_HLEN); - - proto = hdr_buf.h_proto; - dg_size = skb->len; - - /* Set the transmission type for the packet. ARP packets and IP - * broadcast packets are sent via GASP. */ - if (memcmp(hdr_buf.h_dest, dev->broadcast, ETH1394_ALEN) == 0 || - proto == htons(ETH_P_ARP) || - (proto == htons(ETH_P_IP) && - IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { - tx_type = ETH1394_GASP; - dest_node = LOCAL_BUS | ALL_NODES; - max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD; - BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); - dgl = priv->bc_dgl; - if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) - priv->bc_dgl++; - } else { - __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest); - - node = eth1394_find_node_guid(&priv->ip_node_list, - be64_to_cpu(guid)); - if (!node) - goto fail; - - node_info = dev_get_drvdata(&node->ud->device); - if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) - goto fail; - - dest_node = node->ud->ne->nodeid; - max_payload = node_info->maxpayload; - BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); - - dgl = node_info->dgl; - if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) - node_info->dgl++; - tx_type = ETH1394_WRREQ; - } - - /* If this is an ARP packet, convert it */ - if (proto == htons(ETH_P_ARP)) - ether1394_arp_to_1394arp(skb, dev); - - ptask->hdr.words.word1 = 0; - ptask->hdr.words.word2 = 0; - ptask->hdr.words.word3 = 0; - ptask->hdr.words.word4 = 0; - ptask->skb = skb; - ptask->priv = priv; - ptask->tx_type = tx_type; - - if (tx_type != ETH1394_GASP) { - u64 addr; - - spin_lock_irqsave(&priv->lock, flags); - addr = node_info->fifo; - spin_unlock_irqrestore(&priv->lock, flags); - - ptask->addr = addr; - ptask->dest_node = dest_node; - } - - ptask->tx_type = tx_type; - ptask->max_payload = max_payload; - ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, - proto, &ptask->hdr, dg_size, dgl); - - /* Add the encapsulation header to the fragment */ - tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr); - dev->trans_start = jiffies; - if (ether1394_send_packet(ptask, tx_len)) { - if (dest_node == (LOCAL_BUS | ALL_NODES)) - goto fail; - - /* At this point we want to restore the packet. When we return - * here with NETDEV_TX_BUSY we will get another entrance in this - * routine with the same skb and we need it to look the same. - * So we pull 4 more bytes, then build the header again. */ - skb_pull(skb, 4); - ether1394_header(skb, dev, ntohs(hdr_buf.h_proto), - hdr_buf.h_dest, NULL, 0); - - /* Most failures of ether1394_send_packet are recoverable. */ - netif_stop_queue(dev); - priv->wake_node = dest_node; - schedule_work(&priv->wake); - kmem_cache_free(packet_task_cache, ptask); - return NETDEV_TX_BUSY; - } - - return NETDEV_TX_OK; -fail: - if (ptask) - kmem_cache_free(packet_task_cache, ptask); - - if (skb != NULL) - dev_kfree_skb(skb); - - spin_lock_irqsave(&priv->lock, flags); - dev->stats.tx_dropped++; - dev->stats.tx_errors++; - spin_unlock_irqrestore(&priv->lock, flags); - - return NETDEV_TX_OK; -} - -static int __init ether1394_init_module(void) -{ - int err; - - packet_task_cache = kmem_cache_create("packet_task", - sizeof(struct packet_task), - 0, 0, NULL); - if (!packet_task_cache) - return -ENOMEM; - - hpsb_register_highlevel(ð1394_highlevel); - err = hpsb_register_protocol(ð1394_proto_driver); - if (err) { - hpsb_unregister_highlevel(ð1394_highlevel); - kmem_cache_destroy(packet_task_cache); - } - return err; -} - -static void __exit ether1394_exit_module(void) -{ - hpsb_unregister_protocol(ð1394_proto_driver); - hpsb_unregister_highlevel(ð1394_highlevel); - kmem_cache_destroy(packet_task_cache); -} - -module_init(ether1394_init_module); -module_exit(ether1394_exit_module); diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h deleted file mode 100644 index d53bac47b86..00000000000 --- a/drivers/ieee1394/eth1394.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * eth1394.h -- Ethernet driver for Linux IEEE-1394 Subsystem - * - * Copyright (C) 2000 Bonin Franck <boninf@free.fr> - * (C) 2001 Ben Collins <bcollins@debian.org> - * - * Mainly based on work by Emanuel Pirker and Andreas E. Bombe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __ETH1394_H -#define __ETH1394_H - -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <asm/byteorder.h> - -#include "ieee1394.h" -#include "ieee1394_types.h" - -/* Register for incoming packets. This is 4096 bytes, which supports up to - * S3200 (per Table 16-3 of IEEE 1394b-2002). */ -#define ETHER1394_REGION_ADDR_LEN 4096 - -/* GASP identifier numbers for IPv4 over IEEE 1394 */ -#define ETHER1394_GASP_SPECIFIER_ID 0x00005E -#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff) -#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff) -#define ETHER1394_GASP_VERSION 1 - -#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */ - -#define ETHER1394_GASP_BUFFERS 16 - -#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */ - -enum eth1394_bc_states { ETHER1394_BC_ERROR, - ETHER1394_BC_RUNNING, - ETHER1394_BC_STOPPED }; - - -/* Private structure for our ethernet driver */ -struct eth1394_priv { - struct hpsb_host *host; /* The card for this dev */ - u16 bc_maxpayload; /* Max broadcast payload */ - u8 bc_sspd; /* Max broadcast speed */ - u64 local_fifo; /* Local FIFO Address */ - spinlock_t lock; /* Private lock */ - int broadcast_channel; /* Async stream Broadcast Channel */ - enum eth1394_bc_states bc_state; /* broadcast channel state */ - struct hpsb_iso *iso; /* Async stream recv handle */ - int bc_dgl; /* Outgoing broadcast datagram label */ - struct list_head ip_node_list; /* List of IP capable nodes */ - struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */ - - struct work_struct wake; /* Wake up after xmit failure */ - struct net_device *wake_dev; /* Stupid backlink for .wake */ - nodeid_t wake_node; /* Destination of failed xmit */ -}; - - -/* Define a fake hardware header format for the networking core. Note that - * header size cannot exceed 16 bytes as that is the size of the header cache. - * Also, we do not need the source address in the header so we omit it and - * keep the header to under 16 bytes */ -#define ETH1394_ALEN (8) -#define ETH1394_HLEN (10) - -struct eth1394hdr { - unsigned char h_dest[ETH1394_ALEN]; /* destination eth1394 addr */ - __be16 h_proto; /* packet type ID field */ -} __attribute__((packed)); - -static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) -{ - return (struct eth1394hdr *)skb_mac_header(skb); -} - -typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; - -/* IP1394 headers */ - -/* Unfragmented */ -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_uf_hdr { - u16 lf:2; - u16 res:14; - __be16 ether_type; /* Ethernet packet type */ -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_uf_hdr { - u16 res:14; - u16 lf:2; - __be16 ether_type; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -/* First fragment */ -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_ff_hdr { - u16 lf:2; - u16 res1:2; - u16 dg_size:12; /* Datagram size */ - __be16 ether_type; /* Ethernet packet type */ - u16 dgl; /* Datagram label */ - u16 res2; -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_ff_hdr { - u16 dg_size:12; - u16 res1:2; - u16 lf:2; - __be16 ether_type; - u16 dgl; - u16 res2; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -/* XXX: Subsequent fragments, including last */ -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_sf_hdr { - u16 lf:2; - u16 res1:2; - u16 dg_size:12; /* Datagram size */ - u16 res2:4; - u16 fg_off:12; /* Fragment offset */ - u16 dgl; /* Datagram label */ - u16 res3; -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_sf_hdr { - u16 dg_size:12; - u16 res1:2; - u16 lf:2; - u16 fg_off:12; - u16 res2:4; - u16 dgl; - u16 res3; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_common_hdr { - u16 lf:2; - u16 pad1:14; -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_common_hdr { - u16 pad1:14; - u16 lf:2; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -struct eth1394_hdr_words { - u16 word1; - u16 word2; - u16 word3; - u16 word4; -}; - -union eth1394_hdr { - struct eth1394_common_hdr common; - struct eth1394_uf_hdr uf; - struct eth1394_ff_hdr ff; - struct eth1394_sf_hdr sf; - struct eth1394_hdr_words words; -}; - -/* End of IP1394 headers */ - -/* Fragment types */ -#define ETH1394_HDR_LF_UF 0 /* unfragmented */ -#define ETH1394_HDR_LF_FF 1 /* first fragment */ -#define ETH1394_HDR_LF_LF 2 /* last fragment */ -#define ETH1394_HDR_LF_IF 3 /* interior fragment */ - -#define IP1394_HW_ADDR_LEN 16 /* As per RFC */ - -/* Our arp packet (ARPHRD_IEEE1394) */ -struct eth1394_arp { - u16 hw_type; /* 0x0018 */ - u16 proto_type; /* 0x0806 */ - u8 hw_addr_len; /* 16 */ - u8 ip_addr_len; /* 4 */ - u16 opcode; /* ARP Opcode */ - /* Above is exactly the same format as struct arphdr */ - - __be64 s_uniq_id; /* Sender's 64bit EUI */ - u8 max_rec; /* Sender's max packet size */ - u8 sspd; /* Sender's max speed */ - __be16 fifo_hi; /* hi 16bits of sender's FIFO addr */ - __be32 fifo_lo; /* lo 32bits of sender's FIFO addr */ - u32 sip; /* Sender's IP Address */ - u32 tip; /* IP Address of requested hw addr */ -}; - -/* Network timeout */ -#define ETHER1394_TIMEOUT 100000 - -/* This is our task struct. It's used for the packet complete callback. */ -struct packet_task { - struct sk_buff *skb; - int outstanding_pkts; - eth1394_tx_type tx_type; - int max_payload; - struct hpsb_packet *packet; - struct eth1394_priv *priv; - union eth1394_hdr hdr; - u64 addr; - u16 dest_node; -}; - -#endif /* __ETH1394_H */ diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c deleted file mode 100644 index 4bc443546e0..00000000000 --- a/drivers/ieee1394/highlevel.c +++ /dev/null @@ -1,691 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Copyright (C) 1999 Andreas E. Bombe - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Christian Toegel <christian.toegel@gmx.at> - * unregister address space - * - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * unregister address space - * - */ - -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/bitops.h> - -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "nodemgr.h" - - -struct hl_host_info { - struct list_head list; - struct hpsb_host *host; - size_t size; - unsigned long key; - void *data; -}; - - -static LIST_HEAD(hl_drivers); -static DECLARE_RWSEM(hl_drivers_sem); - -static LIST_HEAD(hl_irqs); -static DEFINE_RWLOCK(hl_irqs_lock); - -static DEFINE_RWLOCK(addr_space_lock); - - -static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl, - struct hpsb_host *host) -{ - struct hl_host_info *hi = NULL; - - if (!hl || !host) - return NULL; - - read_lock(&hl->host_info_lock); - list_for_each_entry(hi, &hl->host_info_list, list) { - if (hi->host == host) { - read_unlock(&hl->host_info_lock); - return hi; - } - } - read_unlock(&hl->host_info_lock); - return NULL; -} - -/** - * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host - * - * Returns a per @host and @hl driver data structure that was previously stored - * by hpsb_create_hostinfo. - */ -void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) -{ - struct hl_host_info *hi = hl_get_hostinfo(hl, host); - - return hi ? hi->data : NULL; -} - -/** - * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host - * - * Allocate a hostinfo pointer backed by memory with @data_size and bind it to - * to this @hl driver and @host. If @data_size is zero, then the return here is - * only valid for error checking. - */ -void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - size_t data_size) -{ - struct hl_host_info *hi; - void *data; - unsigned long flags; - - hi = hl_get_hostinfo(hl, host); - if (hi) { - HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already" - " exists", hl->name); - return NULL; - } - - hi = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC); - if (!hi) - return NULL; - - if (data_size) { - data = hi->data = hi + 1; - hi->size = data_size; - } else - data = hi; - - hi->host = host; - - write_lock_irqsave(&hl->host_info_lock, flags); - list_add_tail(&hi->list, &hl->host_info_list); - write_unlock_irqrestore(&hl->host_info_lock, flags); - - return data; -} - -/** - * hpsb_set_hostinfo - set the hostinfo pointer to something useful - * - * Usually follows a call to hpsb_create_hostinfo, where the size is 0. - */ -int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - void *data) -{ - struct hl_host_info *hi; - - hi = hl_get_hostinfo(hl, host); - if (hi) { - if (!hi->size && !hi->data) { - hi->data = data; - return 0; - } else - HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo " - "already has data", hl->name); - } else - HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists", - hl->name); - return -EINVAL; -} - -/** - * hpsb_destroy_hostinfo - free and remove a hostinfo pointer - * - * Free and remove the hostinfo pointer bound to this @hl driver and @host. - */ -void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) -{ - struct hl_host_info *hi; - - hi = hl_get_hostinfo(hl, host); - if (hi) { - unsigned long flags; - write_lock_irqsave(&hl->host_info_lock, flags); - list_del(&hi->list); - write_unlock_irqrestore(&hl->host_info_lock, flags); - kfree(hi); - } - return; -} - -/** - * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo - * - * Sets an alternate lookup key for the hostinfo bound to this @hl driver and - * @host. - */ -void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned long key) -{ - struct hl_host_info *hi; - - hi = hl_get_hostinfo(hl, host); - if (hi) - hi->key = key; - return; -} - -/** - * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key - */ -void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key) -{ - struct hl_host_info *hi; - void *data = NULL; - - if (!hl) - return NULL; - - read_lock(&hl->host_info_lock); - list_for_each_entry(hi, &hl->host_info_list, list) { - if (hi->key == key) { - data = hi->data; - break; - } - } - read_unlock(&hl->host_info_lock); - return data; -} - -static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data) -{ - struct hpsb_highlevel *hl = __data; - - hl->add_host(host); - - if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0) - HPSB_ERR("Failed to generate Configuration ROM image for host " - "%s-%d", hl->name, host->id); - return 0; -} - -/** - * hpsb_register_highlevel - register highlevel driver - * - * The name pointer in @hl has to stay valid at all times because the string is - * not copied. - */ -void hpsb_register_highlevel(struct hpsb_highlevel *hl) -{ - unsigned long flags; - - hpsb_init_highlevel(hl); - INIT_LIST_HEAD(&hl->addr_list); - - down_write(&hl_drivers_sem); - list_add_tail(&hl->hl_list, &hl_drivers); - up_write(&hl_drivers_sem); - - write_lock_irqsave(&hl_irqs_lock, flags); - list_add_tail(&hl->irq_list, &hl_irqs); - write_unlock_irqrestore(&hl_irqs_lock, flags); - - if (hl->add_host) - nodemgr_for_each_host(hl, highlevel_for_each_host_reg); - return; -} - -static void __delete_addr(struct hpsb_address_serve *as) -{ - list_del(&as->host_list); - list_del(&as->hl_list); - kfree(as); -} - -static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host, - int update_cr) -{ - unsigned long flags; - struct list_head *lh, *next; - struct hpsb_address_serve *as; - - /* First, let the highlevel driver unreg */ - if (hl->remove_host) - hl->remove_host(host); - - /* Remove any addresses that are matched for this highlevel driver - * and this particular host. */ - write_lock_irqsave(&addr_space_lock, flags); - list_for_each_safe (lh, next, &hl->addr_list) { - as = list_entry(lh, struct hpsb_address_serve, hl_list); - if (as->host == host) - __delete_addr(as); - } - write_unlock_irqrestore(&addr_space_lock, flags); - - /* Now update the config-rom to reflect anything removed by the - * highlevel driver. */ - if (update_cr && host->update_config_rom && - hpsb_update_config_rom_image(host) < 0) - HPSB_ERR("Failed to generate Configuration ROM image for host " - "%s-%d", hl->name, host->id); - - /* Finally remove all the host info associated between these two. */ - hpsb_destroy_hostinfo(hl, host); -} - -static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data) -{ - struct hpsb_highlevel *hl = __data; - - __unregister_host(hl, host, 1); - return 0; -} - -/** - * hpsb_unregister_highlevel - unregister highlevel driver - */ -void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) -{ - unsigned long flags; - - write_lock_irqsave(&hl_irqs_lock, flags); - list_del(&hl->irq_list); - write_unlock_irqrestore(&hl_irqs_lock, flags); - - down_write(&hl_drivers_sem); - list_del(&hl->hl_list); - up_write(&hl_drivers_sem); - - nodemgr_for_each_host(hl, highlevel_for_each_host_unreg); -} - -/** - * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space - * - * @start and @end are 48 bit pointers and have to be quadlet aligned. - * @end points to the first address behind the handled addresses. This - * function can be called multiple times for a single hpsb_highlevel @hl to - * implement sparse register sets. The requested region must not overlap any - * previously allocated region, otherwise registering will fail. - * - * It returns true for successful allocation. Address spaces can be - * unregistered with hpsb_unregister_addrspace. All remaining address spaces - * are automatically deallocated together with the hpsb_highlevel @hl. - */ -u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, - struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 size, u64 alignment, - u64 start, u64 end) -{ - struct hpsb_address_serve *as, *a1, *a2; - struct list_head *entry; - u64 retval = CSR1212_INVALID_ADDR_SPACE; - unsigned long flags; - u64 align_mask = ~(alignment - 1); - - if ((alignment & 3) || (alignment > 0x800000000000ULL) || - (hweight64(alignment) != 1)) { - HPSB_ERR("%s called with invalid alignment: 0x%048llx", - __func__, (unsigned long long)alignment); - return retval; - } - - /* default range, - * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */ - if (start == CSR1212_INVALID_ADDR_SPACE && - end == CSR1212_INVALID_ADDR_SPACE) { - start = host->middle_addr_space; - end = CSR1212_ALL_SPACE_END; - } - - if (((start|end) & ~align_mask) || (start >= end) || - (end > CSR1212_ALL_SPACE_END)) { - HPSB_ERR("%s called with invalid addresses " - "(start = %012Lx end = %012Lx)", __func__, - (unsigned long long)start,(unsigned long long)end); - return retval; - } - - as = kmalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return retval; - - INIT_LIST_HEAD(&as->host_list); - INIT_LIST_HEAD(&as->hl_list); - as->op = ops; - as->host = host; - - write_lock_irqsave(&addr_space_lock, flags); - list_for_each(entry, &host->addr_space) { - u64 a1sa, a1ea; - u64 a2sa, a2ea; - - a1 = list_entry(entry, struct hpsb_address_serve, host_list); - a2 = list_entry(entry->next, struct hpsb_address_serve, - host_list); - - a1sa = a1->start & align_mask; - a1ea = (a1->end + alignment -1) & align_mask; - a2sa = a2->start & align_mask; - a2ea = (a2->end + alignment -1) & align_mask; - - if ((a2sa - a1ea >= size) && (a2sa - start >= size) && - (a2sa > start)) { - as->start = max(start, a1ea); - as->end = as->start + size; - list_add(&as->host_list, entry); - list_add_tail(&as->hl_list, &hl->addr_list); - retval = as->start; - break; - } - } - write_unlock_irqrestore(&addr_space_lock, flags); - - if (retval == CSR1212_INVALID_ADDR_SPACE) - kfree(as); - return retval; -} - -/** - * hpsb_register_addrspace - register a host address space - * - * @start and @end are 48 bit pointers and have to be quadlet aligned. - * @end points to the first address behind the handled addresses. This - * function can be called multiple times for a single hpsb_highlevel @hl to - * implement sparse register sets. The requested region must not overlap any - * previously allocated region, otherwise registering will fail. - * - * It returns true for successful allocation. Address spaces can be - * unregistered with hpsb_unregister_addrspace. All remaining address spaces - * are automatically deallocated together with the hpsb_highlevel @hl. - */ -int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 start, u64 end) -{ - struct hpsb_address_serve *as; - struct list_head *lh; - int retval = 0; - unsigned long flags; - - if (((start|end) & 3) || (start >= end) || - (end > CSR1212_ALL_SPACE_END)) { - HPSB_ERR("%s called with invalid addresses", __func__); - return 0; - } - - as = kmalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return 0; - - INIT_LIST_HEAD(&as->host_list); - INIT_LIST_HEAD(&as->hl_list); - as->op = ops; - as->start = start; - as->end = end; - as->host = host; - - write_lock_irqsave(&addr_space_lock, flags); - list_for_each(lh, &host->addr_space) { - struct hpsb_address_serve *as_this = - list_entry(lh, struct hpsb_address_serve, host_list); - struct hpsb_address_serve *as_next = - list_entry(lh->next, struct hpsb_address_serve, - host_list); - - if (as_this->end > as->start) - break; - - if (as_next->start >= as->end) { - list_add(&as->host_list, lh); - list_add_tail(&as->hl_list, &hl->addr_list); - retval = 1; - break; - } - } - write_unlock_irqrestore(&addr_space_lock, flags); - - if (retval == 0) - kfree(as); - return retval; -} - -int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - u64 start) -{ - int retval = 0; - struct hpsb_address_serve *as; - struct list_head *lh, *next; - unsigned long flags; - - write_lock_irqsave(&addr_space_lock, flags); - list_for_each_safe (lh, next, &hl->addr_list) { - as = list_entry(lh, struct hpsb_address_serve, hl_list); - if (as->start == start && as->host == host) { - __delete_addr(as); - retval = 1; - break; - } - } - write_unlock_irqrestore(&addr_space_lock, flags); - return retval; -} - -static const struct hpsb_address_ops dummy_ops; - -/* dummy address spaces as lower and upper bounds of the host's a.s. list */ -static void init_hpsb_highlevel(struct hpsb_host *host) -{ - INIT_LIST_HEAD(&host->dummy_zero_addr.host_list); - INIT_LIST_HEAD(&host->dummy_zero_addr.hl_list); - INIT_LIST_HEAD(&host->dummy_max_addr.host_list); - INIT_LIST_HEAD(&host->dummy_max_addr.hl_list); - - host->dummy_zero_addr.op = host->dummy_max_addr.op = &dummy_ops; - - host->dummy_zero_addr.start = host->dummy_zero_addr.end = 0; - host->dummy_max_addr.start = host->dummy_max_addr.end = ((u64) 1) << 48; - - list_add_tail(&host->dummy_zero_addr.host_list, &host->addr_space); - list_add_tail(&host->dummy_max_addr.host_list, &host->addr_space); -} - -void highlevel_add_host(struct hpsb_host *host) -{ - struct hpsb_highlevel *hl; - - init_hpsb_highlevel(host); - - down_read(&hl_drivers_sem); - list_for_each_entry(hl, &hl_drivers, hl_list) { - if (hl->add_host) - hl->add_host(host); - } - up_read(&hl_drivers_sem); - if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0) - HPSB_ERR("Failed to generate Configuration ROM image for host " - "%s-%d", hl->name, host->id); -} - -void highlevel_remove_host(struct hpsb_host *host) -{ - struct hpsb_highlevel *hl; - - down_read(&hl_drivers_sem); - list_for_each_entry(hl, &hl_drivers, hl_list) - __unregister_host(hl, host, 0); - up_read(&hl_drivers_sem); -} - -void highlevel_host_reset(struct hpsb_host *host) -{ - unsigned long flags; - struct hpsb_highlevel *hl; - - read_lock_irqsave(&hl_irqs_lock, flags); - list_for_each_entry(hl, &hl_irqs, irq_list) { - if (hl->host_reset) - hl->host_reset(host); - } - read_unlock_irqrestore(&hl_irqs_lock, flags); -} - -void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, - void *data, size_t length) -{ - unsigned long flags; - struct hpsb_highlevel *hl; - int cts = ((quadlet_t *)data)[0] >> 4; - - read_lock_irqsave(&hl_irqs_lock, flags); - list_for_each_entry(hl, &hl_irqs, irq_list) { - if (hl->fcp_request) - hl->fcp_request(host, nodeid, direction, cts, data, - length); - } - read_unlock_irqrestore(&hl_irqs_lock, flags); -} - -/* - * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64: - * - * These functions are called to handle transactions. They are called when a - * packet arrives. The flags argument contains the second word of the first - * header quadlet of the incoming packet (containing transaction label, retry - * code, transaction code and priority). These functions either return a - * response code or a negative number. In the first case a response will be - * generated. In the latter case, no response will be sent and the driver which - * handled the request will send the response itself. - */ -int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, - unsigned int length, u16 flags) -{ - struct hpsb_address_serve *as; - unsigned int partlength; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - partlength = min(as->end - addr, (u64) length); - - if (as->op->read) - rcode = as->op->read(host, nodeid, data, - addr, partlength, flags); - else - rcode = RCODE_TYPE_ERROR; - - data += partlength; - length -= partlength; - addr += partlength; - - if ((rcode != RCODE_COMPLETE) || !length) - break; - } - } - read_unlock(&addr_space_lock); - - if (length && (rcode == RCODE_COMPLETE)) - rcode = RCODE_ADDRESS_ERROR; - return rcode; -} - -int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, - u64 addr, unsigned int length, u16 flags) -{ - struct hpsb_address_serve *as; - unsigned int partlength; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - partlength = min(as->end - addr, (u64) length); - - if (as->op->write) - rcode = as->op->write(host, nodeid, destid, - data, addr, partlength, - flags); - else - rcode = RCODE_TYPE_ERROR; - - data += partlength; - length -= partlength; - addr += partlength; - - if ((rcode != RCODE_COMPLETE) || !length) - break; - } - } - read_unlock(&addr_space_lock); - - if (length && (rcode == RCODE_COMPLETE)) - rcode = RCODE_ADDRESS_ERROR; - return rcode; -} - -int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags) -{ - struct hpsb_address_serve *as; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - if (as->op->lock) - rcode = as->op->lock(host, nodeid, store, addr, - data, arg, ext_tcode, - flags); - else - rcode = RCODE_TYPE_ERROR; - break; - } - } - read_unlock(&addr_space_lock); - return rcode; -} - -int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags) -{ - struct hpsb_address_serve *as; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - if (as->op->lock64) - rcode = as->op->lock64(host, nodeid, store, - addr, data, arg, - ext_tcode, flags); - else - rcode = RCODE_TYPE_ERROR; - break; - } - } - read_unlock(&addr_space_lock); - return rcode; -} diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h deleted file mode 100644 index 9dba89fc60a..00000000000 --- a/drivers/ieee1394/highlevel.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef IEEE1394_HIGHLEVEL_H -#define IEEE1394_HIGHLEVEL_H - -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/types.h> - -struct module; - -#include "ieee1394_types.h" - -struct hpsb_host; - -/* internal to ieee1394 core */ -struct hpsb_address_serve { - struct list_head host_list; /* per host list */ - struct list_head hl_list; /* hpsb_highlevel list */ - const struct hpsb_address_ops *op; - struct hpsb_host *host; - u64 start; /* first address handled, quadlet aligned */ - u64 end; /* first address behind, quadlet aligned */ -}; - -/* Only the following structures are of interest to actual highlevel drivers. */ - -struct hpsb_highlevel { - const char *name; - - /* Any of the following pointers can legally be NULL. */ - - /* New host initialized. Will also be called during - * hpsb_register_highlevel for all hosts already installed. */ - void (*add_host)(struct hpsb_host *host); - - /* Host about to be removed. Will also be called during - * hpsb_unregister_highlevel once for each host. */ - void (*remove_host)(struct hpsb_host *host); - - /* Host experienced bus reset with possible configuration changes. - * Note that this one may occur during interrupt/bottom half handling. - * You can not expect to be able to do stock hpsb_reads. */ - void (*host_reset)(struct hpsb_host *host); - - /* A write request was received on either the FCP_COMMAND (direction = - * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg - * contains the cts field (first byte of data). */ - void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction, - int cts, u8 *data, size_t length); - - /* These are initialized by the subsystem when the - * hpsb_higlevel is registered. */ - struct list_head hl_list; - struct list_head irq_list; - struct list_head addr_list; - - struct list_head host_info_list; - rwlock_t host_info_lock; -}; - -struct hpsb_address_ops { - /* - * Null function pointers will make the respective operation complete - * with RCODE_TYPE_ERROR. Makes for easy to implement read-only - * registers (just leave everything but read NULL). - * - * All functions shall return appropriate IEEE 1394 rcodes. - */ - - /* These functions have to implement block reads for themselves. - * - * These functions either return a response code or a negative number. - * In the first case a response will be generated. In the latter case, - * no response will be sent and the driver which handled the request - * will send the response itself. */ - int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 flags); - int (*write)(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags); - - /* Lock transactions: write results of ext_tcode operation into - * *store. */ - int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags); - int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags); -}; - -void highlevel_add_host(struct hpsb_host *host); -void highlevel_remove_host(struct hpsb_host *host); -void highlevel_host_reset(struct hpsb_host *host); -int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, - unsigned int length, u16 flags); -int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, - u64 addr, unsigned int length, u16 flags); -int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags); -int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags); -void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, - void *data, size_t length); - -/** - * hpsb_init_highlevel - initialize a struct hpsb_highlevel - * - * This is only necessary if hpsb_get_hostinfo_bykey can be called - * before hpsb_register_highlevel. - */ -static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl) -{ - rwlock_init(&hl->host_info_lock); - INIT_LIST_HEAD(&hl->host_info_list); -} -void hpsb_register_highlevel(struct hpsb_highlevel *hl); -void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); - -u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, - struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 size, u64 alignment, - u64 start, u64 end); -int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 start, u64 end); -int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - u64 start); - -void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); -void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - size_t data_size); -void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); -void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned long key); -void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); -int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - void *data); - -#endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c deleted file mode 100644 index e947d8ffac8..00000000000 --- a/drivers/ieee1394/hosts.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Low level (host adapter) management. - * - * Copyright (C) 1999 Andreas E. Bombe - * Copyright (C) 1999 Emanuel Pirker - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/list.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/timer.h> -#include <linux/jiffies.h> -#include <linux/mutex.h> - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "nodemgr.h" -#include "csr.h" -#include "config_roms.h" - - -static void delayed_reset_bus(struct work_struct *work) -{ - struct hpsb_host *host = - container_of(work, struct hpsb_host, delayed_reset.work); - u8 generation = host->csr.generation + 1; - - /* The generation field rolls over to 2 rather than 0 per IEEE - * 1394a-2000. */ - if (generation > 0xf || generation < 2) - generation = 2; - - csr_set_bus_info_generation(host->csr.rom, generation); - if (csr1212_generate_csr_image(host->csr.rom) != CSR1212_SUCCESS) { - /* CSR image creation failed. - * Reset generation field and do not issue a bus reset. */ - csr_set_bus_info_generation(host->csr.rom, - host->csr.generation); - return; - } - - host->csr.generation = generation; - - host->update_config_rom = 0; - if (host->driver->set_hw_config_rom) - host->driver->set_hw_config_rom(host, - host->csr.rom->bus_info_data); - - host->csr.gen_timestamp[host->csr.generation] = jiffies; - hpsb_reset_bus(host, SHORT_RESET); -} - -static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p) -{ - return 0; -} - -static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg) -{ - return -1; -} - -static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command, - unsigned long arg) -{ - return -1; -} - -static struct hpsb_host_driver dummy_driver = { - .transmit_packet = dummy_transmit_packet, - .devctl = dummy_devctl, - .isoctl = dummy_isoctl -}; - -static int alloc_hostnum_cb(struct hpsb_host *host, void *__data) -{ - int *hostnum = __data; - - if (host->id == *hostnum) - return 1; - - return 0; -} - -static DEFINE_MUTEX(host_num_alloc); - -/** - * hpsb_alloc_host - allocate a new host controller. - * @drv: the driver that will manage the host controller - * @extra: number of extra bytes to allocate for the driver - * - * Allocate a &hpsb_host and initialize the general subsystem specific - * fields. If the driver needs to store per host data, as drivers - * usually do, the amount of memory required can be specified by the - * @extra parameter. Once allocated, the driver should initialize the - * driver specific parts, enable the controller and make it available - * to the general subsystem using hpsb_add_host(). - * - * Return Value: a pointer to the &hpsb_host if successful, %NULL if - * no memory was available. - */ -struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, - struct device *dev) -{ - struct hpsb_host *h; - int i; - int hostnum = 0; - - h = kzalloc(sizeof(*h) + extra, GFP_KERNEL); - if (!h) - return NULL; - - h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h); - if (!h->csr.rom) - goto fail; - - h->hostdata = h + 1; - h->driver = drv; - - INIT_LIST_HEAD(&h->pending_packets); - INIT_LIST_HEAD(&h->addr_space); - - for (i = 2; i < 16; i++) - h->csr.gen_timestamp[i] = jiffies - 60 * HZ; - - atomic_set(&h->generation, 0); - - INIT_DELAYED_WORK(&h->delayed_reset, delayed_reset_bus); - - init_timer(&h->timeout); - h->timeout.data = (unsigned long) h; - h->timeout.function = abort_timedouts; - h->timeout_interval = HZ / 20; /* 50ms, half of minimum SPLIT_TIMEOUT */ - - h->topology_map = h->csr.topology_map + 3; - h->speed_map = (u8 *)(h->csr.speed_map + 2); - - mutex_lock(&host_num_alloc); - while (nodemgr_for_each_host(&hostnum, alloc_hostnum_cb)) - hostnum++; - mutex_unlock(&host_num_alloc); - h->id = hostnum; - - memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device)); - h->device.parent = dev; - set_dev_node(&h->device, dev_to_node(dev)); - dev_set_name(&h->device, "fw-host%d", h->id); - - h->host_dev.parent = &h->device; - h->host_dev.class = &hpsb_host_class; - dev_set_name(&h->host_dev, "fw-host%d", h->id); - - if (device_register(&h->device)) - goto fail; - if (device_register(&h->host_dev)) { - device_unregister(&h->device); - goto fail; - } - get_device(&h->device); - - return h; - -fail: - kfree(h); - return NULL; -} - -int hpsb_add_host(struct hpsb_host *host) -{ - if (hpsb_default_host_entry(host)) - return -ENOMEM; - - highlevel_add_host(host); - return 0; -} - -void hpsb_resume_host(struct hpsb_host *host) -{ - if (host->driver->set_hw_config_rom) - host->driver->set_hw_config_rom(host, - host->csr.rom->bus_info_data); - host->driver->devctl(host, RESET_BUS, SHORT_RESET); -} - -void hpsb_remove_host(struct hpsb_host *host) -{ - host->is_shutdown = 1; - - cancel_delayed_work(&host->delayed_reset); - flush_scheduled_work(); - - host->driver = &dummy_driver; - highlevel_remove_host(host); - - device_unregister(&host->host_dev); - device_unregister(&host->device); -} - -/** - * hpsb_update_config_rom_image - updates configuration ROM image of a host - * - * Updates the configuration ROM image of a host. rom_version must be the - * current version, otherwise it will fail with return value -1. If this - * host does not support config-rom-update, it will return -%EINVAL. - * Return value 0 indicates success. - */ -int hpsb_update_config_rom_image(struct hpsb_host *host) -{ - unsigned long reset_delay; - int next_gen = host->csr.generation + 1; - - if (!host->update_config_rom) - return -EINVAL; - - if (next_gen > 0xf) - next_gen = 2; - - /* Stop the delayed interrupt, we're about to change the config rom and - * it would be a waste to do a bus reset twice. */ - cancel_delayed_work(&host->delayed_reset); - - /* IEEE 1394a-2000 prohibits using the same generation number - * twice in a 60 second period. */ - if (time_before(jiffies, host->csr.gen_timestamp[next_gen] + 60 * HZ)) - /* Wait 60 seconds from the last time this generation number was - * used. */ - reset_delay = - (60 * HZ) + host->csr.gen_timestamp[next_gen] - jiffies; - else - /* Wait 1 second in case some other code wants to change the - * Config ROM in the near future. */ - reset_delay = HZ; - - PREPARE_DELAYED_WORK(&host->delayed_reset, delayed_reset_bus); - schedule_delayed_work(&host->delayed_reset, reset_delay); - - return 0; -} diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h deleted file mode 100644 index 49c359022c5..00000000000 --- a/drivers/ieee1394/hosts.h +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef _IEEE1394_HOSTS_H -#define _IEEE1394_HOSTS_H - -#include <linux/device.h> -#include <linux/list.h> -#include <linux/timer.h> -#include <linux/types.h> -#include <linux/workqueue.h> -#include <asm/atomic.h> - -struct pci_dev; -struct module; - -#include "ieee1394_types.h" -#include "csr.h" -#include "highlevel.h" - -struct hpsb_packet; -struct hpsb_iso; - -struct hpsb_host { - struct list_head host_list; - - void *hostdata; - - atomic_t generation; - - struct list_head pending_packets; - struct timer_list timeout; - unsigned long timeout_interval; - - int node_count; /* number of identified nodes on this bus */ - int selfid_count; /* total number of SelfIDs received */ - int nodes_active; /* number of nodes with active link layer */ - - nodeid_t node_id; /* node ID of this host */ - nodeid_t irm_id; /* ID of this bus' isochronous resource manager */ - nodeid_t busmgr_id; /* ID of this bus' bus manager */ - - /* this nodes state */ - unsigned in_bus_reset:1; - unsigned is_shutdown:1; - unsigned resume_packet_sent:1; - - /* this nodes' duties on the bus */ - unsigned is_root:1; - unsigned is_cycmst:1; - unsigned is_irm:1; - unsigned is_busmgr:1; - - int reset_retries; - quadlet_t *topology_map; - u8 *speed_map; - - int id; - struct hpsb_host_driver *driver; - struct pci_dev *pdev; - struct device device; - struct device host_dev; - - struct delayed_work delayed_reset; - unsigned config_roms:31; - unsigned update_config_rom:1; - - struct list_head addr_space; - u64 low_addr_space; /* upper bound of physical DMA area */ - u64 middle_addr_space; /* upper bound of posted write area */ - - u8 speed[ALL_NODES]; /* speed between each node and local node */ - - /* per node tlabel allocation */ - u8 next_tl[ALL_NODES]; - struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES]; - - struct csr_control csr; - - struct hpsb_address_serve dummy_zero_addr; - struct hpsb_address_serve dummy_max_addr; -}; - -enum devctl_cmd { - /* Host is requested to reset its bus and cancel all outstanding async - * requests. If arg == 1, it shall also attempt to become root on the - * bus. Return void. */ - RESET_BUS, - - /* Arg is void, return value is the hardware cycle counter value. */ - GET_CYCLE_COUNTER, - - /* Set the hardware cycle counter to the value in arg, return void. - * FIXME - setting is probably not required. */ - SET_CYCLE_COUNTER, - - /* Configure hardware for new bus ID in arg, return void. */ - SET_BUS_ID, - - /* If arg true, start sending cycle start packets, stop if arg == 0. - * Return void. */ - ACT_CYCLE_MASTER, - - /* Cancel all outstanding async requests without resetting the bus. - * Return void. */ - CANCEL_REQUESTS, -}; - -enum isoctl_cmd { - /* rawiso API - see iso.h for the meanings of these commands - * (they correspond exactly to the hpsb_iso_* API functions) - * INIT = allocate resources - * START = begin transmission/reception - * STOP = halt transmission/reception - * QUEUE/RELEASE = produce/consume packets - * SHUTDOWN = deallocate resources - */ - - XMIT_INIT, - XMIT_START, - XMIT_STOP, - XMIT_QUEUE, - XMIT_SHUTDOWN, - - RECV_INIT, - RECV_LISTEN_CHANNEL, /* multi-channel only */ - RECV_UNLISTEN_CHANNEL, /* multi-channel only */ - RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */ - RECV_START, - RECV_STOP, - RECV_RELEASE, - RECV_SHUTDOWN, - RECV_FLUSH -}; - -enum reset_types { - /* 166 microsecond reset -- only type of reset available on - non-1394a capable controllers */ - LONG_RESET, - - /* Short (arbitrated) reset -- only available on 1394a capable - controllers */ - SHORT_RESET, - - /* Variants that set force_root before issueing the bus reset */ - LONG_RESET_FORCE_ROOT, SHORT_RESET_FORCE_ROOT, - - /* Variants that clear force_root before issueing the bus reset */ - LONG_RESET_NO_FORCE_ROOT, SHORT_RESET_NO_FORCE_ROOT -}; - -struct hpsb_host_driver { - struct module *owner; - const char *name; - - /* The hardware driver may optionally support a function that is used - * to set the hardware ConfigROM if the hardware supports handling - * reads to the ConfigROM on its own. */ - void (*set_hw_config_rom)(struct hpsb_host *host, - __be32 *config_rom); - - /* This function shall implement packet transmission based on - * packet->type. It shall CRC both parts of the packet (unless - * packet->type == raw) and do byte-swapping as necessary or instruct - * the hardware to do so. It can return immediately after the packet - * was queued for sending. After sending, hpsb_sent_packet() has to be - * called. Return 0 on success, negative errno on failure. - * NOTE: The function must be callable in interrupt context. - */ - int (*transmit_packet)(struct hpsb_host *host, - struct hpsb_packet *packet); - - /* This function requests miscellanous services from the driver, see - * above for command codes and expected actions. Return -1 for unknown - * command, though that should never happen. - */ - int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg); - - /* ISO transmission/reception functions. Return 0 on success, -1 - * (or -EXXX errno code) on failure. If the low-level driver does not - * support the new ISO API, set isoctl to NULL. - */ - int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command, - unsigned long arg); - - /* This function is mainly to redirect local CSR reads/locks to the iso - * management registers (bus manager id, bandwidth available, channels - * available) to the hardware registers in OHCI. reg is 0,1,2,3 for bus - * mgr, bwdth avail, ch avail hi, ch avail lo respectively (the same ids - * as OHCI uses). data and compare are the new data and expected data - * respectively, return value is the old value. - */ - quadlet_t (*hw_csr_reg) (struct hpsb_host *host, int reg, - quadlet_t data, quadlet_t compare); -}; - -struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, - struct device *dev); -int hpsb_add_host(struct hpsb_host *host); -void hpsb_resume_host(struct hpsb_host *host); -void hpsb_remove_host(struct hpsb_host *host); -int hpsb_update_config_rom_image(struct hpsb_host *host); - -#endif /* _IEEE1394_HOSTS_H */ diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h deleted file mode 100644 index 46878fef136..00000000000 --- a/drivers/ieee1394/ieee1394-ioctl.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Base file for all ieee1394 ioctl's. - * Linux-1394 has allocated base '#' with a range of 0x00-0x3f. - */ - -#ifndef __IEEE1394_IOCTL_H -#define __IEEE1394_IOCTL_H - -#include <linux/ioctl.h> -#include <linux/types.h> - -/* DV1394 Gets 10 */ - -/* Get the driver ready to transmit video. pass a struct dv1394_init* as - * the parameter (see below), or NULL to get default parameters */ -#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init) - -/* Stop transmitting video and free the ringbuffer */ -#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07) - -/* Submit N new frames to be transmitted, where the index of the first new - * frame is first_clear_buffer, and the index of the last new frame is - * (first_clear_buffer + N) % n_frames */ -#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08) - -/* Block until N buffers are clear (pass N as the parameter) Because we - * re-transmit the last frame on underrun, there will at most be n_frames - * - 1 clear frames at any time */ -#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09) - -/* Capture new frames that have been received, where the index of the - * first new frame is first_clear_buffer, and the index of the last new - * frame is (first_clear_buffer + N) % n_frames */ -#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a) - -/* Tell card to start receiving DMA */ -#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b) - -/* Pass a struct dv1394_status* as the parameter */ -#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status) - - -/* Video1394 Gets 10 */ - -#define VIDEO1394_IOC_LISTEN_CHANNEL \ - _IOWR('#', 0x10, struct video1394_mmap) -#define VIDEO1394_IOC_UNLISTEN_CHANNEL \ - _IOW ('#', 0x11, int) -#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \ - _IOW ('#', 0x12, struct video1394_wait) -#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \ - _IOWR('#', 0x13, struct video1394_wait) -#define VIDEO1394_IOC_TALK_CHANNEL \ - _IOWR('#', 0x14, struct video1394_mmap) -#define VIDEO1394_IOC_UNTALK_CHANNEL \ - _IOW ('#', 0x15, int) -/* - * This one is broken: it really wanted - * "sizeof (struct video1394_wait) + sizeof (struct video1394_queue_variable)" - * but got just a "size_t" - */ -#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \ - _IOW ('#', 0x16, size_t) -#define VIDEO1394_IOC_TALK_WAIT_BUFFER \ - _IOW ('#', 0x17, struct video1394_wait) -#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \ - _IOWR('#', 0x18, struct video1394_wait) - - -/* Raw1394's ISO interface */ -#define RAW1394_IOC_ISO_XMIT_INIT \ - _IOW ('#', 0x1a, struct raw1394_iso_status) -#define RAW1394_IOC_ISO_RECV_INIT \ - _IOWR('#', 0x1b, struct raw1394_iso_status) -#define RAW1394_IOC_ISO_RECV_START \ - _IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3) -#define RAW1394_IOC_ISO_XMIT_START \ - _IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2) -#define RAW1394_IOC_ISO_XMIT_RECV_STOP \ - _IO ('#', 0x1e) -#define RAW1394_IOC_ISO_GET_STATUS \ - _IOR ('#', 0x1f, struct raw1394_iso_status) -#define RAW1394_IOC_ISO_SHUTDOWN \ - _IO ('#', 0x20) -#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \ - _IO ('#', 0x21) -#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \ - _IOW ('#', 0x22, unsigned char) -#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \ - _IOW ('#', 0x23, unsigned char) -#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \ - _IOW ('#', 0x24, __u64) -#define RAW1394_IOC_ISO_RECV_PACKETS \ - _IOW ('#', 0x25, struct raw1394_iso_packets) -#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \ - _IOW ('#', 0x26, unsigned int) -#define RAW1394_IOC_ISO_XMIT_PACKETS \ - _IOW ('#', 0x27, struct raw1394_iso_packets) -#define RAW1394_IOC_ISO_XMIT_SYNC \ - _IO ('#', 0x28) -#define RAW1394_IOC_ISO_RECV_FLUSH \ - _IO ('#', 0x29) -#define RAW1394_IOC_GET_CYCLE_TIMER \ - _IOR ('#', 0x30, struct raw1394_cycle_timer) - -#endif /* __IEEE1394_IOCTL_H */ diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h deleted file mode 100644 index af320e2c507..00000000000 --- a/drivers/ieee1394/ieee1394.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Generic IEEE 1394 definitions - */ - -#ifndef _IEEE1394_IEEE1394_H -#define _IEEE1394_IEEE1394_H - -#define TCODE_WRITEQ 0x0 -#define TCODE_WRITEB 0x1 -#define TCODE_WRITE_RESPONSE 0x2 -#define TCODE_READQ 0x4 -#define TCODE_READB 0x5 -#define TCODE_READQ_RESPONSE 0x6 -#define TCODE_READB_RESPONSE 0x7 -#define TCODE_CYCLE_START 0x8 -#define TCODE_LOCK_REQUEST 0x9 -#define TCODE_ISO_DATA 0xa -#define TCODE_STREAM_DATA 0xa -#define TCODE_LOCK_RESPONSE 0xb - -#define RCODE_COMPLETE 0x0 -#define RCODE_CONFLICT_ERROR 0x4 -#define RCODE_DATA_ERROR 0x5 -#define RCODE_TYPE_ERROR 0x6 -#define RCODE_ADDRESS_ERROR 0x7 - -#define EXTCODE_MASK_SWAP 0x1 -#define EXTCODE_COMPARE_SWAP 0x2 -#define EXTCODE_FETCH_ADD 0x3 -#define EXTCODE_LITTLE_ADD 0x4 -#define EXTCODE_BOUNDED_ADD 0x5 -#define EXTCODE_WRAP_ADD 0x6 - -#define ACK_COMPLETE 0x1 -#define ACK_PENDING 0x2 -#define ACK_BUSY_X 0x4 -#define ACK_BUSY_A 0x5 -#define ACK_BUSY_B 0x6 -#define ACK_TARDY 0xb -#define ACK_CONFLICT_ERROR 0xc -#define ACK_DATA_ERROR 0xd -#define ACK_TYPE_ERROR 0xe -#define ACK_ADDRESS_ERROR 0xf - -/* Non-standard "ACK codes" for internal use */ -#define ACKX_NONE (-1) -#define ACKX_SEND_ERROR (-2) -#define ACKX_ABORTED (-3) -#define ACKX_TIMEOUT (-4) - -#define IEEE1394_SPEED_100 0x00 -#define IEEE1394_SPEED_200 0x01 -#define IEEE1394_SPEED_400 0x02 -#define IEEE1394_SPEED_800 0x03 -#define IEEE1394_SPEED_1600 0x04 -#define IEEE1394_SPEED_3200 0x05 -#define IEEE1394_SPEED_MAX IEEE1394_SPEED_3200 - -/* Maps speed values above to a string representation */ -extern const char *hpsb_speedto_str[]; - -/* 1394a cable PHY packets */ -#define SELFID_PWRCL_NO_POWER 0x0 -#define SELFID_PWRCL_PROVIDE_15W 0x1 -#define SELFID_PWRCL_PROVIDE_30W 0x2 -#define SELFID_PWRCL_PROVIDE_45W 0x3 -#define SELFID_PWRCL_USE_1W 0x4 -#define SELFID_PWRCL_USE_3W 0x5 -#define SELFID_PWRCL_USE_6W 0x6 -#define SELFID_PWRCL_USE_10W 0x7 - -#define SELFID_PORT_CHILD 0x3 -#define SELFID_PORT_PARENT 0x2 -#define SELFID_PORT_NCONN 0x1 -#define SELFID_PORT_NONE 0x0 - -#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */ - -#define PHYPACKET_LINKON 0x40000000 -#define PHYPACKET_PHYCONFIG_R 0x00800000 -#define PHYPACKET_PHYCONFIG_T 0x00400000 -#define EXTPHYPACKET_TYPE_PING 0x00000000 -#define EXTPHYPACKET_TYPE_REMOTEACCESS_BASE 0x00040000 -#define EXTPHYPACKET_TYPE_REMOTEACCESS_PAGED 0x00140000 -#define EXTPHYPACKET_TYPE_REMOTEREPLY_BASE 0x000C0000 -#define EXTPHYPACKET_TYPE_REMOTEREPLY_PAGED 0x001C0000 -#define EXTPHYPACKET_TYPE_REMOTECOMMAND 0x00200000 -#define EXTPHYPACKET_TYPE_REMOTECONFIRMATION 0x00280000 -#define EXTPHYPACKET_TYPE_RESUME 0x003C0000 - -#define EXTPHYPACKET_TYPEMASK 0xC0FC0000 - -#define PHYPACKET_PORT_SHIFT 24 -#define PHYPACKET_GAPCOUNT_SHIFT 16 - -/* 1394a PHY register map bitmasks */ -#define PHY_00_PHYSICAL_ID 0xFC -#define PHY_00_R 0x02 /* Root */ -#define PHY_00_PS 0x01 /* Power Status*/ -#define PHY_01_RHB 0x80 /* Root Hold-Off */ -#define PHY_01_IBR 0x80 /* Initiate Bus Reset */ -#define PHY_01_GAP_COUNT 0x3F -#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */ -#define PHY_02_TOTAL_PORTS 0x1F -#define PHY_03_MAX_SPEED 0xE0 -#define PHY_03_DELAY 0x0F -#define PHY_04_LCTRL 0x80 /* Link Active Report Control */ -#define PHY_04_CONTENDER 0x40 -#define PHY_04_JITTER 0x38 -#define PHY_04_PWR_CLASS 0x07 /* Power Class */ -#define PHY_05_WATCHDOG 0x80 -#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */ -#define PHY_05_LOOP 0x20 /* Loop Detect */ -#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */ -#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */ -#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */ -#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */ -#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */ - -#include <asm/byteorder.h> - -/* '1' '3' '9' '4' in ASCII */ -#define IEEE1394_BUSID_MAGIC cpu_to_be32(0x31333934) - -#ifdef __BIG_ENDIAN_BITFIELD - -struct selfid { - u32 packet_identifier:2; /* always binary 10 */ - u32 phy_id:6; - /* byte */ - u32 extended:1; /* if true is struct ext_selfid */ - u32 link_active:1; - u32 gap_count:6; - /* byte */ - u32 speed:2; - u32 phy_delay:2; - u32 contender:1; - u32 power_class:3; - /* byte */ - u32 port0:2; - u32 port1:2; - u32 port2:2; - u32 initiated_reset:1; - u32 more_packets:1; -} __attribute__((packed)); - -struct ext_selfid { - u32 packet_identifier:2; /* always binary 10 */ - u32 phy_id:6; - /* byte */ - u32 extended:1; /* if false is struct selfid */ - u32 seq_nr:3; - u32 reserved:2; - u32 porta:2; - /* byte */ - u32 portb:2; - u32 portc:2; - u32 portd:2; - u32 porte:2; - /* byte */ - u32 portf:2; - u32 portg:2; - u32 porth:2; - u32 reserved2:1; - u32 more_packets:1; -} __attribute__((packed)); - -#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */ - -/* - * Note: these mean to be bit fields of a big endian SelfID as seen on a little - * endian machine. Without swapping. - */ - -struct selfid { - u32 phy_id:6; - u32 packet_identifier:2; /* always binary 10 */ - /* byte */ - u32 gap_count:6; - u32 link_active:1; - u32 extended:1; /* if true is struct ext_selfid */ - /* byte */ - u32 power_class:3; - u32 contender:1; - u32 phy_delay:2; - u32 speed:2; - /* byte */ - u32 more_packets:1; - u32 initiated_reset:1; - u32 port2:2; - u32 port1:2; - u32 port0:2; -} __attribute__((packed)); - -struct ext_selfid { - u32 phy_id:6; - u32 packet_identifier:2; /* always binary 10 */ - /* byte */ - u32 porta:2; - u32 reserved:2; - u32 seq_nr:3; - u32 extended:1; /* if false is struct selfid */ - /* byte */ - u32 porte:2; - u32 portd:2; - u32 portc:2; - u32 portb:2; - /* byte */ - u32 more_packets:1; - u32 reserved2:1; - u32 porth:2; - u32 portg:2; - u32 portf:2; -} __attribute__((packed)); - -#else -#error What? PDP endian? -#endif /* __BIG_ENDIAN_BITFIELD */ - -#endif /* _IEEE1394_IEEE1394_H */ diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c deleted file mode 100644 index 87233800372..00000000000 --- a/drivers/ieee1394/ieee1394_core.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Core support: hpsb_packet management, packet handling and forwarding to - * highlevel or lowlevel code - * - * Copyright (C) 1999, 2000 Andreas E. Bombe - * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at> - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * loopback functionality in hpsb_send_packet - * allow highlevel drivers to disable automatic response generation - * and to generate responses themselves (deferred) - * - */ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/bitops.h> -#include <linux/kdev_t.h> -#include <linux/freezer.h> -#include <linux/suspend.h> -#include <linux/kthread.h> -#include <linux/preempt.h> -#include <linux/time.h> - -#include <asm/system.h> -#include <asm/byteorder.h> - -#include "ieee1394_types.h" -#include "ieee1394.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "ieee1394_transactions.h" -#include "csr.h" -#include "nodemgr.h" -#include "dma.h" -#include "iso.h" -#include "config_roms.h" - -/* - * Disable the nodemgr detection and config rom reading functionality. - */ -static int disable_nodemgr; -module_param(disable_nodemgr, int, 0444); -MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality."); - -/* Disable Isochronous Resource Manager functionality */ -int hpsb_disable_irm = 0; -module_param_named(disable_irm, hpsb_disable_irm, bool, 0444); -MODULE_PARM_DESC(disable_irm, - "Disable Isochronous Resource Manager functionality."); - -/* We are GPL, so treat us special */ -MODULE_LICENSE("GPL"); - -/* Some globals used */ -const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" }; -struct class *hpsb_protocol_class; - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -static void dump_packet(const char *text, quadlet_t *data, int size, int speed) -{ - int i; - - size /= 4; - size = (size > 4 ? 4 : size); - - printk(KERN_DEBUG "ieee1394: %s", text); - if (speed > -1 && speed < 6) - printk(" at %s", hpsb_speedto_str[speed]); - printk(":"); - for (i = 0; i < size; i++) - printk(" %08x", data[i]); - printk("\n"); -} -#else -#define dump_packet(a,b,c,d) do {} while (0) -#endif - -static void abort_requests(struct hpsb_host *host); -static void queue_packet_complete(struct hpsb_packet *packet); - - -/** - * hpsb_set_packet_complete_task - set task that runs when a packet completes - * @packet: the packet whose completion we want the task added to - * @routine: function to call - * @data: data (if any) to pass to the above function - * - * Set the task that runs when a packet completes. You cannot call this more - * than once on a single packet before it is sent. - * - * Typically, the complete @routine is responsible to call hpsb_free_packet(). - */ -void hpsb_set_packet_complete_task(struct hpsb_packet *packet, - void (*routine)(void *), void *data) -{ - WARN_ON(packet->complete_routine != NULL); - packet->complete_routine = routine; - packet->complete_data = data; - return; -} - -/** - * hpsb_alloc_packet - allocate new packet structure - * @data_size: size of the data block to be allocated, in bytes - * - * This function allocates, initializes and returns a new &struct hpsb_packet. - * It can be used in interrupt context. A header block is always included and - * initialized with zeros. Its size is big enough to contain all possible 1394 - * headers. The data block is only allocated if @data_size is not zero. - * - * For packets for which responses will be received the @data_size has to be big - * enough to contain the response's data block since no further allocation - * occurs at response matching time. - * - * The packet's generation value will be set to the current generation number - * for ease of use. Remember to overwrite it with your own recorded generation - * number if you can not be sure that your code will not race with a bus reset. - * - * Return value: A pointer to a &struct hpsb_packet or NULL on allocation - * failure. - */ -struct hpsb_packet *hpsb_alloc_packet(size_t data_size) -{ - struct hpsb_packet *packet; - - data_size = ((data_size + 3) & ~3); - - packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC); - if (!packet) - return NULL; - - packet->state = hpsb_unused; - packet->generation = -1; - INIT_LIST_HEAD(&packet->driver_list); - INIT_LIST_HEAD(&packet->queue); - atomic_set(&packet->refcnt, 1); - - if (data_size) { - packet->data = packet->embedded_data; - packet->allocated_data_size = data_size; - } - return packet; -} - -/** - * hpsb_free_packet - free packet and data associated with it - * @packet: packet to free (is NULL safe) - * - * Frees @packet->data only if it was allocated through hpsb_alloc_packet(). - */ -void hpsb_free_packet(struct hpsb_packet *packet) -{ - if (packet && atomic_dec_and_test(&packet->refcnt)) { - BUG_ON(!list_empty(&packet->driver_list) || - !list_empty(&packet->queue)); - kfree(packet); - } -} - -/** - * hpsb_reset_bus - initiate bus reset on the given host - * @host: host controller whose bus to reset - * @type: one of enum reset_types - * - * Returns 1 if bus reset already in progress, 0 otherwise. - */ -int hpsb_reset_bus(struct hpsb_host *host, int type) -{ - if (!host->in_bus_reset) { - host->driver->devctl(host, RESET_BUS, type); - return 0; - } else { - return 1; - } -} - -/** - * hpsb_read_cycle_timer - read cycle timer register and system time - * @host: host whose isochronous cycle timer register is read - * @cycle_timer: address of bitfield to return the register contents - * @local_time: address to return the system time - * - * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This - * format is also read from non-OHCI controllers. * @local_time contains the - * system time in microseconds since the Epoch, read at the moment when the - * cycle timer was read. - * - * Return value: 0 for success or error number otherwise. - */ -int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, - u64 *local_time) -{ - int ctr; - struct timeval tv; - unsigned long flags; - - if (!host || !cycle_timer || !local_time) - return -EINVAL; - - preempt_disable(); - local_irq_save(flags); - - ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0); - if (ctr) - do_gettimeofday(&tv); - - local_irq_restore(flags); - preempt_enable(); - - if (!ctr) - return -EIO; - *cycle_timer = ctr; - *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; - return 0; -} - -/** - * hpsb_bus_reset - notify a bus reset to the core - * - * For host driver module usage. Safe to use in interrupt context, although - * quite complex; so you may want to run it in the bottom rather than top half. - * - * Returns 1 if bus reset already in progress, 0 otherwise. - */ -int hpsb_bus_reset(struct hpsb_host *host) -{ - if (host->in_bus_reset) { - HPSB_NOTICE("%s called while bus reset already in progress", - __func__); - return 1; - } - - abort_requests(host); - host->in_bus_reset = 1; - host->irm_id = -1; - host->is_irm = 0; - host->busmgr_id = -1; - host->is_busmgr = 0; - host->is_cycmst = 0; - host->node_count = 0; - host->selfid_count = 0; - - return 0; -} - - -/* - * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in - * case verification failed. - */ -static int check_selfids(struct hpsb_host *host) -{ - int nodeid = -1; - int rest_of_selfids = host->selfid_count; - struct selfid *sid = (struct selfid *)host->topology_map; - struct ext_selfid *esid; - int esid_seq = 23; - - host->nodes_active = 0; - - while (rest_of_selfids--) { - if (!sid->extended) { - nodeid++; - esid_seq = 0; - - if (sid->phy_id != nodeid) { - HPSB_INFO("SelfIDs failed monotony check with " - "%d", sid->phy_id); - return 0; - } - - if (sid->link_active) { - host->nodes_active++; - if (sid->contender) - host->irm_id = LOCAL_BUS | sid->phy_id; - } - } else { - esid = (struct ext_selfid *)sid; - - if ((esid->phy_id != nodeid) - || (esid->seq_nr != esid_seq)) { - HPSB_INFO("SelfIDs failed monotony check with " - "%d/%d", esid->phy_id, esid->seq_nr); - return 0; - } - esid_seq++; - } - sid++; - } - - esid = (struct ext_selfid *)(sid - 1); - while (esid->extended) { - if ((esid->porta == SELFID_PORT_PARENT) || - (esid->portb == SELFID_PORT_PARENT) || - (esid->portc == SELFID_PORT_PARENT) || - (esid->portd == SELFID_PORT_PARENT) || - (esid->porte == SELFID_PORT_PARENT) || - (esid->portf == SELFID_PORT_PARENT) || - (esid->portg == SELFID_PORT_PARENT) || - (esid->porth == SELFID_PORT_PARENT)) { - HPSB_INFO("SelfIDs failed root check on " - "extended SelfID"); - return 0; - } - esid--; - } - - sid = (struct selfid *)esid; - if ((sid->port0 == SELFID_PORT_PARENT) || - (sid->port1 == SELFID_PORT_PARENT) || - (sid->port2 == SELFID_PORT_PARENT)) { - HPSB_INFO("SelfIDs failed root check"); - return 0; - } - - host->node_count = nodeid + 1; - return 1; -} - -static void build_speed_map(struct hpsb_host *host, int nodecount) -{ - u8 cldcnt[nodecount]; - u8 *map = host->speed_map; - u8 *speedcap = host->speed; - u8 local_link_speed = host->csr.lnk_spd; - struct selfid *sid; - struct ext_selfid *esid; - int i, j, n; - - for (i = 0; i < (nodecount * 64); i += 64) { - for (j = 0; j < nodecount; j++) { - map[i+j] = IEEE1394_SPEED_MAX; - } - } - - for (i = 0; i < nodecount; i++) { - cldcnt[i] = 0; - } - - /* find direct children count and speed */ - for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1], - n = nodecount - 1; - (void *)sid >= (void *)host->topology_map; sid--) { - if (sid->extended) { - esid = (struct ext_selfid *)sid; - - if (esid->porta == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portb == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portc == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portd == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->porte == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portf == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portg == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->porth == SELFID_PORT_CHILD) cldcnt[n]++; - } else { - if (sid->port0 == SELFID_PORT_CHILD) cldcnt[n]++; - if (sid->port1 == SELFID_PORT_CHILD) cldcnt[n]++; - if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++; - - speedcap[n] = sid->speed; - if (speedcap[n] > local_link_speed) - speedcap[n] = local_link_speed; - n--; - } - } - - /* set self mapping */ - for (i = 0; i < nodecount; i++) { - map[64*i + i] = speedcap[i]; - } - - /* fix up direct children count to total children count; - * also fix up speedcaps for sibling and parent communication */ - for (i = 1; i < nodecount; i++) { - for (j = cldcnt[i], n = i - 1; j > 0; j--) { - cldcnt[i] += cldcnt[n]; - speedcap[n] = min(speedcap[n], speedcap[i]); - n -= cldcnt[n] + 1; - } - } - - for (n = 0; n < nodecount; n++) { - for (i = n - cldcnt[n]; i <= n; i++) { - for (j = 0; j < (n - cldcnt[n]); j++) { - map[j*64 + i] = map[i*64 + j] = - min(map[i*64 + j], speedcap[n]); - } - for (j = n + 1; j < nodecount; j++) { - map[j*64 + i] = map[i*64 + j] = - min(map[i*64 + j], speedcap[n]); - } - } - } - - /* assume a maximum speed for 1394b PHYs, nodemgr will correct it */ - if (local_link_speed > SELFID_SPEED_UNKNOWN) - for (i = 0; i < nodecount; i++) - if (speedcap[i] == SELFID_SPEED_UNKNOWN) - speedcap[i] = local_link_speed; -} - - -/** - * hpsb_selfid_received - hand over received selfid packet to the core - * - * For host driver module usage. Safe to use in interrupt context. - * - * The host driver should have done a successful complement check (second - * quadlet is complement of first) beforehand. - */ -void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid) -{ - if (host->in_bus_reset) { - HPSB_VERBOSE("Including SelfID 0x%x", sid); - host->topology_map[host->selfid_count++] = sid; - } else { - HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d", - sid, NODEID_TO_BUS(host->node_id)); - } -} - -/** - * hpsb_selfid_complete - notify completion of SelfID stage to the core - * - * For host driver module usage. Safe to use in interrupt context, although - * quite complex; so you may want to run it in the bottom rather than top half. - * - * Notify completion of SelfID stage to the core and report new physical ID - * and whether host is root now. - */ -void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) -{ - if (!host->in_bus_reset) - HPSB_NOTICE("SelfID completion called outside of bus reset!"); - - host->node_id = LOCAL_BUS | phyid; - host->is_root = isroot; - - if (!check_selfids(host)) { - if (host->reset_retries++ < 20) { - /* selfid stage did not complete without error */ - HPSB_NOTICE("Error in SelfID stage, resetting"); - host->in_bus_reset = 0; - /* this should work from ohci1394 now... */ - hpsb_reset_bus(host, LONG_RESET); - return; - } else { - HPSB_NOTICE("Stopping out-of-control reset loop"); - HPSB_NOTICE("Warning - topology map and speed map will not be valid"); - host->reset_retries = 0; - } - } else { - host->reset_retries = 0; - build_speed_map(host, host->node_count); - } - - HPSB_VERBOSE("selfid_complete called with successful SelfID stage " - "... irm_id: 0x%X node_id: 0x%X",host->irm_id,host->node_id); - - /* irm_id is kept up to date by check_selfids() */ - if (host->irm_id == host->node_id) { - host->is_irm = 1; - } else { - host->is_busmgr = 0; - host->is_irm = 0; - } - - if (isroot) { - host->driver->devctl(host, ACT_CYCLE_MASTER, 1); - host->is_cycmst = 1; - } - atomic_inc(&host->generation); - host->in_bus_reset = 0; - highlevel_host_reset(host); -} - -static DEFINE_SPINLOCK(pending_packets_lock); - -/** - * hpsb_packet_sent - notify core of sending a packet - * - * For host driver module usage. Safe to call from within a transmit packet - * routine. - * - * Notify core of sending a packet. Ackcode is the ack code returned for async - * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE - * for other cases (internal errors that don't justify a panic). - */ -void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, - int ackcode) -{ - unsigned long flags; - - spin_lock_irqsave(&pending_packets_lock, flags); - - packet->ack_code = ackcode; - - if (packet->no_waiter || packet->state == hpsb_complete) { - /* if packet->no_waiter, must not have a tlabel allocated */ - spin_unlock_irqrestore(&pending_packets_lock, flags); - hpsb_free_packet(packet); - return; - } - - atomic_dec(&packet->refcnt); /* drop HC's reference */ - /* here the packet must be on the host->pending_packets queue */ - - if (ackcode != ACK_PENDING || !packet->expect_response) { - packet->state = hpsb_complete; - list_del_init(&packet->queue); - spin_unlock_irqrestore(&pending_packets_lock, flags); - queue_packet_complete(packet); - return; - } - - packet->state = hpsb_pending; - packet->sendtime = jiffies; - - spin_unlock_irqrestore(&pending_packets_lock, flags); - - mod_timer(&host->timeout, jiffies + host->timeout_interval); -} - -/** - * hpsb_send_phy_config - transmit a PHY configuration packet on the bus - * @host: host that PHY config packet gets sent through - * @rootid: root whose force_root bit should get set (-1 = don't set force_root) - * @gapcnt: gap count value to set (-1 = don't set gap count) - * - * This function sends a PHY config packet on the bus through the specified - * host. - * - * Return value: 0 for success or negative error number otherwise. - */ -int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) -{ - struct hpsb_packet *packet; - quadlet_t d = 0; - int retval = 0; - - if (rootid >= ALL_NODES || rootid < -1 || gapcnt > 0x3f || gapcnt < -1 || - (rootid == -1 && gapcnt == -1)) { - HPSB_DEBUG("Invalid Parameter: rootid = %d gapcnt = %d", - rootid, gapcnt); - return -EINVAL; - } - - if (rootid != -1) - d |= PHYPACKET_PHYCONFIG_R | rootid << PHYPACKET_PORT_SHIFT; - if (gapcnt != -1) - d |= PHYPACKET_PHYCONFIG_T | gapcnt << PHYPACKET_GAPCOUNT_SHIFT; - - packet = hpsb_make_phypacket(host, d); - if (!packet) - return -ENOMEM; - - packet->generation = get_hpsb_generation(host); - retval = hpsb_send_packet_and_wait(packet); - hpsb_free_packet(packet); - - return retval; -} - -/** - * hpsb_send_packet - transmit a packet on the bus - * @packet: packet to send - * - * The packet is sent through the host specified in the packet->host field. - * Before sending, the packet's transmit speed is automatically determined - * using the local speed map when it is an async, non-broadcast packet. - * - * Possibilities for failure are that host is either not initialized, in bus - * reset, the packet's generation number doesn't match the current generation - * number or the host reports a transmit error. - * - * Return value: 0 on success, negative errno on failure. - */ -int hpsb_send_packet(struct hpsb_packet *packet) -{ - struct hpsb_host *host = packet->host; - - if (host->is_shutdown) - return -EINVAL; - if (host->in_bus_reset || - (packet->generation != get_hpsb_generation(host))) - return -EAGAIN; - - packet->state = hpsb_queued; - - /* This just seems silly to me */ - WARN_ON(packet->no_waiter && packet->expect_response); - - if (!packet->no_waiter || packet->expect_response) { - unsigned long flags; - - atomic_inc(&packet->refcnt); - /* Set the initial "sendtime" to 10 seconds from now, to - prevent premature expiry. If a packet takes more than - 10 seconds to hit the wire, we have bigger problems :) */ - packet->sendtime = jiffies + 10 * HZ; - spin_lock_irqsave(&pending_packets_lock, flags); - list_add_tail(&packet->queue, &host->pending_packets); - spin_unlock_irqrestore(&pending_packets_lock, flags); - } - - if (packet->node_id == host->node_id) { - /* it is a local request, so handle it locally */ - - quadlet_t *data; - size_t size = packet->data_size + packet->header_size; - - data = kmalloc(size, GFP_ATOMIC); - if (!data) { - HPSB_ERR("unable to allocate memory for concatenating header and data"); - return -ENOMEM; - } - - memcpy(data, packet->header, packet->header_size); - - if (packet->data_size) - memcpy(((u8*)data) + packet->header_size, packet->data, packet->data_size); - - dump_packet("send packet local", packet->header, packet->header_size, -1); - - hpsb_packet_sent(host, packet, packet->expect_response ? ACK_PENDING : ACK_COMPLETE); - hpsb_packet_received(host, data, size, 0); - - kfree(data); - - return 0; - } - - if (packet->type == hpsb_async && - NODEID_TO_NODE(packet->node_id) != ALL_NODES) - packet->speed_code = - host->speed[NODEID_TO_NODE(packet->node_id)]; - - dump_packet("send packet", packet->header, packet->header_size, packet->speed_code); - - return host->driver->transmit_packet(host, packet); -} - -/* We could just use complete() directly as the packet complete - * callback, but this is more typesafe, in the sense that we get a - * compiler error if the prototype for complete() changes. */ - -static void complete_packet(void *data) -{ - complete((struct completion *) data); -} - -/** - * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes - * @packet: packet to send - * - * Return value: 0 on success, negative errno on failure. - */ -int hpsb_send_packet_and_wait(struct hpsb_packet *packet) -{ - struct completion done; - int retval; - - init_completion(&done); - hpsb_set_packet_complete_task(packet, complete_packet, &done); - retval = hpsb_send_packet(packet); - if (retval == 0) - wait_for_completion(&done); - - return retval; -} - -static void send_packet_nocare(struct hpsb_packet *packet) -{ - if (hpsb_send_packet(packet) < 0) { - hpsb_free_packet(packet); - } -} - -static size_t packet_size_to_data_size(size_t packet_size, size_t header_size, - size_t buffer_size, int tcode) -{ - size_t ret = packet_size <= header_size ? 0 : packet_size - header_size; - - if (unlikely(ret > buffer_size)) - ret = buffer_size; - - if (unlikely(ret + header_size != packet_size)) - HPSB_ERR("unexpected packet size %zd (tcode %d), bug?", - packet_size, tcode); - return ret; -} - -static void handle_packet_response(struct hpsb_host *host, int tcode, - quadlet_t *data, size_t size) -{ - struct hpsb_packet *packet; - int tlabel = (data[0] >> 10) & 0x3f; - size_t header_size; - unsigned long flags; - - spin_lock_irqsave(&pending_packets_lock, flags); - - list_for_each_entry(packet, &host->pending_packets, queue) - if (packet->tlabel == tlabel && - packet->node_id == (data[1] >> 16)) - goto found; - - spin_unlock_irqrestore(&pending_packets_lock, flags); - HPSB_DEBUG("unsolicited response packet received - %s", - "no tlabel match"); - dump_packet("contents", data, 16, -1); - return; - -found: - switch (packet->tcode) { - case TCODE_WRITEQ: - case TCODE_WRITEB: - if (unlikely(tcode != TCODE_WRITE_RESPONSE)) - break; - header_size = 12; - size = 0; - goto dequeue; - - case TCODE_READQ: - if (unlikely(tcode != TCODE_READQ_RESPONSE)) - break; - header_size = 16; - size = 0; - goto dequeue; - - case TCODE_READB: - if (unlikely(tcode != TCODE_READB_RESPONSE)) - break; - header_size = 16; - size = packet_size_to_data_size(size, header_size, - packet->allocated_data_size, - tcode); - goto dequeue; - - case TCODE_LOCK_REQUEST: - if (unlikely(tcode != TCODE_LOCK_RESPONSE)) - break; - header_size = 16; - size = packet_size_to_data_size(min(size, (size_t)(16 + 8)), - header_size, - packet->allocated_data_size, - tcode); - goto dequeue; - } - - spin_unlock_irqrestore(&pending_packets_lock, flags); - HPSB_DEBUG("unsolicited response packet received - %s", - "tcode mismatch"); - dump_packet("contents", data, 16, -1); - return; - -dequeue: - list_del_init(&packet->queue); - spin_unlock_irqrestore(&pending_packets_lock, flags); - - if (packet->state == hpsb_queued) { - packet->sendtime = jiffies; - packet->ack_code = ACK_PENDING; - } - packet->state = hpsb_complete; - - memcpy(packet->header, data, header_size); - if (size) - memcpy(packet->data, data + 4, size); - - queue_packet_complete(packet); -} - - -static struct hpsb_packet *create_reply_packet(struct hpsb_host *host, - quadlet_t *data, size_t dsize) -{ - struct hpsb_packet *p; - - p = hpsb_alloc_packet(dsize); - if (unlikely(p == NULL)) { - /* FIXME - send data_error response */ - HPSB_ERR("out of memory, cannot send response packet"); - return NULL; - } - - p->type = hpsb_async; - p->state = hpsb_unused; - p->host = host; - p->node_id = data[1] >> 16; - p->tlabel = (data[0] >> 10) & 0x3f; - p->no_waiter = 1; - - p->generation = get_hpsb_generation(host); - - if (dsize % 4) - p->data[dsize / 4] = 0; - - return p; -} - -#define PREP_ASYNC_HEAD_RCODE(tc) \ - packet->tcode = tc; \ - packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ - | (1 << 8) | (tc << 4); \ - packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \ - packet->header[2] = 0 - -static void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode, - quadlet_t data) -{ - PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE); - packet->header[3] = data; - packet->header_size = 16; - packet->data_size = 0; -} - -static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, - int length) -{ - if (rcode != RCODE_COMPLETE) - length = 0; - - PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); -} - -static void fill_async_write_resp(struct hpsb_packet *packet, int rcode) -{ - PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); - packet->header_size = 12; - packet->data_size = 0; -} - -static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode, - int length) -{ - if (rcode != RCODE_COMPLETE) - length = 0; - - PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE); - packet->header[3] = (length << 16) | extcode; - packet->header_size = 16; - packet->data_size = length; -} - -static void handle_incoming_packet(struct hpsb_host *host, int tcode, - quadlet_t *data, size_t size, - int write_acked) -{ - struct hpsb_packet *packet; - int length, rcode, extcode; - quadlet_t buffer; - nodeid_t source = data[1] >> 16; - nodeid_t dest = data[0] >> 16; - u16 flags = (u16) data[0]; - u64 addr; - - /* FIXME? - * Out-of-bounds lengths are left for highlevel_read|write to cap. */ - - switch (tcode) { - case TCODE_WRITEQ: - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data + 3, - addr, 4, flags); - goto handle_write_request; - - case TCODE_WRITEB: - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data + 4, - addr, data[3] >> 16, flags); -handle_write_request: - if (rcode < 0 || write_acked || - NODEID_TO_NODE(data[0] >> 16) == NODE_MASK) - return; - /* not a broadcast write, reply */ - packet = create_reply_packet(host, data, 0); - if (packet) { - fill_async_write_resp(packet, rcode); - send_packet_nocare(packet); - } - return; - - case TCODE_READQ: - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_read(host, source, &buffer, addr, 4, flags); - if (rcode < 0) - return; - - packet = create_reply_packet(host, data, 0); - if (packet) { - fill_async_readquad_resp(packet, rcode, buffer); - send_packet_nocare(packet); - } - return; - - case TCODE_READB: - length = data[3] >> 16; - packet = create_reply_packet(host, data, length); - if (!packet) - return; - - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_read(host, source, packet->data, addr, - length, flags); - if (rcode < 0) { - hpsb_free_packet(packet); - return; - } - fill_async_readblock_resp(packet, rcode, length); - send_packet_nocare(packet); - return; - - case TCODE_LOCK_REQUEST: - length = data[3] >> 16; - extcode = data[3] & 0xffff; - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - - packet = create_reply_packet(host, data, 8); - if (!packet) - return; - - if (extcode == 0 || extcode >= 7) { - /* let switch default handle error */ - length = 0; - } - - switch (length) { - case 4: - rcode = highlevel_lock(host, source, packet->data, addr, - data[4], 0, extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 4); - break; - case 8: - if (extcode != EXTCODE_FETCH_ADD && - extcode != EXTCODE_LITTLE_ADD) { - rcode = highlevel_lock(host, source, - packet->data, addr, - data[5], data[4], - extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 4); - } else { - rcode = highlevel_lock64(host, source, - (octlet_t *)packet->data, addr, - *(octlet_t *)(data + 4), 0ULL, - extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 8); - } - break; - case 16: - rcode = highlevel_lock64(host, source, - (octlet_t *)packet->data, addr, - *(octlet_t *)(data + 6), - *(octlet_t *)(data + 4), - extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 8); - break; - default: - rcode = RCODE_TYPE_ERROR; - fill_async_lock_resp(packet, rcode, extcode, 0); - } - - if (rcode < 0) - hpsb_free_packet(packet); - else - send_packet_nocare(packet); - return; - } -} - -/** - * hpsb_packet_received - hand over received packet to the core - * - * For host driver module usage. - * - * The contents of data are expected to be the full packet but with the CRCs - * left out (data block follows header immediately), with the header (i.e. the - * first four quadlets) in machine byte order and the data block in big endian. - * *@data can be safely overwritten after this call. - * - * If the packet is a write request, @write_acked is to be set to true if it was - * ack_complete'd already, false otherwise. This argument is ignored for any - * other packet type. - */ -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, - int write_acked) -{ - int tcode; - - if (unlikely(host->in_bus_reset)) { - HPSB_DEBUG("received packet during reset; ignoring"); - return; - } - - dump_packet("received packet", data, size, -1); - - tcode = (data[0] >> 4) & 0xf; - - switch (tcode) { - case TCODE_WRITE_RESPONSE: - case TCODE_READQ_RESPONSE: - case TCODE_READB_RESPONSE: - case TCODE_LOCK_RESPONSE: - handle_packet_response(host, tcode, data, size); - break; - - case TCODE_WRITEQ: - case TCODE_WRITEB: - case TCODE_READQ: - case TCODE_READB: - case TCODE_LOCK_REQUEST: - handle_incoming_packet(host, tcode, data, size, write_acked); - break; - - case TCODE_CYCLE_START: - /* simply ignore this packet if it is passed on */ - break; - - default: - HPSB_DEBUG("received packet with bogus transaction code %d", - tcode); - break; - } -} - -static void abort_requests(struct hpsb_host *host) -{ - struct hpsb_packet *packet, *p; - struct list_head tmp; - unsigned long flags; - - host->driver->devctl(host, CANCEL_REQUESTS, 0); - - INIT_LIST_HEAD(&tmp); - spin_lock_irqsave(&pending_packets_lock, flags); - list_splice_init(&host->pending_packets, &tmp); - spin_unlock_irqrestore(&pending_packets_lock, flags); - - list_for_each_entry_safe(packet, p, &tmp, queue) { - list_del_init(&packet->queue); - packet->state = hpsb_complete; - packet->ack_code = ACKX_ABORTED; - queue_packet_complete(packet); - } -} - -void abort_timedouts(unsigned long __opaque) -{ - struct hpsb_host *host = (struct hpsb_host *)__opaque; - struct hpsb_packet *packet, *p; - struct list_head tmp; - unsigned long flags, expire, j; - - spin_lock_irqsave(&host->csr.lock, flags); - expire = host->csr.expire; - spin_unlock_irqrestore(&host->csr.lock, flags); - - j = jiffies; - INIT_LIST_HEAD(&tmp); - spin_lock_irqsave(&pending_packets_lock, flags); - - list_for_each_entry_safe(packet, p, &host->pending_packets, queue) { - if (time_before(packet->sendtime + expire, j)) - list_move_tail(&packet->queue, &tmp); - else - /* Since packets are added to the tail, the oldest - * ones are first, always. When we get to one that - * isn't timed out, the rest aren't either. */ - break; - } - if (!list_empty(&host->pending_packets)) - mod_timer(&host->timeout, j + host->timeout_interval); - - spin_unlock_irqrestore(&pending_packets_lock, flags); - - list_for_each_entry_safe(packet, p, &tmp, queue) { - list_del_init(&packet->queue); - packet->state = hpsb_complete; - packet->ack_code = ACKX_TIMEOUT; - queue_packet_complete(packet); - } -} - -static struct task_struct *khpsbpkt_thread; -static LIST_HEAD(hpsbpkt_queue); - -static void queue_packet_complete(struct hpsb_packet *packet) -{ - unsigned long flags; - - if (packet->no_waiter) { - hpsb_free_packet(packet); - return; - } - if (packet->complete_routine != NULL) { - spin_lock_irqsave(&pending_packets_lock, flags); - list_add_tail(&packet->queue, &hpsbpkt_queue); - spin_unlock_irqrestore(&pending_packets_lock, flags); - wake_up_process(khpsbpkt_thread); - } - return; -} - -/* - * Kernel thread which handles packets that are completed. This way the - * packet's "complete" function is asynchronously run in process context. - * Only packets which have a "complete" function may be sent here. - */ -static int hpsbpkt_thread(void *__hi) -{ - struct hpsb_packet *packet, *p; - struct list_head tmp; - int may_schedule; - - while (!kthread_should_stop()) { - - INIT_LIST_HEAD(&tmp); - spin_lock_irq(&pending_packets_lock); - list_splice_init(&hpsbpkt_queue, &tmp); - spin_unlock_irq(&pending_packets_lock); - - list_for_each_entry_safe(packet, p, &tmp, queue) { - list_del_init(&packet->queue); - packet->complete_routine(packet->complete_data); - } - - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irq(&pending_packets_lock); - may_schedule = list_empty(&hpsbpkt_queue); - spin_unlock_irq(&pending_packets_lock); - if (may_schedule) - schedule(); - __set_current_state(TASK_RUNNING); - } - return 0; -} - -static int __init ieee1394_init(void) -{ - int i, ret; - - /* non-fatal error */ - if (hpsb_init_config_roms()) { - HPSB_ERR("Failed to initialize some config rom entries.\n"); - HPSB_ERR("Some features may not be available\n"); - } - - khpsbpkt_thread = kthread_run(hpsbpkt_thread, NULL, "khpsbpkt"); - if (IS_ERR(khpsbpkt_thread)) { - HPSB_ERR("Failed to start hpsbpkt thread!\n"); - ret = PTR_ERR(khpsbpkt_thread); - goto exit_cleanup_config_roms; - } - - if (register_chrdev_region(IEEE1394_CORE_DEV, 256, "ieee1394")) { - HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR); - ret = -ENODEV; - goto exit_release_kernel_thread; - } - - ret = bus_register(&ieee1394_bus_type); - if (ret < 0) { - HPSB_INFO("bus register failed"); - goto release_chrdev; - } - - for (i = 0; fw_bus_attrs[i]; i++) { - ret = bus_create_file(&ieee1394_bus_type, fw_bus_attrs[i]); - if (ret < 0) { - while (i >= 0) { - bus_remove_file(&ieee1394_bus_type, - fw_bus_attrs[i--]); - } - bus_unregister(&ieee1394_bus_type); - goto release_chrdev; - } - } - - ret = class_register(&hpsb_host_class); - if (ret < 0) - goto release_all_bus; - - hpsb_protocol_class = class_create(THIS_MODULE, "ieee1394_protocol"); - if (IS_ERR(hpsb_protocol_class)) { - ret = PTR_ERR(hpsb_protocol_class); - goto release_class_host; - } - - ret = init_csr(); - if (ret) { - HPSB_INFO("init csr failed"); - ret = -ENOMEM; - goto release_class_protocol; - } - - if (disable_nodemgr) { - HPSB_INFO("nodemgr and IRM functionality disabled"); - /* We shouldn't contend for IRM with nodemgr disabled, since - nodemgr implements functionality required of ieee1394a-2000 - IRMs */ - hpsb_disable_irm = 1; - - return 0; - } - - if (hpsb_disable_irm) { - HPSB_INFO("IRM functionality disabled"); - } - - ret = init_ieee1394_nodemgr(); - if (ret < 0) { - HPSB_INFO("init nodemgr failed"); - goto cleanup_csr; - } - - return 0; - -cleanup_csr: - cleanup_csr(); -release_class_protocol: - class_destroy(hpsb_protocol_class); -release_class_host: - class_unregister(&hpsb_host_class); -release_all_bus: - for (i = 0; fw_bus_attrs[i]; i++) - bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); - bus_unregister(&ieee1394_bus_type); -release_chrdev: - unregister_chrdev_region(IEEE1394_CORE_DEV, 256); -exit_release_kernel_thread: - kthread_stop(khpsbpkt_thread); -exit_cleanup_config_roms: - hpsb_cleanup_config_roms(); - return ret; -} - -static void __exit ieee1394_cleanup(void) -{ - int i; - - if (!disable_nodemgr) - cleanup_ieee1394_nodemgr(); - - cleanup_csr(); - - class_destroy(hpsb_protocol_class); - class_unregister(&hpsb_host_class); - for (i = 0; fw_bus_attrs[i]; i++) - bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); - bus_unregister(&ieee1394_bus_type); - - kthread_stop(khpsbpkt_thread); - - hpsb_cleanup_config_roms(); - - unregister_chrdev_region(IEEE1394_CORE_DEV, 256); -} - -fs_initcall(ieee1394_init); -module_exit(ieee1394_cleanup); - -/* Exported symbols */ - -/** hosts.c **/ -EXPORT_SYMBOL(hpsb_alloc_host); -EXPORT_SYMBOL(hpsb_add_host); -EXPORT_SYMBOL(hpsb_resume_host); -EXPORT_SYMBOL(hpsb_remove_host); -EXPORT_SYMBOL(hpsb_update_config_rom_image); - -/** ieee1394_core.c **/ -EXPORT_SYMBOL(hpsb_speedto_str); -EXPORT_SYMBOL(hpsb_protocol_class); -EXPORT_SYMBOL(hpsb_set_packet_complete_task); -EXPORT_SYMBOL(hpsb_alloc_packet); -EXPORT_SYMBOL(hpsb_free_packet); -EXPORT_SYMBOL(hpsb_send_packet); -EXPORT_SYMBOL(hpsb_reset_bus); -EXPORT_SYMBOL(hpsb_read_cycle_timer); -EXPORT_SYMBOL(hpsb_bus_reset); -EXPORT_SYMBOL(hpsb_selfid_received); -EXPORT_SYMBOL(hpsb_selfid_complete); -EXPORT_SYMBOL(hpsb_packet_sent); -EXPORT_SYMBOL(hpsb_packet_received); -EXPORT_SYMBOL_GPL(hpsb_disable_irm); - -/** ieee1394_transactions.c **/ -EXPORT_SYMBOL(hpsb_get_tlabel); -EXPORT_SYMBOL(hpsb_free_tlabel); -EXPORT_SYMBOL(hpsb_make_readpacket); -EXPORT_SYMBOL(hpsb_make_writepacket); -EXPORT_SYMBOL(hpsb_make_streampacket); -EXPORT_SYMBOL(hpsb_make_lockpacket); -EXPORT_SYMBOL(hpsb_make_lock64packet); -EXPORT_SYMBOL(hpsb_make_phypacket); -EXPORT_SYMBOL(hpsb_read); -EXPORT_SYMBOL(hpsb_write); -EXPORT_SYMBOL(hpsb_lock); -EXPORT_SYMBOL(hpsb_packet_success); - -/** highlevel.c **/ -EXPORT_SYMBOL(hpsb_register_highlevel); -EXPORT_SYMBOL(hpsb_unregister_highlevel); -EXPORT_SYMBOL(hpsb_register_addrspace); -EXPORT_SYMBOL(hpsb_unregister_addrspace); -EXPORT_SYMBOL(hpsb_allocate_and_register_addrspace); -EXPORT_SYMBOL(hpsb_get_hostinfo); -EXPORT_SYMBOL(hpsb_create_hostinfo); -EXPORT_SYMBOL(hpsb_destroy_hostinfo); -EXPORT_SYMBOL(hpsb_set_hostinfo_key); -EXPORT_SYMBOL(hpsb_get_hostinfo_bykey); -EXPORT_SYMBOL(hpsb_set_hostinfo); - -/** nodemgr.c **/ -EXPORT_SYMBOL(hpsb_node_fill_packet); -EXPORT_SYMBOL(hpsb_node_write); -EXPORT_SYMBOL(__hpsb_register_protocol); -EXPORT_SYMBOL(hpsb_unregister_protocol); - -/** csr.c **/ -EXPORT_SYMBOL(hpsb_update_config_rom); - -/** dma.c **/ -EXPORT_SYMBOL(dma_prog_region_init); -EXPORT_SYMBOL(dma_prog_region_alloc); -EXPORT_SYMBOL(dma_prog_region_free); -EXPORT_SYMBOL(dma_region_init); -EXPORT_SYMBOL(dma_region_alloc); -EXPORT_SYMBOL(dma_region_free); -EXPORT_SYMBOL(dma_region_sync_for_cpu); -EXPORT_SYMBOL(dma_region_sync_for_device); -EXPORT_SYMBOL(dma_region_mmap); -EXPORT_SYMBOL(dma_region_offset_to_bus); - -/** iso.c **/ -EXPORT_SYMBOL(hpsb_iso_xmit_init); -EXPORT_SYMBOL(hpsb_iso_recv_init); -EXPORT_SYMBOL(hpsb_iso_xmit_start); -EXPORT_SYMBOL(hpsb_iso_recv_start); -EXPORT_SYMBOL(hpsb_iso_recv_listen_channel); -EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel); -EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask); -EXPORT_SYMBOL(hpsb_iso_stop); -EXPORT_SYMBOL(hpsb_iso_shutdown); -EXPORT_SYMBOL(hpsb_iso_xmit_queue_packet); -EXPORT_SYMBOL(hpsb_iso_xmit_sync); -EXPORT_SYMBOL(hpsb_iso_recv_release_packets); -EXPORT_SYMBOL(hpsb_iso_n_ready); -EXPORT_SYMBOL(hpsb_iso_packet_sent); -EXPORT_SYMBOL(hpsb_iso_packet_received); -EXPORT_SYMBOL(hpsb_iso_wake); -EXPORT_SYMBOL(hpsb_iso_recv_flush); - -/** csr1212.c **/ -EXPORT_SYMBOL(csr1212_attach_keyval_to_directory); -EXPORT_SYMBOL(csr1212_detach_keyval_from_directory); -EXPORT_SYMBOL(csr1212_get_keyval); -EXPORT_SYMBOL(csr1212_new_directory); -EXPORT_SYMBOL(csr1212_parse_keyval); -EXPORT_SYMBOL(csr1212_read); -EXPORT_SYMBOL(csr1212_release_keyval); diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h deleted file mode 100644 index 28b9f58bafd..00000000000 --- a/drivers/ieee1394/ieee1394_core.h +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef _IEEE1394_CORE_H -#define _IEEE1394_CORE_H - -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/list.h> -#include <linux/types.h> -#include <linux/cdev.h> -#include <asm/atomic.h> - -#include "hosts.h" -#include "ieee1394_types.h" - -struct hpsb_packet { - /* This struct is basically read-only for hosts with the exception of - * the data buffer contents and driver_list. */ - - /* This can be used for host driver internal linking. - * - * NOTE: This must be left in init state when the driver is done - * with it (e.g. by using list_del_init()), since the core does - * some sanity checks to make sure the packet is not on a - * driver_list when free'ing it. */ - struct list_head driver_list; - - nodeid_t node_id; - - /* hpsb_raw = send as-is, do not CRC (but still byte-swap it) */ - enum { hpsb_async, hpsb_raw } __attribute__((packed)) type; - - /* Okay, this is core internal and a no care for hosts. - * queued = queued for sending - * pending = sent, waiting for response - * complete = processing completed, successful or not - */ - enum { - hpsb_unused, hpsb_queued, hpsb_pending, hpsb_complete - } __attribute__((packed)) state; - - /* These are core-internal. */ - signed char tlabel; - signed char ack_code; - unsigned char tcode; - - unsigned expect_response:1; - unsigned no_waiter:1; - - /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */ - unsigned speed_code:2; - - struct hpsb_host *host; - unsigned int generation; - - atomic_t refcnt; - struct list_head queue; - - /* Function (and possible data to pass to it) to call when this - * packet is completed. */ - void (*complete_routine)(void *); - void *complete_data; - - /* Store jiffies for implementing bus timeouts. */ - unsigned long sendtime; - - /* Core-internal. */ - size_t allocated_data_size; /* as allocated */ - - /* Sizes are in bytes. To be set by caller of hpsb_alloc_packet. */ - size_t data_size; /* as filled in */ - size_t header_size; /* as filled in, not counting the CRC */ - - /* Buffers */ - quadlet_t *data; /* can be DMA-mapped */ - quadlet_t header[5]; - quadlet_t embedded_data[0]; /* keep as last member */ -}; - -void hpsb_set_packet_complete_task(struct hpsb_packet *packet, - void (*routine)(void *), void *data); -static inline struct hpsb_packet *driver_packet(struct list_head *l) -{ - return list_entry(l, struct hpsb_packet, driver_list); -} -void abort_timedouts(unsigned long __opaque); -struct hpsb_packet *hpsb_alloc_packet(size_t data_size); -void hpsb_free_packet(struct hpsb_packet *packet); - -/** - * get_hpsb_generation - generation counter for the complete 1394 subsystem - * - * Generation gets incremented on every change in the subsystem (notably on bus - * resets). Use the functions, not the variable. - */ -static inline unsigned int get_hpsb_generation(struct hpsb_host *host) -{ - return atomic_read(&host->generation); -} - -int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); -int hpsb_send_packet(struct hpsb_packet *packet); -int hpsb_send_packet_and_wait(struct hpsb_packet *packet); -int hpsb_reset_bus(struct hpsb_host *host, int type); -int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, - u64 *local_time); - -int hpsb_bus_reset(struct hpsb_host *host); -void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid); -void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot); -void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, - int ackcode); -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, - int write_acked); - -/* - * CHARACTER DEVICE DISPATCHING - * - * All ieee1394 character device drivers share the same major number - * (major 171). The 256 minor numbers are allocated to the various - * task-specific interfaces (raw1394, video1394, dv1394, etc) in - * blocks of 16. - * - * The core ieee1394.o module allocates the device number region - * 171:0-255, the various drivers must then cdev_add() their cdev - * objects to handle their respective sub-regions. - * - * Minor device number block allocations: - * - * Block 0 ( 0- 15) raw1394 - * Block 1 ( 16- 31) video1394 - * Block 2 ( 32- 47) dv1394 - * - * Blocks 3-14 free for future allocation - * - * Block 15 (240-255) reserved for drivers under development, etc. - */ - -#define IEEE1394_MAJOR 171 - -#define IEEE1394_MINOR_BLOCK_RAW1394 0 -#define IEEE1394_MINOR_BLOCK_VIDEO1394 1 -#define IEEE1394_MINOR_BLOCK_DV1394 2 -#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15 - -#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0) -#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_RAW1394 * 16) -#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_VIDEO1394 * 16) -#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_DV1394 * 16) -#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) - -/** - * ieee1394_file_to_instance - get the index within a minor number block - */ -static inline unsigned char ieee1394_file_to_instance(struct file *file) -{ - int idx = cdev_index(file->f_path.dentry->d_inode); - if (idx < 0) - idx = 0; - return idx; -} - -extern int hpsb_disable_irm; - -/* Our sysfs bus entry */ -extern struct bus_type ieee1394_bus_type; -extern struct class hpsb_host_class; -extern struct class *hpsb_protocol_class; - -#endif /* _IEEE1394_CORE_H */ diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h deleted file mode 100644 index dd5500ed832..00000000000 --- a/drivers/ieee1394/ieee1394_hotplug.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _IEEE1394_HOTPLUG_H -#define _IEEE1394_HOTPLUG_H - -/* Unit spec id and sw version entry for some protocols */ -#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D -#define AVC_SW_VERSION_ENTRY 0x00010001 -#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D -#define CAMERA_SW_VERSION_ENTRY 0x00000100 - -/* /include/linux/mod_devicetable.h defines: - * IEEE1394_MATCH_VENDOR_ID - * IEEE1394_MATCH_MODEL_ID - * IEEE1394_MATCH_SPECIFIER_ID - * IEEE1394_MATCH_VERSION - * struct ieee1394_device_id - */ -#include <linux/mod_devicetable.h> - -#endif /* _IEEE1394_HOTPLUG_H */ diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c deleted file mode 100644 index 675b3135d5f..00000000000 --- a/drivers/ieee1394/ieee1394_transactions.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Transaction support. - * - * Copyright (C) 1999 Andreas E. Bombe - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include <linux/bitops.h> -#include <linux/compiler.h> -#include <linux/hardirq.h> -#include <linux/spinlock.h> -#include <linux/string.h> -#include <linux/sched.h> /* because linux/wait.h is broken if CONFIG_SMP=n */ -#include <linux/wait.h> - -#include <asm/bug.h> -#include <asm/errno.h> -#include <asm/system.h> - -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "ieee1394_transactions.h" - -#define PREP_ASYNC_HEAD_ADDRESS(tc) \ - packet->tcode = tc; \ - packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ - | (1 << 8) | (tc << 4); \ - packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \ - packet->header[2] = addr & 0xffffffff - -#ifndef HPSB_DEBUG_TLABELS -static -#endif -DEFINE_SPINLOCK(hpsb_tlabel_lock); - -static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); - -static void fill_async_readquad(struct hpsb_packet *packet, u64 addr) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ); - packet->header_size = 12; - packet->data_size = 0; - packet->expect_response = 1; -} - -static void fill_async_readblock(struct hpsb_packet *packet, u64 addr, - int length) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_READB); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->data_size = 0; - packet->expect_response = 1; -} - -static void fill_async_writequad(struct hpsb_packet *packet, u64 addr, - quadlet_t data) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEQ); - packet->header[3] = data; - packet->header_size = 16; - packet->data_size = 0; - packet->expect_response = 1; -} - -static void fill_async_writeblock(struct hpsb_packet *packet, u64 addr, - int length) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->expect_response = 1; - packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); -} - -static void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode, - int length) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_LOCK_REQUEST); - packet->header[3] = (length << 16) | extcode; - packet->header_size = 16; - packet->data_size = length; - packet->expect_response = 1; -} - -static void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data) -{ - packet->header[0] = data; - packet->header[1] = ~data; - packet->header_size = 8; - packet->data_size = 0; - packet->expect_response = 0; - packet->type = hpsb_raw; /* No CRC added */ - packet->speed_code = IEEE1394_SPEED_100; /* Force speed to be 100Mbps */ -} - -static void fill_async_stream_packet(struct hpsb_packet *packet, int length, - int channel, int tag, int sync) -{ - packet->header[0] = (length << 16) | (tag << 14) | (channel << 8) - | (TCODE_STREAM_DATA << 4) | sync; - - packet->header_size = 4; - packet->data_size = length; - packet->type = hpsb_async; - packet->tcode = TCODE_ISO_DATA; -} - -/* same as hpsb_get_tlabel, except that it returns immediately */ -static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet) -{ - unsigned long flags, *tp; - u8 *next; - int tlabel, n = NODEID_TO_NODE(packet->node_id); - - /* Broadcast transactions are complete once the request has been sent. - * Use the same transaction label for all broadcast transactions. */ - if (unlikely(n == ALL_NODES)) { - packet->tlabel = 0; - return 0; - } - tp = packet->host->tl_pool[n].map; - next = &packet->host->next_tl[n]; - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); - tlabel = find_next_zero_bit(tp, 64, *next); - if (tlabel > 63) - tlabel = find_first_zero_bit(tp, 64); - if (tlabel > 63) { - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - return -EAGAIN; - } - __set_bit(tlabel, tp); - *next = (tlabel + 1) & 63; - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - packet->tlabel = tlabel; - return 0; -} - -/** - * hpsb_get_tlabel - allocate a transaction label - * @packet: the packet whose tlabel and tl_pool we set - * - * Every asynchronous transaction on the 1394 bus needs a transaction - * label to match the response to the request. This label has to be - * different from any other transaction label in an outstanding request to - * the same node to make matching possible without ambiguity. - * - * There are 64 different tlabels, so an allocated tlabel has to be freed - * with hpsb_free_tlabel() after the transaction is complete (unless it's - * reused again for the same target node). - * - * Return value: Zero on success, otherwise non-zero. A non-zero return - * generally means there are no available tlabels. If this is called out - * of interrupt or atomic context, then it will sleep until can return a - * tlabel or a signal is received. - */ -int hpsb_get_tlabel(struct hpsb_packet *packet) -{ - if (irqs_disabled() || in_atomic()) - return hpsb_get_tlabel_atomic(packet); - - /* NB: The macro wait_event_interruptible() is called with a condition - * argument with side effect. This is only possible because the side - * effect does not occur until the condition became true, and - * wait_event_interruptible() won't evaluate the condition again after - * that. */ - return wait_event_interruptible(tlabel_wq, - !hpsb_get_tlabel_atomic(packet)); -} - -/** - * hpsb_free_tlabel - free an allocated transaction label - * @packet: packet whose tlabel and tl_pool needs to be cleared - * - * Frees the transaction label allocated with hpsb_get_tlabel(). The - * tlabel has to be freed after the transaction is complete (i.e. response - * was received for a split transaction or packet was sent for a unified - * transaction). - * - * A tlabel must not be freed twice. - */ -void hpsb_free_tlabel(struct hpsb_packet *packet) -{ - unsigned long flags, *tp; - int tlabel, n = NODEID_TO_NODE(packet->node_id); - - if (unlikely(n == ALL_NODES)) - return; - tp = packet->host->tl_pool[n].map; - tlabel = packet->tlabel; - BUG_ON(tlabel > 63 || tlabel < 0); - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); - BUG_ON(!__test_and_clear_bit(tlabel, tp)); - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - wake_up_interruptible(&tlabel_wq); -} - -/** - * hpsb_packet_success - Make sense of the ack and reply codes - * - * Make sense of the ack and reply codes and return more convenient error codes: - * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can - * probably resolved by retry. -%EREMOTEIO = node suffers from an internal - * error. -%EACCES = this transaction is not allowed on requested address. - * -%EINVAL = invalid address at node. - */ -int hpsb_packet_success(struct hpsb_packet *packet) -{ - switch (packet->ack_code) { - case ACK_PENDING: - switch ((packet->header[1] >> 12) & 0xf) { - case RCODE_COMPLETE: - return 0; - case RCODE_CONFLICT_ERROR: - return -EAGAIN; - case RCODE_DATA_ERROR: - return -EREMOTEIO; - case RCODE_TYPE_ERROR: - return -EACCES; - case RCODE_ADDRESS_ERROR: - return -EINVAL; - default: - HPSB_ERR("received reserved rcode %d from node %d", - (packet->header[1] >> 12) & 0xf, - packet->node_id); - return -EAGAIN; - } - - case ACK_BUSY_X: - case ACK_BUSY_A: - case ACK_BUSY_B: - return -EBUSY; - - case ACK_TYPE_ERROR: - return -EACCES; - - case ACK_COMPLETE: - if (packet->tcode == TCODE_WRITEQ - || packet->tcode == TCODE_WRITEB) { - return 0; - } else { - HPSB_ERR("impossible ack_complete from node %d " - "(tcode %d)", packet->node_id, packet->tcode); - return -EAGAIN; - } - - case ACK_DATA_ERROR: - if (packet->tcode == TCODE_WRITEB - || packet->tcode == TCODE_LOCK_REQUEST) { - return -EAGAIN; - } else { - HPSB_ERR("impossible ack_data_error from node %d " - "(tcode %d)", packet->node_id, packet->tcode); - return -EAGAIN; - } - - case ACK_ADDRESS_ERROR: - return -EINVAL; - - case ACK_TARDY: - case ACK_CONFLICT_ERROR: - case ACKX_NONE: - case ACKX_SEND_ERROR: - case ACKX_ABORTED: - case ACKX_TIMEOUT: - /* error while sending */ - return -EAGAIN; - - default: - HPSB_ERR("got invalid ack %d from node %d (tcode %d)", - packet->ack_code, packet->node_id, packet->tcode); - return -EAGAIN; - } -} - -struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, size_t length) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - packet = hpsb_alloc_packet(length); - if (!packet) - return NULL; - - packet->host = host; - packet->node_id = node; - - if (hpsb_get_tlabel(packet)) { - hpsb_free_packet(packet); - return NULL; - } - - if (length == 4) - fill_async_readquad(packet, addr); - else - fill_async_readblock(packet, addr, length); - - return packet; -} - -struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, nodeid_t node, - u64 addr, quadlet_t * buffer, - size_t length) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - packet = hpsb_alloc_packet(length); - if (!packet) - return NULL; - - if (length % 4) { /* zero padding bytes */ - packet->data[length >> 2] = 0; - } - packet->host = host; - packet->node_id = node; - - if (hpsb_get_tlabel(packet)) { - hpsb_free_packet(packet); - return NULL; - } - - if (length == 4) { - fill_async_writequad(packet, addr, buffer ? *buffer : 0); - } else { - fill_async_writeblock(packet, addr, length); - if (buffer) - memcpy(packet->data, buffer, length); - } - - return packet; -} - -struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer, - int length, int channel, int tag, - int sync) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - packet = hpsb_alloc_packet(length); - if (!packet) - return NULL; - - if (length % 4) { /* zero padding bytes */ - packet->data[length >> 2] = 0; - } - packet->host = host; - - /* Because it is too difficult to determine all PHY speeds and link - * speeds here, we use S100... */ - packet->speed_code = IEEE1394_SPEED_100; - - /* ...and prevent hpsb_send_packet() from overriding it. */ - packet->node_id = LOCAL_BUS | ALL_NODES; - - if (hpsb_get_tlabel(packet)) { - hpsb_free_packet(packet); - return NULL; - } - - fill_async_stream_packet(packet, length, channel, tag, sync); - if (buffer) - memcpy(packet->data, buffer, length); - - return packet; -} - -struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode, - quadlet_t * data, quadlet_t arg) -{ - struct hpsb_packet *p; - u32 length; - - p = hpsb_alloc_packet(8); - if (!p) - return NULL; - - p->host = host; - p->node_id = node; - if (hpsb_get_tlabel(p)) { - hpsb_free_packet(p); - return NULL; - } - - switch (extcode) { - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - length = 4; - if (data) - p->data[0] = *data; - break; - default: - length = 8; - if (data) { - p->data[0] = arg; - p->data[1] = *data; - } - break; - } - fill_async_lock(p, addr, extcode, length); - - return p; -} - -struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, - nodeid_t node, u64 addr, int extcode, - octlet_t * data, octlet_t arg) -{ - struct hpsb_packet *p; - u32 length; - - p = hpsb_alloc_packet(16); - if (!p) - return NULL; - - p->host = host; - p->node_id = node; - if (hpsb_get_tlabel(p)) { - hpsb_free_packet(p); - return NULL; - } - - switch (extcode) { - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - length = 8; - if (data) { - p->data[0] = *data >> 32; - p->data[1] = *data & 0xffffffff; - } - break; - default: - length = 16; - if (data) { - p->data[0] = arg >> 32; - p->data[1] = arg & 0xffffffff; - p->data[2] = *data >> 32; - p->data[3] = *data & 0xffffffff; - } - break; - } - fill_async_lock(p, addr, extcode, length); - - return p; -} - -struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data) -{ - struct hpsb_packet *p; - - p = hpsb_alloc_packet(0); - if (!p) - return NULL; - - p->host = host; - fill_phy_packet(p, data); - - return p; -} - -/* - * FIXME - these functions should probably read from / write to user space to - * avoid in kernel buffers for user space callers - */ - -/** - * hpsb_read - generic read function - * - * Recognizes the local node ID and act accordingly. Automatically uses a - * quadlet read request if @length == 4 and and a block read request otherwise. - * It does not yet support lengths that are not a multiple of 4. - * - * You must explicitly specifiy the @generation for which the node ID is valid, - * to avoid sending packets to the wrong nodes when we race with a bus reset. - */ -int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t * buffer, size_t length) -{ - struct hpsb_packet *packet; - int retval = 0; - - if (length == 0) - return -EINVAL; - - packet = hpsb_make_readpacket(host, node, addr, length); - - if (!packet) { - return -ENOMEM; - } - - packet->generation = generation; - retval = hpsb_send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_read_fail; - - retval = hpsb_packet_success(packet); - - if (retval == 0) { - if (length == 4) { - *buffer = packet->header[3]; - } else { - memcpy(buffer, packet->data, length); - } - } - - hpsb_read_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} - -/** - * hpsb_write - generic write function - * - * Recognizes the local node ID and act accordingly. Automatically uses a - * quadlet write request if @length == 4 and and a block write request - * otherwise. It does not yet support lengths that are not a multiple of 4. - * - * You must explicitly specifiy the @generation for which the node ID is valid, - * to avoid sending packets to the wrong nodes when we race with a bus reset. - */ -int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t * buffer, size_t length) -{ - struct hpsb_packet *packet; - int retval; - - if (length == 0) - return -EINVAL; - - packet = hpsb_make_writepacket(host, node, addr, buffer, length); - - if (!packet) - return -ENOMEM; - - packet->generation = generation; - retval = hpsb_send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_write_fail; - - retval = hpsb_packet_success(packet); - - hpsb_write_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} - -int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, int extcode, quadlet_t *data, quadlet_t arg) -{ - struct hpsb_packet *packet; - int retval = 0; - - packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); - if (!packet) - return -ENOMEM; - - packet->generation = generation; - retval = hpsb_send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_lock_fail; - - retval = hpsb_packet_success(packet); - - if (retval == 0) - *data = packet->data[0]; - -hpsb_lock_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h deleted file mode 100644 index 20b693be14b..00000000000 --- a/drivers/ieee1394/ieee1394_transactions.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _IEEE1394_TRANSACTIONS_H -#define _IEEE1394_TRANSACTIONS_H - -#include <linux/types.h> - -#include "ieee1394_types.h" - -struct hpsb_packet; -struct hpsb_host; - -int hpsb_get_tlabel(struct hpsb_packet *packet); -void hpsb_free_tlabel(struct hpsb_packet *packet); -struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, size_t length); -struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode, quadlet_t *data, - quadlet_t arg); -struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, - nodeid_t node, u64 addr, int extcode, - octlet_t *data, octlet_t arg); -struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data); -struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, - nodeid_t node, u64 addr, - quadlet_t *buffer, size_t length); -struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, - int length, int channel, int tag, - int sync); -int hpsb_packet_success(struct hpsb_packet *packet); -int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t *buffer, size_t length); -int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t *buffer, size_t length); -int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, int extcode, quadlet_t *data, quadlet_t arg); - -#ifdef HPSB_DEBUG_TLABELS -extern spinlock_t hpsb_tlabel_lock; -#endif - -#endif /* _IEEE1394_TRANSACTIONS_H */ diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h deleted file mode 100644 index 9803aaa15be..00000000000 --- a/drivers/ieee1394/ieee1394_types.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef _IEEE1394_TYPES_H -#define _IEEE1394_TYPES_H - -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/types.h> -#include <asm/byteorder.h> - -typedef u32 quadlet_t; -typedef u64 octlet_t; -typedef u16 nodeid_t; - -typedef u8 byte_t; -typedef u64 nodeaddr_t; -typedef u16 arm_length_t; - -#define BUS_MASK 0xffc0 -#define BUS_SHIFT 6 -#define NODE_MASK 0x003f -#define LOCAL_BUS 0xffc0 -#define ALL_NODES 0x003f - -#define NODEID_TO_BUS(nodeid) ((nodeid & BUS_MASK) >> BUS_SHIFT) -#define NODEID_TO_NODE(nodeid) (nodeid & NODE_MASK) - -/* Can be used to consistently print a node/bus ID. */ -#define NODE_BUS_FMT "%d-%02d:%04d" -#define NODE_BUS_ARGS(__host, __nodeid) \ - __host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid) - -#define HPSB_PRINT(level, fmt, args...) \ - printk(level "ieee1394: " fmt "\n" , ## args) - -#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) -#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args) -#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args) -#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args) -#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args) - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) -#define HPSB_DEBUG_TLABELS -#else -#define HPSB_VERBOSE(fmt, args...) do {} while (0) -#endif - -#ifdef __BIG_ENDIAN - -static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count) -{ - void *tmp = dest; - u32 *src = (u32 *)__src; - - count /= 4; - while (count--) - *dest++ = swab32p(src++); - return tmp; -} - -#else - -static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count) -{ - return memcpy(dest, src, count); -} - -#endif /* __BIG_ENDIAN */ - -#endif /* _IEEE1394_TYPES_H */ diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c deleted file mode 100644 index 1cf6487b65b..00000000000 --- a/drivers/ieee1394/iso.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * kernel ISO transmission/reception - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include <linux/pci.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/slab.h> - -#include "hosts.h" -#include "iso.h" - -/** - * hpsb_iso_stop - stop DMA - */ -void hpsb_iso_stop(struct hpsb_iso *iso) -{ - if (!(iso->flags & HPSB_ISO_DRIVER_STARTED)) - return; - - iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? - XMIT_STOP : RECV_STOP, 0); - iso->flags &= ~HPSB_ISO_DRIVER_STARTED; -} - -/** - * hpsb_iso_shutdown - deallocate buffer and DMA context - */ -void hpsb_iso_shutdown(struct hpsb_iso *iso) -{ - if (iso->flags & HPSB_ISO_DRIVER_INIT) { - hpsb_iso_stop(iso); - iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? - XMIT_SHUTDOWN : RECV_SHUTDOWN, 0); - iso->flags &= ~HPSB_ISO_DRIVER_INIT; - } - - dma_region_free(&iso->data_buf); - kfree(iso); -} - -static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host, - enum hpsb_iso_type type, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, int dma_mode, - int irq_interval, - void (*callback) (struct hpsb_iso - *)) -{ - struct hpsb_iso *iso; - int dma_direction; - - /* make sure driver supports the ISO API */ - if (!host->driver->isoctl) { - printk(KERN_INFO - "ieee1394: host driver '%s' does not support the rawiso API\n", - host->driver->name); - return NULL; - } - - /* sanitize parameters */ - - if (buf_packets < 2) - buf_packets = 2; - - if ((dma_mode < HPSB_ISO_DMA_DEFAULT) - || (dma_mode > HPSB_ISO_DMA_PACKET_PER_BUFFER)) - dma_mode = HPSB_ISO_DMA_DEFAULT; - - if ((irq_interval < 0) || (irq_interval > buf_packets / 4)) - irq_interval = buf_packets / 4; - if (irq_interval == 0) /* really interrupt for each packet */ - irq_interval = 1; - - if (channel < -1 || channel >= 64) - return NULL; - - /* channel = -1 is OK for multi-channel recv but not for xmit */ - if (type == HPSB_ISO_XMIT && channel < 0) - return NULL; - - /* allocate and write the struct hpsb_iso */ - - iso = - kmalloc(sizeof(*iso) + - buf_packets * sizeof(struct hpsb_iso_packet_info), - GFP_KERNEL); - if (!iso) - return NULL; - - iso->infos = (struct hpsb_iso_packet_info *)(iso + 1); - - iso->type = type; - iso->host = host; - iso->hostdata = NULL; - iso->callback = callback; - init_waitqueue_head(&iso->waitq); - iso->channel = channel; - iso->irq_interval = irq_interval; - iso->dma_mode = dma_mode; - dma_region_init(&iso->data_buf); - iso->buf_size = PAGE_ALIGN(data_buf_size); - iso->buf_packets = buf_packets; - iso->pkt_dma = 0; - iso->first_packet = 0; - spin_lock_init(&iso->lock); - - if (iso->type == HPSB_ISO_XMIT) { - iso->n_ready_packets = iso->buf_packets; - dma_direction = PCI_DMA_TODEVICE; - } else { - iso->n_ready_packets = 0; - dma_direction = PCI_DMA_FROMDEVICE; - } - - atomic_set(&iso->overflows, 0); - iso->bytes_discarded = 0; - iso->flags = 0; - iso->prebuffer = 0; - - /* allocate the packet buffer */ - if (dma_region_alloc - (&iso->data_buf, iso->buf_size, host->pdev, dma_direction)) - goto err; - - return iso; - - err: - hpsb_iso_shutdown(iso); - return NULL; -} - -/** - * hpsb_iso_n_ready - returns number of packets ready to send or receive - */ -int hpsb_iso_n_ready(struct hpsb_iso *iso) -{ - unsigned long flags; - int val; - - spin_lock_irqsave(&iso->lock, flags); - val = iso->n_ready_packets; - spin_unlock_irqrestore(&iso->lock, flags); - - return val; -} - -/** - * hpsb_iso_xmit_init - allocate the buffer and DMA context - */ -struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int speed, - int irq_interval, - void (*callback) (struct hpsb_iso *)) -{ - struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT, - data_buf_size, buf_packets, - channel, - HPSB_ISO_DMA_DEFAULT, - irq_interval, callback); - if (!iso) - return NULL; - - iso->speed = speed; - - /* tell the driver to start working */ - if (host->driver->isoctl(iso, XMIT_INIT, 0)) - goto err; - - iso->flags |= HPSB_ISO_DRIVER_INIT; - return iso; - - err: - hpsb_iso_shutdown(iso); - return NULL; -} - -/** - * hpsb_iso_recv_init - allocate the buffer and DMA context - * - * Note, if channel = -1, multi-channel receive is enabled. - */ -struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int dma_mode, - int irq_interval, - void (*callback) (struct hpsb_iso *)) -{ - struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV, - data_buf_size, buf_packets, - channel, dma_mode, - irq_interval, callback); - if (!iso) - return NULL; - - /* tell the driver to start working */ - if (host->driver->isoctl(iso, RECV_INIT, 0)) - goto err; - - iso->flags |= HPSB_ISO_DRIVER_INIT; - return iso; - - err: - hpsb_iso_shutdown(iso); - return NULL; -} - -/** - * hpsb_iso_recv_listen_channel - * - * multi-channel only - */ -int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel) -{ - if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel); -} - -/** - * hpsb_iso_recv_unlisten_channel - * - * multi-channel only - */ -int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel) -{ - if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel); -} - -/** - * hpsb_iso_recv_set_channel_mask - * - * multi-channel only - */ -int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) -{ - if (iso->type != HPSB_ISO_RECV || iso->channel != -1) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK, - (unsigned long)&mask); -} - -/** - * hpsb_iso_recv_flush - check for arrival of new packets - * - * check for arrival of new packets immediately (even if irq_interval - * has not yet been reached) - */ -int hpsb_iso_recv_flush(struct hpsb_iso *iso) -{ - if (iso->type != HPSB_ISO_RECV) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_FLUSH, 0); -} - -static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle) -{ - int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle); - if (retval) - return retval; - - iso->flags |= HPSB_ISO_DRIVER_STARTED; - return retval; -} - -/** - * hpsb_iso_xmit_start - start DMA - */ -int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) -{ - if (iso->type != HPSB_ISO_XMIT) - return -1; - - if (iso->flags & HPSB_ISO_DRIVER_STARTED) - return 0; - - if (cycle < -1) - cycle = -1; - else if (cycle >= 8000) - cycle %= 8000; - - iso->xmit_cycle = cycle; - - if (prebuffer < 0) - prebuffer = iso->buf_packets - 1; - else if (prebuffer == 0) - prebuffer = 1; - - if (prebuffer >= iso->buf_packets) - prebuffer = iso->buf_packets - 1; - - iso->prebuffer = prebuffer; - - /* remember the starting cycle; DMA will commence from xmit_queue_packets() - once enough packets have been buffered */ - iso->start_cycle = cycle; - - return 0; -} - -/** - * hpsb_iso_recv_start - start DMA - */ -int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) -{ - int retval = 0; - int isoctl_args[3]; - - if (iso->type != HPSB_ISO_RECV) - return -1; - - if (iso->flags & HPSB_ISO_DRIVER_STARTED) - return 0; - - if (cycle < -1) - cycle = -1; - else if (cycle >= 8000) - cycle %= 8000; - - isoctl_args[0] = cycle; - - if (tag_mask < 0) - /* match all tags */ - tag_mask = 0xF; - isoctl_args[1] = tag_mask; - - isoctl_args[2] = sync; - - retval = - iso->host->driver->isoctl(iso, RECV_START, - (unsigned long)&isoctl_args[0]); - if (retval) - return retval; - - iso->flags |= HPSB_ISO_DRIVER_STARTED; - return retval; -} - -/* check to make sure the user has not supplied bogus values of offset/len - * that would cause the kernel to access memory outside the buffer */ -static int hpsb_iso_check_offset_len(struct hpsb_iso *iso, - unsigned int offset, unsigned short len, - unsigned int *out_offset, - unsigned short *out_len) -{ - if (offset >= iso->buf_size) - return -EFAULT; - - /* make sure the packet does not go beyond the end of the buffer */ - if (offset + len > iso->buf_size) - return -EFAULT; - - /* check for wrap-around */ - if (offset + len < offset) - return -EFAULT; - - /* now we can trust 'offset' and 'length' */ - *out_offset = offset; - *out_len = len; - - return 0; -} - -/** - * hpsb_iso_xmit_queue_packet - queue a packet for transmission. - * - * @offset is relative to the beginning of the DMA buffer, where the packet's - * data payload should already have been placed. - */ -int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, - u8 tag, u8 sy) -{ - struct hpsb_iso_packet_info *info; - unsigned long flags; - int rv; - - if (iso->type != HPSB_ISO_XMIT) - return -EINVAL; - - /* is there space in the buffer? */ - if (iso->n_ready_packets <= 0) { - return -EBUSY; - } - - info = &iso->infos[iso->first_packet]; - - /* check for bogus offset/length */ - if (hpsb_iso_check_offset_len - (iso, offset, len, &info->offset, &info->len)) - return -EFAULT; - - info->tag = tag; - info->sy = sy; - - spin_lock_irqsave(&iso->lock, flags); - - rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long)info); - if (rv) - goto out; - - /* increment cursors */ - iso->first_packet = (iso->first_packet + 1) % iso->buf_packets; - iso->xmit_cycle = (iso->xmit_cycle + 1) % 8000; - iso->n_ready_packets--; - - if (iso->prebuffer != 0) { - iso->prebuffer--; - if (iso->prebuffer <= 0) { - iso->prebuffer = 0; - rv = do_iso_xmit_start(iso, iso->start_cycle); - } - } - - out: - spin_unlock_irqrestore(&iso->lock, flags); - return rv; -} - -/** - * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted - */ -int hpsb_iso_xmit_sync(struct hpsb_iso *iso) -{ - if (iso->type != HPSB_ISO_XMIT) - return -EINVAL; - - return wait_event_interruptible(iso->waitq, - hpsb_iso_n_ready(iso) == - iso->buf_packets); -} - -/** - * hpsb_iso_packet_sent - * - * Available to low-level drivers. - * - * Call after a packet has been transmitted to the bus (interrupt context is - * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be - * non-zero if some sort of error occurred when sending the packet. - */ -void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error) -{ - unsigned long flags; - spin_lock_irqsave(&iso->lock, flags); - - /* predict the cycle of the next packet to be queued */ - - /* jump ahead by the number of packets that are already buffered */ - cycle += iso->buf_packets - iso->n_ready_packets; - cycle %= 8000; - - iso->xmit_cycle = cycle; - iso->n_ready_packets++; - iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets; - - if (iso->n_ready_packets == iso->buf_packets || error != 0) { - /* the buffer has run empty! */ - atomic_inc(&iso->overflows); - } - - spin_unlock_irqrestore(&iso->lock, flags); -} - -/** - * hpsb_iso_packet_received - * - * Available to low-level drivers. - * - * Call after a packet has been received (interrupt context is OK). - */ -void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, - u16 total_len, u16 cycle, u8 channel, u8 tag, - u8 sy) -{ - unsigned long flags; - spin_lock_irqsave(&iso->lock, flags); - - if (iso->n_ready_packets == iso->buf_packets) { - /* overflow! */ - atomic_inc(&iso->overflows); - /* Record size of this discarded packet */ - iso->bytes_discarded += total_len; - } else { - struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma]; - info->offset = offset; - info->len = len; - info->total_len = total_len; - info->cycle = cycle; - info->channel = channel; - info->tag = tag; - info->sy = sy; - - iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets; - iso->n_ready_packets++; - } - - spin_unlock_irqrestore(&iso->lock, flags); -} - -/** - * hpsb_iso_recv_release_packets - release packets, reuse buffer - * - * @n_packets have been read out of the buffer, re-use the buffer space - */ -int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) -{ - unsigned long flags; - unsigned int i; - int rv = 0; - - if (iso->type != HPSB_ISO_RECV) - return -1; - - spin_lock_irqsave(&iso->lock, flags); - for (i = 0; i < n_packets; i++) { - rv = iso->host->driver->isoctl(iso, RECV_RELEASE, - (unsigned long)&iso->infos[iso-> - first_packet]); - if (rv) - break; - - iso->first_packet = (iso->first_packet + 1) % iso->buf_packets; - iso->n_ready_packets--; - - /* release memory from packets discarded when queue was full */ - if (iso->n_ready_packets == 0) { /* Release only after all prior packets handled */ - if (iso->bytes_discarded != 0) { - struct hpsb_iso_packet_info inf; - inf.total_len = iso->bytes_discarded; - iso->host->driver->isoctl(iso, RECV_RELEASE, - (unsigned long)&inf); - iso->bytes_discarded = 0; - } - } - } - spin_unlock_irqrestore(&iso->lock, flags); - return rv; -} - -/** - * hpsb_iso_wake - * - * Available to low-level drivers. - * - * Call to wake waiting processes after buffer space has opened up. - */ -void hpsb_iso_wake(struct hpsb_iso *iso) -{ - wake_up_interruptible(&iso->waitq); - - if (iso->callback) - iso->callback(iso); -} diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h deleted file mode 100644 index c2089c093aa..00000000000 --- a/drivers/ieee1394/iso.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * kernel ISO transmission/reception - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#ifndef IEEE1394_ISO_H -#define IEEE1394_ISO_H - -#include <linux/spinlock_types.h> -#include <linux/wait.h> -#include <asm/atomic.h> -#include <asm/types.h> - -#include "dma.h" - -struct hpsb_host; - -/* high-level ISO interface */ - -/* - * This API sends and receives isochronous packets on a large, - * virtually-contiguous kernel memory buffer. The buffer may be mapped - * into a user-space process for zero-copy transmission and reception. - * - * There are no explicit boundaries between packets in the buffer. A - * packet may be transmitted or received at any location. However, - * low-level drivers may impose certain restrictions on alignment or - * size of packets. (e.g. in OHCI no packet may cross a page boundary, - * and packets should be quadlet-aligned) - */ - -/* Packet descriptor - the API maintains a ring buffer of these packet - * descriptors in kernel memory (hpsb_iso.infos[]). */ -struct hpsb_iso_packet_info { - /* offset of data payload relative to the first byte of the buffer */ - __u32 offset; - - /* length of the data payload, in bytes (not including the isochronous - * header) */ - __u16 len; - - /* (recv only) the cycle number (mod 8000) on which the packet was - * received */ - __u16 cycle; - - /* (recv only) channel on which the packet was received */ - __u8 channel; - - /* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */ - __u8 tag; - __u8 sy; - - /* length in bytes of the packet including header/trailer. - * MUST be at structure end, since the first part of this structure is - * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is - * copied to userspace and is accessed there through libraw1394. */ - __u16 total_len; -}; - -enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 }; - -/* The mode of the dma when receiving iso data. Must be supported by chip */ -enum raw1394_iso_dma_recv_mode { - HPSB_ISO_DMA_DEFAULT = -1, - HPSB_ISO_DMA_OLD_ABI = 0, - HPSB_ISO_DMA_BUFFERFILL = 1, - HPSB_ISO_DMA_PACKET_PER_BUFFER = 2 -}; - -struct hpsb_iso { - enum hpsb_iso_type type; - - /* pointer to low-level driver and its private data */ - struct hpsb_host *host; - void *hostdata; - - /* a function to be called (from interrupt context) after - * outgoing packets have been sent, or incoming packets have - * arrived */ - void (*callback)(struct hpsb_iso*); - - /* wait for buffer space */ - wait_queue_head_t waitq; - - int speed; /* IEEE1394_SPEED_100, 200, or 400 */ - int channel; /* -1 if multichannel */ - int dma_mode; /* dma receive mode */ - - - /* greatest # of packets between interrupts - controls - * the maximum latency of the buffer */ - int irq_interval; - - /* the buffer for packet data payloads */ - struct dma_region data_buf; - - /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */ - unsigned int buf_size; - - /* # of packets in the ringbuffer */ - unsigned int buf_packets; - - /* protects packet cursors */ - spinlock_t lock; - - /* the index of the next packet that will be produced - or consumed by the user */ - int first_packet; - - /* the index of the next packet that will be transmitted - or received by the 1394 hardware */ - int pkt_dma; - - /* how many packets, starting at first_packet: - * (transmit) are ready to be filled with data - * (receive) contain received data */ - int n_ready_packets; - - /* how many times the buffer has overflowed or underflowed */ - atomic_t overflows; - /* how many cycles were skipped for a given context */ - atomic_t skips; - - /* Current number of bytes lost in discarded packets */ - int bytes_discarded; - - /* private flags to track initialization progress */ -#define HPSB_ISO_DRIVER_INIT (1<<0) -#define HPSB_ISO_DRIVER_STARTED (1<<1) - unsigned int flags; - - /* # of packets left to prebuffer (xmit only) */ - int prebuffer; - - /* starting cycle for DMA (xmit only) */ - int start_cycle; - - /* cycle at which next packet will be transmitted, - * -1 if not known */ - int xmit_cycle; - - /* ringbuffer of packet descriptors in regular kernel memory - * XXX Keep this last, since we use over-allocated memory from - * this entry to fill this field. */ - struct hpsb_iso_packet_info *infos; -}; - -/* functions available to high-level drivers (e.g. raw1394) */ - -struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int speed, - int irq_interval, - void (*callback)(struct hpsb_iso*)); -struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int dma_mode, - int irq_interval, - void (*callback)(struct hpsb_iso*)); -int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel); -int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); -int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); -int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, - int prebuffer); -int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, - int tag_mask, int sync); -void hpsb_iso_stop(struct hpsb_iso *iso); -void hpsb_iso_shutdown(struct hpsb_iso *iso); -int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, - u8 tag, u8 sy); -int hpsb_iso_xmit_sync(struct hpsb_iso *iso); -int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, - unsigned int n_packets); -int hpsb_iso_recv_flush(struct hpsb_iso *iso); -int hpsb_iso_n_ready(struct hpsb_iso *iso); - -/* the following are callbacks available to low-level drivers */ - -void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error); -void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, - u16 total_len, u16 cycle, u8 channel, u8 tag, - u8 sy); -void hpsb_iso_wake(struct hpsb_iso *iso); - -#endif /* IEEE1394_ISO_H */ diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c deleted file mode 100644 index 18350213479..00000000000 --- a/drivers/ieee1394/nodemgr.c +++ /dev/null @@ -1,1901 +0,0 @@ -/* - * Node information (ConfigROM) collection and management. - * - * Copyright (C) 2000 Andreas E. Bombe - * 2001-2003 Ben Collins <bcollins@debian.net> - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include <linux/bitmap.h> -#include <linux/kernel.h> -#include <linux/kmemcheck.h> -#include <linux/list.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/kthread.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/mutex.h> -#include <linux/freezer.h> -#include <asm/atomic.h> - -#include "csr.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_types.h" -#include "ieee1394_transactions.h" -#include "nodemgr.h" - -static int ignore_drivers; -module_param(ignore_drivers, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers."); - -struct nodemgr_csr_info { - struct hpsb_host *host; - nodeid_t nodeid; - unsigned int generation; - - kmemcheck_bitfield_begin(flags); - unsigned int speed_unverified:1; - kmemcheck_bitfield_end(flags); -}; - - -/* - * Correct the speed map entry. This is necessary - * - for nodes with link speed < phy speed, - * - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX. - * A possible speed is determined by trial and error, using quadlet reads. - */ -static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr, - quadlet_t *buffer) -{ - quadlet_t q; - u8 i, *speed, old_speed, good_speed; - int error; - - speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]); - old_speed = *speed; - good_speed = IEEE1394_SPEED_MAX + 1; - - /* Try every speed from S100 to old_speed. - * If we did it the other way around, a too low speed could be caught - * if the retry succeeded for some other reason, e.g. because the link - * just finished its initialization. */ - for (i = IEEE1394_SPEED_100; i <= old_speed; i++) { - *speed = i; - error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, - &q, 4); - if (error) - break; - *buffer = q; - good_speed = i; - } - if (good_speed <= IEEE1394_SPEED_MAX) { - HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s", - NODE_BUS_ARGS(ci->host, ci->nodeid), - hpsb_speedto_str[good_speed]); - *speed = good_speed; - ci->speed_unverified = 0; - return 0; - } - *speed = old_speed; - return error; -} - -static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, - void *buffer, void *__ci) -{ - struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci; - int i, error; - - for (i = 1; ; i++) { - error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, - buffer, 4); - if (!error) { - ci->speed_unverified = 0; - break; - } - /* Give up after 3rd failure. */ - if (i == 3) - break; - - /* The ieee1394_core guessed the node's speed capability from - * the self ID. Check whether a lower speed works. */ - if (ci->speed_unverified) { - error = nodemgr_check_speed(ci, addr, buffer); - if (!error) - break; - } - if (msleep_interruptible(334)) - return -EINTR; - } - return error; -} - -static struct csr1212_bus_ops nodemgr_csr_ops = { - .bus_read = nodemgr_bus_read, -}; - - -/* - * Basically what we do here is start off retrieving the bus_info block. - * From there will fill in some info about the node, verify it is of IEEE - * 1394 type, and that the crc checks out ok. After that we start off with - * the root directory, and subdirectories. To do this, we retrieve the - * quadlet header for a directory, find out the length, and retrieve the - * complete directory entry (be it a leaf or a directory). We then process - * it and add the info to our structure for that particular node. - * - * We verify CRC's along the way for each directory/block/leaf. The entire - * node structure is generic, and simply stores the information in a way - * that's easy to parse by the protocol interface. - */ - -/* - * The nodemgr relies heavily on the Driver Model for device callbacks and - * driver/device mappings. The old nodemgr used to handle all this itself, - * but now we are much simpler because of the LDM. - */ - -struct host_info { - struct hpsb_host *host; - struct list_head list; - struct task_struct *thread; -}; - -static int nodemgr_bus_match(struct device * dev, struct device_driver * drv); -static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env); - -struct bus_type ieee1394_bus_type = { - .name = "ieee1394", - .match = nodemgr_bus_match, -}; - -static void host_cls_release(struct device *dev) -{ - put_device(&container_of((dev), struct hpsb_host, host_dev)->device); -} - -struct class hpsb_host_class = { - .name = "ieee1394_host", - .dev_release = host_cls_release, -}; - -static void ne_cls_release(struct device *dev) -{ - put_device(&container_of((dev), struct node_entry, node_dev)->device); -} - -static struct class nodemgr_ne_class = { - .name = "ieee1394_node", - .dev_release = ne_cls_release, -}; - -static void ud_cls_release(struct device *dev) -{ - put_device(&container_of((dev), struct unit_directory, unit_dev)->device); -} - -/* The name here is only so that unit directory hotplug works with old - * style hotplug, which only ever did unit directories anyway. - */ -static struct class nodemgr_ud_class = { - .name = "ieee1394", - .dev_release = ud_cls_release, - .dev_uevent = nodemgr_uevent, -}; - -static struct hpsb_highlevel nodemgr_highlevel; - - -static void nodemgr_release_ud(struct device *dev) -{ - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - - if (ud->vendor_name_kv) - csr1212_release_keyval(ud->vendor_name_kv); - if (ud->model_name_kv) - csr1212_release_keyval(ud->model_name_kv); - - kfree(ud); -} - -static void nodemgr_release_ne(struct device *dev) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - - if (ne->vendor_name_kv) - csr1212_release_keyval(ne->vendor_name_kv); - - kfree(ne); -} - - -static void nodemgr_release_host(struct device *dev) -{ - struct hpsb_host *host = container_of(dev, struct hpsb_host, device); - - csr1212_destroy_csr(host->csr.rom); - - kfree(host); -} - -static int nodemgr_ud_platform_data; - -static struct device nodemgr_dev_template_ud = { - .bus = &ieee1394_bus_type, - .release = nodemgr_release_ud, - .platform_data = &nodemgr_ud_platform_data, -}; - -static struct device nodemgr_dev_template_ne = { - .bus = &ieee1394_bus_type, - .release = nodemgr_release_ne, -}; - -/* This dummy driver prevents the host devices from being scanned. We have no - * useful drivers for them yet, and there would be a deadlock possible if the - * driver core scans the host device while the host's low-level driver (i.e. - * the host's parent device) is being removed. */ -static struct device_driver nodemgr_mid_layer_driver = { - .bus = &ieee1394_bus_type, - .name = "nodemgr", - .owner = THIS_MODULE, -}; - -struct device nodemgr_dev_template_host = { - .bus = &ieee1394_bus_type, - .release = nodemgr_release_host, -}; - - -#define fw_attr(class, class_type, field, type, format_string) \ -static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\ -{ \ - class_type *class; \ - class = container_of(dev, class_type, device); \ - return sprintf(buf, format_string, (type)class->field); \ -} \ -static struct device_attribute dev_attr_##class##_##field = { \ - .attr = {.name = __stringify(field), .mode = S_IRUGO }, \ - .show = fw_show_##class##_##field, \ -}; - -#define fw_attr_td(class, class_type, td_kv) \ -static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\ -{ \ - int len; \ - class_type *class = container_of(dev, class_type, device); \ - len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \ - memcpy(buf, \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \ - len); \ - while (buf[len - 1] == '\0') \ - len--; \ - buf[len++] = '\n'; \ - buf[len] = '\0'; \ - return len; \ -} \ -static struct device_attribute dev_attr_##class##_##td_kv = { \ - .attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \ - .show = fw_show_##class##_##td_kv, \ -}; - - -#define fw_drv_attr(field, type, format_string) \ -static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \ -{ \ - struct hpsb_protocol_driver *driver; \ - driver = container_of(drv, struct hpsb_protocol_driver, driver); \ - return sprintf(buf, format_string, (type)driver->field);\ -} \ -static struct driver_attribute driver_attr_drv_##field = { \ - .attr = {.name = __stringify(field), .mode = S_IRUGO }, \ - .show = fw_drv_show_##field, \ -}; - - -static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - - return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) " - "LSPD(%d) MAX_REC(%d) MAX_ROM(%d) CYC_CLK_ACC(%d)\n", - ne->busopt.irmc, - ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc, - ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd, - ne->busopt.max_rec, - ne->busopt.max_rom, - ne->busopt.cyc_clk_acc); -} -static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL); - - -#ifdef HPSB_DEBUG_TLABELS -static ssize_t fw_show_ne_tlabels_free(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - unsigned long flags; - unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; - int tf; - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); - tf = 64 - bitmap_weight(tp, 64); - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - return sprintf(buf, "%d\n", tf); -} -static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL); - - -static ssize_t fw_show_ne_tlabels_mask(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - unsigned long flags; - unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; - u64 tm; - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); -#if (BITS_PER_LONG <= 32) - tm = ((u64)tp[0] << 32) + tp[1]; -#else - tm = tp[0]; -#endif - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - return sprintf(buf, "0x%016llx\n", (unsigned long long)tm); -} -static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL); -#endif /* HPSB_DEBUG_TLABELS */ - - -static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - int state = simple_strtoul(buf, NULL, 10); - - if (state == 1) { - ud->ignore_driver = 1; - device_release_driver(dev); - } else if (state == 0) - ud->ignore_driver = 0; - - return count; -} -static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - - return sprintf(buf, "%d\n", ud->ignore_driver); -} -static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver); - - -static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, - size_t count) -{ - int error = 0; - - if (simple_strtoul(buf, NULL, 10) == 1) - error = bus_rescan_devices(&ieee1394_bus_type); - return error ? error : count; -} -static ssize_t fw_get_rescan(struct bus_type *bus, char *buf) -{ - return sprintf(buf, "You can force a rescan of the bus for " - "drivers by writing a 1 to this file\n"); -} -static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan); - - -static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count) -{ - int state = simple_strtoul(buf, NULL, 10); - - if (state == 1) - ignore_drivers = 1; - else if (state == 0) - ignore_drivers = 0; - - return count; -} -static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf) -{ - return sprintf(buf, "%d\n", ignore_drivers); -} -static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers); - - -struct bus_attribute *const fw_bus_attrs[] = { - &bus_attr_rescan, - &bus_attr_ignore_drivers, - NULL -}; - - -fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n") -fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n") - -fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n") -fw_attr_td(ne, struct node_entry, vendor_name_kv) - -fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n") -fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n") -fw_attr(ne, struct node_entry, in_limbo, int, "%d\n"); - -static struct device_attribute *const fw_ne_attrs[] = { - &dev_attr_ne_guid, - &dev_attr_ne_guid_vendor_id, - &dev_attr_ne_capabilities, - &dev_attr_ne_vendor_id, - &dev_attr_ne_nodeid, - &dev_attr_bus_options, -#ifdef HPSB_DEBUG_TLABELS - &dev_attr_tlabels_free, - &dev_attr_tlabels_mask, -#endif -}; - - - -fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n") -fw_attr(ud, struct unit_directory, length, int, "%d\n") -/* These are all dependent on the value being provided */ -fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n") -fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n") -fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n") -fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n") -fw_attr_td(ud, struct unit_directory, vendor_name_kv) -fw_attr_td(ud, struct unit_directory, model_name_kv) - -static struct device_attribute *const fw_ud_attrs[] = { - &dev_attr_ud_address, - &dev_attr_ud_length, - &dev_attr_ignore_driver, -}; - - -fw_attr(host, struct hpsb_host, node_count, int, "%d\n") -fw_attr(host, struct hpsb_host, selfid_count, int, "%d\n") -fw_attr(host, struct hpsb_host, nodes_active, int, "%d\n") -fw_attr(host, struct hpsb_host, in_bus_reset, int, "%d\n") -fw_attr(host, struct hpsb_host, is_root, int, "%d\n") -fw_attr(host, struct hpsb_host, is_cycmst, int, "%d\n") -fw_attr(host, struct hpsb_host, is_irm, int, "%d\n") -fw_attr(host, struct hpsb_host, is_busmgr, int, "%d\n") - -static struct device_attribute *const fw_host_attrs[] = { - &dev_attr_host_node_count, - &dev_attr_host_selfid_count, - &dev_attr_host_nodes_active, - &dev_attr_host_in_bus_reset, - &dev_attr_host_is_root, - &dev_attr_host_is_cycmst, - &dev_attr_host_is_irm, - &dev_attr_host_is_busmgr, -}; - - -static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf) -{ - struct hpsb_protocol_driver *driver; - const struct ieee1394_device_id *id; - int length = 0; - char *scratch = buf; - - driver = container_of(drv, struct hpsb_protocol_driver, driver); - id = driver->id_table; - if (!id) - return 0; - - for (; id->match_flags != 0; id++) { - int need_coma = 0; - - if (id->match_flags & IEEE1394_MATCH_VENDOR_ID) { - length += sprintf(scratch, "vendor_id=0x%06x", id->vendor_id); - scratch = buf + length; - need_coma++; - } - - if (id->match_flags & IEEE1394_MATCH_MODEL_ID) { - length += sprintf(scratch, "%smodel_id=0x%06x", - need_coma++ ? "," : "", - id->model_id); - scratch = buf + length; - } - - if (id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) { - length += sprintf(scratch, "%sspecifier_id=0x%06x", - need_coma++ ? "," : "", - id->specifier_id); - scratch = buf + length; - } - - if (id->match_flags & IEEE1394_MATCH_VERSION) { - length += sprintf(scratch, "%sversion=0x%06x", - need_coma++ ? "," : "", - id->version); - scratch = buf + length; - } - - if (need_coma) { - *scratch++ = '\n'; - length++; - } - } - - return length; -} -static DRIVER_ATTR(device_ids,S_IRUGO,fw_show_drv_device_ids,NULL); - - -fw_drv_attr(name, const char *, "%s\n") - -static struct driver_attribute *const fw_drv_attrs[] = { - &driver_attr_drv_name, - &driver_attr_device_ids, -}; - - -static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver) -{ - struct device_driver *drv = &driver->driver; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++) - if (driver_create_file(drv, fw_drv_attrs[i])) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static void nodemgr_remove_drv_files(struct hpsb_protocol_driver *driver) -{ - struct device_driver *drv = &driver->driver; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++) - driver_remove_file(drv, fw_drv_attrs[i]); -} - - -static void nodemgr_create_ne_dev_files(struct node_entry *ne) -{ - struct device *dev = &ne->device; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++) - if (device_create_file(dev, fw_ne_attrs[i])) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static void nodemgr_create_host_dev_files(struct hpsb_host *host) -{ - struct device *dev = &host->device; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++) - if (device_create_file(dev, fw_host_attrs[i])) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, - nodeid_t nodeid); - -static void nodemgr_update_host_dev_links(struct hpsb_host *host) -{ - struct device *dev = &host->device; - struct node_entry *ne; - - sysfs_remove_link(&dev->kobj, "irm_id"); - sysfs_remove_link(&dev->kobj, "busmgr_id"); - sysfs_remove_link(&dev->kobj, "host_id"); - - if ((ne = find_entry_by_nodeid(host, host->irm_id)) && - sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id")) - goto fail; - if ((ne = find_entry_by_nodeid(host, host->busmgr_id)) && - sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id")) - goto fail; - if ((ne = find_entry_by_nodeid(host, host->node_id)) && - sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id")) - goto fail; - return; -fail: - HPSB_ERR("Failed to update sysfs attributes for host %d", host->id); -} - -static void nodemgr_create_ud_dev_files(struct unit_directory *ud) -{ - struct device *dev = &ud->device; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++) - if (device_create_file(dev, fw_ud_attrs[i])) - goto fail; - if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) - if (device_create_file(dev, &dev_attr_ud_specifier_id)) - goto fail; - if (ud->flags & UNIT_DIRECTORY_VERSION) - if (device_create_file(dev, &dev_attr_ud_version)) - goto fail; - if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) { - if (device_create_file(dev, &dev_attr_ud_vendor_id)) - goto fail; - if (ud->vendor_name_kv && - device_create_file(dev, &dev_attr_ud_vendor_name_kv)) - goto fail; - } - if (ud->flags & UNIT_DIRECTORY_MODEL_ID) { - if (device_create_file(dev, &dev_attr_ud_model_id)) - goto fail; - if (ud->model_name_kv && - device_create_file(dev, &dev_attr_ud_model_name_kv)) - goto fail; - } - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static int nodemgr_bus_match(struct device * dev, struct device_driver * drv) -{ - struct hpsb_protocol_driver *driver; - struct unit_directory *ud; - const struct ieee1394_device_id *id; - - /* We only match unit directories */ - if (dev->platform_data != &nodemgr_ud_platform_data) - return 0; - - ud = container_of(dev, struct unit_directory, device); - if (ud->ne->in_limbo || ud->ignore_driver) - return 0; - - /* We only match drivers of type hpsb_protocol_driver */ - if (drv == &nodemgr_mid_layer_driver) - return 0; - - driver = container_of(drv, struct hpsb_protocol_driver, driver); - id = driver->id_table; - if (!id) - return 0; - - for (; id->match_flags != 0; id++) { - if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) && - id->vendor_id != ud->vendor_id) - continue; - - if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) && - id->model_id != ud->model_id) - continue; - - if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) && - id->specifier_id != ud->specifier_id) - continue; - - if ((id->match_flags & IEEE1394_MATCH_VERSION) && - id->version != ud->version) - continue; - - return 1; - } - - return 0; -} - - -static DEFINE_MUTEX(nodemgr_serialize_remove_uds); - -static int match_ne(struct device *dev, void *data) -{ - struct unit_directory *ud; - struct node_entry *ne = data; - - ud = container_of(dev, struct unit_directory, unit_dev); - return ud->ne == ne; -} - -static void nodemgr_remove_uds(struct node_entry *ne) -{ - struct device *dev; - struct unit_directory *ud; - - /* Use class_find device to iterate the devices. Since this code - * may be called from other contexts besides the knodemgrds, - * protect it by nodemgr_serialize_remove_uds. - */ - mutex_lock(&nodemgr_serialize_remove_uds); - for (;;) { - dev = class_find_device(&nodemgr_ud_class, NULL, ne, match_ne); - if (!dev) - break; - ud = container_of(dev, struct unit_directory, unit_dev); - put_device(dev); - device_unregister(&ud->unit_dev); - device_unregister(&ud->device); - } - mutex_unlock(&nodemgr_serialize_remove_uds); -} - - -static void nodemgr_remove_ne(struct node_entry *ne) -{ - struct device *dev; - - dev = get_device(&ne->device); - if (!dev) - return; - - HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); - nodemgr_remove_uds(ne); - - device_unregister(&ne->node_dev); - device_unregister(dev); - - put_device(dev); -} - -static int remove_host_dev(struct device *dev, void *data) -{ - if (dev->bus == &ieee1394_bus_type) - nodemgr_remove_ne(container_of(dev, struct node_entry, - device)); - return 0; -} - -static void nodemgr_remove_host_dev(struct device *dev) -{ - device_for_each_child(dev, NULL, remove_host_dev); - sysfs_remove_link(&dev->kobj, "irm_id"); - sysfs_remove_link(&dev->kobj, "busmgr_id"); - sysfs_remove_link(&dev->kobj, "host_id"); -} - - -static void nodemgr_update_bus_options(struct node_entry *ne) -{ -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - static const u16 mr[] = { 4, 64, 1024, 0}; -#endif - quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]); - - ne->busopt.irmc = (busoptions >> 31) & 1; - ne->busopt.cmc = (busoptions >> 30) & 1; - ne->busopt.isc = (busoptions >> 29) & 1; - ne->busopt.bmc = (busoptions >> 28) & 1; - ne->busopt.pmc = (busoptions >> 27) & 1; - ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff; - ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1); - ne->busopt.max_rom = (busoptions >> 8) & 0x3; - ne->busopt.generation = (busoptions >> 4) & 0xf; - ne->busopt.lnkspd = busoptions & 0x7; - - HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d " - "cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d", - busoptions, ne->busopt.irmc, ne->busopt.cmc, - ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc, - ne->busopt.cyc_clk_acc, ne->busopt.max_rec, - mr[ne->busopt.max_rom], - ne->busopt.generation, ne->busopt.lnkspd); -} - - -static struct node_entry *nodemgr_create_node(octlet_t guid, - struct csr1212_csr *csr, struct hpsb_host *host, - nodeid_t nodeid, unsigned int generation) -{ - struct node_entry *ne; - - ne = kzalloc(sizeof(*ne), GFP_KERNEL); - if (!ne) - goto fail_alloc; - - ne->host = host; - ne->nodeid = nodeid; - ne->generation = generation; - ne->needs_probe = true; - - ne->guid = guid; - ne->guid_vendor_id = (guid >> 40) & 0xffffff; - ne->csr = csr; - - memcpy(&ne->device, &nodemgr_dev_template_ne, - sizeof(ne->device)); - ne->device.parent = &host->device; - dev_set_name(&ne->device, "%016Lx", (unsigned long long)(ne->guid)); - - ne->node_dev.parent = &ne->device; - ne->node_dev.class = &nodemgr_ne_class; - dev_set_name(&ne->node_dev, "%016Lx", (unsigned long long)(ne->guid)); - - if (device_register(&ne->device)) - goto fail_devreg; - if (device_register(&ne->node_dev)) - goto fail_classdevreg; - get_device(&ne->device); - - nodemgr_create_ne_dev_files(ne); - - nodemgr_update_bus_options(ne); - - HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - (host->node_id == nodeid) ? "Host" : "Node", - NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); - - return ne; - -fail_classdevreg: - device_unregister(&ne->device); -fail_devreg: - kfree(ne); -fail_alloc: - HPSB_ERR("Failed to create node ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); - - return NULL; -} - -static int match_ne_guid(struct device *dev, void *data) -{ - struct node_entry *ne; - u64 *guid = data; - - ne = container_of(dev, struct node_entry, node_dev); - return ne->guid == *guid; -} - -static struct node_entry *find_entry_by_guid(u64 guid) -{ - struct device *dev; - struct node_entry *ne; - - dev = class_find_device(&nodemgr_ne_class, NULL, &guid, match_ne_guid); - if (!dev) - return NULL; - ne = container_of(dev, struct node_entry, node_dev); - put_device(dev); - - return ne; -} - -struct match_nodeid_parameter { - struct hpsb_host *host; - nodeid_t nodeid; -}; - -static int match_ne_nodeid(struct device *dev, void *data) -{ - int found = 0; - struct node_entry *ne; - struct match_nodeid_parameter *p = data; - - if (!dev) - goto ret; - ne = container_of(dev, struct node_entry, node_dev); - if (ne->host == p->host && ne->nodeid == p->nodeid) - found = 1; -ret: - return found; -} - -static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, - nodeid_t nodeid) -{ - struct device *dev; - struct node_entry *ne; - struct match_nodeid_parameter p; - - p.host = host; - p.nodeid = nodeid; - - dev = class_find_device(&nodemgr_ne_class, NULL, &p, match_ne_nodeid); - if (!dev) - return NULL; - ne = container_of(dev, struct node_entry, node_dev); - put_device(dev); - - return ne; -} - - -static void nodemgr_register_device(struct node_entry *ne, - struct unit_directory *ud, struct device *parent) -{ - memcpy(&ud->device, &nodemgr_dev_template_ud, - sizeof(ud->device)); - - ud->device.parent = parent; - - dev_set_name(&ud->device, "%s-%u", dev_name(&ne->device), ud->id); - - ud->unit_dev.parent = &ud->device; - ud->unit_dev.class = &nodemgr_ud_class; - dev_set_name(&ud->unit_dev, "%s-%u", dev_name(&ne->device), ud->id); - - if (device_register(&ud->device)) - goto fail_devreg; - if (device_register(&ud->unit_dev)) - goto fail_classdevreg; - get_device(&ud->device); - - nodemgr_create_ud_dev_files(ud); - - return; - -fail_classdevreg: - device_unregister(&ud->device); -fail_devreg: - HPSB_ERR("Failed to create unit %s", dev_name(&ud->device)); -} - - -/* This implementation currently only scans the config rom and its - * immediate unit directories looking for software_id and - * software_version entries, in order to get driver autoloading working. */ -static struct unit_directory *nodemgr_process_unit_directory - (struct node_entry *ne, struct csr1212_keyval *ud_kv, - unsigned int *id, struct unit_directory *parent) -{ - struct unit_directory *ud; - struct unit_directory *ud_child = NULL; - struct csr1212_dentry *dentry; - struct csr1212_keyval *kv; - u8 last_key_id = 0; - - ud = kzalloc(sizeof(*ud), GFP_KERNEL); - if (!ud) - goto unit_directory_error; - - ud->ne = ne; - ud->ignore_driver = ignore_drivers; - ud->address = ud_kv->offset + CSR1212_REGISTER_SPACE_BASE; - ud->directory_id = ud->address & 0xffffff; - ud->ud_kv = ud_kv; - ud->id = (*id)++; - - /* inherit vendor_id from root directory if none exists in unit dir */ - ud->vendor_id = ne->vendor_id; - - csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) { - switch (kv->key.id) { - case CSR1212_KV_ID_VENDOR: - if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { - ud->vendor_id = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_VENDOR_ID; - } - break; - - case CSR1212_KV_ID_MODEL: - ud->model_id = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_MODEL_ID; - break; - - case CSR1212_KV_ID_SPECIFIER_ID: - ud->specifier_id = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID; - break; - - case CSR1212_KV_ID_VERSION: - ud->version = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_VERSION; - break; - - case CSR1212_KV_ID_DESCRIPTOR: - if (kv->key.type == CSR1212_KV_TYPE_LEAF && - CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && - CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { - switch (last_key_id) { - case CSR1212_KV_ID_VENDOR: - csr1212_keep_keyval(kv); - ud->vendor_name_kv = kv; - break; - - case CSR1212_KV_ID_MODEL: - csr1212_keep_keyval(kv); - ud->model_name_kv = kv; - break; - - } - } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */ - break; - - case CSR1212_KV_ID_DEPENDENT_INFO: - /* Logical Unit Number */ - if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { - if (ud->flags & UNIT_DIRECTORY_HAS_LUN) { - ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL); - if (!ud_child) - goto unit_directory_error; - nodemgr_register_device(ne, ud_child, &ne->device); - ud_child = NULL; - - ud->id = (*id)++; - } - ud->lun = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_HAS_LUN; - - /* Logical Unit Directory */ - } else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) { - /* This should really be done in SBP2 as this is - * doing SBP2 specific parsing. - */ - - /* first register the parent unit */ - ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY; - if (ud->device.bus != &ieee1394_bus_type) - nodemgr_register_device(ne, ud, &ne->device); - - /* process the child unit */ - ud_child = nodemgr_process_unit_directory(ne, kv, id, ud); - - if (ud_child == NULL) - break; - - /* inherit unspecified values, the driver core picks it up */ - if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) && - !(ud_child->flags & UNIT_DIRECTORY_MODEL_ID)) - { - ud_child->flags |= UNIT_DIRECTORY_MODEL_ID; - ud_child->model_id = ud->model_id; - } - if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) && - !(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID)) - { - ud_child->flags |= UNIT_DIRECTORY_SPECIFIER_ID; - ud_child->specifier_id = ud->specifier_id; - } - if ((ud->flags & UNIT_DIRECTORY_VERSION) && - !(ud_child->flags & UNIT_DIRECTORY_VERSION)) - { - ud_child->flags |= UNIT_DIRECTORY_VERSION; - ud_child->version = ud->version; - } - - /* register the child unit */ - ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY; - nodemgr_register_device(ne, ud_child, &ud->device); - } - - break; - - case CSR1212_KV_ID_DIRECTORY_ID: - ud->directory_id = kv->value.immediate; - break; - - default: - break; - } - last_key_id = kv->key.id; - } - - /* do not process child units here and only if not already registered */ - if (!parent && ud->device.bus != &ieee1394_bus_type) - nodemgr_register_device(ne, ud, &ne->device); - - return ud; - -unit_directory_error: - kfree(ud); - return NULL; -} - - -static void nodemgr_process_root_directory(struct node_entry *ne) -{ - unsigned int ud_id = 0; - struct csr1212_dentry *dentry; - struct csr1212_keyval *kv, *vendor_name_kv = NULL; - u8 last_key_id = 0; - - ne->needs_probe = false; - - csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) { - switch (kv->key.id) { - case CSR1212_KV_ID_VENDOR: - ne->vendor_id = kv->value.immediate; - break; - - case CSR1212_KV_ID_NODE_CAPABILITIES: - ne->capabilities = kv->value.immediate; - break; - - case CSR1212_KV_ID_UNIT: - nodemgr_process_unit_directory(ne, kv, &ud_id, NULL); - break; - - case CSR1212_KV_ID_DESCRIPTOR: - if (last_key_id == CSR1212_KV_ID_VENDOR) { - if (kv->key.type == CSR1212_KV_TYPE_LEAF && - CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && - CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { - csr1212_keep_keyval(kv); - vendor_name_kv = kv; - } - } - break; - } - last_key_id = kv->key.id; - } - - if (ne->vendor_name_kv) { - kv = ne->vendor_name_kv; - ne->vendor_name_kv = vendor_name_kv; - csr1212_release_keyval(kv); - } else if (vendor_name_kv) { - ne->vendor_name_kv = vendor_name_kv; - if (device_create_file(&ne->device, - &dev_attr_ne_vendor_name_kv) != 0) - HPSB_ERR("Failed to add sysfs attribute"); - } -} - -#ifdef CONFIG_HOTPLUG - -static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct unit_directory *ud; - int retval = 0; - /* ieee1394:venNmoNspNverN */ - char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1]; - - if (!dev) - return -ENODEV; - - ud = container_of(dev, struct unit_directory, unit_dev); - - if (ud->ne->in_limbo || ud->ignore_driver) - return -ENODEV; - -#define PUT_ENVP(fmt,val) \ -do { \ - retval = add_uevent_var(env, fmt, val); \ - if (retval) \ - return retval; \ -} while (0) - - PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id); - PUT_ENVP("MODEL_ID=%06x", ud->model_id); - PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid); - PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id); - PUT_ENVP("VERSION=%06x", ud->version); - snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X", - ud->vendor_id, - ud->model_id, - ud->specifier_id, - ud->version); - PUT_ENVP("MODALIAS=%s", buf); - -#undef PUT_ENVP - - return 0; -} - -#else - -static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - return -ENODEV; -} - -#endif /* CONFIG_HOTPLUG */ - - -int __hpsb_register_protocol(struct hpsb_protocol_driver *drv, - struct module *owner) -{ - int error; - - drv->driver.bus = &ieee1394_bus_type; - drv->driver.owner = owner; - drv->driver.name = drv->name; - - /* This will cause a probe for devices */ - error = driver_register(&drv->driver); - if (!error) - nodemgr_create_drv_files(drv); - return error; -} - -void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver) -{ - nodemgr_remove_drv_files(driver); - /* This will subsequently disconnect all devices that our driver - * is attached to. */ - driver_unregister(&driver->driver); -} - - -/* - * This function updates nodes that were present on the bus before the - * reset and still are after the reset. The nodeid and the config rom - * may have changed, and the drivers managing this device must be - * informed that this device just went through a bus reset, to allow - * the to take whatever actions required. - */ -static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr, - nodeid_t nodeid, unsigned int generation) -{ - if (ne->nodeid != nodeid) { - HPSB_DEBUG("Node changed: " NODE_BUS_FMT " -> " NODE_BUS_FMT, - NODE_BUS_ARGS(ne->host, ne->nodeid), - NODE_BUS_ARGS(ne->host, nodeid)); - ne->nodeid = nodeid; - } - - if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) { - kfree(ne->csr->private); - csr1212_destroy_csr(ne->csr); - ne->csr = csr; - - /* If the node's configrom generation has changed, we - * unregister all the unit directories. */ - nodemgr_remove_uds(ne); - - nodemgr_update_bus_options(ne); - - /* Mark the node as new, so it gets re-probed */ - ne->needs_probe = true; - } else { - /* old cache is valid, so update its generation */ - struct nodemgr_csr_info *ci = ne->csr->private; - ci->generation = generation; - /* free the partially filled now unneeded new cache */ - kfree(csr->private); - csr1212_destroy_csr(csr); - } - - /* Finally, mark the node current */ - smp_wmb(); - ne->generation = generation; - - if (ne->in_limbo) { - device_remove_file(&ne->device, &dev_attr_ne_in_limbo); - ne->in_limbo = false; - - HPSB_DEBUG("Node reactivated: " - "ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(ne->host, ne->nodeid), - (unsigned long long)ne->guid); - } -} - -static void nodemgr_node_scan_one(struct hpsb_host *host, - nodeid_t nodeid, int generation) -{ - struct node_entry *ne; - octlet_t guid; - struct csr1212_csr *csr; - struct nodemgr_csr_info *ci; - u8 *speed; - - ci = kmalloc(sizeof(*ci), GFP_KERNEL); - kmemcheck_annotate_bitfield(ci, flags); - if (!ci) - return; - - ci->host = host; - ci->nodeid = nodeid; - ci->generation = generation; - - /* Prepare for speed probe which occurs when reading the ROM */ - speed = &(host->speed[NODEID_TO_NODE(nodeid)]); - if (*speed > host->csr.lnk_spd) - *speed = host->csr.lnk_spd; - ci->speed_unverified = *speed > IEEE1394_SPEED_100; - - /* We need to detect when the ConfigROM's generation has changed, - * so we only update the node's info when it needs to be. */ - - csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci); - if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) { - HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT, - NODE_BUS_ARGS(host, nodeid)); - if (csr) - csr1212_destroy_csr(csr); - kfree(ci); - return; - } - - if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) { - /* This isn't a 1394 device, but we let it slide. There - * was a report of a device with broken firmware which - * reported '2394' instead of '1394', which is obviously a - * mistake. One would hope that a non-1394 device never - * gets connected to Firewire bus. If someone does, we - * shouldn't be held responsible, so we'll allow it with a - * warning. */ - HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]", - NODE_BUS_ARGS(host, nodeid), csr->bus_info_data[1]); - } - - guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]); - ne = find_entry_by_guid(guid); - - if (ne && ne->host != host && ne->in_limbo) { - /* Must have moved this device from one host to another */ - nodemgr_remove_ne(ne); - ne = NULL; - } - - if (!ne) - nodemgr_create_node(guid, csr, host, nodeid, generation); - else - nodemgr_update_node(ne, csr, nodeid, generation); -} - - -static void nodemgr_node_scan(struct hpsb_host *host, int generation) -{ - int count; - struct selfid *sid = (struct selfid *)host->topology_map; - nodeid_t nodeid = LOCAL_BUS; - - /* Scan each node on the bus */ - for (count = host->selfid_count; count; count--, sid++) { - if (sid->extended) - continue; - - if (!sid->link_active) { - nodeid++; - continue; - } - nodemgr_node_scan_one(host, nodeid++, generation); - } -} - -static void nodemgr_pause_ne(struct node_entry *ne) -{ - HPSB_DEBUG("Node paused: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(ne->host, ne->nodeid), - (unsigned long long)ne->guid); - - ne->in_limbo = true; - WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo)); -} - -static int update_pdrv(struct device *dev, void *data) -{ - struct unit_directory *ud; - struct device_driver *drv; - struct hpsb_protocol_driver *pdrv; - struct node_entry *ne = data; - int error; - - ud = container_of(dev, struct unit_directory, unit_dev); - if (ud->ne == ne) { - drv = get_driver(ud->device.driver); - if (drv) { - error = 0; - pdrv = container_of(drv, struct hpsb_protocol_driver, - driver); - if (pdrv->update) { - device_lock(&ud->device); - error = pdrv->update(ud); - device_unlock(&ud->device); - } - if (error) - device_release_driver(&ud->device); - put_driver(drv); - } - } - - return 0; -} - -static void nodemgr_update_pdrv(struct node_entry *ne) -{ - class_for_each_device(&nodemgr_ud_class, NULL, ne, update_pdrv); -} - -/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This - * seems like an optional service but in the end it is practically mandatory - * as a consequence of these clauses. - * - * Note that we cannot do a broadcast write to all nodes at once because some - * pre-1394a devices would hang. */ -static void nodemgr_irm_write_bc(struct node_entry *ne, int generation) -{ - const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL); - quadlet_t bc_remote, bc_local; - int error; - - if (!ne->host->is_irm || ne->generation != generation || - ne->nodeid == ne->host->node_id) - return; - - bc_local = cpu_to_be32(ne->host->csr.broadcast_channel); - - /* Check if the register is implemented and 1394a compliant. */ - error = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote, - sizeof(bc_remote)); - if (!error && bc_remote & cpu_to_be32(0x80000000) && - bc_remote != bc_local) - hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local)); -} - - -static void nodemgr_probe_ne(struct hpsb_host *host, struct node_entry *ne, - int generation) -{ - struct device *dev; - - if (ne->host != host || ne->in_limbo) - return; - - dev = get_device(&ne->device); - if (!dev) - return; - - nodemgr_irm_write_bc(ne, generation); - - /* If "needs_probe", then this is either a new or changed node we - * rescan totally. If the generation matches for an existing node - * (one that existed prior to the bus reset) we send update calls - * down to the drivers. Otherwise, this is a dead node and we - * suspend it. */ - if (ne->needs_probe) - nodemgr_process_root_directory(ne); - else if (ne->generation == generation) - nodemgr_update_pdrv(ne); - else - nodemgr_pause_ne(ne); - - put_device(dev); -} - -struct node_probe_parameter { - struct hpsb_host *host; - int generation; - bool probe_now; -}; - -static int node_probe(struct device *dev, void *data) -{ - struct node_probe_parameter *p = data; - struct node_entry *ne; - - if (p->generation != get_hpsb_generation(p->host)) - return -EAGAIN; - - ne = container_of(dev, struct node_entry, node_dev); - if (ne->needs_probe == p->probe_now) - nodemgr_probe_ne(p->host, ne, p->generation); - return 0; -} - -static int nodemgr_node_probe(struct hpsb_host *host, int generation) -{ - struct node_probe_parameter p; - - p.host = host; - p.generation = generation; - /* - * Do some processing of the nodes we've probed. This pulls them - * into the sysfs layer if needed, and can result in processing of - * unit-directories, or just updating the node and it's - * unit-directories. - * - * Run updates before probes. Usually, updates are time-critical - * while probes are time-consuming. - * - * Meanwhile, another bus reset may have happened. In this case we - * skip everything here and let the next bus scan handle it. - * Otherwise we may prematurely remove nodes which are still there. - */ - p.probe_now = false; - if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0) - return 0; - - p.probe_now = true; - if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0) - return 0; - /* - * Now let's tell the bus to rescan our devices. This may seem - * like overhead, but the driver-model core will only scan a - * device for a driver when either the device is added, or when a - * new driver is added. A bus reset is a good reason to rescan - * devices that were there before. For example, an sbp2 device - * may become available for login, if the host that held it was - * just removed. - */ - if (bus_rescan_devices(&ieee1394_bus_type) != 0) - HPSB_DEBUG("bus_rescan_devices had an error"); - - return 1; -} - -static int remove_nodes_in_limbo(struct device *dev, void *data) -{ - struct node_entry *ne; - - if (dev->bus != &ieee1394_bus_type) - return 0; - - ne = container_of(dev, struct node_entry, device); - if (ne->in_limbo) - nodemgr_remove_ne(ne); - - return 0; -} - -static void nodemgr_remove_nodes_in_limbo(struct hpsb_host *host) -{ - device_for_each_child(&host->device, NULL, remove_nodes_in_limbo); -} - -static int nodemgr_send_resume_packet(struct hpsb_host *host) -{ - struct hpsb_packet *packet; - int error = -ENOMEM; - - packet = hpsb_make_phypacket(host, - EXTPHYPACKET_TYPE_RESUME | - NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT); - if (packet) { - packet->no_waiter = 1; - packet->generation = get_hpsb_generation(host); - error = hpsb_send_packet(packet); - } - if (error) - HPSB_WARN("fw-host%d: Failed to broadcast resume packet", - host->id); - return error; -} - -/* Perform a few high-level IRM responsibilities. */ -static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles) -{ - quadlet_t bc; - - /* if irm_id == -1 then there is no IRM on this bus */ - if (!host->is_irm || host->irm_id == (nodeid_t)-1) - return 1; - - /* We are a 1394a-2000 compliant IRM. Set the validity bit. */ - host->csr.broadcast_channel |= 0x40000000; - - /* If there is no bus manager then we should set the root node's - * force_root bit to promote bus stability per the 1394 - * spec. (8.4.2.6) */ - if (host->busmgr_id == 0xffff && host->node_count > 1) - { - u16 root_node = host->node_count - 1; - - /* get cycle master capability flag from root node */ - if (host->is_cycmst || - (!hpsb_read(host, LOCAL_BUS | root_node, get_hpsb_generation(host), - (CSR_REGISTER_BASE + CSR_CONFIG_ROM + 2 * sizeof(quadlet_t)), - &bc, sizeof(quadlet_t)) && - be32_to_cpu(bc) & 1 << CSR_CMC_SHIFT)) - hpsb_send_phy_config(host, root_node, -1); - else { - HPSB_DEBUG("The root node is not cycle master capable; " - "selecting a new root node and resetting..."); - - if (cycles >= 5) { - /* Oh screw it! Just leave the bus as it is */ - HPSB_DEBUG("Stopping reset loop for IRM sanity"); - return 1; - } - - hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); - hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); - - return 0; - } - } - - /* Some devices suspend their ports while being connected to an inactive - * host adapter, i.e. if connected before the low-level driver is - * loaded. They become visible either when physically unplugged and - * replugged, or when receiving a resume packet. Send one once. */ - if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host)) - host->resume_packet_sent = 1; - - return 1; -} - -/* We need to ensure that if we are not the IRM, that the IRM node is capable of - * everything we can do, otherwise issue a bus reset and try to become the IRM - * ourselves. */ -static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles) -{ - quadlet_t bc; - int status; - - if (hpsb_disable_irm || host->is_irm) - return 1; - - status = hpsb_read(host, LOCAL_BUS | (host->irm_id), - get_hpsb_generation(host), - (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL), - &bc, sizeof(quadlet_t)); - - if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) { - /* The current irm node does not have a valid BROADCAST_CHANNEL - * register and we do, so reset the bus with force_root set */ - HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting..."); - - if (cycles >= 5) { - /* Oh screw it! Just leave the bus as it is */ - HPSB_DEBUG("Stopping reset loop for IRM sanity"); - return 1; - } - - hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); - hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); - - return 0; - } - - return 1; -} - -static int nodemgr_host_thread(void *data) -{ - struct hpsb_host *host = data; - unsigned int g, generation = 0; - int i, reset_cycles = 0; - - set_freezable(); - /* Setup our device-model entries */ - nodemgr_create_host_dev_files(host); - - for (;;) { - /* Sleep until next bus reset */ - set_current_state(TASK_INTERRUPTIBLE); - if (get_hpsb_generation(host) == generation && - !kthread_should_stop()) - schedule(); - __set_current_state(TASK_RUNNING); - - /* Thread may have been woken up to freeze or to exit */ - if (try_to_freeze()) - continue; - if (kthread_should_stop()) - goto exit; - - /* Pause for 1/4 second in 1/16 second intervals, - * to make sure things settle down. */ - g = get_hpsb_generation(host); - for (i = 0; i < 4 ; i++) { - msleep_interruptible(63); - try_to_freeze(); - if (kthread_should_stop()) - goto exit; - - /* Now get the generation in which the node ID's we collect - * are valid. During the bus scan we will use this generation - * for the read transactions, so that if another reset occurs - * during the scan the transactions will fail instead of - * returning bogus data. */ - generation = get_hpsb_generation(host); - - /* If we get a reset before we are done waiting, then - * start the waiting over again */ - if (generation != g) - g = generation, i = 0; - } - - if (!nodemgr_check_irm_capability(host, reset_cycles) || - !nodemgr_do_irm_duties(host, reset_cycles)) { - reset_cycles++; - continue; - } - reset_cycles = 0; - - /* Scan our nodes to get the bus options and create node - * entries. This does not do the sysfs stuff, since that - * would trigger uevents and such, which is a bad idea at - * this point. */ - nodemgr_node_scan(host, generation); - - /* This actually does the full probe, with sysfs - * registration. */ - if (!nodemgr_node_probe(host, generation)) - continue; - - /* Update some of our sysfs symlinks */ - nodemgr_update_host_dev_links(host); - - /* Sleep 3 seconds */ - for (i = 3000/200; i; i--) { - msleep_interruptible(200); - try_to_freeze(); - if (kthread_should_stop()) - goto exit; - - if (generation != get_hpsb_generation(host)) - break; - } - /* Remove nodes which are gone, unless a bus reset happened */ - if (!i) - nodemgr_remove_nodes_in_limbo(host); - } -exit: - HPSB_VERBOSE("NodeMgr: Exiting thread"); - return 0; -} - -struct per_host_parameter { - void *data; - int (*cb)(struct hpsb_host *, void *); -}; - -static int per_host(struct device *dev, void *data) -{ - struct hpsb_host *host; - struct per_host_parameter *p = data; - - host = container_of(dev, struct hpsb_host, host_dev); - return p->cb(host, p->data); -} - -/** - * nodemgr_for_each_host - call a function for each IEEE 1394 host - * @data: an address to supply to the callback - * @cb: function to call for each host - * - * Iterate the hosts, calling a given function with supplied data for each host. - * If the callback fails on a host, i.e. if it returns a non-zero value, the - * iteration is stopped. - * - * Return value: 0 on success, non-zero on failure (same as returned by last run - * of the callback). - */ -int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) -{ - struct per_host_parameter p; - - p.cb = cb; - p.data = data; - return class_for_each_device(&hpsb_host_class, NULL, &p, per_host); -} - -/* The following two convenience functions use a struct node_entry - * for addressing a node on the bus. They are intended for use by any - * process context, not just the nodemgr thread, so we need to be a - * little careful when reading out the node ID and generation. The - * thing that can go wrong is that we get the node ID, then a bus - * reset occurs, and then we read the generation. The node ID is - * possibly invalid, but the generation is current, and we end up - * sending a packet to a the wrong node. - * - * The solution is to make sure we read the generation first, so that - * if a reset occurs in the process, we end up with a stale generation - * and the transactions will fail instead of silently using wrong node - * ID's. - */ - -/** - * hpsb_node_fill_packet - fill some destination information into a packet - * @ne: destination node - * @packet: packet to fill in - * - * This will fill in the given, pre-initialised hpsb_packet with the current - * information from the node entry (host, node ID, bus generation number). - */ -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) -{ - packet->host = ne->host; - packet->generation = ne->generation; - smp_rmb(); - packet->node_id = ne->nodeid; -} - -int hpsb_node_write(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length) -{ - unsigned int generation = ne->generation; - - smp_rmb(); - return hpsb_write(ne->host, ne->nodeid, generation, - addr, buffer, length); -} - -static void nodemgr_add_host(struct hpsb_host *host) -{ - struct host_info *hi; - - hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi)); - if (!hi) { - HPSB_ERR("NodeMgr: out of memory in add host"); - return; - } - hi->host = host; - hi->thread = kthread_run(nodemgr_host_thread, host, "knodemgrd_%d", - host->id); - if (IS_ERR(hi->thread)) { - HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id); - hpsb_destroy_hostinfo(&nodemgr_highlevel, host); - } -} - -static void nodemgr_host_reset(struct hpsb_host *host) -{ - struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); - - if (hi) { - HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id); - wake_up_process(hi->thread); - } -} - -static void nodemgr_remove_host(struct hpsb_host *host) -{ - struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); - - if (hi) { - kthread_stop(hi->thread); - nodemgr_remove_host_dev(&host->device); - } -} - -static struct hpsb_highlevel nodemgr_highlevel = { - .name = "Node manager", - .add_host = nodemgr_add_host, - .host_reset = nodemgr_host_reset, - .remove_host = nodemgr_remove_host, -}; - -int init_ieee1394_nodemgr(void) -{ - int error; - - error = class_register(&nodemgr_ne_class); - if (error) - goto fail_ne; - error = class_register(&nodemgr_ud_class); - if (error) - goto fail_ud; - error = driver_register(&nodemgr_mid_layer_driver); - if (error) - goto fail_ml; - /* This driver is not used if nodemgr is off (disable_nodemgr=1). */ - nodemgr_dev_template_host.driver = &nodemgr_mid_layer_driver; - - hpsb_register_highlevel(&nodemgr_highlevel); - return 0; - -fail_ml: - class_unregister(&nodemgr_ud_class); -fail_ud: - class_unregister(&nodemgr_ne_class); -fail_ne: - return error; -} - -void cleanup_ieee1394_nodemgr(void) -{ - hpsb_unregister_highlevel(&nodemgr_highlevel); - driver_unregister(&nodemgr_mid_layer_driver); - class_unregister(&nodemgr_ud_class); - class_unregister(&nodemgr_ne_class); -} diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h deleted file mode 100644 index 749b271d310..00000000000 --- a/drivers/ieee1394/nodemgr.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2000 Andreas E. Bombe - * 2001 Ben Collins <bcollins@debian.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _IEEE1394_NODEMGR_H -#define _IEEE1394_NODEMGR_H - -#include <linux/device.h> -#include <asm/system.h> -#include <asm/types.h> - -#include "ieee1394_core.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" - -struct csr1212_csr; -struct csr1212_keyval; -struct hpsb_host; -struct ieee1394_device_id; - -/* This is the start of a Node entry structure. It should be a stable API - * for which to gather info from the Node Manager about devices attached - * to the bus. */ -struct bus_options { - u8 irmc; /* Iso Resource Manager Capable */ - u8 cmc; /* Cycle Master Capable */ - u8 isc; /* Iso Capable */ - u8 bmc; /* Bus Master Capable */ - u8 pmc; /* Power Manager Capable (PNP spec) */ - u8 cyc_clk_acc; /* Cycle clock accuracy */ - u8 max_rom; /* Maximum block read supported in the CSR */ - u8 generation; /* Incremented when configrom changes */ - u8 lnkspd; /* Link speed */ - u16 max_rec; /* Maximum packet size node can receive */ -}; - -#define UNIT_DIRECTORY_VENDOR_ID 0x01 -#define UNIT_DIRECTORY_MODEL_ID 0x02 -#define UNIT_DIRECTORY_SPECIFIER_ID 0x04 -#define UNIT_DIRECTORY_VERSION 0x08 -#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x10 -#define UNIT_DIRECTORY_LUN_DIRECTORY 0x20 -#define UNIT_DIRECTORY_HAS_LUN 0x40 - -/* - * A unit directory corresponds to a protocol supported by the - * node. If a node supports eg. IP/1394 and AV/C, its config rom has a - * unit directory for each of these protocols. - */ -struct unit_directory { - struct node_entry *ne; /* The node which this directory belongs to */ - octlet_t address; /* Address of the unit directory on the node */ - u8 flags; /* Indicates which entries were read */ - - quadlet_t vendor_id; - struct csr1212_keyval *vendor_name_kv; - - quadlet_t model_id; - struct csr1212_keyval *model_name_kv; - quadlet_t specifier_id; - quadlet_t version; - quadlet_t directory_id; - - unsigned int id; - - int ignore_driver; - - int length; /* Number of quadlets */ - - struct device device; - struct device unit_dev; - - struct csr1212_keyval *ud_kv; - u32 lun; /* logical unit number immediate value */ -}; - -struct node_entry { - u64 guid; /* GUID of this node */ - u32 guid_vendor_id; /* Top 24bits of guid */ - - struct hpsb_host *host; /* Host this node is attached to */ - nodeid_t nodeid; /* NodeID */ - struct bus_options busopt; /* Bus Options */ - bool needs_probe; - unsigned int generation; /* Synced with hpsb generation */ - - /* The following is read from the config rom */ - u32 vendor_id; - struct csr1212_keyval *vendor_name_kv; - - u32 capabilities; - - struct device device; - struct device node_dev; - - /* Means this node is not attached anymore */ - bool in_limbo; - - struct csr1212_csr *csr; -}; - -struct hpsb_protocol_driver { - /* The name of the driver, e.g. SBP2 or IP1394 */ - const char *name; - - /* - * The device id table describing the protocols and/or devices - * supported by this driver. This is used by the nodemgr to - * decide if a driver could support a given node, but the - * probe function below can implement further protocol - * dependent or vendor dependent checking. - */ - const struct ieee1394_device_id *id_table; - - /* - * The update function is called when the node has just - * survived a bus reset, i.e. it is still present on the bus. - * However, it may be necessary to reestablish the connection - * or login into the node again, depending on the protocol. If the - * probe fails (returns non-zero), we unbind the driver from this - * device. - */ - int (*update)(struct unit_directory *ud); - - /* Our LDM structure */ - struct device_driver driver; -}; - -int __hpsb_register_protocol(struct hpsb_protocol_driver *, struct module *); -static inline int hpsb_register_protocol(struct hpsb_protocol_driver *driver) -{ - return __hpsb_register_protocol(driver, THIS_MODULE); -} - -void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver); - -static inline int hpsb_node_entry_valid(struct node_entry *ne) -{ - return ne->generation == get_hpsb_generation(ne->host); -} -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); -int hpsb_node_write(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length); -static inline int hpsb_node_read(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length) -{ - unsigned int g = ne->generation; - - smp_rmb(); - return hpsb_read(ne->host, ne->nodeid, g, addr, buffer, length); -} -static inline int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode, - quadlet_t *buffer, quadlet_t arg) -{ - unsigned int g = ne->generation; - - smp_rmb(); - return hpsb_lock(ne->host, ne->nodeid, g, addr, extcode, buffer, arg); -} -int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); - -int init_ieee1394_nodemgr(void); -void cleanup_ieee1394_nodemgr(void); - -/* The template for a host device */ -extern struct device nodemgr_dev_template_host; - -/* Bus attributes we export */ -extern struct bus_attribute *const fw_bus_attrs[]; - -#endif /* _IEEE1394_NODEMGR_H */ diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c deleted file mode 100644 index 50815022cff..00000000000 --- a/drivers/ieee1394/ohci1394.c +++ /dev/null @@ -1,3590 +0,0 @@ -/* - * ohci1394.c - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * Gord Peters <GordPeters@smarttech.com> - * 2001 Ben Collins <bcollins@debian.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * Things known to be working: - * . Async Request Transmit - * . Async Response Receive - * . Async Request Receive - * . Async Response Transmit - * . Iso Receive - * . DMA mmap for iso receive - * . Config ROM generation - * - * Things implemented, but still in test phase: - * . Iso Transmit - * . Async Stream Packets Transmit (Receive done via Iso interface) - * - * Things not implemented: - * . DMA error recovery - * - * Known bugs: - * . devctl BUS_RESET arg confusion (reset type or root holdoff?) - * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk - */ - -/* - * Acknowledgments: - * - * Adam J Richter <adam@yggdrasil.com> - * . Use of pci_class to find device - * - * Emilie Chung <emilie.chung@axis.com> - * . Tip on Async Request Filter - * - * Pascal Drolet <pascal.drolet@informission.ca> - * . Various tips for optimization and functionnalities - * - * Robert Ficklin <rficklin@westengineering.com> - * . Loop in irq_handler - * - * James Goodwin <jamesg@Filanet.com> - * . Various tips on initialization, self-id reception, etc. - * - * Albrecht Dress <ad@mpifr-bonn.mpg.de> - * . Apple PowerBook detection - * - * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> - * . Reset the board properly before leaving + misc cleanups - * - * Leon van Stuivenberg <leonvs@iae.nl> - * . Bug fixes - * - * Ben Collins <bcollins@debian.org> - * . Working big-endian support - * . Updated to 2.4.x module scheme (PCI aswell) - * . Config ROM generation - * - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * . Reworked code for initiating bus resets - * (long, short, with or without hold-off) - * - * Nandu Santhi <contactnandu@users.sourceforge.net> - * . Added support for nVidia nForce2 onboard Firewire chipset - * - */ - -#include <linux/bitops.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/pci.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <asm/byteorder.h> -#include <asm/atomic.h> -#include <asm/uaccess.h> -#include <linux/delay.h> -#include <linux/spinlock.h> - -#include <asm/pgtable.h> -#include <asm/page.h> -#include <asm/irq.h> -#include <linux/types.h> -#include <linux/vmalloc.h> -#include <linux/init.h> - -#ifdef CONFIG_PPC_PMAC -#include <asm/machdep.h> -#include <asm/pmac_feature.h> -#include <asm/prom.h> -#include <asm/pci-bridge.h> -#endif - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "dma.h" -#include "iso.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "ohci1394.h" - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define OHCI1394_DEBUG -#endif - -#ifdef DBGMSG -#undef DBGMSG -#endif - -#ifdef OHCI1394_DEBUG -#define DBGMSG(fmt, args...) \ -printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) -#else -#define DBGMSG(fmt, args...) do {} while (0) -#endif - -/* print general (card independent) information */ -#define PRINT_G(level, fmt, args...) \ -printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args) - -/* print card specific information */ -#define PRINT(level, fmt, args...) \ -printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) - -/* Module Parameters */ -static int phys_dma = 1; -module_param(phys_dma, int, 0444); -MODULE_PARM_DESC(phys_dma, "Enable physical DMA (default = 1)."); - -static void dma_trm_tasklet(unsigned long data); -static void dma_trm_reset(struct dma_trm_ctx *d); - -static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, - enum context_type type, int ctx, int num_desc, - int buf_size, int split_buf_size, int context_base); -static void free_dma_rcv_ctx(struct dma_rcv_ctx *d); - -static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, - enum context_type type, int ctx, int num_desc, - int context_base); - -static void ohci1394_pci_remove(struct pci_dev *pdev); - -#ifndef __LITTLE_ENDIAN -static const size_t hdr_sizes[] = { - 3, /* TCODE_WRITEQ */ - 4, /* TCODE_WRITEB */ - 3, /* TCODE_WRITE_RESPONSE */ - 0, /* reserved */ - 3, /* TCODE_READQ */ - 4, /* TCODE_READB */ - 3, /* TCODE_READQ_RESPONSE */ - 4, /* TCODE_READB_RESPONSE */ - 1, /* TCODE_CYCLE_START */ - 4, /* TCODE_LOCK_REQUEST */ - 2, /* TCODE_ISO_DATA */ - 4, /* TCODE_LOCK_RESPONSE */ - /* rest is reserved or link-internal */ -}; - -static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode) -{ - size_t size; - - if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes))) - return; - - size = hdr_sizes[tcode]; - while (size--) - data[size] = le32_to_cpu(data[size]); -} -#else -#define header_le32_to_cpu(w,x) do {} while (0) -#endif /* !LITTLE_ENDIAN */ - -/*********************************** - * IEEE-1394 functionality section * - ***********************************/ - -static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr) -{ - int i; - unsigned long flags; - quadlet_t r; - - spin_lock_irqsave (&ohci->phy_reg_lock, flags); - - reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000) - break; - - mdelay(1); - } - - r = reg_read(ohci, OHCI1394_PhyControl); - - if (i >= OHCI_LOOP_COUNT) - PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]", - r, r & 0x80000000, i); - - spin_unlock_irqrestore (&ohci->phy_reg_lock, flags); - - return (r & 0x00ff0000) >> 16; -} - -static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data) -{ - int i; - unsigned long flags; - u32 r = 0; - - spin_lock_irqsave (&ohci->phy_reg_lock, flags); - - reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - r = reg_read(ohci, OHCI1394_PhyControl); - if (!(r & 0x00004000)) - break; - - mdelay(1); - } - - if (i == OHCI_LOOP_COUNT) - PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]", - r, r & 0x00004000, i); - - spin_unlock_irqrestore (&ohci->phy_reg_lock, flags); - - return; -} - -/* Or's our value into the current value */ -static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data) -{ - u8 old; - - old = get_phy_reg (ohci, addr); - old |= data; - set_phy_reg (ohci, addr, old); - - return; -} - -static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host, - int phyid, int isroot) -{ - quadlet_t *q = ohci->selfid_buf_cpu; - quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount); - size_t size; - quadlet_t q0, q1; - - /* Check status of self-id reception */ - - if (ohci->selfid_swap) - q0 = le32_to_cpu(q[0]); - else - q0 = q[0]; - - if ((self_id_count & 0x80000000) || - ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) { - PRINT(KERN_ERR, - "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)", - self_id_count, q0, ohci->self_id_errors); - - /* Tip by James Goodwin <jamesg@Filanet.com>: - * We had an error, generate another bus reset in response. */ - if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) { - set_phy_reg_mask (ohci, 1, 0x40); - ohci->self_id_errors++; - } else { - PRINT(KERN_ERR, - "Too many errors on SelfID error reception, giving up!"); - } - return; - } - - /* SelfID Ok, reset error counter. */ - ohci->self_id_errors = 0; - - size = ((self_id_count & 0x00001FFC) >> 2) - 1; - q++; - - while (size > 0) { - if (ohci->selfid_swap) { - q0 = le32_to_cpu(q[0]); - q1 = le32_to_cpu(q[1]); - } else { - q0 = q[0]; - q1 = q[1]; - } - - if (q0 == ~q1) { - DBGMSG ("SelfID packet 0x%x received", q0); - hpsb_selfid_received(host, cpu_to_be32(q0)); - if (((q0 & 0x3f000000) >> 24) == phyid) - DBGMSG ("SelfID for this node is 0x%08x", q0); - } else { - PRINT(KERN_ERR, - "SelfID is inconsistent [0x%08x/0x%08x]", q0, q1); - } - q += 2; - size -= 2; - } - - DBGMSG("SelfID complete"); - - return; -} - -static void ohci_soft_reset(struct ti_ohci *ohci) { - int i; - - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset)) - break; - mdelay(1); - } - DBGMSG ("Soft reset finished"); -} - - -/* Generate the dma receive prgs and start the context */ -static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq) -{ - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - int i; - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - for (i=0; i<d->num_desc; i++) { - u32 c; - - c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH; - if (generate_irq) - c |= DMA_CTL_IRQ; - - d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size); - - /* End of descriptor list? */ - if (i + 1 < d->num_desc) { - d->prg_cpu[i]->branchAddress = - cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1); - } else { - d->prg_cpu[i]->branchAddress = - cpu_to_le32((d->prg_bus[0] & 0xfffffff0)); - } - - d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]); - d->prg_cpu[i]->status = cpu_to_le32(d->buf_size); - } - - d->buf_ind = 0; - d->buf_offset = 0; - - if (d->type == DMA_CTX_ISO) { - /* Clear contextControl */ - reg_write(ohci, d->ctrlClear, 0xffffffff); - - /* Set bufferFill, isochHeader, multichannel for IR context */ - reg_write(ohci, d->ctrlSet, 0xd0000000); - - /* Set the context match register to match on all tags */ - reg_write(ohci, d->ctxtMatch, 0xf0000000); - - /* Clear the multi channel mask high and low registers */ - reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff); - reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx); - } - - /* Tell the controller where the first AR program is */ - reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1); - - /* Run context */ - reg_write(ohci, d->ctrlSet, 0x00008000); - - DBGMSG("Receive DMA ctx=%d initialized", d->ctx); -} - -/* Initialize the dma transmit context */ -static void initialize_dma_trm_ctx(struct dma_trm_ctx *d) -{ - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - - /* Stop the context */ - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - d->prg_ind = 0; - d->sent_ind = 0; - d->free_prgs = d->num_desc; - d->branchAddrPtr = NULL; - INIT_LIST_HEAD(&d->fifo_list); - INIT_LIST_HEAD(&d->pending_list); - - if (d->type == DMA_CTX_ISO) { - /* enable interrupts */ - reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx); - } - - DBGMSG("Transmit DMA ctx=%d initialized", d->ctx); -} - -/* Count the number of available iso contexts */ -static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) -{ - u32 tmp; - - reg_write(ohci, reg, 0xffffffff); - tmp = reg_read(ohci, reg); - - DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp); - - /* Count the number of contexts */ - return hweight32(tmp); -} - -/* Global initialization */ -static void ohci_initialize(struct ti_ohci *ohci) -{ - quadlet_t buf; - int num_ports, i; - - spin_lock_init(&ohci->phy_reg_lock); - - /* Put some defaults to these undefined bus options */ - buf = reg_read(ohci, OHCI1394_BusOptions); - buf |= 0x60000000; /* Enable CMC and ISC */ - if (hpsb_disable_irm) - buf &= ~0x80000000; - else - buf |= 0x80000000; /* Enable IRMC */ - buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */ - buf &= ~0x18000000; /* Disable PMC and BMC */ - reg_write(ohci, OHCI1394_BusOptions, buf); - - /* Set the bus number */ - reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); - - /* Enable posted writes */ - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable); - - /* Clear link control register */ - reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); - - /* Enable cycle timer and cycle master and set the IRM - * contender bit in our self ID packets if appropriate. */ - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_CycleTimerEnable | - OHCI1394_LinkControl_CycleMaster); - i = get_phy_reg(ohci, 4) | PHY_04_LCTRL; - if (hpsb_disable_irm) - i &= ~PHY_04_CONTENDER; - else - i |= PHY_04_CONTENDER; - set_phy_reg(ohci, 4, i); - - /* Set up self-id dma buffer */ - reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); - - /* enable self-id */ - reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID); - - /* Set the Config ROM mapping register */ - reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); - - /* Now get our max packet size */ - ohci->max_packet_size = - 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); - - /* Clear the interrupt mask */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); - - /* Clear the interrupt mask */ - reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); - - /* Initialize AR dma */ - initialize_dma_rcv_ctx(&ohci->ar_req_context, 0); - initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0); - - /* Initialize AT dma */ - initialize_dma_trm_ctx(&ohci->at_req_context); - initialize_dma_trm_ctx(&ohci->at_resp_context); - - /* Accept AR requests from all nodes */ - reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); - - /* Set the address range of the physical response unit. - * Most controllers do not implement it as a writable register though. - * They will keep a hardwired offset of 0x00010000 and show 0x0 as - * register content. - * To actually enable physical responses is the job of our interrupt - * handler which programs the physical request filter. */ - reg_write(ohci, OHCI1394_PhyUpperBound, - OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED >> 16); - - DBGMSG("physUpperBoundOffset=%08x", - reg_read(ohci, OHCI1394_PhyUpperBound)); - - /* Specify AT retries */ - reg_write(ohci, OHCI1394_ATRetries, - OHCI1394_MAX_AT_REQ_RETRIES | - (OHCI1394_MAX_AT_RESP_RETRIES<<4) | - (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); - - /* We don't want hardware swapping */ - reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap); - - /* Enable interrupts */ - reg_write(ohci, OHCI1394_IntMaskSet, - OHCI1394_unrecoverableError | - OHCI1394_masterIntEnable | - OHCI1394_busReset | - OHCI1394_selfIDComplete | - OHCI1394_RSPkt | - OHCI1394_RQPkt | - OHCI1394_respTxComplete | - OHCI1394_reqTxComplete | - OHCI1394_isochRx | - OHCI1394_isochTx | - OHCI1394_postedWriteErr | - OHCI1394_cycleTooLong | - OHCI1394_cycleInconsistent); - - /* Enable link */ - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable); - - buf = reg_read(ohci, OHCI1394_Version); - PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%d] " - "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]", - ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10), - ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq, - (unsigned long long)pci_resource_start(ohci->dev, 0), - (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1, - ohci->max_packet_size, - ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx); - - /* Check all of our ports to make sure that if anything is - * connected, we enable that port. */ - num_ports = get_phy_reg(ohci, 2) & 0xf; - for (i = 0; i < num_ports; i++) { - unsigned int status; - - set_phy_reg(ohci, 7, i); - status = get_phy_reg(ohci, 8); - - if (status & 0x20) - set_phy_reg(ohci, 8, status & ~1); - } - - /* Serial EEPROM Sanity check. */ - if ((ohci->max_packet_size < 512) || - (ohci->max_packet_size > 4096)) { - /* Serial EEPROM contents are suspect, set a sane max packet - * size and print the raw contents for bug reports if verbose - * debug is enabled. */ -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - int i; -#endif - - PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, " - "attempting to set max_packet_size to 512 bytes"); - reg_write(ohci, OHCI1394_BusOptions, - (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002); - ohci->max_packet_size = 512; -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - PRINT(KERN_DEBUG, " EEPROM Present: %d", - (reg_read(ohci, OHCI1394_Version) >> 24) & 0x1); - reg_write(ohci, OHCI1394_GUID_ROM, 0x80000000); - - for (i = 0; - ((i < 1000) && - (reg_read(ohci, OHCI1394_GUID_ROM) & 0x80000000)); i++) - udelay(10); - - for (i = 0; i < 0x20; i++) { - reg_write(ohci, OHCI1394_GUID_ROM, 0x02000000); - PRINT(KERN_DEBUG, " EEPROM %02x: %02x", i, - (reg_read(ohci, OHCI1394_GUID_ROM) >> 16) & 0xff); - } -#endif - } -} - -/* - * Insert a packet in the DMA fifo and generate the DMA prg - * FIXME: rewrite the program in order to accept packets crossing - * page boundaries. - * check also that a single dma descriptor doesn't cross a - * page boundary. - */ -static void insert_packet(struct ti_ohci *ohci, - struct dma_trm_ctx *d, struct hpsb_packet *packet) -{ - u32 cycleTimer; - int idx = d->prg_ind; - - DBGMSG("Inserting packet for node " NODE_BUS_FMT - ", tlabel=%d, tcode=0x%x, speed=%d", - NODE_BUS_ARGS(ohci->host, packet->node_id), packet->tlabel, - packet->tcode, packet->speed_code); - - d->prg_cpu[idx]->begin.address = 0; - d->prg_cpu[idx]->begin.branchAddress = 0; - - if (d->type == DMA_CTX_ASYNC_RESP) { - /* - * For response packets, we need to put a timeout value in - * the 16 lower bits of the status... let's try 1 sec timeout - */ - cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - d->prg_cpu[idx]->begin.status = cpu_to_le32( - (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) | - ((cycleTimer&0x01fff000)>>12)); - - DBGMSG("cycleTimer: %08x timeStamp: %08x", - cycleTimer, d->prg_cpu[idx]->begin.status); - } else - d->prg_cpu[idx]->begin.status = 0; - - if ( (packet->type == hpsb_async) || (packet->type == hpsb_raw) ) { - - if (packet->type == hpsb_raw) { - d->prg_cpu[idx]->data[0] = cpu_to_le32(OHCI1394_TCODE_PHY<<4); - d->prg_cpu[idx]->data[1] = cpu_to_le32(packet->header[0]); - d->prg_cpu[idx]->data[2] = cpu_to_le32(packet->header[1]); - } else { - d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | - (packet->header[0] & 0xFFFF); - - if (packet->tcode == TCODE_ISO_DATA) { - /* Sending an async stream packet */ - d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; - } else { - /* Sending a normal async request or response */ - d->prg_cpu[idx]->data[1] = - (packet->header[1] & 0xFFFF) | - (packet->header[0] & 0xFFFF0000); - d->prg_cpu[idx]->data[2] = packet->header[2]; - d->prg_cpu[idx]->data[3] = packet->header[3]; - } - header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); - } - - if (packet->data_size) { /* block transmit */ - if (packet->tcode == TCODE_STREAM_DATA){ - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 0x8); - } else { - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 0x10); - } - d->prg_cpu[idx]->end.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - packet->data_size); - /* - * Check that the packet data buffer - * does not cross a page boundary. - * - * XXX Fix this some day. eth1394 seems to trigger - * it, but ignoring it doesn't seem to cause a - * problem. - */ -#if 0 - if (cross_bound((unsigned long)packet->data, - packet->data_size)>0) { - /* FIXME: do something about it */ - PRINT(KERN_ERR, - "%s: packet data addr: %p size %Zd bytes " - "cross page boundary", __func__, - packet->data, packet->data_size); - } -#endif - d->prg_cpu[idx]->end.address = cpu_to_le32( - pci_map_single(ohci->dev, packet->data, - packet->data_size, - PCI_DMA_TODEVICE)); - - d->prg_cpu[idx]->end.branchAddress = 0; - d->prg_cpu[idx]->end.status = 0; - if (d->branchAddrPtr) - *(d->branchAddrPtr) = - cpu_to_le32(d->prg_bus[idx] | 0x3); - d->branchAddrPtr = - &(d->prg_cpu[idx]->end.branchAddress); - } else { /* quadlet transmit */ - if (packet->type == hpsb_raw) - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_IMMEDIATE | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - (packet->header_size + 4)); - else - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_IMMEDIATE | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - packet->header_size); - - if (d->branchAddrPtr) - *(d->branchAddrPtr) = - cpu_to_le32(d->prg_bus[idx] | 0x2); - d->branchAddrPtr = - &(d->prg_cpu[idx]->begin.branchAddress); - } - - } else { /* iso packet */ - d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | - (packet->header[0] & 0xFFFF); - d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; - header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); - - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 0x8); - d->prg_cpu[idx]->end.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_UPDATE | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - packet->data_size); - d->prg_cpu[idx]->end.address = cpu_to_le32( - pci_map_single(ohci->dev, packet->data, - packet->data_size, PCI_DMA_TODEVICE)); - - d->prg_cpu[idx]->end.branchAddress = 0; - d->prg_cpu[idx]->end.status = 0; - DBGMSG("Iso xmit context info: header[%08x %08x]\n" - " begin=%08x %08x %08x %08x\n" - " %08x %08x %08x %08x\n" - " end =%08x %08x %08x %08x", - d->prg_cpu[idx]->data[0], d->prg_cpu[idx]->data[1], - d->prg_cpu[idx]->begin.control, - d->prg_cpu[idx]->begin.address, - d->prg_cpu[idx]->begin.branchAddress, - d->prg_cpu[idx]->begin.status, - d->prg_cpu[idx]->data[0], - d->prg_cpu[idx]->data[1], - d->prg_cpu[idx]->data[2], - d->prg_cpu[idx]->data[3], - d->prg_cpu[idx]->end.control, - d->prg_cpu[idx]->end.address, - d->prg_cpu[idx]->end.branchAddress, - d->prg_cpu[idx]->end.status); - if (d->branchAddrPtr) - *(d->branchAddrPtr) = cpu_to_le32(d->prg_bus[idx] | 0x3); - d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress); - } - d->free_prgs--; - - /* queue the packet in the appropriate context queue */ - list_add_tail(&packet->driver_list, &d->fifo_list); - d->prg_ind = (d->prg_ind + 1) % d->num_desc; -} - -/* - * This function fills the FIFO with the (eventual) pending packets - * and runs or wakes up the DMA prg if necessary. - * - * The function MUST be called with the d->lock held. - */ -static void dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d) -{ - struct hpsb_packet *packet, *ptmp; - int idx = d->prg_ind; - int z = 0; - - /* insert the packets into the dma fifo */ - list_for_each_entry_safe(packet, ptmp, &d->pending_list, driver_list) { - if (!d->free_prgs) - break; - - /* For the first packet only */ - if (!z) - z = (packet->data_size) ? 3 : 2; - - /* Insert the packet */ - list_del_init(&packet->driver_list); - insert_packet(ohci, d, packet); - } - - /* Nothing must have been done, either no free_prgs or no packets */ - if (z == 0) - return; - - /* Is the context running ? (should be unless it is - the first packet to be sent in this context) */ - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { - u32 nodeId = reg_read(ohci, OHCI1394_NodeID); - - DBGMSG("Starting transmit DMA ctx=%d",d->ctx); - reg_write(ohci, d->cmdPtr, d->prg_bus[idx] | z); - - /* Check that the node id is valid, and not 63 */ - if (!(nodeId & 0x80000000) || (nodeId & 0x3f) == 63) - PRINT(KERN_ERR, "Running dma failed because Node ID is not valid"); - else - reg_write(ohci, d->ctrlSet, 0x8000); - } else { - /* Wake up the dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) - DBGMSG("Waking transmit DMA ctx=%d",d->ctx); - - /* do this always, to avoid race condition */ - reg_write(ohci, d->ctrlSet, 0x1000); - } - - return; -} - -/* Transmission of an async or iso packet */ -static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) -{ - struct ti_ohci *ohci = host->hostdata; - struct dma_trm_ctx *d; - unsigned long flags; - - if (packet->data_size > ohci->max_packet_size) { - PRINT(KERN_ERR, - "Transmit packet size %Zd is too big", - packet->data_size); - return -EOVERFLOW; - } - - if (packet->type == hpsb_raw) - d = &ohci->at_req_context; - else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA)) - d = &ohci->at_resp_context; - else - d = &ohci->at_req_context; - - spin_lock_irqsave(&d->lock,flags); - - list_add_tail(&packet->driver_list, &d->pending_list); - - dma_trm_flush(ohci, d); - - spin_unlock_irqrestore(&d->lock,flags); - - return 0; -} - -static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) -{ - struct ti_ohci *ohci = host->hostdata; - int retval = 0, phy_reg; - - switch (cmd) { - case RESET_BUS: - switch (arg) { - case SHORT_RESET: - phy_reg = get_phy_reg(ohci, 5); - phy_reg |= 0x40; - set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ - break; - case LONG_RESET: - phy_reg = get_phy_reg(ohci, 1); - phy_reg |= 0x40; - set_phy_reg(ohci, 1, phy_reg); /* set IBR */ - break; - case SHORT_RESET_NO_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - if (phy_reg & 0x80) { - phy_reg &= ~0x80; - set_phy_reg(ohci, 1, phy_reg); /* clear RHB */ - } - - phy_reg = get_phy_reg(ohci, 5); - phy_reg |= 0x40; - set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ - break; - case LONG_RESET_NO_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - phy_reg &= ~0x80; - phy_reg |= 0x40; - set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */ - break; - case SHORT_RESET_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - if (!(phy_reg & 0x80)) { - phy_reg |= 0x80; - set_phy_reg(ohci, 1, phy_reg); /* set RHB */ - } - - phy_reg = get_phy_reg(ohci, 5); - phy_reg |= 0x40; - set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ - break; - case LONG_RESET_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - phy_reg |= 0xc0; - set_phy_reg(ohci, 1, phy_reg); /* set RHB and IBR */ - break; - default: - retval = -1; - } - break; - - case GET_CYCLE_COUNTER: - retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - break; - - case SET_CYCLE_COUNTER: - reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg); - break; - - case SET_BUS_ID: - PRINT(KERN_ERR, "devctl command SET_BUS_ID err"); - break; - - case ACT_CYCLE_MASTER: - if (arg) { - /* check if we are root and other nodes are present */ - u32 nodeId = reg_read(ohci, OHCI1394_NodeID); - if ((nodeId & (1<<30)) && (nodeId & 0x3f)) { - /* - * enable cycleTimer, cycleMaster - */ - DBGMSG("Cycle master enabled"); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_CycleTimerEnable | - OHCI1394_LinkControl_CycleMaster); - } - } else { - /* disable cycleTimer, cycleMaster, cycleSource */ - reg_write(ohci, OHCI1394_LinkControlClear, - OHCI1394_LinkControl_CycleTimerEnable | - OHCI1394_LinkControl_CycleMaster | - OHCI1394_LinkControl_CycleSource); - } - break; - - case CANCEL_REQUESTS: - DBGMSG("Cancel request received"); - dma_trm_reset(&ohci->at_req_context); - dma_trm_reset(&ohci->at_resp_context); - break; - - default: - PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet", - cmd); - break; - } - return retval; -} - -/*********************************** - * rawiso ISO reception * - ***********************************/ - -/* - We use either buffer-fill or packet-per-buffer DMA mode. The DMA - buffer is split into "blocks" (regions described by one DMA - descriptor). Each block must be one page or less in size, and - must not cross a page boundary. - - There is one little wrinkle with buffer-fill mode: a packet that - starts in the final block may wrap around into the first block. But - the user API expects all packets to be contiguous. Our solution is - to keep the very last page of the DMA buffer in reserve - if a - packet spans the gap, we copy its tail into this page. -*/ - -struct ohci_iso_recv { - struct ti_ohci *ohci; - - struct ohci1394_iso_tasklet task; - int task_active; - - enum { BUFFER_FILL_MODE = 0, - PACKET_PER_BUFFER_MODE = 1 } dma_mode; - - /* memory and PCI mapping for the DMA descriptors */ - struct dma_prog_region prog; - struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */ - - /* how many DMA blocks fit in the buffer */ - unsigned int nblocks; - - /* stride of DMA blocks */ - unsigned int buf_stride; - - /* number of blocks to batch between interrupts */ - int block_irq_interval; - - /* block that DMA will finish next */ - int block_dma; - - /* (buffer-fill only) block that the reader will release next */ - int block_reader; - - /* (buffer-fill only) bytes of buffer the reader has released, - less than one block */ - int released_bytes; - - /* (buffer-fill only) buffer offset at which the next packet will appear */ - int dma_offset; - - /* OHCI DMA context control registers */ - u32 ContextControlSet; - u32 ContextControlClear; - u32 CommandPtr; - u32 ContextMatch; -}; - -static void ohci_iso_recv_task(unsigned long data); -static void ohci_iso_recv_stop(struct hpsb_iso *iso); -static void ohci_iso_recv_shutdown(struct hpsb_iso *iso); -static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync); -static void ohci_iso_recv_program(struct hpsb_iso *iso); - -static int ohci_iso_recv_init(struct hpsb_iso *iso) -{ - struct ti_ohci *ohci = iso->host->hostdata; - struct ohci_iso_recv *recv; - int ctx; - int ret = -ENOMEM; - - recv = kmalloc(sizeof(*recv), GFP_KERNEL); - if (!recv) - return -ENOMEM; - - iso->hostdata = recv; - recv->ohci = ohci; - recv->task_active = 0; - dma_prog_region_init(&recv->prog); - recv->block = NULL; - - /* use buffer-fill mode, unless irq_interval is 1 - (note: multichannel requires buffer-fill) */ - - if (((iso->irq_interval == 1 && iso->dma_mode == HPSB_ISO_DMA_OLD_ABI) || - iso->dma_mode == HPSB_ISO_DMA_PACKET_PER_BUFFER) && iso->channel != -1) { - recv->dma_mode = PACKET_PER_BUFFER_MODE; - } else { - recv->dma_mode = BUFFER_FILL_MODE; - } - - /* set nblocks, buf_stride, block_irq_interval */ - - if (recv->dma_mode == BUFFER_FILL_MODE) { - recv->buf_stride = PAGE_SIZE; - - /* one block per page of data in the DMA buffer, minus the final guard page */ - recv->nblocks = iso->buf_size/PAGE_SIZE - 1; - if (recv->nblocks < 3) { - DBGMSG("ohci_iso_recv_init: DMA buffer too small"); - goto err; - } - - /* iso->irq_interval is in packets - translate that to blocks */ - if (iso->irq_interval == 1) - recv->block_irq_interval = 1; - else - recv->block_irq_interval = iso->irq_interval * - ((recv->nblocks+1)/iso->buf_packets); - if (recv->block_irq_interval*4 > recv->nblocks) - recv->block_irq_interval = recv->nblocks/4; - if (recv->block_irq_interval < 1) - recv->block_irq_interval = 1; - - } else { - int max_packet_size; - - recv->nblocks = iso->buf_packets; - recv->block_irq_interval = iso->irq_interval; - if (recv->block_irq_interval * 4 > iso->buf_packets) - recv->block_irq_interval = iso->buf_packets / 4; - if (recv->block_irq_interval < 1) - recv->block_irq_interval = 1; - - /* choose a buffer stride */ - /* must be a power of 2, and <= PAGE_SIZE */ - - max_packet_size = iso->buf_size / iso->buf_packets; - - for (recv->buf_stride = 8; recv->buf_stride < max_packet_size; - recv->buf_stride *= 2); - - if (recv->buf_stride*iso->buf_packets > iso->buf_size || - recv->buf_stride > PAGE_SIZE) { - /* this shouldn't happen, but anyway... */ - DBGMSG("ohci_iso_recv_init: problem choosing a buffer stride"); - goto err; - } - } - - recv->block_reader = 0; - recv->released_bytes = 0; - recv->block_dma = 0; - recv->dma_offset = 0; - - /* size of DMA program = one descriptor per block */ - if (dma_prog_region_alloc(&recv->prog, - sizeof(struct dma_cmd) * recv->nblocks, - recv->ohci->dev)) - goto err; - - recv->block = (struct dma_cmd*) recv->prog.kvirt; - - ohci1394_init_iso_tasklet(&recv->task, - iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE : - OHCI_ISO_RECEIVE, - ohci_iso_recv_task, (unsigned long) iso); - - if (ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) { - ret = -EBUSY; - goto err; - } - - recv->task_active = 1; - - /* recv context registers are spaced 32 bytes apart */ - ctx = recv->task.context; - recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx; - recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx; - recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx; - recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx; - - if (iso->channel == -1) { - /* clear multi-channel selection mask */ - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF); - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF); - } - - /* write the DMA program */ - ohci_iso_recv_program(iso); - - DBGMSG("ohci_iso_recv_init: %s mode, DMA buffer is %lu pages" - " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d", - recv->dma_mode == BUFFER_FILL_MODE ? - "buffer-fill" : "packet-per-buffer", - iso->buf_size/PAGE_SIZE, iso->buf_size, - recv->nblocks, recv->buf_stride, recv->block_irq_interval); - - return 0; - -err: - ohci_iso_recv_shutdown(iso); - return ret; -} - -static void ohci_iso_recv_stop(struct hpsb_iso *iso) -{ - struct ohci_iso_recv *recv = iso->hostdata; - - /* disable interrupts */ - reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context); - - /* halt DMA */ - ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL); -} - -static void ohci_iso_recv_shutdown(struct hpsb_iso *iso) -{ - struct ohci_iso_recv *recv = iso->hostdata; - - if (recv->task_active) { - ohci_iso_recv_stop(iso); - ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task); - recv->task_active = 0; - } - - dma_prog_region_free(&recv->prog); - kfree(recv); - iso->hostdata = NULL; -} - -/* set up a "gapped" ring buffer DMA program */ -static void ohci_iso_recv_program(struct hpsb_iso *iso) -{ - struct ohci_iso_recv *recv = iso->hostdata; - int blk; - - /* address of 'branch' field in previous DMA descriptor */ - u32 *prev_branch = NULL; - - for (blk = 0; blk < recv->nblocks; blk++) { - u32 control; - - /* the DMA descriptor */ - struct dma_cmd *cmd = &recv->block[blk]; - - /* offset of the DMA descriptor relative to the DMA prog buffer */ - unsigned long prog_offset = blk * sizeof(struct dma_cmd); - - /* offset of this packet's data within the DMA buffer */ - unsigned long buf_offset = blk * recv->buf_stride; - - if (recv->dma_mode == BUFFER_FILL_MODE) { - control = 2 << 28; /* INPUT_MORE */ - } else { - control = 3 << 28; /* INPUT_LAST */ - } - - control |= 8 << 24; /* s = 1, update xferStatus and resCount */ - - /* interrupt on last block, and at intervals */ - if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) { - control |= 3 << 20; /* want interrupt */ - } - - control |= 3 << 18; /* enable branch to address */ - control |= recv->buf_stride; - - cmd->control = cpu_to_le32(control); - cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset)); - cmd->branchAddress = 0; /* filled in on next loop */ - cmd->status = cpu_to_le32(recv->buf_stride); - - /* link the previous descriptor to this one */ - if (prev_branch) { - *prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1); - } - - prev_branch = &cmd->branchAddress; - } - - /* the final descriptor's branch address and Z should be left at 0 */ -} - -/* listen or unlisten to a specific channel (multi-channel mode only) */ -static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen) -{ - struct ohci_iso_recv *recv = iso->hostdata; - int reg, i; - - if (channel < 32) { - reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear; - i = channel; - } else { - reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear; - i = channel - 32; - } - - reg_write(recv->ohci, reg, (1 << i)); - - /* issue a dummy read to force all PCI writes to be posted immediately */ - mb(); - reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); -} - -static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) -{ - struct ohci_iso_recv *recv = iso->hostdata; - int i; - - for (i = 0; i < 64; i++) { - if (mask & (1ULL << i)) { - if (i < 32) - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i)); - else - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32))); - } else { - if (i < 32) - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i)); - else - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32))); - } - } - - /* issue a dummy read to force all PCI writes to be posted immediately */ - mb(); - reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); -} - -static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) -{ - struct ohci_iso_recv *recv = iso->hostdata; - struct ti_ohci *ohci = recv->ohci; - u32 command, contextMatch; - - reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF); - wmb(); - - /* always keep ISO headers */ - command = (1 << 30); - - if (recv->dma_mode == BUFFER_FILL_MODE) - command |= (1 << 31); - - reg_write(recv->ohci, recv->ContextControlSet, command); - - /* match on specified tags */ - contextMatch = tag_mask << 28; - - if (iso->channel == -1) { - /* enable multichannel reception */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 28)); - } else { - /* listen on channel */ - contextMatch |= iso->channel; - } - - if (cycle != -1) { - u32 seconds; - - /* enable cycleMatch */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 29)); - - /* set starting cycle */ - cycle &= 0x1FFF; - - /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - - just snarf them from the current time */ - seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25; - - /* advance one second to give some extra time for DMA to start */ - seconds += 1; - - cycle |= (seconds & 3) << 13; - - contextMatch |= cycle << 12; - } - - if (sync != -1) { - /* set sync flag on first DMA descriptor */ - struct dma_cmd *cmd = &recv->block[recv->block_dma]; - cmd->control |= cpu_to_le32(DMA_CTL_WAIT); - - /* match sync field */ - contextMatch |= (sync&0xf)<<8; - } - - reg_write(recv->ohci, recv->ContextMatch, contextMatch); - - /* address of first descriptor block */ - command = dma_prog_region_offset_to_bus(&recv->prog, - recv->block_dma * sizeof(struct dma_cmd)); - command |= 1; /* Z=1 */ - - reg_write(recv->ohci, recv->CommandPtr, command); - - /* enable interrupts */ - reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context); - - wmb(); - - /* run */ - reg_write(recv->ohci, recv->ContextControlSet, 0x8000); - - /* issue a dummy read of the cycle timer register to force - all PCI writes to be posted immediately */ - mb(); - reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); - - /* check RUN */ - if (!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) { - PRINT(KERN_ERR, - "Error starting IR DMA (ContextControl 0x%08x)\n", - reg_read(recv->ohci, recv->ContextControlSet)); - return -1; - } - - return 0; -} - -static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block) -{ - /* re-use the DMA descriptor for the block */ - /* by linking the previous descriptor to it */ - - int next_i = block; - int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1); - - struct dma_cmd *next = &recv->block[next_i]; - struct dma_cmd *prev = &recv->block[prev_i]; - - /* ignore out-of-range requests */ - if ((block < 0) || (block > recv->nblocks)) - return; - - /* 'next' becomes the new end of the DMA chain, - so disable branch and enable interrupt */ - next->branchAddress = 0; - next->control |= cpu_to_le32(3 << 20); - next->status = cpu_to_le32(recv->buf_stride); - - /* link prev to next */ - prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, - sizeof(struct dma_cmd) * next_i) - | 1); /* Z=1 */ - - /* disable interrupt on previous DMA descriptor, except at intervals */ - if ((prev_i % recv->block_irq_interval) == 0) { - prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */ - } else { - prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */ - } - wmb(); - - /* wake up DMA in case it fell asleep */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 12)); -} - -static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv, - struct hpsb_iso_packet_info *info) -{ - /* release the memory where the packet was */ - recv->released_bytes += info->total_len; - - /* have we released enough memory for one block? */ - while (recv->released_bytes > recv->buf_stride) { - ohci_iso_recv_release_block(recv, recv->block_reader); - recv->block_reader = (recv->block_reader + 1) % recv->nblocks; - recv->released_bytes -= recv->buf_stride; - } -} - -static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info) -{ - struct ohci_iso_recv *recv = iso->hostdata; - if (recv->dma_mode == BUFFER_FILL_MODE) { - ohci_iso_recv_bufferfill_release(recv, info); - } else { - ohci_iso_recv_release_block(recv, info - iso->infos); - } -} - -/* parse all packets from blocks that have been fully received */ -static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv) -{ - int wake = 0; - int runaway = 0; - struct ti_ohci *ohci = recv->ohci; - - while (1) { - /* we expect the next parsable packet to begin at recv->dma_offset */ - /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */ - - unsigned int offset; - unsigned short len, cycle, total_len; - unsigned char channel, tag, sy; - - unsigned char *p = iso->data_buf.kvirt; - - unsigned int this_block = recv->dma_offset/recv->buf_stride; - - /* don't loop indefinitely */ - if (runaway++ > 100000) { - atomic_inc(&iso->overflows); - PRINT(KERN_ERR, - "IR DMA error - Runaway during buffer parsing!\n"); - break; - } - - /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */ - if (this_block == recv->block_dma) - break; - - wake = 1; - - /* parse data length, tag, channel, and sy */ - - /* note: we keep our own local copies of 'len' and 'offset' - so the user can't mess with them by poking in the mmap area */ - - len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8); - - if (len > 4096) { - PRINT(KERN_ERR, - "IR DMA error - bogus 'len' value %u\n", len); - } - - channel = p[recv->dma_offset+1] & 0x3F; - tag = p[recv->dma_offset+1] >> 6; - sy = p[recv->dma_offset+0] & 0xF; - - /* advance to data payload */ - recv->dma_offset += 4; - - /* check for wrap-around */ - if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { - recv->dma_offset -= recv->buf_stride*recv->nblocks; - } - - /* dma_offset now points to the first byte of the data payload */ - offset = recv->dma_offset; - - /* advance to xferStatus/timeStamp */ - recv->dma_offset += len; - - total_len = len + 8; /* 8 bytes header+trailer in OHCI packet */ - /* payload is padded to 4 bytes */ - if (len % 4) { - recv->dma_offset += 4 - (len%4); - total_len += 4 - (len%4); - } - - /* check for wrap-around */ - if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { - /* uh oh, the packet data wraps from the last - to the first DMA block - make the packet - contiguous by copying its "tail" into the - guard page */ - - int guard_off = recv->buf_stride*recv->nblocks; - int tail_len = len - (guard_off - offset); - - if (tail_len > 0 && tail_len < recv->buf_stride) { - memcpy(iso->data_buf.kvirt + guard_off, - iso->data_buf.kvirt, - tail_len); - } - - recv->dma_offset -= recv->buf_stride*recv->nblocks; - } - - /* parse timestamp */ - cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8); - cycle &= 0x1FFF; - - /* advance to next packet */ - recv->dma_offset += 4; - - /* check for wrap-around */ - if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { - recv->dma_offset -= recv->buf_stride*recv->nblocks; - } - - hpsb_iso_packet_received(iso, offset, len, total_len, cycle, channel, tag, sy); - } - - if (wake) - hpsb_iso_wake(iso); -} - -static void ohci_iso_recv_bufferfill_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv) -{ - int loop; - struct ti_ohci *ohci = recv->ohci; - - /* loop over all blocks */ - for (loop = 0; loop < recv->nblocks; loop++) { - - /* check block_dma to see if it's done */ - struct dma_cmd *im = &recv->block[recv->block_dma]; - - /* check the DMA descriptor for new writes to xferStatus */ - u16 xferstatus = le32_to_cpu(im->status) >> 16; - - /* rescount is the number of bytes *remaining to be written* in the block */ - u16 rescount = le32_to_cpu(im->status) & 0xFFFF; - - unsigned char event = xferstatus & 0x1F; - - if (!event) { - /* nothing has happened to this block yet */ - break; - } - - if (event != 0x11) { - atomic_inc(&iso->overflows); - PRINT(KERN_ERR, - "IR DMA error - OHCI error code 0x%02x\n", event); - } - - if (rescount != 0) { - /* the card is still writing to this block; - we can't touch it until it's done */ - break; - } - - /* OK, the block is finished... */ - - /* sync our view of the block */ - dma_region_sync_for_cpu(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride); - - /* reset the DMA descriptor */ - im->status = recv->buf_stride; - - /* advance block_dma */ - recv->block_dma = (recv->block_dma + 1) % recv->nblocks; - - if ((recv->block_dma+1) % recv->nblocks == recv->block_reader) { - atomic_inc(&iso->overflows); - DBGMSG("ISO reception overflow - " - "ran out of DMA blocks"); - } - } - - /* parse any packets that have arrived */ - ohci_iso_recv_bufferfill_parse(iso, recv); -} - -static void ohci_iso_recv_packetperbuf_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv) -{ - int count; - int wake = 0; - struct ti_ohci *ohci = recv->ohci; - - /* loop over the entire buffer */ - for (count = 0; count < recv->nblocks; count++) { - u32 packet_len = 0; - - /* pointer to the DMA descriptor */ - struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma; - - /* check the DMA descriptor for new writes to xferStatus */ - u16 xferstatus = le32_to_cpu(il->status) >> 16; - u16 rescount = le32_to_cpu(il->status) & 0xFFFF; - - unsigned char event = xferstatus & 0x1F; - - if (!event) { - /* this packet hasn't come in yet; we are done for now */ - goto out; - } - - if (event == 0x11) { - /* packet received successfully! */ - - /* rescount is the number of bytes *remaining* in the packet buffer, - after the packet was written */ - packet_len = recv->buf_stride - rescount; - - } else if (event == 0x02) { - PRINT(KERN_ERR, "IR DMA error - packet too long for buffer\n"); - } else if (event) { - PRINT(KERN_ERR, "IR DMA error - OHCI error code 0x%02x\n", event); - } - - /* sync our view of the buffer */ - dma_region_sync_for_cpu(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride); - - /* record the per-packet info */ - { - /* iso header is 8 bytes ahead of the data payload */ - unsigned char *hdr; - - unsigned int offset; - unsigned short cycle; - unsigned char channel, tag, sy; - - offset = iso->pkt_dma * recv->buf_stride; - hdr = iso->data_buf.kvirt + offset; - - /* skip iso header */ - offset += 8; - packet_len -= 8; - - cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF; - channel = hdr[5] & 0x3F; - tag = hdr[5] >> 6; - sy = hdr[4] & 0xF; - - hpsb_iso_packet_received(iso, offset, packet_len, - recv->buf_stride, cycle, channel, tag, sy); - } - - /* reset the DMA descriptor */ - il->status = recv->buf_stride; - - wake = 1; - recv->block_dma = iso->pkt_dma; - } - -out: - if (wake) - hpsb_iso_wake(iso); -} - -static void ohci_iso_recv_task(unsigned long data) -{ - struct hpsb_iso *iso = (struct hpsb_iso*) data; - struct ohci_iso_recv *recv = iso->hostdata; - - if (recv->dma_mode == BUFFER_FILL_MODE) - ohci_iso_recv_bufferfill_task(iso, recv); - else - ohci_iso_recv_packetperbuf_task(iso, recv); -} - -/*********************************** - * rawiso ISO transmission * - ***********************************/ - -struct ohci_iso_xmit { - struct ti_ohci *ohci; - struct dma_prog_region prog; - struct ohci1394_iso_tasklet task; - int task_active; - int last_cycle; - atomic_t skips; - - u32 ContextControlSet; - u32 ContextControlClear; - u32 CommandPtr; -}; - -/* transmission DMA program: - one OUTPUT_MORE_IMMEDIATE for the IT header - one OUTPUT_LAST for the buffer data */ - -struct iso_xmit_cmd { - struct dma_cmd output_more_immediate; - u8 iso_hdr[8]; - u32 unused[2]; - struct dma_cmd output_last; -}; - -static int ohci_iso_xmit_init(struct hpsb_iso *iso); -static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle); -static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso); -static void ohci_iso_xmit_task(unsigned long data); - -static int ohci_iso_xmit_init(struct hpsb_iso *iso) -{ - struct ohci_iso_xmit *xmit; - unsigned int prog_size; - int ctx; - int ret = -ENOMEM; - - xmit = kmalloc(sizeof(*xmit), GFP_KERNEL); - if (!xmit) - return -ENOMEM; - - iso->hostdata = xmit; - xmit->ohci = iso->host->hostdata; - xmit->task_active = 0; - xmit->last_cycle = -1; - atomic_set(&iso->skips, 0); - - dma_prog_region_init(&xmit->prog); - - prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets; - - if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev)) - goto err; - - ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT, - ohci_iso_xmit_task, (unsigned long) iso); - - if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) { - ret = -EBUSY; - goto err; - } - - xmit->task_active = 1; - - /* xmit context registers are spaced 16 bytes apart */ - ctx = xmit->task.context; - xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx; - xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx; - xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx; - - return 0; - -err: - ohci_iso_xmit_shutdown(iso); - return ret; -} - -static void ohci_iso_xmit_stop(struct hpsb_iso *iso) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - - /* disable interrupts */ - reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context); - - /* halt DMA */ - if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) { - /* XXX the DMA context will lock up if you try to send too much data! */ - PRINT(KERN_ERR, - "you probably exceeded the OHCI card's bandwidth limit - " - "reload the module and reduce xmit bandwidth"); - } -} - -static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - - if (xmit->task_active) { - ohci_iso_xmit_stop(iso); - ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task); - xmit->task_active = 0; - } - - dma_prog_region_free(&xmit->prog); - kfree(xmit); - iso->hostdata = NULL; -} - -static void ohci_iso_xmit_task(unsigned long data) -{ - struct hpsb_iso *iso = (struct hpsb_iso*) data; - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - int wake = 0; - int count; - - /* check the whole buffer if necessary, starting at pkt_dma */ - for (count = 0; count < iso->buf_packets; count++) { - int cycle; - - /* DMA descriptor */ - struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma); - - /* check for new writes to xferStatus */ - u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16; - u8 event = xferstatus & 0x1F; - - if (!event) { - /* packet hasn't been sent yet; we are done for now */ - break; - } - - if (event != 0x11) - PRINT(KERN_ERR, - "IT DMA error - OHCI error code 0x%02x\n", event); - - /* at least one packet went out, so wake up the writer */ - wake = 1; - - /* parse cycle */ - cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF; - - if (xmit->last_cycle > -1) { - int cycle_diff = cycle - xmit->last_cycle; - int skip; - - /* unwrap */ - if (cycle_diff < 0) { - cycle_diff += 8000; - if (cycle_diff < 0) - PRINT(KERN_ERR, "bogus cycle diff %d\n", - cycle_diff); - } - - skip = cycle_diff - 1; - if (skip > 0) { - DBGMSG("skipped %d cycles without packet loss", skip); - atomic_add(skip, &iso->skips); - } - } - xmit->last_cycle = cycle; - - /* tell the subsystem the packet has gone out */ - hpsb_iso_packet_sent(iso, cycle, event != 0x11); - - /* reset the DMA descriptor for next time */ - cmd->output_last.status = 0; - } - - if (wake) - hpsb_iso_wake(iso); -} - -static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - - int next_i, prev_i; - struct iso_xmit_cmd *next, *prev; - - unsigned int offset; - unsigned short len; - unsigned char tag, sy; - - /* check that the packet doesn't cross a page boundary - (we could allow this if we added OUTPUT_MORE descriptor support) */ - if (cross_bound(info->offset, info->len)) { - PRINT(KERN_ERR, - "rawiso xmit: packet %u crosses a page boundary", - iso->first_packet); - return -EINVAL; - } - - offset = info->offset; - len = info->len; - tag = info->tag; - sy = info->sy; - - /* sync up the card's view of the buffer */ - dma_region_sync_for_device(&iso->data_buf, offset, len); - - /* append first_packet to the DMA chain */ - /* by linking the previous descriptor to it */ - /* (next will become the new end of the DMA chain) */ - - next_i = iso->first_packet; - prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1); - - next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i); - prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i); - - /* set up the OUTPUT_MORE_IMMEDIATE descriptor */ - memset(next, 0, sizeof(struct iso_xmit_cmd)); - next->output_more_immediate.control = cpu_to_le32(0x02000008); - - /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */ - - /* tcode = 0xA, and sy */ - next->iso_hdr[0] = 0xA0 | (sy & 0xF); - - /* tag and channel number */ - next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F); - - /* transmission speed */ - next->iso_hdr[2] = iso->speed & 0x7; - - /* payload size */ - next->iso_hdr[6] = len & 0xFF; - next->iso_hdr[7] = len >> 8; - - /* set up the OUTPUT_LAST */ - next->output_last.control = cpu_to_le32(1 << 28); - next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */ - next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */ - next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */ - next->output_last.control |= cpu_to_le32(len); - - /* payload bus address */ - next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset)); - - /* leave branchAddress at zero for now */ - - /* re-write the previous DMA descriptor to chain to this one */ - - /* set prev branch address to point to next (Z=3) */ - prev->output_last.branchAddress = cpu_to_le32( - dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3); - - /* - * Link the skip address to this descriptor itself. This causes a - * context to skip a cycle whenever lost cycles or FIFO overruns occur, - * without dropping the data at that point the application should then - * decide whether this is an error condition or not. Some protocols - * can deal with this by dropping some rate-matching padding packets. - */ - next->output_more_immediate.branchAddress = - prev->output_last.branchAddress; - - /* disable interrupt, unless required by the IRQ interval */ - if (prev_i % iso->irq_interval) { - prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */ - } else { - prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */ - } - - wmb(); - - /* wake DMA in case it is sleeping */ - reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12); - - /* issue a dummy read of the cycle timer to force all PCI - writes to be posted immediately */ - mb(); - reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer); - - return 0; -} - -static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - - /* clear out the control register */ - reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF); - wmb(); - - /* address and length of first descriptor block (Z=3) */ - reg_write(xmit->ohci, xmit->CommandPtr, - dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3); - - /* cycle match */ - if (cycle != -1) { - u32 start = cycle & 0x1FFF; - - /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - - just snarf them from the current time */ - u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25; - - /* advance one second to give some extra time for DMA to start */ - seconds += 1; - - start |= (seconds & 3) << 13; - - reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16)); - } - - /* enable interrupts */ - reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context); - - /* run */ - reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000); - mb(); - - /* wait 100 usec to give the card time to go active */ - udelay(100); - - /* check the RUN bit */ - if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) { - PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n", - reg_read(xmit->ohci, xmit->ContextControlSet)); - return -1; - } - - return 0; -} - -static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg) -{ - - switch(cmd) { - case XMIT_INIT: - return ohci_iso_xmit_init(iso); - case XMIT_START: - return ohci_iso_xmit_start(iso, arg); - case XMIT_STOP: - ohci_iso_xmit_stop(iso); - return 0; - case XMIT_QUEUE: - return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg); - case XMIT_SHUTDOWN: - ohci_iso_xmit_shutdown(iso); - return 0; - - case RECV_INIT: - return ohci_iso_recv_init(iso); - case RECV_START: { - int *args = (int*) arg; - return ohci_iso_recv_start(iso, args[0], args[1], args[2]); - } - case RECV_STOP: - ohci_iso_recv_stop(iso); - return 0; - case RECV_RELEASE: - ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg); - return 0; - case RECV_FLUSH: - ohci_iso_recv_task((unsigned long) iso); - return 0; - case RECV_SHUTDOWN: - ohci_iso_recv_shutdown(iso); - return 0; - case RECV_LISTEN_CHANNEL: - ohci_iso_recv_change_channel(iso, arg, 1); - return 0; - case RECV_UNLISTEN_CHANNEL: - ohci_iso_recv_change_channel(iso, arg, 0); - return 0; - case RECV_SET_CHANNEL_MASK: - ohci_iso_recv_set_channel_mask(iso, *((u64*) arg)); - return 0; - - default: - PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet", - cmd); - break; - } - return -EINVAL; -} - -/*************************************** - * IEEE-1394 functionality section END * - ***************************************/ - - -/******************************************************** - * Global stuff (interrupt handler, init/shutdown code) * - ********************************************************/ - -static void dma_trm_reset(struct dma_trm_ctx *d) -{ - unsigned long flags; - LIST_HEAD(packet_list); - struct ti_ohci *ohci = d->ohci; - struct hpsb_packet *packet, *ptmp; - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - /* Lock the context, reset it and release it. Move the packets - * that were pending in the context to packet_list and free - * them after releasing the lock. */ - - spin_lock_irqsave(&d->lock, flags); - - list_splice_init(&d->fifo_list, &packet_list); - list_splice_init(&d->pending_list, &packet_list); - - d->branchAddrPtr = NULL; - d->sent_ind = d->prg_ind; - d->free_prgs = d->num_desc; - - spin_unlock_irqrestore(&d->lock, flags); - - if (list_empty(&packet_list)) - return; - - PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx); - - /* Now process subsystem callbacks for the packets from this - * context. */ - list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) { - list_del_init(&packet->driver_list); - hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED); - } -} - -static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci, - quadlet_t rx_event, - quadlet_t tx_event) -{ - struct ohci1394_iso_tasklet *t; - unsigned long mask; - unsigned long flags; - - spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); - - list_for_each_entry(t, &ohci->iso_tasklet_list, link) { - mask = 1 << t->context; - - if (t->type == OHCI_ISO_TRANSMIT) { - if (tx_event & mask) - tasklet_schedule(&t->tasklet); - } else { - /* OHCI_ISO_RECEIVE or OHCI_ISO_MULTICHANNEL_RECEIVE */ - if (rx_event & mask) - tasklet_schedule(&t->tasklet); - } - } - - spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); -} - -static irqreturn_t ohci_irq_handler(int irq, void *dev_id) -{ - quadlet_t event, node_id; - struct ti_ohci *ohci = (struct ti_ohci *)dev_id; - struct hpsb_host *host = ohci->host; - int phyid = -1, isroot = 0; - unsigned long flags; - - /* Read and clear the interrupt event register. Don't clear - * the busReset event, though. This is done when we get the - * selfIDComplete interrupt. */ - spin_lock_irqsave(&ohci->event_lock, flags); - event = reg_read(ohci, OHCI1394_IntEventClear); - reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset); - spin_unlock_irqrestore(&ohci->event_lock, flags); - - if (!event) - return IRQ_NONE; - - /* If event is ~(u32)0 cardbus card was ejected. In this case - * we just return, and clean up in the ohci1394_pci_remove - * function. */ - if (event == ~(u32) 0) { - DBGMSG("Device removed."); - return IRQ_NONE; - } - - DBGMSG("IntEvent: %08x", event); - - if (event & OHCI1394_unrecoverableError) { - int ctx; - PRINT(KERN_ERR, "Unrecoverable error!"); - - if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Req Tx Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsReqTrContextControlSet), - reg_read(ohci, OHCI1394_AsReqTrCommandPtr)); - - if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Rsp Tx Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsRspTrContextControlSet), - reg_read(ohci, OHCI1394_AsRspTrCommandPtr)); - - if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Req Rcv Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsReqRcvContextControlSet), - reg_read(ohci, OHCI1394_AsReqRcvCommandPtr)); - - if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Rsp Rcv Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsRspRcvContextControlSet), - reg_read(ohci, OHCI1394_AsRspRcvCommandPtr)); - - for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) { - if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800) - PRINT(KERN_ERR, "Iso Xmit %d Context died: " - "ctrl[%08x] cmdptr[%08x]", ctx, - reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)), - reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx))); - } - - for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) { - if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800) - PRINT(KERN_ERR, "Iso Recv %d Context died: " - "ctrl[%08x] cmdptr[%08x] match[%08x]", ctx, - reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)), - reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)), - reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx))); - } - - event &= ~OHCI1394_unrecoverableError; - } - if (event & OHCI1394_postedWriteErr) { - PRINT(KERN_ERR, "physical posted write error"); - /* no recovery strategy yet, had to involve protocol drivers */ - event &= ~OHCI1394_postedWriteErr; - } - if (event & OHCI1394_cycleTooLong) { - if(printk_ratelimit()) - PRINT(KERN_WARNING, "isochronous cycle too long"); - else - DBGMSG("OHCI1394_cycleTooLong"); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_CycleMaster); - event &= ~OHCI1394_cycleTooLong; - } - if (event & OHCI1394_cycleInconsistent) { - /* We subscribe to the cycleInconsistent event only to - * clear the corresponding event bit... otherwise, - * isochronous cycleMatch DMA won't work. */ - DBGMSG("OHCI1394_cycleInconsistent"); - event &= ~OHCI1394_cycleInconsistent; - } - if (event & OHCI1394_busReset) { - /* The busReset event bit can't be cleared during the - * selfID phase, so we disable busReset interrupts, to - * avoid burying the cpu in interrupt requests. */ - spin_lock_irqsave(&ohci->event_lock, flags); - reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset); - - if (ohci->check_busreset) { - int loop_count = 0; - - udelay(10); - - while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) { - reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); - - spin_unlock_irqrestore(&ohci->event_lock, flags); - udelay(10); - spin_lock_irqsave(&ohci->event_lock, flags); - - /* The loop counter check is to prevent the driver - * from remaining in this state forever. For the - * initial bus reset, the loop continues for ever - * and the system hangs, until some device is plugged-in - * or out manually into a port! The forced reset seems - * to solve this problem. This mainly effects nForce2. */ - if (loop_count > 10000) { - ohci_devctl(host, RESET_BUS, LONG_RESET); - DBGMSG("Detected bus-reset loop. Forced a bus reset!"); - loop_count = 0; - } - - loop_count++; - } - } - spin_unlock_irqrestore(&ohci->event_lock, flags); - if (!host->in_bus_reset) { - DBGMSG("irq_handler: Bus reset requested"); - - /* Subsystem call */ - hpsb_bus_reset(ohci->host); - } - event &= ~OHCI1394_busReset; - } - if (event & OHCI1394_reqTxComplete) { - struct dma_trm_ctx *d = &ohci->at_req_context; - DBGMSG("Got reqTxComplete interrupt " - "status=0x%08X", reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, - "reqTxComplete"); - else - dma_trm_tasklet((unsigned long)d); - //tasklet_schedule(&d->task); - event &= ~OHCI1394_reqTxComplete; - } - if (event & OHCI1394_respTxComplete) { - struct dma_trm_ctx *d = &ohci->at_resp_context; - DBGMSG("Got respTxComplete interrupt " - "status=0x%08X", reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, - "respTxComplete"); - else - tasklet_schedule(&d->task); - event &= ~OHCI1394_respTxComplete; - } - if (event & OHCI1394_RQPkt) { - struct dma_rcv_ctx *d = &ohci->ar_req_context; - DBGMSG("Got RQPkt interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); - else - tasklet_schedule(&d->task); - event &= ~OHCI1394_RQPkt; - } - if (event & OHCI1394_RSPkt) { - struct dma_rcv_ctx *d = &ohci->ar_resp_context; - DBGMSG("Got RSPkt interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); - else - tasklet_schedule(&d->task); - event &= ~OHCI1394_RSPkt; - } - if (event & OHCI1394_isochRx) { - quadlet_t rx_event; - - rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event); - ohci_schedule_iso_tasklets(ohci, rx_event, 0); - event &= ~OHCI1394_isochRx; - } - if (event & OHCI1394_isochTx) { - quadlet_t tx_event; - - tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event); - ohci_schedule_iso_tasklets(ohci, 0, tx_event); - event &= ~OHCI1394_isochTx; - } - if (event & OHCI1394_selfIDComplete) { - if (host->in_bus_reset) { - node_id = reg_read(ohci, OHCI1394_NodeID); - - if (!(node_id & 0x80000000)) { - PRINT(KERN_ERR, - "SelfID received, but NodeID invalid " - "(probably new bus reset occurred): %08X", - node_id); - goto selfid_not_valid; - } - - phyid = node_id & 0x0000003f; - isroot = (node_id & 0x40000000) != 0; - - DBGMSG("SelfID interrupt received " - "(phyid %d, %s)", phyid, - (isroot ? "root" : "not root")); - - handle_selfid(ohci, host, phyid, isroot); - - /* Clear the bus reset event and re-enable the - * busReset interrupt. */ - spin_lock_irqsave(&ohci->event_lock, flags); - reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); - reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); - spin_unlock_irqrestore(&ohci->event_lock, flags); - - /* Turn on phys dma reception. - * - * TODO: Enable some sort of filtering management. - */ - if (phys_dma) { - reg_write(ohci, OHCI1394_PhyReqFilterHiSet, - 0xffffffff); - reg_write(ohci, OHCI1394_PhyReqFilterLoSet, - 0xffffffff); - } - - DBGMSG("PhyReqFilter=%08x%08x", - reg_read(ohci, OHCI1394_PhyReqFilterHiSet), - reg_read(ohci, OHCI1394_PhyReqFilterLoSet)); - - hpsb_selfid_complete(host, phyid, isroot); - } else - PRINT(KERN_ERR, - "SelfID received outside of bus reset sequence"); - -selfid_not_valid: - event &= ~OHCI1394_selfIDComplete; - } - - /* Make sure we handle everything, just in case we accidentally - * enabled an interrupt that we didn't write a handler for. */ - if (event) - PRINT(KERN_ERR, "Unhandled interrupt(s) 0x%08x", - event); - - return IRQ_HANDLED; -} - -/* Put the buffer back into the dma context */ -static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx) -{ - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - DBGMSG("Inserting dma buf ctx=%d idx=%d", d->ctx, idx); - - d->prg_cpu[idx]->status = cpu_to_le32(d->buf_size); - d->prg_cpu[idx]->branchAddress &= le32_to_cpu(0xfffffff0); - idx = (idx + d->num_desc - 1 ) % d->num_desc; - d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001); - - /* To avoid a race, ensure 1394 interface hardware sees the inserted - * context program descriptors before it sees the wakeup bit set. */ - wmb(); - - /* wake up the dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - PRINT(KERN_INFO, - "Waking dma ctx=%d ... processing is probably too slow", - d->ctx); - } - - /* do this always, to avoid race condition */ - reg_write(ohci, d->ctrlSet, 0x1000); -} - -#define cond_le32_to_cpu(data, noswap) \ - (noswap ? data : le32_to_cpu(data)) - -static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0, - -1, 0, -1, 0, -1, -1, 16, -1}; - -/* - * Determine the length of a packet in the buffer - * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca> - */ -static inline int packet_length(struct dma_rcv_ctx *d, int idx, - quadlet_t *buf_ptr, int offset, - unsigned char tcode, int noswap) -{ - int length = -1; - - if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) { - length = TCODE_SIZE[tcode]; - if (length == 0) { - if (offset + 12 >= d->buf_size) { - length = (cond_le32_to_cpu(d->buf_cpu[(idx + 1) % d->num_desc] - [3 - ((d->buf_size - offset) >> 2)], noswap) >> 16); - } else { - length = (cond_le32_to_cpu(buf_ptr[3], noswap) >> 16); - } - length += 20; - } - } else if (d->type == DMA_CTX_ISO) { - /* Assumption: buffer fill mode with header/trailer */ - length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8; - } - - if (length > 0 && length % 4) - length += 4 - (length % 4); - - return length; -} - -/* Tasklet that processes dma receive buffers */ -static void dma_rcv_tasklet (unsigned long data) -{ - struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data; - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - unsigned int split_left, idx, offset, rescount; - unsigned char tcode; - int length, bytes_left, ack; - unsigned long flags; - quadlet_t *buf_ptr; - char *split_ptr; - char msg[256]; - - spin_lock_irqsave(&d->lock, flags); - - idx = d->buf_ind; - offset = d->buf_offset; - buf_ptr = d->buf_cpu[idx] + offset/4; - - rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; - bytes_left = d->buf_size - rescount - offset; - - while (bytes_left > 0) { - tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf; - - /* packet_length() will return < 4 for an error */ - length = packet_length(d, idx, buf_ptr, offset, tcode, ohci->no_swap_incoming); - - if (length < 4) { /* something is wrong */ - sprintf(msg,"Unexpected tcode 0x%x(0x%08x) in AR ctx=%d, length=%d", - tcode, cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming), - d->ctx, length); - ohci1394_stop_context(ohci, d->ctrlClear, msg); - spin_unlock_irqrestore(&d->lock, flags); - return; - } - - /* The first case is where we have a packet that crosses - * over more than one descriptor. The next case is where - * it's all in the first descriptor. */ - if ((offset + length) > d->buf_size) { - DBGMSG("Split packet rcv'd"); - if (length > d->split_buf_size) { - ohci1394_stop_context(ohci, d->ctrlClear, - "Split packet size exceeded"); - d->buf_ind = idx; - d->buf_offset = offset; - spin_unlock_irqrestore(&d->lock, flags); - return; - } - - if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status) - == d->buf_size) { - /* Other part of packet not written yet. - * this should never happen I think - * anyway we'll get it on the next call. */ - PRINT(KERN_INFO, - "Got only half a packet!"); - d->buf_ind = idx; - d->buf_offset = offset; - spin_unlock_irqrestore(&d->lock, flags); - return; - } - - split_left = length; - split_ptr = (char *)d->spb; - memcpy(split_ptr,buf_ptr,d->buf_size-offset); - split_left -= d->buf_size-offset; - split_ptr += d->buf_size-offset; - insert_dma_buffer(d, idx); - idx = (idx+1) % d->num_desc; - buf_ptr = d->buf_cpu[idx]; - offset=0; - - while (split_left >= d->buf_size) { - memcpy(split_ptr,buf_ptr,d->buf_size); - split_ptr += d->buf_size; - split_left -= d->buf_size; - insert_dma_buffer(d, idx); - idx = (idx+1) % d->num_desc; - buf_ptr = d->buf_cpu[idx]; - } - - if (split_left > 0) { - memcpy(split_ptr, buf_ptr, split_left); - offset = split_left; - buf_ptr += offset/4; - } - } else { - DBGMSG("Single packet rcv'd"); - memcpy(d->spb, buf_ptr, length); - offset += length; - buf_ptr += length/4; - if (offset==d->buf_size) { - insert_dma_buffer(d, idx); - idx = (idx+1) % d->num_desc; - buf_ptr = d->buf_cpu[idx]; - offset=0; - } - } - - /* We get one phy packet to the async descriptor for each - * bus reset. We always ignore it. */ - if (tcode != OHCI1394_TCODE_PHY) { - if (!ohci->no_swap_incoming) - header_le32_to_cpu(d->spb, tcode); - DBGMSG("Packet received from node" - " %d ack=0x%02X spd=%d tcode=0x%X" - " length=%d ctx=%d tlabel=%d", - (d->spb[1]>>16)&0x3f, - (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f, - (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3, - tcode, length, d->ctx, - (d->spb[0]>>10)&0x3f); - - ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f) - == 0x11) ? 1 : 0; - - hpsb_packet_received(ohci->host, d->spb, - length-4, ack); - } -#ifdef OHCI1394_DEBUG - else - PRINT (KERN_DEBUG, "Got phy packet ctx=%d ... discarded", - d->ctx); -#endif - - rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; - - bytes_left = d->buf_size - rescount - offset; - - } - - d->buf_ind = idx; - d->buf_offset = offset; - - spin_unlock_irqrestore(&d->lock, flags); -} - -/* Bottom half that processes sent packets */ -static void dma_trm_tasklet (unsigned long data) -{ - struct dma_trm_ctx *d = (struct dma_trm_ctx*)data; - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - struct hpsb_packet *packet, *ptmp; - unsigned long flags; - u32 status, ack; - size_t datasize; - - spin_lock_irqsave(&d->lock, flags); - - list_for_each_entry_safe(packet, ptmp, &d->fifo_list, driver_list) { - datasize = packet->data_size; - if (datasize && packet->type != hpsb_raw) - status = le32_to_cpu( - d->prg_cpu[d->sent_ind]->end.status) >> 16; - else - status = le32_to_cpu( - d->prg_cpu[d->sent_ind]->begin.status) >> 16; - - if (status == 0) - /* this packet hasn't been sent yet*/ - break; - -#ifdef OHCI1394_DEBUG - if (datasize) - if (((le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf) == 0xa) - DBGMSG("Stream packet sent to channel %d tcode=0x%X " - "ack=0x%X spd=%d dataLength=%d ctx=%d", - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>8)&0x3f, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf, - status&0x1f, (status>>5)&0x3, - le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16, - d->ctx); - else - DBGMSG("Packet sent to node %d tcode=0x%X tLabel=" - "%d ack=0x%X spd=%d dataLength=%d ctx=%d", - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16)&0x3f, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>10)&0x3f, - status&0x1f, (status>>5)&0x3, - le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])>>16, - d->ctx); - else - DBGMSG("Packet sent to node %d tcode=0x%X tLabel=" - "%d ack=0x%X spd=%d data=0x%08X ctx=%d", - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1]) - >>16)&0x3f, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0]) - >>4)&0xf, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0]) - >>10)&0x3f, - status&0x1f, (status>>5)&0x3, - le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]), - d->ctx); -#endif - - if (status & 0x10) { - ack = status & 0xf; - } else { - switch (status & 0x1f) { - case EVT_NO_STATUS: /* that should never happen */ - case EVT_RESERVED_A: /* that should never happen */ - case EVT_LONG_PACKET: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_MISSING_ACK: - ack = ACKX_TIMEOUT; - break; - case EVT_UNDERRUN: - ack = ACKX_SEND_ERROR; - break; - case EVT_OVERRUN: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_DESCRIPTOR_READ: - case EVT_DATA_READ: - case EVT_DATA_WRITE: - ack = ACKX_SEND_ERROR; - break; - case EVT_BUS_RESET: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_TIMEOUT: - ack = ACKX_TIMEOUT; - break; - case EVT_TCODE_ERR: - ack = ACKX_SEND_ERROR; - break; - case EVT_RESERVED_B: /* that should never happen */ - case EVT_RESERVED_C: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_UNKNOWN: - case EVT_FLUSHED: - ack = ACKX_SEND_ERROR; - break; - default: - PRINT(KERN_ERR, "Unhandled OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - BUG(); - } - } - - list_del_init(&packet->driver_list); - hpsb_packet_sent(ohci->host, packet, ack); - - if (datasize) - pci_unmap_single(ohci->dev, - cpu_to_le32(d->prg_cpu[d->sent_ind]->end.address), - datasize, PCI_DMA_TODEVICE); - - d->sent_ind = (d->sent_ind+1)%d->num_desc; - d->free_prgs++; - } - - dma_trm_flush(ohci, d); - - spin_unlock_irqrestore(&d->lock, flags); -} - -static void free_dma_rcv_ctx(struct dma_rcv_ctx *d) -{ - int i; - struct ti_ohci *ohci = d->ohci; - - if (ohci == NULL) - return; - - DBGMSG("Freeing dma_rcv_ctx %d", d->ctx); - - if (d->buf_cpu) { - for (i=0; i<d->num_desc; i++) - if (d->buf_cpu[i] && d->buf_bus[i]) - pci_free_consistent( - ohci->dev, d->buf_size, - d->buf_cpu[i], d->buf_bus[i]); - kfree(d->buf_cpu); - kfree(d->buf_bus); - } - if (d->prg_cpu) { - for (i=0; i<d->num_desc; i++) - if (d->prg_cpu[i] && d->prg_bus[i]) - pci_pool_free(d->prg_pool, d->prg_cpu[i], - d->prg_bus[i]); - pci_pool_destroy(d->prg_pool); - kfree(d->prg_cpu); - kfree(d->prg_bus); - } - kfree(d->spb); - - /* Mark this context as freed. */ - d->ohci = NULL; -} - -static int -alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, - enum context_type type, int ctx, int num_desc, - int buf_size, int split_buf_size, int context_base) -{ - int i, len; - static int num_allocs; - static char pool_name[20]; - - d->ohci = ohci; - d->type = type; - d->ctx = ctx; - - d->num_desc = num_desc; - d->buf_size = buf_size; - d->split_buf_size = split_buf_size; - - d->ctrlSet = 0; - d->ctrlClear = 0; - d->cmdPtr = 0; - - d->buf_cpu = kzalloc(d->num_desc * sizeof(*d->buf_cpu), GFP_ATOMIC); - d->buf_bus = kzalloc(d->num_desc * sizeof(*d->buf_bus), GFP_ATOMIC); - - if (d->buf_cpu == NULL || d->buf_bus == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "DMA buffer"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_ATOMIC); - d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_ATOMIC); - - if (d->prg_cpu == NULL || d->prg_bus == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "DMA prg"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC); - - if (d->spb == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "split buffer"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - len = sprintf(pool_name, "ohci1394_rcv_prg"); - sprintf(pool_name+len, "%d", num_allocs); - d->prg_pool = pci_pool_create(pool_name, ohci->dev, - sizeof(struct dma_cmd), 4, 0); - if(d->prg_pool == NULL) - { - PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - num_allocs++; - - for (i=0; i<d->num_desc; i++) { - d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, - d->buf_size, - d->buf_bus+i); - - if (d->buf_cpu[i] != NULL) { - memset(d->buf_cpu[i], 0, d->buf_size); - } else { - PRINT(KERN_ERR, - "Failed to allocate %s", "DMA buffer"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i); - - if (d->prg_cpu[i] != NULL) { - memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); - } else { - PRINT(KERN_ERR, - "Failed to allocate %s", "DMA prg"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - } - - spin_lock_init(&d->lock); - - d->ctrlSet = context_base + OHCI1394_ContextControlSet; - d->ctrlClear = context_base + OHCI1394_ContextControlClear; - d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; - - tasklet_init(&d->task, dma_rcv_tasklet, (unsigned long) d); - return 0; -} - -static void free_dma_trm_ctx(struct dma_trm_ctx *d) -{ - int i; - struct ti_ohci *ohci = d->ohci; - - if (ohci == NULL) - return; - - DBGMSG("Freeing dma_trm_ctx %d", d->ctx); - - if (d->prg_cpu) { - for (i=0; i<d->num_desc; i++) - if (d->prg_cpu[i] && d->prg_bus[i]) - pci_pool_free(d->prg_pool, d->prg_cpu[i], - d->prg_bus[i]); - pci_pool_destroy(d->prg_pool); - kfree(d->prg_cpu); - kfree(d->prg_bus); - } - - /* Mark this context as freed. */ - d->ohci = NULL; -} - -static int -alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, - enum context_type type, int ctx, int num_desc, - int context_base) -{ - int i, len; - static char pool_name[20]; - static int num_allocs=0; - - d->ohci = ohci; - d->type = type; - d->ctx = ctx; - d->num_desc = num_desc; - d->ctrlSet = 0; - d->ctrlClear = 0; - d->cmdPtr = 0; - - d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_KERNEL); - d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_KERNEL); - - if (d->prg_cpu == NULL || d->prg_bus == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "AT DMA prg"); - free_dma_trm_ctx(d); - return -ENOMEM; - } - - len = sprintf(pool_name, "ohci1394_trm_prg"); - sprintf(pool_name+len, "%d", num_allocs); - d->prg_pool = pci_pool_create(pool_name, ohci->dev, - sizeof(struct at_dma_prg), 4, 0); - if (d->prg_pool == NULL) { - PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name); - free_dma_trm_ctx(d); - return -ENOMEM; - } - num_allocs++; - - for (i = 0; i < d->num_desc; i++) { - d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i); - - if (d->prg_cpu[i] != NULL) { - memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg)); - } else { - PRINT(KERN_ERR, - "Failed to allocate %s", "AT DMA prg"); - free_dma_trm_ctx(d); - return -ENOMEM; - } - } - - spin_lock_init(&d->lock); - - /* initialize tasklet */ - d->ctrlSet = context_base + OHCI1394_ContextControlSet; - d->ctrlClear = context_base + OHCI1394_ContextControlClear; - d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; - tasklet_init(&d->task, dma_trm_tasklet, (unsigned long)d); - return 0; -} - -static void ohci_set_hw_config_rom(struct hpsb_host *host, __be32 *config_rom) -{ - struct ti_ohci *ohci = host->hostdata; - - reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0])); - reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2])); - - memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN); -} - - -static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg, - quadlet_t data, quadlet_t compare) -{ - struct ti_ohci *ohci = host->hostdata; - int i; - - reg_write(ohci, OHCI1394_CSRData, data); - reg_write(ohci, OHCI1394_CSRCompareData, compare); - reg_write(ohci, OHCI1394_CSRControl, reg & 0x3); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) - break; - - mdelay(1); - } - - return reg_read(ohci, OHCI1394_CSRData); -} - -static struct hpsb_host_driver ohci1394_driver = { - .owner = THIS_MODULE, - .name = OHCI1394_DRIVER_NAME, - .set_hw_config_rom = ohci_set_hw_config_rom, - .transmit_packet = ohci_transmit, - .devctl = ohci_devctl, - .isoctl = ohci_isoctl, - .hw_csr_reg = ohci_hw_csr_reg, -}; - -/*********************************** - * PCI Driver Interface functions * - ***********************************/ - -#ifdef CONFIG_PPC_PMAC -static void ohci1394_pmac_on(struct pci_dev *dev) -{ - if (machine_is(powermac)) { - struct device_node *ofn = pci_device_to_OF_node(dev); - - if (ofn) { - pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1); - pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1); - } - } -} - -static void ohci1394_pmac_off(struct pci_dev *dev) -{ - if (machine_is(powermac)) { - struct device_node *ofn = pci_device_to_OF_node(dev); - - if (ofn) { - pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0); - pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0); - } - } -} -#else -#define ohci1394_pmac_on(dev) -#define ohci1394_pmac_off(dev) -#endif /* CONFIG_PPC_PMAC */ - -static int __devinit ohci1394_pci_probe(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - struct hpsb_host *host; - struct ti_ohci *ohci; /* shortcut to currently handled device */ - resource_size_t ohci_base; - int err = -ENOMEM; - - ohci1394_pmac_on(dev); - if (pci_enable_device(dev)) { - PRINT_G(KERN_ERR, "Failed to enable OHCI hardware"); - err = -ENXIO; - goto err; - } - pci_set_master(dev); - - host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev); - if (!host) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "host structure"); - goto err; - } - ohci = host->hostdata; - ohci->dev = dev; - ohci->host = host; - ohci->init_state = OHCI_INIT_ALLOC_HOST; - host->pdev = dev; - pci_set_drvdata(dev, ohci); - - /* We don't want hardware swapping */ - pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); - - /* Some oddball Apple controllers do not order the selfid - * properly, so we make up for it here. */ -#ifndef __LITTLE_ENDIAN - /* XXX: Need a better way to check this. I'm wondering if we can - * read the values of the OHCI1394_PCI_HCI_Control and the - * noByteSwapData registers to see if they were not cleared to - * zero. Should this work? Obviously it's not defined what these - * registers will read when they aren't supported. Bleh! */ - if (dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) { - ohci->no_swap_incoming = 1; - ohci->selfid_swap = 0; - } else - ohci->selfid_swap = 1; -#endif - - -#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW -#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e -#endif - - /* These chipsets require a bit of extra care when checking after - * a busreset. */ - if ((dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) || - (dev->vendor == PCI_VENDOR_ID_NVIDIA && - dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW)) - ohci->check_busreset = 1; - - /* We hardwire the MMIO length, since some CardBus adaptors - * fail to report the right length. Anyway, the ohci spec - * clearly says it's 2kb, so this shouldn't be a problem. */ - ohci_base = pci_resource_start(dev, 0); - if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) - PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!", - (unsigned long long)pci_resource_len(dev, 0)); - - if (!request_mem_region(ohci_base, OHCI1394_REGISTER_SIZE, - OHCI1394_DRIVER_NAME)) { - PRINT_G(KERN_ERR, "MMIO resource (0x%llx - 0x%llx) unavailable", - (unsigned long long)ohci_base, - (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_MEM_REGION; - - ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE); - if (ohci->registers == NULL) { - PRINT_G(KERN_ERR, "Failed to remap registers"); - err = -ENXIO; - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_IOMAPPING; - DBGMSG("Remapped memory spaces reg 0x%p", ohci->registers); - - /* csr_config rom allocation */ - ohci->csr_config_rom_cpu = - pci_alloc_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN, - &ohci->csr_config_rom_bus); - if (ohci->csr_config_rom_cpu == NULL) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "buffer config rom"); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_CONFIG_ROM_BUFFER; - - /* self-id dma buffer allocation */ - ohci->selfid_buf_cpu = - pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, - &ohci->selfid_buf_bus); - if (ohci->selfid_buf_cpu == NULL) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "self-ID buffer"); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER; - - if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff) - PRINT(KERN_INFO, "SelfID buffer %p is not aligned on " - "8Kb boundary... may cause problems on some CXD3222 chip", - ohci->selfid_buf_cpu); - - /* No self-id errors at startup */ - ohci->self_id_errors = 0; - - ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE; - /* AR DMA request context allocation */ - if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context, - DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC, - AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE, - OHCI1394_AsReqRcvContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Req context"); - goto err; - } - /* AR DMA response context allocation */ - if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context, - DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC, - AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE, - OHCI1394_AsRspRcvContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Resp context"); - goto err; - } - /* AT DMA request context */ - if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context, - DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC, - OHCI1394_AsReqTrContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Req context"); - goto err; - } - /* AT DMA response context */ - if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context, - DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC, - OHCI1394_AsRspTrContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Resp context"); - goto err; - } - /* Start off with a soft reset, to clear everything to a sane - * state. */ - ohci_soft_reset(ohci); - - /* Now enable LPS, which we need in order to start accessing - * most of the registers. In fact, on some cards (ALI M5251), - * accessing registers in the SClk domain without LPS enabled - * will lock up the machine. */ - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS); - - /* Disable and clear interrupts */ - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - - /* Flush MMIO writes and wait to make sure we have full link enabled. */ - reg_read(ohci, OHCI1394_Version); - msleep(50); - - /* Determine the number of available IR and IT contexts. */ - ohci->nb_iso_rcv_ctx = - get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); - ohci->nb_iso_xmit_ctx = - get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); - - /* Set the usage bits for non-existent contexts so they can't - * be allocated */ - ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx; - ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx; - - INIT_LIST_HEAD(&ohci->iso_tasklet_list); - spin_lock_init(&ohci->iso_tasklet_list_lock); - ohci->ISO_channel_usage = 0; - spin_lock_init(&ohci->IR_channel_lock); - - spin_lock_init(&ohci->event_lock); - - /* - * interrupts are disabled, all right, but... due to IRQF_SHARED we - * might get called anyway. We'll see no event, of course, but - * we need to get to that "no event", so enough should be initialized - * by that point. - */ - err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED, - OHCI1394_DRIVER_NAME, ohci); - if (err) { - PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_IRQ; - ohci_initialize(ohci); - - /* Set certain csr values */ - host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi); - host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo); - host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */ - host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf; - host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7; - - if (phys_dma) { - host->low_addr_space = - (u64) reg_read(ohci, OHCI1394_PhyUpperBound) << 16; - if (!host->low_addr_space) - host->low_addr_space = OHCI1394_PHYS_UPPER_BOUND_FIXED; - } - host->middle_addr_space = OHCI1394_MIDDLE_ADDRESS_SPACE; - - /* Tell the highlevel this host is ready */ - if (hpsb_add_host(host)) { - PRINT_G(KERN_ERR, "Failed to register host with highlevel"); - goto err; - } - ohci->init_state = OHCI_INIT_DONE; - - return 0; -err: - ohci1394_pci_remove(dev); - return err; -} - -static void ohci1394_pci_remove(struct pci_dev *dev) -{ - struct ti_ohci *ohci; - struct device *device; - - ohci = pci_get_drvdata(dev); - if (!ohci) - goto out; - - device = get_device(&ohci->host->device); - - switch (ohci->init_state) { - case OHCI_INIT_DONE: - hpsb_remove_host(ohci->host); - - /* Clear out BUS Options */ - reg_write(ohci, OHCI1394_ConfigROMhdr, 0); - reg_write(ohci, OHCI1394_BusOptions, - (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) | - 0x00ff0000); - memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN); - - case OHCI_INIT_HAVE_IRQ: - /* Clear interrupt registers */ - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); - - /* Disable IRM Contender */ - set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4)); - - /* Clear link control register */ - reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); - - /* Let all other nodes know to ignore us */ - ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); - - /* Soft reset before we start - this disables - * interrupts and clears linkEnable and LPS. */ - ohci_soft_reset(ohci); - free_irq(dev->irq, ohci); - - case OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE: - /* The ohci_soft_reset() stops all DMA contexts, so we - * dont need to do this. */ - free_dma_rcv_ctx(&ohci->ar_req_context); - free_dma_rcv_ctx(&ohci->ar_resp_context); - free_dma_trm_ctx(&ohci->at_req_context); - free_dma_trm_ctx(&ohci->at_resp_context); - - case OHCI_INIT_HAVE_SELFID_BUFFER: - pci_free_consistent(dev, OHCI1394_SI_DMA_BUF_SIZE, - ohci->selfid_buf_cpu, - ohci->selfid_buf_bus); - - case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER: - pci_free_consistent(dev, OHCI_CONFIG_ROM_LEN, - ohci->csr_config_rom_cpu, - ohci->csr_config_rom_bus); - - case OHCI_INIT_HAVE_IOMAPPING: - iounmap(ohci->registers); - - case OHCI_INIT_HAVE_MEM_REGION: - release_mem_region(pci_resource_start(dev, 0), - OHCI1394_REGISTER_SIZE); - - case OHCI_INIT_ALLOC_HOST: - pci_set_drvdata(dev, NULL); - } - - if (device) - put_device(device); -out: - ohci1394_pmac_off(dev); -} - -#ifdef CONFIG_PM -static int ohci1394_pci_suspend(struct pci_dev *dev, pm_message_t state) -{ - int err; - struct ti_ohci *ohci = pci_get_drvdata(dev); - - if (!ohci) { - printk(KERN_ERR "%s: tried to suspend nonexisting host\n", - OHCI1394_DRIVER_NAME); - return -ENXIO; - } - DBGMSG("suspend called"); - - /* Clear the async DMA contexts and stop using the controller */ - hpsb_bus_reset(ohci->host); - - /* See ohci1394_pci_remove() for comments on this sequence */ - reg_write(ohci, OHCI1394_ConfigROMhdr, 0); - reg_write(ohci, OHCI1394_BusOptions, - (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) | - 0x00ff0000); - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); - set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4)); - reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); - ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); - ohci_soft_reset(ohci); - - free_irq(dev->irq, ohci); - err = pci_save_state(dev); - if (err) { - PRINT(KERN_ERR, "pci_save_state failed with %d", err); - return err; - } - err = pci_set_power_state(dev, pci_choose_state(dev, state)); - if (err) - DBGMSG("pci_set_power_state failed with %d", err); - ohci1394_pmac_off(dev); - - return 0; -} - -static int ohci1394_pci_resume(struct pci_dev *dev) -{ - int err; - struct ti_ohci *ohci = pci_get_drvdata(dev); - - if (!ohci) { - printk(KERN_ERR "%s: tried to resume nonexisting host\n", - OHCI1394_DRIVER_NAME); - return -ENXIO; - } - DBGMSG("resume called"); - - ohci1394_pmac_on(dev); - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - err = pci_enable_device(dev); - if (err) { - PRINT(KERN_ERR, "pci_enable_device failed with %d", err); - return err; - } - - /* See ohci1394_pci_probe() for comments on this sequence */ - ohci_soft_reset(ohci); - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS); - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - reg_read(ohci, OHCI1394_Version); - msleep(50); - - err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED, - OHCI1394_DRIVER_NAME, ohci); - if (err) { - PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq); - return err; - } - - ohci_initialize(ohci); - - hpsb_resume_host(ohci->host); - return 0; -} -#endif /* CONFIG_PM */ - -static struct pci_device_id ohci1394_pci_tbl[] = { - { - .class = PCI_CLASS_SERIAL_FIREWIRE_OHCI, - .class_mask = PCI_ANY_ID, - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl); - -static struct pci_driver ohci1394_pci_driver = { - .name = OHCI1394_DRIVER_NAME, - .id_table = ohci1394_pci_tbl, - .probe = ohci1394_pci_probe, - .remove = ohci1394_pci_remove, -#ifdef CONFIG_PM - .resume = ohci1394_pci_resume, - .suspend = ohci1394_pci_suspend, -#endif -}; - -/*********************************** - * OHCI1394 Video Interface * - ***********************************/ - -/* essentially the only purpose of this code is to allow another - module to hook into ohci's interrupt handler */ - -/* returns zero if successful, one if DMA context is locked up */ -int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) -{ - int i=0; - - /* stop the channel program if it's still running */ - reg_write(ohci, reg, 0x8000); - - /* Wait until it effectively stops */ - while (reg_read(ohci, reg) & 0x400) { - i++; - if (i>5000) { - PRINT(KERN_ERR, - "Runaway loop while stopping context: %s...", msg ? msg : ""); - return 1; - } - - mb(); - udelay(10); - } - if (msg) PRINT(KERN_ERR, "%s: dma prg stopped", msg); - return 0; -} - -void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type, - void (*func)(unsigned long), unsigned long data) -{ - tasklet_init(&tasklet->tasklet, func, data); - tasklet->type = type; - /* We init the tasklet->link field, so we can list_del() it - * without worrying whether it was added to the list or not. */ - INIT_LIST_HEAD(&tasklet->link); -} - -int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet) -{ - unsigned long flags, *usage; - int n, i, r = -EBUSY; - - if (tasklet->type == OHCI_ISO_TRANSMIT) { - n = ohci->nb_iso_xmit_ctx; - usage = &ohci->it_ctx_usage; - } - else { - n = ohci->nb_iso_rcv_ctx; - usage = &ohci->ir_ctx_usage; - - /* only one receive context can be multichannel (OHCI sec 10.4.1) */ - if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) { - if (test_and_set_bit(0, &ohci->ir_multichannel_used)) { - return r; - } - } - } - - spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); - - for (i = 0; i < n; i++) - if (!test_and_set_bit(i, usage)) { - tasklet->context = i; - list_add_tail(&tasklet->link, &ohci->iso_tasklet_list); - r = 0; - break; - } - - spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); - - return r; -} - -void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet) -{ - unsigned long flags; - - tasklet_kill(&tasklet->tasklet); - - spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); - - if (tasklet->type == OHCI_ISO_TRANSMIT) - clear_bit(tasklet->context, &ohci->it_ctx_usage); - else { - clear_bit(tasklet->context, &ohci->ir_ctx_usage); - - if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) { - clear_bit(0, &ohci->ir_multichannel_used); - } - } - - list_del(&tasklet->link); - - spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); -} - -EXPORT_SYMBOL(ohci1394_stop_context); -EXPORT_SYMBOL(ohci1394_init_iso_tasklet); -EXPORT_SYMBOL(ohci1394_register_iso_tasklet); -EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet); - -/*********************************** - * General module initialization * - ***********************************/ - -MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>"); -MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers"); -MODULE_LICENSE("GPL"); - -static void __exit ohci1394_cleanup (void) -{ - pci_unregister_driver(&ohci1394_pci_driver); -} - -static int __init ohci1394_init(void) -{ - return pci_register_driver(&ohci1394_pci_driver); -} - -module_init(ohci1394_init); -module_exit(ohci1394_cleanup); diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h deleted file mode 100644 index 7fb8ab9780a..00000000000 --- a/drivers/ieee1394/ohci1394.h +++ /dev/null @@ -1,453 +0,0 @@ -/* - * ohci1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * Gord Peters <GordPeters@smarttech.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _OHCI1394_H -#define _OHCI1394_H - -#include "ieee1394_types.h" -#include <asm/io.h> - -#define OHCI1394_DRIVER_NAME "ohci1394" - -#define OHCI1394_MAX_AT_REQ_RETRIES 0xf -#define OHCI1394_MAX_AT_RESP_RETRIES 0x2 -#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 -#define OHCI1394_MAX_SELF_ID_ERRORS 16 - -#define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */ -#define AR_REQ_BUF_SIZE PAGE_SIZE /* size of AR req buffers */ -#define AR_REQ_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ - -#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */ -#define AR_RESP_BUF_SIZE PAGE_SIZE /* size of AR resp buffers */ -#define AR_RESP_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ - -#define IR_NUM_DESC 16 /* number of IR descriptors */ -#define IR_BUF_SIZE PAGE_SIZE /* 4096 bytes/buffer */ -#define IR_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ - -#define IT_NUM_DESC 16 /* number of IT descriptors */ - -#define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */ -#define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */ - -#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */ - -#define OHCI_CONFIG_ROM_LEN 1024 /* Length of the mapped configrom space */ - -#define OHCI1394_SI_DMA_BUF_SIZE 8192 /* length of the selfid buffer */ - -/* PCI configuration space addresses */ -#define OHCI1394_PCI_HCI_Control 0x40 - -struct dma_cmd { - u32 control; - u32 address; - u32 branchAddress; - u32 status; -}; - -/* - * FIXME: - * It is important that a single at_dma_prg does not cross a page boundary - * The proper way to do it would be to do the check dynamically as the - * programs are inserted into the AT fifo. - */ -struct at_dma_prg { - struct dma_cmd begin; - quadlet_t data[4]; - struct dma_cmd end; - quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ -}; - -/* identify whether a DMA context is asynchronous or isochronous */ -enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO }; - -/* DMA receive context */ -struct dma_rcv_ctx { - struct ti_ohci *ohci; - enum context_type type; - int ctx; - unsigned int num_desc; - - unsigned int buf_size; - unsigned int split_buf_size; - - /* dma block descriptors */ - struct dma_cmd **prg_cpu; - dma_addr_t *prg_bus; - struct pci_pool *prg_pool; - - /* dma buffers */ - quadlet_t **buf_cpu; - dma_addr_t *buf_bus; - - unsigned int buf_ind; - unsigned int buf_offset; - quadlet_t *spb; - spinlock_t lock; - struct tasklet_struct task; - int ctrlClear; - int ctrlSet; - int cmdPtr; - int ctxtMatch; -}; - -/* DMA transmit context */ -struct dma_trm_ctx { - struct ti_ohci *ohci; - enum context_type type; - int ctx; - unsigned int num_desc; - - /* dma block descriptors */ - struct at_dma_prg **prg_cpu; - dma_addr_t *prg_bus; - struct pci_pool *prg_pool; - - unsigned int prg_ind; - unsigned int sent_ind; - int free_prgs; - quadlet_t *branchAddrPtr; - - /* list of packets inserted in the AT FIFO */ - struct list_head fifo_list; - - /* list of pending packets to be inserted in the AT FIFO */ - struct list_head pending_list; - - spinlock_t lock; - struct tasklet_struct task; - int ctrlClear; - int ctrlSet; - int cmdPtr; -}; - -struct ohci1394_iso_tasklet { - struct tasklet_struct tasklet; - struct list_head link; - int context; - enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE, - OHCI_ISO_MULTICHANNEL_RECEIVE } type; -}; - -struct ti_ohci { - struct pci_dev *dev; - - enum { - OHCI_INIT_ALLOC_HOST, - OHCI_INIT_HAVE_MEM_REGION, - OHCI_INIT_HAVE_IOMAPPING, - OHCI_INIT_HAVE_CONFIG_ROM_BUFFER, - OHCI_INIT_HAVE_SELFID_BUFFER, - OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE, - OHCI_INIT_HAVE_IRQ, - OHCI_INIT_DONE, - } init_state; - - /* remapped memory spaces */ - void __iomem *registers; - - /* dma buffer for self-id packets */ - quadlet_t *selfid_buf_cpu; - dma_addr_t selfid_buf_bus; - - /* buffer for csr config rom */ - quadlet_t *csr_config_rom_cpu; - dma_addr_t csr_config_rom_bus; - int csr_config_rom_length; - - unsigned int max_packet_size; - - /* async receive */ - struct dma_rcv_ctx ar_resp_context; - struct dma_rcv_ctx ar_req_context; - - /* async transmit */ - struct dma_trm_ctx at_resp_context; - struct dma_trm_ctx at_req_context; - - /* iso receive */ - int nb_iso_rcv_ctx; - unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */ - unsigned long ir_multichannel_used; /* ditto */ - spinlock_t IR_channel_lock; - - /* iso transmit */ - int nb_iso_xmit_ctx; - unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */ - - u64 ISO_channel_usage; - - /* IEEE-1394 part follows */ - struct hpsb_host *host; - - int phyid, isroot; - - spinlock_t phy_reg_lock; - spinlock_t event_lock; - - int self_id_errors; - - /* Tasklets for iso receive and transmit, used by video1394 - * and dv1394 */ - struct list_head iso_tasklet_list; - spinlock_t iso_tasklet_list_lock; - - /* Swap the selfid buffer? */ - unsigned int selfid_swap:1; - /* Some Apple chipset seem to swap incoming headers for us */ - unsigned int no_swap_incoming:1; - - /* Force extra paranoia checking on bus-reset handling */ - unsigned int check_busreset:1; -}; - -static inline int cross_bound(unsigned long addr, unsigned int size) -{ - if (size == 0) - return 0; - - if (size > PAGE_SIZE) - return 1; - - if (addr >> PAGE_SHIFT != (addr + size - 1) >> PAGE_SHIFT) - return 1; - - return 0; -} - -/* - * Register read and write helper functions. - */ -static inline void reg_write(const struct ti_ohci *ohci, int offset, u32 data) -{ - writel(data, ohci->registers + offset); -} - -static inline u32 reg_read(const struct ti_ohci *ohci, int offset) -{ - return readl(ohci->registers + offset); -} - - -/* 2 KiloBytes of register space */ -#define OHCI1394_REGISTER_SIZE 0x800 - -/* Offsets relative to context bases defined below */ - -#define OHCI1394_ContextControlSet 0x000 -#define OHCI1394_ContextControlClear 0x004 -#define OHCI1394_ContextCommandPtr 0x00C - -/* register map */ -#define OHCI1394_Version 0x000 -#define OHCI1394_GUID_ROM 0x004 -#define OHCI1394_ATRetries 0x008 -#define OHCI1394_CSRData 0x00C -#define OHCI1394_CSRCompareData 0x010 -#define OHCI1394_CSRControl 0x014 -#define OHCI1394_ConfigROMhdr 0x018 -#define OHCI1394_BusID 0x01C -#define OHCI1394_BusOptions 0x020 -#define OHCI1394_GUIDHi 0x024 -#define OHCI1394_GUIDLo 0x028 -#define OHCI1394_ConfigROMmap 0x034 -#define OHCI1394_PostedWriteAddressLo 0x038 -#define OHCI1394_PostedWriteAddressHi 0x03C -#define OHCI1394_VendorID 0x040 -#define OHCI1394_HCControlSet 0x050 -#define OHCI1394_HCControlClear 0x054 -#define OHCI1394_HCControl_noByteSwap 0x40000000 -#define OHCI1394_HCControl_programPhyEnable 0x00800000 -#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000 -#define OHCI1394_HCControl_LPS 0x00080000 -#define OHCI1394_HCControl_postedWriteEnable 0x00040000 -#define OHCI1394_HCControl_linkEnable 0x00020000 -#define OHCI1394_HCControl_softReset 0x00010000 -#define OHCI1394_SelfIDBuffer 0x064 -#define OHCI1394_SelfIDCount 0x068 -#define OHCI1394_IRMultiChanMaskHiSet 0x070 -#define OHCI1394_IRMultiChanMaskHiClear 0x074 -#define OHCI1394_IRMultiChanMaskLoSet 0x078 -#define OHCI1394_IRMultiChanMaskLoClear 0x07C -#define OHCI1394_IntEventSet 0x080 -#define OHCI1394_IntEventClear 0x084 -#define OHCI1394_IntMaskSet 0x088 -#define OHCI1394_IntMaskClear 0x08C -#define OHCI1394_IsoXmitIntEventSet 0x090 -#define OHCI1394_IsoXmitIntEventClear 0x094 -#define OHCI1394_IsoXmitIntMaskSet 0x098 -#define OHCI1394_IsoXmitIntMaskClear 0x09C -#define OHCI1394_IsoRecvIntEventSet 0x0A0 -#define OHCI1394_IsoRecvIntEventClear 0x0A4 -#define OHCI1394_IsoRecvIntMaskSet 0x0A8 -#define OHCI1394_IsoRecvIntMaskClear 0x0AC -#define OHCI1394_InitialBandwidthAvailable 0x0B0 -#define OHCI1394_InitialChannelsAvailableHi 0x0B4 -#define OHCI1394_InitialChannelsAvailableLo 0x0B8 -#define OHCI1394_FairnessControl 0x0DC -#define OHCI1394_LinkControlSet 0x0E0 -#define OHCI1394_LinkControlClear 0x0E4 -#define OHCI1394_LinkControl_RcvSelfID 0x00000200 -#define OHCI1394_LinkControl_RcvPhyPkt 0x00000400 -#define OHCI1394_LinkControl_CycleTimerEnable 0x00100000 -#define OHCI1394_LinkControl_CycleMaster 0x00200000 -#define OHCI1394_LinkControl_CycleSource 0x00400000 -#define OHCI1394_NodeID 0x0E8 -#define OHCI1394_PhyControl 0x0EC -#define OHCI1394_IsochronousCycleTimer 0x0F0 -#define OHCI1394_AsReqFilterHiSet 0x100 -#define OHCI1394_AsReqFilterHiClear 0x104 -#define OHCI1394_AsReqFilterLoSet 0x108 -#define OHCI1394_AsReqFilterLoClear 0x10C -#define OHCI1394_PhyReqFilterHiSet 0x110 -#define OHCI1394_PhyReqFilterHiClear 0x114 -#define OHCI1394_PhyReqFilterLoSet 0x118 -#define OHCI1394_PhyReqFilterLoClear 0x11C -#define OHCI1394_PhyUpperBound 0x120 - -#define OHCI1394_AsReqTrContextBase 0x180 -#define OHCI1394_AsReqTrContextControlSet 0x180 -#define OHCI1394_AsReqTrContextControlClear 0x184 -#define OHCI1394_AsReqTrCommandPtr 0x18C - -#define OHCI1394_AsRspTrContextBase 0x1A0 -#define OHCI1394_AsRspTrContextControlSet 0x1A0 -#define OHCI1394_AsRspTrContextControlClear 0x1A4 -#define OHCI1394_AsRspTrCommandPtr 0x1AC - -#define OHCI1394_AsReqRcvContextBase 0x1C0 -#define OHCI1394_AsReqRcvContextControlSet 0x1C0 -#define OHCI1394_AsReqRcvContextControlClear 0x1C4 -#define OHCI1394_AsReqRcvCommandPtr 0x1CC - -#define OHCI1394_AsRspRcvContextBase 0x1E0 -#define OHCI1394_AsRspRcvContextControlSet 0x1E0 -#define OHCI1394_AsRspRcvContextControlClear 0x1E4 -#define OHCI1394_AsRspRcvCommandPtr 0x1EC - -/* Isochronous transmit registers */ -/* Add (16 * n) for context n */ -#define OHCI1394_IsoXmitContextBase 0x200 -#define OHCI1394_IsoXmitContextControlSet 0x200 -#define OHCI1394_IsoXmitContextControlClear 0x204 -#define OHCI1394_IsoXmitCommandPtr 0x20C - -/* Isochronous receive registers */ -/* Add (32 * n) for context n */ -#define OHCI1394_IsoRcvContextBase 0x400 -#define OHCI1394_IsoRcvContextControlSet 0x400 -#define OHCI1394_IsoRcvContextControlClear 0x404 -#define OHCI1394_IsoRcvCommandPtr 0x40C -#define OHCI1394_IsoRcvContextMatch 0x410 - -/* Interrupts Mask/Events */ - -#define OHCI1394_reqTxComplete 0x00000001 -#define OHCI1394_respTxComplete 0x00000002 -#define OHCI1394_ARRQ 0x00000004 -#define OHCI1394_ARRS 0x00000008 -#define OHCI1394_RQPkt 0x00000010 -#define OHCI1394_RSPkt 0x00000020 -#define OHCI1394_isochTx 0x00000040 -#define OHCI1394_isochRx 0x00000080 -#define OHCI1394_postedWriteErr 0x00000100 -#define OHCI1394_lockRespErr 0x00000200 -#define OHCI1394_selfIDComplete 0x00010000 -#define OHCI1394_busReset 0x00020000 -#define OHCI1394_phy 0x00080000 -#define OHCI1394_cycleSynch 0x00100000 -#define OHCI1394_cycle64Seconds 0x00200000 -#define OHCI1394_cycleLost 0x00400000 -#define OHCI1394_cycleInconsistent 0x00800000 -#define OHCI1394_unrecoverableError 0x01000000 -#define OHCI1394_cycleTooLong 0x02000000 -#define OHCI1394_phyRegRcvd 0x04000000 -#define OHCI1394_masterIntEnable 0x80000000 - -/* DMA Control flags */ -#define DMA_CTL_OUTPUT_MORE 0x00000000 -#define DMA_CTL_OUTPUT_LAST 0x10000000 -#define DMA_CTL_INPUT_MORE 0x20000000 -#define DMA_CTL_INPUT_LAST 0x30000000 -#define DMA_CTL_UPDATE 0x08000000 -#define DMA_CTL_IMMEDIATE 0x02000000 -#define DMA_CTL_IRQ 0x00300000 -#define DMA_CTL_BRANCH 0x000c0000 -#define DMA_CTL_WAIT 0x00030000 - -/* OHCI evt_* error types, table 3-2 of the OHCI 1.1 spec. */ -#define EVT_NO_STATUS 0x0 /* No event status */ -#define EVT_RESERVED_A 0x1 /* Reserved, not used !!! */ -#define EVT_LONG_PACKET 0x2 /* The revc data was longer than the buf */ -#define EVT_MISSING_ACK 0x3 /* A subaction gap was detected before an ack - arrived, or recv'd ack had a parity error */ -#define EVT_UNDERRUN 0x4 /* Underrun on corresponding FIFO, packet - truncated */ -#define EVT_OVERRUN 0x5 /* A recv FIFO overflowed on reception of ISO - packet */ -#define EVT_DESCRIPTOR_READ 0x6 /* An unrecoverable error occurred while host was - reading a descriptor block */ -#define EVT_DATA_READ 0x7 /* An error occurred while host controller was - attempting to read from host memory in the data - stage of descriptor processing */ -#define EVT_DATA_WRITE 0x8 /* An error occurred while host controller was - attempting to write either during the data stage - of descriptor processing, or when processing a single - 16-bit host memory write */ -#define EVT_BUS_RESET 0x9 /* Identifies a PHY packet in the recv buffer as - being a synthesized bus reset packet */ -#define EVT_TIMEOUT 0xa /* Indicates that the asynchronous transmit response - packet expired and was not transmitted, or that an - IT DMA context experienced a skip processing overflow */ -#define EVT_TCODE_ERR 0xb /* A bad tCode is associated with this packet. - The packet was flushed */ -#define EVT_RESERVED_B 0xc /* Reserved, not used !!! */ -#define EVT_RESERVED_C 0xd /* Reserved, not used !!! */ -#define EVT_UNKNOWN 0xe /* An error condition has occurred that cannot be - represented by any other event codes defined herein. */ -#define EVT_FLUSHED 0xf /* Send by the link side of output FIFO when asynchronous - packets are being flushed due to a bus reset. */ - -#define OHCI1394_TCODE_PHY 0xE - -/* Node offset map (phys DMA area, posted write area). - * The value of OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED may be modified but must - * be lower than OHCI1394_MIDDLE_ADDRESS_SPACE. - * OHCI1394_PHYS_UPPER_BOUND_FIXED and OHCI1394_MIDDLE_ADDRESS_SPACE are - * constants given by the OHCI spec. - */ -#define OHCI1394_PHYS_UPPER_BOUND_FIXED 0x000100000000ULL /* 4 GB */ -#define OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED 0x010000000000ULL /* 1 TB */ -#define OHCI1394_MIDDLE_ADDRESS_SPACE 0xffff00000000ULL - -void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, - int type, - void (*func)(unsigned long), - unsigned long data); -int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet); -void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet); -int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); -struct ti_ohci *ohci1394_get_struct(int card_num); - -#endif diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c deleted file mode 100644 index bf47fee7980..00000000000 --- a/drivers/ieee1394/pcilynx.c +++ /dev/null @@ -1,1554 +0,0 @@ -/* - * pcilynx.c - Texas Instruments PCILynx driver - * Copyright (C) 1999,2000 Andreas Bombe <andreas.bombe@munich.netsurf.de>, - * Stephan Linz <linz@mazet.de> - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * Contributions: - * - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * reading bus info block (containing GUID) from serial - * eeprom via i2c and storing it in config ROM - * Reworked code for initiating bus resets - * (long, short, with or without hold-off) - * Enhancements in async and iso send code - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/kdev_t.h> -#include <linux/dma-mapping.h> -#include <asm/byteorder.h> -#include <asm/atomic.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/irq.h> - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "pcilynx.h" - -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> - -/* print general (card independent) information */ -#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) -/* print card specific information */ -#define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args) - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) -#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args) -#else -#define PRINT_GD(level, fmt, args...) do {} while (0) -#define PRINTD(level, card, fmt, args...) do {} while (0) -#endif - - -/* Module Parameters */ -static int skip_eeprom; -module_param(skip_eeprom, int, 0444); -MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0)."); - - -static struct hpsb_host_driver lynx_driver; -static unsigned int card_id; - - - -/* - * I2C stuff - */ - -/* the i2c stuff was inspired by i2c-philips-par.c */ - -static void bit_setscl(void *data, int state) -{ - if (state) { - ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000040; - } else { - ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000040; - } - reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state); -} - -static void bit_setsda(void *data, int state) -{ - if (state) { - ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000010; - } else { - ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000010; - } - reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state); -} - -static int bit_getscl(void *data) -{ - return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000040; -} - -static int bit_getsda(void *data) -{ - return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000010; -} - -static struct i2c_algo_bit_data bit_data = { - .setsda = bit_setsda, - .setscl = bit_setscl, - .getsda = bit_getsda, - .getscl = bit_getscl, - .udelay = 5, - .timeout = 100, -}; - - -/* - * PCL handling functions. - */ - -static pcl_t alloc_pcl(struct ti_lynx *lynx) -{ - u8 m; - int i, j; - - spin_lock(&lynx->lock); - /* FIXME - use ffz() to make this readable */ - for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) { - m = lynx->pcl_bmap[i]; - for (j = 0; j < 8; j++) { - if (m & 1<<j) { - continue; - } - m |= 1<<j; - lynx->pcl_bmap[i] = m; - spin_unlock(&lynx->lock); - return 8 * i + j; - } - } - spin_unlock(&lynx->lock); - - return -1; -} - - -#if 0 -static void free_pcl(struct ti_lynx *lynx, pcl_t pclid) -{ - int off, bit; - - off = pclid / 8; - bit = pclid % 8; - - if (pclid < 0) { - return; - } - - spin_lock(&lynx->lock); - if (lynx->pcl_bmap[off] & 1<<bit) { - lynx->pcl_bmap[off] &= ~(1<<bit); - } else { - PRINT(KERN_ERR, lynx->id, - "attempted to free unallocated PCL %d", pclid); - } - spin_unlock(&lynx->lock); -} - -/* functions useful for debugging */ -static void pretty_print_pcl(const struct ti_pcl *pcl) -{ - int i; - - printk("PCL next %08x, userdata %08x, status %08x, remtrans %08x, nextbuf %08x\n", - pcl->next, pcl->user_data, pcl->pcl_status, - pcl->remaining_transfer_count, pcl->next_data_buffer); - - printk("PCL"); - for (i=0; i<13; i++) { - printk(" c%x:%08x d%x:%08x", - i, pcl->buffer[i].control, i, pcl->buffer[i].pointer); - if (!(i & 0x3) && (i != 12)) printk("\nPCL"); - } - printk("\n"); -} - -static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid) -{ - struct ti_pcl pcl; - - get_pcl(lynx, pclid, &pcl); - pretty_print_pcl(&pcl); -} -#endif - - - -/*********************************** - * IEEE-1394 functionality section * - ***********************************/ - - -static int get_phy_reg(struct ti_lynx *lynx, int addr) -{ - int retval; - int i = 0; - - unsigned long flags; - - if (addr > 15) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY register address %d out of range", - __func__, addr); - return -1; - } - - spin_lock_irqsave(&lynx->phy_reg_lock, flags); - - reg_write(lynx, LINK_PHY, LINK_PHY_READ | LINK_PHY_ADDR(addr)); - do { - retval = reg_read(lynx, LINK_PHY); - - if (i > 10000) { - PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting", - __func__); - retval = -1; - break; - } - i++; - } while ((retval & 0xf00) != LINK_PHY_RADDR(addr)); - - reg_write(lynx, LINK_INT_STATUS, LINK_INT_PHY_REG_RCVD); - spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); - - if (retval != -1) { - return retval & 0xff; - } else { - return -1; - } -} - -static int set_phy_reg(struct ti_lynx *lynx, int addr, int val) -{ - unsigned long flags; - - if (addr > 15) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY register address %d out of range", __func__, addr); - return -1; - } - - if (val > 0xff) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY register value %d out of range", __func__, val); - return -1; - } - - spin_lock_irqsave(&lynx->phy_reg_lock, flags); - - reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr) - | LINK_PHY_WDATA(val)); - - spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); - - return 0; -} - -static int sel_phy_reg_page(struct ti_lynx *lynx, int page) -{ - int reg; - - if (page > 7) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY page %d out of range", __func__, page); - return -1; - } - - reg = get_phy_reg(lynx, 7); - if (reg != -1) { - reg &= 0x1f; - reg |= (page << 5); - set_phy_reg(lynx, 7, reg); - return 0; - } else { - return -1; - } -} - -#if 0 /* not needed at this time */ -static int sel_phy_reg_port(struct ti_lynx *lynx, int port) -{ - int reg; - - if (port > 15) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY port %d out of range", __func__, port); - return -1; - } - - reg = get_phy_reg(lynx, 7); - if (reg != -1) { - reg &= 0xf0; - reg |= port; - set_phy_reg(lynx, 7, reg); - return 0; - } else { - return -1; - } -} -#endif - -static u32 get_phy_vendorid(struct ti_lynx *lynx) -{ - u32 pvid = 0; - sel_phy_reg_page(lynx, 1); - pvid |= (get_phy_reg(lynx, 10) << 16); - pvid |= (get_phy_reg(lynx, 11) << 8); - pvid |= get_phy_reg(lynx, 12); - PRINT(KERN_INFO, lynx->id, "PHY vendor id 0x%06x", pvid); - return pvid; -} - -static u32 get_phy_productid(struct ti_lynx *lynx) -{ - u32 id = 0; - sel_phy_reg_page(lynx, 1); - id |= (get_phy_reg(lynx, 13) << 16); - id |= (get_phy_reg(lynx, 14) << 8); - id |= get_phy_reg(lynx, 15); - PRINT(KERN_INFO, lynx->id, "PHY product id 0x%06x", id); - return id; -} - -static quadlet_t generate_own_selfid(struct ti_lynx *lynx, - struct hpsb_host *host) -{ - quadlet_t lsid; - char phyreg[7]; - int i; - - phyreg[0] = lynx->phy_reg0; - for (i = 1; i < 7; i++) { - phyreg[i] = get_phy_reg(lynx, i); - } - - /* FIXME? We assume a TSB21LV03A phy here. This code doesn't support - more than 3 ports on the PHY anyway. */ - - lsid = 0x80400000 | ((phyreg[0] & 0xfc) << 22); - lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */ - lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */ - if (!hpsb_disable_irm) - lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dependent) */ - /* lsid |= 1 << 11; *//* set contender (hack) */ - lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */ - - for (i = 0; i < (phyreg[2] & 0xf); i++) { /* ports */ - if (phyreg[3 + i] & 0x4) { - lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3) - << (6 - i*2); - } else { - lsid |= 1 << (6 - i*2); - } - } - - cpu_to_be32s(&lsid); - PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid); - return lsid; -} - -static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host) -{ - quadlet_t *q = lynx->rcv_page; - int phyid, isroot, size; - quadlet_t lsid = 0; - int i; - - if (lynx->phy_reg0 == -1 || lynx->selfid_size == -1) return; - - size = lynx->selfid_size; - phyid = lynx->phy_reg0; - - i = (size > 16 ? 16 : size) / 4 - 1; - while (i >= 0) { - cpu_to_be32s(&q[i]); - i--; - } - - if (!lynx->phyic.reg_1394a) { - lsid = generate_own_selfid(lynx, host); - } - - isroot = (phyid & 2) != 0; - phyid >>= 2; - PRINT(KERN_INFO, lynx->id, "SelfID process finished (phyid %d, %s)", - phyid, (isroot ? "root" : "not root")); - reg_write(lynx, LINK_ID, (0xffc0 | phyid) << 16); - - if (!lynx->phyic.reg_1394a && !size) { - hpsb_selfid_received(host, lsid); - } - - while (size > 0) { - struct selfid *sid = (struct selfid *)q; - - if (!lynx->phyic.reg_1394a && !sid->extended - && (sid->phy_id == (phyid + 1))) { - hpsb_selfid_received(host, lsid); - } - - if (q[0] == ~q[1]) { - PRINT(KERN_DEBUG, lynx->id, "SelfID packet 0x%x rcvd", - q[0]); - hpsb_selfid_received(host, q[0]); - } else { - PRINT(KERN_INFO, lynx->id, - "inconsistent selfid 0x%x/0x%x", q[0], q[1]); - } - q += 2; - size -= 8; - } - - if (!lynx->phyic.reg_1394a && isroot && phyid != 0) { - hpsb_selfid_received(host, lsid); - } - - hpsb_selfid_complete(host, phyid, isroot); - - if (host->in_bus_reset) return; /* in bus reset again */ - - if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); //FIXME: I do not think, we need this here - reg_set_bits(lynx, LINK_CONTROL, - LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN - | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN); -} - - - -/* This must be called with the respective queue_lock held. */ -static void send_next(struct ti_lynx *lynx, int what) -{ - struct ti_pcl pcl; - struct lynx_send_data *d; - struct hpsb_packet *packet; - -#if 0 /* has been removed from ieee1394 core */ - d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async); -#else - d = &lynx->async; -#endif - if (!list_empty(&d->pcl_queue)) { - PRINT(KERN_ERR, lynx->id, "trying to queue a new packet in nonempty fifo"); - BUG(); - } - - packet = driver_packet(d->queue.next); - list_move_tail(&packet->driver_list, &d->pcl_queue); - - d->header_dma = pci_map_single(lynx->dev, packet->header, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - d->data_dma = pci_map_single(lynx->dev, packet->data, - packet->data_size, - PCI_DMA_TODEVICE); - } else { - d->data_dma = 0; - } - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - pcl.pcl_status = 0; - pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[0].pointer = d->header_dma; - pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size; - pcl.buffer[1].pointer = d->data_dma; - - switch (packet->type) { - case hpsb_async: - pcl.buffer[0].control |= PCL_CMD_XMT; - break; -#if 0 /* has been removed from ieee1394 core */ - case hpsb_iso: - pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE; - break; -#endif - case hpsb_raw: - pcl.buffer[0].control |= PCL_CMD_UNFXMT; - break; - } - - put_pcl(lynx, d->pcl, &pcl); - run_pcl(lynx, d->pcl_start, d->channel); -} - - -/* called from subsystem core */ -static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) -{ - struct ti_lynx *lynx = host->hostdata; - struct lynx_send_data *d; - unsigned long flags; - - if (packet->data_size >= 4096) { - PRINT(KERN_ERR, lynx->id, "transmit packet data too big (%Zd)", - packet->data_size); - return -EOVERFLOW; - } - - switch (packet->type) { - case hpsb_async: - case hpsb_raw: - d = &lynx->async; - break; -#if 0 /* has been removed from ieee1394 core */ - case hpsb_iso: - d = &lynx->iso_send; - break; -#endif - default: - PRINT(KERN_ERR, lynx->id, "invalid packet type %d", - packet->type); - return -EINVAL; - } - - if (packet->tcode == TCODE_WRITEQ - || packet->tcode == TCODE_READQ_RESPONSE) { - cpu_to_be32s(&packet->header[3]); - } - - spin_lock_irqsave(&d->queue_lock, flags); - - list_add_tail(&packet->driver_list, &d->queue); - if (list_empty(&d->pcl_queue)) - send_next(lynx, packet->type); - - spin_unlock_irqrestore(&d->queue_lock, flags); - - return 0; -} - - -/* called from subsystem core */ -static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) -{ - struct ti_lynx *lynx = host->hostdata; - int retval = 0; - struct hpsb_packet *packet; - LIST_HEAD(packet_list); - unsigned long flags; - int phy_reg; - - switch (cmd) { - case RESET_BUS: - if (reg_read(lynx, LINK_INT_STATUS) & LINK_INT_PHY_BUSRESET) { - retval = 0; - break; - } - - switch (arg) { - case SHORT_RESET: - if (lynx->phyic.reg_1394a) { - phy_reg = get_phy_reg(lynx, 5); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ - break; - } else { - PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); - /* fall through to long bus reset */ - } - case LONG_RESET: - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ - break; - case SHORT_RESET_NO_FORCE_ROOT: - if (lynx->phyic.reg_1394a) { - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - if (phy_reg & 0x80) { - phy_reg &= ~0x80; - set_phy_reg(lynx, 1, phy_reg); /* clear RHB */ - } - - phy_reg = get_phy_reg(lynx, 5); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, no force_root) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ - break; - } else { - PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); - /* fall through to long bus reset */ - } - case LONG_RESET_NO_FORCE_ROOT: - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg &= ~0x80; - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, no force_root) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ - break; - case SHORT_RESET_FORCE_ROOT: - if (lynx->phyic.reg_1394a) { - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - if (!(phy_reg & 0x80)) { - phy_reg |= 0x80; - set_phy_reg(lynx, 1, phy_reg); /* set RHB */ - } - - phy_reg = get_phy_reg(lynx, 5); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, force_root set) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ - break; - } else { - PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); - /* fall through to long bus reset */ - } - case LONG_RESET_FORCE_ROOT: - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0xc0; - - PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, force_root set) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 1, phy_reg); /* set IBR and RHB */ - break; - default: - PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg); - retval = -1; - } - - break; - - case GET_CYCLE_COUNTER: - retval = reg_read(lynx, CYCLE_TIMER); - break; - - case SET_CYCLE_COUNTER: - reg_write(lynx, CYCLE_TIMER, arg); - break; - - case SET_BUS_ID: - reg_write(lynx, LINK_ID, - (arg << 22) | (reg_read(lynx, LINK_ID) & 0x003f0000)); - break; - - case ACT_CYCLE_MASTER: - if (arg) { - reg_set_bits(lynx, LINK_CONTROL, - LINK_CONTROL_CYCMASTER); - } else { - reg_clear_bits(lynx, LINK_CONTROL, - LINK_CONTROL_CYCMASTER); - } - break; - - case CANCEL_REQUESTS: - spin_lock_irqsave(&lynx->async.queue_lock, flags); - - reg_write(lynx, DMA_CHAN_CTRL(CHANNEL_ASYNC_SEND), 0); - list_splice_init(&lynx->async.queue, &packet_list); - - if (list_empty(&lynx->async.pcl_queue)) { - spin_unlock_irqrestore(&lynx->async.queue_lock, flags); - PRINTD(KERN_DEBUG, lynx->id, "no async packet in PCL to cancel"); - } else { - struct ti_pcl pcl; - u32 ack; - - PRINT(KERN_INFO, lynx->id, "cancelling async packet, that was already in PCL"); - - get_pcl(lynx, lynx->async.pcl, &pcl); - - packet = driver_packet(lynx->async.pcl_queue.next); - list_del_init(&packet->driver_list); - - pci_unmap_single(lynx->dev, lynx->async.header_dma, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - pci_unmap_single(lynx->dev, lynx->async.data_dma, - packet->data_size, PCI_DMA_TODEVICE); - } - - spin_unlock_irqrestore(&lynx->async.queue_lock, flags); - - if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { - if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { - ack = (pcl.pcl_status >> 15) & 0xf; - PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); - ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); - } else { - ack = (pcl.pcl_status >> 15) & 0xf; - } - } else { - PRINT(KERN_INFO, lynx->id, "async packet was not completed"); - ack = ACKX_ABORTED; - } - hpsb_packet_sent(host, packet, ack); - } - - while (!list_empty(&packet_list)) { - packet = driver_packet(packet_list.next); - list_del_init(&packet->driver_list); - hpsb_packet_sent(host, packet, ACKX_ABORTED); - } - - break; -#if 0 /* has been removed from ieee1394 core */ - case ISO_LISTEN_CHANNEL: - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - - if (lynx->iso_rcv.chan_count++ == 0) { - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), - DMA_WORD1_CMP_ENABLE_MASTER); - } - - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); - break; - - case ISO_UNLISTEN_CHANNEL: - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - - if (--lynx->iso_rcv.chan_count == 0) { - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), - 0); - } - - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); - break; -#endif - default: - PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd); - retval = -1; - } - - return retval; -} - - -/*************************************** - * IEEE-1394 functionality section END * - ***************************************/ - - -/******************************************************** - * Global stuff (interrupt handler, init/shutdown code) * - ********************************************************/ - - -static irqreturn_t lynx_irq_handler(int irq, void *dev_id) -{ - struct ti_lynx *lynx = (struct ti_lynx *)dev_id; - struct hpsb_host *host = lynx->host; - u32 intmask; - u32 linkint; - - linkint = reg_read(lynx, LINK_INT_STATUS); - intmask = reg_read(lynx, PCI_INT_STATUS); - - if (!(intmask & PCI_INT_INT_PEND)) - return IRQ_NONE; - - PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask, - linkint); - - reg_write(lynx, LINK_INT_STATUS, linkint); - reg_write(lynx, PCI_INT_STATUS, intmask); - - if (intmask & PCI_INT_1394) { - if (linkint & LINK_INT_PHY_TIMEOUT) { - PRINT(KERN_INFO, lynx->id, "PHY timeout occurred"); - } - if (linkint & LINK_INT_PHY_BUSRESET) { - PRINT(KERN_INFO, lynx->id, "bus reset interrupt"); - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - if (!host->in_bus_reset) - hpsb_bus_reset(host); - } - if (linkint & LINK_INT_PHY_REG_RCVD) { - u32 reg; - - spin_lock(&lynx->phy_reg_lock); - reg = reg_read(lynx, LINK_PHY); - spin_unlock(&lynx->phy_reg_lock); - - if (!host->in_bus_reset) { - PRINT(KERN_INFO, lynx->id, - "phy reg received without reset"); - } else if (reg & 0xf00) { - PRINT(KERN_INFO, lynx->id, - "unsolicited phy reg %d received", - (reg >> 8) & 0xf); - } else { - lynx->phy_reg0 = reg & 0xff; - handle_selfid(lynx, host); - } - } - if (linkint & LINK_INT_ISO_STUCK) { - PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck"); - } - if (linkint & LINK_INT_ASYNC_STUCK) { - PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck"); - } - if (linkint & LINK_INT_SENT_REJECT) { - PRINT(KERN_INFO, lynx->id, "sent reject"); - } - if (linkint & LINK_INT_TX_INVALID_TC) { - PRINT(KERN_INFO, lynx->id, "invalid transaction code"); - } - if (linkint & LINK_INT_GRF_OVERFLOW) { - /* flush FIFO if overflow happens during reset */ - if (host->in_bus_reset) - reg_write(lynx, FIFO_CONTROL, - FIFO_CONTROL_GRF_FLUSH); - PRINT(KERN_INFO, lynx->id, "GRF overflow"); - } - if (linkint & LINK_INT_ITF_UNDERFLOW) { - PRINT(KERN_INFO, lynx->id, "ITF underflow"); - } - if (linkint & LINK_INT_ATF_UNDERFLOW) { - PRINT(KERN_INFO, lynx->id, "ATF underflow"); - } - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) { - PRINTD(KERN_DEBUG, lynx->id, "iso receive"); - - spin_lock(&lynx->iso_rcv.lock); - - lynx->iso_rcv.stat[lynx->iso_rcv.next] = - reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV)); - - lynx->iso_rcv.used++; - lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL; - - if ((lynx->iso_rcv.next == lynx->iso_rcv.last) - || !lynx->iso_rcv.chan_count) { - PRINTD(KERN_DEBUG, lynx->id, "stopped"); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); - } - - run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next, - CHANNEL_ISO_RCV); - - spin_unlock(&lynx->iso_rcv.lock); - - tasklet_schedule(&lynx->iso_rcv.tq); - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) { - PRINTD(KERN_DEBUG, lynx->id, "async sent"); - spin_lock(&lynx->async.queue_lock); - - if (list_empty(&lynx->async.pcl_queue)) { - spin_unlock(&lynx->async.queue_lock); - PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)"); - } else { - struct ti_pcl pcl; - u32 ack; - struct hpsb_packet *packet; - - get_pcl(lynx, lynx->async.pcl, &pcl); - - packet = driver_packet(lynx->async.pcl_queue.next); - list_del_init(&packet->driver_list); - - pci_unmap_single(lynx->dev, lynx->async.header_dma, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - pci_unmap_single(lynx->dev, lynx->async.data_dma, - packet->data_size, PCI_DMA_TODEVICE); - } - - if (!list_empty(&lynx->async.queue)) { - send_next(lynx, hpsb_async); - } - - spin_unlock(&lynx->async.queue_lock); - - if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { - if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { - ack = (pcl.pcl_status >> 15) & 0xf; - PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); - ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); - } else { - ack = (pcl.pcl_status >> 15) & 0xf; - } - } else { - PRINT(KERN_INFO, lynx->id, "async packet was not completed"); - ack = ACKX_SEND_ERROR; - } - hpsb_packet_sent(host, packet, ack); - } - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) { - PRINTD(KERN_DEBUG, lynx->id, "iso sent"); - spin_lock(&lynx->iso_send.queue_lock); - - if (list_empty(&lynx->iso_send.pcl_queue)) { - spin_unlock(&lynx->iso_send.queue_lock); - PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet"); - } else { - struct ti_pcl pcl; - u32 ack; - struct hpsb_packet *packet; - - get_pcl(lynx, lynx->iso_send.pcl, &pcl); - - packet = driver_packet(lynx->iso_send.pcl_queue.next); - list_del_init(&packet->driver_list); - - pci_unmap_single(lynx->dev, lynx->iso_send.header_dma, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - pci_unmap_single(lynx->dev, lynx->iso_send.data_dma, - packet->data_size, PCI_DMA_TODEVICE); - } -#if 0 /* has been removed from ieee1394 core */ - if (!list_empty(&lynx->iso_send.queue)) { - send_next(lynx, hpsb_iso); - } -#endif - spin_unlock(&lynx->iso_send.queue_lock); - - if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { - if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { - ack = (pcl.pcl_status >> 15) & 0xf; - PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); - ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); - } else { - ack = (pcl.pcl_status >> 15) & 0xf; - } - } else { - PRINT(KERN_INFO, lynx->id, "iso send packet was not completed"); - ack = ACKX_SEND_ERROR; - } - - hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR - } - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) { - /* general receive DMA completed */ - int stat = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_RCV)); - - PRINTD(KERN_DEBUG, lynx->id, "received packet size %d", - stat & 0x1fff); - - if (stat & DMA_CHAN_STAT_SELFID) { - lynx->selfid_size = stat & 0x1fff; - handle_selfid(lynx, host); - } else { - quadlet_t *q_data = lynx->rcv_page; - if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE - || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) { - cpu_to_be32s(q_data + 3); - } - hpsb_packet_received(host, q_data, stat & 0x1fff, 0); - } - - run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); - } - - return IRQ_HANDLED; -} - - -static void iso_rcv_bh(struct ti_lynx *lynx) -{ - unsigned int idx; - quadlet_t *data; - unsigned long flags; - - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - - while (lynx->iso_rcv.used) { - idx = lynx->iso_rcv.last; - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); - - data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE] - + (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE; - - if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) { - PRINT(KERN_ERR, lynx->id, - "iso length mismatch 0x%08x/0x%08x", *data, - lynx->iso_rcv.stat[idx]); - } - - if (lynx->iso_rcv.stat[idx] - & (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) { - PRINT(KERN_INFO, lynx->id, - "iso receive error on %d to 0x%p", idx, data); - } else { - hpsb_packet_received(lynx->host, data, - lynx->iso_rcv.stat[idx] & 0x1fff, - 0); - } - - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - lynx->iso_rcv.last = (idx + 1) % NUM_ISORCV_PCL; - lynx->iso_rcv.used--; - } - - if (lynx->iso_rcv.chan_count) { - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), - DMA_WORD1_CMP_ENABLE_MASTER); - } - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); -} - - -static void remove_card(struct pci_dev *dev) -{ - struct ti_lynx *lynx; - struct device *lynx_dev; - int i; - - lynx = pci_get_drvdata(dev); - if (!lynx) return; - pci_set_drvdata(dev, NULL); - - lynx_dev = get_device(&lynx->host->device); - - switch (lynx->state) { - case is_host: - reg_write(lynx, PCI_INT_ENABLE, 0); - hpsb_remove_host(lynx->host); - case have_intr: - reg_write(lynx, PCI_INT_ENABLE, 0); - free_irq(lynx->dev->irq, lynx); - - /* Disable IRM Contender and LCtrl */ - if (lynx->phyic.reg_1394a) - set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4)); - - /* Let all other nodes know to ignore us */ - lynx_devctl(lynx->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); - - case have_iomappings: - reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); - /* Fix buggy cards with autoboot pin not tied low: */ - reg_write(lynx, DMA0_CHAN_CTRL, 0); - iounmap(lynx->registers); - iounmap(lynx->local_rom); - iounmap(lynx->local_ram); - iounmap(lynx->aux_port); - case have_1394_buffers: - for (i = 0; i < ISORCV_PAGES; i++) { - if (lynx->iso_rcv.page[i]) { - pci_free_consistent(lynx->dev, PAGE_SIZE, - lynx->iso_rcv.page[i], - lynx->iso_rcv.page_dma[i]); - } - } - pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, - lynx->rcv_page_dma); - case have_aux_buf: - case have_pcl_mem: - pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, - lynx->pcl_mem_dma); - case clear: - /* do nothing - already freed */ - ; - } - - tasklet_kill(&lynx->iso_rcv.tq); - - if (lynx_dev) - put_device(lynx_dev); -} - - -static int __devinit add_card(struct pci_dev *dev, - const struct pci_device_id *devid_is_unused) -{ -#define FAIL(fmt, args...) do { \ - PRINT_G(KERN_ERR, fmt , ## args); \ - remove_card(dev); \ - return error; \ - } while (0) - - char irq_buf[16]; - struct hpsb_host *host; - struct ti_lynx *lynx; /* shortcut to currently handled device */ - struct ti_pcl pcl; - u32 *pcli; - int i; - int error; - - error = -ENXIO; - - if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) - FAIL("DMA address limits not supported for PCILynx hardware"); - if (pci_enable_device(dev)) - FAIL("failed to enable PCILynx hardware"); - pci_set_master(dev); - - error = -ENOMEM; - - host = hpsb_alloc_host(&lynx_driver, sizeof(struct ti_lynx), &dev->dev); - if (!host) FAIL("failed to allocate control structure memory"); - - lynx = host->hostdata; - lynx->id = card_id++; - lynx->dev = dev; - lynx->state = clear; - lynx->host = host; - host->pdev = dev; - pci_set_drvdata(dev, lynx); - - spin_lock_init(&lynx->lock); - spin_lock_init(&lynx->phy_reg_lock); - - lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE, - &lynx->pcl_mem_dma); - - if (lynx->pcl_mem != NULL) { - lynx->state = have_pcl_mem; - PRINT(KERN_INFO, lynx->id, - "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE, - lynx->pcl_mem); - } else { - FAIL("failed to allocate PCL memory area"); - } - - lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE, - &lynx->rcv_page_dma); - if (lynx->rcv_page == NULL) { - FAIL("failed to allocate receive buffer"); - } - lynx->state = have_1394_buffers; - - for (i = 0; i < ISORCV_PAGES; i++) { - lynx->iso_rcv.page[i] = - pci_alloc_consistent(dev, PAGE_SIZE, - &lynx->iso_rcv.page_dma[i]); - if (lynx->iso_rcv.page[i] == NULL) { - FAIL("failed to allocate iso receive buffers"); - } - } - - lynx->registers = ioremap_nocache(pci_resource_start(dev,0), - PCILYNX_MAX_REGISTER); - lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); - lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE), - PCILYNX_MAX_MEMORY); - lynx->state = have_iomappings; - - if (lynx->registers == NULL) { - FAIL("failed to remap registers - card not accessible"); - } - - reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); - /* Fix buggy cards with autoboot pin not tied low: */ - reg_write(lynx, DMA0_CHAN_CTRL, 0); - - sprintf (irq_buf, "%d", dev->irq); - - if (!request_irq(dev->irq, lynx_irq_handler, IRQF_SHARED, - PCILYNX_DRIVER_NAME, lynx)) { - PRINT(KERN_INFO, lynx->id, "allocated interrupt %s", irq_buf); - lynx->state = have_intr; - } else { - FAIL("failed to allocate shared interrupt %s", irq_buf); - } - - /* alloc_pcl return values are not checked, it is expected that the - * provided PCL space is sufficient for the initial allocations */ - lynx->rcv_pcl = alloc_pcl(lynx); - lynx->rcv_pcl_start = alloc_pcl(lynx); - lynx->async.pcl = alloc_pcl(lynx); - lynx->async.pcl_start = alloc_pcl(lynx); - lynx->iso_send.pcl = alloc_pcl(lynx); - lynx->iso_send.pcl_start = alloc_pcl(lynx); - - for (i = 0; i < NUM_ISORCV_PCL; i++) { - lynx->iso_rcv.pcl[i] = alloc_pcl(lynx); - } - lynx->iso_rcv.pcl_start = alloc_pcl(lynx); - - /* all allocations successful - simple init stuff follows */ - - reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); - - tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh, - (unsigned long)lynx); - - spin_lock_init(&lynx->iso_rcv.lock); - - spin_lock_init(&lynx->async.queue_lock); - lynx->async.channel = CHANNEL_ASYNC_SEND; - spin_lock_init(&lynx->iso_send.queue_lock); - lynx->iso_send.channel = CHANNEL_ISO_SEND; - - PRINT(KERN_INFO, lynx->id, "remapped memory spaces reg 0x%p, rom 0x%p, " - "ram 0x%p, aux 0x%p", lynx->registers, lynx->local_rom, - lynx->local_ram, lynx->aux_port); - - /* now, looking for PHY register set */ - if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { - lynx->phyic.reg_1394a = 1; - PRINT(KERN_INFO, lynx->id, - "found 1394a conform PHY (using extended register set)"); - lynx->phyic.vendor = get_phy_vendorid(lynx); - lynx->phyic.product = get_phy_productid(lynx); - } else { - lynx->phyic.reg_1394a = 0; - PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); - } - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - - INIT_LIST_HEAD(&lynx->async.queue); - INIT_LIST_HEAD(&lynx->async.pcl_queue); - INIT_LIST_HEAD(&lynx->iso_send.queue); - INIT_LIST_HEAD(&lynx->iso_send.pcl_queue); - - pcl.next = pcl_bus(lynx, lynx->rcv_pcl); - put_pcl(lynx, lynx->rcv_pcl_start, &pcl); - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - - pcl.buffer[0].control = PCL_CMD_RCV | 16; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[1].control = PCL_LAST_BUFF | 4080; - - pcl.buffer[0].pointer = lynx->rcv_page_dma; - pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; - put_pcl(lynx, lynx->rcv_pcl, &pcl); - - pcl.next = pcl_bus(lynx, lynx->async.pcl); - pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); - put_pcl(lynx, lynx->async.pcl_start, &pcl); - - pcl.next = pcl_bus(lynx, lynx->iso_send.pcl); - pcl.async_error_next = PCL_NEXT_INVALID; - put_pcl(lynx, lynx->iso_send.pcl_start, &pcl); - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - pcl.buffer[0].control = PCL_CMD_RCV | 4; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[1].control = PCL_LAST_BUFF | 2044; - - for (i = 0; i < NUM_ISORCV_PCL; i++) { - int page = i / ISORCV_PER_PAGE; - int sec = i % ISORCV_PER_PAGE; - - pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] - + sec * MAX_ISORCV_SIZE; - pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; - put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); - } - - pcli = (u32 *)&pcl; - for (i = 0; i < NUM_ISORCV_PCL; i++) { - pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]); - } - put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl); - - /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */ - reg_write(lynx, FIFO_SIZES, 0x003030a0); - /* 20 byte threshold before triggering PCI transfer */ - reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24); - /* threshold on both send FIFOs before transmitting: - FIFO size - cache line size - 1 */ - i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff; - i = 0x30 - i - 1; - reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i); - - reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394); - - reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT - | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET - | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK - | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC - | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW - | LINK_INT_ATF_UNDERFLOW); - - reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); - reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4); - reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV), - DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST - | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST - | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER); - - run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); - - reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0); - reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4); - reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); - - run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV); - - reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID - | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN - | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN - | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX); - - if (!lynx->phyic.reg_1394a) { - if (!hpsb_disable_irm) { - /* attempt to enable contender bit -FIXME- would this - * work elsewhere? */ - reg_set_bits(lynx, GPIO_CTRL_A, 0x1); - reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); - } - } else { - /* set the contender (if appropriate) and LCtrl bit in the - * extended PHY register set. (Should check that PHY_02_EXTENDED - * is set in register 2?) - */ - i = get_phy_reg(lynx, 4); - i |= PHY_04_LCTRL; - if (hpsb_disable_irm) - i &= ~PHY_04_CONTENDER; - else - i |= PHY_04_CONTENDER; - if (i != -1) set_phy_reg(lynx, 4, i); - } - - if (!skip_eeprom) - { - /* needed for i2c communication with serial eeprom */ - struct i2c_adapter *i2c_ad; - struct i2c_algo_bit_data i2c_adapter_data; - - error = -ENOMEM; - i2c_ad = kzalloc(sizeof(*i2c_ad), GFP_KERNEL); - if (!i2c_ad) FAIL("failed to allocate I2C adapter memory"); - - strlcpy(i2c_ad->name, "PCILynx I2C", sizeof(i2c_ad->name)); - i2c_adapter_data = bit_data; - i2c_ad->algo_data = &i2c_adapter_data; - i2c_adapter_data.data = lynx; - i2c_ad->dev.parent = &dev->dev; - - PRINTD(KERN_DEBUG, lynx->id,"original eeprom control: %d", - reg_read(lynx, SERIAL_EEPROM_CONTROL)); - - /* reset hardware to sane state */ - lynx->i2c_driven_state = 0x00000070; - reg_write(lynx, SERIAL_EEPROM_CONTROL, lynx->i2c_driven_state); - - if (i2c_bit_add_bus(i2c_ad) < 0) - { - kfree(i2c_ad); - error = -ENXIO; - FAIL("unable to register i2c"); - } - else - { - /* do i2c stuff */ - unsigned char i2c_cmd = 0x10; - struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd }, - { 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block } - }; - - /* we use i2c_transfer because we have no i2c_client - at hand */ - if (i2c_transfer(i2c_ad, msg, 2) < 0) { - PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c"); - } else { - PRINT(KERN_INFO, lynx->id, "got bus info block from serial eeprom"); - /* FIXME: probably we should rewrite the max_rec, max_ROM(1394a), - * generation(1394a) and link_spd(1394a) field and recalculate - * the CRC */ - - for (i = 0; i < 5 ; i++) - PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x", - i, be32_to_cpu(lynx->bus_info_block[i])); - - /* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */ - if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) && - (lynx->bus_info_block[1] == IEEE1394_BUSID_MAGIC)) - { - PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from"); - } else { - kfree(i2c_ad); - error = -ENXIO; - FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block"); - } - - } - - i2c_del_adapter(i2c_ad); - kfree(i2c_ad); - } - } - - host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]); - host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]); - host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff; - host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf; - if (!lynx->phyic.reg_1394a) - host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6; - else - host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7; - - if (hpsb_add_host(host)) { - error = -ENOMEM; - FAIL("Failed to register host with highlevel"); - } - - lynx->state = is_host; - - return 0; -#undef FAIL -} - - -static struct pci_device_id pci_table[] = { - { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_PCILYNX, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { } /* Terminating entry */ -}; - -static struct pci_driver lynx_pci_driver = { - .name = PCILYNX_DRIVER_NAME, - .id_table = pci_table, - .probe = add_card, - .remove = remove_card, -}; - -static struct hpsb_host_driver lynx_driver = { - .owner = THIS_MODULE, - .name = PCILYNX_DRIVER_NAME, - .set_hw_config_rom = NULL, - .transmit_packet = lynx_transmit, - .devctl = lynx_devctl, - .isoctl = NULL, -}; - -MODULE_AUTHOR("Andreas E. Bombe <andreas.bombe@munich.netsurf.de>"); -MODULE_DESCRIPTION("driver for Texas Instruments PCI Lynx IEEE-1394 controller"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("pcilynx"); -MODULE_DEVICE_TABLE(pci, pci_table); - -static int __init pcilynx_init(void) -{ - int ret; - - ret = pci_register_driver(&lynx_pci_driver); - if (ret < 0) { - PRINT_G(KERN_ERR, "PCI module init failed"); - return ret; - } - - return 0; -} - -static void __exit pcilynx_cleanup(void) -{ - pci_unregister_driver(&lynx_pci_driver); -} - - -module_init(pcilynx_init); -module_exit(pcilynx_cleanup); diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h deleted file mode 100644 index 693a169acea..00000000000 --- a/drivers/ieee1394/pcilynx.h +++ /dev/null @@ -1,468 +0,0 @@ -#ifndef __PCILYNX_H__ -#define __PCILYNX_H__ - - -#define PCILYNX_DRIVER_NAME "pcilynx" -#define PCILYNX_MAJOR 177 - -#define PCILYNX_MINOR_AUX_START 0 -#define PCILYNX_MINOR_ROM_START 16 -#define PCILYNX_MINOR_RAM_START 32 - -#define PCILYNX_MAX_REGISTER 0xfff -#define PCILYNX_MAX_MEMORY 0xffff - -#define PCI_DEVICE_ID_TI_PCILYNX 0x8000 -#define MAX_PCILYNX_CARDS 4 -#define LOCALRAM_SIZE 4096 - -#define NUM_ISORCV_PCL 4 -#define MAX_ISORCV_SIZE 2048 -#define ISORCV_PER_PAGE (PAGE_SIZE / MAX_ISORCV_SIZE) -#define ISORCV_PAGES (NUM_ISORCV_PCL / ISORCV_PER_PAGE) - -#define CHANNEL_LOCALBUS 0 -#define CHANNEL_ASYNC_RCV 1 -#define CHANNEL_ISO_RCV 2 -#define CHANNEL_ASYNC_SEND 3 -#define CHANNEL_ISO_SEND 4 - -#define PCILYNX_CONFIG_ROM_LENGTH 1024 - -typedef int pcl_t; - -struct ti_lynx { - int id; /* sequential card number */ - - spinlock_t lock; - - struct pci_dev *dev; - - struct { - unsigned reg_1394a:1; - u32 vendor; - u32 product; - } phyic; - - enum { clear, have_intr, have_aux_buf, have_pcl_mem, - have_1394_buffers, have_iomappings, is_host } state; - - /* remapped memory spaces */ - void __iomem *registers; - void __iomem *local_rom; - void __iomem *local_ram; - void __iomem *aux_port; - __be32 bus_info_block[5]; - - /* - * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for - * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes); - * the following is an allocation bitmap - */ - u8 pcl_bmap[LOCALRAM_SIZE / 1024]; - - /* point to PCLs memory area if needed */ - void *pcl_mem; - dma_addr_t pcl_mem_dma; - - /* PCLs for local mem / aux transfers */ - pcl_t dmem_pcl; - - /* IEEE-1394 part follows */ - struct hpsb_host *host; - - int phyid, isroot; - int selfid_size; - int phy_reg0; - - spinlock_t phy_reg_lock; - - pcl_t rcv_pcl_start, rcv_pcl; - void *rcv_page; - dma_addr_t rcv_page_dma; - int rcv_active; - - struct lynx_send_data { - pcl_t pcl_start, pcl; - struct list_head queue; - struct list_head pcl_queue; /* this queue contains at most one packet */ - spinlock_t queue_lock; - dma_addr_t header_dma, data_dma; - int channel; - } async, iso_send; - - struct { - pcl_t pcl[NUM_ISORCV_PCL]; - u32 stat[NUM_ISORCV_PCL]; - void *page[ISORCV_PAGES]; - dma_addr_t page_dma[ISORCV_PAGES]; - pcl_t pcl_start; - int chan_count; - int next, last, used, running; - struct tasklet_struct tq; - spinlock_t lock; - } iso_rcv; - - u32 i2c_driven_state; /* the state we currently drive the Serial EEPROM Control register */ -}; - -/* the per-file data structure for mem space access */ -struct memdata { - struct ti_lynx *lynx; - int cid; - atomic_t aux_intr_last_seen; - /* enum values are the same as LBUS_ADDR_SEL_* values below */ - enum { rom = 0x10000, aux = 0x20000, ram = 0 } type; -}; - - - -/* - * Register read and write helper functions. - */ -static inline void reg_write(const struct ti_lynx *lynx, int offset, u32 data) -{ - writel(data, lynx->registers + offset); -} - -static inline u32 reg_read(const struct ti_lynx *lynx, int offset) -{ - return readl(lynx->registers + offset); -} - -static inline void reg_set_bits(const struct ti_lynx *lynx, int offset, - u32 mask) -{ - reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); -} - -static inline void reg_clear_bits(const struct ti_lynx *lynx, int offset, - u32 mask) -{ - reg_write(lynx, offset, (reg_read(lynx, offset) & ~mask)); -} - - - -/* chip register definitions follow */ - -#define PCI_LATENCY_CACHELINE 0x0c - -#define MISC_CONTROL 0x40 -#define MISC_CONTROL_SWRESET (1<<0) - -#define SERIAL_EEPROM_CONTROL 0x44 - -#define PCI_INT_STATUS 0x48 -#define PCI_INT_ENABLE 0x4c -/* status and enable have identical bit numbers */ -#define PCI_INT_INT_PEND (1<<31) -#define PCI_INT_FORCED_INT (1<<30) -#define PCI_INT_SLV_ADR_PERR (1<<28) -#define PCI_INT_SLV_DAT_PERR (1<<27) -#define PCI_INT_MST_DAT_PERR (1<<26) -#define PCI_INT_MST_DEV_TIMEOUT (1<<25) -#define PCI_INT_INTERNAL_SLV_TIMEOUT (1<<23) -#define PCI_INT_AUX_TIMEOUT (1<<18) -#define PCI_INT_AUX_INT (1<<17) -#define PCI_INT_1394 (1<<16) -#define PCI_INT_DMA4_PCL (1<<9) -#define PCI_INT_DMA4_HLT (1<<8) -#define PCI_INT_DMA3_PCL (1<<7) -#define PCI_INT_DMA3_HLT (1<<6) -#define PCI_INT_DMA2_PCL (1<<5) -#define PCI_INT_DMA2_HLT (1<<4) -#define PCI_INT_DMA1_PCL (1<<3) -#define PCI_INT_DMA1_HLT (1<<2) -#define PCI_INT_DMA0_PCL (1<<1) -#define PCI_INT_DMA0_HLT (1<<0) -/* all DMA interrupts combined: */ -#define PCI_INT_DMA_ALL 0x3ff - -#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2)) -#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1)) - -#define LBUS_ADDR 0xb4 -#define LBUS_ADDR_SEL_RAM (0x0<<16) -#define LBUS_ADDR_SEL_ROM (0x1<<16) -#define LBUS_ADDR_SEL_AUX (0x2<<16) -#define LBUS_ADDR_SEL_ZV (0x3<<16) - -#define GPIO_CTRL_A 0xb8 -#define GPIO_CTRL_B 0xbc -#define GPIO_DATA_BASE 0xc0 - -#define DMA_BREG(base, chan) (base + chan * 0x20) -#define DMA_SREG(base, chan) (base + chan * 0x10) - -#define DMA0_PREV_PCL 0x100 -#define DMA1_PREV_PCL 0x120 -#define DMA2_PREV_PCL 0x140 -#define DMA3_PREV_PCL 0x160 -#define DMA4_PREV_PCL 0x180 -#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan)) - -#define DMA0_CURRENT_PCL 0x104 -#define DMA1_CURRENT_PCL 0x124 -#define DMA2_CURRENT_PCL 0x144 -#define DMA3_CURRENT_PCL 0x164 -#define DMA4_CURRENT_PCL 0x184 -#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan)) - -#define DMA0_CHAN_STAT 0x10c -#define DMA1_CHAN_STAT 0x12c -#define DMA2_CHAN_STAT 0x14c -#define DMA3_CHAN_STAT 0x16c -#define DMA4_CHAN_STAT 0x18c -#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan)) -/* CHAN_STATUS registers share bits */ -#define DMA_CHAN_STAT_SELFID (1<<31) -#define DMA_CHAN_STAT_ISOPKT (1<<30) -#define DMA_CHAN_STAT_PCIERR (1<<29) -#define DMA_CHAN_STAT_PKTERR (1<<28) -#define DMA_CHAN_STAT_PKTCMPL (1<<27) -#define DMA_CHAN_STAT_SPECIALACK (1<<14) - - -#define DMA0_CHAN_CTRL 0x110 -#define DMA1_CHAN_CTRL 0x130 -#define DMA2_CHAN_CTRL 0x150 -#define DMA3_CHAN_CTRL 0x170 -#define DMA4_CHAN_CTRL 0x190 -#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan)) -/* CHAN_CTRL registers share bits */ -#define DMA_CHAN_CTRL_ENABLE (1<<31) -#define DMA_CHAN_CTRL_BUSY (1<<30) -#define DMA_CHAN_CTRL_LINK (1<<29) - -#define DMA0_READY 0x114 -#define DMA1_READY 0x134 -#define DMA2_READY 0x154 -#define DMA3_READY 0x174 -#define DMA4_READY 0x194 -#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan)) - -#define DMA_GLOBAL_REGISTER 0x908 - -#define FIFO_SIZES 0xa00 - -#define FIFO_CONTROL 0xa10 -#define FIFO_CONTROL_GRF_FLUSH (1<<4) -#define FIFO_CONTROL_ITF_FLUSH (1<<3) -#define FIFO_CONTROL_ATF_FLUSH (1<<2) - -#define FIFO_XMIT_THRESHOLD 0xa14 - -#define DMA0_WORD0_CMP_VALUE 0xb00 -#define DMA1_WORD0_CMP_VALUE 0xb10 -#define DMA2_WORD0_CMP_VALUE 0xb20 -#define DMA3_WORD0_CMP_VALUE 0xb30 -#define DMA4_WORD0_CMP_VALUE 0xb40 -#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) - -#define DMA0_WORD0_CMP_ENABLE 0xb04 -#define DMA1_WORD0_CMP_ENABLE 0xb14 -#define DMA2_WORD0_CMP_ENABLE 0xb24 -#define DMA3_WORD0_CMP_ENABLE 0xb34 -#define DMA4_WORD0_CMP_ENABLE 0xb44 -#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan)) - -#define DMA0_WORD1_CMP_VALUE 0xb08 -#define DMA1_WORD1_CMP_VALUE 0xb18 -#define DMA2_WORD1_CMP_VALUE 0xb28 -#define DMA3_WORD1_CMP_VALUE 0xb38 -#define DMA4_WORD1_CMP_VALUE 0xb48 -#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) - -#define DMA0_WORD1_CMP_ENABLE 0xb0c -#define DMA1_WORD1_CMP_ENABLE 0xb1c -#define DMA2_WORD1_CMP_ENABLE 0xb2c -#define DMA3_WORD1_CMP_ENABLE 0xb3c -#define DMA4_WORD1_CMP_ENABLE 0xb4c -#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan)) -/* word 1 compare enable flags */ -#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15) -#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14) -#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13) -#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12) -#define DMA_WORD1_CMP_MATCH_EXACT (1<<11) -#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10) -#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8) - -#define LINK_ID 0xf00 -#define LINK_ID_BUS(id) (id<<22) -#define LINK_ID_NODE(id) (id<<16) - -#define LINK_CONTROL 0xf04 -#define LINK_CONTROL_BUSY (1<<29) -#define LINK_CONTROL_TX_ISO_EN (1<<26) -#define LINK_CONTROL_RX_ISO_EN (1<<25) -#define LINK_CONTROL_TX_ASYNC_EN (1<<24) -#define LINK_CONTROL_RX_ASYNC_EN (1<<23) -#define LINK_CONTROL_RESET_TX (1<<21) -#define LINK_CONTROL_RESET_RX (1<<20) -#define LINK_CONTROL_CYCMASTER (1<<11) -#define LINK_CONTROL_CYCSOURCE (1<<10) -#define LINK_CONTROL_CYCTIMEREN (1<<9) -#define LINK_CONTROL_RCV_CMP_VALID (1<<7) -#define LINK_CONTROL_SNOOP_ENABLE (1<<6) - -#define CYCLE_TIMER 0xf08 - -#define LINK_PHY 0xf0c -#define LINK_PHY_READ (1<<31) -#define LINK_PHY_WRITE (1<<30) -#define LINK_PHY_ADDR(addr) (addr<<24) -#define LINK_PHY_WDATA(data) (data<<16) -#define LINK_PHY_RADDR(addr) (addr<<8) - - -#define LINK_INT_STATUS 0xf14 -#define LINK_INT_ENABLE 0xf18 -/* status and enable have identical bit numbers */ -#define LINK_INT_LINK_INT (1<<31) -#define LINK_INT_PHY_TIMEOUT (1<<30) -#define LINK_INT_PHY_REG_RCVD (1<<29) -#define LINK_INT_PHY_BUSRESET (1<<28) -#define LINK_INT_TX_RDY (1<<26) -#define LINK_INT_RX_DATA_RDY (1<<25) -#define LINK_INT_ISO_STUCK (1<<20) -#define LINK_INT_ASYNC_STUCK (1<<19) -#define LINK_INT_SENT_REJECT (1<<17) -#define LINK_INT_HDR_ERR (1<<16) -#define LINK_INT_TX_INVALID_TC (1<<15) -#define LINK_INT_CYC_SECOND (1<<11) -#define LINK_INT_CYC_START (1<<10) -#define LINK_INT_CYC_DONE (1<<9) -#define LINK_INT_CYC_PENDING (1<<8) -#define LINK_INT_CYC_LOST (1<<7) -#define LINK_INT_CYC_ARB_FAILED (1<<6) -#define LINK_INT_GRF_OVERFLOW (1<<5) -#define LINK_INT_ITF_UNDERFLOW (1<<4) -#define LINK_INT_ATF_UNDERFLOW (1<<3) -#define LINK_INT_ISOARB_FAILED (1<<0) - -/* PHY specifics */ -#define PHY_VENDORID_TI 0x800028 -#define PHY_PRODUCTID_TSB41LV03 0x000000 - - -/* this is the physical layout of a PCL, its size is 128 bytes */ -struct ti_pcl { - u32 next; - u32 async_error_next; - u32 user_data; - u32 pcl_status; - u32 remaining_transfer_count; - u32 next_data_buffer; - struct { - u32 control; - u32 pointer; - } buffer[13] __attribute__ ((packed)); -} __attribute__ ((packed)); - -#include <linux/stddef.h> -#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER)) - - -static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid, - const struct ti_pcl *pcl) -{ - memcpy_le32((u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)), - (u32 *)pcl, sizeof(struct ti_pcl)); -} - -static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, - struct ti_pcl *pcl) -{ - memcpy_le32((u32 *)pcl, - (u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)), - sizeof(struct ti_pcl)); -} - -static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) -{ - return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl); -} - - -#if defined (__BIG_ENDIAN) -typedef struct ti_pcl pcltmp_t; - -static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ - get_pcl(lynx, pclid, tmp); - return tmp; -} - -static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ - put_pcl(lynx, pclid, tmp); -} - -#else -typedef int pcltmp_t; /* just a dummy */ - -static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ - return lynx->pcl_mem + pclid * sizeof(struct ti_pcl); -} - -static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ -} -#endif - - -static inline void run_sub_pcl(const struct ti_lynx *lynx, pcl_t pclid, int idx, - int dmachan) -{ - reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, - pcl_bus(lynx, pclid) + idx * 4); - reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, - DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); -} - -static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan) -{ - run_sub_pcl(lynx, pclid, 0, dmachan); -} - -#define PCL_NEXT_INVALID (1<<0) - -/* transfer commands */ -#define PCL_CMD_RCV (0x1<<24) -#define PCL_CMD_RCV_AND_UPDATE (0xa<<24) -#define PCL_CMD_XMT (0x2<<24) -#define PCL_CMD_UNFXMT (0xc<<24) -#define PCL_CMD_PCI_TO_LBUS (0x8<<24) -#define PCL_CMD_LBUS_TO_PCI (0x9<<24) - -/* aux commands */ -#define PCL_CMD_NOP (0x0<<24) -#define PCL_CMD_LOAD (0x3<<24) -#define PCL_CMD_STOREQ (0x4<<24) -#define PCL_CMD_STORED (0xb<<24) -#define PCL_CMD_STORE0 (0x5<<24) -#define PCL_CMD_STORE1 (0x6<<24) -#define PCL_CMD_COMPARE (0xe<<24) -#define PCL_CMD_SWAP_COMPARE (0xf<<24) -#define PCL_CMD_ADD (0xd<<24) -#define PCL_CMD_BRANCH (0x7<<24) - -/* BRANCH condition codes */ -#define PCL_COND_DMARDY_SET (0x1<<20) -#define PCL_COND_DMARDY_CLEAR (0x2<<20) - -#define PCL_GEN_INTR (1<<19) -#define PCL_LAST_BUFF (1<<18) -#define PCL_LAST_CMD (PCL_LAST_BUFF) -#define PCL_WAITSTAT (1<<17) -#define PCL_BIGENDIAN (1<<16) -#define PCL_ISOMODE (1<<12) - -#endif diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h deleted file mode 100644 index 7a225a40598..00000000000 --- a/drivers/ieee1394/raw1394-private.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef IEEE1394_RAW1394_PRIVATE_H -#define IEEE1394_RAW1394_PRIVATE_H - -/* header for definitions that are private to the raw1394 driver - and not visible to user-space */ - -#define RAW1394_DEVICE_MAJOR 171 -#define RAW1394_DEVICE_NAME "raw1394" - -#define RAW1394_MAX_USER_CSR_DIRS 16 - -struct iso_block_store { - atomic_t refcount; - size_t data_size; - quadlet_t data[0]; -}; - -enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0, - RAW1394_ISO_RECV = 1, - RAW1394_ISO_XMIT = 2 }; - -struct file_info { - struct list_head list; - - struct mutex state_mutex; - enum { opened, initialized, connected } state; - unsigned int protocol_version; - - struct hpsb_host *host; - - struct list_head req_pending; /* protected by reqlists_lock */ - struct list_head req_complete; /* protected by reqlists_lock */ - spinlock_t reqlists_lock; - wait_queue_head_t wait_complete; - - struct list_head addr_list; /* protected by host_info_lock */ - - u8 __user *fcp_buffer; - - u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */ - - /* new rawiso API */ - enum raw1394_iso_state iso_state; - struct hpsb_iso *iso_handle; - - /* User space's CSR1212 dynamic ConfigROM directories */ - struct csr1212_keyval *csr1212_dirs[RAW1394_MAX_USER_CSR_DIRS]; - - /* Legacy ConfigROM update flag */ - u8 cfgrom_upd; -}; - -struct arm_addr { - struct list_head addr_list; /* file_info list */ - u64 start, end; - u64 arm_tag; - u8 access_rights; - u8 notification_options; - u8 client_transactions; - u64 recvb; - u16 rec_length; - u8 *addr_space_buffer; /* accessed by read/write/lock requests */ -}; - -struct pending_request { - struct list_head list; - struct file_info *file_info; - struct hpsb_packet *packet; - struct iso_block_store *ibs; - quadlet_t *data; - int free_data; - struct raw1394_request req; -}; - -struct host_info { - struct list_head list; - struct hpsb_host *host; - struct list_head file_info_list; /* protected by host_info_lock */ -}; - -#endif /* IEEE1394_RAW1394_PRIVATE_H */ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c deleted file mode 100644 index f3401427404..00000000000 --- a/drivers/ieee1394/raw1394.c +++ /dev/null @@ -1,3096 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Raw interface to the bus - * - * Copyright (C) 1999, 2000 Andreas E. Bombe - * 2001, 2002 Manfred Weihs <weihs@ict.tuwien.ac.at> - * 2002 Christian Toegel <christian.toegel@gmx.at> - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Manfred Weihs <weihs@ict.tuwien.ac.at> - * configuration ROM manipulation - * address range mapping - * adaptation for new (transparent) loopback mechanism - * sending of arbitrary async packets - * Christian Toegel <christian.toegel@gmx.at> - * address range mapping - * lock64 request - * transmit physical packet - * busreset notification control (switch on/off) - * busreset with selection of type (short/long) - * request_reply - */ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/vmalloc.h> -#include <linux/cdev.h> -#include <asm/uaccess.h> -#include <asm/atomic.h> -#include <linux/compat.h> - -#include "csr1212.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" -#include "iso.h" -#include "nodemgr.h" -#include "raw1394.h" -#include "raw1394-private.h" - -#define int2ptr(x) ((void __user *)(unsigned long)x) -#define ptr2int(x) ((u64)(unsigned long)(void __user *)x) - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define RAW1394_DEBUG -#endif - -#ifdef RAW1394_DEBUG -#define DBGMSG(fmt, args...) \ -printk(KERN_INFO "raw1394:" fmt "\n" , ## args) -#else -#define DBGMSG(fmt, args...) do {} while (0) -#endif - -static LIST_HEAD(host_info_list); -static int host_count; -static DEFINE_SPINLOCK(host_info_lock); -static atomic_t internal_generation = ATOMIC_INIT(0); - -static atomic_t iso_buffer_size; -static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */ - -static struct hpsb_highlevel raw1394_highlevel; - -static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, - u64 addr, size_t length, u16 flags); -static int arm_write(struct hpsb_host *host, int nodeid, int destid, - quadlet_t * data, u64 addr, size_t length, u16 flags); -static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags); -static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags); -static const struct hpsb_address_ops arm_ops = { - .read = arm_read, - .write = arm_write, - .lock = arm_lock, - .lock64 = arm_lock64, -}; - -static void queue_complete_cb(struct pending_request *req); - -static struct pending_request *__alloc_pending_request(gfp_t flags) -{ - struct pending_request *req; - - req = kzalloc(sizeof(*req), flags); - if (req) - INIT_LIST_HEAD(&req->list); - - return req; -} - -static inline struct pending_request *alloc_pending_request(void) -{ - return __alloc_pending_request(GFP_KERNEL); -} - -static void free_pending_request(struct pending_request *req) -{ - if (req->ibs) { - if (atomic_dec_and_test(&req->ibs->refcount)) { - atomic_sub(req->ibs->data_size, &iso_buffer_size); - kfree(req->ibs); - } - } else if (req->free_data) { - kfree(req->data); - } - hpsb_free_packet(req->packet); - kfree(req); -} - -/* fi->reqlists_lock must be taken */ -static void __queue_complete_req(struct pending_request *req) -{ - struct file_info *fi = req->file_info; - - list_move_tail(&req->list, &fi->req_complete); - wake_up(&fi->wait_complete); -} - -static void queue_complete_req(struct pending_request *req) -{ - unsigned long flags; - struct file_info *fi = req->file_info; - - spin_lock_irqsave(&fi->reqlists_lock, flags); - __queue_complete_req(req); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); -} - -static void queue_complete_cb(struct pending_request *req) -{ - struct hpsb_packet *packet = req->packet; - int rcode = (packet->header[1] >> 12) & 0xf; - - switch (packet->ack_code) { - case ACKX_NONE: - case ACKX_SEND_ERROR: - req->req.error = RAW1394_ERROR_SEND_ERROR; - break; - case ACKX_ABORTED: - req->req.error = RAW1394_ERROR_ABORTED; - break; - case ACKX_TIMEOUT: - req->req.error = RAW1394_ERROR_TIMEOUT; - break; - default: - req->req.error = (packet->ack_code << 16) | rcode; - break; - } - - if (!((packet->ack_code == ACK_PENDING) && (rcode == RCODE_COMPLETE))) { - req->req.length = 0; - } - - if ((req->req.type == RAW1394_REQ_ASYNC_READ) || - (req->req.type == RAW1394_REQ_ASYNC_WRITE) || - (req->req.type == RAW1394_REQ_ASYNC_STREAM) || - (req->req.type == RAW1394_REQ_LOCK) || - (req->req.type == RAW1394_REQ_LOCK64)) - hpsb_free_tlabel(packet); - - queue_complete_req(req); -} - -static void add_host(struct hpsb_host *host) -{ - struct host_info *hi; - unsigned long flags; - - hi = kmalloc(sizeof(*hi), GFP_KERNEL); - - if (hi) { - INIT_LIST_HEAD(&hi->list); - hi->host = host; - INIT_LIST_HEAD(&hi->file_info_list); - - spin_lock_irqsave(&host_info_lock, flags); - list_add_tail(&hi->list, &host_info_list); - host_count++; - spin_unlock_irqrestore(&host_info_lock, flags); - } - - atomic_inc(&internal_generation); -} - -static struct host_info *find_host_info(struct hpsb_host *host) -{ - struct host_info *hi; - - list_for_each_entry(hi, &host_info_list, list) - if (hi->host == host) - return hi; - - return NULL; -} - -static void remove_host(struct hpsb_host *host) -{ - struct host_info *hi; - unsigned long flags; - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(host); - - if (hi != NULL) { - list_del(&hi->list); - host_count--; - /* - FIXME: address ranges should be removed - and fileinfo states should be initialized - (including setting generation to - internal-generation ...) - */ - } - spin_unlock_irqrestore(&host_info_lock, flags); - - if (hi == NULL) { - printk(KERN_ERR "raw1394: attempt to remove unknown host " - "0x%p\n", host); - return; - } - - kfree(hi); - - atomic_inc(&internal_generation); -} - -static void host_reset(struct hpsb_host *host) -{ - unsigned long flags; - struct host_info *hi; - struct file_info *fi; - struct pending_request *req; - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(host); - - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - if (fi->notification == RAW1394_NOTIFY_ON) { - req = __alloc_pending_request(GFP_ATOMIC); - - if (req != NULL) { - req->file_info = fi; - req->req.type = RAW1394_REQ_BUS_RESET; - req->req.generation = - get_hpsb_generation(host); - req->req.misc = (host->node_id << 16) - | host->node_count; - if (fi->protocol_version > 3) { - req->req.misc |= - (NODEID_TO_NODE - (host->irm_id) - << 8); - } - - queue_complete_req(req); - } - } - } - } - spin_unlock_irqrestore(&host_info_lock, flags); -} - -static void fcp_request(struct hpsb_host *host, int nodeid, int direction, - int cts, u8 * data, size_t length) -{ - unsigned long flags; - struct host_info *hi; - struct file_info *fi; - struct pending_request *req, *req_next; - struct iso_block_store *ibs = NULL; - LIST_HEAD(reqs); - - if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) { - HPSB_INFO("dropped fcp request"); - return; - } - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(host); - - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - if (!fi->fcp_buffer) - continue; - - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) - break; - - if (!ibs) { - ibs = kmalloc(sizeof(*ibs) + length, - GFP_ATOMIC); - if (!ibs) { - kfree(req); - break; - } - - atomic_add(length, &iso_buffer_size); - atomic_set(&ibs->refcount, 0); - ibs->data_size = length; - memcpy(ibs->data, data, length); - } - - atomic_inc(&ibs->refcount); - - req->file_info = fi; - req->ibs = ibs; - req->data = ibs->data; - req->req.type = RAW1394_REQ_FCP_REQUEST; - req->req.generation = get_hpsb_generation(host); - req->req.misc = nodeid | (direction << 16); - req->req.recvb = ptr2int(fi->fcp_buffer); - req->req.length = length; - - list_add_tail(&req->list, &reqs); - } - } - spin_unlock_irqrestore(&host_info_lock, flags); - - list_for_each_entry_safe(req, req_next, &reqs, list) - queue_complete_req(req); -} - -#ifdef CONFIG_COMPAT -struct compat_raw1394_req { - __u32 type; - __s32 error; - __u32 misc; - - __u32 generation; - __u32 length; - - __u64 address; - - __u64 tag; - - __u64 sendb; - __u64 recvb; -} -#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) -__attribute__((packed)) -#endif -; - -static const char __user *raw1394_compat_write(const char __user *buf) -{ - struct compat_raw1394_req __user *cr = (typeof(cr)) buf; - struct raw1394_request __user *r; - - r = compat_alloc_user_space(sizeof(struct raw1394_request)); - -#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x)) - - if (copy_in_user(r, cr, sizeof(struct compat_raw1394_req)) || - C(address) || - C(tag) || - C(sendb) || - C(recvb)) - return (__force const char __user *)ERR_PTR(-EFAULT); - - return (const char __user *)r; -} -#undef C - -#define P(x) __put_user(r->x, &cr->x) - -static int -raw1394_compat_read(const char __user *buf, struct raw1394_request *r) -{ - struct compat_raw1394_req __user *cr = (typeof(cr)) buf; - - if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) || - P(type) || - P(error) || - P(misc) || - P(generation) || - P(length) || - P(address) || - P(tag) || - P(sendb) || - P(recvb)) - return -EFAULT; - - return sizeof(struct compat_raw1394_req); -} -#undef P - -#endif - -/* get next completed request (caller must hold fi->reqlists_lock) */ -static inline struct pending_request *__next_complete_req(struct file_info *fi) -{ - struct list_head *lh; - struct pending_request *req = NULL; - - if (!list_empty(&fi->req_complete)) { - lh = fi->req_complete.next; - list_del(lh); - req = list_entry(lh, struct pending_request, list); - } - return req; -} - -/* atomically get next completed request */ -static struct pending_request *next_complete_req(struct file_info *fi) -{ - unsigned long flags; - struct pending_request *req; - - spin_lock_irqsave(&fi->reqlists_lock, flags); - req = __next_complete_req(fi); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - return req; -} - -static ssize_t raw1394_read(struct file *file, char __user * buffer, - size_t count, loff_t * offset_is_ignored) -{ - struct file_info *fi = file->private_data; - struct pending_request *req; - ssize_t ret; - -#ifdef CONFIG_COMPAT - if (count == sizeof(struct compat_raw1394_req)) { - /* ok */ - } else -#endif - if (count != sizeof(struct raw1394_request)) { - return -EINVAL; - } - - if (!access_ok(VERIFY_WRITE, buffer, count)) { - return -EFAULT; - } - - if (file->f_flags & O_NONBLOCK) { - if (!(req = next_complete_req(fi))) - return -EAGAIN; - } else { - /* - * NB: We call the macro wait_event_interruptible() with a - * condition argument with side effect. This is only possible - * because the side effect does not occur until the condition - * became true, and wait_event_interruptible() won't evaluate - * the condition again after that. - */ - if (wait_event_interruptible(fi->wait_complete, - (req = next_complete_req(fi)))) - return -ERESTARTSYS; - } - - if (req->req.length) { - if (copy_to_user(int2ptr(req->req.recvb), req->data, - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - } - } - -#ifdef CONFIG_COMPAT - if (count == sizeof(struct compat_raw1394_req) && - sizeof(struct compat_raw1394_req) != - sizeof(struct raw1394_request)) { - ret = raw1394_compat_read(buffer, &req->req); - } else -#endif - { - if (copy_to_user(buffer, &req->req, sizeof(req->req))) { - ret = -EFAULT; - goto out; - } - ret = (ssize_t) sizeof(struct raw1394_request); - } - out: - free_pending_request(req); - return ret; -} - -static int state_opened(struct file_info *fi, struct pending_request *req) -{ - if (req->req.type == RAW1394_REQ_INITIALIZE) { - switch (req->req.misc) { - case RAW1394_KERNELAPI_VERSION: - case 3: - fi->state = initialized; - fi->protocol_version = req->req.misc; - req->req.error = RAW1394_ERROR_NONE; - req->req.generation = atomic_read(&internal_generation); - break; - - default: - req->req.error = RAW1394_ERROR_COMPAT; - req->req.misc = RAW1394_KERNELAPI_VERSION; - } - } else { - req->req.error = RAW1394_ERROR_STATE_ORDER; - } - - req->req.length = 0; - queue_complete_req(req); - return 0; -} - -static int state_initialized(struct file_info *fi, struct pending_request *req) -{ - unsigned long flags; - struct host_info *hi; - struct raw1394_khost_list *khl; - - if (req->req.generation != atomic_read(&internal_generation)) { - req->req.error = RAW1394_ERROR_GENERATION; - req->req.generation = atomic_read(&internal_generation); - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - switch (req->req.type) { - case RAW1394_REQ_LIST_CARDS: - spin_lock_irqsave(&host_info_lock, flags); - khl = kmalloc(sizeof(*khl) * host_count, GFP_ATOMIC); - - if (khl) { - req->req.misc = host_count; - req->data = (quadlet_t *) khl; - - list_for_each_entry(hi, &host_info_list, list) { - khl->nodes = hi->host->node_count; - strcpy(khl->name, hi->host->driver->name); - khl++; - } - } - spin_unlock_irqrestore(&host_info_lock, flags); - - if (khl) { - req->req.error = RAW1394_ERROR_NONE; - req->req.length = min(req->req.length, - (u32) (sizeof - (struct raw1394_khost_list) - * req->req.misc)); - req->free_data = 1; - } else { - return -ENOMEM; - } - break; - - case RAW1394_REQ_SET_CARD: - spin_lock_irqsave(&host_info_lock, flags); - if (req->req.misc >= host_count) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - goto out_set_card; - } - list_for_each_entry(hi, &host_info_list, list) - if (!req->req.misc--) - break; - get_device(&hi->host->device); /* FIXME handle failure case */ - list_add_tail(&fi->list, &hi->file_info_list); - - /* prevent unloading of the host's low-level driver */ - if (!try_module_get(hi->host->driver->owner)) { - req->req.error = RAW1394_ERROR_ABORTED; - goto out_set_card; - } - WARN_ON(fi->host); - fi->host = hi->host; - fi->state = connected; - - req->req.error = RAW1394_ERROR_NONE; - req->req.generation = get_hpsb_generation(fi->host); - req->req.misc = (fi->host->node_id << 16) - | fi->host->node_count; - if (fi->protocol_version > 3) - req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8; -out_set_card: - spin_unlock_irqrestore(&host_info_lock, flags); - - req->req.length = 0; - break; - - default: - req->req.error = RAW1394_ERROR_STATE_ORDER; - req->req.length = 0; - break; - } - - queue_complete_req(req); - return 0; -} - -static void handle_fcp_listen(struct file_info *fi, struct pending_request *req) -{ - if (req->req.misc) { - if (fi->fcp_buffer) { - req->req.error = RAW1394_ERROR_ALREADY; - } else { - fi->fcp_buffer = int2ptr(req->req.recvb); - } - } else { - if (!fi->fcp_buffer) { - req->req.error = RAW1394_ERROR_ALREADY; - } else { - fi->fcp_buffer = NULL; - } - } - - req->req.length = 0; - queue_complete_req(req); -} - -static int handle_async_request(struct file_info *fi, - struct pending_request *req, int node) -{ - unsigned long flags; - struct hpsb_packet *packet = NULL; - u64 addr = req->req.address & 0xffffffffffffULL; - - switch (req->req.type) { - case RAW1394_REQ_ASYNC_READ: - DBGMSG("read_request called"); - packet = - hpsb_make_readpacket(fi->host, node, addr, req->req.length); - - if (!packet) - return -ENOMEM; - - if (req->req.length == 4) - req->data = &packet->header[3]; - else - req->data = packet->data; - - break; - - case RAW1394_REQ_ASYNC_WRITE: - DBGMSG("write_request called"); - - packet = hpsb_make_writepacket(fi->host, node, addr, NULL, - req->req.length); - if (!packet) - return -ENOMEM; - - if (req->req.length == 4) { - if (copy_from_user - (&packet->header[3], int2ptr(req->req.sendb), - req->req.length)) - req->req.error = RAW1394_ERROR_MEMFAULT; - } else { - if (copy_from_user - (packet->data, int2ptr(req->req.sendb), - req->req.length)) - req->req.error = RAW1394_ERROR_MEMFAULT; - } - - req->req.length = 0; - break; - - case RAW1394_REQ_ASYNC_STREAM: - DBGMSG("stream_request called"); - - packet = - hpsb_make_streampacket(fi->host, NULL, req->req.length, - node & 0x3f /*channel */ , - (req->req.misc >> 16) & 0x3, - req->req.misc & 0xf); - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) - req->req.error = RAW1394_ERROR_MEMFAULT; - - req->req.length = 0; - break; - - case RAW1394_REQ_LOCK: - DBGMSG("lock_request called"); - if ((req->req.misc == EXTCODE_FETCH_ADD) - || (req->req.misc == EXTCODE_LITTLE_ADD)) { - if (req->req.length != 4) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } else { - if (req->req.length != 8) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } - - packet = hpsb_make_lockpacket(fi->host, node, addr, - req->req.misc, NULL, 0); - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - break; - } - - req->data = packet->data; - req->req.length = 4; - break; - - case RAW1394_REQ_LOCK64: - DBGMSG("lock64_request called"); - if ((req->req.misc == EXTCODE_FETCH_ADD) - || (req->req.misc == EXTCODE_LITTLE_ADD)) { - if (req->req.length != 8) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } else { - if (req->req.length != 16) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } - packet = hpsb_make_lock64packet(fi->host, node, addr, - req->req.misc, NULL, 0); - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - break; - } - - req->data = packet->data; - req->req.length = 8; - break; - - default: - req->req.error = RAW1394_ERROR_STATE_ORDER; - } - - req->packet = packet; - - if (req->req.error) { - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - hpsb_set_packet_complete_task(packet, - (void (*)(void *))queue_complete_cb, req); - - spin_lock_irqsave(&fi->reqlists_lock, flags); - list_add_tail(&req->list, &fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - packet->generation = req->req.generation; - - if (hpsb_send_packet(packet) < 0) { - req->req.error = RAW1394_ERROR_SEND_ERROR; - req->req.length = 0; - hpsb_free_tlabel(packet); - queue_complete_req(req); - } - return 0; -} - -static int handle_async_send(struct file_info *fi, struct pending_request *req) -{ - unsigned long flags; - struct hpsb_packet *packet; - int header_length = req->req.misc & 0xffff; - int expect_response = req->req.misc >> 16; - size_t data_size; - - if (header_length > req->req.length || header_length < 12 || - header_length > FIELD_SIZEOF(struct hpsb_packet, header)) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - data_size = req->req.length - header_length; - packet = hpsb_alloc_packet(data_size); - req->packet = packet; - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->header, int2ptr(req->req.sendb), - header_length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - if (copy_from_user - (packet->data, int2ptr(req->req.sendb) + header_length, - data_size)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - packet->type = hpsb_async; - packet->node_id = packet->header[0] >> 16; - packet->tcode = (packet->header[0] >> 4) & 0xf; - packet->tlabel = (packet->header[0] >> 10) & 0x3f; - packet->host = fi->host; - packet->expect_response = expect_response; - packet->header_size = header_length; - packet->data_size = data_size; - - req->req.length = 0; - hpsb_set_packet_complete_task(packet, - (void (*)(void *))queue_complete_cb, req); - - spin_lock_irqsave(&fi->reqlists_lock, flags); - list_add_tail(&req->list, &fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - /* Update the generation of the packet just before sending. */ - packet->generation = req->req.generation; - - if (hpsb_send_packet(packet) < 0) { - req->req.error = RAW1394_ERROR_SEND_ERROR; - queue_complete_req(req); - } - - return 0; -} - -static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, - u64 addr, size_t length, u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - struct arm_request_response *arm_req_resp = NULL; - - DBGMSG("arm_read called by node: %X " - "addr: %4.4x %8.8x length: %Zu", nodeid, - (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), - length); - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search address-entry */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= (addr + length))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR "raw1394: arm_read FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_read addr_entry FOUND"); - } - if (arm_addr->rec_length < length) { - DBGMSG("arm_read blocklength too big -> rcode_data_error"); - rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */ - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_READ) { - if (!(arm_addr->client_transactions & ARM_READ)) { - memcpy(buffer, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - length); - DBGMSG("arm_read -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG("arm_read -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_READ) { - DBGMSG("arm_read -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - DBGMSG("arm_read -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - if (rcode == RCODE_COMPLETE) { - size = - sizeof(struct arm_request) + - sizeof(struct arm_response) + - length * sizeof(byte_t) + - sizeof(struct arm_request_response); - } else { - size = - sizeof(struct arm_request) + - sizeof(struct arm_response) + - sizeof(struct arm_request_response); - } - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - DBGMSG("arm_read -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = - (((length << 16) & (0xFFFF0000)) | (ARM_READ & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - arm_req->buffer = NULL; - arm_resp->buffer = NULL; - if (rcode == RCODE_COMPLETE) { - byte_t *buf = - (byte_t *) arm_resp + sizeof(struct arm_response); - memcpy(buf, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - length); - arm_resp->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - } - arm_resp->buffer_length = - (rcode == RCODE_COMPLETE) ? length : 0; - arm_resp->response_code = rcode; - arm_req->buffer_length = 0; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = 0; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = host->node_id; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_write(struct hpsb_host *host, int nodeid, int destid, - quadlet_t * data, u64 addr, size_t length, u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - struct arm_request_response *arm_req_resp = NULL; - - DBGMSG("arm_write called by node: %X " - "addr: %4.4x %8.8x length: %Zu", nodeid, - (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), - length); - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search address-entry */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= (addr + length))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR "raw1394: arm_write FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_write addr_entry FOUND"); - } - if (arm_addr->rec_length < length) { - DBGMSG("arm_write blocklength too big -> rcode_data_error"); - rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */ - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_WRITE) { - if (!(arm_addr->client_transactions & ARM_WRITE)) { - memcpy((arm_addr->addr_space_buffer) + - (addr - (arm_addr->start)), data, - length); - DBGMSG("arm_write -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG("arm_write -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_WRITE) { - DBGMSG("arm_write -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - DBGMSG("arm_write -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request my be retried */ - } - size = - sizeof(struct arm_request) + sizeof(struct arm_response) + - (length) * sizeof(byte_t) + - sizeof(struct arm_request_response); - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - DBGMSG("arm_write -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = - (((length << 16) & (0xFFFF0000)) | (ARM_WRITE & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - arm_resp->buffer = NULL; - memcpy((byte_t *) arm_resp + sizeof(struct arm_response), - data, length); - arm_req->buffer = int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - arm_req->buffer_length = length; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = 0; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = destid; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_resp->buffer_length = 0; - arm_resp->response_code = rcode; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - quadlet_t old, new; - struct arm_request_response *arm_req_resp = NULL; - - if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || - ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { - DBGMSG("arm_lock called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF, - be32_to_cpu(data)); - } else { - DBGMSG("arm_lock called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X arg: %8.8X", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF, - be32_to_cpu(data), be32_to_cpu(arg)); - } - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search address-entry */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= - (addr + sizeof(*store)))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR "raw1394: arm_lock FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_lock addr_entry FOUND"); - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_LOCK) { - if (!(arm_addr->client_transactions & ARM_LOCK)) { - memcpy(&old, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - sizeof(old)); - switch (ext_tcode) { - case (EXTCODE_MASK_SWAP): - new = data | (old & ~arg); - break; - case (EXTCODE_COMPARE_SWAP): - if (old == arg) { - new = data; - } else { - new = old; - } - break; - case (EXTCODE_FETCH_ADD): - new = - cpu_to_be32(be32_to_cpu(data) + - be32_to_cpu(old)); - break; - case (EXTCODE_LITTLE_ADD): - new = - cpu_to_le32(le32_to_cpu(data) + - le32_to_cpu(old)); - break; - case (EXTCODE_BOUNDED_ADD): - if (old != arg) { - new = - cpu_to_be32(be32_to_cpu - (data) + - be32_to_cpu - (old)); - } else { - new = old; - } - break; - case (EXTCODE_WRAP_ADD): - if (old != arg) { - new = - cpu_to_be32(be32_to_cpu - (data) + - be32_to_cpu - (old)); - } else { - new = data; - } - break; - default: - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - printk(KERN_ERR - "raw1394: arm_lock FAILED " - "ext_tcode not allowed -> rcode_type_error\n"); - break; - } /*switch */ - if (rcode == -1) { - DBGMSG("arm_lock -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - memcpy(store, &old, sizeof(*store)); - memcpy((arm_addr->addr_space_buffer) + - (addr - (arm_addr->start)), - &new, sizeof(*store)); - } - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG("arm_lock -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_LOCK) { - byte_t *buf1, *buf2; - DBGMSG("arm_lock -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - DBGMSG("arm_lock -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */ - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - DBGMSG("arm_lock -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - buf1 = (byte_t *) arm_resp + sizeof(struct arm_response); - buf2 = buf1 + 2 * sizeof(*store); - if ((ext_tcode == EXTCODE_FETCH_ADD) || - (ext_tcode == EXTCODE_LITTLE_ADD)) { - arm_req->buffer_length = sizeof(*store); - memcpy(buf1, &data, sizeof(*store)); - - } else { - arm_req->buffer_length = 2 * sizeof(*store); - memcpy(buf1, &arg, sizeof(*store)); - memcpy(buf1 + sizeof(*store), &data, sizeof(*store)); - } - if (rcode == RCODE_COMPLETE) { - arm_resp->buffer_length = sizeof(*store); - memcpy(buf2, &old, sizeof(*store)); - } else { - arm_resp->buffer_length = 0; - } - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) | - (ARM_LOCK & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = ext_tcode; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = host->node_id; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_resp->response_code = rcode; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - arm_req->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - arm_resp->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response) + 2 * sizeof(*store)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - octlet_t old, new; - struct arm_request_response *arm_req_resp = NULL; - - if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || - ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { - DBGMSG("arm_lock64 called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X ", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), - ext_tcode & 0xFF, - (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), - (u32) (be64_to_cpu(data) & 0xFFFFFFFF)); - } else { - DBGMSG("arm_lock64 called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X arg: " - "%8.8X %8.8X ", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), - ext_tcode & 0xFF, - (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), - (u32) (be64_to_cpu(data) & 0xFFFFFFFF), - (u32) ((be64_to_cpu(arg) >> 32) & 0xFFFFFFFF), - (u32) (be64_to_cpu(arg) & 0xFFFFFFFF)); - } - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search addressentry in file_info's for host */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= - (addr + sizeof(*store)))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR - "raw1394: arm_lock64 FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_lock64 addr_entry FOUND"); - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_LOCK) { - if (!(arm_addr->client_transactions & ARM_LOCK)) { - memcpy(&old, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - sizeof(old)); - switch (ext_tcode) { - case (EXTCODE_MASK_SWAP): - new = data | (old & ~arg); - break; - case (EXTCODE_COMPARE_SWAP): - if (old == arg) { - new = data; - } else { - new = old; - } - break; - case (EXTCODE_FETCH_ADD): - new = - cpu_to_be64(be64_to_cpu(data) + - be64_to_cpu(old)); - break; - case (EXTCODE_LITTLE_ADD): - new = - cpu_to_le64(le64_to_cpu(data) + - le64_to_cpu(old)); - break; - case (EXTCODE_BOUNDED_ADD): - if (old != arg) { - new = - cpu_to_be64(be64_to_cpu - (data) + - be64_to_cpu - (old)); - } else { - new = old; - } - break; - case (EXTCODE_WRAP_ADD): - if (old != arg) { - new = - cpu_to_be64(be64_to_cpu - (data) + - be64_to_cpu - (old)); - } else { - new = data; - } - break; - default: - printk(KERN_ERR - "raw1394: arm_lock64 FAILED " - "ext_tcode not allowed -> rcode_type_error\n"); - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - break; - } /*switch */ - if (rcode == -1) { - DBGMSG - ("arm_lock64 -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - memcpy(store, &old, sizeof(*store)); - memcpy((arm_addr->addr_space_buffer) + - (addr - (arm_addr->start)), - &new, sizeof(*store)); - } - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG - ("arm_lock64 -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_LOCK) { - byte_t *buf1, *buf2; - DBGMSG("arm_lock64 -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - spin_unlock_irqrestore(&host_info_lock, irqflags); - DBGMSG("arm_lock64 -> rcode_conflict_error"); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */ - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - spin_unlock_irqrestore(&host_info_lock, irqflags); - DBGMSG("arm_lock64 -> rcode_conflict_error"); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - buf1 = (byte_t *) arm_resp + sizeof(struct arm_response); - buf2 = buf1 + 2 * sizeof(*store); - if ((ext_tcode == EXTCODE_FETCH_ADD) || - (ext_tcode == EXTCODE_LITTLE_ADD)) { - arm_req->buffer_length = sizeof(*store); - memcpy(buf1, &data, sizeof(*store)); - - } else { - arm_req->buffer_length = 2 * sizeof(*store); - memcpy(buf1, &arg, sizeof(*store)); - memcpy(buf1 + sizeof(*store), &data, sizeof(*store)); - } - if (rcode == RCODE_COMPLETE) { - arm_resp->buffer_length = sizeof(*store); - memcpy(buf2, &old, sizeof(*store)); - } else { - arm_resp->buffer_length = 0; - } - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) | - (ARM_LOCK & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = ext_tcode; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = host->node_id; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_resp->response_code = rcode; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - arm_req->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - arm_resp->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response) + 2 * sizeof(*store)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_register(struct file_info *fi, struct pending_request *req) -{ - int retval; - struct arm_addr *addr; - struct host_info *hi; - struct file_info *fi_hlp = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - int same_host, another_host; - unsigned long flags; - - DBGMSG("arm_register called " - "addr(Offset): %8.8x %8.8x length: %u " - "rights: %2.2X notify: %2.2X " - "max_blk_len: %4.4X", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF), - req->req.length, ((req->req.misc >> 8) & 0xFF), - (req->req.misc & 0xFF), ((req->req.misc >> 16) & 0xFFFF)); - /* check addressrange */ - if ((((req->req.address) & ~(0xFFFFFFFFFFFFULL)) != 0) || - (((req->req.address + req->req.length) & ~(0xFFFFFFFFFFFFULL)) != - 0)) { - req->req.length = 0; - return (-EINVAL); - } - /* addr-list-entry for fileinfo */ - addr = kmalloc(sizeof(*addr), GFP_KERNEL); - if (!addr) { - req->req.length = 0; - return (-ENOMEM); - } - /* allocation of addr_space_buffer */ - addr->addr_space_buffer = vmalloc(req->req.length); - if (!(addr->addr_space_buffer)) { - kfree(addr); - req->req.length = 0; - return (-ENOMEM); - } - /* initialization of addr_space_buffer */ - if ((req->req.sendb) == (unsigned long)NULL) { - /* init: set 0 */ - memset(addr->addr_space_buffer, 0, req->req.length); - } else { - /* init: user -> kernel */ - if (copy_from_user - (addr->addr_space_buffer, int2ptr(req->req.sendb), - req->req.length)) { - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EFAULT); - } - } - INIT_LIST_HEAD(&addr->addr_list); - addr->arm_tag = req->req.tag; - addr->start = req->req.address; - addr->end = req->req.address + req->req.length; - addr->access_rights = (u8) (req->req.misc & 0x0F); - addr->notification_options = (u8) ((req->req.misc >> 4) & 0x0F); - addr->client_transactions = (u8) ((req->req.misc >> 8) & 0x0F); - addr->access_rights |= addr->client_transactions; - addr->notification_options |= addr->client_transactions; - addr->recvb = req->req.recvb; - addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF); - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(fi->host); - same_host = 0; - another_host = 0; - /* same host with address-entry containing same addressrange ? */ - list_for_each_entry(fi_hlp, &hi->file_info_list, list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, addr_list); - if ((arm_addr->start == addr->start) - && (arm_addr->end == addr->end)) { - DBGMSG("same host ownes same " - "addressrange -> EALREADY"); - same_host = 1; - break; - } - entry = entry->next; - } - if (same_host) { - break; - } - } - if (same_host) { - /* addressrange occupied by same host */ - spin_unlock_irqrestore(&host_info_lock, flags); - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EALREADY); - } - /* another host with valid address-entry containing same addressrange */ - list_for_each_entry(hi, &host_info_list, list) { - if (hi->host != fi->host) { - list_for_each_entry(fi_hlp, &hi->file_info_list, list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if ((arm_addr->start == addr->start) - && (arm_addr->end == addr->end)) { - DBGMSG - ("another host ownes same " - "addressrange"); - another_host = 1; - break; - } - entry = entry->next; - } - if (another_host) { - break; - } - } - } - } - spin_unlock_irqrestore(&host_info_lock, flags); - - if (another_host) { - DBGMSG("another hosts entry is valid -> SUCCESS"); - if (copy_to_user(int2ptr(req->req.recvb), - &addr->start, sizeof(u64))) { - printk(KERN_ERR "raw1394: arm_register failed " - " address-range-entry is invalid -> EFAULT !!!\n"); - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EFAULT); - } - free_pending_request(req); /* immediate success or fail */ - /* INSERT ENTRY */ - spin_lock_irqsave(&host_info_lock, flags); - list_add_tail(&addr->addr_list, &fi->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - return 0; - } - retval = - hpsb_register_addrspace(&raw1394_highlevel, fi->host, &arm_ops, - req->req.address, - req->req.address + req->req.length); - if (retval) { - /* INSERT ENTRY */ - spin_lock_irqsave(&host_info_lock, flags); - list_add_tail(&addr->addr_list, &fi->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - } else { - DBGMSG("arm_register failed errno: %d \n", retval); - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EALREADY); - } - free_pending_request(req); /* immediate success or fail */ - return 0; -} - -static int arm_unregister(struct file_info *fi, struct pending_request *req) -{ - int found = 0; - int retval = 0; - struct list_head *entry; - struct arm_addr *addr = NULL; - struct host_info *hi; - struct file_info *fi_hlp = NULL; - struct arm_addr *arm_addr = NULL; - int another_host; - unsigned long flags; - - DBGMSG("arm_Unregister called addr(Offset): " - "%8.8x %8.8x", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF)); - spin_lock_irqsave(&host_info_lock, flags); - /* get addr */ - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - addr = list_entry(entry, struct arm_addr, addr_list); - if (addr->start == req->req.address) { - found = 1; - break; - } - entry = entry->next; - } - if (!found) { - DBGMSG("arm_Unregister addr not found"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - DBGMSG("arm_Unregister addr found"); - another_host = 0; - /* another host with valid address-entry containing - same addressrange */ - list_for_each_entry(hi, &host_info_list, list) { - if (hi->host != fi->host) { - list_for_each_entry(fi_hlp, &hi->file_info_list, list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = list_entry(entry, - struct arm_addr, - addr_list); - if (arm_addr->start == addr->start) { - DBGMSG("another host ownes " - "same addressrange"); - another_host = 1; - break; - } - entry = entry->next; - } - if (another_host) { - break; - } - } - } - } - if (another_host) { - DBGMSG("delete entry from list -> success"); - list_del(&addr->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - vfree(addr->addr_space_buffer); - kfree(addr); - free_pending_request(req); /* immediate success or fail */ - return 0; - } - retval = - hpsb_unregister_addrspace(&raw1394_highlevel, fi->host, - addr->start); - if (!retval) { - printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - DBGMSG("delete entry from list -> success"); - list_del(&addr->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - vfree(addr->addr_space_buffer); - kfree(addr); - free_pending_request(req); /* immediate success or fail */ - return 0; -} - -/* Copy data from ARM buffer(s) to user buffer. */ -static int arm_get_buf(struct file_info *fi, struct pending_request *req) -{ - struct arm_addr *arm_addr = NULL; - unsigned long flags; - unsigned long offset; - - struct list_head *entry; - - DBGMSG("arm_get_buf " - "addr(Offset): %04X %08X length: %u", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length); - - spin_lock_irqsave(&host_info_lock, flags); - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = list_entry(entry, struct arm_addr, addr_list); - if ((arm_addr->start <= req->req.address) && - (arm_addr->end > req->req.address)) { - if (req->req.address + req->req.length <= arm_addr->end) { - offset = req->req.address - arm_addr->start; - spin_unlock_irqrestore(&host_info_lock, flags); - - DBGMSG - ("arm_get_buf copy_to_user( %08X, %p, %u )", - (u32) req->req.recvb, - arm_addr->addr_space_buffer + offset, - (u32) req->req.length); - if (copy_to_user - (int2ptr(req->req.recvb), - arm_addr->addr_space_buffer + offset, - req->req.length)) - return (-EFAULT); - - /* We have to free the request, because we - * queue no response, and therefore nobody - * will free it. */ - free_pending_request(req); - return 0; - } else { - DBGMSG("arm_get_buf request exceeded mapping"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - } - entry = entry->next; - } - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); -} - -/* Copy data from user buffer to ARM buffer(s). */ -static int arm_set_buf(struct file_info *fi, struct pending_request *req) -{ - struct arm_addr *arm_addr = NULL; - unsigned long flags; - unsigned long offset; - - struct list_head *entry; - - DBGMSG("arm_set_buf " - "addr(Offset): %04X %08X length: %u", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length); - - spin_lock_irqsave(&host_info_lock, flags); - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = list_entry(entry, struct arm_addr, addr_list); - if ((arm_addr->start <= req->req.address) && - (arm_addr->end > req->req.address)) { - if (req->req.address + req->req.length <= arm_addr->end) { - offset = req->req.address - arm_addr->start; - spin_unlock_irqrestore(&host_info_lock, flags); - - DBGMSG - ("arm_set_buf copy_from_user( %p, %08X, %u )", - arm_addr->addr_space_buffer + offset, - (u32) req->req.sendb, - (u32) req->req.length); - if (copy_from_user - (arm_addr->addr_space_buffer + offset, - int2ptr(req->req.sendb), - req->req.length)) - return (-EFAULT); - - /* We have to free the request, because we - * queue no response, and therefore nobody - * will free it. */ - free_pending_request(req); - return 0; - } else { - DBGMSG("arm_set_buf request exceeded mapping"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - } - entry = entry->next; - } - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); -} - -static int reset_notification(struct file_info *fi, struct pending_request *req) -{ - DBGMSG("reset_notification called - switch %s ", - (req->req.misc == RAW1394_NOTIFY_OFF) ? "OFF" : "ON"); - if ((req->req.misc == RAW1394_NOTIFY_OFF) || - (req->req.misc == RAW1394_NOTIFY_ON)) { - fi->notification = (u8) req->req.misc; - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - return 0; - } - /* error EINVAL (22) invalid argument */ - return (-EINVAL); -} - -static int write_phypacket(struct file_info *fi, struct pending_request *req) -{ - struct hpsb_packet *packet = NULL; - int retval = 0; - quadlet_t data; - unsigned long flags; - - data = be32_to_cpu((u32) req->req.sendb); - DBGMSG("write_phypacket called - quadlet 0x%8.8x ", data); - packet = hpsb_make_phypacket(fi->host, data); - if (!packet) - return -ENOMEM; - req->req.length = 0; - req->packet = packet; - hpsb_set_packet_complete_task(packet, - (void (*)(void *))queue_complete_cb, req); - spin_lock_irqsave(&fi->reqlists_lock, flags); - list_add_tail(&req->list, &fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - packet->generation = req->req.generation; - retval = hpsb_send_packet(packet); - DBGMSG("write_phypacket send_packet called => retval: %d ", retval); - if (retval < 0) { - req->req.error = RAW1394_ERROR_SEND_ERROR; - req->req.length = 0; - queue_complete_req(req); - } - return 0; -} - -static int get_config_rom(struct file_info *fi, struct pending_request *req) -{ - int ret = 0; - quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL); - int status; - - if (!data) - return -ENOMEM; - - status = - csr1212_read(fi->host->csr.rom, CSR1212_CONFIG_ROM_SPACE_OFFSET, - data, req->req.length); - if (copy_to_user(int2ptr(req->req.recvb), data, req->req.length)) - ret = -EFAULT; - if (copy_to_user - (int2ptr(req->req.tag), &fi->host->csr.rom->cache_head->len, - sizeof(fi->host->csr.rom->cache_head->len))) - ret = -EFAULT; - if (copy_to_user(int2ptr(req->req.address), &fi->host->csr.generation, - sizeof(fi->host->csr.generation))) - ret = -EFAULT; - if (copy_to_user(int2ptr(req->req.sendb), &status, sizeof(status))) - ret = -EFAULT; - kfree(data); - if (ret >= 0) { - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - } - return ret; -} - -static int update_config_rom(struct file_info *fi, struct pending_request *req) -{ - int ret = 0; - quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL); - if (!data) - return -ENOMEM; - if (copy_from_user(data, int2ptr(req->req.sendb), req->req.length)) { - ret = -EFAULT; - } else { - int status = hpsb_update_config_rom(fi->host, - data, req->req.length, - (unsigned char)req->req. - misc); - if (copy_to_user - (int2ptr(req->req.recvb), &status, sizeof(status))) - ret = -ENOMEM; - } - kfree(data); - if (ret >= 0) { - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - fi->cfgrom_upd = 1; - } - return ret; -} - -static int modify_config_rom(struct file_info *fi, struct pending_request *req) -{ - struct csr1212_keyval *kv; - struct csr1212_csr_rom_cache *cache; - struct csr1212_dentry *dentry; - u32 dr; - int ret = 0; - - if (req->req.misc == ~0) { - if (req->req.length == 0) - return -EINVAL; - - /* Find an unused slot */ - for (dr = 0; - dr < RAW1394_MAX_USER_CSR_DIRS && fi->csr1212_dirs[dr]; - dr++) ; - - if (dr == RAW1394_MAX_USER_CSR_DIRS) - return -ENOMEM; - - fi->csr1212_dirs[dr] = - csr1212_new_directory(CSR1212_KV_ID_VENDOR); - if (!fi->csr1212_dirs[dr]) - return -ENOMEM; - } else { - dr = req->req.misc; - if (!fi->csr1212_dirs[dr]) - return -EINVAL; - - /* Delete old stuff */ - for (dentry = - fi->csr1212_dirs[dr]->value.directory.dentries_head; - dentry; dentry = dentry->next) { - csr1212_detach_keyval_from_directory(fi->host->csr.rom-> - root_kv, - dentry->kv); - } - - if (req->req.length == 0) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - - hpsb_update_config_rom_image(fi->host); - free_pending_request(req); - return 0; - } - } - - cache = csr1212_rom_cache_malloc(0, req->req.length); - if (!cache) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - return -ENOMEM; - } - - cache->filled_head = kmalloc(sizeof(*cache->filled_head), GFP_KERNEL); - if (!cache->filled_head) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - CSR1212_FREE(cache); - return -ENOMEM; - } - cache->filled_tail = cache->filled_head; - - if (copy_from_user(cache->data, int2ptr(req->req.sendb), - req->req.length)) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - ret = -EFAULT; - } else { - cache->len = req->req.length; - cache->filled_head->offset_start = 0; - cache->filled_head->offset_end = cache->size - 1; - - cache->layout_head = cache->layout_tail = fi->csr1212_dirs[dr]; - - ret = CSR1212_SUCCESS; - /* parse all the items */ - for (kv = cache->layout_head; ret == CSR1212_SUCCESS && kv; - kv = kv->next) { - ret = csr1212_parse_keyval(kv, cache); - } - - /* attach top level items to the root directory */ - for (dentry = - fi->csr1212_dirs[dr]->value.directory.dentries_head; - ret == CSR1212_SUCCESS && dentry; dentry = dentry->next) { - ret = - csr1212_attach_keyval_to_directory(fi->host->csr. - rom->root_kv, - dentry->kv); - } - - if (ret == CSR1212_SUCCESS) { - ret = hpsb_update_config_rom_image(fi->host); - - if (ret >= 0 && copy_to_user(int2ptr(req->req.recvb), - &dr, sizeof(dr))) { - ret = -ENOMEM; - } - } - } - kfree(cache->filled_head); - CSR1212_FREE(cache); - - if (ret >= 0) { - /* we have to free the request, because we queue no response, - * and therefore nobody will free it */ - free_pending_request(req); - return 0; - } else { - for (dentry = - fi->csr1212_dirs[dr]->value.directory.dentries_head; - dentry; dentry = dentry->next) { - csr1212_detach_keyval_from_directory(fi->host->csr.rom-> - root_kv, - dentry->kv); - } - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - return ret; - } -} - -static int state_connected(struct file_info *fi, struct pending_request *req) -{ - int node = req->req.address >> 48; - - req->req.error = RAW1394_ERROR_NONE; - - switch (req->req.type) { - - case RAW1394_REQ_ECHO: - queue_complete_req(req); - return 0; - - case RAW1394_REQ_ARM_REGISTER: - return arm_register(fi, req); - - case RAW1394_REQ_ARM_UNREGISTER: - return arm_unregister(fi, req); - - case RAW1394_REQ_ARM_SET_BUF: - return arm_set_buf(fi, req); - - case RAW1394_REQ_ARM_GET_BUF: - return arm_get_buf(fi, req); - - case RAW1394_REQ_RESET_NOTIFY: - return reset_notification(fi, req); - - case RAW1394_REQ_ISO_SEND: - case RAW1394_REQ_ISO_LISTEN: - printk(KERN_DEBUG "raw1394: old iso ABI has been removed\n"); - req->req.error = RAW1394_ERROR_COMPAT; - req->req.misc = RAW1394_KERNELAPI_VERSION; - queue_complete_req(req); - return 0; - - case RAW1394_REQ_FCP_LISTEN: - handle_fcp_listen(fi, req); - return 0; - - case RAW1394_REQ_RESET_BUS: - if (req->req.misc == RAW1394_LONG_RESET) { - DBGMSG("busreset called (type: LONG)"); - hpsb_reset_bus(fi->host, LONG_RESET); - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - return 0; - } - if (req->req.misc == RAW1394_SHORT_RESET) { - DBGMSG("busreset called (type: SHORT)"); - hpsb_reset_bus(fi->host, SHORT_RESET); - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - return 0; - } - /* error EINVAL (22) invalid argument */ - return (-EINVAL); - case RAW1394_REQ_GET_ROM: - return get_config_rom(fi, req); - - case RAW1394_REQ_UPDATE_ROM: - return update_config_rom(fi, req); - - case RAW1394_REQ_MODIFY_ROM: - return modify_config_rom(fi, req); - } - - if (req->req.generation != get_hpsb_generation(fi->host)) { - req->req.error = RAW1394_ERROR_GENERATION; - req->req.generation = get_hpsb_generation(fi->host); - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - switch (req->req.type) { - case RAW1394_REQ_PHYPACKET: - return write_phypacket(fi, req); - case RAW1394_REQ_ASYNC_SEND: - return handle_async_send(fi, req); - } - - if (req->req.length == 0) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - queue_complete_req(req); - return 0; - } - - return handle_async_request(fi, req, node); -} - -static ssize_t raw1394_write(struct file *file, const char __user * buffer, - size_t count, loff_t * offset_is_ignored) -{ - struct file_info *fi = file->private_data; - struct pending_request *req; - ssize_t retval = -EBADFD; - -#ifdef CONFIG_COMPAT - if (count == sizeof(struct compat_raw1394_req) && - sizeof(struct compat_raw1394_req) != - sizeof(struct raw1394_request)) { - buffer = raw1394_compat_write(buffer); - if (IS_ERR((__force void *)buffer)) - return PTR_ERR((__force void *)buffer); - } else -#endif - if (count != sizeof(struct raw1394_request)) { - return -EINVAL; - } - - req = alloc_pending_request(); - if (req == NULL) { - return -ENOMEM; - } - req->file_info = fi; - - if (copy_from_user(&req->req, buffer, sizeof(struct raw1394_request))) { - free_pending_request(req); - return -EFAULT; - } - - if (!mutex_trylock(&fi->state_mutex)) { - free_pending_request(req); - return -EAGAIN; - } - - switch (fi->state) { - case opened: - retval = state_opened(fi, req); - break; - - case initialized: - retval = state_initialized(fi, req); - break; - - case connected: - retval = state_connected(fi, req); - break; - } - - mutex_unlock(&fi->state_mutex); - - if (retval < 0) { - free_pending_request(req); - } else { - BUG_ON(retval); - retval = count; - } - - return retval; -} - -/* rawiso operations */ - -/* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the - * completion queue (reqlists_lock must be taken) */ -static inline int __rawiso_event_in_queue(struct file_info *fi) -{ - struct pending_request *req; - - list_for_each_entry(req, &fi->req_complete, list) - if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) - return 1; - - return 0; -} - -/* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */ -static void queue_rawiso_event(struct file_info *fi) -{ - unsigned long flags; - - spin_lock_irqsave(&fi->reqlists_lock, flags); - - /* only one ISO activity event may be in the queue */ - if (!__rawiso_event_in_queue(fi)) { - struct pending_request *req = - __alloc_pending_request(GFP_ATOMIC); - - if (req) { - req->file_info = fi; - req->req.type = RAW1394_REQ_RAWISO_ACTIVITY; - req->req.generation = get_hpsb_generation(fi->host); - __queue_complete_req(req); - } else { - /* on allocation failure, signal an overflow */ - if (fi->iso_handle) { - atomic_inc(&fi->iso_handle->overflows); - } - } - } - spin_unlock_irqrestore(&fi->reqlists_lock, flags); -} - -static void rawiso_activity_cb(struct hpsb_iso *iso) -{ - unsigned long flags; - struct host_info *hi; - struct file_info *fi; - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(iso->host); - - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - if (fi->iso_handle == iso) - queue_rawiso_event(fi); - } - } - - spin_unlock_irqrestore(&host_info_lock, flags); -} - -/* helper function - gather all the kernel iso status bits for returning to user-space */ -static void raw1394_iso_fill_status(struct hpsb_iso *iso, - struct raw1394_iso_status *stat) -{ - int overflows = atomic_read(&iso->overflows); - int skips = atomic_read(&iso->skips); - - stat->config.data_buf_size = iso->buf_size; - stat->config.buf_packets = iso->buf_packets; - stat->config.channel = iso->channel; - stat->config.speed = iso->speed; - stat->config.irq_interval = iso->irq_interval; - stat->n_packets = hpsb_iso_n_ready(iso); - stat->overflows = ((skips & 0xFFFF) << 16) | ((overflows & 0xFFFF)); - stat->xmit_cycle = iso->xmit_cycle; -} - -static int raw1394_iso_xmit_init(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_status stat; - - if (!fi->host) - return -EINVAL; - - if (copy_from_user(&stat, uaddr, sizeof(stat))) - return -EFAULT; - - fi->iso_handle = hpsb_iso_xmit_init(fi->host, - stat.config.data_buf_size, - stat.config.buf_packets, - stat.config.channel, - stat.config.speed, - stat.config.irq_interval, - rawiso_activity_cb); - if (!fi->iso_handle) - return -ENOMEM; - - fi->iso_state = RAW1394_ISO_XMIT; - - raw1394_iso_fill_status(fi->iso_handle, &stat); - if (copy_to_user(uaddr, &stat, sizeof(stat))) - return -EFAULT; - - /* queue an event to get things started */ - rawiso_activity_cb(fi->iso_handle); - - return 0; -} - -static int raw1394_iso_recv_init(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_status stat; - - if (!fi->host) - return -EINVAL; - - if (copy_from_user(&stat, uaddr, sizeof(stat))) - return -EFAULT; - - fi->iso_handle = hpsb_iso_recv_init(fi->host, - stat.config.data_buf_size, - stat.config.buf_packets, - stat.config.channel, - stat.config.dma_mode, - stat.config.irq_interval, - rawiso_activity_cb); - if (!fi->iso_handle) - return -ENOMEM; - - fi->iso_state = RAW1394_ISO_RECV; - - raw1394_iso_fill_status(fi->iso_handle, &stat); - if (copy_to_user(uaddr, &stat, sizeof(stat))) - return -EFAULT; - return 0; -} - -static int raw1394_iso_get_status(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_status stat; - struct hpsb_iso *iso = fi->iso_handle; - - raw1394_iso_fill_status(fi->iso_handle, &stat); - if (copy_to_user(uaddr, &stat, sizeof(stat))) - return -EFAULT; - - /* reset overflow counter */ - atomic_set(&iso->overflows, 0); - /* reset skip counter */ - atomic_set(&iso->skips, 0); - - return 0; -} - -/* copy N packet_infos out of the ringbuffer into user-supplied array */ -static int raw1394_iso_recv_packets(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_packets upackets; - unsigned int packet = fi->iso_handle->first_packet; - int i; - - if (copy_from_user(&upackets, uaddr, sizeof(upackets))) - return -EFAULT; - - if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) - return -EINVAL; - - /* ensure user-supplied buffer is accessible and big enough */ - if (!access_ok(VERIFY_WRITE, upackets.infos, - upackets.n_packets * - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - /* copy the packet_infos out */ - for (i = 0; i < upackets.n_packets; i++) { - if (__copy_to_user(&upackets.infos[i], - &fi->iso_handle->infos[packet], - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - packet = (packet + 1) % fi->iso_handle->buf_packets; - } - - return 0; -} - -/* copy N packet_infos from user to ringbuffer, and queue them for transmission */ -static int raw1394_iso_send_packets(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_packets upackets; - int i, rv; - - if (copy_from_user(&upackets, uaddr, sizeof(upackets))) - return -EFAULT; - - if (upackets.n_packets >= fi->iso_handle->buf_packets) - return -EINVAL; - - if (upackets.n_packets >= hpsb_iso_n_ready(fi->iso_handle)) - return -EAGAIN; - - /* ensure user-supplied buffer is accessible and big enough */ - if (!access_ok(VERIFY_READ, upackets.infos, - upackets.n_packets * - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - /* copy the infos structs in and queue the packets */ - for (i = 0; i < upackets.n_packets; i++) { - struct raw1394_iso_packet_info info; - - if (__copy_from_user(&info, &upackets.infos[i], - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset, - info.len, info.tag, info.sy); - if (rv) - return rv; - } - - return 0; -} - -static void raw1394_iso_shutdown(struct file_info *fi) -{ - if (fi->iso_handle) - hpsb_iso_shutdown(fi->iso_handle); - - fi->iso_handle = NULL; - fi->iso_state = RAW1394_ISO_INACTIVE; -} - -static int raw1394_read_cycle_timer(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_cycle_timer ct; - int err; - - err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); - if (!err) - if (copy_to_user(uaddr, &ct, sizeof(ct))) - err = -EFAULT; - return err; -} - -/* mmap the rawiso xmit/recv buffer */ -static int raw1394_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct file_info *fi = file->private_data; - int ret; - - if (!mutex_trylock(&fi->state_mutex)) - return -EAGAIN; - - if (fi->iso_state == RAW1394_ISO_INACTIVE) - ret = -EINVAL; - else - ret = dma_region_mmap(&fi->iso_handle->data_buf, file, vma); - - mutex_unlock(&fi->state_mutex); - - return ret; -} - -static long raw1394_ioctl_inactive(struct file_info *fi, unsigned int cmd, - void __user *argp) -{ - switch (cmd) { - case RAW1394_IOC_ISO_XMIT_INIT: - return raw1394_iso_xmit_init(fi, argp); - case RAW1394_IOC_ISO_RECV_INIT: - return raw1394_iso_recv_init(fi, argp); - default: - return -EINVAL; - } -} - -static long raw1394_ioctl_recv(struct file_info *fi, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - - switch (cmd) { - case RAW1394_IOC_ISO_RECV_START:{ - int args[3]; - - if (copy_from_user(&args[0], argp, sizeof(args))) - return -EFAULT; - return hpsb_iso_recv_start(fi->iso_handle, - args[0], args[1], args[2]); - } - case RAW1394_IOC_ISO_XMIT_RECV_STOP: - hpsb_iso_stop(fi->iso_handle); - return 0; - case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: - return hpsb_iso_recv_listen_channel(fi->iso_handle, arg); - case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: - return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg); - case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:{ - u64 mask; - - if (copy_from_user(&mask, argp, sizeof(mask))) - return -EFAULT; - return hpsb_iso_recv_set_channel_mask(fi->iso_handle, - mask); - } - case RAW1394_IOC_ISO_GET_STATUS: - return raw1394_iso_get_status(fi, argp); - case RAW1394_IOC_ISO_RECV_PACKETS: - return raw1394_iso_recv_packets(fi, argp); - case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: - return hpsb_iso_recv_release_packets(fi->iso_handle, arg); - case RAW1394_IOC_ISO_RECV_FLUSH: - return hpsb_iso_recv_flush(fi->iso_handle); - case RAW1394_IOC_ISO_SHUTDOWN: - raw1394_iso_shutdown(fi); - return 0; - case RAW1394_IOC_ISO_QUEUE_ACTIVITY: - queue_rawiso_event(fi); - return 0; - default: - return -EINVAL; - } -} - -static long raw1394_ioctl_xmit(struct file_info *fi, unsigned int cmd, - void __user *argp) -{ - switch (cmd) { - case RAW1394_IOC_ISO_XMIT_START:{ - int args[2]; - - if (copy_from_user(&args[0], argp, sizeof(args))) - return -EFAULT; - return hpsb_iso_xmit_start(fi->iso_handle, - args[0], args[1]); - } - case RAW1394_IOC_ISO_XMIT_SYNC: - return hpsb_iso_xmit_sync(fi->iso_handle); - case RAW1394_IOC_ISO_XMIT_RECV_STOP: - hpsb_iso_stop(fi->iso_handle); - return 0; - case RAW1394_IOC_ISO_GET_STATUS: - return raw1394_iso_get_status(fi, argp); - case RAW1394_IOC_ISO_XMIT_PACKETS: - return raw1394_iso_send_packets(fi, argp); - case RAW1394_IOC_ISO_SHUTDOWN: - raw1394_iso_shutdown(fi); - return 0; - case RAW1394_IOC_ISO_QUEUE_ACTIVITY: - queue_rawiso_event(fi); - return 0; - default: - return -EINVAL; - } -} - -/* ioctl is only used for rawiso operations */ -static long raw1394_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct file_info *fi = file->private_data; - void __user *argp = (void __user *)arg; - long ret; - - /* state-independent commands */ - switch(cmd) { - case RAW1394_IOC_GET_CYCLE_TIMER: - return raw1394_read_cycle_timer(fi, argp); - default: - break; - } - - if (!mutex_trylock(&fi->state_mutex)) - return -EAGAIN; - - switch (fi->iso_state) { - case RAW1394_ISO_INACTIVE: - ret = raw1394_ioctl_inactive(fi, cmd, argp); - break; - case RAW1394_ISO_RECV: - ret = raw1394_ioctl_recv(fi, cmd, arg); - break; - case RAW1394_ISO_XMIT: - ret = raw1394_ioctl_xmit(fi, cmd, argp); - break; - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&fi->state_mutex); - - return ret; -} - -#ifdef CONFIG_COMPAT -struct raw1394_iso_packets32 { - __u32 n_packets; - compat_uptr_t infos; -} __attribute__((packed)); - -struct raw1394_cycle_timer32 { - __u32 cycle_timer; - __u64 local_time; -} -#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) -__attribute__((packed)) -#endif -; - -#define RAW1394_IOC_ISO_RECV_PACKETS32 \ - _IOW ('#', 0x25, struct raw1394_iso_packets32) -#define RAW1394_IOC_ISO_XMIT_PACKETS32 \ - _IOW ('#', 0x27, struct raw1394_iso_packets32) -#define RAW1394_IOC_GET_CYCLE_TIMER32 \ - _IOR ('#', 0x30, struct raw1394_cycle_timer32) - -static long raw1394_iso_xmit_recv_packets32(struct file *file, unsigned int cmd, - struct raw1394_iso_packets32 __user *arg) -{ - compat_uptr_t infos32; - void __user *infos; - long err = -EFAULT; - struct raw1394_iso_packets __user *dst = compat_alloc_user_space(sizeof(struct raw1394_iso_packets)); - - if (!copy_in_user(&dst->n_packets, &arg->n_packets, sizeof arg->n_packets) && - !copy_from_user(&infos32, &arg->infos, sizeof infos32)) { - infos = compat_ptr(infos32); - if (!copy_to_user(&dst->infos, &infos, sizeof infos)) - err = raw1394_ioctl(file, cmd, (unsigned long)dst); - } - return err; -} - -static long raw1394_read_cycle_timer32(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_cycle_timer32 ct; - int err; - - err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); - if (!err) - if (copy_to_user(uaddr, &ct, sizeof(ct))) - err = -EFAULT; - return err; -} - -static long raw1394_compat_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct file_info *fi = file->private_data; - void __user *argp = (void __user *)arg; - long err; - - switch (cmd) { - /* These requests have same format as long as 'int' has same size. */ - case RAW1394_IOC_ISO_RECV_INIT: - case RAW1394_IOC_ISO_RECV_START: - case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: - case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: - case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK: - case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: - case RAW1394_IOC_ISO_RECV_FLUSH: - case RAW1394_IOC_ISO_XMIT_RECV_STOP: - case RAW1394_IOC_ISO_XMIT_INIT: - case RAW1394_IOC_ISO_XMIT_START: - case RAW1394_IOC_ISO_XMIT_SYNC: - case RAW1394_IOC_ISO_GET_STATUS: - case RAW1394_IOC_ISO_SHUTDOWN: - case RAW1394_IOC_ISO_QUEUE_ACTIVITY: - err = raw1394_ioctl(file, cmd, arg); - break; - /* These request have different format. */ - case RAW1394_IOC_ISO_RECV_PACKETS32: - err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_RECV_PACKETS, argp); - break; - case RAW1394_IOC_ISO_XMIT_PACKETS32: - err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_XMIT_PACKETS, argp); - break; - case RAW1394_IOC_GET_CYCLE_TIMER32: - err = raw1394_read_cycle_timer32(fi, argp); - break; - default: - err = -EINVAL; - break; - } - - return err; -} -#endif - -static unsigned int raw1394_poll(struct file *file, poll_table * pt) -{ - struct file_info *fi = file->private_data; - unsigned int mask = POLLOUT | POLLWRNORM; - unsigned long flags; - - poll_wait(file, &fi->wait_complete, pt); - - spin_lock_irqsave(&fi->reqlists_lock, flags); - if (!list_empty(&fi->req_complete)) { - mask |= POLLIN | POLLRDNORM; - } - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - return mask; -} - -static int raw1394_open(struct inode *inode, struct file *file) -{ - struct file_info *fi; - - fi = kzalloc(sizeof(*fi), GFP_KERNEL); - if (!fi) - return -ENOMEM; - - fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */ - - INIT_LIST_HEAD(&fi->list); - mutex_init(&fi->state_mutex); - fi->state = opened; - INIT_LIST_HEAD(&fi->req_pending); - INIT_LIST_HEAD(&fi->req_complete); - spin_lock_init(&fi->reqlists_lock); - init_waitqueue_head(&fi->wait_complete); - INIT_LIST_HEAD(&fi->addr_list); - - file->private_data = fi; - - return nonseekable_open(inode, file); -} - -static int raw1394_release(struct inode *inode, struct file *file) -{ - struct file_info *fi = file->private_data; - struct list_head *lh; - struct pending_request *req; - int i, fail; - int retval = 0; - struct list_head *entry; - struct arm_addr *addr = NULL; - struct host_info *hi; - struct file_info *fi_hlp = NULL; - struct arm_addr *arm_addr = NULL; - int another_host; - int csr_mod = 0; - unsigned long flags; - - if (fi->iso_state != RAW1394_ISO_INACTIVE) - raw1394_iso_shutdown(fi); - - spin_lock_irqsave(&host_info_lock, flags); - - fail = 0; - /* set address-entries invalid */ - - while (!list_empty(&fi->addr_list)) { - another_host = 0; - lh = fi->addr_list.next; - addr = list_entry(lh, struct arm_addr, addr_list); - /* another host with valid address-entry containing - same addressrange? */ - list_for_each_entry(hi, &host_info_list, list) { - if (hi->host != fi->host) { - list_for_each_entry(fi_hlp, &hi->file_info_list, - list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = list_entry(entry, struct - arm_addr, - addr_list); - if (arm_addr->start == - addr->start) { - DBGMSG - ("raw1394_release: " - "another host ownes " - "same addressrange"); - another_host = 1; - break; - } - entry = entry->next; - } - if (another_host) { - break; - } - } - } - } - if (!another_host) { - DBGMSG("raw1394_release: call hpsb_arm_unregister"); - retval = - hpsb_unregister_addrspace(&raw1394_highlevel, - fi->host, addr->start); - if (!retval) { - ++fail; - printk(KERN_ERR - "raw1394_release arm_Unregister failed\n"); - } - } - DBGMSG("raw1394_release: delete addr_entry from list"); - list_del(&addr->addr_list); - vfree(addr->addr_space_buffer); - kfree(addr); - } /* while */ - spin_unlock_irqrestore(&host_info_lock, flags); - if (fail > 0) { - printk(KERN_ERR "raw1394: during addr_list-release " - "error(s) occurred \n"); - } - - for (;;) { - /* This locked section guarantees that neither - * complete nor pending requests exist once i!=0 */ - spin_lock_irqsave(&fi->reqlists_lock, flags); - while ((req = __next_complete_req(fi))) - free_pending_request(req); - - i = list_empty(&fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - if (i) - break; - /* - * Sleep until more requests can be freed. - * - * NB: We call the macro wait_event() with a condition argument - * with side effect. This is only possible because the side - * effect does not occur until the condition became true, and - * wait_event() won't evaluate the condition again after that. - */ - wait_event(fi->wait_complete, (req = next_complete_req(fi))); - free_pending_request(req); - } - - /* Remove any sub-trees left by user space programs */ - for (i = 0; i < RAW1394_MAX_USER_CSR_DIRS; i++) { - struct csr1212_dentry *dentry; - if (!fi->csr1212_dirs[i]) - continue; - for (dentry = - fi->csr1212_dirs[i]->value.directory.dentries_head; dentry; - dentry = dentry->next) { - csr1212_detach_keyval_from_directory(fi->host->csr.rom-> - root_kv, - dentry->kv); - } - csr1212_release_keyval(fi->csr1212_dirs[i]); - fi->csr1212_dirs[i] = NULL; - csr_mod = 1; - } - - if ((csr_mod || fi->cfgrom_upd) - && hpsb_update_config_rom_image(fi->host) < 0) - HPSB_ERR - ("Failed to generate Configuration ROM image for host %d", - fi->host->id); - - if (fi->state == connected) { - spin_lock_irqsave(&host_info_lock, flags); - list_del(&fi->list); - spin_unlock_irqrestore(&host_info_lock, flags); - - put_device(&fi->host->device); - } - - spin_lock_irqsave(&host_info_lock, flags); - if (fi->host) - module_put(fi->host->driver->owner); - spin_unlock_irqrestore(&host_info_lock, flags); - - kfree(fi); - - return 0; -} - -/*** HOTPLUG STUFF **********************************************************/ -/* - * Export information about protocols/devices supported by this driver. - */ -#ifdef MODULE -static const struct ieee1394_device_id raw1394_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = AVC_SW_VERSION_ENTRY & 0xffffff}, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = CAMERA_SW_VERSION_ENTRY & 0xffffff}, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff}, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff}, - {} -}; - -MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); -#endif /* MODULE */ - -static struct hpsb_protocol_driver raw1394_driver = { - .name = "raw1394", -}; - -/******************************************************************************/ - -static struct hpsb_highlevel raw1394_highlevel = { - .name = RAW1394_DEVICE_NAME, - .add_host = add_host, - .remove_host = remove_host, - .host_reset = host_reset, - .fcp_request = fcp_request, -}; - -static struct cdev raw1394_cdev; -static const struct file_operations raw1394_fops = { - .owner = THIS_MODULE, - .read = raw1394_read, - .write = raw1394_write, - .mmap = raw1394_mmap, - .unlocked_ioctl = raw1394_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = raw1394_compat_ioctl, -#endif - .poll = raw1394_poll, - .open = raw1394_open, - .release = raw1394_release, - .llseek = no_llseek, -}; - -static int __init init_raw1394(void) -{ - int ret = 0; - - hpsb_register_highlevel(&raw1394_highlevel); - - if (IS_ERR - (device_create(hpsb_protocol_class, NULL, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_RAW1394 * 16), - NULL, RAW1394_DEVICE_NAME))) { - ret = -EFAULT; - goto out_unreg; - } - - cdev_init(&raw1394_cdev, &raw1394_fops); - raw1394_cdev.owner = THIS_MODULE; - ret = cdev_add(&raw1394_cdev, IEEE1394_RAW1394_DEV, 1); - if (ret) { - HPSB_ERR("raw1394 failed to register minor device block"); - goto out_dev; - } - - HPSB_INFO("raw1394: /dev/%s device initialized", RAW1394_DEVICE_NAME); - - ret = hpsb_register_protocol(&raw1394_driver); - if (ret) { - HPSB_ERR("raw1394: failed to register protocol"); - cdev_del(&raw1394_cdev); - goto out_dev; - } - - goto out; - - out_dev: - device_destroy(hpsb_protocol_class, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_RAW1394 * 16)); - out_unreg: - hpsb_unregister_highlevel(&raw1394_highlevel); - out: - return ret; -} - -static void __exit cleanup_raw1394(void) -{ - device_destroy(hpsb_protocol_class, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_RAW1394 * 16)); - cdev_del(&raw1394_cdev); - hpsb_unregister_highlevel(&raw1394_highlevel); - hpsb_unregister_protocol(&raw1394_driver); -} - -module_init(init_raw1394); -module_exit(cleanup_raw1394); -MODULE_LICENSE("GPL"); diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h deleted file mode 100644 index 963ac20373d..00000000000 --- a/drivers/ieee1394/raw1394.h +++ /dev/null @@ -1,191 +0,0 @@ -#ifndef IEEE1394_RAW1394_H -#define IEEE1394_RAW1394_H - -/* header for the raw1394 API that is exported to user-space */ - -#define RAW1394_KERNELAPI_VERSION 4 - -/* state: opened */ -#define RAW1394_REQ_INITIALIZE 1 - -/* state: initialized */ -#define RAW1394_REQ_LIST_CARDS 2 -#define RAW1394_REQ_SET_CARD 3 - -/* state: connected */ -#define RAW1394_REQ_ASYNC_READ 100 -#define RAW1394_REQ_ASYNC_WRITE 101 -#define RAW1394_REQ_LOCK 102 -#define RAW1394_REQ_LOCK64 103 -#define RAW1394_REQ_ISO_SEND 104 /* removed ABI, now a no-op */ -#define RAW1394_REQ_ASYNC_SEND 105 -#define RAW1394_REQ_ASYNC_STREAM 106 - -#define RAW1394_REQ_ISO_LISTEN 200 /* removed ABI, now a no-op */ -#define RAW1394_REQ_FCP_LISTEN 201 -#define RAW1394_REQ_RESET_BUS 202 -#define RAW1394_REQ_GET_ROM 203 -#define RAW1394_REQ_UPDATE_ROM 204 -#define RAW1394_REQ_ECHO 205 -#define RAW1394_REQ_MODIFY_ROM 206 - -#define RAW1394_REQ_ARM_REGISTER 300 -#define RAW1394_REQ_ARM_UNREGISTER 301 -#define RAW1394_REQ_ARM_SET_BUF 302 -#define RAW1394_REQ_ARM_GET_BUF 303 - -#define RAW1394_REQ_RESET_NOTIFY 400 - -#define RAW1394_REQ_PHYPACKET 500 - -/* kernel to user */ -#define RAW1394_REQ_BUS_RESET 10000 -#define RAW1394_REQ_ISO_RECEIVE 10001 -#define RAW1394_REQ_FCP_REQUEST 10002 -#define RAW1394_REQ_ARM 10003 -#define RAW1394_REQ_RAWISO_ACTIVITY 10004 - -/* error codes */ -#define RAW1394_ERROR_NONE 0 -#define RAW1394_ERROR_COMPAT (-1001) -#define RAW1394_ERROR_STATE_ORDER (-1002) -#define RAW1394_ERROR_GENERATION (-1003) -#define RAW1394_ERROR_INVALID_ARG (-1004) -#define RAW1394_ERROR_MEMFAULT (-1005) -#define RAW1394_ERROR_ALREADY (-1006) - -#define RAW1394_ERROR_EXCESSIVE (-1020) -#define RAW1394_ERROR_UNTIDY_LEN (-1021) - -#define RAW1394_ERROR_SEND_ERROR (-1100) -#define RAW1394_ERROR_ABORTED (-1101) -#define RAW1394_ERROR_TIMEOUT (-1102) - -/* arm_codes */ -#define ARM_READ 1 -#define ARM_WRITE 2 -#define ARM_LOCK 4 - -#define RAW1394_LONG_RESET 0 -#define RAW1394_SHORT_RESET 1 - -/* busresetnotify ... */ -#define RAW1394_NOTIFY_OFF 0 -#define RAW1394_NOTIFY_ON 1 - -#include <asm/types.h> - -struct raw1394_request { - __u32 type; - __s32 error; - __u32 misc; - - __u32 generation; - __u32 length; - - __u64 address; - - __u64 tag; - - __u64 sendb; - __u64 recvb; -}; - -struct raw1394_khost_list { - __u32 nodes; - __u8 name[32]; -}; - -typedef struct arm_request { - __u16 destination_nodeid; - __u16 source_nodeid; - __u64 destination_offset; - __u8 tlabel; - __u8 tcode; - __u8 extended_transaction_code; - __u32 generation; - __u16 buffer_length; - __u8 __user *buffer; -} *arm_request_t; - -typedef struct arm_response { - __s32 response_code; - __u16 buffer_length; - __u8 __user *buffer; -} *arm_response_t; - -typedef struct arm_request_response { - struct arm_request __user *request; - struct arm_response __user *response; -} *arm_request_response_t; - -/* rawiso API */ -#include "ieee1394-ioctl.h" - -/* per-packet metadata embedded in the ringbuffer */ -/* must be identical to hpsb_iso_packet_info in iso.h! */ -struct raw1394_iso_packet_info { - __u32 offset; - __u16 len; - __u16 cycle; /* recv only */ - __u8 channel; /* recv only */ - __u8 tag; - __u8 sy; -}; - -/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */ -struct raw1394_iso_packets { - __u32 n_packets; - struct raw1394_iso_packet_info __user *infos; -}; - -struct raw1394_iso_config { - /* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */ - __u32 data_buf_size; - - /* # of packets to buffer */ - __u32 buf_packets; - - /* iso channel (set to -1 for multi-channel recv) */ - __s32 channel; - - /* xmit only - iso transmission speed */ - __u8 speed; - - /* The mode of the dma when receiving iso data. Must be supported by chip */ - __u8 dma_mode; - - /* max. latency of buffer, in packets (-1 if you don't care) */ - __s32 irq_interval; -}; - -/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */ -struct raw1394_iso_status { - /* current settings */ - struct raw1394_iso_config config; - - /* number of packets waiting to be filled with data (ISO transmission) - or containing data received (ISO reception) */ - __u32 n_packets; - - /* approximate number of packets dropped due to overflow or - underflow of the packet buffer (a value of zero guarantees - that no packets have been dropped) */ - __u32 overflows; - - /* cycle number at which next packet will be transmitted; - -1 if not known */ - __s16 xmit_cycle; -}; - -/* argument to RAW1394_IOC_GET_CYCLE_TIMER ioctl */ -struct raw1394_cycle_timer { - /* contents of Isochronous Cycle Timer register, - as in OHCI 1.1 clause 5.13 (also with non-OHCI hosts) */ - __u32 cycle_timer; - - /* local time in microseconds since Epoch, - simultaneously read with cycle timer */ - __u64 local_time; -}; -#endif /* IEEE1394_RAW1394_H */ diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c deleted file mode 100644 index d6e251a300c..00000000000 --- a/drivers/ieee1394/sbp2.c +++ /dev/null @@ -1,2138 +0,0 @@ -/* - * sbp2.c - SBP-2 protocol driver for IEEE-1394 - * - * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) - * jamesg@filanet.com (JSG) - * - * Copyright (C) 2003 Ben Collins <bcollins@debian.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * Brief Description: - * - * This driver implements the Serial Bus Protocol 2 (SBP-2) over IEEE-1394 - * under Linux. The SBP-2 driver is implemented as an IEEE-1394 high-level - * driver. It also registers as a SCSI lower-level driver in order to accept - * SCSI commands for transport using SBP-2. - * - * You may access any attached SBP-2 (usually storage devices) as regular - * SCSI devices. E.g. mount /dev/sda1, fdisk, mkfs, etc.. - * - * See http://www.t10.org/drafts.htm#sbp2 for the final draft of the SBP-2 - * specification and for where to purchase the official standard. - * - * TODO: - * - look into possible improvements of the SCSI error handlers - * - handle Unit_Characteristics.mgt_ORB_timeout and .ORB_size - * - handle Logical_Unit_Number.ordered - * - handle src == 1 in status blocks - * - reimplement the DMA mapping in absence of physical DMA so that - * bus_to_virt is no longer required - * - debug the handling of absent physical DMA - * - replace CONFIG_IEEE1394_SBP2_PHYS_DMA by automatic detection - * (this is easy but depends on the previous two TODO items) - * - make the parameter serialize_io configurable per device - * - move all requests to fetch agent registers into non-atomic context, - * replace all usages of sbp2util_node_write_no_wait by true transactions - * Grep for inline FIXME comments below. - */ - -#include <linux/blkdev.h> -#include <linux/compiler.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/stat.h> -#include <linux/string.h> -#include <linux/stringify.h> -#include <linux/types.h> -#include <linux/wait.h> -#include <linux/workqueue.h> -#include <linux/scatterlist.h> - -#include <asm/byteorder.h> -#include <asm/errno.h> -#include <asm/param.h> -#include <asm/system.h> -#include <asm/types.h> - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA -#include <asm/io.h> /* for bus_to_virt */ -#endif - -#include <scsi/scsi.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_dbg.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_host.h> - -#include "csr1212.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" -#include "nodemgr.h" -#include "sbp2.h" - -/* - * Module load parameter definitions - */ - -/* - * Change max_speed on module load if you have a bad IEEE-1394 - * controller that has trouble running 2KB packets at 400mb. - * - * NOTE: On certain OHCI parts I have seen short packets on async transmit - * (probably due to PCI latency/throughput issues with the part). You can - * bump down the speed if you are running into problems. - */ -static int sbp2_max_speed = IEEE1394_SPEED_MAX; -module_param_named(max_speed, sbp2_max_speed, int, 0644); -MODULE_PARM_DESC(max_speed, "Limit data transfer speed (5 <= 3200, " - "4 <= 1600, 3 <= 800, 2 <= 400, 1 <= 200, 0 = 100 Mb/s)"); - -/* - * Set serialize_io to 0 or N to use dynamically appended lists of command ORBs. - * This is and always has been buggy in multiple subtle ways. See above TODOs. - */ -static int sbp2_serialize_io = 1; -module_param_named(serialize_io, sbp2_serialize_io, bool, 0444); -MODULE_PARM_DESC(serialize_io, "Serialize requests coming from SCSI drivers " - "(default = Y, faster but buggy = N)"); - -/* - * Adjust max_sectors if you'd like to influence how many sectors each SCSI - * command can transfer at most. Please note that some older SBP-2 bridge - * chips are broken for transfers greater or equal to 128KB, therefore - * max_sectors used to be a safe 255 sectors for many years. We now have a - * default of 0 here which means that we let the SCSI stack choose a limit. - * - * The SBP2_WORKAROUND_128K_MAX_TRANS flag, if set either in the workarounds - * module parameter or in the sbp2_workarounds_table[], will override the - * value of max_sectors. We should use sbp2_workarounds_table[] to cover any - * bridge chip which becomes known to need the 255 sectors limit. - */ -static int sbp2_max_sectors; -module_param_named(max_sectors, sbp2_max_sectors, int, 0444); -MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported " - "(default = 0 = use SCSI stack's default)"); - -/* - * Exclusive login to sbp2 device? In most cases, the sbp2 driver should - * do an exclusive login, as it's generally unsafe to have two hosts - * talking to a single sbp2 device at the same time (filesystem coherency, - * etc.). If you're running an sbp2 device that supports multiple logins, - * and you're either running read-only filesystems or some sort of special - * filesystem supporting multiple hosts, e.g. OpenGFS, Oracle Cluster - * File System, or Lustre, then set exclusive_login to zero. - * - * So far only bridges from Oxford Semiconductor are known to support - * concurrent logins. Depending on firmware, four or two concurrent logins - * are possible on OXFW911 and newer Oxsemi bridges. - */ -static int sbp2_exclusive_login = 1; -module_param_named(exclusive_login, sbp2_exclusive_login, bool, 0644); -MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " - "(default = Y, use N for concurrent initiators)"); - -/* - * If any of the following workarounds is required for your device to work, - * please submit the kernel messages logged by sbp2 to the linux1394-devel - * mailing list. - * - * - 128kB max transfer - * Limit transfer size. Necessary for some old bridges. - * - * - 36 byte inquiry - * When scsi_mod probes the device, let the inquiry command look like that - * from MS Windows. - * - * - skip mode page 8 - * Suppress sending of mode_sense for mode page 8 if the device pretends to - * support the SCSI Primary Block commands instead of Reduced Block Commands. - * - * - fix capacity - * Tell sd_mod to correct the last sector number reported by read_capacity. - * Avoids access beyond actual disk limits on devices with an off-by-one bug. - * Don't use this with devices which don't have this bug. - * - * - delay inquiry - * Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry. - * - * - power condition - * Set the power condition field in the START STOP UNIT commands sent by - * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on). - * Some disks need this to spin down or to resume properly. - * - * - override internal blacklist - * Instead of adding to the built-in blacklist, use only the workarounds - * specified in the module load parameter. - * Useful if a blacklist entry interfered with a non-broken device. - */ -static int sbp2_default_workarounds; -module_param_named(workarounds, sbp2_default_workarounds, int, 0644); -MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" - ", 128kB max transfer = " __stringify(SBP2_WORKAROUND_128K_MAX_TRANS) - ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36) - ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8) - ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY) - ", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY) - ", set power condition in start stop unit = " - __stringify(SBP2_WORKAROUND_POWER_CONDITION) - ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) - ", or a combination)"); - -/* - * This influences the format of the sysfs attribute - * /sys/bus/scsi/devices/.../ieee1394_id. - * - * The default format is like in older kernels: %016Lx:%d:%d - * It contains the target's EUI-64, a number given to the logical unit by - * the ieee1394 driver's nodemgr (starting at 0), and the LUN. - * - * The long format is: %016Lx:%06x:%04x - * It contains the target's EUI-64, the unit directory's directory_ID as per - * IEEE 1212 clause 7.7.19, and the LUN. This format comes closest to the - * format of SBP(-3) target port and logical unit identifier as per SAM (SCSI - * Architecture Model) rev.2 to 4 annex A. Therefore and because it is - * independent of the implementation of the ieee1394 nodemgr, the longer format - * is recommended for future use. - */ -static int sbp2_long_sysfs_ieee1394_id; -module_param_named(long_ieee1394_id, sbp2_long_sysfs_ieee1394_id, bool, 0644); -MODULE_PARM_DESC(long_ieee1394_id, "8+3+2 bytes format of ieee1394_id in sysfs " - "(default = backwards-compatible = N, SAM-conforming = Y)"); - - -#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args) -#define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) - -/* - * Globals - */ -static void sbp2scsi_complete_all_commands(struct sbp2_lu *, u32); -static void sbp2scsi_complete_command(struct sbp2_lu *, u32, struct scsi_cmnd *, - void (*)(struct scsi_cmnd *)); -static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *); -static int sbp2_start_device(struct sbp2_lu *); -static void sbp2_remove_device(struct sbp2_lu *); -static int sbp2_login_device(struct sbp2_lu *); -static int sbp2_reconnect_device(struct sbp2_lu *); -static int sbp2_logout_device(struct sbp2_lu *); -static void sbp2_host_reset(struct hpsb_host *); -static int sbp2_handle_status_write(struct hpsb_host *, int, int, quadlet_t *, - u64, size_t, u16); -static int sbp2_agent_reset(struct sbp2_lu *, int); -static void sbp2_parse_unit_directory(struct sbp2_lu *, - struct unit_directory *); -static int sbp2_set_busy_timeout(struct sbp2_lu *); -static int sbp2_max_speed_and_size(struct sbp2_lu *); - - -static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xa, 0xa, 0xa }; - -static DEFINE_RWLOCK(sbp2_hi_logical_units_lock); - -static struct hpsb_highlevel sbp2_highlevel = { - .name = SBP2_DEVICE_NAME, - .host_reset = sbp2_host_reset, -}; - -static const struct hpsb_address_ops sbp2_ops = { - .write = sbp2_handle_status_write -}; - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA -static int sbp2_handle_physdma_write(struct hpsb_host *, int, int, quadlet_t *, - u64, size_t, u16); -static int sbp2_handle_physdma_read(struct hpsb_host *, int, quadlet_t *, u64, - size_t, u16); - -static const struct hpsb_address_ops sbp2_physdma_ops = { - .read = sbp2_handle_physdma_read, - .write = sbp2_handle_physdma_write, -}; -#endif - - -/* - * Interface to driver core and IEEE 1394 core - */ -static const struct ieee1394_device_id sbp2_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = SBP2_SW_VERSION_ENTRY & 0xffffff}, - {} -}; -MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table); - -static int sbp2_probe(struct device *); -static int sbp2_remove(struct device *); -static int sbp2_update(struct unit_directory *); - -static struct hpsb_protocol_driver sbp2_driver = { - .name = SBP2_DEVICE_NAME, - .id_table = sbp2_id_table, - .update = sbp2_update, - .driver = { - .probe = sbp2_probe, - .remove = sbp2_remove, - }, -}; - - -/* - * Interface to SCSI core - */ -static int sbp2scsi_queuecommand(struct scsi_cmnd *, - void (*)(struct scsi_cmnd *)); -static int sbp2scsi_abort(struct scsi_cmnd *); -static int sbp2scsi_reset(struct scsi_cmnd *); -static int sbp2scsi_slave_alloc(struct scsi_device *); -static int sbp2scsi_slave_configure(struct scsi_device *); -static void sbp2scsi_slave_destroy(struct scsi_device *); -static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *, - struct device_attribute *, char *); - -static DEVICE_ATTR(ieee1394_id, S_IRUGO, sbp2_sysfs_ieee1394_id_show, NULL); - -static struct device_attribute *sbp2_sysfs_sdev_attrs[] = { - &dev_attr_ieee1394_id, - NULL -}; - -static struct scsi_host_template sbp2_shost_template = { - .module = THIS_MODULE, - .name = "SBP-2 IEEE-1394", - .proc_name = SBP2_DEVICE_NAME, - .queuecommand = sbp2scsi_queuecommand, - .eh_abort_handler = sbp2scsi_abort, - .eh_device_reset_handler = sbp2scsi_reset, - .slave_alloc = sbp2scsi_slave_alloc, - .slave_configure = sbp2scsi_slave_configure, - .slave_destroy = sbp2scsi_slave_destroy, - .this_id = -1, - .sg_tablesize = SG_ALL, - .use_clustering = ENABLE_CLUSTERING, - .cmd_per_lun = SBP2_MAX_CMDS, - .can_queue = SBP2_MAX_CMDS, - .sdev_attrs = sbp2_sysfs_sdev_attrs, -}; - -#define SBP2_ROM_VALUE_WILDCARD ~0 /* match all */ -#define SBP2_ROM_VALUE_MISSING 0xff000000 /* not present in the unit dir. */ - -/* - * List of devices with known bugs. - * - * The firmware_revision field, masked with 0xffff00, is the best indicator - * for the type of bridge chip of a device. It yields a few false positives - * but this did not break correctly behaving devices so far. - */ -static const struct { - u32 firmware_revision; - u32 model; - unsigned workarounds; -} sbp2_workarounds_table[] = { - /* DViCO Momobay CX-1 with TSB42AA9 bridge */ { - .firmware_revision = 0x002800, - .model = 0x001010, - .workarounds = SBP2_WORKAROUND_INQUIRY_36 | - SBP2_WORKAROUND_MODE_SENSE_8 | - SBP2_WORKAROUND_POWER_CONDITION, - }, - /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { - .firmware_revision = 0x002800, - .model = 0x000000, - .workarounds = SBP2_WORKAROUND_POWER_CONDITION, - }, - /* Initio bridges, actually only needed for some older ones */ { - .firmware_revision = 0x000200, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_INQUIRY_36, - }, - /* PL-3507 bridge with Prolific firmware */ { - .firmware_revision = 0x012800, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_POWER_CONDITION, - }, - /* Symbios bridge */ { - .firmware_revision = 0xa0b800, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, - }, - /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ { - .firmware_revision = 0x002600, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, - }, - /* - * iPod 2nd generation: needs 128k max transfer size workaround - * iPod 3rd generation: needs fix capacity workaround - */ - { - .firmware_revision = 0x0a2700, - .model = 0x000000, - .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS | - SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod 4th generation */ { - .firmware_revision = 0x0a2700, - .model = 0x000021, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod mini */ { - .firmware_revision = 0x0a2700, - .model = 0x000022, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod mini */ { - .firmware_revision = 0x0a2700, - .model = 0x000023, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod Photo */ { - .firmware_revision = 0x0a2700, - .model = 0x00007e, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - } -}; - -/************************************** - * General utility functions - **************************************/ - -#ifndef __BIG_ENDIAN -/* - * Converts a buffer from be32 to cpu byte ordering. Length is in bytes. - */ -static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length) -{ - u32 *temp = buffer; - - for (length = (length >> 2); length--; ) - temp[length] = be32_to_cpu(temp[length]); -} - -/* - * Converts a buffer from cpu to be32 byte ordering. Length is in bytes. - */ -static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length) -{ - u32 *temp = buffer; - - for (length = (length >> 2); length--; ) - temp[length] = cpu_to_be32(temp[length]); -} -#else /* BIG_ENDIAN */ -/* Why waste the cpu cycles? */ -#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0) -#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0) -#endif - -static DECLARE_WAIT_QUEUE_HEAD(sbp2_access_wq); - -/* - * Waits for completion of an SBP-2 access request. - * Returns nonzero if timed out or prematurely interrupted. - */ -static int sbp2util_access_timeout(struct sbp2_lu *lu, int timeout) -{ - long leftover; - - leftover = wait_event_interruptible_timeout( - sbp2_access_wq, lu->access_complete, timeout); - lu->access_complete = 0; - return leftover <= 0; -} - -static void sbp2_free_packet(void *packet) -{ - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); -} - -/* - * This is much like hpsb_node_write(), except it ignores the response - * subaction and returns immediately. Can be used from atomic context. - */ -static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr, - quadlet_t *buf, size_t len) -{ - struct hpsb_packet *packet; - - packet = hpsb_make_writepacket(ne->host, ne->nodeid, addr, buf, len); - if (!packet) - return -ENOMEM; - - hpsb_set_packet_complete_task(packet, sbp2_free_packet, packet); - hpsb_node_fill_packet(ne, packet); - if (hpsb_send_packet(packet) < 0) { - sbp2_free_packet(packet); - return -EIO; - } - return 0; -} - -static void sbp2util_notify_fetch_agent(struct sbp2_lu *lu, u64 offset, - quadlet_t *data, size_t len) -{ - /* There is a small window after a bus reset within which the node - * entry's generation is current but the reconnect wasn't completed. */ - if (unlikely(atomic_read(&lu->state) == SBP2LU_STATE_IN_RESET)) - return; - - if (hpsb_node_write(lu->ne, lu->command_block_agent_addr + offset, - data, len)) - SBP2_ERR("sbp2util_notify_fetch_agent failed."); - - /* Now accept new SCSI commands, unless a bus reset happended during - * hpsb_node_write. */ - if (likely(atomic_read(&lu->state) != SBP2LU_STATE_IN_RESET)) - scsi_unblock_requests(lu->shost); -} - -static void sbp2util_write_orb_pointer(struct work_struct *work) -{ - struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work); - quadlet_t data[2]; - - data[0] = ORB_SET_NODE_ID(lu->hi->host->node_id); - data[1] = lu->last_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - sbp2util_notify_fetch_agent(lu, SBP2_ORB_POINTER_OFFSET, data, 8); -} - -static void sbp2util_write_doorbell(struct work_struct *work) -{ - struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work); - - sbp2util_notify_fetch_agent(lu, SBP2_DOORBELL_OFFSET, NULL, 4); -} - -static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu) -{ - struct sbp2_command_info *cmd; - struct device *dmadev = lu->hi->host->device.parent; - int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; - - for (i = 0; i < orbs; i++) { - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) - goto failed_alloc; - - cmd->command_orb_dma = - dma_map_single(dmadev, &cmd->command_orb, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - if (dma_mapping_error(dmadev, cmd->command_orb_dma)) - goto failed_orb; - - cmd->sge_dma = - dma_map_single(dmadev, &cmd->scatter_gather_element, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - if (dma_mapping_error(dmadev, cmd->sge_dma)) - goto failed_sge; - - INIT_LIST_HEAD(&cmd->list); - list_add_tail(&cmd->list, &lu->cmd_orb_completed); - } - return 0; - -failed_sge: - dma_unmap_single(dmadev, cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); -failed_orb: - kfree(cmd); -failed_alloc: - return -ENOMEM; -} - -static void sbp2util_remove_command_orb_pool(struct sbp2_lu *lu, - struct hpsb_host *host) -{ - struct list_head *lh, *next; - struct sbp2_command_info *cmd; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (!list_empty(&lu->cmd_orb_completed)) - list_for_each_safe(lh, next, &lu->cmd_orb_completed) { - cmd = list_entry(lh, struct sbp2_command_info, list); - dma_unmap_single(host->device.parent, - cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - dma_unmap_single(host->device.parent, cmd->sge_dma, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - kfree(cmd); - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - return; -} - -/* - * Finds the sbp2_command for a given outstanding command ORB. - * Only looks at the in-use list. - */ -static struct sbp2_command_info *sbp2util_find_command_for_orb( - struct sbp2_lu *lu, dma_addr_t orb) -{ - struct sbp2_command_info *cmd; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (!list_empty(&lu->cmd_orb_inuse)) - list_for_each_entry(cmd, &lu->cmd_orb_inuse, list) - if (cmd->command_orb_dma == orb) { - spin_unlock_irqrestore( - &lu->cmd_orb_lock, flags); - return cmd; - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - return NULL; -} - -/* - * Finds the sbp2_command for a given outstanding SCpnt. - * Only looks at the in-use list. - * Must be called with lu->cmd_orb_lock held. - */ -static struct sbp2_command_info *sbp2util_find_command_for_SCpnt( - struct sbp2_lu *lu, void *SCpnt) -{ - struct sbp2_command_info *cmd; - - if (!list_empty(&lu->cmd_orb_inuse)) - list_for_each_entry(cmd, &lu->cmd_orb_inuse, list) - if (cmd->Current_SCpnt == SCpnt) - return cmd; - return NULL; -} - -static struct sbp2_command_info *sbp2util_allocate_command_orb( - struct sbp2_lu *lu, - struct scsi_cmnd *Current_SCpnt, - void (*Current_done)(struct scsi_cmnd *)) -{ - struct list_head *lh; - struct sbp2_command_info *cmd = NULL; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (!list_empty(&lu->cmd_orb_completed)) { - lh = lu->cmd_orb_completed.next; - list_del(lh); - cmd = list_entry(lh, struct sbp2_command_info, list); - cmd->Current_done = Current_done; - cmd->Current_SCpnt = Current_SCpnt; - list_add_tail(&cmd->list, &lu->cmd_orb_inuse); - } else - SBP2_ERR("%s: no orbs available", __func__); - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - return cmd; -} - -/* - * Unmaps the DMAs of a command and moves the command to the completed ORB list. - * Must be called with lu->cmd_orb_lock held. - */ -static void sbp2util_mark_command_completed(struct sbp2_lu *lu, - struct sbp2_command_info *cmd) -{ - if (scsi_sg_count(cmd->Current_SCpnt)) - dma_unmap_sg(lu->ud->ne->host->device.parent, - scsi_sglist(cmd->Current_SCpnt), - scsi_sg_count(cmd->Current_SCpnt), - cmd->Current_SCpnt->sc_data_direction); - list_move_tail(&cmd->list, &lu->cmd_orb_completed); -} - -/* - * Is lu valid? Is the 1394 node still present? - */ -static inline int sbp2util_node_is_available(struct sbp2_lu *lu) -{ - return lu && lu->ne && !lu->ne->in_limbo; -} - -/********************************************* - * IEEE-1394 core driver stack related section - *********************************************/ - -static int sbp2_probe(struct device *dev) -{ - struct unit_directory *ud; - struct sbp2_lu *lu; - - ud = container_of(dev, struct unit_directory, device); - - /* Don't probe UD's that have the LUN flag. We'll probe the LUN(s) - * instead. */ - if (ud->flags & UNIT_DIRECTORY_HAS_LUN_DIRECTORY) - return -ENODEV; - - lu = sbp2_alloc_device(ud); - if (!lu) - return -ENOMEM; - - sbp2_parse_unit_directory(lu, ud); - return sbp2_start_device(lu); -} - -static int sbp2_remove(struct device *dev) -{ - struct unit_directory *ud; - struct sbp2_lu *lu; - struct scsi_device *sdev; - - ud = container_of(dev, struct unit_directory, device); - lu = dev_get_drvdata(&ud->device); - if (!lu) - return 0; - - if (lu->shost) { - /* Get rid of enqueued commands if there is no chance to - * send them. */ - if (!sbp2util_node_is_available(lu)) - sbp2scsi_complete_all_commands(lu, DID_NO_CONNECT); - /* scsi_remove_device() may trigger shutdown functions of SCSI - * highlevel drivers which would deadlock if blocked. */ - atomic_set(&lu->state, SBP2LU_STATE_IN_SHUTDOWN); - scsi_unblock_requests(lu->shost); - } - sdev = lu->sdev; - if (sdev) { - lu->sdev = NULL; - scsi_remove_device(sdev); - } - - sbp2_logout_device(lu); - sbp2_remove_device(lu); - - return 0; -} - -static int sbp2_update(struct unit_directory *ud) -{ - struct sbp2_lu *lu = dev_get_drvdata(&ud->device); - - if (sbp2_reconnect_device(lu) != 0) { - /* - * Reconnect failed. If another bus reset happened, - * let nodemgr proceed and call sbp2_update again later - * (or sbp2_remove if this node went away). - */ - if (!hpsb_node_entry_valid(lu->ne)) - return 0; - /* - * Or the target rejected the reconnect because we weren't - * fast enough. Try a regular login, but first log out - * just in case of any weirdness. - */ - sbp2_logout_device(lu); - - if (sbp2_login_device(lu) != 0) { - if (!hpsb_node_entry_valid(lu->ne)) - return 0; - - /* Maybe another initiator won the login. */ - SBP2_ERR("Failed to reconnect to sbp2 device!"); - return -EBUSY; - } - } - - sbp2_set_busy_timeout(lu); - sbp2_agent_reset(lu, 1); - sbp2_max_speed_and_size(lu); - - /* Complete any pending commands with busy (so they get retried) - * and remove them from our queue. */ - sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY); - - /* Accept new commands unless there was another bus reset in the - * meantime. */ - if (hpsb_node_entry_valid(lu->ne)) { - atomic_set(&lu->state, SBP2LU_STATE_RUNNING); - scsi_unblock_requests(lu->shost); - } - return 0; -} - -static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) -{ - struct sbp2_fwhost_info *hi; - struct Scsi_Host *shost = NULL; - struct sbp2_lu *lu = NULL; - unsigned long flags; - - lu = kzalloc(sizeof(*lu), GFP_KERNEL); - if (!lu) { - SBP2_ERR("failed to create lu"); - goto failed_alloc; - } - - lu->ne = ud->ne; - lu->ud = ud; - lu->speed_code = IEEE1394_SPEED_100; - lu->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100]; - lu->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE; - INIT_LIST_HEAD(&lu->cmd_orb_inuse); - INIT_LIST_HEAD(&lu->cmd_orb_completed); - INIT_LIST_HEAD(&lu->lu_list); - spin_lock_init(&lu->cmd_orb_lock); - atomic_set(&lu->state, SBP2LU_STATE_RUNNING); - INIT_WORK(&lu->protocol_work, NULL); - - dev_set_drvdata(&ud->device, lu); - - hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host); - if (!hi) { - hi = hpsb_create_hostinfo(&sbp2_highlevel, ud->ne->host, - sizeof(*hi)); - if (!hi) { - SBP2_ERR("failed to allocate hostinfo"); - goto failed_alloc; - } - hi->host = ud->ne->host; - INIT_LIST_HEAD(&hi->logical_units); - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA - /* Handle data movement if physical dma is not - * enabled or not supported on host controller */ - if (!hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, - &sbp2_physdma_ops, - 0x0ULL, 0xfffffffcULL)) { - SBP2_ERR("failed to register lower 4GB address range"); - goto failed_alloc; - } -#endif - } - - if (dma_get_max_seg_size(hi->host->device.parent) > SBP2_MAX_SEG_SIZE) - BUG_ON(dma_set_max_seg_size(hi->host->device.parent, - SBP2_MAX_SEG_SIZE)); - - /* Prevent unloading of the 1394 host */ - if (!try_module_get(hi->host->driver->owner)) { - SBP2_ERR("failed to get a reference on 1394 host driver"); - goto failed_alloc; - } - - lu->hi = hi; - - write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - list_add_tail(&lu->lu_list, &hi->logical_units); - write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); - - /* Register the status FIFO address range. We could use the same FIFO - * for targets at different nodes. However we need different FIFOs per - * target in order to support multi-unit devices. - * The FIFO is located out of the local host controller's physical range - * but, if possible, within the posted write area. Status writes will - * then be performed as unified transactions. This slightly reduces - * bandwidth usage, and some Prolific based devices seem to require it. - */ - lu->status_fifo_addr = hpsb_allocate_and_register_addrspace( - &sbp2_highlevel, ud->ne->host, &sbp2_ops, - sizeof(struct sbp2_status_block), sizeof(quadlet_t), - ud->ne->host->low_addr_space, CSR1212_ALL_SPACE_END); - if (lu->status_fifo_addr == CSR1212_INVALID_ADDR_SPACE) { - SBP2_ERR("failed to allocate status FIFO address range"); - goto failed_alloc; - } - - shost = scsi_host_alloc(&sbp2_shost_template, sizeof(unsigned long)); - if (!shost) { - SBP2_ERR("failed to register scsi host"); - goto failed_alloc; - } - - shost->hostdata[0] = (unsigned long)lu; - shost->max_cmd_len = SBP2_MAX_CDB_SIZE; - - if (!scsi_add_host(shost, &ud->device)) { - lu->shost = shost; - return lu; - } - - SBP2_ERR("failed to add scsi host"); - scsi_host_put(shost); - -failed_alloc: - sbp2_remove_device(lu); - return NULL; -} - -static void sbp2_host_reset(struct hpsb_host *host) -{ - struct sbp2_fwhost_info *hi; - struct sbp2_lu *lu; - unsigned long flags; - - hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - if (!hi) - return; - - read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - - list_for_each_entry(lu, &hi->logical_units, lu_list) - if (atomic_cmpxchg(&lu->state, - SBP2LU_STATE_RUNNING, SBP2LU_STATE_IN_RESET) - == SBP2LU_STATE_RUNNING) - scsi_block_requests(lu->shost); - - read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); -} - -static int sbp2_start_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - int error; - - lu->login_response = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_response), - &lu->login_response_dma, GFP_KERNEL); - if (!lu->login_response) - goto alloc_fail; - - lu->query_logins_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_orb), - &lu->query_logins_orb_dma, GFP_KERNEL); - if (!lu->query_logins_orb) - goto alloc_fail; - - lu->query_logins_response = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_response), - &lu->query_logins_response_dma, GFP_KERNEL); - if (!lu->query_logins_response) - goto alloc_fail; - - lu->reconnect_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_reconnect_orb), - &lu->reconnect_orb_dma, GFP_KERNEL); - if (!lu->reconnect_orb) - goto alloc_fail; - - lu->logout_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_logout_orb), - &lu->logout_orb_dma, GFP_KERNEL); - if (!lu->logout_orb) - goto alloc_fail; - - lu->login_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_orb), - &lu->login_orb_dma, GFP_KERNEL); - if (!lu->login_orb) - goto alloc_fail; - - if (sbp2util_create_command_orb_pool(lu)) - goto alloc_fail; - - /* Wait a second before trying to log in. Previously logged in - * initiators need a chance to reconnect. */ - if (msleep_interruptible(1000)) { - sbp2_remove_device(lu); - return -EINTR; - } - - if (sbp2_login_device(lu)) { - sbp2_remove_device(lu); - return -EBUSY; - } - - sbp2_set_busy_timeout(lu); - sbp2_agent_reset(lu, 1); - sbp2_max_speed_and_size(lu); - - if (lu->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) - ssleep(SBP2_INQUIRY_DELAY); - - error = scsi_add_device(lu->shost, 0, lu->ud->id, 0); - if (error) { - SBP2_ERR("scsi_add_device failed"); - sbp2_logout_device(lu); - sbp2_remove_device(lu); - return error; - } - - return 0; - -alloc_fail: - SBP2_ERR("Could not allocate memory for lu"); - sbp2_remove_device(lu); - return -ENOMEM; -} - -static void sbp2_remove_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi; - unsigned long flags; - - if (!lu) - return; - hi = lu->hi; - if (!hi) - goto no_hi; - - if (lu->shost) { - scsi_remove_host(lu->shost); - scsi_host_put(lu->shost); - } - flush_scheduled_work(); - sbp2util_remove_command_orb_pool(lu, hi->host); - - write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - list_del(&lu->lu_list); - write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); - - if (lu->login_response) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_response), - lu->login_response, - lu->login_response_dma); - if (lu->login_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_orb), - lu->login_orb, - lu->login_orb_dma); - if (lu->reconnect_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_reconnect_orb), - lu->reconnect_orb, - lu->reconnect_orb_dma); - if (lu->logout_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_logout_orb), - lu->logout_orb, - lu->logout_orb_dma); - if (lu->query_logins_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_orb), - lu->query_logins_orb, - lu->query_logins_orb_dma); - if (lu->query_logins_response) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_response), - lu->query_logins_response, - lu->query_logins_response_dma); - - if (lu->status_fifo_addr != CSR1212_INVALID_ADDR_SPACE) - hpsb_unregister_addrspace(&sbp2_highlevel, hi->host, - lu->status_fifo_addr); - - dev_set_drvdata(&lu->ud->device, NULL); - - module_put(hi->host->driver->owner); -no_hi: - kfree(lu); -} - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA -/* - * Deal with write requests on adapters which do not support physical DMA or - * have it switched off. - */ -static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, - int destid, quadlet_t *data, u64 addr, - size_t length, u16 flags) -{ - memcpy(bus_to_virt((u32) addr), data, length); - return RCODE_COMPLETE; -} - -/* - * Deal with read requests on adapters which do not support physical DMA or - * have it switched off. - */ -static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid, - quadlet_t *data, u64 addr, size_t length, - u16 flags) -{ - memcpy(data, bus_to_virt((u32) addr), length); - return RCODE_COMPLETE; -} -#endif - -/************************************** - * SBP-2 protocol related section - **************************************/ - -static int sbp2_query_logins(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - int max_logins; - int active_logins; - - lu->query_logins_orb->reserved1 = 0x0; - lu->query_logins_orb->reserved2 = 0x0; - - lu->query_logins_orb->query_response_lo = lu->query_logins_response_dma; - lu->query_logins_orb->query_response_hi = - ORB_SET_NODE_ID(hi->host->node_id); - lu->query_logins_orb->lun_misc = - ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST); - lu->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1); - lu->query_logins_orb->lun_misc |= ORB_SET_LUN(lu->lun); - - lu->query_logins_orb->reserved_resp_length = - ORB_SET_QUERY_LOGINS_RESP_LENGTH( - sizeof(struct sbp2_query_logins_response)); - - lu->query_logins_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->query_logins_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->query_logins_orb, - sizeof(struct sbp2_query_logins_orb)); - - memset(lu->query_logins_response, 0, - sizeof(struct sbp2_query_logins_response)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->query_logins_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - - if (sbp2util_access_timeout(lu, 2*HZ)) { - SBP2_INFO("Error querying logins to SBP-2 device - timed out"); - return -EIO; - } - - if (lu->status_block.ORB_offset_lo != lu->query_logins_orb_dma) { - SBP2_INFO("Error querying logins to SBP-2 device - timed out"); - return -EIO; - } - - if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { - SBP2_INFO("Error querying logins to SBP-2 device - failed"); - return -EIO; - } - - sbp2util_cpu_to_be32_buffer(lu->query_logins_response, - sizeof(struct sbp2_query_logins_response)); - - max_logins = RESPONSE_GET_MAX_LOGINS( - lu->query_logins_response->length_max_logins); - SBP2_INFO("Maximum concurrent logins supported: %d", max_logins); - - active_logins = RESPONSE_GET_ACTIVE_LOGINS( - lu->query_logins_response->length_max_logins); - SBP2_INFO("Number of active logins: %d", active_logins); - - if (active_logins >= max_logins) { - return -EIO; - } - - return 0; -} - -static int sbp2_login_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - - if (!lu->login_orb) - return -EIO; - - if (!sbp2_exclusive_login && sbp2_query_logins(lu)) { - SBP2_INFO("Device does not support any more concurrent logins"); - return -EIO; - } - - /* assume no password */ - lu->login_orb->password_hi = 0; - lu->login_orb->password_lo = 0; - - lu->login_orb->login_response_lo = lu->login_response_dma; - lu->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id); - lu->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST); - - /* one second reconnect time */ - lu->login_orb->lun_misc |= ORB_SET_RECONNECT(0); - lu->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(sbp2_exclusive_login); - lu->login_orb->lun_misc |= ORB_SET_NOTIFY(1); - lu->login_orb->lun_misc |= ORB_SET_LUN(lu->lun); - - lu->login_orb->passwd_resp_lengths = - ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); - - lu->login_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->login_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->login_orb, - sizeof(struct sbp2_login_orb)); - - memset(lu->login_response, 0, sizeof(struct sbp2_login_response)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->login_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - - /* wait up to 20 seconds for login status */ - if (sbp2util_access_timeout(lu, 20*HZ)) { - SBP2_ERR("Error logging into SBP-2 device - timed out"); - return -EIO; - } - - /* make sure that the returned status matches the login ORB */ - if (lu->status_block.ORB_offset_lo != lu->login_orb_dma) { - SBP2_ERR("Error logging into SBP-2 device - timed out"); - return -EIO; - } - - if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { - SBP2_ERR("Error logging into SBP-2 device - failed"); - return -EIO; - } - - sbp2util_cpu_to_be32_buffer(lu->login_response, - sizeof(struct sbp2_login_response)); - lu->command_block_agent_addr = - ((u64)lu->login_response->command_block_agent_hi) << 32; - lu->command_block_agent_addr |= - ((u64)lu->login_response->command_block_agent_lo); - lu->command_block_agent_addr &= 0x0000ffffffffffffULL; - - SBP2_INFO("Logged into SBP-2 device"); - return 0; -} - -static int sbp2_logout_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - int error; - - lu->logout_orb->reserved1 = 0x0; - lu->logout_orb->reserved2 = 0x0; - lu->logout_orb->reserved3 = 0x0; - lu->logout_orb->reserved4 = 0x0; - - lu->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST); - lu->logout_orb->login_ID_misc |= - ORB_SET_LOGIN_ID(lu->login_response->length_login_ID); - lu->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1); - - lu->logout_orb->reserved5 = 0x0; - lu->logout_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->logout_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->logout_orb, - sizeof(struct sbp2_logout_orb)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->logout_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - if (error) - return error; - - /* wait up to 1 second for the device to complete logout */ - if (sbp2util_access_timeout(lu, HZ)) - return -EIO; - - SBP2_INFO("Logged out of SBP-2 device"); - return 0; -} - -static int sbp2_reconnect_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - int error; - - lu->reconnect_orb->reserved1 = 0x0; - lu->reconnect_orb->reserved2 = 0x0; - lu->reconnect_orb->reserved3 = 0x0; - lu->reconnect_orb->reserved4 = 0x0; - - lu->reconnect_orb->login_ID_misc = - ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST); - lu->reconnect_orb->login_ID_misc |= - ORB_SET_LOGIN_ID(lu->login_response->length_login_ID); - lu->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1); - - lu->reconnect_orb->reserved5 = 0x0; - lu->reconnect_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->reconnect_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->reconnect_orb, - sizeof(struct sbp2_reconnect_orb)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->reconnect_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - if (error) - return error; - - /* wait up to 1 second for reconnect status */ - if (sbp2util_access_timeout(lu, HZ)) { - SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); - return -EIO; - } - - /* make sure that the returned status matches the reconnect ORB */ - if (lu->status_block.ORB_offset_lo != lu->reconnect_orb_dma) { - SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); - return -EIO; - } - - if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { - SBP2_ERR("Error reconnecting to SBP-2 device - failed"); - return -EIO; - } - - SBP2_INFO("Reconnected to SBP-2 device"); - return 0; -} - -/* - * Set the target node's Single Phase Retry limit. Affects the target's retry - * behaviour if our node is too busy to accept requests. - */ -static int sbp2_set_busy_timeout(struct sbp2_lu *lu) -{ - quadlet_t data; - - data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE); - if (hpsb_node_write(lu->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4)) - SBP2_ERR("%s error", __func__); - return 0; -} - -static void sbp2_parse_unit_directory(struct sbp2_lu *lu, - struct unit_directory *ud) -{ - struct csr1212_keyval *kv; - struct csr1212_dentry *dentry; - u64 management_agent_addr; - u32 firmware_revision, model; - unsigned workarounds; - int i; - - management_agent_addr = 0; - firmware_revision = SBP2_ROM_VALUE_MISSING; - model = ud->flags & UNIT_DIRECTORY_MODEL_ID ? - ud->model_id : SBP2_ROM_VALUE_MISSING; - - csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) { - switch (kv->key.id) { - case CSR1212_KV_ID_DEPENDENT_INFO: - if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET) - management_agent_addr = - CSR1212_REGISTER_SPACE_BASE + - (kv->value.csr_offset << 2); - - else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) - lu->lun = ORB_SET_LUN(kv->value.immediate); - break; - - - case SBP2_FIRMWARE_REVISION_KEY: - firmware_revision = kv->value.immediate; - break; - - default: - /* FIXME: Check for SBP2_UNIT_CHARACTERISTICS_KEY - * mgt_ORB_timeout and ORB_size, SBP-2 clause 7.4.8. */ - - /* FIXME: Check for SBP2_DEVICE_TYPE_AND_LUN_KEY. - * Its "ordered" bit has consequences for command ORB - * list handling. See SBP-2 clauses 4.6, 7.4.11, 10.2 */ - break; - } - } - - workarounds = sbp2_default_workarounds; - - if (!(workarounds & SBP2_WORKAROUND_OVERRIDE)) - for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) { - if (sbp2_workarounds_table[i].firmware_revision != - SBP2_ROM_VALUE_WILDCARD && - sbp2_workarounds_table[i].firmware_revision != - (firmware_revision & 0xffff00)) - continue; - if (sbp2_workarounds_table[i].model != - SBP2_ROM_VALUE_WILDCARD && - sbp2_workarounds_table[i].model != model) - continue; - workarounds |= sbp2_workarounds_table[i].workarounds; - break; - } - - if (workarounds) - SBP2_INFO("Workarounds for node " NODE_BUS_FMT ": 0x%x " - "(firmware_revision 0x%06x, vendor_id 0x%06x," - " model_id 0x%06x)", - NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), - workarounds, firmware_revision, ud->vendor_id, - model); - - /* We would need one SCSI host template for each target to adjust - * max_sectors on the fly, therefore warn only. */ - if (workarounds & SBP2_WORKAROUND_128K_MAX_TRANS && - (sbp2_max_sectors * 512) > (128 * 1024)) - SBP2_INFO("Node " NODE_BUS_FMT ": Bridge only supports 128KB " - "max transfer size. WARNING: Current max_sectors " - "setting is larger than 128KB (%d sectors)", - NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), - sbp2_max_sectors); - - /* If this is a logical unit directory entry, process the parent - * to get the values. */ - if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) { - struct unit_directory *parent_ud = container_of( - ud->device.parent, struct unit_directory, device); - sbp2_parse_unit_directory(lu, parent_ud); - } else { - lu->management_agent_addr = management_agent_addr; - lu->workarounds = workarounds; - if (ud->flags & UNIT_DIRECTORY_HAS_LUN) - lu->lun = ORB_SET_LUN(ud->lun); - } -} - -#define SBP2_PAYLOAD_TO_BYTES(p) (1 << ((p) + 2)) - -/* - * This function is called in order to determine the max speed and packet - * size we can use in our ORBs. Note, that we (the driver and host) only - * initiate the transaction. The SBP-2 device actually transfers the data - * (by reading from the DMA area we tell it). This means that the SBP-2 - * device decides the actual maximum data it can transfer. We just tell it - * the speed that it needs to use, and the max_rec the host supports, and - * it takes care of the rest. - */ -static int sbp2_max_speed_and_size(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - u8 payload; - - lu->speed_code = hi->host->speed[NODEID_TO_NODE(lu->ne->nodeid)]; - - if (lu->speed_code > sbp2_max_speed) { - lu->speed_code = sbp2_max_speed; - SBP2_INFO("Reducing speed to %s", - hpsb_speedto_str[sbp2_max_speed]); - } - - /* Payload size is the lesser of what our speed supports and what - * our host supports. */ - payload = min(sbp2_speedto_max_payload[lu->speed_code], - (u8) (hi->host->csr.max_rec - 1)); - - /* If physical DMA is off, work around limitation in ohci1394: - * packet size must not exceed PAGE_SIZE */ - if (lu->ne->host->low_addr_space < (1ULL << 32)) - while (SBP2_PAYLOAD_TO_BYTES(payload) + 24 > PAGE_SIZE && - payload) - payload--; - - SBP2_INFO("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]", - NODE_BUS_ARGS(hi->host, lu->ne->nodeid), - hpsb_speedto_str[lu->speed_code], - SBP2_PAYLOAD_TO_BYTES(payload)); - - lu->max_payload_size = payload; - return 0; -} - -static int sbp2_agent_reset(struct sbp2_lu *lu, int wait) -{ - quadlet_t data; - u64 addr; - int retval; - unsigned long flags; - - /* flush lu->protocol_work */ - if (wait) - flush_scheduled_work(); - - data = ntohl(SBP2_AGENT_RESET_DATA); - addr = lu->command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; - - if (wait) - retval = hpsb_node_write(lu->ne, addr, &data, 4); - else - retval = sbp2util_node_write_no_wait(lu->ne, addr, &data, 4); - - if (retval < 0) { - SBP2_ERR("hpsb_node_write failed.\n"); - return -EIO; - } - - /* make sure that the ORB_POINTER is written on next command */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - lu->last_orb = NULL; - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - return 0; -} - -static int sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb, - struct sbp2_fwhost_info *hi, - struct sbp2_command_info *cmd, - unsigned int sg_count, - struct scatterlist *sg, - u32 orb_direction, - enum dma_data_direction dma_dir) -{ - struct device *dmadev = hi->host->device.parent; - struct sbp2_unrestricted_page_table *pt; - int i, n; - - n = dma_map_sg(dmadev, sg, sg_count, dma_dir); - if (n == 0) - return -ENOMEM; - - orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); - orb->misc |= ORB_SET_DIRECTION(orb_direction); - - /* special case if only one element (and less than 64KB in size) */ - if (n == 1) { - orb->misc |= ORB_SET_DATA_SIZE(sg_dma_len(sg)); - orb->data_descriptor_lo = sg_dma_address(sg); - } else { - pt = &cmd->scatter_gather_element[0]; - - dma_sync_single_for_cpu(dmadev, cmd->sge_dma, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - - for_each_sg(sg, sg, n, i) { - pt[i].high = cpu_to_be32(sg_dma_len(sg) << 16); - pt[i].low = cpu_to_be32(sg_dma_address(sg)); - } - - orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1) | - ORB_SET_DATA_SIZE(n); - orb->data_descriptor_lo = cmd->sge_dma; - - dma_sync_single_for_device(dmadev, cmd->sge_dma, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - } - return 0; -} - -static int sbp2_create_command_orb(struct sbp2_lu *lu, - struct sbp2_command_info *cmd, - struct scsi_cmnd *SCpnt) -{ - struct device *dmadev = lu->hi->host->device.parent; - struct sbp2_command_orb *orb = &cmd->command_orb; - unsigned int scsi_request_bufflen = scsi_bufflen(SCpnt); - enum dma_data_direction dma_dir = SCpnt->sc_data_direction; - u32 orb_direction; - int ret; - - dma_sync_single_for_cpu(dmadev, cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); - /* - * Set-up our command ORB. - * - * NOTE: We're doing unrestricted page tables (s/g), as this is - * best performance (at least with the devices I have). This means - * that data_size becomes the number of s/g elements, and - * page_size should be zero (for unrestricted). - */ - orb->next_ORB_hi = ORB_SET_NULL_PTR(1); - orb->next_ORB_lo = 0x0; - orb->misc = ORB_SET_MAX_PAYLOAD(lu->max_payload_size); - orb->misc |= ORB_SET_SPEED(lu->speed_code); - orb->misc |= ORB_SET_NOTIFY(1); - - if (dma_dir == DMA_NONE) - orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; - else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen) - orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; - else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen) - orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; - else { - SBP2_INFO("Falling back to DMA_NONE"); - orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; - } - - /* set up our page table stuff */ - if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { - orb->data_descriptor_hi = 0x0; - orb->data_descriptor_lo = 0x0; - orb->misc |= ORB_SET_DIRECTION(1); - ret = 0; - } else { - ret = sbp2_prep_command_orb_sg(orb, lu->hi, cmd, - scsi_sg_count(SCpnt), - scsi_sglist(SCpnt), - orb_direction, dma_dir); - } - sbp2util_cpu_to_be32_buffer(orb, sizeof(*orb)); - - memset(orb->cdb, 0, sizeof(orb->cdb)); - memcpy(orb->cdb, SCpnt->cmnd, SCpnt->cmd_len); - - dma_sync_single_for_device(dmadev, cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); - return ret; -} - -static void sbp2_link_orb_command(struct sbp2_lu *lu, - struct sbp2_command_info *cmd) -{ - struct sbp2_fwhost_info *hi = lu->hi; - struct sbp2_command_orb *last_orb; - dma_addr_t last_orb_dma; - u64 addr = lu->command_block_agent_addr; - quadlet_t data[2]; - size_t length; - unsigned long flags; - - /* check to see if there are any previous orbs to use */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - last_orb = lu->last_orb; - last_orb_dma = lu->last_orb_dma; - if (!last_orb) { - /* - * last_orb == NULL means: We know that the target's fetch agent - * is not active right now. - */ - addr += SBP2_ORB_POINTER_OFFSET; - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = cmd->command_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - length = 8; - } else { - /* - * last_orb != NULL means: We know that the target's fetch agent - * is (very probably) not dead or in reset state right now. - * We have an ORB already sent that we can append a new one to. - * The target's fetch agent may or may not have read this - * previous ORB yet. - */ - dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma); - wmb(); - /* Tells hardware that this pointer is valid */ - last_orb->next_ORB_hi = 0; - dma_sync_single_for_device(hi->host->device.parent, - last_orb_dma, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - addr += SBP2_DOORBELL_OFFSET; - data[0] = 0; - length = 4; - } - lu->last_orb = &cmd->command_orb; - lu->last_orb_dma = cmd->command_orb_dma; - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - if (sbp2util_node_write_no_wait(lu->ne, addr, data, length)) { - /* - * sbp2util_node_write_no_wait failed. We certainly ran out - * of transaction labels, perhaps just because there were no - * context switches which gave khpsbpkt a chance to collect - * free tlabels. Try again in non-atomic context. If necessary, - * the workqueue job will sleep to guaranteedly get a tlabel. - * We do not accept new commands until the job is over. - */ - scsi_block_requests(lu->shost); - PREPARE_WORK(&lu->protocol_work, - last_orb ? sbp2util_write_doorbell: - sbp2util_write_orb_pointer); - schedule_work(&lu->protocol_work); - } -} - -static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - struct sbp2_command_info *cmd; - - cmd = sbp2util_allocate_command_orb(lu, SCpnt, done); - if (!cmd) - return -EIO; - - if (sbp2_create_command_orb(lu, cmd, SCpnt)) - return -ENOMEM; - - sbp2_link_orb_command(lu, cmd); - return 0; -} - -/* - * Translates SBP-2 status into SCSI sense data for check conditions - */ -static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, - unchar *sense_data) -{ - /* OK, it's pretty ugly... ;-) */ - sense_data[0] = 0x70; - sense_data[1] = 0x0; - sense_data[2] = sbp2_status[9]; - sense_data[3] = sbp2_status[12]; - sense_data[4] = sbp2_status[13]; - sense_data[5] = sbp2_status[14]; - sense_data[6] = sbp2_status[15]; - sense_data[7] = 10; - sense_data[8] = sbp2_status[16]; - sense_data[9] = sbp2_status[17]; - sense_data[10] = sbp2_status[18]; - sense_data[11] = sbp2_status[19]; - sense_data[12] = sbp2_status[10]; - sense_data[13] = sbp2_status[11]; - sense_data[14] = sbp2_status[20]; - sense_data[15] = sbp2_status[21]; - - return sbp2_status[8] & 0x3f; -} - -static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, - int destid, quadlet_t *data, u64 addr, - size_t length, u16 fl) -{ - struct sbp2_fwhost_info *hi; - struct sbp2_lu *lu = NULL, *lu_tmp; - struct scsi_cmnd *SCpnt = NULL; - struct sbp2_status_block *sb; - u32 scsi_status = SBP2_SCSI_STATUS_GOOD; - struct sbp2_command_info *cmd; - unsigned long flags; - - if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) { - SBP2_ERR("Wrong size of status block"); - return RCODE_ADDRESS_ERROR; - } - if (unlikely(!host)) { - SBP2_ERR("host is NULL - this is bad!"); - return RCODE_ADDRESS_ERROR; - } - hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - if (unlikely(!hi)) { - SBP2_ERR("host info is NULL - this is bad!"); - return RCODE_ADDRESS_ERROR; - } - - /* Find the unit which wrote the status. */ - read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) { - if (lu_tmp->ne->nodeid == nodeid && - lu_tmp->status_fifo_addr == addr) { - lu = lu_tmp; - break; - } - } - read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); - - if (unlikely(!lu)) { - SBP2_ERR("lu is NULL - device is gone?"); - return RCODE_ADDRESS_ERROR; - } - - /* Put response into lu status fifo buffer. The first two bytes - * come in big endian bit order. Often the target writes only a - * truncated status block, minimally the first two quadlets. The rest - * is implied to be zeros. */ - sb = &lu->status_block; - memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent)); - memcpy(sb, data, length); - sbp2util_be32_to_cpu_buffer(sb, 8); - - /* Ignore unsolicited status. Handle command ORB status. */ - if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2)) - cmd = NULL; - else - cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo); - if (cmd) { - /* Grab SCSI command pointers and check status. */ - /* - * FIXME: If the src field in the status is 1, the ORB DMA must - * not be reused until status for a subsequent ORB is received. - */ - SCpnt = cmd->Current_SCpnt; - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - sbp2util_mark_command_completed(lu, cmd); - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - if (SCpnt) { - u32 h = sb->ORB_offset_hi_misc; - u32 r = STATUS_GET_RESP(h); - - if (r != RESP_STATUS_REQUEST_COMPLETE) { - SBP2_INFO("resp 0x%x, sbp_status 0x%x", - r, STATUS_GET_SBP_STATUS(h)); - scsi_status = - r == RESP_STATUS_TRANSPORT_FAILURE ? - SBP2_SCSI_STATUS_BUSY : - SBP2_SCSI_STATUS_COMMAND_TERMINATED; - } - - if (STATUS_GET_LEN(h) > 1) - scsi_status = sbp2_status_to_sense_data( - (unchar *)sb, SCpnt->sense_buffer); - - if (STATUS_TEST_DEAD(h)) - sbp2_agent_reset(lu, 0); - } - - /* Check here to see if there are no commands in-use. If there - * are none, we know that the fetch agent left the active state - * _and_ that we did not reactivate it yet. Therefore clear - * last_orb so that next time we write directly to the - * ORB_POINTER register. That way the fetch agent does not need - * to refetch the next_ORB. */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (list_empty(&lu->cmd_orb_inuse)) - lu->last_orb = NULL; - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - } else { - /* It's probably status after a management request. */ - if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) || - (sb->ORB_offset_lo == lu->login_orb_dma) || - (sb->ORB_offset_lo == lu->query_logins_orb_dma) || - (sb->ORB_offset_lo == lu->logout_orb_dma)) { - lu->access_complete = 1; - wake_up_interruptible(&sbp2_access_wq); - } - } - - if (SCpnt) - sbp2scsi_complete_command(lu, scsi_status, SCpnt, - cmd->Current_done); - return RCODE_COMPLETE; -} - -/************************************** - * SCSI interface related section - **************************************/ - -static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; - struct sbp2_fwhost_info *hi; - int result = DID_NO_CONNECT << 16; - - if (unlikely(!sbp2util_node_is_available(lu))) - goto done; - - hi = lu->hi; - - if (unlikely(!hi)) { - SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!"); - goto done; - } - - /* Multiple units are currently represented to the SCSI core as separate - * targets, not as one target with multiple LUs. Therefore return - * selection time-out to any IO directed at non-zero LUNs. */ - if (unlikely(SCpnt->device->lun)) - goto done; - - if (unlikely(!hpsb_node_entry_valid(lu->ne))) { - SBP2_ERR("Bus reset in progress - rejecting command"); - result = DID_BUS_BUSY << 16; - goto done; - } - - /* Bidirectional commands are not yet implemented, - * and unknown transfer direction not handled. */ - if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) { - SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command"); - result = DID_ERROR << 16; - goto done; - } - - if (sbp2_send_command(lu, SCpnt, done)) { - SBP2_ERR("Error sending SCSI command"); - sbp2scsi_complete_command(lu, - SBP2_SCSI_STATUS_SELECTION_TIMEOUT, - SCpnt, done); - } - return 0; - -done: - SCpnt->result = result; - done(SCpnt); - return 0; -} - -static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status) -{ - struct list_head *lh; - struct sbp2_command_info *cmd; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - while (!list_empty(&lu->cmd_orb_inuse)) { - lh = lu->cmd_orb_inuse.next; - cmd = list_entry(lh, struct sbp2_command_info, list); - sbp2util_mark_command_completed(lu, cmd); - if (cmd->Current_SCpnt) { - cmd->Current_SCpnt->result = status << 16; - cmd->Current_done(cmd->Current_SCpnt); - } - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - return; -} - -/* - * Complete a regular SCSI command. Can be called in atomic context. - */ -static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status, - struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - if (!SCpnt) { - SBP2_ERR("SCpnt is NULL"); - return; - } - - switch (scsi_status) { - case SBP2_SCSI_STATUS_GOOD: - SCpnt->result = DID_OK << 16; - break; - - case SBP2_SCSI_STATUS_BUSY: - SBP2_ERR("SBP2_SCSI_STATUS_BUSY"); - SCpnt->result = DID_BUS_BUSY << 16; - break; - - case SBP2_SCSI_STATUS_CHECK_CONDITION: - SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16; - break; - - case SBP2_SCSI_STATUS_SELECTION_TIMEOUT: - SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT"); - SCpnt->result = DID_NO_CONNECT << 16; - scsi_print_command(SCpnt); - break; - - case SBP2_SCSI_STATUS_CONDITION_MET: - case SBP2_SCSI_STATUS_RESERVATION_CONFLICT: - case SBP2_SCSI_STATUS_COMMAND_TERMINATED: - SBP2_ERR("Bad SCSI status = %x", scsi_status); - SCpnt->result = DID_ERROR << 16; - scsi_print_command(SCpnt); - break; - - default: - SBP2_ERR("Unsupported SCSI status = %x", scsi_status); - SCpnt->result = DID_ERROR << 16; - } - - /* If a bus reset is in progress and there was an error, complete - * the command as busy so that it will get retried. */ - if (!hpsb_node_entry_valid(lu->ne) - && (scsi_status != SBP2_SCSI_STATUS_GOOD)) { - SBP2_ERR("Completing command with busy (bus reset)"); - SCpnt->result = DID_BUS_BUSY << 16; - } - - /* Tell the SCSI stack that we're done with this command. */ - done(SCpnt); -} - -static int sbp2scsi_slave_alloc(struct scsi_device *sdev) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; - - if (sdev->lun != 0 || sdev->id != lu->ud->id || sdev->channel != 0) - return -ENODEV; - - lu->sdev = sdev; - sdev->allow_restart = 1; - - /* SBP-2 requires quadlet alignment of the data buffers. */ - blk_queue_update_dma_alignment(sdev->request_queue, 4 - 1); - - if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36) - sdev->inquiry_len = 36; - return 0; -} - -static int sbp2scsi_slave_configure(struct scsi_device *sdev) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; - - sdev->use_10_for_rw = 1; - - if (sbp2_exclusive_login) - sdev->manage_start_stop = 1; - if (sdev->type == TYPE_ROM) - sdev->use_10_for_ms = 1; - if (sdev->type == TYPE_DISK && - lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8) - sdev->skip_ms_page_8 = 1; - if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY) - sdev->fix_capacity = 1; - if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION) - sdev->start_stop_pwr_cond = 1; - if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS) - blk_queue_max_hw_sectors(sdev->request_queue, 128 * 1024 / 512); - - blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE); - return 0; -} - -static void sbp2scsi_slave_destroy(struct scsi_device *sdev) -{ - ((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL; - return; -} - -/* - * Called by scsi stack when something has really gone wrong. - * Usually called when a command has timed-out for some reason. - */ -static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; - struct sbp2_command_info *cmd; - unsigned long flags; - - SBP2_INFO("aborting sbp2 command"); - scsi_print_command(SCpnt); - - if (sbp2util_node_is_available(lu)) { - sbp2_agent_reset(lu, 1); - - /* Return a matching command structure to the free pool. */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt); - if (cmd) { - sbp2util_mark_command_completed(lu, cmd); - if (cmd->Current_SCpnt) { - cmd->Current_SCpnt->result = DID_ABORT << 16; - cmd->Current_done(cmd->Current_SCpnt); - } - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY); - } - - return SUCCESS; -} - -/* - * Called by scsi stack when something has really gone wrong. - */ -static int sbp2scsi_reset(struct scsi_cmnd *SCpnt) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; - - SBP2_INFO("reset requested"); - - if (sbp2util_node_is_available(lu)) { - SBP2_INFO("generating sbp2 fetch agent reset"); - sbp2_agent_reset(lu, 1); - } - - return SUCCESS; -} - -static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct scsi_device *sdev; - struct sbp2_lu *lu; - - if (!(sdev = to_scsi_device(dev))) - return 0; - - if (!(lu = (struct sbp2_lu *)sdev->host->hostdata[0])) - return 0; - - if (sbp2_long_sysfs_ieee1394_id) - return sprintf(buf, "%016Lx:%06x:%04x\n", - (unsigned long long)lu->ne->guid, - lu->ud->directory_id, ORB_SET_LUN(lu->lun)); - else - return sprintf(buf, "%016Lx:%d:%d\n", - (unsigned long long)lu->ne->guid, - lu->ud->id, ORB_SET_LUN(lu->lun)); -} - -MODULE_AUTHOR("Ben Collins <bcollins@debian.org>"); -MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); -MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); -MODULE_LICENSE("GPL"); - -static int sbp2_module_init(void) -{ - int ret; - - if (sbp2_serialize_io) { - sbp2_shost_template.can_queue = 1; - sbp2_shost_template.cmd_per_lun = 1; - } - - sbp2_shost_template.max_sectors = sbp2_max_sectors; - - hpsb_register_highlevel(&sbp2_highlevel); - ret = hpsb_register_protocol(&sbp2_driver); - if (ret) { - SBP2_ERR("Failed to register protocol"); - hpsb_unregister_highlevel(&sbp2_highlevel); - return ret; - } - return 0; -} - -static void __exit sbp2_module_exit(void) -{ - hpsb_unregister_protocol(&sbp2_driver); - hpsb_unregister_highlevel(&sbp2_highlevel); -} - -module_init(sbp2_module_init); -module_exit(sbp2_module_exit); diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h deleted file mode 100644 index 64a3a66a8a3..00000000000 --- a/drivers/ieee1394/sbp2.h +++ /dev/null @@ -1,346 +0,0 @@ -/* - * sbp2.h - Defines and prototypes for sbp2.c - * - * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) - * jamesg@filanet.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef SBP2_H -#define SBP2_H - -#define SBP2_DEVICE_NAME "sbp2" - -/* - * There is no transport protocol limit to the CDB length, but we implement - * a fixed length only. 16 bytes is enough for disks larger than 2 TB. - */ -#define SBP2_MAX_CDB_SIZE 16 - -/* - * SBP-2 specific definitions - */ - -#define ORB_DIRECTION_WRITE_TO_MEDIA 0x0 -#define ORB_DIRECTION_READ_FROM_MEDIA 0x1 -#define ORB_DIRECTION_NO_DATA_TRANSFER 0x2 - -#define ORB_SET_NULL_PTR(v) (((v) & 0x1) << 31) -#define ORB_SET_NOTIFY(v) (((v) & 0x1) << 31) -#define ORB_SET_RQ_FMT(v) (((v) & 0x3) << 29) -#define ORB_SET_NODE_ID(v) (((v) & 0xffff) << 16) -#define ORB_SET_STATUS_FIFO_HI(v, id) ((v) >> 32 | ORB_SET_NODE_ID(id)) -#define ORB_SET_STATUS_FIFO_LO(v) ((v) & 0xffffffff) -#define ORB_SET_DATA_SIZE(v) ((v) & 0xffff) -#define ORB_SET_PAGE_SIZE(v) (((v) & 0x7) << 16) -#define ORB_SET_PAGE_TABLE_PRESENT(v) (((v) & 0x1) << 19) -#define ORB_SET_MAX_PAYLOAD(v) (((v) & 0xf) << 20) -#define ORB_SET_SPEED(v) (((v) & 0x7) << 24) -#define ORB_SET_DIRECTION(v) (((v) & 0x1) << 27) - -struct sbp2_command_orb { - u32 next_ORB_hi; - u32 next_ORB_lo; - u32 data_descriptor_hi; - u32 data_descriptor_lo; - u32 misc; - u8 cdb[SBP2_MAX_CDB_SIZE]; -} __attribute__((packed)); - -#define SBP2_LOGIN_REQUEST 0x0 -#define SBP2_QUERY_LOGINS_REQUEST 0x1 -#define SBP2_RECONNECT_REQUEST 0x3 -#define SBP2_SET_PASSWORD_REQUEST 0x4 -#define SBP2_LOGOUT_REQUEST 0x7 -#define SBP2_ABORT_TASK_REQUEST 0xb -#define SBP2_ABORT_TASK_SET 0xc -#define SBP2_LOGICAL_UNIT_RESET 0xe -#define SBP2_TARGET_RESET_REQUEST 0xf - -#define ORB_SET_LUN(v) ((v) & 0xffff) -#define ORB_SET_FUNCTION(v) (((v) & 0xf) << 16) -#define ORB_SET_RECONNECT(v) (((v) & 0xf) << 20) -#define ORB_SET_EXCLUSIVE(v) ((v) ? 1 << 28 : 0) -#define ORB_SET_LOGIN_RESP_LENGTH(v) ((v) & 0xffff) -#define ORB_SET_PASSWD_LENGTH(v) (((v) & 0xffff) << 16) - -struct sbp2_login_orb { - u32 password_hi; - u32 password_lo; - u32 login_response_hi; - u32 login_response_lo; - u32 lun_misc; - u32 passwd_resp_lengths; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -#define RESPONSE_GET_LOGIN_ID(v) ((v) & 0xffff) -#define RESPONSE_GET_LENGTH(v) (((v) >> 16) & 0xffff) -#define RESPONSE_GET_RECONNECT_HOLD(v) ((v) & 0xffff) - -struct sbp2_login_response { - u32 length_login_ID; - u32 command_block_agent_hi; - u32 command_block_agent_lo; - u32 reconnect_hold; -} __attribute__((packed)); - -#define ORB_SET_LOGIN_ID(v) ((v) & 0xffff) -#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(v) ((v) & 0xffff) - -struct sbp2_query_logins_orb { - u32 reserved1; - u32 reserved2; - u32 query_response_hi; - u32 query_response_lo; - u32 lun_misc; - u32 reserved_resp_length; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -#define RESPONSE_GET_MAX_LOGINS(v) ((v) & 0xffff) -#define RESPONSE_GET_ACTIVE_LOGINS(v) ((RESPONSE_GET_LENGTH((v)) - 4) / 12) - -struct sbp2_query_logins_response { - u32 length_max_logins; - u32 misc_IDs; - u32 initiator_misc_hi; - u32 initiator_misc_lo; -} __attribute__((packed)); - -struct sbp2_reconnect_orb { - u32 reserved1; - u32 reserved2; - u32 reserved3; - u32 reserved4; - u32 login_ID_misc; - u32 reserved5; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -struct sbp2_logout_orb { - u32 reserved1; - u32 reserved2; - u32 reserved3; - u32 reserved4; - u32 login_ID_misc; - u32 reserved5; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -struct sbp2_unrestricted_page_table { - __be32 high; - __be32 low; -}; - -#define RESP_STATUS_REQUEST_COMPLETE 0x0 -#define RESP_STATUS_TRANSPORT_FAILURE 0x1 -#define RESP_STATUS_ILLEGAL_REQUEST 0x2 -#define RESP_STATUS_VENDOR_DEPENDENT 0x3 - -#define SBP2_STATUS_NO_ADDITIONAL_INFO 0x0 -#define SBP2_STATUS_REQ_TYPE_NOT_SUPPORTED 0x1 -#define SBP2_STATUS_SPEED_NOT_SUPPORTED 0x2 -#define SBP2_STATUS_PAGE_SIZE_NOT_SUPPORTED 0x3 -#define SBP2_STATUS_ACCESS_DENIED 0x4 -#define SBP2_STATUS_LU_NOT_SUPPORTED 0x5 -#define SBP2_STATUS_MAX_PAYLOAD_TOO_SMALL 0x6 -#define SBP2_STATUS_RESOURCES_UNAVAILABLE 0x8 -#define SBP2_STATUS_FUNCTION_REJECTED 0x9 -#define SBP2_STATUS_LOGIN_ID_NOT_RECOGNIZED 0xa -#define SBP2_STATUS_DUMMY_ORB_COMPLETED 0xb -#define SBP2_STATUS_REQUEST_ABORTED 0xc -#define SBP2_STATUS_UNSPECIFIED_ERROR 0xff - -#define SFMT_CURRENT_ERROR 0x0 -#define SFMT_DEFERRED_ERROR 0x1 -#define SFMT_VENDOR_DEPENDENT_STATUS 0x3 - -#define STATUS_GET_SRC(v) (((v) >> 30) & 0x3) -#define STATUS_GET_RESP(v) (((v) >> 28) & 0x3) -#define STATUS_GET_LEN(v) (((v) >> 24) & 0x7) -#define STATUS_GET_SBP_STATUS(v) (((v) >> 16) & 0xff) -#define STATUS_GET_ORB_OFFSET_HI(v) ((v) & 0x0000ffff) -#define STATUS_TEST_DEAD(v) ((v) & 0x08000000) -/* test 'resp' | 'dead' | 'sbp2_status' */ -#define STATUS_TEST_RDS(v) ((v) & 0x38ff0000) - -struct sbp2_status_block { - u32 ORB_offset_hi_misc; - u32 ORB_offset_lo; - u8 command_set_dependent[24]; -} __attribute__((packed)); - - -/* - * SBP2 related configuration ROM definitions - */ - -#define SBP2_UNIT_DIRECTORY_OFFSET_KEY 0xd1 -#define SBP2_CSR_OFFSET_KEY 0x54 -#define SBP2_UNIT_SPEC_ID_KEY 0x12 -#define SBP2_UNIT_SW_VERSION_KEY 0x13 -#define SBP2_COMMAND_SET_SPEC_ID_KEY 0x38 -#define SBP2_COMMAND_SET_KEY 0x39 -#define SBP2_UNIT_CHARACTERISTICS_KEY 0x3a -#define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14 -#define SBP2_FIRMWARE_REVISION_KEY 0x3c - -#define SBP2_AGENT_STATE_OFFSET 0x00ULL -#define SBP2_AGENT_RESET_OFFSET 0x04ULL -#define SBP2_ORB_POINTER_OFFSET 0x08ULL -#define SBP2_DOORBELL_OFFSET 0x10ULL -#define SBP2_UNSOLICITED_STATUS_ENABLE_OFFSET 0x14ULL -#define SBP2_UNSOLICITED_STATUS_VALUE 0xf - -#define SBP2_BUSY_TIMEOUT_ADDRESS 0xfffff0000210ULL -/* biggest possible value for Single Phase Retry count is 0xf */ -#define SBP2_BUSY_TIMEOUT_VALUE 0xf - -#define SBP2_AGENT_RESET_DATA 0xf - -#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e -#define SBP2_SW_VERSION_ENTRY 0x00010483 - -/* - * The default maximum s/g segment size of a FireWire controller is - * usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to - * be quadlet-aligned, we set the length limit to 0xffff & ~3. - */ -#define SBP2_MAX_SEG_SIZE 0xfffc - -/* - * There is no real limitation of the queue depth (i.e. length of the linked - * list of command ORBs) at the target. The chosen depth is merely an - * implementation detail of the sbp2 driver. - */ -#define SBP2_MAX_CMDS 8 - -#define SBP2_SCSI_STATUS_GOOD 0x0 -#define SBP2_SCSI_STATUS_CHECK_CONDITION 0x2 -#define SBP2_SCSI_STATUS_CONDITION_MET 0x4 -#define SBP2_SCSI_STATUS_BUSY 0x8 -#define SBP2_SCSI_STATUS_RESERVATION_CONFLICT 0x18 -#define SBP2_SCSI_STATUS_COMMAND_TERMINATED 0x22 -#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff - - -/* - * Representations of commands and devices - */ - -/* Per SCSI command */ -struct sbp2_command_info { - struct list_head list; - struct sbp2_command_orb command_orb; - dma_addr_t command_orb_dma; - struct scsi_cmnd *Current_SCpnt; - void (*Current_done)(struct scsi_cmnd *); - - /* Also need s/g structure for each sbp2 command */ - struct sbp2_unrestricted_page_table - scatter_gather_element[SG_ALL] __attribute__((aligned(8))); - dma_addr_t sge_dma; -}; - -/* Per FireWire host */ -struct sbp2_fwhost_info { - struct hpsb_host *host; - struct list_head logical_units; -}; - -/* Per logical unit */ -struct sbp2_lu { - /* Operation request blocks */ - struct sbp2_command_orb *last_orb; - dma_addr_t last_orb_dma; - struct sbp2_login_orb *login_orb; - dma_addr_t login_orb_dma; - struct sbp2_login_response *login_response; - dma_addr_t login_response_dma; - struct sbp2_query_logins_orb *query_logins_orb; - dma_addr_t query_logins_orb_dma; - struct sbp2_query_logins_response *query_logins_response; - dma_addr_t query_logins_response_dma; - struct sbp2_reconnect_orb *reconnect_orb; - dma_addr_t reconnect_orb_dma; - struct sbp2_logout_orb *logout_orb; - dma_addr_t logout_orb_dma; - struct sbp2_status_block status_block; - - /* How to talk to the unit */ - u64 management_agent_addr; - u64 command_block_agent_addr; - u32 speed_code; - u32 max_payload_size; - u16 lun; - - /* Address for the unit to write status blocks to */ - u64 status_fifo_addr; - - /* Waitqueue flag for logins, reconnects, logouts, query logins */ - unsigned int access_complete:1; - - /* Pool of command ORBs for this logical unit */ - spinlock_t cmd_orb_lock; - struct list_head cmd_orb_inuse; - struct list_head cmd_orb_completed; - - /* Backlink to FireWire host; list of units attached to the host */ - struct sbp2_fwhost_info *hi; - struct list_head lu_list; - - /* IEEE 1394 core's device representations */ - struct node_entry *ne; - struct unit_directory *ud; - - /* SCSI core's device representations */ - struct scsi_device *sdev; - struct Scsi_Host *shost; - - /* Device specific workarounds/brokeness */ - unsigned workarounds; - - /* Connection state */ - atomic_t state; - - /* For deferred requests to the fetch agent */ - struct work_struct protocol_work; -}; - -/* For use in sbp2_lu.state */ -enum sbp2lu_state_types { - SBP2LU_STATE_RUNNING, /* all normal */ - SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */ - SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */ -}; - -/* For use in sbp2_lu.workarounds and in the corresponding - * module load parameter */ -#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1 -#define SBP2_WORKAROUND_INQUIRY_36 0x2 -#define SBP2_WORKAROUND_MODE_SENSE_8 0x4 -#define SBP2_WORKAROUND_FIX_CAPACITY 0x8 -#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10 -#define SBP2_INQUIRY_DELAY 12 -#define SBP2_WORKAROUND_POWER_CONDITION 0x20 -#define SBP2_WORKAROUND_OVERRIDE 0x100 - -#endif /* SBP2_H */ diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c deleted file mode 100644 index 5c74f796d7f..00000000000 --- a/drivers/ieee1394/video1394.c +++ /dev/null @@ -1,1528 +0,0 @@ -/* - * video1394.c - video driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * Peter Schlaile <udbz@rz.uni-karlsruhe.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * NOTES: - * - * ioctl return codes: - * EFAULT is only for invalid address for the argp - * EINVAL for out of range values - * EBUSY when trying to use an already used resource - * ESRCH when trying to free/stop a not used resource - * EAGAIN for resource allocation failure that could perhaps succeed later - * ENOTTY for unsupported ioctl request - * - */ -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/delay.h> -#include <linux/bitops.h> -#include <linux/types.h> -#include <linux/vmalloc.h> -#include <linux/timex.h> -#include <linux/mm.h> -#include <linux/compat.h> -#include <linux/cdev.h> - -#include "dma.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_types.h" -#include "nodemgr.h" -#include "ohci1394.h" -#include "video1394.h" - -#define ISO_CHANNELS 64 - -struct it_dma_prg { - struct dma_cmd begin; - quadlet_t data[4]; - struct dma_cmd end; - quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ -}; - -struct dma_iso_ctx { - struct ti_ohci *ohci; - int type; /* OHCI_ISO_TRANSMIT or OHCI_ISO_RECEIVE */ - struct ohci1394_iso_tasklet iso_tasklet; - int channel; - int ctx; - int last_buffer; - int * next_buffer; /* For ISO Transmit of video packets - to write the correct SYT field - into the next block */ - unsigned int num_desc; - unsigned int buf_size; - unsigned int frame_size; - unsigned int packet_size; - unsigned int left_size; - unsigned int nb_cmd; - - struct dma_region dma; - - struct dma_prog_region *prg_reg; - - struct dma_cmd **ir_prg; - struct it_dma_prg **it_prg; - - unsigned int *buffer_status; - unsigned int *buffer_prg_assignment; - struct timeval *buffer_time; /* time when the buffer was received */ - unsigned int *last_used_cmd; /* For ISO Transmit with - variable sized packets only ! */ - int ctrlClear; - int ctrlSet; - int cmdPtr; - int ctxMatch; - wait_queue_head_t waitq; - spinlock_t lock; - unsigned int syt_offset; - int flags; - - struct list_head link; -}; - - -struct file_ctx { - struct ti_ohci *ohci; - struct list_head context_list; - struct dma_iso_ctx *current_ctx; -}; - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define VIDEO1394_DEBUG -#endif - -#ifdef DBGMSG -#undef DBGMSG -#endif - -#ifdef VIDEO1394_DEBUG -#define DBGMSG(card, fmt, args...) \ -printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args) -#else -#define DBGMSG(card, fmt, args...) do {} while (0) -#endif - -/* print general (card independent) information */ -#define PRINT_G(level, fmt, args...) \ -printk(level "video1394: " fmt "\n" , ## args) - -/* print card specific information */ -#define PRINT(level, card, fmt, args...) \ -printk(level "video1394_%d: " fmt "\n" , card , ## args) - -static void wakeup_dma_ir_ctx(unsigned long l); -static void wakeup_dma_it_ctx(unsigned long l); - -static struct hpsb_highlevel video1394_highlevel; - -static int free_dma_iso_ctx(struct dma_iso_ctx *d) -{ - int i; - - DBGMSG(d->ohci->host->id, "Freeing dma_iso_ctx %d", d->ctx); - - ohci1394_stop_context(d->ohci, d->ctrlClear, NULL); - if (d->iso_tasklet.link.next != NULL) - ohci1394_unregister_iso_tasklet(d->ohci, &d->iso_tasklet); - - dma_region_free(&d->dma); - - if (d->prg_reg) { - for (i = 0; i < d->num_desc; i++) - dma_prog_region_free(&d->prg_reg[i]); - kfree(d->prg_reg); - } - - kfree(d->ir_prg); - kfree(d->it_prg); - kfree(d->buffer_status); - kfree(d->buffer_prg_assignment); - kfree(d->buffer_time); - kfree(d->last_used_cmd); - kfree(d->next_buffer); - list_del(&d->link); - kfree(d); - - return 0; -} - -static struct dma_iso_ctx * -alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc, - int buf_size, int channel, unsigned int packet_size) -{ - struct dma_iso_ctx *d; - int i; - - d = kzalloc(sizeof(*d), GFP_KERNEL); - if (!d) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx"); - return NULL; - } - - d->ohci = ohci; - d->type = type; - d->channel = channel; - d->num_desc = num_desc; - d->frame_size = buf_size; - d->buf_size = PAGE_ALIGN(buf_size); - d->last_buffer = -1; - INIT_LIST_HEAD(&d->link); - init_waitqueue_head(&d->waitq); - - /* Init the regions for easy cleanup */ - dma_region_init(&d->dma); - - if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev, - PCI_DMA_BIDIRECTIONAL)) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer"); - free_dma_iso_ctx(d); - return NULL; - } - - if (type == OHCI_ISO_RECEIVE) - ohci1394_init_iso_tasklet(&d->iso_tasklet, type, - wakeup_dma_ir_ctx, - (unsigned long) d); - else - ohci1394_init_iso_tasklet(&d->iso_tasklet, type, - wakeup_dma_it_ctx, - (unsigned long) d); - - if (ohci1394_register_iso_tasklet(ohci, &d->iso_tasklet) < 0) { - PRINT(KERN_ERR, ohci->host->id, "no free iso %s contexts", - type == OHCI_ISO_RECEIVE ? "receive" : "transmit"); - free_dma_iso_ctx(d); - return NULL; - } - d->ctx = d->iso_tasklet.context; - - d->prg_reg = kmalloc(d->num_desc * sizeof(*d->prg_reg), GFP_KERNEL); - if (!d->prg_reg) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate ir prg regs"); - free_dma_iso_ctx(d); - return NULL; - } - /* Makes for easier cleanup */ - for (i = 0; i < d->num_desc; i++) - dma_prog_region_init(&d->prg_reg[i]); - - if (type == OHCI_ISO_RECEIVE) { - d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx; - d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx; - d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx; - d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx; - - d->ir_prg = kzalloc(d->num_desc * sizeof(*d->ir_prg), - GFP_KERNEL); - - if (!d->ir_prg) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); - free_dma_iso_ctx(d); - return NULL; - } - - d->nb_cmd = d->buf_size / PAGE_SIZE + 1; - d->left_size = (d->frame_size % PAGE_SIZE) ? - d->frame_size % PAGE_SIZE : PAGE_SIZE; - - for (i = 0;i < d->num_desc; i++) { - if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd * - sizeof(struct dma_cmd), ohci->dev)) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); - free_dma_iso_ctx(d); - return NULL; - } - d->ir_prg[i] = (struct dma_cmd *)d->prg_reg[i].kvirt; - } - - } else { /* OHCI_ISO_TRANSMIT */ - d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx; - d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx; - d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx; - - d->it_prg = kzalloc(d->num_desc * sizeof(*d->it_prg), - GFP_KERNEL); - - if (!d->it_prg) { - PRINT(KERN_ERR, ohci->host->id, - "Failed to allocate dma it prg"); - free_dma_iso_ctx(d); - return NULL; - } - - d->packet_size = packet_size; - - if (PAGE_SIZE % packet_size || packet_size>4096) { - PRINT(KERN_ERR, ohci->host->id, - "Packet size %d (page_size: %ld) " - "not yet supported\n", - packet_size, PAGE_SIZE); - free_dma_iso_ctx(d); - return NULL; - } - - d->nb_cmd = d->frame_size / d->packet_size; - if (d->frame_size % d->packet_size) { - d->nb_cmd++; - d->left_size = d->frame_size % d->packet_size; - } else - d->left_size = d->packet_size; - - for (i = 0; i < d->num_desc; i++) { - if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd * - sizeof(struct it_dma_prg), ohci->dev)) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma it prg"); - free_dma_iso_ctx(d); - return NULL; - } - d->it_prg[i] = (struct it_dma_prg *)d->prg_reg[i].kvirt; - } - } - - d->buffer_status = - kzalloc(d->num_desc * sizeof(*d->buffer_status), GFP_KERNEL); - d->buffer_prg_assignment = - kzalloc(d->num_desc * sizeof(*d->buffer_prg_assignment), GFP_KERNEL); - d->buffer_time = - kzalloc(d->num_desc * sizeof(*d->buffer_time), GFP_KERNEL); - d->last_used_cmd = - kzalloc(d->num_desc * sizeof(*d->last_used_cmd), GFP_KERNEL); - d->next_buffer = - kzalloc(d->num_desc * sizeof(*d->next_buffer), GFP_KERNEL); - - if (!d->buffer_status || !d->buffer_prg_assignment || !d->buffer_time || - !d->last_used_cmd || !d->next_buffer) { - PRINT(KERN_ERR, ohci->host->id, - "Failed to allocate dma_iso_ctx member"); - free_dma_iso_ctx(d); - return NULL; - } - - spin_lock_init(&d->lock); - - DBGMSG(ohci->host->id, "Iso %s DMA: %d buffers " - "of size %d allocated for a frame size %d, each with %d prgs", - (type == OHCI_ISO_RECEIVE) ? "receive" : "transmit", - d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd); - - return d; -} - -static void reset_ir_status(struct dma_iso_ctx *d, int n) -{ - int i; - d->ir_prg[n][0].status = cpu_to_le32(4); - d->ir_prg[n][1].status = cpu_to_le32(PAGE_SIZE-4); - for (i = 2; i < d->nb_cmd - 1; i++) - d->ir_prg[n][i].status = cpu_to_le32(PAGE_SIZE); - d->ir_prg[n][i].status = cpu_to_le32(d->left_size); -} - -static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags) -{ - struct dma_cmd *ir_prg = d->ir_prg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size; - int i; - - d->buffer_prg_assignment[n] = buffer; - - ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf - - (unsigned long)d->dma.kvirt)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf + 4) - (unsigned long)d->dma.kvirt)); - - for (i=2;i<d->nb_cmd-1;i++) { - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - } - - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt)); -} - -static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags) -{ - struct dma_cmd *ir_prg = d->ir_prg[n]; - struct dma_prog_region *ir_reg = &d->prg_reg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt; - int i; - - /* the first descriptor will read only 4 bytes */ - ir_prg[0].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | 4); - - /* set the sync flag */ - if (flags & VIDEO1394_SYNC_FRAMES) - ir_prg[0].control |= cpu_to_le32(DMA_CTL_WAIT); - - ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf - - (unsigned long)d->dma.kvirt)); - ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - 1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - - /* If there is *not* only one DMA page per frame (hence, d->nb_cmd==2) */ - if (d->nb_cmd > 2) { - /* The second descriptor will read PAGE_SIZE-4 bytes */ - ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | (PAGE_SIZE-4)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) - - (unsigned long)d->dma.kvirt)); - ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - - for (i = 2; i < d->nb_cmd - 1; i++) { - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | PAGE_SIZE); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - - ir_prg[i].branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - } - - /* The last descriptor will generate an interrupt */ - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - } else { - /* Only one DMA page is used. Read d->left_size immediately and */ - /* generate an interrupt as this is also the last page. */ - ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | (d->left_size-4)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf + 4) - (unsigned long)d->dma.kvirt)); - } -} - -static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags) -{ - struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; - int i; - - d->flags = flags; - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - for (i=0;i<d->num_desc;i++) { - initialize_dma_ir_prg(d, i, flags); - reset_ir_status(d, i); - } - - /* reset the ctrl register */ - reg_write(ohci, d->ctrlClear, 0xf0000000); - - /* Set bufferFill */ - reg_write(ohci, d->ctrlSet, 0x80000000); - - /* Set isoch header */ - if (flags & VIDEO1394_INCLUDE_ISO_HEADERS) - reg_write(ohci, d->ctrlSet, 0x40000000); - - /* Set the context match register to match on all tags, - sync for sync tag, and listen to d->channel */ - reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx); -} - -/* find which context is listening to this channel */ -static struct dma_iso_ctx * -find_ctx(struct list_head *list, int type, int channel) -{ - struct dma_iso_ctx *ctx; - - list_for_each_entry(ctx, list, link) { - if (ctx->type == type && ctx->channel == channel) - return ctx; - } - - return NULL; -} - -static void wakeup_dma_ir_ctx(unsigned long l) -{ - struct dma_iso_ctx *d = (struct dma_iso_ctx *) l; - int i; - - spin_lock(&d->lock); - - for (i = 0; i < d->num_desc; i++) { - if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) { - reset_ir_status(d, i); - d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; - do_gettimeofday(&d->buffer_time[d->buffer_prg_assignment[i]]); - dma_region_sync_for_cpu(&d->dma, - d->buffer_prg_assignment[i] * d->buf_size, - d->buf_size); - } - } - - spin_unlock(&d->lock); - - if (waitqueue_active(&d->waitq)) - wake_up_interruptible(&d->waitq); -} - -static inline void put_timestamp(struct ti_ohci *ohci, struct dma_iso_ctx * d, - int n) -{ - unsigned char* buf = d->dma.kvirt + n * d->buf_size; - u32 cycleTimer; - u32 timeStamp; - - if (n == -1) { - return; - } - - cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - - timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */ - timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12) - + (cycleTimer & 0xf000)) & 0xffff; - - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - - /* if first packet is empty packet, then put timestamp into the next full one too */ - if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { - buf += d->packet_size; - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - } - - /* do the next buffer frame too in case of irq latency */ - n = d->next_buffer[n]; - if (n == -1) { - return; - } - buf = d->dma.kvirt + n * d->buf_size; - - timeStamp += (d->last_used_cmd[n] << 12) & 0xffff; - - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - - /* if first packet is empty packet, then put timestamp into the next full one too */ - if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { - buf += d->packet_size; - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - } - -#if 0 - printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n", - curr, n, cycleTimer, timeStamp); -#endif -} - -static void wakeup_dma_it_ctx(unsigned long l) -{ - struct dma_iso_ctx *d = (struct dma_iso_ctx *) l; - struct ti_ohci *ohci = d->ohci; - int i; - - spin_lock(&d->lock); - - for (i = 0; i < d->num_desc; i++) { - if (d->it_prg[i][d->last_used_cmd[i]].end.status & - cpu_to_le32(0xFFFF0000)) { - int next = d->next_buffer[i]; - put_timestamp(ohci, d, next); - d->it_prg[i][d->last_used_cmd[i]].end.status = 0; - d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; - } - } - - spin_unlock(&d->lock); - - if (waitqueue_active(&d->waitq)) - wake_up_interruptible(&d->waitq); -} - -static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer) -{ - struct it_dma_prg *it_prg = d->it_prg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size; - int i; - - d->buffer_prg_assignment[n] = buffer; - for (i=0;i<d->nb_cmd;i++) { - it_prg[i].end.address = - cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+i*d->packet_size) - (unsigned long)d->dma.kvirt)); - } -} - -static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag) -{ - struct it_dma_prg *it_prg = d->it_prg[n]; - struct dma_prog_region *it_reg = &d->prg_reg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt; - int i; - d->last_used_cmd[n] = d->nb_cmd - 1; - for (i=0;i<d->nb_cmd;i++) { - - it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 8) ; - it_prg[i].begin.address = 0; - - it_prg[i].begin.status = 0; - - it_prg[i].data[0] = cpu_to_le32( - (IEEE1394_SPEED_100 << 16) - | (/* tag */ 1 << 14) - | (d->channel << 8) - | (TCODE_ISO_DATA << 4)); - if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag); - it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16); - it_prg[i].data[2] = 0; - it_prg[i].data[3] = 0; - - it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_BRANCH); - it_prg[i].end.address = - cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) - - (unsigned long)d->dma.kvirt)); - - if (i<d->nb_cmd-1) { - it_prg[i].end.control |= cpu_to_le32(d->packet_size); - it_prg[i].begin.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - it_prg[i].end.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - } else { - /* the last prg generates an interrupt */ - it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | - DMA_CTL_IRQ | d->left_size); - /* the last prg doesn't branch */ - it_prg[i].begin.branchAddress = 0; - it_prg[i].end.branchAddress = 0; - } - it_prg[i].end.status = 0; - } -} - -static void initialize_dma_it_prg_var_packet_queue( - struct dma_iso_ctx *d, int n, unsigned int * packet_sizes, - struct ti_ohci *ohci) -{ - struct it_dma_prg *it_prg = d->it_prg[n]; - struct dma_prog_region *it_reg = &d->prg_reg[n]; - int i; - -#if 0 - if (n != -1) { - put_timestamp(ohci, d, n); - } -#endif - d->last_used_cmd[n] = d->nb_cmd - 1; - - for (i = 0; i < d->nb_cmd; i++) { - unsigned int size; - if (packet_sizes[i] > d->packet_size) { - size = d->packet_size; - } else { - size = packet_sizes[i]; - } - it_prg[i].data[1] = cpu_to_le32(size << 16); - it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH); - - if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) { - it_prg[i].end.control |= cpu_to_le32(size); - it_prg[i].begin.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - it_prg[i].end.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - } else { - /* the last prg generates an interrupt */ - it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | - DMA_CTL_IRQ | size); - /* the last prg doesn't branch */ - it_prg[i].begin.branchAddress = 0; - it_prg[i].end.branchAddress = 0; - d->last_used_cmd[n] = i; - break; - } - } -} - -static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag, - unsigned int syt_offset, int flags) -{ - struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; - int i; - - d->flags = flags; - d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset); - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - for (i=0;i<d->num_desc;i++) - initialize_dma_it_prg(d, i, sync_tag); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx); -} - -static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d, - unsigned int buffer) -{ - unsigned long flags; - unsigned int ret; - spin_lock_irqsave(&d->lock, flags); - ret = d->buffer_status[buffer]; - spin_unlock_irqrestore(&d->lock, flags); - return ret; -} - -static long video1394_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct file_ctx *ctx = file->private_data; - struct ti_ohci *ohci = ctx->ohci; - unsigned long flags; - void __user *argp = (void __user *)arg; - - switch(cmd) - { - case VIDEO1394_IOC_LISTEN_CHANNEL: - case VIDEO1394_IOC_TALK_CHANNEL: - { - struct video1394_mmap v; - u64 mask; - struct dma_iso_ctx *d; - int i; - - if (copy_from_user(&v, argp, sizeof(v))) - return -EFAULT; - - /* if channel < 0, find lowest available one */ - if (v.channel < 0) { - mask = (u64)0x1; - for (i=0; ; i++) { - if (i == ISO_CHANNELS) { - PRINT(KERN_ERR, ohci->host->id, - "No free channel found"); - return -EAGAIN; - } - if (!(ohci->ISO_channel_usage & mask)) { - v.channel = i; - PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i); - break; - } - mask = mask << 1; - } - } else if (v.channel >= ISO_CHANNELS) { - PRINT(KERN_ERR, ohci->host->id, - "Iso channel %d out of bounds", v.channel); - return -EINVAL; - } else { - mask = (u64)0x1<<v.channel; - } - DBGMSG(ohci->host->id, "mask: %08X%08X usage: %08X%08X\n", - (u32)(mask>>32),(u32)(mask&0xffffffff), - (u32)(ohci->ISO_channel_usage>>32), - (u32)(ohci->ISO_channel_usage&0xffffffff)); - if (ohci->ISO_channel_usage & mask) { - PRINT(KERN_ERR, ohci->host->id, - "Channel %d is already taken", v.channel); - return -EBUSY; - } - - if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->host->id, - "Invalid %d length buffer requested",v.buf_size); - return -EINVAL; - } - - if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->host->id, - "Invalid %d buffers requested",v.nb_buffers); - return -EINVAL; - } - - if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->host->id, - "%d buffers of size %d bytes is too big", - v.nb_buffers, v.buf_size); - return -EINVAL; - } - - if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) { - d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE, - v.nb_buffers + 1, v.buf_size, - v.channel, 0); - - if (d == NULL) { - PRINT(KERN_ERR, ohci->host->id, - "Couldn't allocate ir context"); - return -EAGAIN; - } - initialize_dma_ir_ctx(d, v.sync_tag, v.flags); - - ctx->current_ctx = d; - - v.buf_size = d->buf_size; - list_add_tail(&d->link, &ctx->context_list); - - DBGMSG(ohci->host->id, - "iso context %d listen on channel %d", - d->ctx, v.channel); - } - else { - d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT, - v.nb_buffers + 1, v.buf_size, - v.channel, v.packet_size); - - if (d == NULL) { - PRINT(KERN_ERR, ohci->host->id, - "Couldn't allocate it context"); - return -EAGAIN; - } - initialize_dma_it_ctx(d, v.sync_tag, - v.syt_offset, v.flags); - - ctx->current_ctx = d; - - v.buf_size = d->buf_size; - - list_add_tail(&d->link, &ctx->context_list); - - DBGMSG(ohci->host->id, - "Iso context %d talk on channel %d", d->ctx, - v.channel); - } - - if (copy_to_user(argp, &v, sizeof(v))) { - /* FIXME : free allocated dma resources */ - return -EFAULT; - } - - ohci->ISO_channel_usage |= mask; - - return 0; - } - case VIDEO1394_IOC_UNLISTEN_CHANNEL: - case VIDEO1394_IOC_UNTALK_CHANNEL: - { - int channel; - u64 mask; - struct dma_iso_ctx *d; - - if (copy_from_user(&channel, argp, sizeof(int))) - return -EFAULT; - - if (channel < 0 || channel >= ISO_CHANNELS) { - PRINT(KERN_ERR, ohci->host->id, - "Iso channel %d out of bound", channel); - return -EINVAL; - } - mask = (u64)0x1<<channel; - if (!(ohci->ISO_channel_usage & mask)) { - PRINT(KERN_ERR, ohci->host->id, - "Channel %d is not being used", channel); - return -ESRCH; - } - - /* Mark this channel as unused */ - ohci->ISO_channel_usage &= ~mask; - - if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL) - d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel); - else - d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel); - - if (d == NULL) return -ESRCH; - DBGMSG(ohci->host->id, "Iso context %d " - "stop talking on channel %d", d->ctx, channel); - free_dma_iso_ctx(d); - - return 0; - } - case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER: - { - struct video1394_wait v; - struct dma_iso_ctx *d; - int next_prg; - - if (unlikely(copy_from_user(&v, argp, sizeof(v)))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); - if (unlikely(d == NULL)) - return -EFAULT; - - if (unlikely(v.buffer >= d->num_desc - 1)) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - spin_lock_irqsave(&d->lock,flags); - - if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is already used",v.buffer); - spin_unlock_irqrestore(&d->lock,flags); - return -EBUSY; - } - - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; - - next_prg = (d->last_buffer + 1) % d->num_desc; - if (d->last_buffer>=0) - d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) - & 0xfffffff0) | 0x1); - - d->last_buffer = next_prg; - reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags); - - d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0; - - spin_unlock_irqrestore(&d->lock,flags); - - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) - { - DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx); - - /* Tell the controller where the first program is */ - reg_write(ohci, d->cmdPtr, - dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1); - - /* Run IR context */ - reg_write(ohci, d->ctrlSet, 0x8000); - } - else { - /* Wake up dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - DBGMSG(ohci->host->id, - "Waking up iso dma ctx=%d", d->ctx); - reg_write(ohci, d->ctrlSet, 0x1000); - } - } - return 0; - - } - case VIDEO1394_IOC_LISTEN_WAIT_BUFFER: - case VIDEO1394_IOC_LISTEN_POLL_BUFFER: - { - struct video1394_wait v; - struct dma_iso_ctx *d; - int i = 0; - - if (unlikely(copy_from_user(&v, argp, sizeof(v)))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); - if (unlikely(d == NULL)) - return -EFAULT; - - if (unlikely(v.buffer > d->num_desc - 1)) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - /* - * I change the way it works so that it returns - * the last received frame. - */ - spin_lock_irqsave(&d->lock, flags); - switch(d->buffer_status[v.buffer]) { - case VIDEO1394_BUFFER_READY: - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - break; - case VIDEO1394_BUFFER_QUEUED: - if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) { - /* for polling, return error code EINTR */ - spin_unlock_irqrestore(&d->lock, flags); - return -EINTR; - } - - spin_unlock_irqrestore(&d->lock, flags); - wait_event_interruptible(d->waitq, - video1394_buffer_state(d, v.buffer) == - VIDEO1394_BUFFER_READY); - if (signal_pending(current)) - return -EINTR; - spin_lock_irqsave(&d->lock, flags); - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - break; - default: - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is not queued",v.buffer); - spin_unlock_irqrestore(&d->lock, flags); - return -ESRCH; - } - - /* set time of buffer */ - v.filltime = d->buffer_time[v.buffer]; - - /* - * Look ahead to see how many more buffers have been received - */ - i=0; - while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]== - VIDEO1394_BUFFER_READY) { - v.buffer=(v.buffer+1)%(d->num_desc - 1); - i++; - } - spin_unlock_irqrestore(&d->lock, flags); - - v.buffer=i; - if (unlikely(copy_to_user(argp, &v, sizeof(v)))) - return -EFAULT; - - return 0; - } - case VIDEO1394_IOC_TALK_QUEUE_BUFFER: - { - struct video1394_wait v; - unsigned int *psizes = NULL; - struct dma_iso_ctx *d; - int next_prg; - - if (copy_from_user(&v, argp, sizeof(v))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); - if (d == NULL) return -EFAULT; - - if (v.buffer >= d->num_desc - 1) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { - int buf_size = d->nb_cmd * sizeof(*psizes); - struct video1394_queue_variable __user *p = argp; - unsigned int __user *qv; - - if (get_user(qv, &p->packet_sizes)) - return -EFAULT; - - psizes = memdup_user(qv, buf_size); - if (IS_ERR(psizes)) - return PTR_ERR(psizes); - } - - spin_lock_irqsave(&d->lock,flags); - - /* last_buffer is last_prg */ - next_prg = (d->last_buffer + 1) % d->num_desc; - if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is already used",v.buffer); - spin_unlock_irqrestore(&d->lock,flags); - kfree(psizes); - return -EBUSY; - } - - if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { - initialize_dma_it_prg_var_packet_queue( - d, next_prg, psizes, ohci); - } - - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; - - if (d->last_buffer >= 0) { - d->it_prg[d->last_buffer] - [ d->last_used_cmd[d->last_buffer] ].end.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], - 0) & 0xfffffff0) | 0x3); - - d->it_prg[d->last_buffer] - [ d->last_used_cmd[d->last_buffer] ].begin.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], - 0) & 0xfffffff0) | 0x3); - d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1); - } - d->last_buffer = next_prg; - reprogram_dma_it_prg(d, d->last_buffer, v.buffer); - d->next_buffer[d->last_buffer] = -1; - - d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0; - - spin_unlock_irqrestore(&d->lock,flags); - - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) - { - DBGMSG(ohci->host->id, "Starting iso transmit DMA ctx=%d", - d->ctx); - put_timestamp(ohci, d, d->last_buffer); - dma_region_sync_for_device(&d->dma, - v.buffer * d->buf_size, d->buf_size); - - /* Tell the controller where the first program is */ - reg_write(ohci, d->cmdPtr, - dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3); - - /* Run IT context */ - reg_write(ohci, d->ctrlSet, 0x8000); - } - else { - /* Wake up dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - DBGMSG(ohci->host->id, - "Waking up iso transmit dma ctx=%d", - d->ctx); - put_timestamp(ohci, d, d->last_buffer); - dma_region_sync_for_device(&d->dma, - v.buffer * d->buf_size, d->buf_size); - - reg_write(ohci, d->ctrlSet, 0x1000); - } - } - - kfree(psizes); - return 0; - - } - case VIDEO1394_IOC_TALK_WAIT_BUFFER: - { - struct video1394_wait v; - struct dma_iso_ctx *d; - - if (copy_from_user(&v, argp, sizeof(v))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); - if (d == NULL) return -EFAULT; - - if (v.buffer >= d->num_desc - 1) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - switch(d->buffer_status[v.buffer]) { - case VIDEO1394_BUFFER_READY: - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - case VIDEO1394_BUFFER_QUEUED: - wait_event_interruptible(d->waitq, - (d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY)); - if (signal_pending(current)) - return -EINTR; - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - default: - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is not queued",v.buffer); - return -ESRCH; - } - } - default: - return -ENOTTY; - } -} - -/* - * This maps the vmalloced and reserved buffer to user space. - * - * FIXME: - * - PAGE_READONLY should suffice!? - * - remap_pfn_range is kind of inefficient for page by page remapping. - * But e.g. pte_alloc() does not work in modules ... :-( - */ - -static int video1394_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct file_ctx *ctx = file->private_data; - - if (ctx->current_ctx == NULL) { - PRINT(KERN_ERR, ctx->ohci->host->id, - "Current iso context not set"); - return -EINVAL; - } - - return dma_region_mmap(&ctx->current_ctx->dma, file, vma); -} - -static unsigned int video1394_poll(struct file *file, poll_table *pt) -{ - struct file_ctx *ctx; - unsigned int mask = 0; - unsigned long flags; - struct dma_iso_ctx *d; - int i; - - ctx = file->private_data; - d = ctx->current_ctx; - if (d == NULL) { - PRINT(KERN_ERR, ctx->ohci->host->id, - "Current iso context not set"); - return POLLERR; - } - - poll_wait(file, &d->waitq, pt); - - spin_lock_irqsave(&d->lock, flags); - for (i = 0; i < d->num_desc; i++) { - if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) { - mask |= POLLIN | POLLRDNORM; - break; - } - } - spin_unlock_irqrestore(&d->lock, flags); - - return mask; -} - -static int video1394_open(struct inode *inode, struct file *file) -{ - int i = ieee1394_file_to_instance(file); - struct ti_ohci *ohci; - struct file_ctx *ctx; - - ohci = hpsb_get_hostinfo_bykey(&video1394_highlevel, i); - if (ohci == NULL) - return -EIO; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx"); - return -ENOMEM; - } - - ctx->ohci = ohci; - INIT_LIST_HEAD(&ctx->context_list); - ctx->current_ctx = NULL; - file->private_data = ctx; - - return nonseekable_open(inode, file); -} - -static int video1394_release(struct inode *inode, struct file *file) -{ - struct file_ctx *ctx = file->private_data; - struct ti_ohci *ohci = ctx->ohci; - struct list_head *lh, *next; - u64 mask; - - list_for_each_safe(lh, next, &ctx->context_list) { - struct dma_iso_ctx *d; - d = list_entry(lh, struct dma_iso_ctx, link); - mask = (u64) 1 << d->channel; - - if (!(ohci->ISO_channel_usage & mask)) - PRINT(KERN_ERR, ohci->host->id, "On release: Channel %d " - "is not being used", d->channel); - else - ohci->ISO_channel_usage &= ~mask; - DBGMSG(ohci->host->id, "On release: Iso %s context " - "%d stop listening on channel %d", - d->type == OHCI_ISO_RECEIVE ? "receive" : "transmit", - d->ctx, d->channel); - free_dma_iso_ctx(d); - } - - kfree(ctx); - file->private_data = NULL; - - return 0; -} - -#ifdef CONFIG_COMPAT -static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg); -#endif - -static struct cdev video1394_cdev; -static const struct file_operations video1394_fops= -{ - .owner = THIS_MODULE, - .unlocked_ioctl = video1394_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = video1394_compat_ioctl, -#endif - .poll = video1394_poll, - .mmap = video1394_mmap, - .open = video1394_open, - .release = video1394_release, - .llseek = no_llseek, -}; - -/*** HOTPLUG STUFF **********************************************************/ -/* - * Export information about protocols/devices supported by this driver. - */ -#ifdef MODULE -static const struct ieee1394_device_id video1394_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = CAMERA_SW_VERSION_ENTRY & 0xffffff - }, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff - }, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff - }, - { } -}; - -MODULE_DEVICE_TABLE(ieee1394, video1394_id_table); -#endif /* MODULE */ - -static struct hpsb_protocol_driver video1394_driver = { - .name = VIDEO1394_DRIVER_NAME, -}; - - -static void video1394_add_host (struct hpsb_host *host) -{ - struct ti_ohci *ohci; - int minor; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - ohci = (struct ti_ohci *)host->hostdata; - - if (!hpsb_create_hostinfo(&video1394_highlevel, host, 0)) { - PRINT(KERN_ERR, ohci->host->id, "Cannot allocate hostinfo"); - return; - } - - hpsb_set_hostinfo(&video1394_highlevel, host, ohci); - hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id); - - minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id; - device_create(hpsb_protocol_class, NULL, MKDEV(IEEE1394_MAJOR, minor), - NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); -} - - -static void video1394_remove_host (struct hpsb_host *host) -{ - struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host); - - if (ohci) - device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id)); - return; -} - - -static struct hpsb_highlevel video1394_highlevel = { - .name = VIDEO1394_DRIVER_NAME, - .add_host = video1394_add_host, - .remove_host = video1394_remove_host, -}; - -MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>"); -MODULE_DESCRIPTION("driver for digital video on OHCI board"); -MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME); -MODULE_LICENSE("GPL"); - -#ifdef CONFIG_COMPAT - -#define VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER \ - _IOW ('#', 0x12, struct video1394_wait32) -#define VIDEO1394_IOC32_LISTEN_WAIT_BUFFER \ - _IOWR('#', 0x13, struct video1394_wait32) -#define VIDEO1394_IOC32_TALK_WAIT_BUFFER \ - _IOW ('#', 0x17, struct video1394_wait32) -#define VIDEO1394_IOC32_LISTEN_POLL_BUFFER \ - _IOWR('#', 0x18, struct video1394_wait32) - -struct video1394_wait32 { - u32 channel; - u32 buffer; - struct compat_timeval filltime; -}; - -static int video1394_wr_wait32(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video1394_wait32 __user *argp = (void __user *)arg; - struct video1394_wait32 wait32; - struct video1394_wait wait; - mm_segment_t old_fs; - int ret; - - if (copy_from_user(&wait32, argp, sizeof(wait32))) - return -EFAULT; - - wait.channel = wait32.channel; - wait.buffer = wait32.buffer; - wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec; - wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER) - ret = video1394_ioctl(file, - VIDEO1394_IOC_LISTEN_WAIT_BUFFER, - (unsigned long) &wait); - else - ret = video1394_ioctl(file, - VIDEO1394_IOC_LISTEN_POLL_BUFFER, - (unsigned long) &wait); - set_fs(old_fs); - - if (!ret) { - wait32.channel = wait.channel; - wait32.buffer = wait.buffer; - wait32.filltime.tv_sec = (int)wait.filltime.tv_sec; - wait32.filltime.tv_usec = (int)wait.filltime.tv_usec; - - if (copy_to_user(argp, &wait32, sizeof(wait32))) - ret = -EFAULT; - } - - return ret; -} - -static int video1394_w_wait32(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video1394_wait32 wait32; - struct video1394_wait wait; - mm_segment_t old_fs; - int ret; - - if (copy_from_user(&wait32, (void __user *)arg, sizeof(wait32))) - return -EFAULT; - - wait.channel = wait32.channel; - wait.buffer = wait32.buffer; - wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec; - wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER) - ret = video1394_ioctl(file, - VIDEO1394_IOC_LISTEN_QUEUE_BUFFER, - (unsigned long) &wait); - else - ret = video1394_ioctl(file, - VIDEO1394_IOC_TALK_WAIT_BUFFER, - (unsigned long) &wait); - set_fs(old_fs); - - return ret; -} - -static int video1394_queue_buf32(struct file *file, unsigned int cmd, unsigned long arg) -{ - return -EFAULT; /* ??? was there before. */ - - return video1394_ioctl(file, - VIDEO1394_IOC_TALK_QUEUE_BUFFER, arg); -} - -static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg) -{ - switch (cmd) { - case VIDEO1394_IOC_LISTEN_CHANNEL: - case VIDEO1394_IOC_UNLISTEN_CHANNEL: - case VIDEO1394_IOC_TALK_CHANNEL: - case VIDEO1394_IOC_UNTALK_CHANNEL: - return video1394_ioctl(f, cmd, arg); - - case VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER: - return video1394_w_wait32(f, cmd, arg); - case VIDEO1394_IOC32_LISTEN_WAIT_BUFFER: - return video1394_wr_wait32(f, cmd, arg); - case VIDEO1394_IOC_TALK_QUEUE_BUFFER: - return video1394_queue_buf32(f, cmd, arg); - case VIDEO1394_IOC32_TALK_WAIT_BUFFER: - return video1394_w_wait32(f, cmd, arg); - case VIDEO1394_IOC32_LISTEN_POLL_BUFFER: - return video1394_wr_wait32(f, cmd, arg); - default: - return -ENOIOCTLCMD; - } -} - -#endif /* CONFIG_COMPAT */ - -static void __exit video1394_exit_module (void) -{ - hpsb_unregister_protocol(&video1394_driver); - hpsb_unregister_highlevel(&video1394_highlevel); - cdev_del(&video1394_cdev); - PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); -} - -static int __init video1394_init_module (void) -{ - int ret; - - hpsb_init_highlevel(&video1394_highlevel); - - cdev_init(&video1394_cdev, &video1394_fops); - video1394_cdev.owner = THIS_MODULE; - ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16); - if (ret) { - PRINT_G(KERN_ERR, "video1394: unable to get minor device block"); - return ret; - } - - hpsb_register_highlevel(&video1394_highlevel); - - ret = hpsb_register_protocol(&video1394_driver); - if (ret) { - PRINT_G(KERN_ERR, "video1394: failed to register protocol"); - hpsb_unregister_highlevel(&video1394_highlevel); - cdev_del(&video1394_cdev); - return ret; - } - - PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module"); - return 0; -} - - -module_init(video1394_init_module); -module_exit(video1394_exit_module); diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h deleted file mode 100644 index 9a89d9cc3c8..00000000000 --- a/drivers/ieee1394/video1394.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * video1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> - * Peter Schlaile <udbz@rz.uni-karlsruhe.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _VIDEO_1394_H -#define _VIDEO_1394_H - -#include "ieee1394-ioctl.h" - -#define VIDEO1394_DRIVER_NAME "video1394" - -#define VIDEO1394_MAX_SIZE 0x4000000 - -enum { - VIDEO1394_BUFFER_FREE = 0, - VIDEO1394_BUFFER_QUEUED, - VIDEO1394_BUFFER_READY -}; - -#define VIDEO1394_SYNC_FRAMES 0x00000001 -#define VIDEO1394_INCLUDE_ISO_HEADERS 0x00000002 -#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004 - -struct video1394_mmap { - int channel; /* -1 to find an open channel in LISTEN/TALK */ - unsigned int sync_tag; - unsigned int nb_buffers; - unsigned int buf_size; - unsigned int packet_size; /* For VARIABLE_PACKET_SIZE: - Maximum packet size */ - unsigned int fps; - unsigned int syt_offset; - unsigned int flags; -}; - -/* For TALK_QUEUE_BUFFER with VIDEO1394_VARIABLE_PACKET_SIZE use */ -struct video1394_queue_variable { - unsigned int channel; - unsigned int buffer; - unsigned int __user * packet_sizes; /* Buffer of size: - buf_size / packet_size */ -}; - -struct video1394_wait { - unsigned int channel; - unsigned int buffer; - struct timeval filltime; /* time of buffer full */ -}; - - -#endif diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 535fea4fe67..e3f7fc6f956 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -534,6 +534,80 @@ static int handle_eviocgbit(struct input_dev *dev, } #undef OLD_KEY_MAX +static int evdev_handle_get_keycode(struct input_dev *dev, + void __user *p, size_t size) +{ + struct input_keymap_entry ke; + int error; + + memset(&ke, 0, sizeof(ke)); + + if (size == sizeof(unsigned int[2])) { + /* legacy case */ + int __user *ip = (int __user *)p; + + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + ke.len = sizeof(unsigned int); + ke.flags = 0; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (put_user(ke.keycode, ip + 1)) + return -EFAULT; + + } else { + size = min(size, sizeof(ke)); + + if (copy_from_user(&ke, p, size)) + return -EFAULT; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (copy_to_user(p, &ke, size)) + return -EFAULT; + } + return 0; +} + +static int evdev_handle_set_keycode(struct input_dev *dev, + void __user *p, size_t size) +{ + struct input_keymap_entry ke; + + memset(&ke, 0, sizeof(ke)); + + if (size == sizeof(unsigned int[2])) { + /* legacy case */ + int __user *ip = (int __user *)p; + + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + if (get_user(ke.keycode, ip + 1)) + return -EFAULT; + + ke.len = sizeof(unsigned int); + ke.flags = 0; + + } else { + size = min(size, sizeof(ke)); + + if (copy_from_user(&ke, p, size)) + return -EFAULT; + + if (ke.len > sizeof(ke.scancode)) + return -EINVAL; + } + + return input_set_keycode(dev, &ke); +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -580,25 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return 0; - case EVIOCGKEYCODE: - if (get_user(t, ip)) - return -EFAULT; - - error = input_get_keycode(dev, t, &v); - if (error) - return error; - - if (put_user(v, ip + 1)) - return -EFAULT; - - return 0; - - case EVIOCSKEYCODE: - if (get_user(t, ip) || get_user(v, ip + 1)) - return -EFAULT; - - return input_set_keycode(dev, t, v); - case EVIOCRMFF: return input_ff_erase(dev, (int)(unsigned long) p, file); @@ -620,7 +675,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, /* Now check variable-length commands */ #define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) - switch (EVIOC_MASK_SIZE(cmd)) { case EVIOCGKEY(0): @@ -654,6 +708,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return -EFAULT; return error; + + case EVIOC_MASK_SIZE(EVIOCGKEYCODE): + return evdev_handle_get_keycode(dev, p, size); + + case EVIOC_MASK_SIZE(EVIOCSKEYCODE): + return evdev_handle_set_keycode(dev, p, size); } /* Multi-number variable-length handlers */ diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 7392992da42..422aa0a6b77 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -59,44 +59,52 @@ MODULE_DEVICE_TABLE(pci, emu_tbl); static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - int ioport, iolen; struct emu *emu; struct gameport *port; - - if (pci_enable_device(pdev)) - return -EBUSY; - - ioport = pci_resource_start(pdev, 0); - iolen = pci_resource_len(pdev, 0); - - if (!request_region(ioport, iolen, "emu10k1-gp")) - return -EBUSY; + int error; emu = kzalloc(sizeof(struct emu), GFP_KERNEL); port = gameport_allocate_port(); if (!emu || !port) { printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n"); - release_region(ioport, iolen); - kfree(emu); - gameport_free_port(port); - return -ENOMEM; + error = -ENOMEM; + goto err_out_free; } - emu->io = ioport; - emu->size = iolen; + error = pci_enable_device(pdev); + if (error) + goto err_out_free; + + emu->io = pci_resource_start(pdev, 0); + emu->size = pci_resource_len(pdev, 0); + emu->dev = pdev; emu->gameport = port; gameport_set_name(port, "EMU10K1"); gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); port->dev.parent = &pdev->dev; - port->io = ioport; + port->io = emu->io; + + if (!request_region(emu->io, emu->size, "emu10k1-gp")) { + printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n", + emu->io, emu->io + emu->size - 1); + error = -EBUSY; + goto err_out_disable_dev; + } pci_set_drvdata(pdev, emu); gameport_register_port(port); return 0; + + err_out_disable_dev: + pci_disable_device(pdev); + err_out_free: + gameport_free_port(port); + kfree(emu); + return error; } static void __devexit emu_remove(struct pci_dev *pdev) @@ -106,6 +114,8 @@ static void __devexit emu_remove(struct pci_dev *pdev) gameport_unregister_port(emu->gameport); release_region(emu->io, emu->size); kfree(emu); + + pci_disable_device(pdev); } static struct pci_driver emu_driver = { diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 14d3f3e208a..a3b70ff2101 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci) { struct fm801_gp *gp = pci_get_drvdata(pci); - if (gp) { - gameport_unregister_port(gp->gameport); - release_resource(gp->res_port); - kfree(gp); - } + gameport_unregister_port(gp->gameport); + release_resource(gp->res_port); + kfree(gp); + + pci_disable_device(pci); } static const struct pci_device_id fm801_gp_id_table[] = { diff --git a/drivers/input/input.c b/drivers/input/input.c index 7919c253722..d092ef9291d 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev, if (code == ABS_MT_SLOT) { /* * "Stage" the event; we'll flush it later, when we - * get actiual touch data. + * get actual touch data. */ if (*pval >= 0 && *pval < dev->mtsize) dev->slot = *pval; @@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev, pold = &mtslot->abs[code - ABS_MT_FIRST]; } else { /* - * Bypass filtering for multitouch events when + * Bypass filtering for multi-touch events when * not employing slots. */ pold = NULL; @@ -634,78 +634,141 @@ static void input_disconnect_device(struct input_dev *dev) spin_unlock_irq(&dev->event_lock); } -static int input_fetch_keycode(struct input_dev *dev, int scancode) +/** + * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry + * @ke: keymap entry containing scancode to be converted. + * @scancode: pointer to the location where converted scancode should + * be stored. + * + * This function is used to convert scancode stored in &struct keymap_entry + * into scalar form understood by legacy keymap handling methods. These + * methods expect scancodes to be represented as 'unsigned int'. + */ +int input_scancode_to_scalar(const struct input_keymap_entry *ke, + unsigned int *scancode) +{ + switch (ke->len) { + case 1: + *scancode = *((u8 *)ke->scancode); + break; + + case 2: + *scancode = *((u16 *)ke->scancode); + break; + + case 4: + *scancode = *((u32 *)ke->scancode); + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(input_scancode_to_scalar); + +/* + * Those routines handle the default case where no [gs]etkeycode() is + * defined. In this case, an array indexed by the scancode is used. + */ + +static unsigned int input_fetch_keycode(struct input_dev *dev, + unsigned int index) { switch (dev->keycodesize) { - case 1: - return ((u8 *)dev->keycode)[scancode]; + case 1: + return ((u8 *)dev->keycode)[index]; - case 2: - return ((u16 *)dev->keycode)[scancode]; + case 2: + return ((u16 *)dev->keycode)[index]; - default: - return ((u32 *)dev->keycode)[scancode]; + default: + return ((u32 *)dev->keycode)[index]; } } static int input_default_getkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int *keycode) + struct input_keymap_entry *ke) { + unsigned int index; + int error; + if (!dev->keycodesize) return -EINVAL; - if (scancode >= dev->keycodemax) + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + index = ke->index; + else { + error = input_scancode_to_scalar(ke, &index); + if (error) + return error; + } + + if (index >= dev->keycodemax) return -EINVAL; - *keycode = input_fetch_keycode(dev, scancode); + ke->keycode = input_fetch_keycode(dev, index); + ke->index = index; + ke->len = sizeof(index); + memcpy(ke->scancode, &index, sizeof(index)); return 0; } static int input_default_setkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { - int old_keycode; + unsigned int index; + int error; int i; - if (scancode >= dev->keycodemax) + if (!dev->keycodesize) return -EINVAL; - if (!dev->keycodesize) + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + } else { + error = input_scancode_to_scalar(ke, &index); + if (error) + return error; + } + + if (index >= dev->keycodemax) return -EINVAL; - if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8))) + if (dev->keycodesize < sizeof(dev->keycode) && + (ke->keycode >> (dev->keycodesize * 8))) return -EINVAL; switch (dev->keycodesize) { case 1: { u8 *k = (u8 *)dev->keycode; - old_keycode = k[scancode]; - k[scancode] = keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; break; } case 2: { u16 *k = (u16 *)dev->keycode; - old_keycode = k[scancode]; - k[scancode] = keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; break; } default: { u32 *k = (u32 *)dev->keycode; - old_keycode = k[scancode]; - k[scancode] = keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; break; } } - __clear_bit(old_keycode, dev->keybit); - __set_bit(keycode, dev->keybit); + __clear_bit(*old_keycode, dev->keybit); + __set_bit(ke->keycode, dev->keybit); for (i = 0; i < dev->keycodemax; i++) { - if (input_fetch_keycode(dev, i) == old_keycode) { - __set_bit(old_keycode, dev->keybit); + if (input_fetch_keycode(dev, i) == *old_keycode) { + __set_bit(*old_keycode, dev->keybit); break; /* Setting the bit twice is useless, so break */ } } @@ -716,53 +779,86 @@ static int input_default_setkeycode(struct input_dev *dev, /** * input_get_keycode - retrieve keycode currently mapped to a given scancode * @dev: input device which keymap is being queried - * @scancode: scancode (or its equivalent for device in question) for which - * keycode is needed - * @keycode: result + * @ke: keymap entry * * This function should be called by anyone interested in retrieving current - * keymap. Presently keyboard and evdev handlers use it. + * keymap. Presently evdev handlers use it. */ -int input_get_keycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) +int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke) { unsigned long flags; int retval; spin_lock_irqsave(&dev->event_lock, flags); - retval = dev->getkeycode(dev, scancode, keycode); - spin_unlock_irqrestore(&dev->event_lock, flags); + if (dev->getkeycode) { + /* + * Support for legacy drivers, that don't implement the new + * ioctls + */ + u32 scancode = ke->index; + + memcpy(ke->scancode, &scancode, sizeof(scancode)); + ke->len = sizeof(scancode); + retval = dev->getkeycode(dev, scancode, &ke->keycode); + } else { + retval = dev->getkeycode_new(dev, ke); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); return retval; } EXPORT_SYMBOL(input_get_keycode); /** - * input_get_keycode - assign new keycode to a given scancode + * input_set_keycode - attribute a keycode to a given scancode * @dev: input device which keymap is being updated - * @scancode: scancode (or its equivalent for device in question) - * @keycode: new keycode to be assigned to the scancode + * @ke: new keymap entry * * This function should be called by anyone needing to update current * keymap. Presently keyboard and evdev handlers use it. */ int input_set_keycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke) { unsigned long flags; unsigned int old_keycode; int retval; - if (keycode > KEY_MAX) + if (ke->keycode > KEY_MAX) return -EINVAL; spin_lock_irqsave(&dev->event_lock, flags); - retval = dev->getkeycode(dev, scancode, &old_keycode); - if (retval) - goto out; + if (dev->setkeycode) { + /* + * Support for legacy drivers, that don't implement the new + * ioctls + */ + unsigned int scancode; + + retval = input_scancode_to_scalar(ke, &scancode); + if (retval) + goto out; + + /* + * We need to know the old scancode, in order to generate a + * keyup effect, if the set operation happens successfully + */ + if (!dev->getkeycode) { + retval = -EINVAL; + goto out; + } + + retval = dev->getkeycode(dev, scancode, &old_keycode); + if (retval) + goto out; + + retval = dev->setkeycode(dev, scancode, ke->keycode); + } else { + retval = dev->setkeycode_new(dev, ke, &old_keycode); + } - retval = dev->setkeycode(dev, scancode, keycode); if (retval) goto out; @@ -1601,7 +1697,7 @@ EXPORT_SYMBOL(input_free_device); * * This function allocates all necessary memory for MT slot handling in the * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1. + * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. */ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) { @@ -1759,11 +1855,11 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } - if (!dev->getkeycode) - dev->getkeycode = input_default_getkeycode; + if (!dev->getkeycode && !dev->getkeycode_new) + dev->getkeycode_new = input_default_getkeycode; - if (!dev->setkeycode) - dev->setkeycode = input_default_setkeycode; + if (!dev->setkeycode && !dev->setkeycode_new) + dev->setkeycode_new = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index aa037fec2f8..b8c51b9781d 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -327,6 +327,16 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_NOMADIK + tristate "ST-Ericsson Nomadik SKE keyboard" + depends on PLAT_NOMADIK + help + Say Y here if you want to use a keypad provided on the SKE controller + used on the Ux500 and Nomadik platforms + + To compile this driver as a module, choose M here: the + module will be called nmk-ske-keypad. + config KEYBOARD_OPENCORES tristate "OpenCores Keyboard Controller" help @@ -424,6 +434,24 @@ config KEYBOARD_OMAP To compile this driver as a module, choose M here: the module will be called omap-keypad. +config KEYBOARD_OMAP4 + tristate "TI OMAP4 keypad support" + depends on ARCH_OMAP4 + help + Say Y here if you want to use the OMAP4 keypad. + + To compile this driver as a module, choose M here: the + module will be called omap4-keypad. + +config KEYBOARD_TNETV107X + tristate "TI TNETV107X keypad support" + depends on ARCH_DAVINCI_TNETV107X + help + Say Y here if you want to use the TNETV107X keypad. + + To compile this driver as a module, choose M here: the + module will be called tnetv107x-keypad. + config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 504b591be0c..a34452e8ebe 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -28,7 +28,9 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o +obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o @@ -38,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index d6918cb966c..b92d1cd5cba 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = { #endif static const struct i2c_device_id adp5588_id[] = { - { KBUILD_MODNAME, 0 }, + { "adp5588-keys", 0 }, { "adp5587-keys", 0 }, { } }; diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 19fa94af207..fed31e0947a 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = { { 0 } }; +MODULE_DEVICE_TABLE(serio, hil_dev_ids); + static struct serio_driver hil_serio_drv = { .driver = { .name = "hil_dev", diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c new file mode 100644 index 00000000000..6e0f2309136 --- /dev/null +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson + * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson + * + * License terms:GNU General Public License (GPL) version 2 + * + * Keypad controller driver for the SKE (Scroll Key Encoder) module used in + * the Nomadik 8815 and Ux500 platforms. + */ + +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/clk.h> + +#include <plat/ske.h> + +/* SKE_CR bits */ +#define SKE_KPMLT (0x1 << 6) +#define SKE_KPCN (0x7 << 3) +#define SKE_KPASEN (0x1 << 2) +#define SKE_KPASON (0x1 << 7) + +/* SKE_IMSC bits */ +#define SKE_KPIMA (0x1 << 2) + +/* SKE_ICR bits */ +#define SKE_KPICS (0x1 << 3) +#define SKE_KPICA (0x1 << 2) + +/* SKE_RIS bits */ +#define SKE_KPRISA (0x1 << 2) + +#define SKE_KEYPAD_ROW_SHIFT 3 +#define SKE_KPD_KEYMAP_SIZE (8 * 8) + +/* keypad auto scan registers */ +#define SKE_ASR0 0x20 +#define SKE_ASR1 0x24 +#define SKE_ASR2 0x28 +#define SKE_ASR3 0x2C + +#define SKE_NUM_ASRX_REGISTERS (4) + +/** + * struct ske_keypad - data structure used by keypad driver + * @irq: irq no + * @reg_base: ske regsiters base address + * @input: pointer to input device object + * @board: keypad platform device + * @keymap: matrix scan code table for keycodes + * @clk: clock structure pointer + */ +struct ske_keypad { + int irq; + void __iomem *reg_base; + struct input_dev *input; + const struct ske_keypad_platform_data *board; + unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; + struct clk *clk; + spinlock_t ske_keypad_lock; +}; + +static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, + u8 mask, u8 data) +{ + u32 ret; + + spin_lock(&keypad->ske_keypad_lock); + + ret = readl(keypad->reg_base + addr); + ret &= ~mask; + ret |= data; + writel(ret, keypad->reg_base + addr); + + spin_unlock(&keypad->ske_keypad_lock); +} + +/* + * ske_keypad_chip_init: init keypad controller configuration + * + * Enable Multi key press detection, auto scan mode + */ +static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) +{ + u32 value; + int timeout = 50; + + /* check SKE_RIS to be 0 */ + while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) + cpu_relax(); + + if (!timeout) + return -EINVAL; + + /* + * set debounce value + * keypad dbounce is configured in DBCR[15:8] + * dbounce value in steps of 32/32.768 ms + */ + spin_lock(&keypad->ske_keypad_lock); + value = readl(keypad->reg_base + SKE_DBCR); + value = value & 0xff; + value |= ((keypad->board->debounce_ms * 32000)/32768) << 8; + writel(value, keypad->reg_base + SKE_DBCR); + spin_unlock(&keypad->ske_keypad_lock); + + /* enable multi key detection */ + ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT); + + /* + * set up the number of columns + * KPCN[5:3] defines no. of keypad columns to be auto scanned + */ + value = (keypad->board->kcol - 1) << 3; + ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value); + + /* clear keypad interrupt for auto(and pending SW) scans */ + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS); + + /* un-mask keypad interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + /* enable automatic scan */ + ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN); + + return 0; +} + +static void ske_keypad_read_data(struct ske_keypad *keypad) +{ + struct input_dev *input = keypad->input; + u16 status; + int col = 0, row = 0, code; + int ske_asr, ske_ris, key_pressed, i; + + /* + * Read the auto scan registers + * + * Each SKE_ASRx (x=0 to x=3) contains two row values. + * lower byte contains row value for column 2*x, + * upper byte contains row value for column 2*x + 1 + */ + for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) { + ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i)); + if (!ske_asr) + continue; + + /* now that ASRx is zero, find out the column x and row y*/ + if (ske_asr & 0xff) { + col = i * 2; + status = ske_asr & 0xff; + } else { + col = (i * 2) + 1; + status = (ske_asr & 0xff00) >> 8; + } + + /* find out the row */ + row = __ffs(status); + + code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); + ske_ris = readl(keypad->reg_base + SKE_RIS); + key_pressed = ske_ris & SKE_KPRISA; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], key_pressed); + input_sync(input); + } +} + +static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + int retries = 20; + + /* disable auto scan interrupt; mask the interrupt generated */ + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); + + while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) + msleep(5); + + if (retries) { + /* SKEx registers are stable and can be read */ + ske_keypad_read_data(keypad); + } + + /* enable auto scan interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + return IRQ_HANDLED; +} + +static int __devinit ske_keypad_probe(struct platform_device *pdev) +{ + const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; + struct ske_keypad *keypad; + struct input_dev *input; + struct resource *res; + int irq; + int error; + + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing platform resources\n"); + return -EINVAL; + } + + keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->irq = irq; + keypad->board = plat; + keypad->input = input; + spin_lock_init(&keypad->ske_keypad_lock); + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err_free_mem; + } + + keypad->reg_base = ioremap(res->start, resource_size(res)); + if (!keypad->reg_base) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err_free_mem_region; + } + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get clk\n"); + error = PTR_ERR(keypad->clk); + goto err_iounmap; + } + + input->id.bustype = BUS_HOST; + input->name = "ux500-ske-keypad"; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, + input->keycode, input->keybit); + + clk_enable(keypad->clk); + + /* go through board initialization helpers */ + if (keypad->board->init) + keypad->board->init(); + + error = ske_keypad_chip_init(keypad); + if (error) { + dev_err(&pdev->dev, "unable to init keypad hardware\n"); + goto err_clk_disable; + } + + error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, + IRQF_ONESHOT, "ske-keypad", keypad); + if (error) { + dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); + goto err_clk_disable; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", error); + goto err_free_irq; + } + + if (plat->wakeup_enable) + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(keypad->irq, keypad); +err_clk_disable: + clk_disable(keypad->clk); + clk_put(keypad->clk); +err_iounmap: + iounmap(keypad->reg_base); +err_free_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit ske_keypad_remove(struct platform_device *pdev) +{ + struct ske_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + free_irq(keypad->irq, keypad); + + input_unregister_device(keypad->input); + + clk_disable(keypad->clk); + clk_put(keypad->clk); + + if (keypad->board->exit) + keypad->board->exit(); + + iounmap(keypad->reg_base); + release_mem_region(res->start, resource_size(res)); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int ske_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + else + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + + return 0; +} + +static int ske_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + else + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + return 0; +} + +static const struct dev_pm_ops ske_keypad_dev_pm_ops = { + .suspend = ske_keypad_suspend, + .resume = ske_keypad_resume, +}; +#endif + +struct platform_driver ske_keypad_driver = { + .driver = { + .name = "nmk-ske-keypad", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ske_keypad_dev_pm_ops, +#endif + }, + .probe = ske_keypad_probe, + .remove = __devexit_p(ske_keypad_remove), +}; + +static int __init ske_keypad_init(void) +{ + return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); +} +module_init(ske_keypad_init); + +static void __exit ske_keypad_exit(void) +{ + platform_driver_unregister(&ske_keypad_driver); +} +module_exit(ske_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>"); +MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver"); +MODULE_ALIAS("platform:nomadik-ske-keypad"); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c new file mode 100644 index 00000000000..45bd0977d00 --- /dev/null +++ b/drivers/input/keyboard/omap4-keypad.c @@ -0,0 +1,318 @@ +/* + * OMAP4 Keypad Driver + * + * Copyright (C) 2010 Texas Instruments + * + * Author: Abraham Arce <x0066660@ti.com> + * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/input.h> +#include <linux/slab.h> + +#include <plat/omap4-keypad.h> + +/* OMAP4 registers */ +#define OMAP4_KBD_REVISION 0x00 +#define OMAP4_KBD_SYSCONFIG 0x10 +#define OMAP4_KBD_SYSSTATUS 0x14 +#define OMAP4_KBD_IRQSTATUS 0x18 +#define OMAP4_KBD_IRQENABLE 0x1C +#define OMAP4_KBD_WAKEUPENABLE 0x20 +#define OMAP4_KBD_PENDING 0x24 +#define OMAP4_KBD_CTRL 0x28 +#define OMAP4_KBD_DEBOUNCINGTIME 0x2C +#define OMAP4_KBD_LONGKEYTIME 0x30 +#define OMAP4_KBD_TIMEOUT 0x34 +#define OMAP4_KBD_STATEMACHINE 0x38 +#define OMAP4_KBD_ROWINPUTS 0x3C +#define OMAP4_KBD_COLUMNOUTPUTS 0x40 +#define OMAP4_KBD_FULLCODE31_0 0x44 +#define OMAP4_KBD_FULLCODE63_32 0x48 + +/* OMAP4 bit definitions */ +#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) +#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) +#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) +#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0) +#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1) +#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) +#define OMAP4_DEF_CTRLPTVVALUE (1 << 2) +#define OMAP4_DEF_CTRLPTV (1 << 1) + +/* OMAP4 values */ +#define OMAP4_VAL_IRQDISABLE 0x00 +#define OMAP4_VAL_DEBOUNCINGTIME 0x07 +#define OMAP4_VAL_FUNCTIONALCFG 0x1E + +#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF + +struct omap4_keypad { + struct input_dev *input; + + void __iomem *base; + int irq; + + unsigned int rows; + unsigned int cols; + unsigned int row_shift; + unsigned char key_state[8]; + unsigned short keymap[]; +}; + +static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data) +{ + __raw_writel(OMAP4_VAL_FUNCTIONALCFG, + keypad_data->base + OMAP4_KBD_CTRL); + __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, + keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQSTATUS); + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); + __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, + keypad_data->base + OMAP4_KBD_WAKEUPENABLE); +} + +/* Interrupt handler */ +static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) +{ + struct omap4_keypad *keypad_data = dev_id; + struct input_dev *input_dev = keypad_data->input; + unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)]; + unsigned int col, row, code, changed; + u32 *new_state = (u32 *) key_state; + + /* Disable interrupts */ + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0); + *(new_state + 1) = __raw_readl(keypad_data->base + + OMAP4_KBD_FULLCODE63_32); + + for (row = 0; row < keypad_data->rows; row++) { + changed = key_state[row] ^ keypad_data->key_state[row]; + if (!changed) + continue; + + for (col = 0; col < keypad_data->cols; col++) { + if (changed & (1 << col)) { + code = MATRIX_SCAN_CODE(row, col, + keypad_data->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, + keypad_data->keymap[code], + key_state[row] & (1 << col)); + } + } + } + + input_sync(input_dev); + + memcpy(keypad_data->key_state, key_state, + sizeof(keypad_data->key_state)); + + /* clear pending interrupts */ + __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), + keypad_data->base + OMAP4_KBD_IRQSTATUS); + + /* enable interrupts */ + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + return IRQ_HANDLED; +} + +static int __devinit omap4_keypad_probe(struct platform_device *pdev) +{ + const struct omap4_keypad_platform_data *pdata; + struct omap4_keypad *keypad_data; + struct input_dev *input_dev; + struct resource *res; + resource_size_t size; + unsigned int row_shift, max_keys; + int irq; + int error; + + /* platform data */ + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no base address specified\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "no keyboard irq assigned\n"); + return -EINVAL; + } + + if (!pdata->keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->cols); + max_keys = pdata->rows << row_shift; + + keypad_data = kzalloc(sizeof(struct omap4_keypad) + + max_keys * sizeof(keypad_data->keymap[0]), + GFP_KERNEL); + if (!keypad_data) { + dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); + return -ENOMEM; + } + + size = resource_size(res); + + res = request_mem_region(res->start, size, pdev->name); + if (!res) { + dev_err(&pdev->dev, "can't request mem region\n"); + error = -EBUSY; + goto err_free_keypad; + } + + keypad_data->base = ioremap(res->start, resource_size(res)); + if (!keypad_data->base) { + dev_err(&pdev->dev, "can't ioremap mem resource\n"); + error = -ENOMEM; + goto err_release_mem; + } + + keypad_data->irq = irq; + keypad_data->row_shift = row_shift; + keypad_data->rows = pdata->rows; + keypad_data->cols = pdata->cols; + + /* input device allocation */ + keypad_data->input = input_dev = input_allocate_device(); + if (!input_dev) { + error = -ENOMEM; + goto err_unmap; + } + + input_dev->name = pdev->name; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0001; + + input_dev->keycode = keypad_data->keymap; + input_dev->keycodesize = sizeof(keypad_data->keymap[0]); + input_dev->keycodemax = max_keys; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_set_drvdata(input_dev, keypad_data); + + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + omap4_keypad_config(keypad_data); + + error = request_irq(keypad_data->irq, omap4_keypad_interrupt, + IRQF_TRIGGER_RISING, + "omap4-keypad", keypad_data); + if (error) { + dev_err(&pdev->dev, "failed to register interrupt\n"); + goto err_free_input; + } + + error = input_register_device(keypad_data->input); + if (error < 0) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err_free_irq; + } + + + platform_set_drvdata(pdev, keypad_data); + return 0; + +err_free_irq: + free_irq(keypad_data->irq, keypad_data); +err_free_input: + input_free_device(input_dev); +err_unmap: + iounmap(keypad_data->base); +err_release_mem: + release_mem_region(res->start, size); +err_free_keypad: + kfree(keypad_data); + return error; +} + +static int __devexit omap4_keypad_remove(struct platform_device *pdev) +{ + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(keypad_data->irq, keypad_data); + input_unregister_device(keypad_data->input); + + iounmap(keypad_data->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(keypad_data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver omap4_keypad_driver = { + .probe = omap4_keypad_probe, + .remove = __devexit_p(omap4_keypad_remove), + .driver = { + .name = "omap4-keypad", + .owner = THIS_MODULE, + }, +}; + +static int __init omap4_keypad_init(void) +{ + return platform_driver_register(&omap4_keypad_driver); +} +module_init(omap4_keypad_init); + +static void __exit omap4_keypad_exit(void) +{ + platform_driver_unregister(&omap4_keypad_driver); +} +module_exit(omap4_keypad_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP4 Keypad Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap4-keypad"); diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c new file mode 100644 index 00000000000..b4a81ebfab9 --- /dev/null +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -0,0 +1,340 @@ +/* + * Texas Instruments TNETV107X Keypad Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/input/matrix_keypad.h> + +#define BITS(x) (BIT(x) - 1) + +#define KEYPAD_ROWS 9 +#define KEYPAD_COLS 9 + +#define DEBOUNCE_MIN 0x400ul +#define DEBOUNCE_MAX 0x3ffffffful + +struct keypad_regs { + u32 rev; + u32 mode; + u32 mask; + u32 pol; + u32 dclock; + u32 rclock; + u32 stable_cnt; + u32 in_en; + u32 out; + u32 out_en; + u32 in; + u32 lock; + u32 pres[3]; +}; + +#define keypad_read(kp, reg) __raw_readl(&(kp)->regs->reg) +#define keypad_write(kp, reg, val) __raw_writel(val, &(kp)->regs->reg) + +struct keypad_data { + struct input_dev *input_dev; + struct resource *res; + struct keypad_regs __iomem *regs; + struct clk *clk; + struct device *dev; + spinlock_t lock; + u32 irq_press; + u32 irq_release; + int rows, cols, row_shift; + int debounce_ms, active_low; + u32 prev_keys[3]; + unsigned short keycodes[]; +}; + +static irqreturn_t keypad_irq(int irq, void *data) +{ + struct keypad_data *kp = data; + int i, bit, val, row, col, code; + unsigned long flags; + u32 curr_keys[3]; + u32 change; + + spin_lock_irqsave(&kp->lock, flags); + + memset(curr_keys, 0, sizeof(curr_keys)); + if (irq == kp->irq_press) + for (i = 0; i < 3; i++) + curr_keys[i] = keypad_read(kp, pres[i]); + + for (i = 0; i < 3; i++) { + change = curr_keys[i] ^ kp->prev_keys[i]; + + while (change) { + bit = fls(change) - 1; + change ^= BIT(bit); + val = curr_keys[i] & BIT(bit); + bit += i * 32; + row = bit / KEYPAD_COLS; + col = bit % KEYPAD_COLS; + + code = MATRIX_SCAN_CODE(row, col, kp->row_shift); + input_event(kp->input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(kp->input_dev, kp->keycodes[code], + val); + } + } + input_sync(kp->input_dev); + memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys)); + + if (irq == kp->irq_press) + keypad_write(kp, lock, 0); /* Allow hardware updates */ + + spin_unlock_irqrestore(&kp->lock, flags); + + return IRQ_HANDLED; +} + +static int keypad_start(struct input_dev *dev) +{ + struct keypad_data *kp = input_get_drvdata(dev); + unsigned long mask, debounce, clk_rate_khz; + unsigned long flags; + + clk_enable(kp->clk); + clk_rate_khz = clk_get_rate(kp->clk) / 1000; + + spin_lock_irqsave(&kp->lock, flags); + + /* Initialize device registers */ + keypad_write(kp, mode, 0); + + mask = BITS(kp->rows) << KEYPAD_COLS; + mask |= BITS(kp->cols); + keypad_write(kp, mask, ~mask); + + keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff); + keypad_write(kp, stable_cnt, 3); + + debounce = kp->debounce_ms * clk_rate_khz; + debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX); + keypad_write(kp, dclock, debounce); + keypad_write(kp, rclock, 4 * debounce); + + keypad_write(kp, in_en, 1); + + spin_unlock_irqrestore(&kp->lock, flags); + + return 0; +} + +static void keypad_stop(struct input_dev *dev) +{ + struct keypad_data *kp = input_get_drvdata(dev); + + synchronize_irq(kp->irq_press); + synchronize_irq(kp->irq_release); + clk_disable(kp->clk); +} + +static int __devinit keypad_probe(struct platform_device *pdev) +{ + const struct matrix_keypad_platform_data *pdata; + const struct matrix_keymap_data *keymap_data; + struct device *dev = &pdev->dev; + struct keypad_data *kp; + int error = 0, sz, row_shift; + u32 rev = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "cannot find device data\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(dev, "cannot find keymap data\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->num_col_gpios); + sz = offsetof(struct keypad_data, keycodes); + sz += (pdata->num_row_gpios << row_shift) * sizeof(kp->keycodes[0]); + kp = kzalloc(sz, GFP_KERNEL); + if (!kp) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + kp->dev = dev; + kp->rows = pdata->num_row_gpios; + kp->cols = pdata->num_col_gpios; + kp->row_shift = row_shift; + platform_set_drvdata(pdev, kp); + spin_lock_init(&kp->lock); + + kp->irq_press = platform_get_irq_byname(pdev, "press"); + kp->irq_release = platform_get_irq_byname(pdev, "release"); + if (kp->irq_press < 0 || kp->irq_release < 0) { + dev_err(dev, "cannot determine device interrupts\n"); + error = -ENODEV; + goto error_res; + } + + kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!kp->res) { + dev_err(dev, "cannot determine register area\n"); + error = -ENODEV; + goto error_res; + } + + if (!request_mem_region(kp->res->start, resource_size(kp->res), + pdev->name)) { + dev_err(dev, "cannot claim register memory\n"); + kp->res = NULL; + error = -EINVAL; + goto error_res; + } + + kp->regs = ioremap(kp->res->start, resource_size(kp->res)); + if (!kp->regs) { + dev_err(dev, "cannot map register memory\n"); + error = -ENOMEM; + goto error_map; + } + + kp->clk = clk_get(dev, NULL); + if (!kp->clk) { + dev_err(dev, "cannot claim device clock\n"); + error = -EINVAL; + goto error_clk; + } + + error = request_threaded_irq(kp->irq_press, NULL, keypad_irq, 0, + dev_name(dev), kp); + if (error < 0) { + dev_err(kp->dev, "Could not allocate keypad press key irq\n"); + goto error_irq_press; + } + + error = request_threaded_irq(kp->irq_release, NULL, keypad_irq, 0, + dev_name(dev), kp); + if (error < 0) { + dev_err(kp->dev, "Could not allocate keypad release key irq\n"); + goto error_irq_release; + } + + kp->input_dev = input_allocate_device(); + if (!kp->input_dev) { + dev_err(dev, "cannot allocate input device\n"); + error = -ENOMEM; + goto error_input; + } + input_set_drvdata(kp->input_dev, kp); + + kp->input_dev->name = pdev->name; + kp->input_dev->dev.parent = &pdev->dev; + kp->input_dev->open = keypad_start; + kp->input_dev->close = keypad_stop; + kp->input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); + + clk_enable(kp->clk); + rev = keypad_read(kp, rev); + kp->input_dev->id.bustype = BUS_HOST; + kp->input_dev->id.product = ((rev >> 8) & 0x07); + kp->input_dev->id.version = ((rev >> 16) & 0xfff); + clk_disable(kp->clk); + + kp->input_dev->keycode = kp->keycodes; + kp->input_dev->keycodesize = sizeof(kp->keycodes[0]); + kp->input_dev->keycodemax = kp->rows << kp->row_shift; + + matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes, + kp->input_dev->keybit); + + input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN); + + error = input_register_device(kp->input_dev); + if (error < 0) { + dev_err(dev, "Could not register input device\n"); + goto error_reg; + } + + return 0; + + +error_reg: + input_free_device(kp->input_dev); +error_input: + free_irq(kp->irq_release, kp); +error_irq_release: + free_irq(kp->irq_press, kp); +error_irq_press: + clk_put(kp->clk); +error_clk: + iounmap(kp->regs); +error_map: + release_mem_region(kp->res->start, resource_size(kp->res)); +error_res: + platform_set_drvdata(pdev, NULL); + kfree(kp); + return error; +} + +static int __devexit keypad_remove(struct platform_device *pdev) +{ + struct keypad_data *kp = platform_get_drvdata(pdev); + + free_irq(kp->irq_press, kp); + free_irq(kp->irq_release, kp); + input_unregister_device(kp->input_dev); + clk_put(kp->clk); + iounmap(kp->regs); + release_mem_region(kp->res->start, resource_size(kp->res)); + platform_set_drvdata(pdev, NULL); + kfree(kp); + + return 0; +} + +static struct platform_driver keypad_driver = { + .probe = keypad_probe, + .remove = __devexit_p(keypad_remove), + .driver.name = "tnetv107x-keypad", + .driver.owner = THIS_MODULE, +}; + +static int __init keypad_init(void) +{ + return platform_driver_register(&keypad_driver); +} + +static void __exit keypad_exit(void) +{ + platform_driver_unregister(&keypad_driver); +} + +module_init(keypad_init); +module_exit(keypad_exit); + +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_DESCRIPTION("TNETV107X Keypad Driver"); +MODULE_ALIAS("platform: tnetv107x-keypad"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index fb16b5e5ea1..09bef79d9da 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) if (error) { dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n", kp->irq); - goto err3; + goto err2; } /* Enable KP and TO interrupts now. */ reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO); if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) { error = -EIO; - goto err4; + goto err3; } platform_set_drvdata(pdev, kp); return 0; -err4: +err3: /* mask all events - we don't care about the result */ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); -err3: free_irq(kp->irq, NULL); err2: input_unregister_device(input); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index b49e2337972..b99b8cbde02 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. +config INPUT_AB8500_PONKEY + tristate "AB8500 Pon (PowerOn) Key" + depends on AB8500_CORE + help + Say Y here to use the PowerOn Key for ST-Ericsson's AB8500 + Mix-Sig PMIC. + + To compile this driver as a module, choose M here: the module + will be called ab8500-ponkey. + config INPUT_AD714X tristate "Analog Devices AD714x Capacitance Touch Sensor" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 19ccca78fa7..1fe1f6c8b73 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c new file mode 100644 index 00000000000..3d3288a78fd --- /dev/null +++ b/drivers/input/misc/ab8500-ponkey.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson + * + * AB8500 Power-On Key handler + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/ab8500.h> +#include <linux/slab.h> + +/** + * struct ab8500_ponkey - ab8500 ponkey information + * @input_dev: pointer to input device + * @ab8500: ab8500 parent + * @irq_dbf: irq number for falling transition + * @irq_dbr: irq number for rising transition + */ +struct ab8500_ponkey { + struct input_dev *idev; + struct ab8500 *ab8500; + int irq_dbf; + int irq_dbr; +}; + +/* AB8500 gives us an interrupt when ONKEY is held */ +static irqreturn_t ab8500_ponkey_handler(int irq, void *data) +{ + struct ab8500_ponkey *ponkey = data; + + if (irq == ponkey->irq_dbf) + input_report_key(ponkey->idev, KEY_POWER, true); + else if (irq == ponkey->irq_dbr) + input_report_key(ponkey->idev, KEY_POWER, false); + + input_sync(ponkey->idev); + + return IRQ_HANDLED; +} + +static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_ponkey *ponkey; + struct input_dev *input; + int irq_dbf, irq_dbr; + int error; + + irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF"); + if (irq_dbf < 0) { + dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf); + return irq_dbf; + } + + irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR"); + if (irq_dbr < 0) { + dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr); + return irq_dbr; + } + + ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL); + input = input_allocate_device(); + if (!ponkey || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + ponkey->idev = input; + ponkey->ab8500 = ab8500; + ponkey->irq_dbf = irq_dbf; + ponkey->irq_dbr = irq_dbr; + + input->name = "AB8500 POn(PowerOn) Key"; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler, + 0, "ab8500-ponkey-dbf", ponkey); + if (error < 0) { + dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", + ponkey->irq_dbf, error); + goto err_free_mem; + } + + error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler, + 0, "ab8500-ponkey-dbr", ponkey); + if (error < 0) { + dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", + ponkey->irq_dbr, error); + goto err_free_dbf_irq; + } + + error = input_register_device(ponkey->idev); + if (error) { + dev_err(ab8500->dev, "Can't register input device: %d\n", error); + goto err_free_dbr_irq; + } + + platform_set_drvdata(pdev, ponkey); + return 0; + +err_free_dbr_irq: + free_irq(ponkey->irq_dbr, ponkey); +err_free_dbf_irq: + free_irq(ponkey->irq_dbf, ponkey); +err_free_mem: + input_free_device(input); + kfree(ponkey); + + return error; +} + +static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) +{ + struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev); + + free_irq(ponkey->irq_dbf, ponkey); + free_irq(ponkey->irq_dbr, ponkey); + input_unregister_device(ponkey->idev); + kfree(ponkey); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ab8500_ponkey_driver = { + .driver = { + .name = "ab8500-poweron-key", + .owner = THIS_MODULE, + }, + .probe = ab8500_ponkey_probe, + .remove = __devexit_p(ab8500_ponkey_remove), +}; + +static int __init ab8500_ponkey_init(void) +{ + return platform_driver_register(&ab8500_ponkey_driver); +} +module_init(ab8500_ponkey_init); + +static void __exit ab8500_ponkey_exit(void) +{ + platform_driver_unregister(&ab8500_ponkey_driver); +} +module_exit(ab8500_ponkey_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>"); +MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver"); diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 23257652b8e..0b0e9be6354 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -483,51 +483,88 @@ static void ati_remote2_complete_key(struct urb *urb) } static int ati_remote2_getkeycode(struct input_dev *idev, - unsigned int scancode, unsigned int *keycode) + struct input_keymap_entry *ke) { struct ati_remote2 *ar2 = input_get_drvdata(idev); unsigned int mode; - int index; + int offset; + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + if (index >= ATI_REMOTE2_MODES * + ARRAY_SIZE(ati_remote2_key_table)) + return -EINVAL; + + mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); + offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); + scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code; + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC) + return -EINVAL; + + offset = ati_remote2_lookup(scancode & 0xff); + if (offset < 0) + return -EINVAL; + + index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset; + } - mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) - return -EINVAL; + ke->keycode = ar2->keycode[mode][offset]; + ke->len = sizeof(scancode); + memcpy(&ke->scancode, &scancode, sizeof(scancode)); + ke->index = index; - index = ati_remote2_lookup(scancode & 0xFF); - if (index < 0) - return -EINVAL; - - *keycode = ar2->keycode[mode][index]; return 0; } static int ati_remote2_setkeycode(struct input_dev *idev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { struct ati_remote2 *ar2 = input_get_drvdata(idev); - unsigned int mode, old_keycode; - int index; - - mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) - return -EINVAL; - - index = ati_remote2_lookup(scancode & 0xFF); - if (index < 0) - return -EINVAL; + unsigned int mode; + int offset; + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + if (ke->index >= ATI_REMOTE2_MODES * + ARRAY_SIZE(ati_remote2_key_table)) + return -EINVAL; + + mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); + offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC) + return -EINVAL; + + offset = ati_remote2_lookup(scancode & 0xff); + if (offset < 0) + return -EINVAL; + } - old_keycode = ar2->keycode[mode][index]; - ar2->keycode[mode][index] = keycode; - __set_bit(keycode, idev->keybit); + *old_keycode = ar2->keycode[mode][offset]; + ar2->keycode[mode][offset] = ke->keycode; + __set_bit(ke->keycode, idev->keybit); for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { - if (ar2->keycode[mode][index] == old_keycode) + if (ar2->keycode[mode][index] == *old_keycode) return 0; } } - __clear_bit(old_keycode, idev->keybit); + __clear_bit(*old_keycode, idev->keybit); return 0; } @@ -575,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->open = ati_remote2_open; idev->close = ati_remote2_close; - idev->getkeycode = ati_remote2_getkeycode; - idev->setkeycode = ati_remote2_setkeycode; + idev->getkeycode_new = ati_remote2_getkeycode; + idev->setkeycode_new = ati_remote2_setkeycode; idev->name = ar2->name; idev->phys = ar2->phys; diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index bf170f6b442..f45947190e4 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL); if (!pm->configcr) - return -1; + return -ENOMEM; return 0; } diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 4f9b2afc24e..014dd4ad0d4 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -271,7 +271,7 @@ static struct platform_driver twl4030_vibra_driver = { .probe = twl4030_vibra_probe, .remove = __devexit_p(twl4030_vibra_remove), .driver = { - .name = "twl4030_codec_vibra", + .name = "twl4030-vibra", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &twl4030_vibra_pm_ops, @@ -291,7 +291,7 @@ static void __exit twl4030_vibra_exit(void) } module_exit(twl4030_vibra_exit); -MODULE_ALIAS("platform:twl4030_codec_vibra"); +MODULE_ALIAS("platform:twl4030-vibra"); MODULE_DESCRIPTION("TWL4030 Vibra driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 48311204ba5..04d9bf320a4 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse) psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); if (!etd) - return -1; + return -ENOMEM; etd->parity[0] = 1; for (i = 1; i < 256; i++) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 73a7af2542a..cd9d0c97e42 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co if (!new_dev) return -ENOMEM; - while (serio->child) { + while (!list_empty(&serio->children)) { if (++retry > 3) { printk(KERN_WARNING - "psmouse: failed to destroy child port, " + "psmouse: failed to destroy children ports, " "protocol change aborted.\n"); input_free_device(new_dev); return -EIO; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 96b70a43515..2e300a46055 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c) return 0; } -static inline int synaptics_is_pt_packet(unsigned char *buf) +static int synaptics_pt_start(struct serio *serio) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + struct synaptics_data *priv = parent->private; + + serio_pause_rx(parent->ps2dev.serio); + priv->pt_port = serio; + serio_continue_rx(parent->ps2dev.serio); + + return 0; +} + +static void synaptics_pt_stop(struct serio *serio) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + struct synaptics_data *priv = parent->private; + + serio_pause_rx(parent->ps2dev.serio); + priv->pt_port = NULL; + serio_continue_rx(parent->ps2dev.serio); +} + +static int synaptics_is_pt_packet(unsigned char *buf) { return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; } @@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet static void synaptics_pt_activate(struct psmouse *psmouse) { - struct serio *ptport = psmouse->ps2dev.serio->child; - struct psmouse *child = serio_get_drvdata(ptport); struct synaptics_data *priv = psmouse->private; + struct psmouse *child = serio_get_drvdata(priv->pt_port); /* adjust the touchpad to child's choice of protocol */ if (child) { @@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse) strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); serio->write = synaptics_pt_write; + serio->start = synaptics_pt_start; + serio->stop = synaptics_pt_stop; serio->parent = psmouse->ps2dev.serio; psmouse->pt_activate = synaptics_pt_activate; @@ -578,9 +601,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (unlikely(priv->pkt_type == SYN_NEWABS)) priv->pkt_type = synaptics_detect_pkt_type(psmouse); - if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { - if (psmouse->ps2dev.serio->child) - synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); + if (SYN_CAP_PASS_THROUGH(priv->capabilities) && + synaptics_is_pt_packet(psmouse->packet)) { + if (priv->pt_port) + synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); } else synaptics_process_packet(psmouse); @@ -731,7 +755,7 @@ int synaptics_init(struct psmouse *psmouse) psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) - return -1; + return -ENOMEM; psmouse_reset(psmouse); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index b6aa7d20d8a..613a3652f98 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -110,6 +110,8 @@ struct synaptics_data { unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char mode; /* current mode byte */ int scroll; + + struct serio *pt_port; /* Pass-through serio port */ }; void synaptics_module_init(void); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 0643e49ca60..54b2fa892e1 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); if (!psmouse->private) - return -1; + return -ENOMEM; psmouse->vendor = "IBM"; psmouse->name = "TrackPoint"; diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 31ec7265aac..2a00ddf4f23 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -867,7 +867,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev, spin_lock_init(&mousedev->client_lock); mutex_init(&mousedev->mutex); lockdep_set_subclass(&mousedev->mutex, - minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0); + minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); init_waitqueue_head(&mousedev->wait); if (minor == MOUSEDEV_MIX) diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 3bfe8fafc6a..6256233d2bf 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -226,4 +226,13 @@ config SERIO_AMS_DELTA To compile this driver as a module, choose M here; the module will be called ams_delta_serio. +config SERIO_PS2MULT + tristate "TQC PS/2 multiplexer" + help + Say Y here if you have the PS/2 line multiplexer like the one + present on TQC boads. + + To compile this driver as a module, choose M here: the + module will be called ps2mult. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 84c80bf7185..dbbe37616c9 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_HP_SDC) += hp_sdc.o obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o +obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index f5851316048..18db5a8c747 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state) #ifdef CONFIG_X86 static void i8042_dritek_enable(void) { - char param = 0x90; + unsigned char param = 0x90; int error; error = i8042_command(¶m, 0x1059); diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c new file mode 100644 index 00000000000..6bce22e4e49 --- /dev/null +++ b/drivers/input/serio/ps2mult.c @@ -0,0 +1,318 @@ +/* + * TQC PS/2 Multiplexer driver + * + * Copyright (C) 2010 Dmitry Eremin-Solenikov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/serio.h> + +MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>"); +MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver"); +MODULE_LICENSE("GPL"); + +#define PS2MULT_KB_SELECTOR 0xA0 +#define PS2MULT_MS_SELECTOR 0xA1 +#define PS2MULT_ESCAPE 0x7D +#define PS2MULT_BSYNC 0x7E +#define PS2MULT_SESSION_START 0x55 +#define PS2MULT_SESSION_END 0x56 + +struct ps2mult_port { + struct serio *serio; + unsigned char sel; + bool registered; +}; + +#define PS2MULT_NUM_PORTS 2 +#define PS2MULT_KBD_PORT 0 +#define PS2MULT_MOUSE_PORT 1 + +struct ps2mult { + struct serio *mx_serio; + struct ps2mult_port ports[PS2MULT_NUM_PORTS]; + + spinlock_t lock; + struct ps2mult_port *in_port; + struct ps2mult_port *out_port; + bool escape; +}; + +/* First MUST come PS2MULT_NUM_PORTS selectors */ +static const unsigned char ps2mult_controls[] = { + PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR, + PS2MULT_ESCAPE, PS2MULT_BSYNC, + PS2MULT_SESSION_START, PS2MULT_SESSION_END, +}; + +static const struct serio_device_id ps2mult_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PS2MULT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids); + +static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port) +{ + struct serio *mx_serio = psm->mx_serio; + + serio_write(mx_serio, port->sel); + psm->out_port = port; + dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel); +} + +static int ps2mult_serio_write(struct serio *serio, unsigned char data) +{ + struct serio *mx_port = serio->parent; + struct ps2mult *psm = serio_get_drvdata(mx_port); + struct ps2mult_port *port = serio->port_data; + bool need_escape; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->out_port != port) + ps2mult_select_port(psm, port); + + need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls)); + + dev_dbg(&serio->dev, + "write: %s%02x\n", need_escape ? "ESC " : "", data); + + if (need_escape) + serio_write(mx_port, PS2MULT_ESCAPE); + + serio_write(mx_port, data); + + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static int ps2mult_serio_start(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = true; + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static void ps2mult_serio_stop(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = false; + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_create_port(struct ps2mult *psm, int i) +{ + struct serio *mx_serio = psm->mx_serio; + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), + "%s/port%d", mx_serio->phys, i); + serio->id.type = SERIO_8042; + serio->write = ps2mult_serio_write; + serio->start = ps2mult_serio_start; + serio->stop = ps2mult_serio_stop; + serio->parent = psm->mx_serio; + serio->port_data = &psm->ports[i]; + + psm->ports[i].serio = serio; + + return 0; +} + +static void ps2mult_reset(struct ps2mult *psm) +{ + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + serio_write(psm->mx_serio, PS2MULT_SESSION_END); + serio_write(psm->mx_serio, PS2MULT_SESSION_START); + + ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); + + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) +{ + struct ps2mult *psm; + int i; + int error; + + if (!serio->write) + return -EINVAL; + + psm = kzalloc(sizeof(*psm), GFP_KERNEL); + if (!psm) + return -ENOMEM; + + spin_lock_init(&psm->lock); + psm->mx_serio = serio; + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + psm->ports[i].sel = ps2mult_controls[i]; + error = ps2mult_create_port(psm, i); + if (error) + goto err_out; + } + + psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; + + serio_set_drvdata(serio, psm); + error = serio_open(serio, drv); + if (error) + goto err_out; + + ps2mult_reset(psm); + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + struct serio *s = psm->ports[i].serio; + + dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys); + serio_register_port(s); + } + + return 0; + +err_out: + while (--i >= 0) + kfree(psm->ports[i].serio); + kfree(serio); + return error; +} + +static void ps2mult_disconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + /* Note that serio core already take care of children ports */ + serio_write(serio, PS2MULT_SESSION_END); + serio_close(serio); + kfree(psm); + + serio_set_drvdata(serio, NULL); +} + +static int ps2mult_reconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + ps2mult_reset(psm); + + return 0; +} + +static irqreturn_t ps2mult_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + struct ps2mult_port *in_port; + unsigned long flags; + + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->escape) { + psm->escape = false; + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + goto out; + } + + switch (data) { + case PS2MULT_ESCAPE: + dev_dbg(&serio->dev, "ESCAPE\n"); + psm->escape = true; + break; + + case PS2MULT_BSYNC: + dev_dbg(&serio->dev, "BSYNC\n"); + psm->in_port = psm->out_port; + break; + + case PS2MULT_SESSION_START: + dev_dbg(&serio->dev, "SS\n"); + break; + + case PS2MULT_SESSION_END: + dev_dbg(&serio->dev, "SE\n"); + break; + + case PS2MULT_KB_SELECTOR: + dev_dbg(&serio->dev, "KB\n"); + psm->in_port = &psm->ports[PS2MULT_KBD_PORT]; + break; + + case PS2MULT_MS_SELECTOR: + dev_dbg(&serio->dev, "MS\n"); + psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT]; + break; + + default: + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + break; + } + + out: + spin_unlock_irqrestore(&psm->lock, flags); + return IRQ_HANDLED; +} + +static struct serio_driver ps2mult_drv = { + .driver = { + .name = "ps2mult", + }, + .description = "TQC PS/2 Multiplexer driver", + .id_table = ps2mult_serio_ids, + .interrupt = ps2mult_interrupt, + .connect = ps2mult_connect, + .disconnect = ps2mult_disconnect, + .reconnect = ps2mult_reconnect, +}; + +static int __init ps2mult_init(void) +{ + return serio_register_driver(&ps2mult_drv); +} + +static void __exit ps2mult_exit(void) +{ + serio_unregister_driver(&ps2mult_drv); +} + +module_init(ps2mult_init); +module_exit(ps2mult_exit); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index c3b626e9eae..405bf214527 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -37,7 +37,6 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/mutex.h> -#include <linux/freezer.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Serio abstraction core"); @@ -56,7 +55,7 @@ static struct bus_type serio_bus; static void serio_add_port(struct serio *serio); static int serio_reconnect_port(struct serio *serio); static void serio_disconnect_port(struct serio *serio); -static void serio_reconnect_chain(struct serio *serio); +static void serio_reconnect_subtree(struct serio *serio); static void serio_attach_driver(struct serio_driver *drv); static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) @@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio) enum serio_event_type { SERIO_RESCAN_PORT, SERIO_RECONNECT_PORT, - SERIO_RECONNECT_CHAIN, + SERIO_RECONNECT_SUBTREE, SERIO_REGISTER_PORT, SERIO_ATTACH_DRIVER, }; @@ -292,8 +291,8 @@ static void serio_handle_event(void) serio_find_driver(event->object); break; - case SERIO_RECONNECT_CHAIN: - serio_reconnect_chain(event->object); + case SERIO_RECONNECT_SUBTREE: + serio_reconnect_subtree(event->object); break; case SERIO_ATTACH_DRIVER: @@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object) } /* - * Destroy child serio port (if any) that has not been fully registered yet. + * Locate child serio port (if any) that has not been fully registered yet. * - * Note that we rely on the fact that port can have only one child and therefore - * only one child registration request can be pending. Additionally, children - * are registered by driver's connect() handler so there can't be a grandchild - * pending registration together with a child. + * Children are registered by driver's connect() handler so there can't be a + * grandchild pending registration together with a child. */ static struct serio *serio_get_pending_child(struct serio *parent) { @@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { - serio_reconnect_chain(serio); + serio_reconnect_subtree(serio); } else if (!strncmp(buf, "rescan", count)) { serio_disconnect_port(serio); serio_find_driver(serio); @@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio) __module_get(THIS_MODULE); INIT_LIST_HEAD(&serio->node); + INIT_LIST_HEAD(&serio->child_node); + INIT_LIST_HEAD(&serio->children); spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); @@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio) */ static void serio_add_port(struct serio *serio) { + struct serio *parent = serio->parent; int error; - if (serio->parent) { - serio_pause_rx(serio->parent); - serio->parent->child = serio; - serio_continue_rx(serio->parent); + if (parent) { + serio_pause_rx(parent); + list_add_tail(&serio->child_node, &parent->children); + serio_continue_rx(parent); } list_add_tail(&serio->node, &serio_list); @@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio) } /* - * serio_destroy_port() completes deregistration process and removes + * serio_destroy_port() completes unregistration process and removes * port from the system */ static void serio_destroy_port(struct serio *serio) { struct serio *child; - child = serio_get_pending_child(serio); - if (child) { + while ((child = serio_get_pending_child(serio)) != NULL) { serio_remove_pending_events(child); put_device(&child->dev); } @@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio) if (serio->parent) { serio_pause_rx(serio->parent); - serio->parent->child = NULL; + list_del_init(&serio->child_node); serio_continue_rx(serio->parent); serio->parent = NULL; } @@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio) } /* - * Reconnect serio port and all its children (re-initialize attached devices) + * Reconnect serio port and all its children (re-initialize attached + * devices). */ -static void serio_reconnect_chain(struct serio *serio) +static void serio_reconnect_subtree(struct serio *root) { + struct serio *s = root; + int error; + do { - if (serio_reconnect_port(serio)) { - /* Ok, old children are now gone, we are done */ - break; + error = serio_reconnect_port(s); + if (!error) { + /* + * Reconnect was successful, move on to do the + * first child. + */ + if (!list_empty(&s->children)) { + s = list_first_entry(&s->children, + struct serio, child_node); + continue; + } } - serio = serio->child; - } while (serio); + + /* + * Either it was a leaf node or reconnect failed and it + * became a leaf node. Continue reconnecting starting with + * the next sibling of the parent node. + */ + while (s != root) { + struct serio *parent = s->parent; + + if (!list_is_last(&s->child_node, &parent->children)) { + s = list_entry(s->child_node.next, + struct serio, child_node); + break; + } + + s = parent; + } + } while (s != root); } /* * serio_disconnect_port() unbinds a port from its driver. As a side effect - * all child ports are unbound and destroyed. + * all children ports are unbound and destroyed. */ static void serio_disconnect_port(struct serio *serio) { - struct serio *s, *parent; + struct serio *s = serio; + + /* + * Children ports should be disconnected and destroyed + * first; we travel the tree in depth-first order. + */ + while (!list_empty(&serio->children)) { + + /* Locate a leaf */ + while (!list_empty(&s->children)) + s = list_first_entry(&s->children, + struct serio, child_node); - if (serio->child) { /* - * Children ports should be disconnected and destroyed - * first, staring with the leaf one, since we don't want - * to do recursion + * Prune this leaf node unless it is the one we + * started with. */ - for (s = serio; s->child; s = s->child) - /* empty */; - - do { - parent = s->parent; + if (s != serio) { + struct serio *parent = s->parent; device_release_driver(&s->dev); serio_destroy_port(s); - } while ((s = parent) != serio); + + s = parent; + } } /* - * Ok, no children left, now disconnect this port + * OK, no children left, now disconnect this port. */ device_release_driver(&serio->dev); } @@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan); void serio_reconnect(struct serio *serio) { - serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); + serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); } EXPORT_SYMBOL(serio_reconnect); @@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio) EXPORT_SYMBOL(serio_unregister_port); /* - * Safely unregisters child port if one is present. + * Safely unregisters children ports if they are present. */ void serio_unregister_child_port(struct serio *serio) { + struct serio *s, *next; + mutex_lock(&serio_mutex); - if (serio->child) { - serio_disconnect_port(serio->child); - serio_destroy_port(serio->child); + list_for_each_entry_safe(s, next, &serio->children, child_node) { + serio_disconnect_port(s); + serio_destroy_port(s); } mutex_unlock(&serio_mutex); } diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 01424834476..a29a7812bd4 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -22,6 +22,37 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1"); +static unsigned int sparse_keymap_get_key_index(struct input_dev *dev, + const struct key_entry *k) +{ + struct key_entry *key; + unsigned int idx = 0; + + for (key = dev->keycode; key->type != KE_END; key++) { + if (key->type == KE_KEY) { + if (key == k) + break; + idx++; + } + } + + return idx; +} + +static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev, + unsigned int index) +{ + struct key_entry *key; + unsigned int key_cnt = 0; + + for (key = dev->keycode; key->type != KE_END; key++) + if (key->type == KE_KEY) + if (key_cnt++ == index) + return key; + + return NULL; +} + /** * sparse_keymap_entry_from_scancode - perform sparse keymap lookup * @dev: Input device using sparse keymap @@ -64,16 +95,36 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev, } EXPORT_SYMBOL(sparse_keymap_entry_from_keycode); +static struct key_entry *sparse_keymap_locate(struct input_dev *dev, + const struct input_keymap_entry *ke) +{ + struct key_entry *key; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + key = sparse_keymap_entry_by_index(dev, ke->index); + else if (input_scancode_to_scalar(ke, &scancode) == 0) + key = sparse_keymap_entry_from_scancode(dev, scancode); + else + key = NULL; + + return key; +} + static int sparse_keymap_getkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int *keycode) + struct input_keymap_entry *ke) { const struct key_entry *key; if (dev->keycode) { - key = sparse_keymap_entry_from_scancode(dev, scancode); + key = sparse_keymap_locate(dev, ke); if (key && key->type == KE_KEY) { - *keycode = key->keycode; + ke->keycode = key->keycode; + if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) + ke->index = + sparse_keymap_get_key_index(dev, key); + ke->len = sizeof(key->code); + memcpy(ke->scancode, &key->code, sizeof(key->code)); return 0; } } @@ -82,20 +133,19 @@ static int sparse_keymap_getkeycode(struct input_dev *dev, } static int sparse_keymap_setkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { struct key_entry *key; - int old_keycode; if (dev->keycode) { - key = sparse_keymap_entry_from_scancode(dev, scancode); + key = sparse_keymap_locate(dev, ke); if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) - clear_bit(old_keycode, dev->keybit); + *old_keycode = key->keycode; + key->keycode = ke->keycode; + set_bit(ke->keycode, dev->keybit); + if (!sparse_keymap_entry_from_keycode(dev, *old_keycode)) + clear_bit(*old_keycode, dev->keybit); return 0; } } @@ -159,15 +209,14 @@ int sparse_keymap_setup(struct input_dev *dev, dev->keycode = map; dev->keycodemax = map_size; - dev->getkeycode = sparse_keymap_getkeycode; - dev->setkeycode = sparse_keymap_setkeycode; + dev->getkeycode_new = sparse_keymap_getkeycode; + dev->setkeycode_new = sparse_keymap_setkeycode; return 0; err_out: kfree(map); return error; - } EXPORT_SYMBOL(sparse_keymap_setup); diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index effb49ea24a..58a87755b93 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -49,6 +49,17 @@ config TABLET_USB_GTCO To compile this driver as a module, choose M here: the module will be called gtco. +config TABLET_USB_HANWANG + tristate "Hanwang Art Master III tablet support (USB)" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the USB version of the Hanwang Art + Master III tablet. + + To compile this driver as a module, choose M here: the + module will be called hanwang. + config TABLET_USB_KBTAB tristate "KB Gear JamStudio tablet support (USB)" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile index ce8b9a9cfa4..3f6c2522063 100644 --- a/drivers/input/tablet/Makefile +++ b/drivers/input/tablet/Makefile @@ -8,5 +8,6 @@ wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o +obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c new file mode 100644 index 00000000000..6504b627b23 --- /dev/null +++ b/drivers/input/tablet/hanwang.c @@ -0,0 +1,446 @@ +/* + * USB Hanwang tablet support + * + * Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn> + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb/input.h> + +#define DRIVER_AUTHOR "Xing Wei <weixing@hanwang.com.cn>" +#define DRIVER_DESC "USB Hanwang tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_HANWANG 0x0b57 +#define HANWANG_TABLET_INT_CLASS 0x0003 +#define HANWANG_TABLET_INT_SUB_CLASS 0x0001 +#define HANWANG_TABLET_INT_PROTOCOL 0x0002 + +#define ART_MASTER_PKGLEN_MAX 10 + +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define TOUCH_DEVICE_ID 0x03 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F + +/* match vendor and interface info */ +#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR \ + | USB_DEVICE_ID_MATCH_INT_INFO, \ + .idVendor = (vend), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), \ + .bInterfaceProtocol = (pr) + +enum hanwang_tablet_type { + HANWANG_ART_MASTER_III, + HANWANG_ART_MASTER_HD, +}; + +struct hanwang { + unsigned char *data; + dma_addr_t data_dma; + struct input_dev *dev; + struct usb_device *usbdev; + struct urb *irq; + const struct hanwang_features *features; + unsigned int current_tool; + unsigned int current_id; + char name[64]; + char phys[32]; +}; + +struct hanwang_features { + unsigned short pid; + char *name; + enum hanwang_tablet_type type; + int pkg_len; + int max_x; + int max_y; + int max_tilt_x; + int max_tilt_y; + int max_pressure; +}; + +static const struct hanwang_features features_array[] = { + { 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 }, + { 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 }, + { 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 }, + { 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD, + ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 }, +}; + +static const int hw_eventtypes[] = { + EV_KEY, EV_ABS, EV_MSC, +}; + +static const int hw_absevents[] = { + ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, + ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC, +}; + +static const int hw_btnevents[] = { + BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER, + BTN_TOOL_MOUSE, BTN_TOOL_FINGER, + BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8, +}; + +static const int hw_mscevents[] = { + MSC_SERIAL, +}; + +static void hanwang_parse_packet(struct hanwang *hanwang) +{ + unsigned char *data = hanwang->data; + struct input_dev *input_dev = hanwang->dev; + struct usb_device *dev = hanwang->usbdev; + enum hanwang_tablet_type type = hanwang->features->type; + int i; + u16 x, y, p; + + switch (data[0]) { + case 0x02: /* data packet */ + switch (data[1]) { + case 0x80: /* tool prox out */ + hanwang->current_id = 0; + input_report_key(input_dev, hanwang->current_tool, 0); + break; + + case 0xc2: /* first time tool prox in */ + switch (data[3] & 0xf0) { + case 0x20: /* art_master III */ + case 0x30: /* art_master_HD */ + hanwang->current_id = STYLUS_DEVICE_ID; + hanwang->current_tool = BTN_TOOL_PEN; + input_report_key(input_dev, BTN_TOOL_PEN, 1); + break; + case 0xa0: /* art_master III */ + case 0xb0: /* art_master_HD */ + hanwang->current_id = ERASER_DEVICE_ID; + hanwang->current_tool = BTN_TOOL_RUBBER; + input_report_key(input_dev, BTN_TOOL_RUBBER, 1); + break; + default: + hanwang->current_id = 0; + dev_dbg(&dev->dev, + "unknown tablet tool %02x ", data[0]); + break; + } + break; + + default: /* tool data packet */ + x = (data[2] << 8) | data[3]; + y = (data[4] << 8) | data[5]; + + switch (type) { + case HANWANG_ART_MASTER_III: + p = (data[6] << 3) | + ((data[7] & 0xc0) >> 5) | + (data[1] & 0x01); + break; + + case HANWANG_ART_MASTER_HD: + p = (data[7] >> 6) | (data[6] << 2); + break; + + default: + p = 0; + break; + } + + input_report_abs(input_dev, ABS_X, + le16_to_cpup((__le16 *)&x)); + input_report_abs(input_dev, ABS_Y, + le16_to_cpup((__le16 *)&y)); + input_report_abs(input_dev, ABS_PRESSURE, + le16_to_cpup((__le16 *)&p)); + input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f); + input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04); + break; + } + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); + input_event(input_dev, EV_MSC, MSC_SERIAL, + hanwang->features->pid); + break; + + case 0x0c: + /* roll wheel */ + hanwang->current_id = PAD_DEVICE_ID; + + switch (type) { + case HANWANG_ART_MASTER_III: + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3]); + input_report_abs(input_dev, ABS_WHEEL, data[1]); + input_report_key(input_dev, BTN_0, data[2]); + for (i = 0; i < 8; i++) + input_report_key(input_dev, + BTN_1 + i, data[3] & (1 << i)); + break; + + case HANWANG_ART_MASTER_HD: + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3] || data[4] || + data[5] || data[6]); + input_report_abs(input_dev, ABS_RX, + ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(input_dev, ABS_RY, + ((data[3] & 0x1f) << 8) | data[4]); + input_report_key(input_dev, BTN_0, data[5] & 0x01); + for (i = 0; i < 4; i++) { + input_report_key(input_dev, + BTN_1 + i, data[5] & (1 << i)); + input_report_key(input_dev, + BTN_5 + i, data[6] & (1 << i)); + } + break; + } + + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); + input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff); + break; + + default: + dev_dbg(&dev->dev, "error packet %02x ", data[0]); + break; + } + + input_sync(input_dev); +} + +static void hanwang_irq(struct urb *urb) +{ + struct hanwang *hanwang = urb->context; + struct usb_device *dev = hanwang->usbdev; + int retval; + + switch (urb->status) { + case 0: + /* success */; + hanwang_parse_packet(hanwang); + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dev_err(&dev->dev, "%s - urb shutting down with status: %d", + __func__, urb->status); + return; + default: + dev_err(&dev->dev, "%s - nonzero urb status received: %d", + __func__, urb->status); + break; + } + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static int hanwang_open(struct input_dev *dev) +{ + struct hanwang *hanwang = input_get_drvdata(dev); + + hanwang->irq->dev = hanwang->usbdev; + if (usb_submit_urb(hanwang->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void hanwang_close(struct input_dev *dev) +{ + struct hanwang *hanwang = input_get_drvdata(dev); + + usb_kill_urb(hanwang->irq); +} + +static bool get_features(struct usb_device *dev, struct hanwang *hanwang) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(features_array); i++) { + if (le16_to_cpu(dev->descriptor.idProduct) == + features_array[i].pid) { + hanwang->features = &features_array[i]; + return true; + } + } + + return false; +} + + +static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct hanwang *hanwang; + struct input_dev *input_dev; + int error; + int i; + + hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!hanwang || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + if (!get_features(dev, hanwang)) { + error = -ENXIO; + goto fail1; + } + + hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len, + GFP_KERNEL, &hanwang->data_dma); + if (!hanwang->data) { + error = -ENOMEM; + goto fail1; + } + + hanwang->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!hanwang->irq) { + error = -ENOMEM; + goto fail2; + } + + hanwang->usbdev = dev; + hanwang->dev = input_dev; + + usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys)); + strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys)); + + strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name)); + input_dev->name = hanwang->name; + input_dev->phys = hanwang->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, hanwang); + + input_dev->open = hanwang_open; + input_dev->close = hanwang_close; + + for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i) + __set_bit(hw_eventtypes[i], input_dev->evbit); + + for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i) + __set_bit(hw_absevents[i], input_dev->absbit); + + for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i) + __set_bit(hw_btnevents[i], input_dev->keybit); + + for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i) + __set_bit(hw_mscevents[i], input_dev->mscbit); + + input_set_abs_params(input_dev, ABS_X, + 0, hanwang->features->max_x, 4, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, hanwang->features->max_y, 4, 0); + input_set_abs_params(input_dev, ABS_TILT_X, + 0, hanwang->features->max_tilt_x, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, + 0, hanwang->features->max_tilt_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, hanwang->features->max_pressure, 0, 0); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + usb_fill_int_urb(hanwang->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + hanwang->data, hanwang->features->pkg_len, + hanwang_irq, hanwang, endpoint->bInterval); + hanwang->irq->transfer_dma = hanwang->data_dma; + hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(hanwang->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, hanwang); + + return 0; + + fail3: usb_free_urb(hanwang->irq); + fail2: usb_free_coherent(dev, hanwang->features->pkg_len, + hanwang->data, hanwang->data_dma); + fail1: input_free_device(input_dev); + kfree(hanwang); + return error; + +} + +static void hanwang_disconnect(struct usb_interface *intf) +{ + struct hanwang *hanwang = usb_get_intfdata(intf); + + input_unregister_device(hanwang->dev); + usb_free_urb(hanwang->irq); + usb_free_coherent(interface_to_usbdev(intf), + hanwang->features->pkg_len, hanwang->data, + hanwang->data_dma); + kfree(hanwang); + usb_set_intfdata(intf, NULL); +} + +static const struct usb_device_id hanwang_ids[] = { + { HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS, + HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, hanwang_ids); + +static struct usb_driver hanwang_driver = { + .name = "hanwang", + .probe = hanwang_probe, + .disconnect = hanwang_disconnect, + .id_table = hanwang_ids, +}; + +static int __init hanwang_init(void) +{ + return usb_register(&hanwang_driver); +} + +static void __exit hanwang_exit(void) +{ + usb_deregister(&hanwang_driver); +} + +module_init(hanwang_init); +module_exit(hanwang_exit); diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 284dfaab6b8..de5adb10903 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -118,6 +118,7 @@ struct wacom { extern const struct usb_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); +void wacom_setup_device_quirks(struct wacom_features *features); void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac); #endif diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index b35876ee690..fc381498b79 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -120,14 +120,16 @@ static int wacom_open(struct input_dev *dev) out: mutex_unlock(&wacom->lock); - if (retval) - usb_autopm_put_interface(wacom->intf); + usb_autopm_put_interface(wacom->intf); return retval; } static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); + int autopm_error; + + autopm_error = usb_autopm_get_interface(wacom->intf); mutex_lock(&wacom->lock); usb_kill_urb(wacom->irq); @@ -135,7 +137,8 @@ static void wacom_close(struct input_dev *dev) wacom->intf->needs_remote_wakeup = 0; mutex_unlock(&wacom->lock); - usb_autopm_put_interface(wacom->intf); + if (!autopm_error) + usb_autopm_put_interface(wacom->intf); } static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, @@ -196,17 +199,30 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->pktlen = WACOM_PKGLEN_TPC2FG; features->device_type = BTN_TOOL_TRIPLETAP; } - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i + 9]; - features->unitExpo = report[i + 11]; - i += 12; + if (features->type == BAMBOO_PT) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_BBTOUCH; + features->device_type = BTN_TOOL_TRIPLETAP; + features->x_phy = + get_unaligned_le16(&report[i + 5]); + features->x_max = + get_unaligned_le16(&report[i + 8]); + i += 15; + } else { + features->x_max = + get_unaligned_le16(&report[i + 3]); + features->x_phy = + get_unaligned_le16(&report[i + 6]); + features->unit = report[i + 9]; + features->unitExpo = report[i + 11]; + i += 12; + } } else if (pen) { /* penabled only accepts exact bytes of data */ if (features->type == TABLETPC2FG) features->pktlen = WACOM_PKGLEN_GRAPHIRE; + if (features->type == BAMBOO_PT) + features->pktlen = WACOM_PKGLEN_BBFUN; features->device_type = BTN_TOOL_PEN; features->x_max = get_unaligned_le16(&report[i + 3]); @@ -235,6 +251,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->y_phy = get_unaligned_le16(&report[i + 6]); i += 7; + } else if (features->type == BAMBOO_PT) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_BBTOUCH; + features->device_type = BTN_TOOL_TRIPLETAP; + features->y_phy = + get_unaligned_le16(&report[i + 3]); + features->y_max = + get_unaligned_le16(&report[i + 6]); + i += 12; } else { features->y_max = features->x_max; @@ -246,6 +271,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi /* penabled only accepts exact bytes of data */ if (features->type == TABLETPC2FG) features->pktlen = WACOM_PKGLEN_GRAPHIRE; + if (features->type == BAMBOO_PT) + features->pktlen = WACOM_PKGLEN_BBFUN; features->device_type = BTN_TOOL_PEN; features->y_max = get_unaligned_le16(&report[i + 3]); @@ -296,8 +323,9 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat if (!rep_data) return error; - /* ask to report tablet data if it is 2FGT or not a Tablet PC */ - if (features->device_type == BTN_TOOL_TRIPLETAP) { + /* ask to report tablet data if it is 2FGT Tablet PC or + * not a Tablet PC */ + if (features->type == TABLETPC2FG) { do { rep_data[0] = 3; rep_data[1] = 4; @@ -309,7 +337,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat WAC_HID_FEATURE_REPORT, report_id, rep_data, 3); } while ((error < 0 || rep_data[1] != 4) && limit++ < 5); - } else if (features->type != TABLETPC && features->type != TABLETPC2FG) { + } else if (features->type != TABLETPC) { do { rep_data[0] = 2; rep_data[1] = 2; @@ -334,11 +362,16 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, struct usb_host_interface *interface = intf->cur_altsetting; struct hid_descriptor *hid_desc; - /* default device to penabled */ + /* default features */ features->device_type = BTN_TOOL_PEN; - - /* only Tablet PCs need to retrieve the info */ - if ((features->type != TABLETPC) && (features->type != TABLETPC2FG)) + features->x_fuzz = 4; + features->y_fuzz = 4; + features->pressure_fuzz = 0; + features->distance_fuzz = 0; + + /* only Tablet PCs and Bamboo P&T need to retrieve the info */ + if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && + (features->type != BAMBOO_PT)) goto out; if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) { @@ -353,12 +386,6 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, if (error) goto out; - /* touch device found but size is not defined. use default */ - if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { - features->x_max = 1023; - features->y_max = 1023; - } - out: return error; } @@ -494,9 +521,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail2; + wacom_setup_device_quirks(features); + strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); - if (features->type == TABLETPC || features->type == TABLETPC2FG) { + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { /* Append the device type to the name */ strlcat(wacom_wac->name, features->device_type == BTN_TOOL_PEN ? diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 47fd7a041c5..b3252ef1e27 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -857,6 +857,134 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) return retval; } +static int wacom_bpt_touch(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int sp = 0, sx = 0, sy = 0, count = 0; + int i; + + for (i = 0; i < 2; i++) { + int p = data[9 * i + 2]; + input_mt_slot(input, i); + /* + * Touch events need to be disabled while stylus is + * in proximity because user's hand is resting on touchpad + * and sending unwanted events. User expects tablet buttons + * to continue working though. + */ + if (p && !wacom->shared->stylus_in_proximity) { + int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; + int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; + if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { + x <<= 5; + y <<= 5; + } + input_report_abs(input, ABS_MT_PRESSURE, p); + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + if (wacom->id[i] < 0) + wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID; + if (!count++) + sp = p, sx = x, sy = y; + } else { + wacom->id[i] = -1; + } + input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]); + } + + input_report_key(input, BTN_TOUCH, count > 0); + input_report_key(input, BTN_TOOL_FINGER, count == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2); + + input_report_abs(input, ABS_PRESSURE, sp); + input_report_abs(input, ABS_X, sx); + input_report_abs(input, ABS_Y, sy); + + input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); + input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); + input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); + input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); + + input_sync(input); + + return 0; +} + +static int wacom_bpt_pen(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; + + /* + * Similar to Graphire protocol, data[1] & 0x20 is proximity and + * data[1] & 0x18 is tool ID. 0x30 is safety check to ignore + * 2 unused tool ID's. + */ + prox = (data[1] & 0x30) == 0x30; + + /* + * All reports shared between PEN and RUBBER tool must be + * forced to a known starting value (zero) when transitioning to + * out-of-prox. + * + * If not reset then, to userspace, it will look like lost events + * if new tool comes in-prox with same values as previous tool sent. + * + * Hardware does report zero in most out-of-prox cases but not all. + */ + if (prox) { + if (!wacom->shared->stylus_in_proximity) { + if (data[1] & 0x08) { + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + } else { + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + wacom->shared->stylus_in_proximity = true; + } + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + p = le16_to_cpup((__le16 *)&data[6]); + d = data[8]; + pen = data[1] & 0x01; + btn1 = data[1] & 0x02; + btn2 = data[1] & 0x04; + } + + input_report_key(input, BTN_TOUCH, pen); + input_report_key(input, BTN_STYLUS, btn1); + input_report_key(input, BTN_STYLUS2, btn2); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, p); + input_report_abs(input, ABS_DISTANCE, d); + + if (!prox) { + wacom->id[0] = 0; + wacom->shared->stylus_in_proximity = false; + } + + input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ + + return 1; +} + +static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) +{ + if (len == WACOM_PKGLEN_BBTOUCH) + return wacom_bpt_touch(wacom); + else if (len == WACOM_PKGLEN_BBFUN) + return wacom_bpt_pen(wacom); + + return 0; +} + void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; @@ -902,6 +1030,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) sync = wacom_tpc_irq(wacom_wac, len); break; + case BAMBOO_PT: + sync = wacom_bpt_irq(wacom_wac, len); + break; + default: sync = false; break; @@ -911,26 +1043,17 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) input_sync(wacom_wac->input); } -static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) { struct input_dev *input_dev = wacom_wac->input; input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - input_set_capability(input_dev, EV_REL, REL_WHEEL); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_MIDDLE, input_dev->keybit); - __set_bit(BTN_SIDE, input_dev->keybit); - __set_bit(BTN_EXTRA, input_dev->keybit); __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); - __set_bit(BTN_TOOL_LENS, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); @@ -939,10 +1062,55 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac) input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); +} + +static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + wacom_setup_cintiq(wacom_wac); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_TOOL_LENS, input_dev->keybit); + input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); } +void wacom_setup_device_quirks(struct wacom_features *features) +{ + + /* touch device found but size is not defined. use default */ + if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { + features->x_max = 1023; + features->y_max = 1023; + } + + /* these device have multiple inputs */ + if (features->type == TABLETPC || features->type == TABLETPC2FG || + features->type == BAMBOO_PT) + features->quirks |= WACOM_QUIRK_MULTI_INPUT; + + /* quirks for bamboo touch */ + if (features->type == BAMBOO_PT && + features->device_type == BTN_TOOL_TRIPLETAP) { + features->x_max <<= 5; + features->y_max <<= 5; + features->x_fuzz <<= 5; + features->y_fuzz <<= 5; + features->pressure_max = 256; + features->pressure_fuzz = 16; + features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; + } +} + void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { @@ -953,9 +1121,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOUCH, input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); - input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0); + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, + features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, + features->pressure_fuzz, 0); __set_bit(ABS_MISC, input_dev->absbit); @@ -1005,9 +1176,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_9, input_dev->keybit); /* fall through */ + case CINTIQ: + for (i = 0; i < 8; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + wacom_setup_cintiq(wacom_wac); + break; + case INTUOS3: case INTUOS3L: - case CINTIQ: __set_bit(BTN_4, input_dev->keybit); __set_bit(BTN_5, input_dev->keybit); __set_bit(BTN_6, input_dev->keybit); @@ -1078,6 +1259,38 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case PENPARTNER: __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); break; + + case BAMBOO_PT: + __clear_bit(ABS_MISC, input_dev->absbit); + + if (features->device_type == BTN_TOOL_TRIPLETAP) { + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + + input_mt_create_slots(input_dev, 2); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, features->y_max, + features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, features->pressure_max, + features->pressure_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + MAX_TRACKING_ID, 0, 0); + } else if (features->device_type == BTN_TOOL_PEN) { + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + } + break; } } @@ -1215,6 +1428,14 @@ static const struct wacom_features wacom_features_0xE3 = { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG }; static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; +static struct wacom_features wacom_features_0xD0 = + { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD1 = + { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD2 = + { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD3 = + { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1279,6 +1500,10 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xC6) }, { USB_DEVICE_WACOM(0xC7) }, { USB_DEVICE_WACOM(0xCE) }, + { USB_DEVICE_WACOM(0xD0) }, + { USB_DEVICE_WACOM(0xD1) }, + { USB_DEVICE_WACOM(0xD2) }, + { USB_DEVICE_WACOM(0xD3) }, { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 99e1a54cd30..00ca01541d8 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -21,6 +21,7 @@ #define WACOM_PKGLEN_INTUOS 10 #define WACOM_PKGLEN_TPC1FG 5 #define WACOM_PKGLEN_TPC2FG 14 +#define WACOM_PKGLEN_BBTOUCH 20 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -37,6 +38,13 @@ #define WACOM_REPORT_TPC1FG 6 #define WACOM_REPORT_TPC2FG 13 +/* device quirks */ +#define WACOM_QUIRK_MULTI_INPUT 0x0001 +#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 + +/* largest reported tracking id */ +#define MAX_TRACKING_ID 0xfff + enum { PENPARTNER = 0, GRAPHIRE, @@ -44,6 +52,7 @@ enum { PTU, PL, DTU, + BAMBOO_PT, INTUOS, INTUOS3S, INTUOS3, @@ -73,6 +82,11 @@ struct wacom_features { int y_phy; unsigned char unit; unsigned char unitExpo; + int x_fuzz; + int y_fuzz; + int pressure_fuzz; + int distance_fuzz; + unsigned quirks; }; struct wacom_shared { @@ -86,6 +100,7 @@ struct wacom_wac { int id[3]; __u32 serial[2]; int last_finger; + int trk_id; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0069d9703fd..06ea8da95c6 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -98,6 +98,18 @@ config TOUCHSCREEN_BITSY To compile this driver as a module, choose M here: the module will be called h3600_ts_input. +config TOUCHSCREEN_BU21013 + tristate "BU21013 based touch panel controllers" + depends on I2C + help + Say Y here if you have a bu21013 touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bu21013_ts. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C @@ -214,6 +226,16 @@ config TOUCHSCREEN_WACOM_W8001 To compile this driver as a module, choose M here: the module will be called wacom_w8001. +config TOUCHSCREEN_LPC32XX + tristate "LPC32XX touchscreen controller" + depends on ARCH_LPC32XX + help + Say Y here if you have a LPC32XX device and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called lpc32xx_ts. + config TOUCHSCREEN_MCS5000 tristate "MELFAS MCS-5000 touchscreen" depends on I2C @@ -250,6 +272,18 @@ config TOUCHSCREEN_INEXIO To compile this driver as a module, choose M here: the module will be called inexio. +config TOUCHSCREEN_INTEL_MID + tristate "Intel MID platform resistive touchscreen" + depends on INTEL_SCU_IPC + help + Say Y here if you have a Intel MID based touchscreen in + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called intel_mid_touch. + config TOUCHSCREEN_MK712 tristate "ICS MicroClock MK712 touchscreen" help @@ -328,6 +362,15 @@ config TOUCHSCREEN_MIGOR To compile this driver as a module, choose M here: the module will be called migor_ts. +config TOUCHSCREEN_TNETV107X + tristate "TI TNETV107X touchscreen support" + depends on ARCH_DAVINCI_TNETV107X + help + Say Y here if you want to use the TNETV107X touchscreen. + + To compile this driver as a module, choose M here: the + module will be called tnetv107x-ts. + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 28217e1dcaf..7cc1b4f4b67 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o @@ -23,6 +24,8 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o +obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o @@ -37,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 5f0221cffef..a1952fcc083 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -191,13 +191,12 @@ struct ad7877 { struct spi_message msg; struct mutex mutex; - unsigned disabled:1; /* P: mutex */ - unsigned gpio3:1; /* P: mutex */ - unsigned gpio4:1; /* P: mutex */ + bool disabled; /* P: mutex */ + bool gpio3; /* P: mutex */ + bool gpio4; /* P: mutex */ spinlock_t lock; struct timer_list timer; /* P: lock */ - unsigned pending:1; /* P: lock */ /* * DMA (thus cache coherency maintenance) requires the @@ -206,8 +205,8 @@ struct ad7877 { u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned; }; -static int gpio3; -module_param(gpio3, int, 0); +static bool gpio3; +module_param(gpio3, bool, 0); MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3"); /* @@ -230,6 +229,7 @@ static int ad7877_read(struct spi_device *spi, u16 reg) AD7877_READADD(reg)); req->xfer[0].tx_buf = &req->command; req->xfer[0].len = 2; + req->xfer[0].cs_change = 1; req->xfer[1].rx_buf = &req->sample; req->xfer[1].len = 2; @@ -295,20 +295,25 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) req->xfer[0].tx_buf = &req->reset; req->xfer[0].len = 2; + req->xfer[0].cs_change = 1; req->xfer[1].tx_buf = &req->ref_on; req->xfer[1].len = 2; req->xfer[1].delay_usecs = ts->vref_delay_usecs; + req->xfer[1].cs_change = 1; req->xfer[2].tx_buf = &req->command; req->xfer[2].len = 2; req->xfer[2].delay_usecs = ts->vref_delay_usecs; + req->xfer[2].cs_change = 1; req->xfer[3].rx_buf = &req->sample; req->xfer[3].len = 2; + req->xfer[3].cs_change = 1; req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/ req->xfer[4].len = 2; + req->xfer[4].cs_change = 1; req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/ req->xfer[5].len = 2; @@ -327,7 +332,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) return status ? : sample; } -static void ad7877_rx(struct ad7877 *ts) +static int ad7877_process_data(struct ad7877 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -354,11 +359,25 @@ static void ad7877_rx(struct ad7877 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; + /* + * Sample found inconsistent, pressure is beyond + * the maximum. Don't report it to user space. + */ + if (Rt > ts->pressure_max) + return -EINVAL; + + if (!timer_pending(&ts->timer)) + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, Rt); input_sync(input_dev); + + return 0; } + + return -EINVAL; } static inline void ad7877_ts_event_release(struct ad7877 *ts) @@ -366,72 +385,56 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts) struct input_dev *input_dev = ts->input; input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } static void ad7877_timer(unsigned long handle) { struct ad7877 *ts = (void *)handle; + unsigned long flags; + spin_lock_irqsave(&ts->lock, flags); ad7877_ts_event_release(ts); + spin_unlock_irqrestore(&ts->lock, flags); } static irqreturn_t ad7877_irq(int irq, void *handle) { struct ad7877 *ts = handle; unsigned long flags; - int status; + int error; - /* - * The repeated conversion sequencer controlled by TMR kicked off - * too fast. We ignore the last and process the sample sequence - * currently in the queue. It can't be older than 9.4ms, and we - * need to avoid that ts->msg doesn't get issued twice while in work. - */ + error = spi_sync(ts->spi, &ts->msg); + if (error) { + dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); + goto out; + } spin_lock_irqsave(&ts->lock, flags); - if (!ts->pending) { - ts->pending = 1; - - status = spi_async(ts->spi, &ts->msg); - if (status) - dev_err(&ts->spi->dev, "spi_sync --> %d\n", status); - } + error = ad7877_process_data(ts); + if (!error) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); spin_unlock_irqrestore(&ts->lock, flags); +out: return IRQ_HANDLED; } -static void ad7877_callback(void *_ts) -{ - struct ad7877 *ts = _ts; - - spin_lock_irq(&ts->lock); - - ad7877_rx(ts); - ts->pending = 0; - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); - - spin_unlock_irq(&ts->lock); -} - static void ad7877_disable(struct ad7877 *ts) { mutex_lock(&ts->mutex); if (!ts->disabled) { - ts->disabled = 1; + ts->disabled = true; disable_irq(ts->spi->irq); - /* Wait for spi_async callback */ - while (ts->pending) - msleep(1); - if (del_timer_sync(&ts->timer)) ad7877_ts_event_release(ts); } - /* we know the chip's in lowpower mode since we always + /* + * We know the chip's in lowpower mode since we always * leave it that way after every request */ @@ -443,7 +446,7 @@ static void ad7877_enable(struct ad7877 *ts) mutex_lock(&ts->mutex); if (ts->disabled) { - ts->disabled = 0; + ts->disabled = false; enable_irq(ts->spi->irq); } @@ -453,7 +456,7 @@ static void ad7877_enable(struct ad7877 *ts) #define SHOW(name) static ssize_t \ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ - struct ad7877 *ts = dev_get_drvdata(dev); \ + struct ad7877 *ts = dev_get_drvdata(dev); \ ssize_t v = ad7877_read_adc(ts->spi, \ AD7877_READ_CHAN(name)); \ if (v < 0) \ @@ -473,7 +476,7 @@ SHOW(temp2) static ssize_t ad7877_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->disabled); } @@ -503,7 +506,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store); static ssize_t ad7877_dac_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->dac); } @@ -533,7 +536,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store); static ssize_t ad7877_gpio3_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->gpio3); } @@ -564,7 +567,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store); static ssize_t ad7877_gpio4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->gpio4); } @@ -597,16 +600,35 @@ static struct attribute *ad7877_attributes[] = { &dev_attr_temp2.attr, &dev_attr_aux1.attr, &dev_attr_aux2.attr, + &dev_attr_aux3.attr, &dev_attr_bat1.attr, &dev_attr_bat2.attr, &dev_attr_disable.attr, &dev_attr_dac.attr, + &dev_attr_gpio3.attr, &dev_attr_gpio4.attr, NULL }; +static mode_t ad7877_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + mode_t mode = attr->mode; + + if (attr == &dev_attr_aux3.attr) { + if (gpio3) + mode = 0; + } else if (attr == &dev_attr_gpio3.attr) { + if (!gpio3) + mode = 0; + } + + return mode; +} + static const struct attribute_group ad7877_attr_group = { - .attrs = ad7877_attributes, + .is_visible = ad7877_attr_is_visible, + .attrs = ad7877_attributes, }; static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) @@ -635,22 +657,25 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) spi_message_init(m); - m->complete = ad7877_callback; m->context = ts; ts->xfer[0].tx_buf = &ts->cmd_crtl1; ts->xfer[0].len = 2; + ts->xfer[0].cs_change = 1; spi_message_add_tail(&ts->xfer[0], m); ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */ ts->xfer[1].len = 2; + ts->xfer[1].cs_change = 1; spi_message_add_tail(&ts->xfer[1], m); - for (i = 0; i < 11; i++) { + for (i = 0; i < AD7877_NR_SENSE; i++) { ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i]; ts->xfer[i + 2].len = 2; + if (i < (AD7877_NR_SENSE - 1)) + ts->xfer[i + 2].cs_change = 1; spi_message_add_tail(&ts->xfer[i + 2], m); } } @@ -718,6 +743,8 @@ static int __devinit ad7877_probe(struct spi_device *spi) input_dev->phys = ts->phys; input_dev->dev.parent = &spi->dev; + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); @@ -752,8 +779,9 @@ static int __devinit ad7877_probe(struct spi_device *spi) /* Request AD7877 /DAV GPIO interrupt */ - err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING, - spi->dev.driver->name, ts); + err = request_threaded_irq(spi->irq, NULL, ad7877_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi->dev.driver->name, ts); if (err) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); goto err_free_mem; @@ -763,20 +791,12 @@ static int __devinit ad7877_probe(struct spi_device *spi) if (err) goto err_free_irq; - err = device_create_file(&spi->dev, - gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); - if (err) - goto err_remove_attr_group; - err = input_register_device(input_dev); if (err) - goto err_remove_attr; + goto err_remove_attr_group; return 0; -err_remove_attr: - device_remove_file(&spi->dev, - gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); err_remove_attr_group: sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); err_free_irq: @@ -790,11 +810,9 @@ err_free_mem: static int __devexit ad7877_remove(struct spi_device *spi) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = dev_get_drvdata(&spi->dev); sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); - device_remove_file(&spi->dev, - gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); ad7877_disable(ts); free_irq(ts->spi->irq, ts); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 16031933a8f..14ea54b78e4 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -17,9 +17,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/types.h> #include <linux/hwmon.h> #include <linux/init.h> #include <linux/err.h> +#include <linux/sched.h> #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -52,22 +54,23 @@ * files. */ -#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ -#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */ +#define TS_POLL_DELAY 1 /* ms delay before the first sample */ +#define TS_POLL_PERIOD 5 /* ms delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) struct ts_event { - /* For portability, we can't read 12 bit values using SPI (which - * would make the controller deliver them as native byteorder u16 + /* + * For portability, we can't read 12 bit values using SPI (which + * would make the controller deliver them as native byte order u16 * with msbs zeroed). Instead, we read them as two 8-bit values, * *** WHICH NEED BYTESWAPPING *** and range adjustment. */ u16 x; u16 y; u16 z1, z2; - int ignore; + bool ignore; u8 x_buf[3]; u8 y_buf[3]; }; @@ -110,8 +113,11 @@ struct ads7846 { struct spi_transfer xfer[18]; struct spi_message msg[5]; - struct spi_message *last_msg; - int msg_idx; + int msg_count; + wait_queue_head_t wait; + + bool pendown; + int read_cnt; int read_rep; int last_read; @@ -122,14 +128,10 @@ struct ads7846 { u16 penirq_recheck_delay_usecs; - spinlock_t lock; - struct hrtimer timer; - unsigned pendown:1; /* P: lock */ - unsigned pending:1; /* P: lock */ -// FIXME remove "irq_disabled" - unsigned irq_disabled:1; /* P: lock */ - unsigned disabled:1; - unsigned is_suspended:1; + struct mutex lock; + bool stopped; /* P: lock */ + bool disabled; /* P: lock */ + bool suspended; /* P: lock */ int (*filter)(void *data, int data_idx, int *val); void *filter_data; @@ -165,7 +167,7 @@ struct ads7846 { #define ADS_12_BIT (0 << 3) #define ADS_SER (1 << 2) /* non-differential */ #define ADS_DFR (0 << 2) /* differential */ -#define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */ +#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */ #define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ #define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ #define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ @@ -193,6 +195,78 @@ struct ads7846 { #define REF_ON (READ_12BIT_DFR(x, 1, 1)) #define REF_OFF (READ_12BIT_DFR(y, 0, 0)) +/* Must be called with ts->lock held */ +static void ads7846_stop(struct ads7846 *ts) +{ + if (!ts->disabled && !ts->suspended) { + /* Signal IRQ thread to stop polling and disable the handler. */ + ts->stopped = true; + mb(); + wake_up(&ts->wait); + disable_irq(ts->spi->irq); + } +} + +/* Must be called with ts->lock held */ +static void ads7846_restart(struct ads7846 *ts) +{ + if (!ts->disabled && !ts->suspended) { + /* Tell IRQ thread that it may poll the device. */ + ts->stopped = false; + mb(); + enable_irq(ts->spi->irq); + } +} + +/* Must be called with ts->lock held */ +static void __ads7846_disable(struct ads7846 *ts) +{ + ads7846_stop(ts); + regulator_disable(ts->reg); + + /* + * We know the chip's in low power mode since we always + * leave it that way after every request + */ +} + +/* Must be called with ts->lock held */ +static void __ads7846_enable(struct ads7846 *ts) +{ + regulator_enable(ts->reg); + ads7846_restart(ts); +} + +static void ads7846_disable(struct ads7846 *ts) +{ + mutex_lock(&ts->lock); + + if (!ts->disabled) { + + if (!ts->suspended) + __ads7846_disable(ts); + + ts->disabled = true; + } + + mutex_unlock(&ts->lock); +} + +static void ads7846_enable(struct ads7846 *ts) +{ + mutex_lock(&ts->lock); + + if (ts->disabled) { + + ts->disabled = false; + + if (!ts->suspended) + __ads7846_enable(ts); + } + + mutex_unlock(&ts->lock); +} + /*--------------------------------------------------------------------------*/ /* @@ -219,23 +293,15 @@ struct ads7845_ser_req { struct spi_transfer xfer[2]; }; -static void ads7846_enable(struct ads7846 *ts); -static void ads7846_disable(struct ads7846 *ts); - -static int device_suspended(struct device *dev) -{ - struct ads7846 *ts = dev_get_drvdata(dev); - return ts->is_suspended || ts->disabled; -} - static int ads7846_read12_ser(struct device *dev, unsigned command) { - struct spi_device *spi = to_spi_device(dev); - struct ads7846 *ts = dev_get_drvdata(dev); - struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); - int status; - int use_internal; + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ser_req *req; + int status; + int use_internal; + req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; @@ -282,11 +348,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) CS_CHANGE(req->xfer[5]); spi_message_add_tail(&req->xfer[5], &req->msg); - ts->irq_disabled = 1; - disable_irq(spi->irq); + mutex_lock(&ts->lock); + ads7846_stop(ts); status = spi_sync(spi, &req->msg); - ts->irq_disabled = 0; - enable_irq(spi->irq); + ads7846_restart(ts); + mutex_unlock(&ts->lock); if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ @@ -301,11 +367,12 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) static int ads7845_read12_ser(struct device *dev, unsigned command) { - struct spi_device *spi = to_spi_device(dev); - struct ads7846 *ts = dev_get_drvdata(dev); - struct ads7845_ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); - int status; + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7845_ser_req *req; + int status; + req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; @@ -317,11 +384,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); - ts->irq_disabled = 1; - disable_irq(spi->irq); + mutex_lock(&ts->lock); + ads7846_stop(ts); status = spi_sync(spi, &req->msg); - ts->irq_disabled = 0; - enable_irq(spi->irq); + ads7846_restart(ts); + mutex_unlock(&ts->lock); if (status == 0) { /* BE12 value, then padding */ @@ -374,6 +441,7 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) /* external resistors may scale vAUX into 0..vREF */ retval *= ts->vref_mv; retval = retval >> 12; + return retval; } @@ -384,13 +452,13 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) /* ads7846 has a resistor ladder to scale this signal down */ if (ts->model == 7846) retval *= 4; + return retval; } SHOW(in0_input, vaux, vaux_adjust) SHOW(in1_input, vbatt, vbatt_adjust) - static struct attribute *ads7846_attributes[] = { &dev_attr_temp0.attr, &dev_attr_temp1.attr, @@ -498,17 +566,12 @@ static inline void ads784x_hwmon_unregister(struct spi_device *spi, } #endif -static int is_pen_down(struct device *dev) -{ - struct ads7846 *ts = dev_get_drvdata(dev); - - return ts->pendown; -} - static ssize_t ads7846_pen_down_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", is_pen_down(dev)); + struct ads7846 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->pendown); } static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); @@ -516,7 +579,7 @@ static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); static ssize_t ads7846_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7846 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->disabled); } @@ -531,15 +594,11 @@ static ssize_t ads7846_disable_store(struct device *dev, if (strict_strtoul(buf, 10, &i)) return -EINVAL; - spin_lock_irq(&ts->lock); - if (i) ads7846_disable(ts); else ads7846_enable(ts); - spin_unlock_irq(&ts->lock); - return count; } @@ -569,23 +628,141 @@ static void null_wait_for_sync(void) { } -/* - * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, - * to retrieve touchscreen status. - * - * The SPI transfer completion callback does the real work. It reports - * touchscreen events and reactivates the timer (or IRQ) as appropriate. - */ +static int ads7846_debounce_filter(void *ads, int data_idx, int *val) +{ + struct ads7846 *ts = ads; + + if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 0; + /* + * Repeat it, if this was the first read or the read + * wasn't consistent enough. + */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = *val; + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } else { + /* + * Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->read_cnt = 0; + return ADS7846_FILTER_IGNORE; + } + } else { + if (++ts->read_rep > ts->debounce_rep) { + /* + * Got a good reading for this coordinate, + * go for the next one. + */ + ts->read_cnt = 0; + ts->read_rep = 0; + return ADS7846_FILTER_OK; + } else { + /* Read more values that are consistent. */ + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } + } +} + +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ + return ADS7846_FILTER_OK; +} + +static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) +{ + struct spi_transfer *t = + list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + if (ts->model == 7845) { + return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; + } else { + /* + * adjust: on-wire is a must-ignore bit, a BE12 value, then + * padding; built from two 8 bit values written msb-first. + */ + return be16_to_cpup((__be16 *)t->rx_buf) >> 3; + } +} + +static void ads7846_update_value(struct spi_message *m, int val) +{ + struct spi_transfer *t = + list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + *(u16 *)t->rx_buf = val; +} + +static void ads7846_read_state(struct ads7846 *ts) +{ + struct ads7846_packet *packet = ts->packet; + struct spi_message *m; + int msg_idx = 0; + int val; + int action; + int error; + + while (msg_idx < ts->msg_count) { + + ts->wait_for_sync(); + + m = &ts->msg[msg_idx]; + error = spi_sync(ts->spi, m); + if (error) { + dev_err(&ts->spi->dev, "spi_async --> %d\n", error); + packet->tc.ignore = true; + return; + } + + /* + * Last message is power down request, no need to convert + * or filter the value. + */ + if (msg_idx < ts->msg_count - 1) { -static void ads7846_rx(void *ads) + val = ads7846_get_value(ts, m); + + action = ts->filter(ts->filter_data, msg_idx, &val); + switch (action) { + case ADS7846_FILTER_REPEAT: + continue; + + case ADS7846_FILTER_IGNORE: + packet->tc.ignore = true; + msg_idx = ts->msg_count - 1; + continue; + + case ADS7846_FILTER_OK: + ads7846_update_value(m, val); + packet->tc.ignore = false; + msg_idx++; + break; + + default: + BUG(); + } + } else { + msg_idx++; + } + } +} + +static void ads7846_report_state(struct ads7846 *ts) { - struct ads7846 *ts = ads; - struct ads7846_packet *packet = ts->packet; - unsigned Rt; - u16 x, y, z1, z2; + struct ads7846_packet *packet = ts->packet; + unsigned int Rt; + u16 x, y, z1, z2; - /* ads7846_rx_val() did in-place conversion (including byteswap) from - * on-the-wire format as part of debouncing to get stable readings. + /* + * ads7846_get_value() does in-place conversion (including byte swap) + * from on-the-wire format as part of debouncing to get stable + * readings. */ if (ts->model == 7845) { x = *(u16 *)packet->tc.x_buf; @@ -623,19 +800,19 @@ static void ads7846_rx(void *ads) Rt = 0; } - /* Sample found inconsistent by debouncing or pressure is beyond + /* + * Sample found inconsistent by debouncing or pressure is beyond * the maximum. Don't report it to user space, repeat at least * once more the measurement */ if (packet->tc.ignore || Rt > ts->pressure_max) { dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", packet->tc.ignore, Rt); - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); return; } - /* Maybe check the pendown state before reporting. This discards + /* + * Maybe check the pendown state before reporting. This discards * false readings when the pen is lifted. */ if (ts->penirq_recheck_delay_usecs) { @@ -644,8 +821,9 @@ static void ads7846_rx(void *ads) Rt = 0; } - /* NOTE: We can't rely on the pressure to determine the pen down - * state, even this controller has a pressure sensor. The pressure + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure * value can fluctuate for quite a while after lifting the pen and * in some cases may not even settle at the expected value. * @@ -655,15 +833,15 @@ static void ads7846_rx(void *ads) if (Rt) { struct input_dev *input = ts->input; + if (ts->swap_xy) + swap(x, y); + if (!ts->pendown) { input_report_key(input, BTN_TOUCH, 1); - ts->pendown = 1; + ts->pendown = true; dev_vdbg(&ts->spi->dev, "DOWN\n"); } - if (ts->swap_xy) - swap(x, y); - input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); @@ -671,246 +849,94 @@ static void ads7846_rx(void *ads) input_sync(input); dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); } - - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); -} - -static int ads7846_debounce(void *ads, int data_idx, int *val) -{ - struct ads7846 *ts = ads; - - if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { - /* Start over collecting consistent readings. */ - ts->read_rep = 0; - /* Repeat it, if this was the first read or the read - * wasn't consistent enough. */ - if (ts->read_cnt < ts->debounce_max) { - ts->last_read = *val; - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } else { - /* Maximum number of debouncing reached and still - * not enough number of consistent readings. Abort - * the whole sample, repeat it in the next sampling - * period. - */ - ts->read_cnt = 0; - return ADS7846_FILTER_IGNORE; - } - } else { - if (++ts->read_rep > ts->debounce_rep) { - /* Got a good reading for this coordinate, - * go for the next one. */ - ts->read_cnt = 0; - ts->read_rep = 0; - return ADS7846_FILTER_OK; - } else { - /* Read more values that are consistent. */ - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } - } } -static int ads7846_no_filter(void *ads, int data_idx, int *val) +static irqreturn_t ads7846_hard_irq(int irq, void *handle) { - return ADS7846_FILTER_OK; -} - -static void ads7846_rx_val(void *ads) -{ - struct ads7846 *ts = ads; - struct ads7846_packet *packet = ts->packet; - struct spi_message *m; - struct spi_transfer *t; - int val; - int action; - int status; - - m = &ts->msg[ts->msg_idx]; - t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - - if (ts->model == 7845) { - val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; - } else { - /* adjust: on-wire is a must-ignore bit, a BE12 value, then - * padding; built from two 8 bit values written msb-first. - */ - val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; - } + struct ads7846 *ts = handle; - action = ts->filter(ts->filter_data, ts->msg_idx, &val); - switch (action) { - case ADS7846_FILTER_REPEAT: - break; - case ADS7846_FILTER_IGNORE: - packet->tc.ignore = 1; - /* Last message will contain ads7846_rx() as the - * completion function. - */ - m = ts->last_msg; - break; - case ADS7846_FILTER_OK: - *(u16 *)t->rx_buf = val; - packet->tc.ignore = 0; - m = &ts->msg[++ts->msg_idx]; - break; - default: - BUG(); - } - ts->wait_for_sync(); - status = spi_async(ts->spi, m); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", - status); + return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED; } -static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) -{ - struct ads7846 *ts = container_of(handle, struct ads7846, timer); - int status = 0; - - spin_lock(&ts->lock); - - if (unlikely(!get_pendown_state(ts) || - device_suspended(&ts->spi->dev))) { - if (ts->pendown) { - struct input_dev *input = ts->input; - - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_sync(input); - - ts->pendown = 0; - dev_vdbg(&ts->spi->dev, "UP\n"); - } - - /* measurement cycle ended */ - if (!device_suspended(&ts->spi->dev)) { - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); - } - ts->pending = 0; - } else { - /* pen is still down, continue with the measurement */ - ts->msg_idx = 0; - ts->wait_for_sync(); - status = spi_async(ts->spi, &ts->msg[0]); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", status); - } - - spin_unlock(&ts->lock); - return HRTIMER_NORESTART; -} static irqreturn_t ads7846_irq(int irq, void *handle) { struct ads7846 *ts = handle; - unsigned long flags; - - spin_lock_irqsave(&ts->lock, flags); - if (likely(get_pendown_state(ts))) { - if (!ts->irq_disabled) { - /* The ARM do_simple_IRQ() dispatcher doesn't act - * like the other dispatchers: it will report IRQs - * even after they've been disabled. We work around - * that here. (The "generic irq" framework may help...) - */ - ts->irq_disabled = 1; - disable_irq_nosync(ts->spi->irq); - ts->pending = 1; - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), - HRTIMER_MODE_REL); - } - } - spin_unlock_irqrestore(&ts->lock, flags); - return IRQ_HANDLED; -} + /* Start with a small delay before checking pendown state */ + msleep(TS_POLL_DELAY); -/*--------------------------------------------------------------------------*/ + while (!ts->stopped && get_pendown_state(ts)) { -/* Must be called with ts->lock held */ -static void ads7846_disable(struct ads7846 *ts) -{ - if (ts->disabled) - return; + /* pen is down, continue with the measurement */ + ads7846_read_state(ts); - ts->disabled = 1; + if (!ts->stopped) + ads7846_report_state(ts); - /* are we waiting for IRQ, or polling? */ - if (!ts->pending) { - ts->irq_disabled = 1; - disable_irq(ts->spi->irq); - } else { - /* the timer will run at least once more, and - * leave everything in a clean state, IRQ disabled - */ - while (ts->pending) { - spin_unlock_irq(&ts->lock); - msleep(1); - spin_lock_irq(&ts->lock); - } + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(TS_POLL_PERIOD)); } - regulator_disable(ts->reg); - - /* we know the chip's in lowpower mode since we always - * leave it that way after every request - */ -} + if (ts->pendown) { + struct input_dev *input = ts->input; -/* Must be called with ts->lock held */ -static void ads7846_enable(struct ads7846 *ts) -{ - if (!ts->disabled) - return; + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); - regulator_enable(ts->reg); + ts->pendown = false; + dev_vdbg(&ts->spi->dev, "UP\n"); + } - ts->disabled = 0; - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); + return IRQ_HANDLED; } static int ads7846_suspend(struct spi_device *spi, pm_message_t message) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - spin_lock_irq(&ts->lock); + mutex_lock(&ts->lock); - ts->is_suspended = 1; - ads7846_disable(ts); + if (!ts->suspended) { - spin_unlock_irq(&ts->lock); + if (!ts->disabled) + __ads7846_disable(ts); - if (device_may_wakeup(&ts->spi->dev)) - enable_irq_wake(ts->spi->irq); + if (device_may_wakeup(&ts->spi->dev)) + enable_irq_wake(ts->spi->irq); - return 0; + ts->suspended = true; + } + + mutex_unlock(&ts->lock); + return 0; } static int ads7846_resume(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - if (device_may_wakeup(&ts->spi->dev)) - disable_irq_wake(ts->spi->irq); + mutex_lock(&ts->lock); + + if (ts->suspended) { - spin_lock_irq(&ts->lock); + ts->suspended = false; - ts->is_suspended = 0; - ads7846_enable(ts); + if (device_may_wakeup(&ts->spi->dev)) + disable_irq_wake(ts->spi->irq); - spin_unlock_irq(&ts->lock); + if (!ts->disabled) + __ads7846_enable(ts); + } + + mutex_unlock(&ts->lock); return 0; } -static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) +static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) { struct ads7846_platform_data *pdata = spi->dev.platform_data; int err; @@ -932,146 +958,40 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); if (err) { dev_err(&spi->dev, "failed to request pendown GPIO%d\n", - pdata->gpio_pendown); + pdata->gpio_pendown); return err; } ts->gpio_pendown = pdata->gpio_pendown; + return 0; } -static int __devinit ads7846_probe(struct spi_device *spi) +/* + * Set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ +static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, + const struct ads7846_platform_data *pdata) { - struct ads7846 *ts; - struct ads7846_packet *packet; - struct input_dev *input_dev; - const struct ads7846_platform_data *pdata = spi->dev.platform_data; - struct spi_message *m; - struct spi_transfer *x; - unsigned long irq_flags; - int vref; - int err; - - if (!spi->irq) { - dev_dbg(&spi->dev, "no IRQ?\n"); - return -ENODEV; - } - - if (!pdata) { - dev_dbg(&spi->dev, "no platform data?\n"); - return -ENODEV; - } - - /* don't exceed max specified sample rate */ - if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { - dev_dbg(&spi->dev, "f(sample) %d KHz?\n", - (spi->max_speed_hz/SAMPLE_BITS)/1000); - return -EINVAL; - } - - /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except - * that even if the hardware can do that, the SPI controller driver - * may not. So we stick to very-portable 8 bit words, both RX and TX. - */ - spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; - err = spi_setup(spi); - if (err < 0) - return err; - - ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); - packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !packet || !input_dev) { - err = -ENOMEM; - goto err_free_mem; - } - - dev_set_drvdata(&spi->dev, ts); - - ts->packet = packet; - ts->spi = spi; - ts->input = input_dev; - ts->vref_mv = pdata->vref_mv; - ts->swap_xy = pdata->swap_xy; - - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - ts->timer.function = ads7846_timer; - - spin_lock_init(&ts->lock); - - ts->model = pdata->model ? : 7846; - ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - - if (pdata->filter != NULL) { - if (pdata->filter_init != NULL) { - err = pdata->filter_init(pdata, &ts->filter_data); - if (err < 0) - goto err_free_mem; - } - ts->filter = pdata->filter; - ts->filter_cleanup = pdata->filter_cleanup; - } else if (pdata->debounce_max) { - ts->debounce_max = pdata->debounce_max; - if (ts->debounce_max < 2) - ts->debounce_max = 2; - ts->debounce_tol = pdata->debounce_tol; - ts->debounce_rep = pdata->debounce_rep; - ts->filter = ads7846_debounce; - ts->filter_data = ts; - } else - ts->filter = ads7846_no_filter; - - err = setup_pendown(spi, ts); - if (err) - goto err_cleanup_filter; - - if (pdata->penirq_recheck_delay_usecs) - ts->penirq_recheck_delay_usecs = - pdata->penirq_recheck_delay_usecs; - - ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; - - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); - snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); - - input_dev->name = ts->name; - input_dev->phys = ts->phys; - input_dev->dev.parent = &spi->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(input_dev, ABS_X, - pdata->x_min ? : 0, - pdata->x_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_Y, - pdata->y_min ? : 0, - pdata->y_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, pdata->pressure_max, 0, 0); - - vref = pdata->keep_vref_on; + struct spi_message *m = &ts->msg[0]; + struct spi_transfer *x = ts->xfer; + struct ads7846_packet *packet = ts->packet; + int vref = pdata->keep_vref_on; if (ts->model == 7873) { - /* The AD7873 is almost identical to the ADS7846 + /* + * The AD7873 is almost identical to the ADS7846 * keep VREF off during differential/ratiometric - * conversion modes + * conversion modes. */ ts->model = 7846; vref = 0; } - /* set up the transfers to read touchscreen state; this assumes we - * use formula #2 for pressure, not #3. - */ - m = &ts->msg[0]; - x = ts->xfer; - + ts->msg_count = 1; spi_message_init(m); + m->context = ts; if (ts->model == 7845) { packet->read_y_cmd[0] = READ_Y(vref); @@ -1094,7 +1014,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - /* the first sample after switching drivers can be low quality; + /* + * The first sample after switching drivers can be low quality; * optionally discard it, using a second one after the signals * have had enough time to stabilize. */ @@ -1112,11 +1033,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - m->complete = ads7846_rx_val; - m->context = ts; - + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; if (ts->model == 7845) { x++; @@ -1156,13 +1076,12 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - m->complete = ads7846_rx_val; - m->context = ts; - /* turn y+ off, x- on; we'll use formula #2 */ if (ts->model == 7846) { + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; x++; packet->read_z1 = READ_Z1(vref); @@ -1190,11 +1109,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - m->complete = ads7846_rx_val; - m->context = ts; - + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; x++; packet->read_z2 = READ_Z2(vref); @@ -1221,14 +1139,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); } - - m->complete = ads7846_rx_val; - m->context = ts; } /* power down */ + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; if (ts->model == 7845) { x++; @@ -1251,11 +1168,119 @@ static int __devinit ads7846_probe(struct spi_device *spi) CS_CHANGE(*x); spi_message_add_tail(x, m); +} - m->complete = ads7846_rx; - m->context = ts; +static int __devinit ads7846_probe(struct spi_device *spi) +{ + struct ads7846 *ts; + struct ads7846_packet *packet; + struct input_dev *input_dev; + struct ads7846_platform_data *pdata = spi->dev.platform_data; + unsigned long irq_flags; + int err; + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified sample rate */ + if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { + dev_dbg(&spi->dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/SAMPLE_BITS)/1000); + return -EINVAL; + } + + /* We'd set TX word size 8 bits and RX word size to 13 bits ... except + * that even if the hardware can do that, the SPI controller driver + * may not. So we stick to very-portable 8 bit words, both RX and TX. + */ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; - ts->last_msg = m; + ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); + packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !packet || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + + ts->packet = packet; + ts->spi = spi; + ts->input = input_dev; + ts->vref_mv = pdata->vref_mv; + ts->swap_xy = pdata->swap_xy; + + mutex_init(&ts->lock); + init_waitqueue_head(&ts->wait); + + ts->model = pdata->model ? : 7846; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < 2) + ts->debounce_max = 2; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + ts->filter = ads7846_debounce_filter; + ts->filter_data = ts; + } else { + ts->filter = ads7846_no_filter; + } + + err = ads7846_setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; + + if (pdata->penirq_recheck_delay_usecs) + ts->penirq_recheck_delay_usecs = + pdata->penirq_recheck_delay_usecs; + + ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); + + input_dev->name = ts->name; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + ads7846_setup_spi_msg(ts, pdata); ts->reg = regulator_get(&spi->dev, "vcc"); if (IS_ERR(ts->reg)) { @@ -1271,16 +1296,17 @@ static int __devinit ads7846_probe(struct spi_device *spi) } irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; - err = request_irq(spi->irq, ads7846_irq, irq_flags, - spi->dev.driver->name, ts); - + err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq, + irq_flags, spi->dev.driver->name, ts); if (err && !pdata->irq_flags) { dev_info(&spi->dev, "trying pin change workaround on irq %d\n", spi->irq); - err = request_irq(spi->irq, ads7846_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - spi->dev.driver->name, ts); + irq_flags |= IRQF_TRIGGER_RISING; + err = request_threaded_irq(spi->irq, + ads7846_hard_irq, ads7846_irq, + irq_flags, spi->dev.driver->name, ts); } if (err) { @@ -1294,7 +1320,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); - /* take a first sample, leaving nPENIRQ active and vREF off; avoid + /* + * Take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ if (ts->model == 7845) @@ -1340,20 +1367,18 @@ static int __devinit ads7846_probe(struct spi_device *spi) static int __devexit ads7846_remove(struct spi_device *spi) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); + struct ads7846 *ts = dev_get_drvdata(&spi->dev); device_init_wakeup(&spi->dev, false); - ads784x_hwmon_unregister(spi, ts); - input_unregister_device(ts->input); - - ads7846_suspend(spi, PMSG_SUSPEND); - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + ads7846_disable(ts); free_irq(ts->spi->irq, ts); - /* suspend left the IRQ disabled */ - enable_irq(ts->spi->irq); + + input_unregister_device(ts->input); + + ads784x_hwmon_unregister(spi, ts); regulator_disable(ts->reg); regulator_put(ts->reg); @@ -1368,6 +1393,7 @@ static int __devexit ads7846_remove(struct spi_device *spi) kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); + return 0; } diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c new file mode 100644 index 00000000000..ccde5860256 --- /dev/null +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -0,0 +1,648 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson + * License terms:GNU General Public License (GPL) version 2 + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> +#include <linux/input.h> +#include <linux/input/bu21013.h> +#include <linux/slab.h> + +#define PEN_DOWN_INTR 0 +#define MAX_FINGERS 2 +#define RESET_DELAY 30 +#define PENUP_TIMEOUT (10) +#define DELTA_MIN 16 +#define MASK_BITS 0x03 +#define SHIFT_8 8 +#define SHIFT_2 2 +#define LENGTH_OF_BUFFER 11 +#define I2C_RETRY_COUNT 5 + +#define BU21013_SENSORS_BTN_0_7_REG 0x70 +#define BU21013_SENSORS_BTN_8_15_REG 0x71 +#define BU21013_SENSORS_BTN_16_23_REG 0x72 +#define BU21013_X1_POS_MSB_REG 0x73 +#define BU21013_X1_POS_LSB_REG 0x74 +#define BU21013_Y1_POS_MSB_REG 0x75 +#define BU21013_Y1_POS_LSB_REG 0x76 +#define BU21013_X2_POS_MSB_REG 0x77 +#define BU21013_X2_POS_LSB_REG 0x78 +#define BU21013_Y2_POS_MSB_REG 0x79 +#define BU21013_Y2_POS_LSB_REG 0x7A +#define BU21013_INT_CLR_REG 0xE8 +#define BU21013_INT_MODE_REG 0xE9 +#define BU21013_GAIN_REG 0xEA +#define BU21013_OFFSET_MODE_REG 0xEB +#define BU21013_XY_EDGE_REG 0xEC +#define BU21013_RESET_REG 0xED +#define BU21013_CALIB_REG 0xEE +#define BU21013_DONE_REG 0xEF +#define BU21013_SENSOR_0_7_REG 0xF0 +#define BU21013_SENSOR_8_15_REG 0xF1 +#define BU21013_SENSOR_16_23_REG 0xF2 +#define BU21013_POS_MODE1_REG 0xF3 +#define BU21013_POS_MODE2_REG 0xF4 +#define BU21013_CLK_MODE_REG 0xF5 +#define BU21013_IDLE_REG 0xFA +#define BU21013_FILTER_REG 0xFB +#define BU21013_TH_ON_REG 0xFC +#define BU21013_TH_OFF_REG 0xFD + + +#define BU21013_RESET_ENABLE 0x01 + +#define BU21013_SENSORS_EN_0_7 0x3F +#define BU21013_SENSORS_EN_8_15 0xFC +#define BU21013_SENSORS_EN_16_23 0x1F + +#define BU21013_POS_MODE1_0 0x02 +#define BU21013_POS_MODE1_1 0x04 +#define BU21013_POS_MODE1_2 0x08 + +#define BU21013_POS_MODE2_ZERO 0x01 +#define BU21013_POS_MODE2_AVG1 0x02 +#define BU21013_POS_MODE2_AVG2 0x04 +#define BU21013_POS_MODE2_EN_XY 0x08 +#define BU21013_POS_MODE2_EN_RAW 0x10 +#define BU21013_POS_MODE2_MULTI 0x80 + +#define BU21013_CLK_MODE_DIV 0x01 +#define BU21013_CLK_MODE_EXT 0x02 +#define BU21013_CLK_MODE_CALIB 0x80 + +#define BU21013_IDLET_0 0x01 +#define BU21013_IDLET_1 0x02 +#define BU21013_IDLET_2 0x04 +#define BU21013_IDLET_3 0x08 +#define BU21013_IDLE_INTERMIT_EN 0x10 + +#define BU21013_DELTA_0_6 0x7F +#define BU21013_FILTER_EN 0x80 + +#define BU21013_INT_MODE_LEVEL 0x00 +#define BU21013_INT_MODE_EDGE 0x01 + +#define BU21013_GAIN_0 0x01 +#define BU21013_GAIN_1 0x02 +#define BU21013_GAIN_2 0x04 + +#define BU21013_OFFSET_MODE_DEFAULT 0x00 +#define BU21013_OFFSET_MODE_MOVE 0x01 +#define BU21013_OFFSET_MODE_DISABLE 0x02 + +#define BU21013_TH_ON_0 0x01 +#define BU21013_TH_ON_1 0x02 +#define BU21013_TH_ON_2 0x04 +#define BU21013_TH_ON_3 0x08 +#define BU21013_TH_ON_4 0x10 +#define BU21013_TH_ON_5 0x20 +#define BU21013_TH_ON_6 0x40 +#define BU21013_TH_ON_7 0x80 +#define BU21013_TH_ON_MAX 0xFF + +#define BU21013_TH_OFF_0 0x01 +#define BU21013_TH_OFF_1 0x02 +#define BU21013_TH_OFF_2 0x04 +#define BU21013_TH_OFF_3 0x08 +#define BU21013_TH_OFF_4 0x10 +#define BU21013_TH_OFF_5 0x20 +#define BU21013_TH_OFF_6 0x40 +#define BU21013_TH_OFF_7 0x80 +#define BU21013_TH_OFF_MAX 0xFF + +#define BU21013_X_EDGE_0 0x01 +#define BU21013_X_EDGE_1 0x02 +#define BU21013_X_EDGE_2 0x04 +#define BU21013_X_EDGE_3 0x08 +#define BU21013_Y_EDGE_0 0x10 +#define BU21013_Y_EDGE_1 0x20 +#define BU21013_Y_EDGE_2 0x40 +#define BU21013_Y_EDGE_3 0x80 + +#define BU21013_DONE 0x01 +#define BU21013_NUMBER_OF_X_SENSORS (6) +#define BU21013_NUMBER_OF_Y_SENSORS (11) + +#define DRIVER_TP "bu21013_tp" + +/** + * struct bu21013_ts_data - touch panel data structure + * @client: pointer to the i2c client + * @wait: variable to wait_queue_head_t structure + * @touch_stopped: touch stop flag + * @chip: pointer to the touch panel controller + * @in_dev: pointer to the input device structure + * @intr_pin: interrupt pin value + * + * Touch panel device data structure + */ +struct bu21013_ts_data { + struct i2c_client *client; + wait_queue_head_t wait; + bool touch_stopped; + const struct bu21013_platform_device *chip; + struct input_dev *in_dev; + unsigned int intr_pin; +}; + +/** + * bu21013_read_block_data(): read the touch co-ordinates + * @data: bu21013_ts_data structure pointer + * @buf: byte pointer + * + * Read the touch co-ordinates using i2c read block into buffer + * and returns integer. + */ +static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf) +{ + int ret, i; + + for (i = 0; i < I2C_RETRY_COUNT; i++) { + ret = i2c_smbus_read_i2c_block_data + (data->client, BU21013_SENSORS_BTN_0_7_REG, + LENGTH_OF_BUFFER, buf); + if (ret == LENGTH_OF_BUFFER) + return 0; + } + return -EINVAL; +} + +/** + * bu21013_do_touch_report(): Get the touch co-ordinates + * @data: bu21013_ts_data structure pointer + * + * Get the touch co-ordinates from touch sensor registers and writes + * into device structure and returns integer. + */ +static int bu21013_do_touch_report(struct bu21013_ts_data *data) +{ + u8 buf[LENGTH_OF_BUFFER]; + unsigned int pos_x[2], pos_y[2]; + bool has_x_sensors, has_y_sensors; + int finger_down_count = 0; + int i; + + if (data == NULL) + return -EINVAL; + + if (bu21013_read_block_data(data, buf) < 0) + return -EINVAL; + + has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7); + has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) | + ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2); + if (!has_x_sensors || !has_y_sensors) + return 0; + + for (i = 0; i < MAX_FINGERS; i++) { + const u8 *p = &buf[4 * i + 3]; + unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS); + unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS); + if (x == 0 || y == 0) + continue; + pos_x[finger_down_count] = x; + pos_y[finger_down_count] = y; + finger_down_count++; + } + + if (finger_down_count) { + if (finger_down_count == 2 && + (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || + abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) { + return 0; + } + + for (i = 0; i < finger_down_count; i++) { + if (data->chip->x_flip) + pos_x[i] = data->chip->touch_x_max - pos_x[i]; + if (data->chip->y_flip) + pos_y[i] = data->chip->touch_y_max - pos_y[i]; + + input_report_abs(data->in_dev, + ABS_MT_POSITION_X, pos_x[i]); + input_report_abs(data->in_dev, + ABS_MT_POSITION_Y, pos_y[i]); + input_mt_sync(data->in_dev); + } + } else + input_mt_sync(data->in_dev); + + input_sync(data->in_dev); + + return 0; +} +/** + * bu21013_gpio_irq() - gpio thread function for touch interrupt + * @irq: irq value + * @device_data: void pointer + * + * This gpio thread function for touch interrupt + * and returns irqreturn_t. + */ +static irqreturn_t bu21013_gpio_irq(int irq, void *device_data) +{ + struct bu21013_ts_data *data = device_data; + struct i2c_client *i2c = data->client; + int retval; + + do { + retval = bu21013_do_touch_report(data); + if (retval < 0) { + dev_err(&i2c->dev, "bu21013_do_touch_report failed\n"); + return IRQ_NONE; + } + + data->intr_pin = data->chip->irq_read_val(); + if (data->intr_pin == PEN_DOWN_INTR) + wait_event_timeout(data->wait, data->touch_stopped, + msecs_to_jiffies(2)); + } while (!data->intr_pin && !data->touch_stopped); + + return IRQ_HANDLED; +} + +/** + * bu21013_init_chip() - power on sequence for the bu21013 controller + * @data: device structure pointer + * + * This function is used to power on + * the bu21013 controller and returns integer. + */ +static int bu21013_init_chip(struct bu21013_ts_data *data) +{ + int retval; + struct i2c_client *i2c = data->client; + + retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG, + BU21013_RESET_ENABLE); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_RESET reg write failed\n"); + return retval; + } + msleep(RESET_DELAY); + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG, + BU21013_SENSORS_EN_0_7); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG, + BU21013_SENSORS_EN_8_15); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG, + BU21013_SENSORS_EN_16_23); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG, + (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG, + (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 | + BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW | + BU21013_POS_MODE2_MULTI)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n"); + return retval; + } + + if (data->chip->ext_clk) + retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, + (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB)); + else + retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, + (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG, + (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG, + BU21013_INT_MODE_LEVEL); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG, + (BU21013_DELTA_0_6 | + BU21013_FILTER_EN)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG, + BU21013_TH_ON_5); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, + BU21013_TH_OFF_4 || BU21013_TH_OFF_3); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG, + (BU21013_GAIN_0 | BU21013_GAIN_1)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG, + BU21013_OFFSET_MODE_DEFAULT); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG, + (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 | + BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG, + BU21013_DONE); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n"); + return retval; + } + + return 0; +} + +/** + * bu21013_free_irq() - frees IRQ registered for touchscreen + * @bu21013_data: device structure pointer + * + * This function signals interrupt thread to stop processing and + * frees interrupt. + */ +static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) +{ + bu21013_data->touch_stopped = true; + wake_up(&bu21013_data->wait); + free_irq(bu21013_data->chip->irq, bu21013_data); +} + +/** + * bu21013_probe() - initializes the i2c-client touchscreen driver + * @client: i2c client structure pointer + * @id: i2c device id pointer + * + * This function used to initializes the i2c-client touchscreen + * driver and returns integer. + */ +static int __devinit bu21013_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bu21013_ts_data *bu21013_data; + struct input_dev *in_dev; + const struct bu21013_platform_device *pdata = + client->dev.platform_data; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "i2c smbus byte data not supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "platform data not defined\n"); + return -EINVAL; + } + + bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL); + in_dev = input_allocate_device(); + if (!bu21013_data || !in_dev) { + dev_err(&client->dev, "device memory alloc failed\n"); + error = -ENOMEM; + goto err_free_mem; + } + + bu21013_data->in_dev = in_dev; + bu21013_data->chip = pdata; + bu21013_data->client = client; + bu21013_data->touch_stopped = false; + init_waitqueue_head(&bu21013_data->wait); + + /* configure the gpio pins */ + if (pdata->cs_en) { + error = pdata->cs_en(pdata->cs_pin); + if (error < 0) { + dev_err(&client->dev, "chip init failed\n"); + goto err_free_mem; + } + } + + /* configure the touch panel controller */ + error = bu21013_init_chip(bu21013_data); + if (error) { + dev_err(&client->dev, "error in bu21013 config\n"); + goto err_cs_disable; + } + + /* register the device to input subsystem */ + in_dev->name = DRIVER_TP; + in_dev->id.bustype = BUS_I2C; + in_dev->dev.parent = &client->dev; + + __set_bit(EV_SYN, in_dev->evbit); + __set_bit(EV_KEY, in_dev->evbit); + __set_bit(EV_ABS, in_dev->evbit); + + input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, + pdata->x_max_res, 0, 0); + input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, + pdata->y_max_res, 0, 0); + input_set_drvdata(in_dev, bu21013_data); + + error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, + IRQF_TRIGGER_FALLING | IRQF_SHARED, + DRIVER_TP, bu21013_data); + if (error) { + dev_err(&client->dev, "request irq %d failed\n", pdata->irq); + goto err_cs_disable; + } + + error = input_register_device(in_dev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_free_irq; + } + + device_init_wakeup(&client->dev, pdata->wakeup); + i2c_set_clientdata(client, bu21013_data); + + return 0; + +err_free_irq: + bu21013_free_irq(bu21013_data); +err_cs_disable: + pdata->cs_dis(pdata->cs_pin); +err_free_mem: + input_free_device(bu21013_data->in_dev); + kfree(bu21013_data); + + return error; +} +/** + * bu21013_remove() - removes the i2c-client touchscreen driver + * @client: i2c client structure pointer + * + * This function uses to remove the i2c-client + * touchscreen driver and returns integer. + */ +static int __devexit bu21013_remove(struct i2c_client *client) +{ + struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client); + + bu21013_free_irq(bu21013_data); + + bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); + + input_unregister_device(bu21013_data->in_dev); + kfree(bu21013_data); + + device_init_wakeup(&client->dev, false); + + return 0; +} + +#ifdef CONFIG_PM +/** + * bu21013_suspend() - suspend the touch screen controller + * @dev: pointer to device structure + * + * This function is used to suspend the + * touch panel controller and returns integer + */ +static int bu21013_suspend(struct device *dev) +{ + struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); + struct i2c_client *client = bu21013_data->client; + + bu21013_data->touch_stopped = true; + if (device_may_wakeup(&client->dev)) + enable_irq_wake(bu21013_data->chip->irq); + else + disable_irq(bu21013_data->chip->irq); + + return 0; +} + +/** + * bu21013_resume() - resume the touch screen controller + * @dev: pointer to device structure + * + * This function is used to resume the touch panel + * controller and returns integer. + */ +static int bu21013_resume(struct device *dev) +{ + struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); + struct i2c_client *client = bu21013_data->client; + int retval; + + retval = bu21013_init_chip(bu21013_data); + if (retval < 0) { + dev_err(&client->dev, "bu21013 controller config failed\n"); + return retval; + } + + bu21013_data->touch_stopped = false; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(bu21013_data->chip->irq); + else + enable_irq(bu21013_data->chip->irq); + + return 0; +} + +static const struct dev_pm_ops bu21013_dev_pm_ops = { + .suspend = bu21013_suspend, + .resume = bu21013_resume, +}; +#endif + +static const struct i2c_device_id bu21013_id[] = { + { DRIVER_TP, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bu21013_id); + +static struct i2c_driver bu21013_driver = { + .driver = { + .name = DRIVER_TP, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &bu21013_dev_pm_ops, +#endif + }, + .probe = bu21013_probe, + .remove = __devexit_p(bu21013_remove), + .id_table = bu21013_id, +}; + +/** + * bu21013_init() - initializes the bu21013 touchscreen driver + * + * This function used to initializes the bu21013 + * touchscreen driver and returns integer. + */ +static int __init bu21013_init(void) +{ + return i2c_add_driver(&bu21013_driver); +} + +/** + * bu21013_exit() - de-initializes the bu21013 touchscreen driver + * + * This function uses to de-initializes the bu21013 + * touchscreen driver and returns none. + */ +static void __exit bu21013_exit(void) +{ + i2c_del_driver(&bu21013_driver); +} + +module_init(bu21013_init); +module_exit(bu21013_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>"); +MODULE_DESCRIPTION("bu21013 touch screen controller driver"); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index 5ec0946938f..d0c3a7229ad 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client, input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, - CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); + CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0); input_set_abs_params(input_dev, ABS_Y, - CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0); if (ts->reset_pin) { err = gpio_request(ts->reset_pin, NULL); diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index a89700e7ace..498bd62af09 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -107,8 +107,7 @@ static int __init hp680_ts_init(void) return 0; fail2: free_irq(HP680_TS_IRQ, NULL); - cancel_delayed_work(&work); - flush_scheduled_work(); + cancel_delayed_work_sync(&work); fail1: input_free_device(hp680_ts_dev); return err; } @@ -116,8 +115,7 @@ static int __init hp680_ts_init(void) static void __exit hp680_ts_exit(void) { free_irq(HP680_TS_IRQ, NULL); - cancel_delayed_work(&work); - flush_scheduled_work(); + cancel_delayed_work_sync(&work); input_unregister_device(hp680_ts_dev); } diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c new file mode 100644 index 00000000000..c0307b22d86 --- /dev/null +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -0,0 +1,687 @@ +/* + * Intel MID Resistive Touch Screen Driver + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) + * Ramesh Agarwal (ramesh.agarwal@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * TODO: + * review conversion of r/m/w sequences + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/param.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <asm/intel_scu_ipc.h> + +/* PMIC Interrupt registers */ +#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */ + +/* PMIC Interrupt registers */ +#define PMIC_REG_INT 0x04 /* PMIC interrupt register */ +#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */ + +/* ADC Interrupt registers */ +#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */ +#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */ + +/* ADC Control registers */ +#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */ + +/* ADC Channel Selection registers */ +#define PMICADDR0 0xA4 +#define END_OF_CHANNEL 0x1F + +/* ADC Result register */ +#define PMIC_REG_ADCSNS0H 0x64 + +/* ADC channels for touch screen */ +#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ +#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ +#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ +#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ + +/* Touch screen channel BIAS constants */ +#define MRST_XBIAS 0x20 +#define MRST_YBIAS 0x40 +#define MRST_ZBIAS 0x80 + +/* Touch screen coordinates */ +#define MRST_X_MIN 10 +#define MRST_X_MAX 1024 +#define MRST_X_FUZZ 5 +#define MRST_Y_MIN 10 +#define MRST_Y_MAX 1024 +#define MRST_Y_FUZZ 5 +#define MRST_PRESSURE_MIN 0 +#define MRST_PRESSURE_NOMINAL 50 +#define MRST_PRESSURE_MAX 100 + +#define WAIT_ADC_COMPLETION 10 /* msec */ + +/* PMIC ADC round robin delays */ +#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ +#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ + +/* PMIC Vendor Identifiers */ +#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ +#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ +#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ +#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ + +/* Touch screen device structure */ +struct mrstouch_dev { + struct device *dev; /* device associated with touch screen */ + struct input_dev *input; + char phys[32]; + u16 asr; /* Address selection register */ + int irq; + unsigned int vendor; /* PMIC vendor */ + unsigned int rev; /* PMIC revision */ + + int (*read_prepare)(struct mrstouch_dev *tsdev); + int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z); + int (*read_finish)(struct mrstouch_dev *tsdev); +}; + + +/*************************** NEC and Maxim Interface ************************/ + +static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev) +{ + return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20); +} + +/* + * Enables PENDET interrupt. + */ +static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev) +{ + int err; + + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20); + if (!err) + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05); + + return err; +} + +/* + * Reads PMIC ADC touch screen result + * Reads ADC storage registers for higher 7 and lower 3 bits and + * converts the two readings into a single value and turns off gain bit + */ +static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) +{ + int err; + u16 result; + u32 res; + + result = PMIC_REG_ADCSNS0H + offset; + + if (chan == MRST_TS_CHAN12) + result += 4; + + err = intel_scu_ipc_ioread32(result, &res); + if (err) + return err; + + /* Mash the bits up */ + + *vp = (res & 0xFF) << 3; /* Highest 7 bits */ + *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vp &= 0x3FF; + + res >>= 16; + + *vm = (res & 0xFF) << 3; /* Highest 7 bits */ + *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vm &= 0x3FF; + + return 0; +} + +/* + * Enables X, Y and Z bias values + * Enables YPYM for X channels and XPXM for Y channels + */ +static int mrstouch_ts_bias_set(uint offset, uint bias) +{ + int count; + u16 chan, start; + u16 reg[4]; + u8 data[4]; + + chan = PMICADDR0 + offset; + start = MRST_TS_CHAN10; + + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = bias | (start + count); + } + + return intel_scu_ipc_writev(reg, data, 4); +} + +/* To read touch screen channel values */ +static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev, + u16 *x, u16 *y, u16 *z) +{ + int err; + u16 xm, ym, zm; + + /* configure Y bias for X channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read x+ and x- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm); + if (err) + goto ipc_error; + + /* configure x bias for y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read y+ and y- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym); + if (err) + goto ipc_error; + + /* configure z bias for x and y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read z+ and z- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during adc read\n"); + return err; +} + + +/*************************** Freescale Interface ************************/ + +static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev) +{ + int err, count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + /* Stop the ADC */ + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); + if (err) + goto ipc_error; + + chan = PMICADDR0 + tsdev->asr; + + /* Set X BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x2A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Y BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x4A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Z BIAS */ + err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev, + u16 *x, u16 *y, u16 *z) +{ + int err; + u16 result; + u16 reg[4]; + u8 data[4]; + + result = PMIC_REG_ADCSNS0H + tsdev->asr; + + reg[0] = result + 4; + reg[1] = result + 5; + reg[2] = result + 16; + reg[3] = result + 17; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + *x = data[0] << 3; /* Higher 7 bits */ + *x |= data[1] & 0x7; /* Lower 3 bits */ + *x &= 0x3FF; + + *y = data[2] << 3; /* Higher 7 bits */ + *y |= data[3] & 0x7; /* Lower 3 bits */ + *y &= 0x3FF; + + /* Read Z value */ + reg[0] = result + 28; + reg[1] = result + 29; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + *z = data[0] << 3; /* Higher 7 bits */ + *z |= data[1] & 0x7; /* Lower 3 bits */ + *z &= 0x3FF; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev) +{ + int err, count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + /* Clear all TS channels */ + chan = PMICADDR0 + tsdev->asr; + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); + if (err) + goto ipc_error; + + /* Start ADC */ + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static void mrstouch_report_event(struct input_dev *input, + unsigned int x, unsigned int y, unsigned int z) +{ + if (z > MRST_PRESSURE_NOMINAL) { + /* Pen touched, report button touch and coordinates */ + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } else { + input_report_key(input, BTN_TOUCH, 0); + } + + input_report_abs(input, ABS_PRESSURE, z); + input_sync(input); +} + +/* PENDET interrupt handler */ +static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id) +{ + struct mrstouch_dev *tsdev = dev_id; + u16 x, y, z; + + /* + * Should we lower thread priority? Probably not, since we are + * not spinning but sleeping... + */ + + if (tsdev->read_prepare(tsdev)) + goto out; + + do { + if (tsdev->read(tsdev, &x, &y, &z)) + break; + + mrstouch_report_event(tsdev->input, x, y, z); + } while (z > MRST_PRESSURE_NOMINAL); + + tsdev->read_finish(tsdev); + +out: + return IRQ_HANDLED; +} + +/* Utility to read PMIC ID */ +static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev) +{ + int err; + u8 r; + + err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); + if (err) + return err; + + *vendor = r & 0x7; + *rev = (r >> 3) & 0x7; + + return 0; +} + +/* + * Parse ADC channels to find end of the channel configured by other ADC user + * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels + */ +static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev) +{ + int err, i, found; + u8 r8; + + found = -1; + + for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { + if (found >= 0) + break; + + err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8); + if (err) + return err; + + if (r8 == END_OF_CHANNEL) { + found = i; + break; + } + } + if (found < 0) + return 0; + + if (tsdev->vendor == PMIC_VENDOR_FS) { + if (found && found > (MRSTOUCH_MAX_CHANNELS - 18)) + return -ENOSPC; + } else { + if (found && found > (MRSTOUCH_MAX_CHANNELS - 4)) + return -ENOSPC; + } + return found; +} + + +/* + * Writes touch screen channels to ADC address selection registers + */ +static int __devinit mrstouch_ts_chan_set(uint offset) +{ + u16 chan; + + int ret, count; + + chan = PMICADDR0 + offset; + for (count = 0; count <= 3; count++) { + ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count); + if (ret) + return ret; + } + return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL); +} + +/* Initialize ADC */ +static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev) +{ + int err, start; + u8 ra, rm; + + err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev); + if (err) { + dev_err(tsdev->dev, "Unable to read PMIC id\n"); + return err; + } + + switch (tsdev->vendor) { + case PMIC_VENDOR_NEC: + case PMIC_VENDOR_MAXIM: + tsdev->read_prepare = mrstouch_nec_adc_read_prepare; + tsdev->read = mrstouch_nec_adc_read; + tsdev->read_finish = mrstouch_nec_adc_read_finish; + break; + + case PMIC_VENDOR_FS: + tsdev->read_prepare = mrstouch_fs_adc_read_prepare; + tsdev->read = mrstouch_fs_adc_read; + tsdev->read_finish = mrstouch_fs_adc_read_finish; + break; + + default: + dev_err(tsdev->dev, + "Unsupported touchscreen: %d\n", tsdev->vendor); + return -ENXIO; + } + + start = mrstouch_chan_parse(tsdev); + if (start < 0) { + dev_err(tsdev->dev, "Unable to parse channels\n"); + return start; + } + + tsdev->asr = start; + + /* + * ADC power on, start, enable PENDET and set loop delay + * ADC loop delay is set to 4.5 ms approximately + * Loop delay more than this results in jitter in adc readings + * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET + * interrupt generation sometimes. + */ + + if (tsdev->vendor == PMIC_VENDOR_FS) { + ra = 0xE0 | ADC_LOOP_DELAY0; + rm = 0x5; + } else { + /* NEC and MAXIm not consistent with loop delay 0 */ + ra = 0xE0 | ADC_LOOP_DELAY1; + rm = 0x0; + + /* configure touch screen channels */ + err = mrstouch_ts_chan_set(tsdev->asr); + if (err) + return err; + } + + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); + if (err) + return err; + + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); + if (err) + return err; + + return 0; +} + + +/* Probe function for touch screen driver */ +static int __devinit mrstouch_probe(struct platform_device *pdev) +{ + struct mrstouch_dev *tsdev; + struct input_dev *input; + int err; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no interrupt assigned\n"); + return -EINVAL; + } + + tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); + input = input_allocate_device(); + if (!tsdev || !input) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + err = -ENOMEM; + goto err_free_mem; + } + + tsdev->dev = &pdev->dev; + tsdev->input = input; + tsdev->irq = irq; + + snprintf(tsdev->phys, sizeof(tsdev->phys), + "%s/input0", dev_name(tsdev->dev)); + + err = mrstouch_adc_init(tsdev); + if (err) { + dev_err(&pdev->dev, "ADC initialization failed\n"); + goto err_free_mem; + } + + input->name = "mrst_touchscreen"; + input->phys = tsdev->phys; + input->dev.parent = tsdev->dev; + + input->id.vendor = tsdev->vendor; + input->id.version = tsdev->rev; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(tsdev->input, ABS_X, + MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0); + input_set_abs_params(tsdev->input, ABS_Y, + MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0); + input_set_abs_params(tsdev->input, ABS_PRESSURE, + MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); + + err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq, + 0, "mrstouch", tsdev); + if (err) { + dev_err(tsdev->dev, "unable to allocate irq\n"); + goto err_free_mem; + } + + err = input_register_device(tsdev->input); + if (err) { + dev_err(tsdev->dev, "unable to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, tsdev); + return 0; + +err_free_irq: + free_irq(tsdev->irq, tsdev); +err_free_mem: + input_free_device(input); + kfree(tsdev); + return err; +} + +static int __devexit mrstouch_remove(struct platform_device *pdev) +{ + struct mrstouch_dev *tsdev = platform_get_drvdata(pdev); + + free_irq(tsdev->irq, tsdev); + input_unregister_device(tsdev->input); + kfree(tsdev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mrstouch_driver = { + .driver = { + .name = "pmic_touch", + .owner = THIS_MODULE, + }, + .probe = mrstouch_probe, + .remove = __devexit_p(mrstouch_remove), +}; + +static int __init mrstouch_init(void) +{ + return platform_driver_register(&mrstouch_driver); +} +module_init(mrstouch_init); + +static void __exit mrstouch_exit(void) +{ + platform_driver_unregister(&mrstouch_driver); +} +module_exit(mrstouch_exit); + +MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); +MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c new file mode 100644 index 00000000000..dcf803f5a1f --- /dev/null +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -0,0 +1,411 @@ +/* + * LPC32xx built-in touchscreen driver + * + * Copyright (C) 2010 NXP Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> + +/* + * Touchscreen controller register offsets + */ +#define LPC32XX_TSC_STAT 0x00 +#define LPC32XX_TSC_SEL 0x04 +#define LPC32XX_TSC_CON 0x08 +#define LPC32XX_TSC_FIFO 0x0C +#define LPC32XX_TSC_DTR 0x10 +#define LPC32XX_TSC_RTR 0x14 +#define LPC32XX_TSC_UTR 0x18 +#define LPC32XX_TSC_TTR 0x1C +#define LPC32XX_TSC_DXP 0x20 +#define LPC32XX_TSC_MIN_X 0x24 +#define LPC32XX_TSC_MAX_X 0x28 +#define LPC32XX_TSC_MIN_Y 0x2C +#define LPC32XX_TSC_MAX_Y 0x30 +#define LPC32XX_TSC_AUX_UTR 0x34 +#define LPC32XX_TSC_AUX_MIN 0x38 +#define LPC32XX_TSC_AUX_MAX 0x3C + +#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) +#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) + +#define LPC32XX_TSC_SEL_DEFVAL 0x0284 + +#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11) +#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7) +#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4) +#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) +#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) + +#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) +#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16) +#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF) + +#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF + +#define LPC32XX_TSC_MIN_XY_VAL 0x0 +#define LPC32XX_TSC_MAX_XY_VAL 0x3FF + +#define MOD_NAME "ts-lpc32xx" + +#define tsc_readl(dev, reg) \ + __raw_readl((dev)->tsc_base + (reg)) +#define tsc_writel(dev, reg, val) \ + __raw_writel((val), (dev)->tsc_base + (reg)) + +struct lpc32xx_tsc { + struct input_dev *dev; + void __iomem *tsc_base; + int irq; + struct clk *clk; +}; + +static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc) +{ + while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) & + LPC32XX_TSC_STAT_FIFO_EMPTY)) + tsc_readl(tsc, LPC32XX_TSC_FIFO); +} + +static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id) +{ + u32 tmp, rv[4], xs[4], ys[4]; + int idx; + struct lpc32xx_tsc *tsc = dev_id; + struct input_dev *input = tsc->dev; + + tmp = tsc_readl(tsc, LPC32XX_TSC_STAT); + + if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) { + /* FIFO overflow - throw away samples */ + lpc32xx_fifo_clear(tsc); + return IRQ_HANDLED; + } + + /* + * Gather and normalize 4 samples. Pen-up events may have less + * than 4 samples, but its ok to pop 4 and let the last sample + * pen status check drop the samples. + */ + idx = 0; + while (idx < 4 && + !(tsc_readl(tsc, LPC32XX_TSC_STAT) & + LPC32XX_TSC_STAT_FIFO_EMPTY)) { + tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO); + xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - + LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp); + ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - + LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp); + rv[idx] = tmp; + idx++; + } + + /* Data is only valid if pen is still down in last sample */ + if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) { + /* Use average of 2nd and 3rd sample for position */ + input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2); + input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2); + input_report_key(input, BTN_TOUCH, 1); + } else { + input_report_key(input, BTN_TOUCH, 0); + } + + input_sync(input); + + return IRQ_HANDLED; +} + +static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc) +{ + /* Disable auto mode */ + tsc_writel(tsc, LPC32XX_TSC_CON, + tsc_readl(tsc, LPC32XX_TSC_CON) & + ~LPC32XX_TSC_ADCCON_AUTO_EN); + + clk_disable(tsc->clk); +} + +static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc) +{ + u32 tmp; + + clk_enable(tsc->clk); + + tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP; + + /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */ + tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 | + LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) | + LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10); + tsc_writel(tsc, LPC32XX_TSC_CON, tmp); + + /* These values are all preset */ + tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL); + tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL); + + /* Aux support is not used */ + tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0); + tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0); + tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0); + + /* + * Set sample rate to about 240Hz per X/Y pair. A single measurement + * consists of 4 pairs which gives about a 60Hz sample rate based on + * a stable 32768Hz clock source. Values are in clocks. + * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4 + */ + tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2); + tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2); + tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10); + tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4); + tsc_writel(tsc, LPC32XX_TSC_UTR, 88); + + lpc32xx_fifo_clear(tsc); + + /* Enable automatic ts event capture */ + tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); +} + +static int lpc32xx_ts_open(struct input_dev *dev) +{ + struct lpc32xx_tsc *tsc = input_get_drvdata(dev); + + lpc32xx_setup_tsc(tsc); + + return 0; +} + +static void lpc32xx_ts_close(struct input_dev *dev) +{ + struct lpc32xx_tsc *tsc = input_get_drvdata(dev); + + lpc32xx_stop_tsc(tsc); +} + +static int __devinit lpc32xx_ts_probe(struct platform_device *pdev) +{ + struct lpc32xx_tsc *tsc; + struct input_dev *input; + struct resource *res; + resource_size_t size; + int irq; + int error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Can't get memory resource\n"); + return -ENOENT; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Can't get interrupt resource\n"); + return irq; + } + + tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); + input = input_allocate_device(); + if (!tsc || !input) { + dev_err(&pdev->dev, "failed allocating memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + tsc->dev = input; + tsc->irq = irq; + + size = resource_size(res); + + if (!request_mem_region(res->start, size, pdev->name)) { + dev_err(&pdev->dev, "TSC registers are not free\n"); + error = -EBUSY; + goto err_free_mem; + } + + tsc->tsc_base = ioremap(res->start, size); + if (!tsc->tsc_base) { + dev_err(&pdev->dev, "Can't map memory\n"); + error = -ENOMEM; + goto err_release_mem; + } + + tsc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(tsc->clk)) { + dev_err(&pdev->dev, "failed getting clock\n"); + error = PTR_ERR(tsc->clk); + goto err_unmap; + } + + input->name = MOD_NAME; + input->phys = "lpc32xx/input0"; + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0002; + input->id.version = 0x0100; + input->dev.parent = &pdev->dev; + input->open = lpc32xx_ts_open; + input->close = lpc32xx_ts_close; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, + LPC32XX_TSC_MAX_XY_VAL, 0, 0); + input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, + LPC32XX_TSC_MAX_XY_VAL, 0, 0); + + input_set_drvdata(input, tsc); + + error = request_irq(tsc->irq, lpc32xx_ts_interrupt, + IRQF_DISABLED, pdev->name, tsc); + if (error) { + dev_err(&pdev->dev, "failed requesting interrupt\n"); + goto err_put_clock; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "failed registering input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, tsc); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +err_free_irq: + free_irq(tsc->irq, tsc); +err_put_clock: + clk_put(tsc->clk); +err_unmap: + iounmap(tsc->tsc_base); +err_release_mem: + release_mem_region(res->start, size); +err_free_mem: + input_free_device(input); + kfree(tsc); + + return error; +} + +static int __devexit lpc32xx_ts_remove(struct platform_device *pdev) +{ + struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); + struct resource *res; + + device_init_wakeup(&pdev->dev, 0); + free_irq(tsc->irq, tsc); + + input_unregister_device(tsc->dev); + + clk_put(tsc->clk); + + iounmap(tsc->tsc_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(tsc); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_ts_suspend(struct device *dev) +{ + struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); + struct input_dev *input = tsc->dev; + + /* + * Suspend and resume can be called when the device hasn't been + * enabled. If there are no users that have the device open, then + * avoid calling the TSC stop and start functions as the TSC + * isn't yet clocked. + */ + mutex_lock(&input->mutex); + + if (input->users) { + if (device_may_wakeup(dev)) + enable_irq_wake(tsc->irq); + else + lpc32xx_stop_tsc(tsc); + } + + mutex_unlock(&input->mutex); + + return 0; +} + +static int lpc32xx_ts_resume(struct device *dev) +{ + struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); + struct input_dev *input = tsc->dev; + + mutex_lock(&input->mutex); + + if (input->users) { + if (device_may_wakeup(dev)) + disable_irq_wake(tsc->irq); + else + lpc32xx_setup_tsc(tsc); + } + + mutex_unlock(&input->mutex); + + return 0; +} + +static const struct dev_pm_ops lpc32xx_ts_pm_ops = { + .suspend = lpc32xx_ts_suspend, + .resume = lpc32xx_ts_resume, +}; +#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops) +#else +#define LPC32XX_TS_PM_OPS NULL +#endif + +static struct platform_driver lpc32xx_ts_driver = { + .probe = lpc32xx_ts_probe, + .remove = __devexit_p(lpc32xx_ts_remove), + .driver = { + .name = MOD_NAME, + .owner = THIS_MODULE, + .pm = LPC32XX_TS_PM_OPS, + }, +}; + +static int __init lpc32xx_ts_init(void) +{ + return platform_driver_register(&lpc32xx_ts_driver); +} +module_init(lpc32xx_ts_init); + +static void __exit lpc32xx_ts_exit(void) +{ + platform_driver_unregister(&lpc32xx_ts_driver); +} +module_exit(lpc32xx_ts_exit); + +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com"); +MODULE_DESCRIPTION("LPC32XX TSC Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lpc32xx_ts"); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 6085d12fd56..8feb7f3c8be 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -350,7 +350,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev) err_tcirq: free_irq(ts.irq_tc, ts.input); err_inputdev: - input_unregister_device(ts.input); + input_free_device(ts.input); err_iomap: iounmap(ts.io); err_clk: diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 656148ec002..ae88e13c99f 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -268,7 +268,7 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev) struct stmpe_touch *ts; struct input_dev *idev; struct stmpe_ts_platform_data *ts_pdata = NULL; - int ret = 0; + int ret; int ts_irq; ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); @@ -276,12 +276,16 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev) return ts_irq; ts = kzalloc(sizeof(*ts), GFP_KERNEL); - if (!ts) + if (!ts) { + ret = -ENOMEM; goto err_out; + } idev = input_allocate_device(); - if (!idev) + if (!idev) { + ret = -ENOMEM; goto err_free_ts; + } platform_set_drvdata(pdev, ts); ts->stmpe = stmpe; @@ -361,7 +365,6 @@ static int __devexit stmpe_ts_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); input_unregister_device(ts->idev); - input_free_device(ts->idev); kfree(ts); diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c new file mode 100644 index 00000000000..cf1dba2e267 --- /dev/null +++ b/drivers/input/touchscreen/tnetv107x-ts.c @@ -0,0 +1,396 @@ +/* + * Texas Instruments TNETV107X Touchscreen Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <mach/tnetv107x.h> + +#define TSC_PENUP_POLL (HZ / 5) +#define IDLE_TIMEOUT 100 /* msec */ + +/* + * The first and last samples of a touch interval are usually garbage and need + * to be filtered out with these devices. The following definitions control + * the number of samples skipped. + */ +#define TSC_HEAD_SKIP 1 +#define TSC_TAIL_SKIP 1 +#define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1) +#define TSC_SAMPLES (TSC_SKIP + 1) + +/* Register Offsets */ +struct tsc_regs { + u32 rev; + u32 tscm; + u32 bwcm; + u32 swc; + u32 adcchnl; + u32 adcdata; + u32 chval[4]; +}; + +/* TSC Mode Configuration Register (tscm) bits */ +#define WMODE BIT(0) +#define TSKIND BIT(1) +#define ZMEASURE_EN BIT(2) +#define IDLE BIT(3) +#define TSC_EN BIT(4) +#define STOP BIT(5) +#define ONE_SHOT BIT(6) +#define SINGLE BIT(7) +#define AVG BIT(8) +#define AVGNUM(x) (((x) & 0x03) << 9) +#define PVSTC(x) (((x) & 0x07) << 11) +#define PON BIT(14) +#define PONBG BIT(15) +#define AFERST BIT(16) + +/* ADC DATA Capture Register bits */ +#define DATA_VALID BIT(16) + +/* Register Access Macros */ +#define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg) +#define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg); +#define tsc_set_bits(ts, reg, val) \ + tsc_write(ts, reg, tsc_read(ts, reg) | (val)) +#define tsc_clr_bits(ts, reg, val) \ + tsc_write(ts, reg, tsc_read(ts, reg) & ~(val)) + +struct sample { + int x, y, p; +}; + +struct tsc_data { + struct input_dev *input_dev; + struct resource *res; + struct tsc_regs __iomem *regs; + struct timer_list timer; + spinlock_t lock; + struct clk *clk; + struct device *dev; + int sample_count; + struct sample samples[TSC_SAMPLES]; + int tsc_irq; +}; + +static int tsc_read_sample(struct tsc_data *ts, struct sample* sample) +{ + int x, y, z1, z2, t, p = 0; + u32 val; + + val = tsc_read(ts, chval[0]); + if (val & DATA_VALID) + x = val & 0xffff; + else + return -EINVAL; + + y = tsc_read(ts, chval[1]) & 0xffff; + z1 = tsc_read(ts, chval[2]) & 0xffff; + z2 = tsc_read(ts, chval[3]) & 0xffff; + + if (z1) { + t = ((600 * x) * (z2 - z1)); + p = t / (u32) (z1 << 12); + if (p < 0) + p = 0; + } + + sample->x = x; + sample->y = y; + sample->p = p; + + return 0; +} + +static void tsc_poll(unsigned long data) +{ + struct tsc_data *ts = (struct tsc_data *)data; + unsigned long flags; + int i, val, x, y, p; + + spin_lock_irqsave(&ts->lock, flags); + + if (ts->sample_count >= TSC_SKIP) { + input_report_abs(ts->input_dev, ABS_PRESSURE, 0); + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); + } else if (ts->sample_count > 0) { + /* + * A touch event lasted less than our skip count. Salvage and + * report anyway. + */ + for (i = 0, val = 0; i < ts->sample_count; i++) + val += ts->samples[i].x; + x = val / ts->sample_count; + + for (i = 0, val = 0; i < ts->sample_count; i++) + val += ts->samples[i].y; + y = val / ts->sample_count; + + for (i = 0, val = 0; i < ts->sample_count; i++) + val += ts->samples[i].p; + p = val / ts->sample_count; + + input_report_abs(ts->input_dev, ABS_X, x); + input_report_abs(ts->input_dev, ABS_Y, y); + input_report_abs(ts->input_dev, ABS_PRESSURE, p); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_sync(ts->input_dev); + } + + ts->sample_count = 0; + + spin_unlock_irqrestore(&ts->lock, flags); +} + +static irqreturn_t tsc_irq(int irq, void *dev_id) +{ + struct tsc_data *ts = (struct tsc_data *)dev_id; + struct sample *sample; + int index; + + spin_lock(&ts->lock); + + index = ts->sample_count % TSC_SAMPLES; + sample = &ts->samples[index]; + if (tsc_read_sample(ts, sample) < 0) + goto out; + + if (++ts->sample_count >= TSC_SKIP) { + index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES; + sample = &ts->samples[index]; + + input_report_abs(ts->input_dev, ABS_X, sample->x); + input_report_abs(ts->input_dev, ABS_Y, sample->y); + input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p); + if (ts->sample_count == TSC_SKIP) + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_sync(ts->input_dev); + } + mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL); +out: + spin_unlock(&ts->lock); + return IRQ_HANDLED; +} + +static int tsc_start(struct input_dev *dev) +{ + struct tsc_data *ts = input_get_drvdata(dev); + unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT); + u32 val; + + clk_enable(ts->clk); + + /* Go to idle mode, before any initialization */ + while (time_after(timeout, jiffies)) { + if (tsc_read(ts, tscm) & IDLE) + break; + } + + if (time_before(timeout, jiffies)) { + dev_warn(ts->dev, "timeout waiting for idle\n"); + clk_disable(ts->clk); + return -EIO; + } + + /* Configure TSC Control register*/ + val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN); + tsc_write(ts, tscm, val); + + /* Bring TSC out of reset: Clear AFE reset bit */ + val &= ~(AFERST); + tsc_write(ts, tscm, val); + + /* Configure all pins for hardware control*/ + tsc_write(ts, bwcm, 0); + + /* Finally enable the TSC */ + tsc_set_bits(ts, tscm, TSC_EN); + + return 0; +} + +static void tsc_stop(struct input_dev *dev) +{ + struct tsc_data *ts = input_get_drvdata(dev); + + tsc_clr_bits(ts, tscm, TSC_EN); + synchronize_irq(ts->tsc_irq); + del_timer_sync(&ts->timer); + clk_disable(ts->clk); +} + +static int __devinit tsc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tsc_data *ts; + int error = 0; + u32 rev = 0; + + ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL); + if (!ts) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + ts->dev = dev; + spin_lock_init(&ts->lock); + setup_timer(&ts->timer, tsc_poll, (unsigned long)ts); + platform_set_drvdata(pdev, ts); + + ts->tsc_irq = platform_get_irq(pdev, 0); + if (ts->tsc_irq < 0) { + dev_err(dev, "cannot determine device interrupt\n"); + error = -ENODEV; + goto error_res; + } + + ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ts->res) { + dev_err(dev, "cannot determine register area\n"); + error = -ENODEV; + goto error_res; + } + + if (!request_mem_region(ts->res->start, resource_size(ts->res), + pdev->name)) { + dev_err(dev, "cannot claim register memory\n"); + ts->res = NULL; + error = -EINVAL; + goto error_res; + } + + ts->regs = ioremap(ts->res->start, resource_size(ts->res)); + if (!ts->regs) { + dev_err(dev, "cannot map register memory\n"); + error = -ENOMEM; + goto error_map; + } + + ts->clk = clk_get(dev, NULL); + if (!ts->clk) { + dev_err(dev, "cannot claim device clock\n"); + error = -EINVAL; + goto error_clk; + } + + error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0, + dev_name(dev), ts); + if (error < 0) { + dev_err(ts->dev, "Could not allocate ts irq\n"); + goto error_irq; + } + + ts->input_dev = input_allocate_device(); + if (!ts->input_dev) { + dev_err(dev, "cannot allocate input device\n"); + error = -ENOMEM; + goto error_input; + } + input_set_drvdata(ts->input_dev, ts); + + ts->input_dev->name = pdev->name; + ts->input_dev->id.bustype = BUS_HOST; + ts->input_dev->dev.parent = &pdev->dev; + ts->input_dev->open = tsc_start; + ts->input_dev->close = tsc_stop; + + clk_enable(ts->clk); + rev = tsc_read(ts, rev); + ts->input_dev->id.product = ((rev >> 8) & 0x07); + ts->input_dev->id.version = ((rev >> 16) & 0xfff); + clk_disable(ts->clk); + + __set_bit(EV_KEY, ts->input_dev->evbit); + __set_bit(EV_ABS, ts->input_dev->evbit); + __set_bit(BTN_TOUCH, ts->input_dev->keybit); + + input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0); + input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0); + + error = input_register_device(ts->input_dev); + if (error < 0) { + dev_err(dev, "failed input device registration\n"); + goto error_reg; + } + + return 0; + +error_reg: + input_free_device(ts->input_dev); +error_input: + free_irq(ts->tsc_irq, ts); +error_irq: + clk_put(ts->clk); +error_clk: + iounmap(ts->regs); +error_map: + release_mem_region(ts->res->start, resource_size(ts->res)); +error_res: + platform_set_drvdata(pdev, NULL); + kfree(ts); + + return error; +} + +static int __devexit tsc_remove(struct platform_device *pdev) +{ + struct tsc_data *ts = platform_get_drvdata(pdev); + + input_unregister_device(ts->input_dev); + free_irq(ts->tsc_irq, ts); + clk_put(ts->clk); + iounmap(ts->regs); + release_mem_region(ts->res->start, resource_size(ts->res)); + platform_set_drvdata(pdev, NULL); + kfree(ts); + + return 0; +} + +static struct platform_driver tsc_driver = { + .probe = tsc_probe, + .remove = __devexit_p(tsc_remove), + .driver.name = "tnetv107x-ts", + .driver.owner = THIS_MODULE, +}; + +static int __init tsc_init(void) +{ + return platform_driver_register(&tsc_driver); +} + +static void __exit tsc_exit(void) +{ + platform_driver_unregister(&tsc_driver); +} + +module_init(tsc_init); +module_exit(tsc_exit); + +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_DESCRIPTION("TNETV107X Touchscreen Driver"); +MODULE_ALIAS("platform: tnetv107x-ts"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index a644d18c04d..c8c136cf7bb 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -335,6 +335,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev) dev_err(tsc->dev, "schedule failed"); goto err2; } + platform_set_drvdata(pdev, tps6507x_dev); return 0; @@ -358,7 +359,7 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev) cancel_delayed_work_sync(&tsc->work); destroy_workqueue(tsc->wq); - input_free_device(input_dev); + input_unregister_device(input_dev); tps6507x_dev->ts = NULL; kfree(tsc); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index be23780e8a3..80467f26233 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -265,7 +265,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tsc2007 *ts; - struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; + struct tsc2007_platform_data *pdata = client->dev.platform_data; struct input_dev *input_dev; int err; diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 56dc35c94bb..9ae4c7b16ba 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -2,6 +2,7 @@ * Wacom W8001 penabled serial touchscreen driver * * Copyright (c) 2008 Jaya Kumar + * Copyright (c) 2010 Red Hat, Inc. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for @@ -30,11 +31,24 @@ MODULE_LICENSE("GPL"); #define W8001_LEAD_BYTE 0x80 #define W8001_TAB_MASK 0x40 #define W8001_TAB_BYTE 0x40 +/* set in first byte of touch data packets */ +#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) +#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) #define W8001_QUERY_PACKET 0x20 #define W8001_CMD_START '1' #define W8001_CMD_QUERY '*' +#define W8001_CMD_TOUCHQUERY '%' + +/* length of data packets in bytes, depends on device. */ +#define W8001_PKTLEN_TOUCH93 5 +#define W8001_PKTLEN_TOUCH9A 7 +#define W8001_PKTLEN_TPCPEN 9 +#define W8001_PKTLEN_TPCCTL 11 /* control packet */ +#define W8001_PKTLEN_TOUCH2FG 13 + +#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */ struct w8001_coord { u8 rdy; @@ -48,6 +62,15 @@ struct w8001_coord { u8 tilt_y; }; +/* touch query reply packet */ +struct w8001_touch_query { + u8 panel_res; + u8 capacity_res; + u8 sensor_id; + u16 x; + u16 y; +}; + /* * Per-touchscreen data. */ @@ -62,6 +85,9 @@ struct w8001 { unsigned char response[W8001_MAX_LENGTH]; unsigned char data[W8001_MAX_LENGTH]; char phys[32]; + int type; + unsigned int pktlen; + int trkid[2]; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -88,11 +114,98 @@ static void parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_y = data[8] & 0x7F; } +static void parse_touch(struct w8001 *w8001) +{ + static int trkid; + struct input_dev *dev = w8001->dev; + unsigned char *data = w8001->data; + int i; + + for (i = 0; i < 2; i++) { + input_mt_slot(dev, i); + + if (data[0] & (1 << i)) { + int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); + int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); + /* data[5,6] and [11,12] is finger capacity */ + + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + if (w8001->trkid[i] < 0) + w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; + } else { + w8001->trkid[i] = -1; + } + input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); + } + + input_sync(dev); +} + +static void parse_touchquery(u8 *data, struct w8001_touch_query *query) +{ + memset(query, 0, sizeof(*query)); + + query->panel_res = data[1]; + query->sensor_id = data[2] & 0x7; + query->capacity_res = data[7]; + + query->x = data[3] << 9; + query->x |= data[4] << 2; + query->x |= (data[2] >> 5) & 0x3; + + query->y = data[5] << 9; + query->y |= data[6] << 2; + query->y |= (data[2] >> 3) & 0x3; +} + +static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) +{ + struct input_dev *dev = w8001->dev; + + /* + * We have 1 bit for proximity (rdy) and 3 bits for tip, side, + * side2/eraser. If rdy && f2 are set, this can be either pen + side2, + * or eraser. assume + * - if dev is already in proximity and f2 is toggled → pen + side2 + * - if dev comes into proximity with f2 set → eraser + * If f2 disappears after assuming eraser, fake proximity out for + * eraser and in for pen. + */ + + if (!w8001->type) { + w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + } else if (w8001->type == BTN_TOOL_RUBBER) { + if (!coord->f2) { + input_report_abs(dev, ABS_PRESSURE, 0); + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_STYLUS, 0); + input_report_key(dev, BTN_STYLUS2, 0); + input_report_key(dev, BTN_TOOL_RUBBER, 0); + input_sync(dev); + w8001->type = BTN_TOOL_PEN; + } + } else { + input_report_key(dev, BTN_STYLUS2, coord->f2); + } + + input_report_abs(dev, ABS_X, coord->x); + input_report_abs(dev, ABS_Y, coord->y); + input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); + input_report_key(dev, BTN_TOUCH, coord->tsw); + input_report_key(dev, BTN_STYLUS, coord->f1); + input_report_key(dev, w8001->type, coord->rdy); + input_sync(dev); + + if (!coord->rdy) + w8001->type = 0; +} + static irqreturn_t w8001_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { struct w8001 *w8001 = serio_get_drvdata(serio); - struct input_dev *dev = w8001->dev; struct w8001_coord coord; unsigned char tmp; @@ -105,26 +218,45 @@ static irqreturn_t w8001_interrupt(struct serio *serio, } break; - case 8: + case W8001_PKTLEN_TOUCH93 - 1: + case W8001_PKTLEN_TOUCH9A - 1: + /* ignore one-finger touch packet. */ + if (w8001->pktlen == w8001->idx) + w8001->idx = 0; + break; + + /* Pen coordinates packet */ + case W8001_PKTLEN_TPCPEN - 1: tmp = w8001->data[0] & W8001_TAB_MASK; if (unlikely(tmp == W8001_TAB_BYTE)) break; + tmp = (w8001->data[0] & W8001_TOUCH_BYTE); + if (tmp == W8001_TOUCH_BYTE) + break; + w8001->idx = 0; parse_data(w8001->data, &coord); - input_report_abs(dev, ABS_X, coord.x); - input_report_abs(dev, ABS_Y, coord.y); - input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure); - input_report_key(dev, BTN_TOUCH, coord.tsw); - input_sync(dev); + report_pen_events(w8001, &coord); break; - case 10: + /* control packet */ + case W8001_PKTLEN_TPCCTL - 1: + tmp = (w8001->data[0] & W8001_TOUCH_MASK); + if (tmp == W8001_TOUCH_BYTE) + break; + w8001->idx = 0; memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); w8001->response_type = W8001_QUERY_PACKET; complete(&w8001->cmd_done); break; + + /* 2 finger touch packet */ + case W8001_PKTLEN_TOUCH2FG - 1: + w8001->idx = 0; + parse_touch(w8001); + break; } return IRQ_HANDLED; @@ -167,6 +299,38 @@ static int w8001_setup(struct w8001 *w8001) input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + if (!error) { + struct w8001_touch_query touch; + + parse_touchquery(w8001->response, &touch); + + switch (touch.sensor_id) { + case 0: + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + break; + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; + break; + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + input_mt_create_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_TRACKING_ID, + 0, MAX_TRACKING_ID, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, touch.y, 0, 0); + input_set_abs_params(dev, ABS_MT_TOOL_TYPE, + 0, 0, 0, 0); + break; + } + } + return w8001_command(w8001, W8001_CMD_START, false); } @@ -208,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; w8001->id = serio->id.id; w8001->dev = input_dev; + w8001->trkid[0] = w8001->trkid[1] = -1; init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -221,6 +386,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); + input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); + input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); + input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index cbfef1ea7e3..6b75c9f660a 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -125,6 +125,8 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) { int power_adc = 0, auxval; u16 power = 0; + int rc = 0; + int timeout = 0; /* get codec */ mutex_lock(&wm->codec_mutex); @@ -143,7 +145,9 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) /* Turn polling mode on to read AUX ADC */ wm->pen_probably_down = 1; - wm->codec->poll_sample(wm, adcsel, &auxval); + + while (rc != RC_VALID && timeout++ < 5) + rc = wm->codec->poll_sample(wm, adcsel, &auxval); if (power_adc) wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); @@ -152,8 +156,15 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) wm->pen_probably_down = 0; + if (timeout >= 5) { + dev_err(wm->dev, + "timeout reading auxadc %d, disabling digitiser\n", + adcsel); + wm->codec->dig_enable(wm, false); + } + mutex_unlock(&wm->codec_mutex); - return auxval & 0xfff; + return (rc == RC_VALID ? auxval & 0xfff : -EBUSY); } EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); @@ -684,8 +695,7 @@ static int wm97xx_probe(struct device *dev) touch_reg_err: platform_device_put(wm->touch_dev); touch_err: - platform_device_unregister(wm->battery_dev); - wm->battery_dev = NULL; + platform_device_del(wm->battery_dev); batt_reg_err: platform_device_put(wm->battery_dev); batt_err: diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index 7961d59f5ca..c06b4d50a3d 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -25,14 +25,56 @@ #define IR_KEYPRESS_TIMEOUT 250 /** + * ir_create_table() - initializes a scancode table + * @rc_tab: the ir_scancode_table to initialize + * @name: name to assign to the table + * @ir_type: ir type to assign to the new table + * @size: initial size of the table + * @return: zero on success or a negative error code + * + * This routine will initialize the ir_scancode_table and will allocate + * memory to hold at least the specified number elements. + */ +static int ir_create_table(struct ir_scancode_table *rc_tab, + const char *name, u64 ir_type, size_t size) +{ + rc_tab->name = name; + rc_tab->ir_type = ir_type; + rc_tab->alloc = roundup_pow_of_two(size * sizeof(struct ir_scancode)); + rc_tab->size = rc_tab->alloc / sizeof(struct ir_scancode); + rc_tab->scan = kmalloc(rc_tab->alloc, GFP_KERNEL); + if (!rc_tab->scan) + return -ENOMEM; + + IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", + rc_tab->size, rc_tab->alloc); + return 0; +} + +/** + * ir_free_table() - frees memory allocated by a scancode table + * @rc_tab: the table whose mappings need to be freed + * + * This routine will free memory alloctaed for key mappings used by given + * scancode table. + */ +static void ir_free_table(struct ir_scancode_table *rc_tab) +{ + rc_tab->size = 0; + kfree(rc_tab->scan); + rc_tab->scan = NULL; +} + +/** * ir_resize_table() - resizes a scancode table if necessary * @rc_tab: the ir_scancode_table to resize + * @gfp_flags: gfp flags to use when allocating memory * @return: zero on success or a negative error code * * This routine will shrink the ir_scancode_table if it has lots of * unused entries and grow it if it is full. */ -static int ir_resize_table(struct ir_scancode_table *rc_tab) +static int ir_resize_table(struct ir_scancode_table *rc_tab, gfp_t gfp_flags) { unsigned int oldalloc = rc_tab->alloc; unsigned int newalloc = oldalloc; @@ -57,7 +99,7 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab) if (newalloc == oldalloc) return 0; - newscan = kmalloc(newalloc, GFP_ATOMIC); + newscan = kmalloc(newalloc, gfp_flags); if (!newscan) { IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc); return -ENOMEM; @@ -72,26 +114,78 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab) } /** - * ir_do_setkeycode() - internal function to set a keycode in the - * scancode->keycode table + * ir_update_mapping() - set a keycode in the scancode->keycode table * @dev: the struct input_dev device descriptor - * @rc_tab: the struct ir_scancode_table to set the keycode in - * @scancode: the scancode for the ir command - * @keycode: the keycode for the ir command - * @resize: whether the keytable may be shrunk - * @return: -EINVAL if the keycode could not be inserted, otherwise zero. + * @rc_tab: scancode table to be adjusted + * @index: index of the mapping that needs to be updated + * @keycode: the desired keycode + * @return: previous keycode assigned to the mapping + * + * This routine is used to update scancode->keycopde mapping at given + * position. + */ +static unsigned int ir_update_mapping(struct input_dev *dev, + struct ir_scancode_table *rc_tab, + unsigned int index, + unsigned int new_keycode) +{ + int old_keycode = rc_tab->scan[index].keycode; + int i; + + /* Did the user wish to remove the mapping? */ + if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) { + IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", + index, rc_tab->scan[index].scancode); + rc_tab->len--; + memmove(&rc_tab->scan[index], &rc_tab->scan[index+ 1], + (rc_tab->len - index) * sizeof(struct ir_scancode)); + } else { + IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n", + index, + old_keycode == KEY_RESERVED ? "New" : "Replacing", + rc_tab->scan[index].scancode, new_keycode); + rc_tab->scan[index].keycode = new_keycode; + __set_bit(new_keycode, dev->keybit); + } + + if (old_keycode != KEY_RESERVED) { + /* A previous mapping was updated... */ + __clear_bit(old_keycode, dev->keybit); + /* ... but another scancode might use the same keycode */ + for (i = 0; i < rc_tab->len; i++) { + if (rc_tab->scan[i].keycode == old_keycode) { + __set_bit(old_keycode, dev->keybit); + break; + } + } + + /* Possibly shrink the keytable, failure is not a problem */ + ir_resize_table(rc_tab, GFP_ATOMIC); + } + + return old_keycode; +} + +/** + * ir_locate_scancode() - set a keycode in the scancode->keycode table + * @ir_dev: the struct ir_input_dev device descriptor + * @rc_tab: scancode table to be searched + * @scancode: the desired scancode + * @resize: controls whether we allowed to resize the table to + * accomodate not yet present scancodes + * @return: index of the mapping containing scancode in question + * or -1U in case of failure. * - * This routine is used internally to manipulate the scancode->keycode table. - * The caller has to hold @rc_tab->lock. + * This routine is used to locate given scancode in ir_scancode_table. + * If scancode is not yet present the routine will allocate a new slot + * for it. */ -static int ir_do_setkeycode(struct input_dev *dev, - struct ir_scancode_table *rc_tab, - unsigned scancode, unsigned keycode, - bool resize) +static unsigned int ir_establish_scancode(struct ir_input_dev *ir_dev, + struct ir_scancode_table *rc_tab, + unsigned int scancode, + bool resize) { unsigned int i; - int old_keycode = KEY_RESERVED; - struct ir_input_dev *ir_dev = input_get_drvdata(dev); /* * Unfortunately, some hardware-based IR decoders don't provide @@ -100,65 +194,34 @@ static int ir_do_setkeycode(struct input_dev *dev, * the provided IR with another one, it is needed to allow loading * IR tables from other remotes. So, */ - if (ir_dev->props && ir_dev->props->scanmask) { + if (ir_dev->props && ir_dev->props->scanmask) scancode &= ir_dev->props->scanmask; - } /* First check if we already have a mapping for this ir command */ for (i = 0; i < rc_tab->len; i++) { + if (rc_tab->scan[i].scancode == scancode) + return i; + /* Keytable is sorted from lowest to highest scancode */ - if (rc_tab->scan[i].scancode > scancode) + if (rc_tab->scan[i].scancode >= scancode) break; - else if (rc_tab->scan[i].scancode < scancode) - continue; - - old_keycode = rc_tab->scan[i].keycode; - rc_tab->scan[i].keycode = keycode; - - /* Did the user wish to remove the mapping? */ - if (keycode == KEY_RESERVED || keycode == KEY_UNKNOWN) { - IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", - i, scancode); - rc_tab->len--; - memmove(&rc_tab->scan[i], &rc_tab->scan[i + 1], - (rc_tab->len - i) * sizeof(struct ir_scancode)); - } - - /* Possibly shrink the keytable, failure is not a problem */ - ir_resize_table(rc_tab); - break; } - if (old_keycode == KEY_RESERVED && keycode != KEY_RESERVED) { - /* No previous mapping found, we might need to grow the table */ - if (resize && ir_resize_table(rc_tab)) - return -ENOMEM; - - IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", - i, scancode, keycode); + /* No previous mapping found, we might need to grow the table */ + if (rc_tab->size == rc_tab->len) { + if (!resize || ir_resize_table(rc_tab, GFP_ATOMIC)) + return -1U; + } - /* i is the proper index to insert our new keycode */ + /* i is the proper index to insert our new keycode */ + if (i < rc_tab->len) memmove(&rc_tab->scan[i + 1], &rc_tab->scan[i], (rc_tab->len - i) * sizeof(struct ir_scancode)); - rc_tab->scan[i].scancode = scancode; - rc_tab->scan[i].keycode = keycode; - rc_tab->len++; - set_bit(keycode, dev->keybit); - } else { - IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", - i, scancode, keycode); - /* A previous mapping was updated... */ - clear_bit(old_keycode, dev->keybit); - /* ...but another scancode might use the same keycode */ - for (i = 0; i < rc_tab->len; i++) { - if (rc_tab->scan[i].keycode == old_keycode) { - set_bit(old_keycode, dev->keybit); - break; - } - } - } + rc_tab->scan[i].scancode = scancode; + rc_tab->scan[i].keycode = KEY_RESERVED; + rc_tab->len++; - return 0; + return i; } /** @@ -171,17 +234,41 @@ static int ir_do_setkeycode(struct input_dev *dev, * This routine is used to handle evdev EVIOCSKEY ioctl. */ static int ir_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { - int rc; - unsigned long flags; struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; + unsigned int index; + unsigned int scancode; + int retval; + unsigned long flags; spin_lock_irqsave(&rc_tab->lock, flags); - rc = ir_do_setkeycode(dev, rc_tab, scancode, keycode, true); + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + if (index >= rc_tab->len) { + retval = -EINVAL; + goto out; + } + } else { + retval = input_scancode_to_scalar(ke, &scancode); + if (retval) + goto out; + + index = ir_establish_scancode(ir_dev, rc_tab, scancode, true); + if (index >= rc_tab->len) { + retval = -ENOMEM; + goto out; + } + } + + *old_keycode = ir_update_mapping(dev, rc_tab, index, ke->keycode); + +out: spin_unlock_irqrestore(&rc_tab->lock, flags); - return rc; + return retval; } /** @@ -189,32 +276,73 @@ static int ir_setkeycode(struct input_dev *dev, * @dev: the struct input_dev device descriptor * @to: the struct ir_scancode_table to copy entries to * @from: the struct ir_scancode_table to copy entries from - * @return: -EINVAL if all keycodes could not be inserted, otherwise zero. + * @return: -ENOMEM if all keycodes could not be inserted, otherwise zero. * * This routine is used to handle table initialization. */ -static int ir_setkeytable(struct input_dev *dev, - struct ir_scancode_table *to, +static int ir_setkeytable(struct ir_input_dev *ir_dev, const struct ir_scancode_table *from) { - struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - unsigned long flags; - unsigned int i; - int rc = 0; + unsigned int i, index; + int rc; + + rc = ir_create_table(&ir_dev->rc_tab, + from->name, from->ir_type, from->size); + if (rc) + return rc; + + IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", + rc_tab->size, rc_tab->alloc); - spin_lock_irqsave(&rc_tab->lock, flags); for (i = 0; i < from->size; i++) { - rc = ir_do_setkeycode(dev, to, from->scan[i].scancode, - from->scan[i].keycode, false); - if (rc) + index = ir_establish_scancode(ir_dev, rc_tab, + from->scan[i].scancode, false); + if (index >= rc_tab->len) { + rc = -ENOMEM; break; + } + + ir_update_mapping(ir_dev->input_dev, rc_tab, index, + from->scan[i].keycode); } - spin_unlock_irqrestore(&rc_tab->lock, flags); + + if (rc) + ir_free_table(rc_tab); + return rc; } /** + * ir_lookup_by_scancode() - locate mapping by scancode + * @rc_tab: the &struct ir_scancode_table to search + * @scancode: scancode to look for in the table + * @return: index in the table, -1U if not found + * + * This routine performs binary search in RC keykeymap table for + * given scancode. + */ +static unsigned int ir_lookup_by_scancode(const struct ir_scancode_table *rc_tab, + unsigned int scancode) +{ + unsigned int start = 0; + unsigned int end = rc_tab->len - 1; + unsigned int mid; + + while (start <= end) { + mid = (start + end) / 2; + if (rc_tab->scan[mid].scancode < scancode) + start = mid + 1; + else if (rc_tab->scan[mid].scancode > scancode) + end = mid - 1; + else + return mid; + } + + return -1U; +} + +/** * ir_getkeycode() - get a keycode from the scancode->keycode table * @dev: the struct input_dev device descriptor * @scancode: the desired scancode @@ -224,36 +352,46 @@ static int ir_setkeytable(struct input_dev *dev, * This routine is used to handle evdev EVIOCGKEY ioctl. */ static int ir_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) + struct input_keymap_entry *ke) { - int start, end, mid; - unsigned long flags; - int key = KEY_RESERVED; struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; + struct ir_scancode *entry; + unsigned long flags; + unsigned int index; + unsigned int scancode; + int retval; spin_lock_irqsave(&rc_tab->lock, flags); - start = 0; - end = rc_tab->len - 1; - while (start <= end) { - mid = (start + end) / 2; - if (rc_tab->scan[mid].scancode < scancode) - start = mid + 1; - else if (rc_tab->scan[mid].scancode > scancode) - end = mid - 1; - else { - key = rc_tab->scan[mid].keycode; - break; - } + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + } else { + retval = input_scancode_to_scalar(ke, &scancode); + if (retval) + goto out; + + index = ir_lookup_by_scancode(rc_tab, scancode); + } + + if (index >= rc_tab->len) { + if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) + IR_dprintk(1, "unknown key for scancode 0x%04x\n", + scancode); + retval = -EINVAL; + goto out; } - spin_unlock_irqrestore(&rc_tab->lock, flags); - if (key == KEY_RESERVED) - IR_dprintk(1, "unknown key for scancode 0x%04x\n", - scancode); + entry = &rc_tab->scan[index]; - *keycode = key; - return 0; + ke->index = index; + ke->keycode = entry->keycode; + ke->len = sizeof(entry->scancode); + memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode)); + +out: + spin_unlock_irqrestore(&rc_tab->lock, flags); + return retval; } /** @@ -268,12 +406,24 @@ static int ir_getkeycode(struct input_dev *dev, */ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) { - int keycode; + struct ir_input_dev *ir_dev = input_get_drvdata(dev); + struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; + unsigned int keycode; + unsigned int index; + unsigned long flags; + + spin_lock_irqsave(&rc_tab->lock, flags); + + index = ir_lookup_by_scancode(rc_tab, scancode); + keycode = index < rc_tab->len ? + rc_tab->scan[index].keycode : KEY_RESERVED; + + spin_unlock_irqrestore(&rc_tab->lock, flags); - ir_getkeycode(dev, scancode, &keycode); if (keycode != KEY_RESERVED) IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", dev->name, scancode, keycode); + return keycode; } EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); @@ -453,8 +603,8 @@ int __ir_input_register(struct input_dev *input_dev, goto out_dev; } - input_dev->getkeycode = ir_getkeycode; - input_dev->setkeycode = ir_setkeycode; + input_dev->getkeycode_new = ir_getkeycode; + input_dev->setkeycode_new = ir_setkeycode; input_set_drvdata(input_dev, ir_dev); ir_dev->input_dev = input_dev; @@ -462,12 +612,6 @@ int __ir_input_register(struct input_dev *input_dev, spin_lock_init(&ir_dev->keylock); setup_timer(&ir_dev->timer_keyup, ir_timer_keyup, (unsigned long)ir_dev); - ir_dev->rc_tab.name = rc_tab->name; - ir_dev->rc_tab.ir_type = rc_tab->ir_type; - ir_dev->rc_tab.alloc = roundup_pow_of_two(rc_tab->size * - sizeof(struct ir_scancode)); - ir_dev->rc_tab.scan = kmalloc(ir_dev->rc_tab.alloc, GFP_KERNEL); - ir_dev->rc_tab.size = ir_dev->rc_tab.alloc / sizeof(struct ir_scancode); if (props) { ir_dev->props = props; if (props->open) @@ -476,23 +620,14 @@ int __ir_input_register(struct input_dev *input_dev, input_dev->close = ir_close; } - if (!ir_dev->rc_tab.scan) { - rc = -ENOMEM; - goto out_name; - } - - IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", - ir_dev->rc_tab.size, ir_dev->rc_tab.alloc); - set_bit(EV_KEY, input_dev->evbit); set_bit(EV_REP, input_dev->evbit); set_bit(EV_MSC, input_dev->evbit); set_bit(MSC_SCAN, input_dev->mscbit); - if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) { - rc = -ENOMEM; - goto out_table; - } + rc = ir_setkeytable(ir_dev, rc_tab); + if (rc) + goto out_name; rc = ir_register_class(input_dev); if (rc < 0) @@ -522,7 +657,7 @@ int __ir_input_register(struct input_dev *input_dev, out_event: ir_unregister_class(input_dev); out_table: - kfree(ir_dev->rc_tab.scan); + ir_free_table(&ir_dev->rc_tab); out_name: kfree(ir_dev->driver_name); out_dev: @@ -540,7 +675,6 @@ EXPORT_SYMBOL_GPL(__ir_input_register); void ir_input_unregister(struct input_dev *input_dev) { struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct ir_scancode_table *rc_tab; if (!ir_dev) return; @@ -552,10 +686,7 @@ void ir_input_unregister(struct input_dev *input_dev) if (ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(input_dev); - rc_tab = &ir_dev->rc_tab; - rc_tab->size = 0; - kfree(rc_tab->scan); - rc_tab->scan = NULL; + ir_free_table(&ir_dev->rc_tab); ir_unregister_class(input_dev); diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index defa786dee3..b8c4b80e4c4 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -338,6 +338,21 @@ static struct resource ab8500_rtc_resources[] = { }, }; +static struct resource ab8500_poweronkey_db_resources[] = { + { + .name = "ONKEY_DBF", + .start = AB8500_INT_PON_KEY1DB_F, + .end = AB8500_INT_PON_KEY1DB_F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "ONKEY_DBR", + .start = AB8500_INT_PON_KEY1DB_R, + .end = AB8500_INT_PON_KEY1DB_R, + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-gpadc", @@ -354,6 +369,11 @@ static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-usb", }, { .name = "ab8500-pwm", }, { .name = "ab8500-regulator", }, + { + .name = "ab8500-poweron-key", + .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), + .resources = ab8500_poweronkey_db_resources, + }, }; int __devinit ab8500_init(struct ab8500 *ab8500) diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c index cd164595f08..49b4d069cbf 100644 --- a/drivers/mfd/sh_mobile_sdhi.c +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -65,7 +65,7 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state) p->set_pwr(pdev, state); } -static int __init sh_mobile_sdhi_probe(struct platform_device *pdev) +static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) { struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 720e099e506..5d0fb60a4c1 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -698,17 +698,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_codec() && pdata->codec && twl_class_is_4030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl4030_codec", + child = add_child(sub_chip_id, "twl4030-audio", pdata->codec, sizeof(*pdata->codec), false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } - /* Phoenix*/ + /* Phoenix codec driver is probed directly atm */ if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6040_codec", + child = add_child(sub_chip_id, "twl6040-codec", pdata->codec, sizeof(*pdata->codec), false, 0, 0); if (IS_ERR(child)) diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index add6f67d803..9a4b196d6de 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -207,14 +207,14 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) if (pdata->audio) { cell = &codec->cells[childs]; - cell->name = "twl4030_codec_audio"; + cell->name = "twl4030-codec"; cell->platform_data = pdata->audio; cell->data_size = sizeof(*pdata->audio); childs++; } if (pdata->vibra) { cell = &codec->cells[childs]; - cell->name = "twl4030_codec_vibra"; + cell->name = "twl4030-vibra"; cell->platform_data = pdata->vibra; cell->data_size = sizeof(*pdata->vibra); childs++; @@ -249,14 +249,14 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev) return 0; } -MODULE_ALIAS("platform:twl4030_codec"); +MODULE_ALIAS("platform:twl4030-audio"); static struct platform_driver twl4030_codec_driver = { .probe = twl4030_codec_probe, .remove = __devexit_p(twl4030_codec_remove), .driver = { .owner = THIS_MODULE, - .name = "twl4030_codec", + .name = "twl4030-audio", }, }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4526d2791f2..4693e62145a 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -364,6 +364,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { struct regulator *reg; int ret = 0; + int ocr_value = 0; switch (host->id) { case OMAP_MMC1_DEVID: @@ -396,6 +397,17 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } } else { host->vcc = reg; + ocr_value = mmc_regulator_get_ocrmask(reg); + if (!mmc_slot(host).ocr_mask) { + mmc_slot(host).ocr_mask = ocr_value; + } else { + if (!(mmc_slot(host).ocr_mask & ocr_value)) { + pr_err("MMC%d ocrmask %x is not supported\n", + host->id, mmc_slot(host).ocr_mask); + mmc_slot(host).ocr_mask = 0; + return -EINVAL; + } + } mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg); /* Allow an aux regulator */ @@ -982,6 +994,17 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | bit); + /* + * OMAP4 ES2 and greater has an updated reset logic. + * Monitor a 0->1 transition first + */ + if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { + while ((!(OMAP_HSMMC_READ(host, SYSCTL) & bit)) + && (i++ < limit)) + cpu_relax(); + } + i = 0; + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && (i++ < limit)) cpu_relax(); @@ -2003,6 +2026,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) if (res == NULL || irq < 0) return -ENXIO; + res->start += pdata->reg_offset; + res->end += pdata->reg_offset; res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (res == NULL) @@ -2116,23 +2141,9 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - switch (mmc_slot(host).wires) { - case 8: - mmc->caps |= MMC_CAP_8_BIT_DATA; - /* Fall through */ - case 4: + mmc->caps |= mmc_slot(host).caps; + if (mmc->caps & MMC_CAP_8_BIT_DATA) mmc->caps |= MMC_CAP_4_BIT_DATA; - break; - case 1: - /* Nothing to crib here */ - case 0: - /* Assuming nothing was given by board, Core use's 1-Bit */ - break; - default: - /* Completely unexpected.. Core goes with 1-Bit Width */ - dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!" - "using 1 instead\n", mmc_slot(host).wires); - } if (mmc_slot(host).nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 6f512b5c117..ea22520c040 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -924,6 +924,13 @@ static int __devinit m25p_probe(struct spi_device *spi) nr_parts = data->nr_parts; } +#ifdef CONFIG_OF + if (nr_parts <= 0 && spi->dev.of_node) { + nr_parts = of_mtd_parse_partitions(&spi->dev, + spi->dev.of_node, &parts); + } +#endif + if (nr_parts > 0) { for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index fe63f6bd663..ec3edf6e68b 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -294,7 +294,7 @@ static int __devinit of_flash_probe(struct platform_device *dev, info->list[i].map.name = dev_name(&dev->dev); info->list[i].map.phys = res.start; info->list[i].map.size = res_size; - info->list[i].map.bankwidth = *width; + info->list[i].map.bankwidth = be32_to_cpup(width); err = -ENOMEM; info->list[i].map.virt = ioremap(info->list[i].map.phys, diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 2ac7367afe7..8beb0d0233b 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -35,6 +35,7 @@ #include <linux/slab.h> #include <mach/nand.h> +#include <mach/aemif.h> #include <asm/mach-types.h> @@ -74,6 +75,8 @@ struct davinci_nand_info { uint32_t mask_cle; uint32_t core_chipsel; + + struct davinci_aemif_timing *timing; }; static DEFINE_SPINLOCK(davinci_nand_lock); @@ -478,36 +481,6 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd) return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0); } -static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info) -{ - uint32_t regval, a1cr; - - /* - * NAND FLASH timings @ PLL1 == 459 MHz - * - AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz - * - AEMIF.CLK period = 1/76.5 MHz = 13.1 ns - */ - regval = 0 - | (0 << 31) /* selectStrobe */ - | (0 << 30) /* extWait (never with NAND) */ - | (1 << 26) /* writeSetup 10 ns */ - | (3 << 20) /* writeStrobe 40 ns */ - | (1 << 17) /* writeHold 10 ns */ - | (0 << 13) /* readSetup 10 ns */ - | (3 << 7) /* readStrobe 60 ns */ - | (0 << 4) /* readHold 10 ns */ - | (3 << 2) /* turnAround ?? ns */ - | (0 << 0) /* asyncSize 8-bit bus */ - ; - a1cr = davinci_nand_readl(info, A1CR_OFFSET); - if (a1cr != regval) { - dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \ - "reg to 0x%08x, was 0x%08x, should be done by " \ - "bootloader.\n", regval, a1cr); - davinci_nand_writel(info, A1CR_OFFSET, regval); - } -} - /*----------------------------------------------------------------------*/ /* An ECC layout for using 4-bit ECC with small-page flash, storing @@ -611,6 +584,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.options = pdata->options; info->chip.bbt_td = pdata->bbt_td; info->chip.bbt_md = pdata->bbt_md; + info->timing = pdata->timing; info->ioaddr = (uint32_t __force) vaddr; @@ -688,15 +662,25 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_clk_enable; } - /* EMIF timings should normally be set by the boot loader, - * especially after boot-from-NAND. The *only* reason to - * have this special casing for the DM6446 EVM is to work - * with boot-from-NOR ... with CS0 manually re-jumpered - * (after startup) so it addresses the NAND flash, not NOR. - * Even for dev boards, that's unusually rude... + /* + * Setup Async configuration register in case we did not boot from + * NAND and so bootloader did not bother to set it up. */ - if (machine_is_davinci_evm()) - nand_dm6446evm_flash_init(info); + val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4); + + /* Extended Wait is not valid and Select Strobe mode is not used */ + val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK); + if (info->chip.options & NAND_BUSWIDTH_16) + val |= 0x1; + + davinci_nand_writel(info, A1CR_OFFSET + info->core_chipsel * 4, val); + + ret = davinci_aemif_setup_timing(info->timing, info->base, + info->core_chipsel); + if (ret < 0) { + dev_dbg(&pdev->dev, "NAND timing values setup fail\n"); + goto err_timing; + } spin_lock_irq(&davinci_nand_lock); @@ -809,6 +793,7 @@ syndrome_done: return 0; err_scan: +err_timing: clk_disable(info->clk); err_clk_enable: diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 8bf7dc6d1ce..7bd171eefd2 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -53,8 +53,8 @@ int __devinit of_mtd_parse_partitions(struct device *dev, continue; } - (*pparts)[i].offset = reg[0]; - (*pparts)[i].size = reg[1]; + (*pparts)[i].offset = be32_to_cpu(reg[0]); + (*pparts)[i].size = be32_to_cpu(reg[1]); partname = of_get_property(pp, "label", &len); if (!partname) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c598fe008e3..86fe67fd49b 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -946,6 +946,8 @@ config NET_NETX config TI_DAVINCI_EMAC tristate "TI DaVinci EMAC Support" depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 ) + select TI_DAVINCI_MDIO + select TI_DAVINCI_CPDMA select PHYLIB help This driver supports TI's DaVinci Ethernet . @@ -953,6 +955,25 @@ config TI_DAVINCI_EMAC To compile this driver as a module, choose M here: the module will be called davinci_emac_driver. This is recommended. +config TI_DAVINCI_MDIO + tristate "TI DaVinci MDIO Support" + depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 ) + select PHYLIB + help + This driver supports TI's DaVinci MDIO module. + + To compile this driver as a module, choose M here: the module + will be called davinci_mdio. This is recommended. + +config TI_DAVINCI_CPDMA + tristate "TI DaVinci CPDMA Support" + depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 ) + help + This driver supports TI's DaVinci CPDMA dma engine. + + To compile this driver as a module, choose M here: the module + will be called davinci_cpdma. This is recommended. + config DM9000 tristate "DM9000 support" depends on ARM || BLACKFIN || MIPS diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b8bf93d4a13..652fc6b9803 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_MDIO) += mdio.o obj-$(CONFIG_PHYLIB) += phy/ obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o +obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o +obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o obj-$(CONFIG_E1000) += e1000/ obj-$(CONFIG_E1000E) += e1000e/ diff --git a/drivers/net/davinci_cpdma.c b/drivers/net/davinci_cpdma.c new file mode 100644 index 00000000000..e92b2b6cd8c --- /dev/null +++ b/drivers/net/davinci_cpdma.c @@ -0,0 +1,965 @@ +/* + * Texas Instruments CPDMA Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> + +#include "davinci_cpdma.h" + +/* DMA Registers */ +#define CPDMA_TXIDVER 0x00 +#define CPDMA_TXCONTROL 0x04 +#define CPDMA_TXTEARDOWN 0x08 +#define CPDMA_RXIDVER 0x10 +#define CPDMA_RXCONTROL 0x14 +#define CPDMA_SOFTRESET 0x1c +#define CPDMA_RXTEARDOWN 0x18 +#define CPDMA_TXINTSTATRAW 0x80 +#define CPDMA_TXINTSTATMASKED 0x84 +#define CPDMA_TXINTMASKSET 0x88 +#define CPDMA_TXINTMASKCLEAR 0x8c +#define CPDMA_MACINVECTOR 0x90 +#define CPDMA_MACEOIVECTOR 0x94 +#define CPDMA_RXINTSTATRAW 0xa0 +#define CPDMA_RXINTSTATMASKED 0xa4 +#define CPDMA_RXINTMASKSET 0xa8 +#define CPDMA_RXINTMASKCLEAR 0xac +#define CPDMA_DMAINTSTATRAW 0xb0 +#define CPDMA_DMAINTSTATMASKED 0xb4 +#define CPDMA_DMAINTMASKSET 0xb8 +#define CPDMA_DMAINTMASKCLEAR 0xbc +#define CPDMA_DMAINT_HOSTERR BIT(1) + +/* the following exist only if has_ext_regs is set */ +#define CPDMA_DMACONTROL 0x20 +#define CPDMA_DMASTATUS 0x24 +#define CPDMA_RXBUFFOFS 0x28 +#define CPDMA_EM_CONTROL 0x2c + +/* Descriptor mode bits */ +#define CPDMA_DESC_SOP BIT(31) +#define CPDMA_DESC_EOP BIT(30) +#define CPDMA_DESC_OWNER BIT(29) +#define CPDMA_DESC_EOQ BIT(28) +#define CPDMA_DESC_TD_COMPLETE BIT(27) +#define CPDMA_DESC_PASS_CRC BIT(26) + +#define CPDMA_TEARDOWN_VALUE 0xfffffffc + +struct cpdma_desc { + /* hardware fields */ + u32 hw_next; + u32 hw_buffer; + u32 hw_len; + u32 hw_mode; + /* software fields */ + void *sw_token; + u32 sw_buffer; + u32 sw_len; +}; + +struct cpdma_desc_pool { + u32 phys; + void __iomem *iomap; /* ioremap map */ + void *cpumap; /* dma_alloc map */ + int desc_size, mem_size; + int num_desc, used_desc; + unsigned long *bitmap; + struct device *dev; + spinlock_t lock; +}; + +enum cpdma_state { + CPDMA_STATE_IDLE, + CPDMA_STATE_ACTIVE, + CPDMA_STATE_TEARDOWN, +}; + +const char *cpdma_state_str[] = { "idle", "active", "teardown" }; + +struct cpdma_ctlr { + enum cpdma_state state; + struct cpdma_params params; + struct device *dev; + struct cpdma_desc_pool *pool; + spinlock_t lock; + struct cpdma_chan *channels[2 * CPDMA_MAX_CHANNELS]; +}; + +struct cpdma_chan { + enum cpdma_state state; + struct cpdma_ctlr *ctlr; + int chan_num; + spinlock_t lock; + struct cpdma_desc __iomem *head, *tail; + int count; + void __iomem *hdp, *cp, *rxfree; + u32 mask; + cpdma_handler_fn handler; + enum dma_data_direction dir; + struct cpdma_chan_stats stats; + /* offsets into dmaregs */ + int int_set, int_clear, td; +}; + +/* The following make access to common cpdma_ctlr params more readable */ +#define dmaregs params.dmaregs +#define num_chan params.num_chan + +/* various accessors */ +#define dma_reg_read(ctlr, ofs) __raw_readl((ctlr)->dmaregs + (ofs)) +#define chan_read(chan, fld) __raw_readl((chan)->fld) +#define desc_read(desc, fld) __raw_readl(&(desc)->fld) +#define dma_reg_write(ctlr, ofs, v) __raw_writel(v, (ctlr)->dmaregs + (ofs)) +#define chan_write(chan, fld, v) __raw_writel(v, (chan)->fld) +#define desc_write(desc, fld, v) __raw_writel((u32)(v), &(desc)->fld) + +/* + * Utility constructs for a cpdma descriptor pool. Some devices (e.g. davinci + * emac) have dedicated on-chip memory for these descriptors. Some other + * devices (e.g. cpsw switches) use plain old memory. Descriptor pools + * abstract out these details + */ +static struct cpdma_desc_pool * +cpdma_desc_pool_create(struct device *dev, u32 phys, int size, int align) +{ + int bitmap_size; + struct cpdma_desc_pool *pool; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return NULL; + + spin_lock_init(&pool->lock); + + pool->dev = dev; + pool->mem_size = size; + pool->desc_size = ALIGN(sizeof(struct cpdma_desc), align); + pool->num_desc = size / pool->desc_size; + + bitmap_size = (pool->num_desc / BITS_PER_LONG) * sizeof(long); + pool->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!pool->bitmap) + goto fail; + + if (phys) { + pool->phys = phys; + pool->iomap = ioremap(phys, size); + } else { + pool->cpumap = dma_alloc_coherent(dev, size, &pool->phys, + GFP_KERNEL); + pool->iomap = (void __force __iomem *)pool->cpumap; + } + + if (pool->iomap) + return pool; + +fail: + kfree(pool->bitmap); + kfree(pool); + return NULL; +} + +static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool) +{ + unsigned long flags; + + if (!pool) + return; + + spin_lock_irqsave(&pool->lock, flags); + WARN_ON(pool->used_desc); + kfree(pool->bitmap); + if (pool->cpumap) { + dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap, + pool->phys); + } else { + iounmap(pool->iomap); + } + spin_unlock_irqrestore(&pool->lock, flags); + kfree(pool); +} + +static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool, + struct cpdma_desc __iomem *desc) +{ + if (!desc) + return 0; + return pool->phys + (__force dma_addr_t)desc - + (__force dma_addr_t)pool->iomap; +} + +static inline struct cpdma_desc __iomem * +desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma) +{ + return dma ? pool->iomap + dma - pool->phys : NULL; +} + +static struct cpdma_desc __iomem * +cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc) +{ + unsigned long flags; + int index; + struct cpdma_desc __iomem *desc = NULL; + + spin_lock_irqsave(&pool->lock, flags); + + index = bitmap_find_next_zero_area(pool->bitmap, pool->num_desc, 0, + num_desc, 0); + if (index < pool->num_desc) { + bitmap_set(pool->bitmap, index, num_desc); + desc = pool->iomap + pool->desc_size * index; + pool->used_desc++; + } + + spin_unlock_irqrestore(&pool->lock, flags); + return desc; +} + +static void cpdma_desc_free(struct cpdma_desc_pool *pool, + struct cpdma_desc __iomem *desc, int num_desc) +{ + unsigned long flags, index; + + index = ((unsigned long)desc - (unsigned long)pool->iomap) / + pool->desc_size; + spin_lock_irqsave(&pool->lock, flags); + bitmap_clear(pool->bitmap, index, num_desc); + pool->used_desc--; + spin_unlock_irqrestore(&pool->lock, flags); +} + +struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) +{ + struct cpdma_ctlr *ctlr; + + ctlr = kzalloc(sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) + return NULL; + + ctlr->state = CPDMA_STATE_IDLE; + ctlr->params = *params; + ctlr->dev = params->dev; + spin_lock_init(&ctlr->lock); + + ctlr->pool = cpdma_desc_pool_create(ctlr->dev, + ctlr->params.desc_mem_phys, + ctlr->params.desc_mem_size, + ctlr->params.desc_align); + if (!ctlr->pool) { + kfree(ctlr); + return NULL; + } + + if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS)) + ctlr->num_chan = CPDMA_MAX_CHANNELS; + return ctlr; +} + +int cpdma_ctlr_start(struct cpdma_ctlr *ctlr) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&ctlr->lock, flags); + if (ctlr->state != CPDMA_STATE_IDLE) { + spin_unlock_irqrestore(&ctlr->lock, flags); + return -EBUSY; + } + + if (ctlr->params.has_soft_reset) { + unsigned long timeout = jiffies + HZ/10; + + dma_reg_write(ctlr, CPDMA_SOFTRESET, 1); + while (time_before(jiffies, timeout)) { + if (dma_reg_read(ctlr, CPDMA_SOFTRESET) == 0) + break; + } + WARN_ON(!time_before(jiffies, timeout)); + } + + for (i = 0; i < ctlr->num_chan; i++) { + __raw_writel(0, ctlr->params.txhdp + 4 * i); + __raw_writel(0, ctlr->params.rxhdp + 4 * i); + __raw_writel(0, ctlr->params.txcp + 4 * i); + __raw_writel(0, ctlr->params.rxcp + 4 * i); + } + + dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff); + dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff); + + dma_reg_write(ctlr, CPDMA_TXCONTROL, 1); + dma_reg_write(ctlr, CPDMA_RXCONTROL, 1); + + ctlr->state = CPDMA_STATE_ACTIVE; + + for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { + if (ctlr->channels[i]) + cpdma_chan_start(ctlr->channels[i]); + } + spin_unlock_irqrestore(&ctlr->lock, flags); + return 0; +} + +int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&ctlr->lock, flags); + if (ctlr->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&ctlr->lock, flags); + return -EINVAL; + } + + ctlr->state = CPDMA_STATE_TEARDOWN; + + for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { + if (ctlr->channels[i]) + cpdma_chan_stop(ctlr->channels[i]); + } + + dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff); + dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff); + + dma_reg_write(ctlr, CPDMA_TXCONTROL, 0); + dma_reg_write(ctlr, CPDMA_RXCONTROL, 0); + + ctlr->state = CPDMA_STATE_IDLE; + + spin_unlock_irqrestore(&ctlr->lock, flags); + return 0; +} + +int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr) +{ + struct device *dev = ctlr->dev; + unsigned long flags; + int i; + + spin_lock_irqsave(&ctlr->lock, flags); + + dev_info(dev, "CPDMA: state: %s", cpdma_state_str[ctlr->state]); + + dev_info(dev, "CPDMA: txidver: %x", + dma_reg_read(ctlr, CPDMA_TXIDVER)); + dev_info(dev, "CPDMA: txcontrol: %x", + dma_reg_read(ctlr, CPDMA_TXCONTROL)); + dev_info(dev, "CPDMA: txteardown: %x", + dma_reg_read(ctlr, CPDMA_TXTEARDOWN)); + dev_info(dev, "CPDMA: rxidver: %x", + dma_reg_read(ctlr, CPDMA_RXIDVER)); + dev_info(dev, "CPDMA: rxcontrol: %x", + dma_reg_read(ctlr, CPDMA_RXCONTROL)); + dev_info(dev, "CPDMA: softreset: %x", + dma_reg_read(ctlr, CPDMA_SOFTRESET)); + dev_info(dev, "CPDMA: rxteardown: %x", + dma_reg_read(ctlr, CPDMA_RXTEARDOWN)); + dev_info(dev, "CPDMA: txintstatraw: %x", + dma_reg_read(ctlr, CPDMA_TXINTSTATRAW)); + dev_info(dev, "CPDMA: txintstatmasked: %x", + dma_reg_read(ctlr, CPDMA_TXINTSTATMASKED)); + dev_info(dev, "CPDMA: txintmaskset: %x", + dma_reg_read(ctlr, CPDMA_TXINTMASKSET)); + dev_info(dev, "CPDMA: txintmaskclear: %x", + dma_reg_read(ctlr, CPDMA_TXINTMASKCLEAR)); + dev_info(dev, "CPDMA: macinvector: %x", + dma_reg_read(ctlr, CPDMA_MACINVECTOR)); + dev_info(dev, "CPDMA: maceoivector: %x", + dma_reg_read(ctlr, CPDMA_MACEOIVECTOR)); + dev_info(dev, "CPDMA: rxintstatraw: %x", + dma_reg_read(ctlr, CPDMA_RXINTSTATRAW)); + dev_info(dev, "CPDMA: rxintstatmasked: %x", + dma_reg_read(ctlr, CPDMA_RXINTSTATMASKED)); + dev_info(dev, "CPDMA: rxintmaskset: %x", + dma_reg_read(ctlr, CPDMA_RXINTMASKSET)); + dev_info(dev, "CPDMA: rxintmaskclear: %x", + dma_reg_read(ctlr, CPDMA_RXINTMASKCLEAR)); + dev_info(dev, "CPDMA: dmaintstatraw: %x", + dma_reg_read(ctlr, CPDMA_DMAINTSTATRAW)); + dev_info(dev, "CPDMA: dmaintstatmasked: %x", + dma_reg_read(ctlr, CPDMA_DMAINTSTATMASKED)); + dev_info(dev, "CPDMA: dmaintmaskset: %x", + dma_reg_read(ctlr, CPDMA_DMAINTMASKSET)); + dev_info(dev, "CPDMA: dmaintmaskclear: %x", + dma_reg_read(ctlr, CPDMA_DMAINTMASKCLEAR)); + + if (!ctlr->params.has_ext_regs) { + dev_info(dev, "CPDMA: dmacontrol: %x", + dma_reg_read(ctlr, CPDMA_DMACONTROL)); + dev_info(dev, "CPDMA: dmastatus: %x", + dma_reg_read(ctlr, CPDMA_DMASTATUS)); + dev_info(dev, "CPDMA: rxbuffofs: %x", + dma_reg_read(ctlr, CPDMA_RXBUFFOFS)); + } + + for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) + if (ctlr->channels[i]) + cpdma_chan_dump(ctlr->channels[i]); + + spin_unlock_irqrestore(&ctlr->lock, flags); + return 0; +} + +int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr) +{ + unsigned long flags; + int ret = 0, i; + + if (!ctlr) + return -EINVAL; + + spin_lock_irqsave(&ctlr->lock, flags); + if (ctlr->state != CPDMA_STATE_IDLE) + cpdma_ctlr_stop(ctlr); + + for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { + if (ctlr->channels[i]) + cpdma_chan_destroy(ctlr->channels[i]); + } + + cpdma_desc_pool_destroy(ctlr->pool); + spin_unlock_irqrestore(&ctlr->lock, flags); + kfree(ctlr); + return ret; +} + +int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable) +{ + unsigned long flags; + int i, reg; + + spin_lock_irqsave(&ctlr->lock, flags); + if (ctlr->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&ctlr->lock, flags); + return -EINVAL; + } + + reg = enable ? CPDMA_DMAINTMASKSET : CPDMA_DMAINTMASKCLEAR; + dma_reg_write(ctlr, reg, CPDMA_DMAINT_HOSTERR); + + for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { + if (ctlr->channels[i]) + cpdma_chan_int_ctrl(ctlr->channels[i], enable); + } + + spin_unlock_irqrestore(&ctlr->lock, flags); + return 0; +} + +void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr) +{ + dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0); +} + +struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, + cpdma_handler_fn handler) +{ + struct cpdma_chan *chan; + int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4; + unsigned long flags; + + if (__chan_linear(chan_num) >= ctlr->num_chan) + return NULL; + + ret = -ENOMEM; + chan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + goto err_chan_alloc; + + spin_lock_irqsave(&ctlr->lock, flags); + ret = -EBUSY; + if (ctlr->channels[chan_num]) + goto err_chan_busy; + + chan->ctlr = ctlr; + chan->state = CPDMA_STATE_IDLE; + chan->chan_num = chan_num; + chan->handler = handler; + + if (is_rx_chan(chan)) { + chan->hdp = ctlr->params.rxhdp + offset; + chan->cp = ctlr->params.rxcp + offset; + chan->rxfree = ctlr->params.rxfree + offset; + chan->int_set = CPDMA_RXINTMASKSET; + chan->int_clear = CPDMA_RXINTMASKCLEAR; + chan->td = CPDMA_RXTEARDOWN; + chan->dir = DMA_FROM_DEVICE; + } else { + chan->hdp = ctlr->params.txhdp + offset; + chan->cp = ctlr->params.txcp + offset; + chan->int_set = CPDMA_TXINTMASKSET; + chan->int_clear = CPDMA_TXINTMASKCLEAR; + chan->td = CPDMA_TXTEARDOWN; + chan->dir = DMA_TO_DEVICE; + } + chan->mask = BIT(chan_linear(chan)); + + spin_lock_init(&chan->lock); + + ctlr->channels[chan_num] = chan; + spin_unlock_irqrestore(&ctlr->lock, flags); + return chan; + +err_chan_busy: + spin_unlock_irqrestore(&ctlr->lock, flags); + kfree(chan); +err_chan_alloc: + return ERR_PTR(ret); +} + +int cpdma_chan_destroy(struct cpdma_chan *chan) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + unsigned long flags; + + if (!chan) + return -EINVAL; + + spin_lock_irqsave(&ctlr->lock, flags); + if (chan->state != CPDMA_STATE_IDLE) + cpdma_chan_stop(chan); + ctlr->channels[chan->chan_num] = NULL; + spin_unlock_irqrestore(&ctlr->lock, flags); + kfree(chan); + return 0; +} + +int cpdma_chan_get_stats(struct cpdma_chan *chan, + struct cpdma_chan_stats *stats) +{ + unsigned long flags; + if (!chan) + return -EINVAL; + spin_lock_irqsave(&chan->lock, flags); + memcpy(stats, &chan->stats, sizeof(*stats)); + spin_unlock_irqrestore(&chan->lock, flags); + return 0; +} + +int cpdma_chan_dump(struct cpdma_chan *chan) +{ + unsigned long flags; + struct device *dev = chan->ctlr->dev; + + spin_lock_irqsave(&chan->lock, flags); + + dev_info(dev, "channel %d (%s %d) state %s", + chan->chan_num, is_rx_chan(chan) ? "rx" : "tx", + chan_linear(chan), cpdma_state_str[chan->state]); + dev_info(dev, "\thdp: %x\n", chan_read(chan, hdp)); + dev_info(dev, "\tcp: %x\n", chan_read(chan, cp)); + if (chan->rxfree) { + dev_info(dev, "\trxfree: %x\n", + chan_read(chan, rxfree)); + } + + dev_info(dev, "\tstats head_enqueue: %d\n", + chan->stats.head_enqueue); + dev_info(dev, "\tstats tail_enqueue: %d\n", + chan->stats.tail_enqueue); + dev_info(dev, "\tstats pad_enqueue: %d\n", + chan->stats.pad_enqueue); + dev_info(dev, "\tstats misqueued: %d\n", + chan->stats.misqueued); + dev_info(dev, "\tstats desc_alloc_fail: %d\n", + chan->stats.desc_alloc_fail); + dev_info(dev, "\tstats pad_alloc_fail: %d\n", + chan->stats.pad_alloc_fail); + dev_info(dev, "\tstats runt_receive_buff: %d\n", + chan->stats.runt_receive_buff); + dev_info(dev, "\tstats runt_transmit_buff: %d\n", + chan->stats.runt_transmit_buff); + dev_info(dev, "\tstats empty_dequeue: %d\n", + chan->stats.empty_dequeue); + dev_info(dev, "\tstats busy_dequeue: %d\n", + chan->stats.busy_dequeue); + dev_info(dev, "\tstats good_dequeue: %d\n", + chan->stats.good_dequeue); + dev_info(dev, "\tstats requeue: %d\n", + chan->stats.requeue); + dev_info(dev, "\tstats teardown_dequeue: %d\n", + chan->stats.teardown_dequeue); + + spin_unlock_irqrestore(&chan->lock, flags); + return 0; +} + +static void __cpdma_chan_submit(struct cpdma_chan *chan, + struct cpdma_desc __iomem *desc) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc __iomem *prev = chan->tail; + struct cpdma_desc_pool *pool = ctlr->pool; + dma_addr_t desc_dma; + u32 mode; + + desc_dma = desc_phys(pool, desc); + + /* simple case - idle channel */ + if (!chan->head) { + chan->stats.head_enqueue++; + chan->head = desc; + chan->tail = desc; + if (chan->state == CPDMA_STATE_ACTIVE) + chan_write(chan, hdp, desc_dma); + return; + } + + /* first chain the descriptor at the tail of the list */ + desc_write(prev, hw_next, desc_dma); + chan->tail = desc; + chan->stats.tail_enqueue++; + + /* next check if EOQ has been triggered already */ + mode = desc_read(prev, hw_mode); + if (((mode & (CPDMA_DESC_EOQ | CPDMA_DESC_OWNER)) == CPDMA_DESC_EOQ) && + (chan->state == CPDMA_STATE_ACTIVE)) { + desc_write(prev, hw_mode, mode & ~CPDMA_DESC_EOQ); + chan_write(chan, hdp, desc_dma); + chan->stats.misqueued++; + } +} + +int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, + int len, gfp_t gfp_mask) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc __iomem *desc; + dma_addr_t buffer; + unsigned long flags; + u32 mode; + int ret = 0; + + spin_lock_irqsave(&chan->lock, flags); + + if (chan->state == CPDMA_STATE_TEARDOWN) { + ret = -EINVAL; + goto unlock_ret; + } + + desc = cpdma_desc_alloc(ctlr->pool, 1); + if (!desc) { + chan->stats.desc_alloc_fail++; + ret = -ENOMEM; + goto unlock_ret; + } + + if (len < ctlr->params.min_packet_size) { + len = ctlr->params.min_packet_size; + chan->stats.runt_transmit_buff++; + } + + buffer = dma_map_single(ctlr->dev, data, len, chan->dir); + mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; + + desc_write(desc, hw_next, 0); + desc_write(desc, hw_buffer, buffer); + desc_write(desc, hw_len, len); + desc_write(desc, hw_mode, mode | len); + desc_write(desc, sw_token, token); + desc_write(desc, sw_buffer, buffer); + desc_write(desc, sw_len, len); + + __cpdma_chan_submit(chan, desc); + + if (chan->state == CPDMA_STATE_ACTIVE && chan->rxfree) + chan_write(chan, rxfree, 1); + + chan->count++; + +unlock_ret: + spin_unlock_irqrestore(&chan->lock, flags); + return ret; +} + +static void __cpdma_chan_free(struct cpdma_chan *chan, + struct cpdma_desc __iomem *desc, + int outlen, int status) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc_pool *pool = ctlr->pool; + dma_addr_t buff_dma; + int origlen; + void *token; + + token = (void *)desc_read(desc, sw_token); + buff_dma = desc_read(desc, sw_buffer); + origlen = desc_read(desc, sw_len); + + dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir); + cpdma_desc_free(pool, desc, 1); + (*chan->handler)(token, outlen, status); +} + +static int __cpdma_chan_process(struct cpdma_chan *chan) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc __iomem *desc; + int status, outlen; + struct cpdma_desc_pool *pool = ctlr->pool; + dma_addr_t desc_dma; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + desc = chan->head; + if (!desc) { + chan->stats.empty_dequeue++; + status = -ENOENT; + goto unlock_ret; + } + desc_dma = desc_phys(pool, desc); + + status = __raw_readl(&desc->hw_mode); + outlen = status & 0x7ff; + if (status & CPDMA_DESC_OWNER) { + chan->stats.busy_dequeue++; + status = -EBUSY; + goto unlock_ret; + } + status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE); + + chan->head = desc_from_phys(pool, desc_read(desc, hw_next)); + chan_write(chan, cp, desc_dma); + chan->count--; + chan->stats.good_dequeue++; + + if (status & CPDMA_DESC_EOQ) { + chan->stats.requeue++; + chan_write(chan, hdp, desc_phys(pool, chan->head)); + } + + spin_unlock_irqrestore(&chan->lock, flags); + + __cpdma_chan_free(chan, desc, outlen, status); + return status; + +unlock_ret: + spin_unlock_irqrestore(&chan->lock, flags); + return status; +} + +int cpdma_chan_process(struct cpdma_chan *chan, int quota) +{ + int used = 0, ret = 0; + + if (chan->state != CPDMA_STATE_ACTIVE) + return -EINVAL; + + while (used < quota) { + ret = __cpdma_chan_process(chan); + if (ret < 0) + break; + used++; + } + return used; +} + +int cpdma_chan_start(struct cpdma_chan *chan) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc_pool *pool = ctlr->pool; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->state != CPDMA_STATE_IDLE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EBUSY; + } + if (ctlr->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + dma_reg_write(ctlr, chan->int_set, chan->mask); + chan->state = CPDMA_STATE_ACTIVE; + if (chan->head) { + chan_write(chan, hdp, desc_phys(pool, chan->head)); + if (chan->rxfree) + chan_write(chan, rxfree, chan->count); + } + + spin_unlock_irqrestore(&chan->lock, flags); + return 0; +} + +int cpdma_chan_stop(struct cpdma_chan *chan) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc_pool *pool = ctlr->pool; + unsigned long flags; + int ret; + unsigned long timeout; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + + chan->state = CPDMA_STATE_TEARDOWN; + dma_reg_write(ctlr, chan->int_clear, chan->mask); + + /* trigger teardown */ + dma_reg_write(ctlr, chan->td, chan->chan_num); + + /* wait for teardown complete */ + timeout = jiffies + HZ/10; /* 100 msec */ + while (time_before(jiffies, timeout)) { + u32 cp = chan_read(chan, cp); + if ((cp & CPDMA_TEARDOWN_VALUE) == CPDMA_TEARDOWN_VALUE) + break; + cpu_relax(); + } + WARN_ON(!time_before(jiffies, timeout)); + chan_write(chan, cp, CPDMA_TEARDOWN_VALUE); + + /* handle completed packets */ + do { + ret = __cpdma_chan_process(chan); + if (ret < 0) + break; + } while ((ret & CPDMA_DESC_TD_COMPLETE) == 0); + + /* remaining packets haven't been tx/rx'ed, clean them up */ + while (chan->head) { + struct cpdma_desc __iomem *desc = chan->head; + dma_addr_t next_dma; + + next_dma = desc_read(desc, hw_next); + chan->head = desc_from_phys(pool, next_dma); + chan->stats.teardown_dequeue++; + + /* issue callback without locks held */ + spin_unlock_irqrestore(&chan->lock, flags); + __cpdma_chan_free(chan, desc, 0, -ENOSYS); + spin_lock_irqsave(&chan->lock, flags); + } + + chan->state = CPDMA_STATE_IDLE; + spin_unlock_irqrestore(&chan->lock, flags); + return 0; +} + +int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + + dma_reg_write(chan->ctlr, enable ? chan->int_set : chan->int_clear, + chan->mask); + spin_unlock_irqrestore(&chan->lock, flags); + + return 0; +} + +struct cpdma_control_info { + u32 reg; + u32 shift, mask; + int access; +#define ACCESS_RO BIT(0) +#define ACCESS_WO BIT(1) +#define ACCESS_RW (ACCESS_RO | ACCESS_WO) +}; + +struct cpdma_control_info controls[] = { + [CPDMA_CMD_IDLE] = {CPDMA_DMACONTROL, 3, 1, ACCESS_WO}, + [CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL, 4, 1, ACCESS_RW}, + [CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL, 2, 1, ACCESS_RW}, + [CPDMA_RX_OWNERSHIP_FLIP] = {CPDMA_DMACONTROL, 1, 1, ACCESS_RW}, + [CPDMA_TX_PRIO_FIXED] = {CPDMA_DMACONTROL, 0, 1, ACCESS_RW}, + [CPDMA_STAT_IDLE] = {CPDMA_DMASTATUS, 31, 1, ACCESS_RO}, + [CPDMA_STAT_TX_ERR_CODE] = {CPDMA_DMASTATUS, 20, 0xf, ACCESS_RW}, + [CPDMA_STAT_TX_ERR_CHAN] = {CPDMA_DMASTATUS, 16, 0x7, ACCESS_RW}, + [CPDMA_STAT_RX_ERR_CODE] = {CPDMA_DMASTATUS, 12, 0xf, ACCESS_RW}, + [CPDMA_STAT_RX_ERR_CHAN] = {CPDMA_DMASTATUS, 8, 0x7, ACCESS_RW}, + [CPDMA_RX_BUFFER_OFFSET] = {CPDMA_RXBUFFOFS, 0, 0xffff, ACCESS_RW}, +}; + +int cpdma_control_get(struct cpdma_ctlr *ctlr, int control) +{ + unsigned long flags; + struct cpdma_control_info *info = &controls[control]; + int ret; + + spin_lock_irqsave(&ctlr->lock, flags); + + ret = -ENOTSUPP; + if (!ctlr->params.has_ext_regs) + goto unlock_ret; + + ret = -EINVAL; + if (ctlr->state != CPDMA_STATE_ACTIVE) + goto unlock_ret; + + ret = -ENOENT; + if (control < 0 || control >= ARRAY_SIZE(controls)) + goto unlock_ret; + + ret = -EPERM; + if ((info->access & ACCESS_RO) != ACCESS_RO) + goto unlock_ret; + + ret = (dma_reg_read(ctlr, info->reg) >> info->shift) & info->mask; + +unlock_ret: + spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; +} + +int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) +{ + unsigned long flags; + struct cpdma_control_info *info = &controls[control]; + int ret; + u32 val; + + spin_lock_irqsave(&ctlr->lock, flags); + + ret = -ENOTSUPP; + if (!ctlr->params.has_ext_regs) + goto unlock_ret; + + ret = -EINVAL; + if (ctlr->state != CPDMA_STATE_ACTIVE) + goto unlock_ret; + + ret = -ENOENT; + if (control < 0 || control >= ARRAY_SIZE(controls)) + goto unlock_ret; + + ret = -EPERM; + if ((info->access & ACCESS_WO) != ACCESS_WO) + goto unlock_ret; + + val = dma_reg_read(ctlr, info->reg); + val &= ~(info->mask << info->shift); + val |= (value & info->mask) << info->shift; + dma_reg_write(ctlr, info->reg, val); + ret = 0; + +unlock_ret: + spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; +} diff --git a/drivers/net/davinci_cpdma.h b/drivers/net/davinci_cpdma.h new file mode 100644 index 00000000000..868e50ebde4 --- /dev/null +++ b/drivers/net/davinci_cpdma.h @@ -0,0 +1,108 @@ +/* + * Texas Instruments CPDMA Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __DAVINCI_CPDMA_H__ +#define __DAVINCI_CPDMA_H__ + +#define CPDMA_MAX_CHANNELS BITS_PER_LONG + +#define tx_chan_num(chan) (chan) +#define rx_chan_num(chan) ((chan) + CPDMA_MAX_CHANNELS) +#define is_rx_chan(chan) ((chan)->chan_num >= CPDMA_MAX_CHANNELS) +#define is_tx_chan(chan) (!is_rx_chan(chan)) +#define __chan_linear(chan_num) ((chan_num) & (CPDMA_MAX_CHANNELS - 1)) +#define chan_linear(chan) __chan_linear((chan)->chan_num) + +struct cpdma_params { + struct device *dev; + void __iomem *dmaregs; + void __iomem *txhdp, *rxhdp, *txcp, *rxcp; + void __iomem *rxthresh, *rxfree; + int num_chan; + bool has_soft_reset; + int min_packet_size; + u32 desc_mem_phys; + int desc_mem_size; + int desc_align; + + /* + * Some instances of embedded cpdma controllers have extra control and + * status registers. The following flag enables access to these + * "extended" registers. + */ + bool has_ext_regs; +}; + +struct cpdma_chan_stats { + u32 head_enqueue; + u32 tail_enqueue; + u32 pad_enqueue; + u32 misqueued; + u32 desc_alloc_fail; + u32 pad_alloc_fail; + u32 runt_receive_buff; + u32 runt_transmit_buff; + u32 empty_dequeue; + u32 busy_dequeue; + u32 good_dequeue; + u32 requeue; + u32 teardown_dequeue; +}; + +struct cpdma_ctlr; +struct cpdma_chan; + +typedef void (*cpdma_handler_fn)(void *token, int len, int status); + +struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params); +int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr); +int cpdma_ctlr_start(struct cpdma_ctlr *ctlr); +int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr); +int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr); + +struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, + cpdma_handler_fn handler); +int cpdma_chan_destroy(struct cpdma_chan *chan); +int cpdma_chan_start(struct cpdma_chan *chan); +int cpdma_chan_stop(struct cpdma_chan *chan); +int cpdma_chan_dump(struct cpdma_chan *chan); + +int cpdma_chan_get_stats(struct cpdma_chan *chan, + struct cpdma_chan_stats *stats); +int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, + int len, gfp_t gfp_mask); +int cpdma_chan_process(struct cpdma_chan *chan, int quota); + +int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable); +void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr); +int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable); + +enum cpdma_control { + CPDMA_CMD_IDLE, /* write-only */ + CPDMA_COPY_ERROR_FRAMES, /* read-write */ + CPDMA_RX_OFF_LEN_UPDATE, /* read-write */ + CPDMA_RX_OWNERSHIP_FLIP, /* read-write */ + CPDMA_TX_PRIO_FIXED, /* read-write */ + CPDMA_STAT_IDLE, /* read-only */ + CPDMA_STAT_TX_ERR_CHAN, /* read-only */ + CPDMA_STAT_TX_ERR_CODE, /* read-only */ + CPDMA_STAT_RX_ERR_CHAN, /* read-only */ + CPDMA_STAT_RX_ERR_CODE, /* read-only */ + CPDMA_RX_BUFFER_OFFSET, /* read-write */ +}; + +int cpdma_control_get(struct cpdma_ctlr *ctlr, int control); +int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value); + +#endif diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 7fbd052ddb0..2a628d17d17 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -63,6 +63,8 @@ #include <asm/irq.h> #include <asm/page.h> +#include "davinci_cpdma.h" + static int debug_level; module_param(debug_level, int, 0); MODULE_PARM_DESC(debug_level, "DaVinci EMAC debug level (NETIF_MSG bits)"); @@ -113,7 +115,7 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_DEF_MAX_FRAME_SIZE (1500 + 14 + 4 + 4) #define EMAC_DEF_TX_CH (0) /* Default 0th channel */ #define EMAC_DEF_RX_CH (0) /* Default 0th channel */ -#define EMAC_DEF_MDIO_TICK_MS (10) /* typically 1 tick=1 ms) */ +#define EMAC_DEF_RX_NUM_DESC (128) #define EMAC_DEF_MAX_TX_CH (1) /* Max TX channels configured */ #define EMAC_DEF_MAX_RX_CH (1) /* Max RX channels configured */ #define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */ @@ -125,7 +127,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; /* EMAC register related defines */ #define EMAC_ALL_MULTI_REG_VALUE (0xFFFFFFFF) #define EMAC_NUM_MULTICAST_BITS (64) -#define EMAC_TEARDOWN_VALUE (0xFFFFFFFC) #define EMAC_TX_CONTROL_TX_ENABLE_VAL (0x1) #define EMAC_RX_CONTROL_RX_ENABLE_VAL (0x1) #define EMAC_MAC_HOST_ERR_INTMASK_VAL (0x2) @@ -212,24 +213,10 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_DEF_MAX_MULTICAST_ADDRESSES (64) /* Max mcast addr's */ /* EMAC Peripheral Device Register Memory Layout structure */ -#define EMAC_TXIDVER 0x0 -#define EMAC_TXCONTROL 0x4 -#define EMAC_TXTEARDOWN 0x8 -#define EMAC_RXIDVER 0x10 -#define EMAC_RXCONTROL 0x14 -#define EMAC_RXTEARDOWN 0x18 -#define EMAC_TXINTSTATRAW 0x80 -#define EMAC_TXINTSTATMASKED 0x84 -#define EMAC_TXINTMASKSET 0x88 -#define EMAC_TXINTMASKCLEAR 0x8C #define EMAC_MACINVECTOR 0x90 #define EMAC_DM646X_MACEOIVECTOR 0x94 -#define EMAC_RXINTSTATRAW 0xA0 -#define EMAC_RXINTSTATMASKED 0xA4 -#define EMAC_RXINTMASKSET 0xA8 -#define EMAC_RXINTMASKCLEAR 0xAC #define EMAC_MACINTSTATRAW 0xB0 #define EMAC_MACINTSTATMASKED 0xB4 #define EMAC_MACINTMASKSET 0xB8 @@ -256,12 +243,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_MACADDRHI 0x504 #define EMAC_MACINDEX 0x508 -/* EMAC HDP and Completion registors */ -#define EMAC_TXHDP(ch) (0x600 + (ch * 4)) -#define EMAC_RXHDP(ch) (0x620 + (ch * 4)) -#define EMAC_TXCP(ch) (0x640 + (ch * 4)) -#define EMAC_RXCP(ch) (0x660 + (ch * 4)) - /* EMAC statistics registers */ #define EMAC_RXGOODFRAMES 0x200 #define EMAC_RXBCASTFRAMES 0x204 @@ -303,25 +284,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_DM644X_INTMIN_INTVL 0x1 #define EMAC_DM644X_INTMAX_INTVL (EMAC_DM644X_EWINTCNT_MASK) -/* EMAC MDIO related */ -/* Mask & Control defines */ -#define MDIO_CONTROL_CLKDIV (0xFF) -#define MDIO_CONTROL_ENABLE BIT(30) -#define MDIO_USERACCESS_GO BIT(31) -#define MDIO_USERACCESS_WRITE BIT(30) -#define MDIO_USERACCESS_READ (0) -#define MDIO_USERACCESS_REGADR (0x1F << 21) -#define MDIO_USERACCESS_PHYADR (0x1F << 16) -#define MDIO_USERACCESS_DATA (0xFFFF) -#define MDIO_USERPHYSEL_LINKSEL BIT(7) -#define MDIO_VER_MODID (0xFFFF << 16) -#define MDIO_VER_REVMAJ (0xFF << 8) -#define MDIO_VER_REVMIN (0xFF) - -#define MDIO_USERACCESS(inst) (0x80 + (inst * 8)) -#define MDIO_USERPHYSEL(inst) (0x84 + (inst * 8)) -#define MDIO_CONTROL (0x04) - /* EMAC DM646X control module registers */ #define EMAC_DM646X_CMINTCTRL 0x0C #define EMAC_DM646X_CMRXINTEN 0x14 @@ -345,120 +307,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; /* EMAC Stats Clear Mask */ #define EMAC_STATS_CLR_MASK (0xFFFFFFFF) -/** net_buf_obj: EMAC network bufferdata structure - * - * EMAC network buffer data structure - */ -struct emac_netbufobj { - void *buf_token; - char *data_ptr; - int length; -}; - -/** net_pkt_obj: EMAC network packet data structure - * - * EMAC network packet data structure - supports buffer list (for future) - */ -struct emac_netpktobj { - void *pkt_token; /* data token may hold tx/rx chan id */ - struct emac_netbufobj *buf_list; /* array of network buffer objects */ - int num_bufs; - int pkt_length; -}; - -/** emac_tx_bd: EMAC TX Buffer descriptor data structure - * - * EMAC TX Buffer descriptor data structure - */ -struct emac_tx_bd { - int h_next; - int buff_ptr; - int off_b_len; - int mode; /* SOP, EOP, ownership, EOQ, teardown,Qstarv, length */ - struct emac_tx_bd __iomem *next; - void *buf_token; -}; - -/** emac_txch: EMAC TX Channel data structure - * - * EMAC TX Channel data structure - */ -struct emac_txch { - /* Config related */ - u32 num_bd; - u32 service_max; - - /* CPPI specific */ - u32 alloc_size; - void __iomem *bd_mem; - struct emac_tx_bd __iomem *bd_pool_head; - struct emac_tx_bd __iomem *active_queue_head; - struct emac_tx_bd __iomem *active_queue_tail; - struct emac_tx_bd __iomem *last_hw_bdprocessed; - u32 queue_active; - u32 teardown_pending; - u32 *tx_complete; - - /** statistics */ - u32 proc_count; /* TX: # of times emac_tx_bdproc is called */ - u32 mis_queued_packets; - u32 queue_reinit; - u32 end_of_queue_add; - u32 out_of_tx_bd; - u32 no_active_pkts; /* IRQ when there were no packets to process */ - u32 active_queue_count; -}; - -/** emac_rx_bd: EMAC RX Buffer descriptor data structure - * - * EMAC RX Buffer descriptor data structure - */ -struct emac_rx_bd { - int h_next; - int buff_ptr; - int off_b_len; - int mode; - struct emac_rx_bd __iomem *next; - void *data_ptr; - void *buf_token; -}; - -/** emac_rxch: EMAC RX Channel data structure - * - * EMAC RX Channel data structure - */ -struct emac_rxch { - /* configuration info */ - u32 num_bd; - u32 service_max; - u32 buf_size; - char mac_addr[6]; - - /** CPPI specific */ - u32 alloc_size; - void __iomem *bd_mem; - struct emac_rx_bd __iomem *bd_pool_head; - struct emac_rx_bd __iomem *active_queue_head; - struct emac_rx_bd __iomem *active_queue_tail; - u32 queue_active; - u32 teardown_pending; - - /* packet and buffer objects */ - struct emac_netpktobj pkt_queue; - struct emac_netbufobj buf_queue; - - /** statistics */ - u32 proc_count; /* number of times emac_rx_bdproc is called */ - u32 processed_bd; - u32 recycled_bd; - u32 out_of_rx_bd; - u32 out_of_rx_buffers; - u32 queue_reinit; - u32 end_of_queue_add; - u32 end_of_queue; - u32 mis_queued_packets; -}; - /* emac_priv: EMAC private data structure * * EMAC adapter private data structure @@ -469,17 +317,13 @@ struct emac_priv { struct platform_device *pdev; struct napi_struct napi; char mac_addr[6]; - spinlock_t tx_lock; - spinlock_t rx_lock; void __iomem *remap_addr; u32 emac_base_phys; void __iomem *emac_base; void __iomem *ctrl_base; - void __iomem *emac_ctrl_ram; - u32 ctrl_ram_size; - u32 hw_ram_addr; - struct emac_txch *txch[EMAC_DEF_MAX_TX_CH]; - struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH]; + struct cpdma_ctlr *dma; + struct cpdma_chan *txchan; + struct cpdma_chan *rxchan; u32 link; /* 1=link on, 0=link off */ u32 speed; /* 0=Auto Neg, 1=No PHY, 10,100, 1000 - mbps */ u32 duplex; /* Link duplex: 0=Half, 1=Full */ @@ -493,13 +337,7 @@ struct emac_priv { u32 mac_hash2; u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS]; u32 rx_addr_type; - /* periodic timer required for MDIO polling */ - struct timer_list periodic_timer; - u32 periodic_ticks; - u32 timer_active; - u32 phy_mask; - /* mii_bus,phy members */ - struct mii_bus *mii_bus; + const char *phy_id; struct phy_device *phydev; spinlock_t lock; /*platform specific members*/ @@ -510,19 +348,6 @@ struct emac_priv { /* clock frequency for EMAC */ static struct clk *emac_clk; static unsigned long emac_bus_frequency; -static unsigned long mdio_max_freq; - -#define emac_virt_to_phys(addr, priv) \ - (((u32 __force)(addr) - (u32 __force)(priv->emac_ctrl_ram)) \ - + priv->hw_ram_addr) - -/* Cache macros - Packet buffers would be from skb pool which is cached */ -#define EMAC_VIRT_NOCACHE(addr) (addr) - -/* DM644x does not have BD's in cached memory - so no cache functions */ -#define BD_CACHE_INVALIDATE(addr, size) -#define BD_CACHE_WRITEBACK(addr, size) -#define BD_CACHE_WRITEBACK_INVALIDATE(addr, size) /* EMAC TX Host Error description strings */ static char *emac_txhost_errcodes[16] = { @@ -548,9 +373,6 @@ static char *emac_rxhost_errcodes[16] = { #define emac_ctrl_read(reg) ioread32((priv->ctrl_base + (reg))) #define emac_ctrl_write(reg, val) iowrite32(val, (priv->ctrl_base + (reg))) -#define emac_mdio_read(reg) ioread32(bus->priv + (reg)) -#define emac_mdio_write(reg, val) iowrite32(val, (bus->priv + (reg))) - /** * emac_dump_regs: Dump important EMAC registers to debug terminal * @priv: The DaVinci EMAC private adapter structure @@ -569,20 +391,6 @@ static void emac_dump_regs(struct emac_priv *priv) emac_ctrl_read(EMAC_CTRL_EWCTL), emac_ctrl_read(EMAC_CTRL_EWINTTCNT)); } - dev_info(emac_dev, "EMAC: TXID: %08X %s, RXID: %08X %s\n", - emac_read(EMAC_TXIDVER), - ((emac_read(EMAC_TXCONTROL)) ? "enabled" : "disabled"), - emac_read(EMAC_RXIDVER), - ((emac_read(EMAC_RXCONTROL)) ? "enabled" : "disabled")); - dev_info(emac_dev, "EMAC: TXIntRaw:%08X, TxIntMasked: %08X, "\ - "TxIntMasSet: %08X\n", emac_read(EMAC_TXINTSTATRAW), - emac_read(EMAC_TXINTSTATMASKED), emac_read(EMAC_TXINTMASKSET)); - dev_info(emac_dev, "EMAC: RXIntRaw:%08X, RxIntMasked: %08X, "\ - "RxIntMasSet: %08X\n", emac_read(EMAC_RXINTSTATRAW), - emac_read(EMAC_RXINTSTATMASKED), emac_read(EMAC_RXINTMASKSET)); - dev_info(emac_dev, "EMAC: MacIntRaw:%08X, MacIntMasked: %08X, "\ - "MacInVector=%08X\n", emac_read(EMAC_MACINTSTATRAW), - emac_read(EMAC_MACINTSTATMASKED), emac_read(EMAC_MACINVECTOR)); dev_info(emac_dev, "EMAC: EmuControl:%08X, FifoControl: %08X\n", emac_read(EMAC_EMCONTROL), emac_read(EMAC_FIFOCONTROL)); dev_info(emac_dev, "EMAC: MBPEnable:%08X, RXUnicastSet: %08X, "\ @@ -591,8 +399,6 @@ static void emac_dump_regs(struct emac_priv *priv) dev_info(emac_dev, "EMAC: MacControl:%08X, MacStatus: %08X, "\ "MacConfig=%08X\n", emac_read(EMAC_MACCONTROL), emac_read(EMAC_MACSTATUS), emac_read(EMAC_MACCONFIG)); - dev_info(emac_dev, "EMAC: TXHDP[0]:%08X, RXHDP[0]: %08X\n", - emac_read(EMAC_TXHDP(0)), emac_read(EMAC_RXHDP(0))); dev_info(emac_dev, "EMAC Statistics\n"); dev_info(emac_dev, "EMAC: rx_good_frames:%d\n", emac_read(EMAC_RXGOODFRAMES)); @@ -654,11 +460,10 @@ static void emac_dump_regs(struct emac_priv *priv) emac_read(EMAC_RXMOFOVERRUNS)); dev_info(emac_dev, "EMAC: rx_dma_overruns:%d\n", emac_read(EMAC_RXDMAOVERRUNS)); + + cpdma_ctlr_dump(priv->dma); } -/************************************************************************* - * EMAC MDIO/Phy Functionality - *************************************************************************/ /** * emac_get_drvinfo: Get EMAC driver information * @ndev: The DaVinci EMAC network adapter @@ -686,7 +491,7 @@ static int emac_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) { struct emac_priv *priv = netdev_priv(ndev); - if (priv->phy_mask) + if (priv->phydev) return phy_ethtool_gset(priv->phydev, ecmd); else return -EOPNOTSUPP; @@ -704,7 +509,7 @@ static int emac_get_settings(struct net_device *ndev, static int emac_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) { struct emac_priv *priv = netdev_priv(ndev); - if (priv->phy_mask) + if (priv->phydev) return phy_ethtool_sset(priv->phydev, ecmd); else return -EOPNOTSUPP; @@ -841,7 +646,7 @@ static void emac_update_phystatus(struct emac_priv *priv) mac_control = emac_read(EMAC_MACCONTROL); cur_duplex = (mac_control & EMAC_MACCONTROL_FULLDUPLEXEN) ? DUPLEX_FULL : DUPLEX_HALF; - if (priv->phy_mask) + if (priv->phydev) new_duplex = priv->phydev->duplex; else new_duplex = DUPLEX_FULL; @@ -1184,371 +989,68 @@ static irqreturn_t emac_irq(int irq, void *dev_id) return IRQ_HANDLED; } -/** EMAC on-chip buffer descriptor memory - * - * WARNING: Please note that the on chip memory is used for both TX and RX - * buffer descriptor queues and is equally divided between TX and RX desc's - * If the number of TX or RX descriptors change this memory pointers need - * to be adjusted. If external memory is allocated then these pointers can - * pointer to the memory - * - */ -#define EMAC_TX_BD_MEM(priv) ((priv)->emac_ctrl_ram) -#define EMAC_RX_BD_MEM(priv) ((priv)->emac_ctrl_ram + \ - (((priv)->ctrl_ram_size) >> 1)) - -/** - * emac_init_txch: TX channel initialization - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number - * - * Called during device init to setup a TX channel (allocate buffer desc - * create free pool and keep ready for transmission - * - * Returns success(0) or mem alloc failures error code - */ -static int emac_init_txch(struct emac_priv *priv, u32 ch) -{ - struct device *emac_dev = &priv->ndev->dev; - u32 cnt, bd_size; - void __iomem *mem; - struct emac_tx_bd __iomem *curr_bd; - struct emac_txch *txch = NULL; - - txch = kzalloc(sizeof(struct emac_txch), GFP_KERNEL); - if (NULL == txch) { - dev_err(emac_dev, "DaVinci EMAC: TX Ch mem alloc failed"); - return -ENOMEM; - } - priv->txch[ch] = txch; - txch->service_max = EMAC_DEF_TX_MAX_SERVICE; - txch->active_queue_head = NULL; - txch->active_queue_tail = NULL; - txch->queue_active = 0; - txch->teardown_pending = 0; - - /* allocate memory for TX CPPI channel on a 4 byte boundry */ - txch->tx_complete = kzalloc(txch->service_max * sizeof(u32), - GFP_KERNEL); - if (NULL == txch->tx_complete) { - dev_err(emac_dev, "DaVinci EMAC: Tx service mem alloc failed"); - kfree(txch); - return -ENOMEM; - } - - /* allocate buffer descriptor pool align every BD on four word - * boundry for future requirements */ - bd_size = (sizeof(struct emac_tx_bd) + 0xF) & ~0xF; - txch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size; - txch->alloc_size = (((bd_size * txch->num_bd) + 0xF) & ~0xF); - - /* alloc TX BD memory */ - txch->bd_mem = EMAC_TX_BD_MEM(priv); - __memzero((void __force *)txch->bd_mem, txch->alloc_size); - - /* initialize the BD linked list */ - mem = (void __force __iomem *) - (((u32 __force) txch->bd_mem + 0xF) & ~0xF); - txch->bd_pool_head = NULL; - for (cnt = 0; cnt < txch->num_bd; cnt++) { - curr_bd = mem + (cnt * bd_size); - curr_bd->next = txch->bd_pool_head; - txch->bd_pool_head = curr_bd; - } - - /* reset statistics counters */ - txch->out_of_tx_bd = 0; - txch->no_active_pkts = 0; - txch->active_queue_count = 0; - - return 0; -} - -/** - * emac_cleanup_txch: Book-keep function to clean TX channel resources - * @priv: The DaVinci EMAC private adapter structure - * @ch: TX channel number - * - * Called to clean up TX channel resources - * - */ -static void emac_cleanup_txch(struct emac_priv *priv, u32 ch) +static struct sk_buff *emac_rx_alloc(struct emac_priv *priv) { - struct emac_txch *txch = priv->txch[ch]; - - if (txch) { - if (txch->bd_mem) - txch->bd_mem = NULL; - kfree(txch->tx_complete); - kfree(txch); - priv->txch[ch] = NULL; - } + struct sk_buff *skb = dev_alloc_skb(priv->rx_buf_size); + if (WARN_ON(!skb)) + return NULL; + skb->dev = priv->ndev; + skb_reserve(skb, NET_IP_ALIGN); + return skb; } -/** - * emac_net_tx_complete: TX packet completion function - * @priv: The DaVinci EMAC private adapter structure - * @net_data_tokens: packet token - skb pointer - * @num_tokens: number of skb's to free - * @ch: TX channel number - * - * Frees the skb once packet is transmitted - * - */ -static int emac_net_tx_complete(struct emac_priv *priv, - void **net_data_tokens, - int num_tokens, u32 ch) +static void emac_rx_handler(void *token, int len, int status) { - struct net_device *ndev = priv->ndev; - u32 cnt; - - if (unlikely(num_tokens && netif_queue_stopped(ndev))) - netif_start_queue(ndev); - for (cnt = 0; cnt < num_tokens; cnt++) { - struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt]; - if (skb == NULL) - continue; - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; + struct sk_buff *skb = token; + struct net_device *ndev = skb->dev; + struct emac_priv *priv = netdev_priv(ndev); + struct device *emac_dev = &ndev->dev; + int ret; + + /* free and bail if we are shutting down */ + if (unlikely(!netif_running(ndev))) { dev_kfree_skb_any(skb); + return; } - return 0; -} - -/** - * emac_txch_teardown: TX channel teardown - * @priv: The DaVinci EMAC private adapter structure - * @ch: TX channel number - * - * Called to teardown TX channel - * - */ -static void emac_txch_teardown(struct emac_priv *priv, u32 ch) -{ - struct device *emac_dev = &priv->ndev->dev; - u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */ - struct emac_txch *txch = priv->txch[ch]; - struct emac_tx_bd __iomem *curr_bd; - - while ((emac_read(EMAC_TXCP(ch)) & EMAC_TEARDOWN_VALUE) != - EMAC_TEARDOWN_VALUE) { - /* wait till tx teardown complete */ - cpu_relax(); /* TODO: check if this helps ... */ - --teardown_cnt; - if (0 == teardown_cnt) { - dev_err(emac_dev, "EMAC: TX teardown aborted\n"); - break; - } - } - emac_write(EMAC_TXCP(ch), EMAC_TEARDOWN_VALUE); - - /* process sent packets and return skb's to upper layer */ - if (1 == txch->queue_active) { - curr_bd = txch->active_queue_head; - while (curr_bd != NULL) { - dma_unmap_single(emac_dev, curr_bd->buff_ptr, - curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE, - DMA_TO_DEVICE); - - emac_net_tx_complete(priv, (void __force *) - &curr_bd->buf_token, 1, ch); - if (curr_bd != txch->active_queue_tail) - curr_bd = curr_bd->next; - else - break; - } - txch->bd_pool_head = txch->active_queue_head; - txch->active_queue_head = - txch->active_queue_tail = NULL; - } -} -/** - * emac_stop_txch: Stop TX channel operation - * @priv: The DaVinci EMAC private adapter structure - * @ch: TX channel number - * - * Called to stop TX channel operation - * - */ -static void emac_stop_txch(struct emac_priv *priv, u32 ch) -{ - struct emac_txch *txch = priv->txch[ch]; - - if (txch) { - txch->teardown_pending = 1; - emac_write(EMAC_TXTEARDOWN, 0); - emac_txch_teardown(priv, ch); - txch->teardown_pending = 0; - emac_write(EMAC_TXINTMASKCLEAR, BIT(ch)); + /* recycle on recieve error */ + if (status < 0) { + ndev->stats.rx_errors++; + goto recycle; } -} -/** - * emac_tx_bdproc: TX buffer descriptor (packet) processing - * @priv: The DaVinci EMAC private adapter structure - * @ch: TX channel number to process buffer descriptors for - * @budget: number of packets allowed to process - * @pending: indication to caller that packets are pending to process - * - * Processes TX buffer descriptors after packets are transmitted - checks - * ownership bit on the TX * descriptor and requeues it to free pool & frees - * the SKB buffer. Only "budget" number of packets are processed and - * indication of pending packets provided to the caller - * - * Returns number of packets processed - */ -static int emac_tx_bdproc(struct emac_priv *priv, u32 ch, u32 budget) -{ - struct device *emac_dev = &priv->ndev->dev; - unsigned long flags; - u32 frame_status; - u32 pkts_processed = 0; - u32 tx_complete_cnt = 0; - struct emac_tx_bd __iomem *curr_bd; - struct emac_txch *txch = priv->txch[ch]; - u32 *tx_complete_ptr = txch->tx_complete; - - if (unlikely(1 == txch->teardown_pending)) { - if (netif_msg_tx_err(priv) && net_ratelimit()) { - dev_err(emac_dev, "DaVinci EMAC:emac_tx_bdproc: "\ - "teardown pending\n"); - } - return 0; /* dont handle any pkt completions */ - } + /* feed received packet up the stack */ + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, ndev); + netif_receive_skb(skb); + ndev->stats.rx_bytes += len; + ndev->stats.rx_packets++; - ++txch->proc_count; - spin_lock_irqsave(&priv->tx_lock, flags); - curr_bd = txch->active_queue_head; - if (NULL == curr_bd) { - emac_write(EMAC_TXCP(ch), - emac_virt_to_phys(txch->last_hw_bdprocessed, priv)); - txch->no_active_pkts++; - spin_unlock_irqrestore(&priv->tx_lock, flags); - return 0; + /* alloc a new packet for receive */ + skb = emac_rx_alloc(priv); + if (!skb) { + if (netif_msg_rx_err(priv) && net_ratelimit()) + dev_err(emac_dev, "failed rx buffer alloc\n"); + return; } - BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); - frame_status = curr_bd->mode; - while ((curr_bd) && - ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) && - (pkts_processed < budget)) { - emac_write(EMAC_TXCP(ch), emac_virt_to_phys(curr_bd, priv)); - txch->active_queue_head = curr_bd->next; - if (frame_status & EMAC_CPPI_EOQ_BIT) { - if (curr_bd->next) { /* misqueued packet */ - emac_write(EMAC_TXHDP(ch), curr_bd->h_next); - ++txch->mis_queued_packets; - } else { - txch->queue_active = 0; /* end of queue */ - } - } - dma_unmap_single(emac_dev, curr_bd->buff_ptr, - curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE, - DMA_TO_DEVICE); - - *tx_complete_ptr = (u32) curr_bd->buf_token; - ++tx_complete_ptr; - ++tx_complete_cnt; - curr_bd->next = txch->bd_pool_head; - txch->bd_pool_head = curr_bd; - --txch->active_queue_count; - pkts_processed++; - txch->last_hw_bdprocessed = curr_bd; - curr_bd = txch->active_queue_head; - if (curr_bd) { - BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); - frame_status = curr_bd->mode; - } - } /* end of pkt processing loop */ - - emac_net_tx_complete(priv, - (void *)&txch->tx_complete[0], - tx_complete_cnt, ch); - spin_unlock_irqrestore(&priv->tx_lock, flags); - return pkts_processed; +recycle: + ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, + skb_tailroom(skb), GFP_KERNEL); + if (WARN_ON(ret < 0)) + dev_kfree_skb_any(skb); } -#define EMAC_ERR_TX_OUT_OF_BD -1 - -/** - * emac_send: EMAC Transmit function (internal) - * @priv: The DaVinci EMAC private adapter structure - * @pkt: packet pointer (contains skb ptr) - * @ch: TX channel number - * - * Called by the transmit function to queue the packet in EMAC hardware queue - * - * Returns success(0) or error code (typically out of desc's) - */ -static int emac_send(struct emac_priv *priv, struct emac_netpktobj *pkt, u32 ch) +static void emac_tx_handler(void *token, int len, int status) { - unsigned long flags; - struct emac_tx_bd __iomem *curr_bd; - struct emac_txch *txch; - struct emac_netbufobj *buf_list; - - txch = priv->txch[ch]; - buf_list = pkt->buf_list; /* get handle to the buffer array */ - - /* check packet size and pad if short */ - if (pkt->pkt_length < EMAC_DEF_MIN_ETHPKTSIZE) { - buf_list->length += (EMAC_DEF_MIN_ETHPKTSIZE - pkt->pkt_length); - pkt->pkt_length = EMAC_DEF_MIN_ETHPKTSIZE; - } + struct sk_buff *skb = token; + struct net_device *ndev = skb->dev; - spin_lock_irqsave(&priv->tx_lock, flags); - curr_bd = txch->bd_pool_head; - if (curr_bd == NULL) { - txch->out_of_tx_bd++; - spin_unlock_irqrestore(&priv->tx_lock, flags); - return EMAC_ERR_TX_OUT_OF_BD; - } - - txch->bd_pool_head = curr_bd->next; - curr_bd->buf_token = buf_list->buf_token; - curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buf_list->data_ptr, - buf_list->length, DMA_TO_DEVICE); - curr_bd->off_b_len = buf_list->length; - curr_bd->h_next = 0; - curr_bd->next = NULL; - curr_bd->mode = (EMAC_CPPI_SOP_BIT | EMAC_CPPI_OWNERSHIP_BIT | - EMAC_CPPI_EOP_BIT | pkt->pkt_length); - - /* flush the packet from cache if write back cache is present */ - BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); - - /* send the packet */ - if (txch->active_queue_head == NULL) { - txch->active_queue_head = curr_bd; - txch->active_queue_tail = curr_bd; - if (1 != txch->queue_active) { - emac_write(EMAC_TXHDP(ch), - emac_virt_to_phys(curr_bd, priv)); - txch->queue_active = 1; - } - ++txch->queue_reinit; - } else { - register struct emac_tx_bd __iomem *tail_bd; - register u32 frame_status; - - tail_bd = txch->active_queue_tail; - tail_bd->next = curr_bd; - txch->active_queue_tail = curr_bd; - tail_bd = EMAC_VIRT_NOCACHE(tail_bd); - tail_bd->h_next = (int)emac_virt_to_phys(curr_bd, priv); - frame_status = tail_bd->mode; - if (frame_status & EMAC_CPPI_EOQ_BIT) { - emac_write(EMAC_TXHDP(ch), - emac_virt_to_phys(curr_bd, priv)); - frame_status &= ~(EMAC_CPPI_EOQ_BIT); - tail_bd->mode = frame_status; - ++txch->end_of_queue_add; - } - } - txch->active_queue_count++; - spin_unlock_irqrestore(&priv->tx_lock, flags); - return 0; + if (unlikely(netif_queue_stopped(ndev))) + netif_start_queue(ndev); + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += len; + dev_kfree_skb_any(skb); } /** @@ -1565,42 +1067,36 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev) { struct device *emac_dev = &ndev->dev; int ret_code; - struct emac_netbufobj tx_buf; /* buffer obj-only single frame support */ - struct emac_netpktobj tx_packet; /* packet object */ struct emac_priv *priv = netdev_priv(ndev); /* If no link, return */ if (unlikely(!priv->link)) { if (netif_msg_tx_err(priv) && net_ratelimit()) dev_err(emac_dev, "DaVinci EMAC: No link to transmit"); - return NETDEV_TX_BUSY; + goto fail_tx; + } + + ret_code = skb_padto(skb, EMAC_DEF_MIN_ETHPKTSIZE); + if (unlikely(ret_code < 0)) { + if (netif_msg_tx_err(priv) && net_ratelimit()) + dev_err(emac_dev, "DaVinci EMAC: packet pad failed"); + goto fail_tx; } - /* Build the buffer and packet objects - Since only single fragment is - * supported, need not set length and token in both packet & object. - * Doing so for completeness sake & to show that this needs to be done - * in multifragment case - */ - tx_packet.buf_list = &tx_buf; - tx_packet.num_bufs = 1; /* only single fragment supported */ - tx_packet.pkt_length = skb->len; - tx_packet.pkt_token = (void *)skb; - tx_buf.length = skb->len; - tx_buf.buf_token = (void *)skb; - tx_buf.data_ptr = skb->data; - ret_code = emac_send(priv, &tx_packet, EMAC_DEF_TX_CH); + ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len, + GFP_KERNEL); if (unlikely(ret_code != 0)) { - if (ret_code == EMAC_ERR_TX_OUT_OF_BD) { - if (netif_msg_tx_err(priv) && net_ratelimit()) - dev_err(emac_dev, "DaVinci EMAC: xmit() fatal"\ - " err. Out of TX BD's"); - netif_stop_queue(priv->ndev); - } - ndev->stats.tx_dropped++; - return NETDEV_TX_BUSY; + if (netif_msg_tx_err(priv) && net_ratelimit()) + dev_err(emac_dev, "DaVinci EMAC: desc submit failed"); + goto fail_tx; } return NETDEV_TX_OK; + +fail_tx: + ndev->stats.tx_dropped++; + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; } /** @@ -1621,218 +1117,16 @@ static void emac_dev_tx_timeout(struct net_device *ndev) if (netif_msg_tx_err(priv)) dev_err(emac_dev, "DaVinci EMAC: xmit timeout, restarting TX"); + emac_dump_regs(priv); + ndev->stats.tx_errors++; emac_int_disable(priv); - emac_stop_txch(priv, EMAC_DEF_TX_CH); - emac_cleanup_txch(priv, EMAC_DEF_TX_CH); - emac_init_txch(priv, EMAC_DEF_TX_CH); - emac_write(EMAC_TXHDP(0), 0); - emac_write(EMAC_TXINTMASKSET, BIT(EMAC_DEF_TX_CH)); + cpdma_chan_stop(priv->txchan); + cpdma_chan_start(priv->txchan); emac_int_enable(priv); } /** - * emac_net_alloc_rx_buf: Allocate a skb for RX - * @priv: The DaVinci EMAC private adapter structure - * @buf_size: size of SKB data buffer to allocate - * @data_token: data token returned (skb handle for storing in buffer desc) - * @ch: RX channel number - * - * Called during RX channel setup - allocates skb buffer of required size - * and provides the skb handle and allocated buffer data pointer to caller - * - * Returns skb data pointer or 0 on failure to alloc skb - */ -static void *emac_net_alloc_rx_buf(struct emac_priv *priv, int buf_size, - void **data_token, u32 ch) -{ - struct net_device *ndev = priv->ndev; - struct device *emac_dev = &ndev->dev; - struct sk_buff *p_skb; - - p_skb = dev_alloc_skb(buf_size); - if (unlikely(NULL == p_skb)) { - if (netif_msg_rx_err(priv) && net_ratelimit()) - dev_err(emac_dev, "DaVinci EMAC: failed to alloc skb"); - return NULL; - } - - /* set device pointer in skb and reserve space for extra bytes */ - p_skb->dev = ndev; - skb_reserve(p_skb, NET_IP_ALIGN); - *data_token = (void *) p_skb; - return p_skb->data; -} - -/** - * emac_init_rxch: RX channel initialization - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number - * @param: mac address for RX channel - * - * Called during device init to setup a RX channel (allocate buffers and - * buffer descriptors, create queue and keep ready for reception - * - * Returns success(0) or mem alloc failures error code - */ -static int emac_init_rxch(struct emac_priv *priv, u32 ch, char *param) -{ - struct device *emac_dev = &priv->ndev->dev; - u32 cnt, bd_size; - void __iomem *mem; - struct emac_rx_bd __iomem *curr_bd; - struct emac_rxch *rxch = NULL; - - rxch = kzalloc(sizeof(struct emac_rxch), GFP_KERNEL); - if (NULL == rxch) { - dev_err(emac_dev, "DaVinci EMAC: RX Ch mem alloc failed"); - return -ENOMEM; - } - priv->rxch[ch] = rxch; - rxch->buf_size = priv->rx_buf_size; - rxch->service_max = EMAC_DEF_RX_MAX_SERVICE; - rxch->queue_active = 0; - rxch->teardown_pending = 0; - - /* save mac address */ - for (cnt = 0; cnt < 6; cnt++) - rxch->mac_addr[cnt] = param[cnt]; - - /* allocate buffer descriptor pool align every BD on four word - * boundry for future requirements */ - bd_size = (sizeof(struct emac_rx_bd) + 0xF) & ~0xF; - rxch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size; - rxch->alloc_size = (((bd_size * rxch->num_bd) + 0xF) & ~0xF); - rxch->bd_mem = EMAC_RX_BD_MEM(priv); - __memzero((void __force *)rxch->bd_mem, rxch->alloc_size); - rxch->pkt_queue.buf_list = &rxch->buf_queue; - - /* allocate RX buffer and initialize the BD linked list */ - mem = (void __force __iomem *) - (((u32 __force) rxch->bd_mem + 0xF) & ~0xF); - rxch->active_queue_head = NULL; - rxch->active_queue_tail = mem; - for (cnt = 0; cnt < rxch->num_bd; cnt++) { - curr_bd = mem + (cnt * bd_size); - /* for future use the last parameter contains the BD ptr */ - curr_bd->data_ptr = emac_net_alloc_rx_buf(priv, - rxch->buf_size, - (void __force **)&curr_bd->buf_token, - EMAC_DEF_RX_CH); - if (curr_bd->data_ptr == NULL) { - dev_err(emac_dev, "DaVinci EMAC: RX buf mem alloc " \ - "failed for ch %d\n", ch); - kfree(rxch); - return -ENOMEM; - } - - /* populate the hardware descriptor */ - curr_bd->h_next = emac_virt_to_phys(rxch->active_queue_head, - priv); - curr_bd->buff_ptr = dma_map_single(emac_dev, curr_bd->data_ptr, - rxch->buf_size, DMA_FROM_DEVICE); - curr_bd->off_b_len = rxch->buf_size; - curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT; - - /* write back to hardware memory */ - BD_CACHE_WRITEBACK_INVALIDATE((u32) curr_bd, - EMAC_BD_LENGTH_FOR_CACHE); - curr_bd->next = rxch->active_queue_head; - rxch->active_queue_head = curr_bd; - } - - /* At this point rxCppi->activeQueueHead points to the first - RX BD ready to be given to RX HDP and rxch->active_queue_tail - points to the last RX BD - */ - return 0; -} - -/** - * emac_rxch_teardown: RX channel teardown - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number - * - * Called during device stop to teardown RX channel - * - */ -static void emac_rxch_teardown(struct emac_priv *priv, u32 ch) -{ - struct device *emac_dev = &priv->ndev->dev; - u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */ - - while ((emac_read(EMAC_RXCP(ch)) & EMAC_TEARDOWN_VALUE) != - EMAC_TEARDOWN_VALUE) { - /* wait till tx teardown complete */ - cpu_relax(); /* TODO: check if this helps ... */ - --teardown_cnt; - if (0 == teardown_cnt) { - dev_err(emac_dev, "EMAC: RX teardown aborted\n"); - break; - } - } - emac_write(EMAC_RXCP(ch), EMAC_TEARDOWN_VALUE); -} - -/** - * emac_stop_rxch: Stop RX channel operation - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number - * - * Called during device stop to stop RX channel operation - * - */ -static void emac_stop_rxch(struct emac_priv *priv, u32 ch) -{ - struct emac_rxch *rxch = priv->rxch[ch]; - - if (rxch) { - rxch->teardown_pending = 1; - emac_write(EMAC_RXTEARDOWN, ch); - /* wait for teardown complete */ - emac_rxch_teardown(priv, ch); - rxch->teardown_pending = 0; - emac_write(EMAC_RXINTMASKCLEAR, BIT(ch)); - } -} - -/** - * emac_cleanup_rxch: Book-keep function to clean RX channel resources - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number - * - * Called during device stop to clean up RX channel resources - * - */ -static void emac_cleanup_rxch(struct emac_priv *priv, u32 ch) -{ - struct emac_rxch *rxch = priv->rxch[ch]; - struct emac_rx_bd __iomem *curr_bd; - - if (rxch) { - /* free the receive buffers previously allocated */ - curr_bd = rxch->active_queue_head; - while (curr_bd) { - if (curr_bd->buf_token) { - dma_unmap_single(&priv->ndev->dev, - curr_bd->buff_ptr, - curr_bd->off_b_len - & EMAC_RX_BD_BUF_SIZE, - DMA_FROM_DEVICE); - - dev_kfree_skb_any((struct sk_buff *)\ - curr_bd->buf_token); - } - curr_bd = curr_bd->next; - } - if (rxch->bd_mem) - rxch->bd_mem = NULL; - kfree(rxch); - priv->rxch[ch] = NULL; - } -} - -/** * emac_set_type0addr: Set EMAC Type0 mac address * @priv: The DaVinci EMAC private adapter structure * @ch: RX channel number @@ -1948,7 +1242,6 @@ static void emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr) static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) { struct emac_priv *priv = netdev_priv(ndev); - struct emac_rxch *rxch = priv->rxch[EMAC_DEF_RX_CH]; struct device *emac_dev = &priv->ndev->dev; struct sockaddr *sa = addr; @@ -1959,11 +1252,10 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len); memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len); - /* If the interface is down - rxch is NULL. */ /* MAC address is configured only after the interface is enabled. */ if (netif_running(ndev)) { - memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len); - emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr); + memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len); + emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr); } if (netif_msg_drv(priv)) @@ -1974,194 +1266,6 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) } /** - * emac_addbd_to_rx_queue: Recycle RX buffer descriptor - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number to process buffer descriptors for - * @curr_bd: current buffer descriptor - * @buffer: buffer pointer for descriptor - * @buf_token: buffer token (stores skb information) - * - * Prepares the recycled buffer descriptor and addes it to hardware - * receive queue - if queue empty this descriptor becomes the head - * else addes the descriptor to end of queue - * - */ -static void emac_addbd_to_rx_queue(struct emac_priv *priv, u32 ch, - struct emac_rx_bd __iomem *curr_bd, - char *buffer, void *buf_token) -{ - struct emac_rxch *rxch = priv->rxch[ch]; - - /* populate the hardware descriptor */ - curr_bd->h_next = 0; - curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buffer, - rxch->buf_size, DMA_FROM_DEVICE); - curr_bd->off_b_len = rxch->buf_size; - curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT; - curr_bd->next = NULL; - curr_bd->data_ptr = buffer; - curr_bd->buf_token = buf_token; - - /* write back */ - BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); - if (rxch->active_queue_head == NULL) { - rxch->active_queue_head = curr_bd; - rxch->active_queue_tail = curr_bd; - if (0 != rxch->queue_active) { - emac_write(EMAC_RXHDP(ch), - emac_virt_to_phys(rxch->active_queue_head, priv)); - rxch->queue_active = 1; - } - } else { - struct emac_rx_bd __iomem *tail_bd; - u32 frame_status; - - tail_bd = rxch->active_queue_tail; - rxch->active_queue_tail = curr_bd; - tail_bd->next = curr_bd; - tail_bd = EMAC_VIRT_NOCACHE(tail_bd); - tail_bd->h_next = emac_virt_to_phys(curr_bd, priv); - frame_status = tail_bd->mode; - if (frame_status & EMAC_CPPI_EOQ_BIT) { - emac_write(EMAC_RXHDP(ch), - emac_virt_to_phys(curr_bd, priv)); - frame_status &= ~(EMAC_CPPI_EOQ_BIT); - tail_bd->mode = frame_status; - ++rxch->end_of_queue_add; - } - } - ++rxch->recycled_bd; -} - -/** - * emac_net_rx_cb: Prepares packet and sends to upper layer - * @priv: The DaVinci EMAC private adapter structure - * @net_pkt_list: Network packet list (received packets) - * - * Invalidates packet buffer memory and sends the received packet to upper - * layer - * - * Returns success or appropriate error code (none as of now) - */ -static int emac_net_rx_cb(struct emac_priv *priv, - struct emac_netpktobj *net_pkt_list) -{ - struct net_device *ndev = priv->ndev; - struct sk_buff *p_skb = net_pkt_list->pkt_token; - /* set length of packet */ - skb_put(p_skb, net_pkt_list->pkt_length); - p_skb->protocol = eth_type_trans(p_skb, priv->ndev); - netif_receive_skb(p_skb); - ndev->stats.rx_bytes += net_pkt_list->pkt_length; - ndev->stats.rx_packets++; - return 0; -} - -/** - * emac_rx_bdproc: RX buffer descriptor (packet) processing - * @priv: The DaVinci EMAC private adapter structure - * @ch: RX channel number to process buffer descriptors for - * @budget: number of packets allowed to process - * @pending: indication to caller that packets are pending to process - * - * Processes RX buffer descriptors - checks ownership bit on the RX buffer - * descriptor, sends the receive packet to upper layer, allocates a new SKB - * and recycles the buffer descriptor (requeues it in hardware RX queue). - * Only "budget" number of packets are processed and indication of pending - * packets provided to the caller. - * - * Returns number of packets processed (and indication of pending packets) - */ -static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget) -{ - unsigned long flags; - u32 frame_status; - u32 pkts_processed = 0; - char *new_buffer; - struct emac_rx_bd __iomem *curr_bd; - struct emac_rx_bd __iomem *last_bd; - struct emac_netpktobj *curr_pkt, pkt_obj; - struct emac_netbufobj buf_obj; - struct emac_netbufobj *rx_buf_obj; - void *new_buf_token; - struct emac_rxch *rxch = priv->rxch[ch]; - - if (unlikely(1 == rxch->teardown_pending)) - return 0; - ++rxch->proc_count; - spin_lock_irqsave(&priv->rx_lock, flags); - pkt_obj.buf_list = &buf_obj; - curr_pkt = &pkt_obj; - curr_bd = rxch->active_queue_head; - BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); - frame_status = curr_bd->mode; - - while ((curr_bd) && - ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) && - (pkts_processed < budget)) { - - new_buffer = emac_net_alloc_rx_buf(priv, rxch->buf_size, - &new_buf_token, EMAC_DEF_RX_CH); - if (unlikely(NULL == new_buffer)) { - ++rxch->out_of_rx_buffers; - goto end_emac_rx_bdproc; - } - - /* populate received packet data structure */ - rx_buf_obj = &curr_pkt->buf_list[0]; - rx_buf_obj->data_ptr = (char *)curr_bd->data_ptr; - rx_buf_obj->length = curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE; - rx_buf_obj->buf_token = curr_bd->buf_token; - - dma_unmap_single(&priv->ndev->dev, curr_bd->buff_ptr, - curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE, - DMA_FROM_DEVICE); - - curr_pkt->pkt_token = curr_pkt->buf_list->buf_token; - curr_pkt->num_bufs = 1; - curr_pkt->pkt_length = - (frame_status & EMAC_RX_BD_PKT_LENGTH_MASK); - emac_write(EMAC_RXCP(ch), emac_virt_to_phys(curr_bd, priv)); - ++rxch->processed_bd; - last_bd = curr_bd; - curr_bd = last_bd->next; - rxch->active_queue_head = curr_bd; - - /* check if end of RX queue ? */ - if (frame_status & EMAC_CPPI_EOQ_BIT) { - if (curr_bd) { - ++rxch->mis_queued_packets; - emac_write(EMAC_RXHDP(ch), - emac_virt_to_phys(curr_bd, priv)); - } else { - ++rxch->end_of_queue; - rxch->queue_active = 0; - } - } - - /* recycle BD */ - emac_addbd_to_rx_queue(priv, ch, last_bd, new_buffer, - new_buf_token); - - /* return the packet to the user - BD ptr passed in - * last parameter for potential *future* use */ - spin_unlock_irqrestore(&priv->rx_lock, flags); - emac_net_rx_cb(priv, curr_pkt); - spin_lock_irqsave(&priv->rx_lock, flags); - curr_bd = rxch->active_queue_head; - if (curr_bd) { - BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); - frame_status = curr_bd->mode; - } - ++pkts_processed; - } - -end_emac_rx_bdproc: - spin_unlock_irqrestore(&priv->rx_lock, flags); - return pkts_processed; -} - -/** * emac_hw_enable: Enable EMAC hardware for packet transmission/reception * @priv: The DaVinci EMAC private adapter structure * @@ -2172,7 +1276,7 @@ end_emac_rx_bdproc: */ static int emac_hw_enable(struct emac_priv *priv) { - u32 ch, val, mbp_enable, mac_control; + u32 val, mbp_enable, mac_control; /* Soft reset */ emac_write(EMAC_SOFTRESET, 1); @@ -2215,26 +1319,9 @@ static int emac_hw_enable(struct emac_priv *priv) emac_write(EMAC_RXUNICASTCLEAR, EMAC_RX_UNICAST_CLEAR_ALL); priv->rx_addr_type = (emac_read(EMAC_MACCONFIG) >> 8) & 0xFF; - val = emac_read(EMAC_TXCONTROL); - val |= EMAC_TX_CONTROL_TX_ENABLE_VAL; - emac_write(EMAC_TXCONTROL, val); - val = emac_read(EMAC_RXCONTROL); - val |= EMAC_RX_CONTROL_RX_ENABLE_VAL; - emac_write(EMAC_RXCONTROL, val); emac_write(EMAC_MACINTMASKSET, EMAC_MAC_HOST_ERR_INTMASK_VAL); - for (ch = 0; ch < EMAC_DEF_MAX_TX_CH; ch++) { - emac_write(EMAC_TXHDP(ch), 0); - emac_write(EMAC_TXINTMASKSET, BIT(ch)); - } - for (ch = 0; ch < EMAC_DEF_MAX_RX_CH; ch++) { - struct emac_rxch *rxch = priv->rxch[ch]; - emac_setmac(priv, ch, rxch->mac_addr); - emac_write(EMAC_RXINTMASKSET, BIT(ch)); - rxch->queue_active = 1; - emac_write(EMAC_RXHDP(ch), - emac_virt_to_phys(rxch->active_queue_head, priv)); - } + emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr); /* Enable MII */ val = emac_read(EMAC_MACCONTROL); @@ -2279,8 +1366,8 @@ static int emac_poll(struct napi_struct *napi, int budget) mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC; if (status & mask) { - num_tx_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH, - EMAC_DEF_TX_MAX_SERVICE); + num_tx_pkts = cpdma_chan_process(priv->txchan, + EMAC_DEF_TX_MAX_SERVICE); } /* TX processing */ mask = EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC; @@ -2289,7 +1376,7 @@ static int emac_poll(struct napi_struct *napi, int budget) mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC; if (status & mask) { - num_rx_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH, budget); + num_rx_pkts = cpdma_chan_process(priv->rxchan, budget); } /* RX processing */ mask = EMAC_DM644X_MAC_IN_VECTOR_HOST_INT; @@ -2348,79 +1435,6 @@ void emac_poll_controller(struct net_device *ndev) } #endif -/* PHY/MII bus related */ - -/* Wait until mdio is ready for next command */ -#define MDIO_WAIT_FOR_USER_ACCESS\ - while ((emac_mdio_read((MDIO_USERACCESS(0))) &\ - MDIO_USERACCESS_GO) != 0) - -static int emac_mii_read(struct mii_bus *bus, int phy_id, int phy_reg) -{ - unsigned int phy_data = 0; - unsigned int phy_control; - - /* Wait until mdio is ready for next command */ - MDIO_WAIT_FOR_USER_ACCESS; - - phy_control = (MDIO_USERACCESS_GO | - MDIO_USERACCESS_READ | - ((phy_reg << 21) & MDIO_USERACCESS_REGADR) | - ((phy_id << 16) & MDIO_USERACCESS_PHYADR) | - (phy_data & MDIO_USERACCESS_DATA)); - emac_mdio_write(MDIO_USERACCESS(0), phy_control); - - /* Wait until mdio is ready for next command */ - MDIO_WAIT_FOR_USER_ACCESS; - - return emac_mdio_read(MDIO_USERACCESS(0)) & MDIO_USERACCESS_DATA; - -} - -static int emac_mii_write(struct mii_bus *bus, int phy_id, - int phy_reg, u16 phy_data) -{ - - unsigned int control; - - /* until mdio is ready for next command */ - MDIO_WAIT_FOR_USER_ACCESS; - - control = (MDIO_USERACCESS_GO | - MDIO_USERACCESS_WRITE | - ((phy_reg << 21) & MDIO_USERACCESS_REGADR) | - ((phy_id << 16) & MDIO_USERACCESS_PHYADR) | - (phy_data & MDIO_USERACCESS_DATA)); - emac_mdio_write(MDIO_USERACCESS(0), control); - - return 0; -} - -static int emac_mii_reset(struct mii_bus *bus) -{ - unsigned int clk_div; - int mdio_bus_freq = emac_bus_frequency; - - if (mdio_max_freq && mdio_bus_freq) - clk_div = ((mdio_bus_freq / mdio_max_freq) - 1); - else - clk_div = 0xFF; - - clk_div &= MDIO_CONTROL_CLKDIV; - - /* Set enable and clock divider in MDIOControl */ - emac_mdio_write(MDIO_CONTROL, (clk_div | MDIO_CONTROL_ENABLE)); - - return 0; - -} - -static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, PHY_POLL }; - -/* emac_driver: EMAC MII bus structure */ - -static struct mii_bus *emac_mii; - static void emac_adjust_link(struct net_device *ndev) { struct emac_priv *priv = netdev_priv(ndev); @@ -2485,6 +1499,11 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) return -EOPNOTSUPP; } +static int match_first_device(struct device *dev, void *data) +{ + return 1; +} + /** * emac_dev_open: EMAC device open * @ndev: The DaVinci EMAC network adapter @@ -2498,10 +1517,9 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) static int emac_dev_open(struct net_device *ndev) { struct device *emac_dev = &ndev->dev; - u32 rc, cnt, ch; - int phy_addr; + u32 cnt; struct resource *res; - int q, m; + int q, m, ret; int i = 0; int k = 0; struct emac_priv *priv = netdev_priv(ndev); @@ -2513,29 +1531,21 @@ static int emac_dev_open(struct net_device *ndev) /* Configuration items */ priv->rx_buf_size = EMAC_DEF_MAX_FRAME_SIZE + NET_IP_ALIGN; - /* Clear basic hardware */ - for (ch = 0; ch < EMAC_MAX_TXRX_CHANNELS; ch++) { - emac_write(EMAC_TXHDP(ch), 0); - emac_write(EMAC_RXHDP(ch), 0); - emac_write(EMAC_RXHDP(ch), 0); - emac_write(EMAC_RXINTMASKCLEAR, EMAC_INT_MASK_CLEAR); - emac_write(EMAC_TXINTMASKCLEAR, EMAC_INT_MASK_CLEAR); - } priv->mac_hash1 = 0; priv->mac_hash2 = 0; emac_write(EMAC_MACHASH1, 0); emac_write(EMAC_MACHASH2, 0); - /* multi ch not supported - open 1 TX, 1RX ch by default */ - rc = emac_init_txch(priv, EMAC_DEF_TX_CH); - if (0 != rc) { - dev_err(emac_dev, "DaVinci EMAC: emac_init_txch() failed"); - return rc; - } - rc = emac_init_rxch(priv, EMAC_DEF_RX_CH, priv->mac_addr); - if (0 != rc) { - dev_err(emac_dev, "DaVinci EMAC: emac_init_rxch() failed"); - return rc; + for (i = 0; i < EMAC_DEF_RX_NUM_DESC; i++) { + struct sk_buff *skb = emac_rx_alloc(priv); + + if (!skb) + break; + + ret = cpdma_chan_submit(priv->rxchan, skb, skb->data, + skb_tailroom(skb), GFP_KERNEL); + if (WARN_ON(ret < 0)) + break; } /* Request IRQ */ @@ -2560,28 +1570,28 @@ static int emac_dev_open(struct net_device *ndev) emac_set_coalesce(ndev, &coal); } - /* find the first phy */ + cpdma_ctlr_start(priv->dma); + priv->phydev = NULL; - if (priv->phy_mask) { - emac_mii_reset(priv->mii_bus); - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (priv->mii_bus->phy_map[phy_addr]) { - priv->phydev = priv->mii_bus->phy_map[phy_addr]; - break; - } - } + /* use the first phy on the bus if pdata did not give us a phy id */ + if (!priv->phy_id) { + struct device *phy; - if (!priv->phydev) { - printk(KERN_ERR "%s: no PHY found\n", ndev->name); - return -1; - } + phy = bus_find_device(&mdio_bus_type, NULL, NULL, + match_first_device); + if (phy) + priv->phy_id = dev_name(phy); + } - priv->phydev = phy_connect(ndev, dev_name(&priv->phydev->dev), - &emac_adjust_link, 0, PHY_INTERFACE_MODE_MII); + if (priv->phy_id && *priv->phy_id) { + priv->phydev = phy_connect(ndev, priv->phy_id, + &emac_adjust_link, 0, + PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { - printk(KERN_ERR "%s: Could not attach to PHY\n", - ndev->name); + dev_err(emac_dev, "could not connect to phy %s\n", + priv->phy_id); + priv->phydev = NULL; return PTR_ERR(priv->phydev); } @@ -2589,12 +1599,13 @@ static int emac_dev_open(struct net_device *ndev) priv->speed = 0; priv->duplex = ~0; - printk(KERN_INFO "%s: attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, id=%x)\n", ndev->name, + dev_info(emac_dev, "attached PHY driver [%s] " + "(mii_bus:phy_addr=%s, id=%x)\n", priv->phydev->drv->name, dev_name(&priv->phydev->dev), priv->phydev->phy_id); - } else{ + } else { /* No PHY , fix the link, speed and duplex settings */ + dev_notice(emac_dev, "no phy, defaulting to 100/full\n"); priv->link = 1; priv->speed = SPEED_100; priv->duplex = DUPLEX_FULL; @@ -2607,7 +1618,7 @@ static int emac_dev_open(struct net_device *ndev) if (netif_msg_drv(priv)) dev_notice(emac_dev, "DaVinci EMAC: Opened %s\n", ndev->name); - if (priv->phy_mask) + if (priv->phydev) phy_start(priv->phydev); return 0; @@ -2648,10 +1659,7 @@ static int emac_dev_stop(struct net_device *ndev) netif_carrier_off(ndev); emac_int_disable(priv); - emac_stop_txch(priv, EMAC_DEF_TX_CH); - emac_stop_rxch(priv, EMAC_DEF_RX_CH); - emac_cleanup_txch(priv, EMAC_DEF_TX_CH); - emac_cleanup_rxch(priv, EMAC_DEF_RX_CH); + cpdma_ctlr_stop(priv->dma); emac_write(EMAC_SOFTRESET, 1); if (priv->phydev) @@ -2756,9 +1764,10 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) struct resource *res; struct net_device *ndev; struct emac_priv *priv; - unsigned long size; + unsigned long size, hw_ram_addr; struct emac_platform_data *pdata; struct device *emac_dev; + struct cpdma_params dma_params; /* obtain emac clock from kernel */ emac_clk = clk_get(&pdev->dev, NULL); @@ -2782,8 +1791,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) priv->ndev = ndev; priv->msg_enable = netif_msg_init(debug_level, DAVINCI_EMAC_DEBUG); - spin_lock_init(&priv->tx_lock); - spin_lock_init(&priv->rx_lock); spin_lock_init(&priv->lock); pdata = pdev->dev.platform_data; @@ -2794,7 +1801,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) /* MAC addr and PHY mask , RMII enable info from platform_data */ memcpy(priv->mac_addr, pdata->mac_addr, 6); - priv->phy_mask = pdata->phy_mask; + priv->phy_id = pdata->phy_id; priv->rmii_en = pdata->rmii_en; priv->version = pdata->version; priv->int_enable = pdata->interrupt_enable; @@ -2831,14 +1838,41 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) ndev->base_addr = (unsigned long)priv->remap_addr; priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset; - priv->ctrl_ram_size = pdata->ctrl_ram_size; - priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset; - if (pdata->hw_ram_addr) - priv->hw_ram_addr = pdata->hw_ram_addr; - else - priv->hw_ram_addr = (u32 __force)res->start + - pdata->ctrl_ram_offset; + hw_ram_addr = pdata->hw_ram_addr; + if (!hw_ram_addr) + hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset; + + memset(&dma_params, 0, sizeof(dma_params)); + dma_params.dev = emac_dev; + dma_params.dmaregs = priv->emac_base; + dma_params.rxthresh = priv->emac_base + 0x120; + dma_params.rxfree = priv->emac_base + 0x140; + dma_params.txhdp = priv->emac_base + 0x600; + dma_params.rxhdp = priv->emac_base + 0x620; + dma_params.txcp = priv->emac_base + 0x640; + dma_params.rxcp = priv->emac_base + 0x660; + dma_params.num_chan = EMAC_MAX_TXRX_CHANNELS; + dma_params.min_packet_size = EMAC_DEF_MIN_ETHPKTSIZE; + dma_params.desc_mem_phys = hw_ram_addr; + dma_params.desc_mem_size = pdata->ctrl_ram_size; + dma_params.desc_align = 16; + + priv->dma = cpdma_ctlr_create(&dma_params); + if (!priv->dma) { + dev_err(emac_dev, "DaVinci EMAC: Error initializing DMA\n"); + rc = -ENOMEM; + goto no_dma; + } + + priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH), + emac_tx_handler); + priv->rxchan = cpdma_chan_create(priv->dma, rx_chan_num(EMAC_DEF_RX_CH), + emac_rx_handler); + if (WARN_ON(!priv->txchan || !priv->rxchan)) { + rc = -ENOMEM; + goto no_irq_res; + } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -2871,32 +1905,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) } - /* MII/Phy intialisation, mdio bus registration */ - emac_mii = mdiobus_alloc(); - if (emac_mii == NULL) { - dev_err(emac_dev, "DaVinci EMAC: Error allocating mii_bus\n"); - rc = -ENOMEM; - goto mdio_alloc_err; - } - - priv->mii_bus = emac_mii; - emac_mii->name = "emac-mii", - emac_mii->read = emac_mii_read, - emac_mii->write = emac_mii_write, - emac_mii->reset = emac_mii_reset, - emac_mii->irq = mii_irqs, - emac_mii->phy_mask = ~(priv->phy_mask); - emac_mii->parent = &pdev->dev; - emac_mii->priv = priv->remap_addr + pdata->mdio_reg_offset; - snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", priv->pdev->id); - mdio_max_freq = pdata->mdio_max_freq; - emac_mii->reset(emac_mii); - - /* Register the MII bus */ - rc = mdiobus_register(emac_mii); - if (rc) - goto mdiobus_quit; - if (netif_msg_probe(priv)) { dev_notice(emac_dev, "DaVinci EMAC Probe found device "\ "(regs: %p, irq: %d)\n", @@ -2904,13 +1912,15 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) } return 0; -mdiobus_quit: - mdiobus_free(emac_mii); - netdev_reg_err: -mdio_alloc_err: clk_disable(emac_clk); no_irq_res: + if (priv->txchan) + cpdma_chan_destroy(priv->txchan); + if (priv->rxchan) + cpdma_chan_destroy(priv->rxchan); + cpdma_ctlr_destroy(priv->dma); +no_dma: res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, res->end - res->start + 1); iounmap(priv->remap_addr); @@ -2938,8 +1948,12 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); + + if (priv->txchan) + cpdma_chan_destroy(priv->txchan); + if (priv->rxchan) + cpdma_chan_destroy(priv->rxchan); + cpdma_ctlr_destroy(priv->dma); release_mem_region(res->start, res->end - res->start + 1); diff --git a/drivers/net/davinci_mdio.c b/drivers/net/davinci_mdio.c new file mode 100644 index 00000000000..7615040df75 --- /dev/null +++ b/drivers/net/davinci_mdio.c @@ -0,0 +1,475 @@ +/* + * DaVinci MDIO Module driver + * + * Copyright (C) 2010 Texas Instruments. + * + * Shamelessly ripped out of davinci_emac.c, original copyrights follow: + * + * Copyright (C) 2009 Texas Instruments. + * + * --------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --------------------------------------------------------------------------- + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/phy.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/davinci_emac.h> + +/* + * This timeout definition is a worst-case ultra defensive measure against + * unexpected controller lock ups. Ideally, we should never ever hit this + * scenario in practice. + */ +#define MDIO_TIMEOUT 100 /* msecs */ + +#define PHY_REG_MASK 0x1f +#define PHY_ID_MASK 0x1f + +#define DEF_OUT_FREQ 2200000 /* 2.2 MHz */ + +struct davinci_mdio_regs { + u32 version; + u32 control; +#define CONTROL_IDLE BIT(31) +#define CONTROL_ENABLE BIT(30) +#define CONTROL_MAX_DIV (0xff) + + u32 alive; + u32 link; + u32 linkintraw; + u32 linkintmasked; + u32 __reserved_0[2]; + u32 userintraw; + u32 userintmasked; + u32 userintmaskset; + u32 userintmaskclr; + u32 __reserved_1[20]; + + struct { + u32 access; +#define USERACCESS_GO BIT(31) +#define USERACCESS_WRITE BIT(30) +#define USERACCESS_ACK BIT(29) +#define USERACCESS_READ (0) +#define USERACCESS_DATA (0xffff) + + u32 physel; + } user[0]; +}; + +struct mdio_platform_data default_pdata = { + .bus_freq = DEF_OUT_FREQ, +}; + +struct davinci_mdio_data { + struct mdio_platform_data pdata; + struct davinci_mdio_regs __iomem *regs; + spinlock_t lock; + struct clk *clk; + struct device *dev; + struct mii_bus *bus; + bool suspended; + unsigned long access_time; /* jiffies */ +}; + +static void __davinci_mdio_reset(struct davinci_mdio_data *data) +{ + u32 mdio_in, div, mdio_out_khz, access_time; + + mdio_in = clk_get_rate(data->clk); + div = (mdio_in / data->pdata.bus_freq) - 1; + if (div > CONTROL_MAX_DIV) + div = CONTROL_MAX_DIV; + + /* set enable and clock divider */ + __raw_writel(div | CONTROL_ENABLE, &data->regs->control); + + /* + * One mdio transaction consists of: + * 32 bits of preamble + * 32 bits of transferred data + * 24 bits of bus yield (not needed unless shared?) + */ + mdio_out_khz = mdio_in / (1000 * (div + 1)); + access_time = (88 * 1000) / mdio_out_khz; + + /* + * In the worst case, we could be kicking off a user-access immediately + * after the mdio bus scan state-machine triggered its own read. If + * so, our request could get deferred by one access cycle. We + * defensively allow for 4 access cycles. + */ + data->access_time = usecs_to_jiffies(access_time * 4); + if (!data->access_time) + data->access_time = 1; +} + +static int davinci_mdio_reset(struct mii_bus *bus) +{ + struct davinci_mdio_data *data = bus->priv; + u32 phy_mask, ver; + + __davinci_mdio_reset(data); + + /* wait for scan logic to settle */ + msleep(PHY_MAX_ADDR * data->access_time); + + /* dump hardware version info */ + ver = __raw_readl(&data->regs->version); + dev_info(data->dev, "davinci mdio revision %d.%d\n", + (ver >> 8) & 0xff, ver & 0xff); + + /* get phy mask from the alive register */ + phy_mask = __raw_readl(&data->regs->alive); + if (phy_mask) { + /* restrict mdio bus to live phys only */ + dev_info(data->dev, "detected phy mask %x\n", ~phy_mask); + phy_mask = ~phy_mask; + } else { + /* desperately scan all phys */ + dev_warn(data->dev, "no live phy, scanning all\n"); + phy_mask = 0; + } + data->bus->phy_mask = phy_mask; + + return 0; +} + +/* wait until hardware is ready for another user access */ +static inline int wait_for_user_access(struct davinci_mdio_data *data) +{ + struct davinci_mdio_regs __iomem *regs = data->regs; + unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT); + u32 reg; + + while (time_after(timeout, jiffies)) { + reg = __raw_readl(®s->user[0].access); + if ((reg & USERACCESS_GO) == 0) + return 0; + + reg = __raw_readl(®s->control); + if ((reg & CONTROL_IDLE) == 0) + continue; + + /* + * An emac soft_reset may have clobbered the mdio controller's + * state machine. We need to reset and retry the current + * operation + */ + dev_warn(data->dev, "resetting idled controller\n"); + __davinci_mdio_reset(data); + return -EAGAIN; + } + dev_err(data->dev, "timed out waiting for user access\n"); + return -ETIMEDOUT; +} + +/* wait until hardware state machine is idle */ +static inline int wait_for_idle(struct davinci_mdio_data *data) +{ + struct davinci_mdio_regs __iomem *regs = data->regs; + unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT); + + while (time_after(timeout, jiffies)) { + if (__raw_readl(®s->control) & CONTROL_IDLE) + return 0; + } + dev_err(data->dev, "timed out waiting for idle\n"); + return -ETIMEDOUT; +} + +static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg) +{ + struct davinci_mdio_data *data = bus->priv; + u32 reg; + int ret; + + if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) + return -EINVAL; + + spin_lock(&data->lock); + + if (data->suspended) { + spin_unlock(&data->lock); + return -ENODEV; + } + + reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) | + (phy_id << 16)); + + while (1) { + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + if (ret < 0) + break; + + __raw_writel(reg, &data->regs->user[0].access); + + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + if (ret < 0) + break; + + reg = __raw_readl(&data->regs->user[0].access); + ret = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -EIO; + break; + } + + spin_unlock(&data->lock); + + return ret; +} + +static int davinci_mdio_write(struct mii_bus *bus, int phy_id, + int phy_reg, u16 phy_data) +{ + struct davinci_mdio_data *data = bus->priv; + u32 reg; + int ret; + + if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) + return -EINVAL; + + spin_lock(&data->lock); + + if (data->suspended) { + spin_unlock(&data->lock); + return -ENODEV; + } + + reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) | + (phy_id << 16) | (phy_data & USERACCESS_DATA)); + + while (1) { + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + if (ret < 0) + break; + + __raw_writel(reg, &data->regs->user[0].access); + + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + break; + } + + spin_unlock(&data->lock); + + return 0; +} + +static int __devinit davinci_mdio_probe(struct platform_device *pdev) +{ + struct mdio_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct davinci_mdio_data *data; + struct resource *res; + struct phy_device *phy; + int ret, addr; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(dev, "failed to alloc device data\n"); + return -ENOMEM; + } + + data->pdata = pdata ? (*pdata) : default_pdata; + + data->bus = mdiobus_alloc(); + if (!data->bus) { + dev_err(dev, "failed to alloc mii bus\n"); + ret = -ENOMEM; + goto bail_out; + } + + data->bus->name = dev_name(dev); + data->bus->read = davinci_mdio_read, + data->bus->write = davinci_mdio_write, + data->bus->reset = davinci_mdio_reset, + data->bus->parent = dev; + data->bus->priv = data; + snprintf(data->bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); + + data->clk = clk_get(dev, NULL); + if (IS_ERR(data->clk)) { + data->clk = NULL; + dev_err(dev, "failed to get device clock\n"); + ret = PTR_ERR(data->clk); + goto bail_out; + } + + clk_enable(data->clk); + + dev_set_drvdata(dev, data); + data->dev = dev; + spin_lock_init(&data->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "could not find register map resource\n"); + ret = -ENOENT; + goto bail_out; + } + + res = devm_request_mem_region(dev, res->start, resource_size(res), + dev_name(dev)); + if (!res) { + dev_err(dev, "could not allocate register map resource\n"); + ret = -ENXIO; + goto bail_out; + } + + data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!data->regs) { + dev_err(dev, "could not map mdio registers\n"); + ret = -ENOMEM; + goto bail_out; + } + + /* register the mii bus */ + ret = mdiobus_register(data->bus); + if (ret) + goto bail_out; + + /* scan and dump the bus */ + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { + phy = data->bus->phy_map[addr]; + if (phy) { + dev_info(dev, "phy[%d]: device %s, driver %s\n", + phy->addr, dev_name(&phy->dev), + phy->drv ? phy->drv->name : "unknown"); + } + } + + return 0; + +bail_out: + if (data->bus) + mdiobus_free(data->bus); + + if (data->clk) { + clk_disable(data->clk); + clk_put(data->clk); + } + + kfree(data); + + return ret; +} + +static int __devexit davinci_mdio_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct davinci_mdio_data *data = dev_get_drvdata(dev); + + if (data->bus) + mdiobus_free(data->bus); + + if (data->clk) { + clk_disable(data->clk); + clk_put(data->clk); + } + + dev_set_drvdata(dev, NULL); + + kfree(data); + + return 0; +} + +static int davinci_mdio_suspend(struct device *dev) +{ + struct davinci_mdio_data *data = dev_get_drvdata(dev); + u32 ctrl; + + spin_lock(&data->lock); + + /* shutdown the scan state machine */ + ctrl = __raw_readl(&data->regs->control); + ctrl &= ~CONTROL_ENABLE; + __raw_writel(ctrl, &data->regs->control); + wait_for_idle(data); + + if (data->clk) + clk_disable(data->clk); + + data->suspended = true; + spin_unlock(&data->lock); + + return 0; +} + +static int davinci_mdio_resume(struct device *dev) +{ + struct davinci_mdio_data *data = dev_get_drvdata(dev); + u32 ctrl; + + spin_lock(&data->lock); + if (data->clk) + clk_enable(data->clk); + + /* restart the scan state machine */ + ctrl = __raw_readl(&data->regs->control); + ctrl |= CONTROL_ENABLE; + __raw_writel(ctrl, &data->regs->control); + + data->suspended = false; + spin_unlock(&data->lock); + + return 0; +} + +static const struct dev_pm_ops davinci_mdio_pm_ops = { + .suspend = davinci_mdio_suspend, + .resume = davinci_mdio_resume, +}; + +static struct platform_driver davinci_mdio_driver = { + .driver = { + .name = "davinci_mdio", + .owner = THIS_MODULE, + .pm = &davinci_mdio_pm_ops, + }, + .probe = davinci_mdio_probe, + .remove = __devexit_p(davinci_mdio_remove), +}; + +static int __init davinci_mdio_init(void) +{ + return platform_driver_register(&davinci_mdio_driver); +} +device_initcall(davinci_mdio_init); + +static void __exit davinci_mdio_exit(void) +{ + platform_driver_unregister(&davinci_mdio_driver); +} +module_exit(davinci_mdio_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DaVinci MDIO driver"); diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index f3f8be5a35f..14f0955eca6 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -430,8 +430,8 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) } /* Get the protocol type of the ethernet frame that arrived */ - proto_type = ((in_be32(addr + XEL_HEADER_OFFSET + - XEL_RXBUFF_OFFSET) >> XEL_HEADER_SHIFT) & + proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET + + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); /* Check if received ethernet frame is a raw ethernet frame @@ -439,9 +439,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) { if (proto_type == ETH_P_IP) { - length = ((in_be32(addr + + length = ((ntohl(in_be32(addr + XEL_HEADER_IP_LENGTH_OFFSET + - XEL_RXBUFF_OFFSET) >> + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); length += ETH_HLEN + ETH_FCS_LEN; diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 6acbff389ab..aa675ebd8eb 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -4,7 +4,7 @@ config DTC config OF bool -menu "Flattened Device Tree and Open Firmware support" +menu "Device Tree and Open Firmware support" depends on OF config PROC_DEVICETREE @@ -19,6 +19,9 @@ config OF_FLATTREE bool select DTC +config OF_PROMTREE + bool + config OF_DYNAMIC def_bool y depends on PPC_OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 0052c405463..7888155bea0 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,6 @@ obj-y = base.o obj-$(CONFIG_OF_FLATTREE) += fdt.o +obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o obj-$(CONFIG_OF_DEVICE) += device.o platform.o diff --git a/drivers/of/address.c b/drivers/of/address.c index fcadb726d4f..3a1c7e70b19 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -163,7 +163,7 @@ static int of_bus_pci_translate(u32 *addr, u64 offset, int na) const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, unsigned int *flags) { - const u32 *prop; + const __be32 *prop; unsigned int psize; struct device_node *parent; struct of_bus *bus; diff --git a/drivers/of/base.c b/drivers/of/base.c index aa805250de7..710b53bfac6 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -33,7 +33,7 @@ DEFINE_RWLOCK(devtree_lock); int of_n_addr_cells(struct device_node *np) { - const int *ip; + const __be32 *ip; do { if (np->parent) @@ -49,7 +49,7 @@ EXPORT_SYMBOL(of_n_addr_cells); int of_n_size_cells(struct device_node *np) { - const int *ip; + const __be32 *ip; do { if (np->parent) diff --git a/drivers/of/device.c b/drivers/of/device.c index 92de0eb74ae..45d86530799 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -81,29 +81,10 @@ struct device_attribute of_platform_device_attrs[] = { __ATTR_NULL }; -/** - * of_release_dev - free an of device structure when all users of it are finished. - * @dev: device that's been disconnected - * - * Will be called only by the device core when all users of this of device are - * done. - */ -void of_release_dev(struct device *dev) -{ - struct platform_device *ofdev; - - ofdev = to_platform_device(dev); - of_node_put(ofdev->dev.of_node); - kfree(ofdev); -} -EXPORT_SYMBOL(of_release_dev); - -int of_device_register(struct platform_device *ofdev) +int of_device_add(struct platform_device *ofdev) { BUG_ON(ofdev->dev.of_node == NULL); - device_initialize(&ofdev->dev); - /* name and id have to be set so that the platform bus doesn't get * confused on matching */ ofdev->name = dev_name(&ofdev->dev); @@ -117,6 +98,12 @@ int of_device_register(struct platform_device *ofdev) return device_add(&ofdev->dev); } + +int of_device_register(struct platform_device *pdev) +{ + device_initialize(&pdev->dev); + return of_device_add(pdev); +} EXPORT_SYMBOL(of_device_register); void of_device_unregister(struct platform_device *ofdev) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65da5aec755..c1360e02f92 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -533,8 +533,6 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ - early_init_dt_scan_chosen_arch(node); - pr_debug("Command line is: %s\n", cmd_line); /* break now */ diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 6e595e5a397..75b0d3cb767 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -24,6 +24,11 @@ #include <linux/of_irq.h> #include <linux/string.h> +/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */ +#ifndef NO_IRQ +#define NO_IRQ 0 +#endif + /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space * @device: Device node of the device whose interrupt is to be mapped @@ -347,3 +352,37 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) return irq; } EXPORT_SYMBOL_GPL(of_irq_to_resource); + +/** + * of_irq_count - Count the number of IRQs a node uses + * @dev: pointer to device tree node + */ +int of_irq_count(struct device_node *dev) +{ + int nr = 0; + + while (of_irq_to_resource(dev, nr, NULL) != NO_IRQ) + nr++; + + return nr; +} + +/** + * of_irq_to_resource_table - Fill in resource table with node's IRQ info + * @dev: pointer to device tree node + * @res: array of resources to fill in + * @nr_irqs: the number of IRQs (and upper bound for num of @res elements) + * + * Returns the size of the filled in table (up to @nr_irqs). + */ +int of_irq_to_resource_table(struct device_node *dev, struct resource *res, + int nr_irqs) +{ + int i; + + for (i = 0; i < nr_irqs; i++, res++) + if (of_irq_to_resource(dev, i, res) == NO_IRQ) + break; + + return i; +} diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 0a694debd22..c85d3c7421f 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -12,6 +12,7 @@ */ #include <linux/i2c.h> +#include <linux/irq.h> #include <linux/of.h> #include <linux/of_i2c.h> #include <linux/of_irq.h> diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c new file mode 100644 index 00000000000..28295d0a50f --- /dev/null +++ b/drivers/of/pdt.c @@ -0,0 +1,276 @@ +/* pdt.c: OF PROM device tree support code. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * Adapted for sparc by David S. Miller davem@davemloft.net + * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_pdt.h> +#include <asm/prom.h> + +static struct of_pdt_ops *of_pdt_prom_ops __initdata; + +void __initdata (*of_pdt_build_more)(struct device_node *dp, + struct device_node ***nextp); + +#if defined(CONFIG_SPARC) +unsigned int of_pdt_unique_id __initdata; + +#define of_pdt_incr_unique_id(p) do { \ + (p)->unique_id = of_pdt_unique_id++; \ +} while (0) + +static inline const char *of_pdt_node_name(struct device_node *dp) +{ + return dp->path_component_name; +} + +#else + +static inline void of_pdt_incr_unique_id(void *p) { } +static inline void irq_trans_init(struct device_node *dp) { } + +static inline const char *of_pdt_node_name(struct device_node *dp) +{ + return dp->name; +} + +#endif /* !CONFIG_SPARC */ + +static struct property * __init of_pdt_build_one_prop(phandle node, char *prev, + char *special_name, + void *special_val, + int special_len) +{ + static struct property *tmp = NULL; + struct property *p; + int err; + + if (tmp) { + p = tmp; + memset(p, 0, sizeof(*p) + 32); + tmp = NULL; + } else { + p = prom_early_alloc(sizeof(struct property) + 32); + of_pdt_incr_unique_id(p); + } + + p->name = (char *) (p + 1); + if (special_name) { + strcpy(p->name, special_name); + p->length = special_len; + p->value = prom_early_alloc(special_len); + memcpy(p->value, special_val, special_len); + } else { + err = of_pdt_prom_ops->nextprop(node, prev, p->name); + if (err) { + tmp = p; + return NULL; + } + p->length = of_pdt_prom_ops->getproplen(node, p->name); + if (p->length <= 0) { + p->length = 0; + } else { + int len; + + p->value = prom_early_alloc(p->length + 1); + len = of_pdt_prom_ops->getproperty(node, p->name, + p->value, p->length); + if (len <= 0) + p->length = 0; + ((unsigned char *)p->value)[p->length] = '\0'; + } + } + return p; +} + +static struct property * __init of_pdt_build_prop_list(phandle node) +{ + struct property *head, *tail; + + head = tail = of_pdt_build_one_prop(node, NULL, + ".node", &node, sizeof(node)); + + tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); + tail = tail->next; + while(tail) { + tail->next = of_pdt_build_one_prop(node, tail->name, + NULL, NULL, 0); + tail = tail->next; + } + + return head; +} + +static char * __init of_pdt_get_one_property(phandle node, const char *name) +{ + char *buf = "<NULL>"; + int len; + + len = of_pdt_prom_ops->getproplen(node, name); + if (len > 0) { + buf = prom_early_alloc(len); + len = of_pdt_prom_ops->getproperty(node, name, buf, len); + } + + return buf; +} + +static char * __init of_pdt_try_pkg2path(phandle node) +{ + char *res, *buf = NULL; + int len; + + if (!of_pdt_prom_ops->pkg2path) + return NULL; + + if (of_pdt_prom_ops->pkg2path(node, buf, 0, &len)) + return NULL; + buf = prom_early_alloc(len + 1); + if (of_pdt_prom_ops->pkg2path(node, buf, len, &len)) { + pr_err("%s: package-to-path failed\n", __func__); + return NULL; + } + + res = strrchr(buf, '/'); + if (!res) { + pr_err("%s: couldn't find / in %s\n", __func__, buf); + return NULL; + } + return res+1; +} + +/* + * When fetching the node's name, first try using package-to-path; if + * that fails (either because the arch hasn't supplied a PROM callback, + * or some other random failure), fall back to just looking at the node's + * 'name' property. + */ +static char * __init of_pdt_build_name(phandle node) +{ + char *buf; + + buf = of_pdt_try_pkg2path(node); + if (!buf) + buf = of_pdt_get_one_property(node, "name"); + + return buf; +} + +static struct device_node * __init of_pdt_create_node(phandle node, + struct device_node *parent) +{ + struct device_node *dp; + + if (!node) + return NULL; + + dp = prom_early_alloc(sizeof(*dp)); + of_pdt_incr_unique_id(dp); + dp->parent = parent; + + kref_init(&dp->kref); + + dp->name = of_pdt_build_name(node); + dp->type = of_pdt_get_one_property(node, "device_type"); + dp->phandle = node; + + dp->properties = of_pdt_build_prop_list(node); + + irq_trans_init(dp); + + return dp; +} + +static char * __init of_pdt_build_full_name(struct device_node *dp) +{ + int len, ourlen, plen; + char *n; + + plen = strlen(dp->parent->full_name); + ourlen = strlen(of_pdt_node_name(dp)); + len = ourlen + plen + 2; + + n = prom_early_alloc(len); + strcpy(n, dp->parent->full_name); + if (!of_node_is_root(dp->parent)) { + strcpy(n + plen, "/"); + plen++; + } + strcpy(n + plen, of_pdt_node_name(dp)); + + return n; +} + +static struct device_node * __init of_pdt_build_tree(struct device_node *parent, + phandle node, + struct device_node ***nextp) +{ + struct device_node *ret = NULL, *prev_sibling = NULL; + struct device_node *dp; + + while (1) { + dp = of_pdt_create_node(node, parent); + if (!dp) + break; + + if (prev_sibling) + prev_sibling->sibling = dp; + + if (!ret) + ret = dp; + prev_sibling = dp; + + *(*nextp) = dp; + *nextp = &dp->allnext; + +#if defined(CONFIG_SPARC) + dp->path_component_name = build_path_component(dp); +#endif + dp->full_name = of_pdt_build_full_name(dp); + + dp->child = of_pdt_build_tree(dp, + of_pdt_prom_ops->getchild(node), nextp); + + if (of_pdt_build_more) + of_pdt_build_more(dp, nextp); + + node = of_pdt_prom_ops->getsibling(node); + } + + return ret; +} + +void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) +{ + struct device_node **nextp; + + BUG_ON(!ops); + of_pdt_prom_ops = ops; + + allnodes = of_pdt_create_node(root_node, NULL); +#if defined(CONFIG_SPARC) + allnodes->path_component_name = ""; +#endif + allnodes->full_name = "/"; + + nextp = &allnodes->allnext; + allnodes->child = of_pdt_build_tree(allnodes, + of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); +} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index bb72223c22a..5b4a07f1220 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -584,34 +584,33 @@ struct platform_device *of_device_alloc(struct device_node *np, struct device *parent) { struct platform_device *dev; - int rc, i, num_reg = 0, num_irq = 0; + int rc, i, num_reg = 0, num_irq; struct resource *res, temp_res; - /* First count how many resources are needed */ - while (of_address_to_resource(np, num_reg, &temp_res) == 0) - num_reg++; - while (of_irq_to_resource(np, num_irq, &temp_res) != NO_IRQ) - num_irq++; - - /* Allocate memory for both the struct device and the resource table */ - dev = kzalloc(sizeof(*dev) + (sizeof(*res) * (num_reg + num_irq)), - GFP_KERNEL); + dev = platform_device_alloc("", -1); if (!dev) return NULL; - res = (struct resource *) &dev[1]; + + /* count the io and irq resources */ + while (of_address_to_resource(np, num_reg, &temp_res) == 0) + num_reg++; + num_irq = of_irq_count(np); /* Populate the resource table */ if (num_irq || num_reg) { + res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); + if (!res) { + platform_device_put(dev); + return NULL; + } + dev->num_resources = num_reg + num_irq; dev->resource = res; for (i = 0; i < num_reg; i++, res++) { rc = of_address_to_resource(np, i, res); WARN_ON(rc); } - for (i = 0; i < num_irq; i++, res++) { - rc = of_irq_to_resource(np, i, res); - WARN_ON(rc == NO_IRQ); - } + WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq); } dev->dev.of_node = of_node_get(np); @@ -619,7 +618,6 @@ struct platform_device *of_device_alloc(struct device_node *np, dev->dev.dma_mask = &dev->archdata.dma_mask; #endif dev->dev.parent = parent; - dev->dev.release = of_release_dev; if (bus_id) dev_set_name(&dev->dev, "%s", bus_id); @@ -657,8 +655,8 @@ struct platform_device *of_platform_device_create(struct device_node *np, * to do such, possibly using a device notifier */ - if (of_device_register(dev) != 0) { - of_device_free(dev); + if (of_device_add(dev) != 0) { + platform_device_put(dev); return NULL; } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index cff7cc2c1f0..faec777b1ed 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -92,6 +92,7 @@ config DELL_WMI tristate "Dell WMI extras" depends on ACPI_WMI depends on INPUT + select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops. @@ -140,6 +141,7 @@ config HP_WMI depends on ACPI_WMI depends on INPUT depends on RFKILL || RFKILL = n + select INPUT_SPARSEKMAP help Say Y here if you want to support WMI-based hotkeys on HP laptops and to read data from WMI such as docking or ambient light sensor state. @@ -171,6 +173,7 @@ config PANASONIC_LAPTOP tristate "Panasonic Laptop Extras" depends on INPUT && ACPI depends on BACKLIGHT_CLASS_DEVICE + select INPUT_SPARSEKMAP ---help--- This driver adds support for access to backlight control and hotkeys on Panasonic Let's Note laptops. @@ -219,8 +222,8 @@ config SONYPI_COMPAT ---help--- Build the sonypi driver compatibility code into the sony-laptop driver. -config IDEAPAD_ACPI - tristate "Lenovo IdeaPad ACPI Laptop Extras" +config IDEAPAD_LAPTOP + tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI depends on RFKILL help @@ -365,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL If you are not sure, say Y here. The driver enables polling only if it is strictly necessary to do so. +config SENSORS_HDAPS + tristate "Thinkpad Hard Drive Active Protection System (hdaps)" + depends on INPUT && X86 + select INPUT_POLLDEV + default n + help + This driver provides support for the IBM Hard Drive Active Protection + System (hdaps), which provides an accelerometer and other misc. data. + ThinkPads starting with the R50, T41, and X40 are supported. The + accelerometer data is readable via sysfs. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + If your ThinkPad is not recognized by the driver, please update to latest + BIOS. This is especially the case for some R52 ThinkPads. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of hdaps. + config INTEL_MENLOW tristate "Thermal Management driver for Intel menlow platform" depends on ACPI_THERMAL @@ -478,6 +501,7 @@ config TOPSTAR_LAPTOP tristate "Topstar Laptop Extras" depends on ACPI depends on INPUT + select INPUT_SPARSEKMAP ---help--- This driver adds support for hotkeys found on Topstar laptops. @@ -492,6 +516,7 @@ config ACPI_TOSHIBA depends on INPUT depends on RFKILL || RFKILL = n select INPUT_POLLDEV + select INPUT_SPARSEKMAP ---help--- This driver adds support for access to certain system settings on "legacy free" Toshiba laptops. These laptops can be recognized by @@ -590,4 +615,28 @@ config INTEL_IPS functionality. If in doubt, say Y here; it will only load on supported platforms. +config IBM_RTL + tristate "Device driver to enable PRTL support" + depends on X86 && PCI + ---help--- + Enable support for IBM Premium Real Time Mode (PRTM). + This module will allow you the enter and exit PRTM in the BIOS via + sysfs on platforms that support this feature. System in PRTM will + not receive CPU-generated SMIs for recoverable errors. Use of this + feature without proper support may void your hardware warranty. + + If the proper BIOS support is found the driver will load and create + /sys/devices/system/ibm_rtl/. The "state" variable will indicate + whether or not the BIOS is in PRTM. + state = 0 (BIOS SMIs on) + state = 1 (BIOS SMIs off) + +config XO1_RFKILL + tristate "OLPC XO-1 software RF kill switch" + depends on OLPC + depends on RFKILL + ---help--- + Support for enabling/disabling the WLAN interface on the OLPC XO-1 + laptop. + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 85fb2b84f57..9950ccc940b 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o -obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o +obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o @@ -30,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o - +obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o +obj-$(CONFIG_IBM_RTL) += ibm_rtl.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 2badee2fdee..c8c65375bfe 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void) AMW0_find_mailled(); if (!interface) { - printk(ACER_ERR "No or unsupported WMI interface, unable to " + printk(ACER_INFO "No or unsupported WMI interface, unable to " "load\n"); return -ENODEV; } diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b756e07d41b..60a5a5c6b50 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -236,7 +236,6 @@ struct asus_laptop { u8 light_level; /* light sensor level */ u8 light_switch; /* light sensor switch value */ u16 event_count[128]; /* count for each event TODO make this better */ - u16 *keycode_map; }; static const struct key_entry asus_keymap[] = { @@ -278,6 +277,7 @@ static const struct key_entry asus_keymap[] = { {KE_KEY, 0x99, { KEY_PHONE } }, {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, + {KE_KEY, 0xb5, { KEY_CALC } }, {KE_END, 0}, }; @@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus) static int asus_backlight_init(struct asus_laptop *asus) { struct backlight_device *bd; - struct device *dev = &asus->platform_device->dev; struct backlight_properties props; - if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && - !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && - lcd_switch_handle) { - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 15; - - bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, - asus, &asusbl_ops, &props); - if (IS_ERR(bd)) { - pr_err("Could not register asus backlight device\n"); - asus->backlight_device = NULL; - return PTR_ERR(bd); - } + if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) || + acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) || + !lcd_switch_handle) + return 0; - asus->backlight_device = bd; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; - bd->props.power = FB_BLANK_UNBLANK; - bd->props.brightness = asus_read_brightness(bd); - backlight_update_status(bd); + bd = backlight_device_register(ASUS_LAPTOP_FILE, + &asus->platform_device->dev, asus, + &asusbl_ops, &props); + if (IS_ERR(bd)) { + pr_err("Could not register asus backlight device\n"); + asus->backlight_device = NULL; + return PTR_ERR(bd); } + + asus->backlight_device = bd; + bd->props.brightness = asus_read_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); return 0; } @@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, */ static int asus_gps_rfkill_set(void *data, bool blocked) { - acpi_handle handle = data; + struct asus_laptop *asus = data; - return asus_gps_switch(handle, !blocked); + return asus_gps_switch(asus, !blocked); } static const struct rfkill_ops asus_gps_rfkill_ops = { @@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus) asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, RFKILL_TYPE_GPS, - &asus_gps_rfkill_ops, NULL); + &asus_gps_rfkill_ops, asus); if (!asus->gps_rfkill) return -EINVAL; @@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus) input->phys = ASUS_LAPTOP_FILE "/input0"; input->id.bustype = BUS_HOST; input->dev.parent = &asus->platform_device->dev; - input_set_drvdata(input, asus); error = sparse_keymap_setup(input, asus_keymap, NULL); if (error) { @@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus) sparse_keymap_free(asus->inputdev); input_unregister_device(asus->inputdev); } + asus->inputdev = NULL; } /* @@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); -static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, - store_bluetooth); +static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, + show_bluetooth, store_bluetooth); static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); -static void asus_sysfs_exit(struct asus_laptop *asus) -{ - struct platform_device *device = asus->platform_device; - - device_remove_file(&device->dev, &dev_attr_infos); - device_remove_file(&device->dev, &dev_attr_wlan); - device_remove_file(&device->dev, &dev_attr_bluetooth); - device_remove_file(&device->dev, &dev_attr_display); - device_remove_file(&device->dev, &dev_attr_ledd); - device_remove_file(&device->dev, &dev_attr_ls_switch); - device_remove_file(&device->dev, &dev_attr_ls_level); - device_remove_file(&device->dev, &dev_attr_gps); -} +static struct attribute *asus_attributes[] = { + &dev_attr_infos.attr, + &dev_attr_wlan.attr, + &dev_attr_bluetooth.attr, + &dev_attr_display.attr, + &dev_attr_ledd.attr, + &dev_attr_ls_level.attr, + &dev_attr_ls_switch.attr, + &dev_attr_gps.attr, + NULL +}; -static int asus_sysfs_init(struct asus_laptop *asus) +static mode_t asus_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, + int idx) { - struct platform_device *device = asus->platform_device; - int err; + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev); + struct asus_laptop *asus = platform_get_drvdata(pdev); + acpi_handle handle = asus->handle; + bool supported; - err = device_create_file(&device->dev, &dev_attr_infos); - if (err) - return err; + if (attr == &dev_attr_wlan.attr) { + supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); - if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { - err = device_create_file(&device->dev, &dev_attr_wlan); - if (err) - return err; - } + } else if (attr == &dev_attr_bluetooth.attr) { + supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL); - if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { - err = device_create_file(&device->dev, &dev_attr_bluetooth); - if (err) - return err; - } + } else if (attr == &dev_attr_display.attr) { + supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL); - if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { - err = device_create_file(&device->dev, &dev_attr_display); - if (err) - return err; - } + } else if (attr == &dev_attr_ledd.attr) { + supported = !acpi_check_handle(handle, METHOD_LEDD, NULL); - if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { - err = device_create_file(&device->dev, &dev_attr_ledd); - if (err) - return err; - } - - if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && - !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { - err = device_create_file(&device->dev, &dev_attr_ls_switch); - if (err) - return err; - err = device_create_file(&device->dev, &dev_attr_ls_level); - if (err) - return err; - } + } else if (attr == &dev_attr_ls_switch.attr || + attr == &dev_attr_ls_level.attr) { + supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && + !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); - if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && - !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && - !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) { - err = device_create_file(&device->dev, &dev_attr_gps); - if (err) - return err; + } else if (attr == &dev_attr_gps.attr) { + supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && + !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && + !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL); + } else { + supported = true; } - return err; + return supported ? attr->mode : 0; } + +static const struct attribute_group asus_attr_group = { + .is_visible = asus_sysfs_is_visible, + .attrs = asus_attributes, +}; + static int asus_platform_init(struct asus_laptop *asus) { - int err; + int result; asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); if (!asus->platform_device) return -ENOMEM; platform_set_drvdata(asus->platform_device, asus); - err = platform_device_add(asus->platform_device); - if (err) + result = platform_device_add(asus->platform_device); + if (result) goto fail_platform_device; - err = asus_sysfs_init(asus); - if (err) + result = sysfs_create_group(&asus->platform_device->dev.kobj, + &asus_attr_group); + if (result) goto fail_sysfs; + return 0; fail_sysfs: - asus_sysfs_exit(asus); platform_device_del(asus->platform_device); fail_platform_device: platform_device_put(asus->platform_device); - return err; + return result; } static void asus_platform_exit(struct asus_laptop *asus) { - asus_sysfs_exit(asus); + sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group); platform_device_unregister(asus->platform_device); } @@ -1428,8 +1417,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus) return AE_OK; } -static bool asus_device_present; - static int __devinit asus_acpi_init(struct asus_laptop *asus) { int result = 0; @@ -1474,6 +1461,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) return result; } +static bool asus_device_present; + static int __devinit asus_acpi_add(struct acpi_device *device) { struct asus_laptop *asus; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 4413975912e..cf8a89a0d8f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -25,6 +25,8 @@ #include <linux/mm.h> #include <linux/i8042.h> #include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = { .query = dell_rfkill_query, }; +static struct dentry *dell_laptop_dir; + +static int dell_debugfs_show(struct seq_file *s, void *data) +{ + int status; + + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); + + seq_printf(s, "status:\t0x%X\n", status); + seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", + status & BIT(0)); + seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n", + (status & BIT(1)) >> 1); + seq_printf(s, "Bit 2 : Wifi is supported: %lu\n", + (status & BIT(2)) >> 2); + seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n", + (status & BIT(3)) >> 3); + seq_printf(s, "Bit 4 : WWAN is supported: %lu\n", + (status & BIT(4)) >> 4); + seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n", + (status & BIT(5)) >> 5); + seq_printf(s, "Bit 8 : Wifi is installed: %lu\n", + (status & BIT(8)) >> 8); + seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n", + (status & BIT(9)) >> 9); + seq_printf(s, "Bit 10: WWAN is installed: %lu\n", + (status & BIT(10)) >> 10); + seq_printf(s, "Bit 16: Hardware switch is on: %lu\n", + (status & BIT(16)) >> 16); + seq_printf(s, "Bit 17: Wifi is blocked: %lu\n", + (status & BIT(17)) >> 17); + seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n", + (status & BIT(18)) >> 18); + seq_printf(s, "Bit 19: WWAN is blocked: %lu\n", + (status & BIT(19)) >> 19); + + seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); + seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", + hwswitch_state & BIT(0)); + seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", + (hwswitch_state & BIT(1)) >> 1); + seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n", + (hwswitch_state & BIT(2)) >> 2); + seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n", + (hwswitch_state & BIT(7)) >> 7); + seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n", + (hwswitch_state & BIT(8)) >> 8); + seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n", + (hwswitch_state & BIT(15)) >> 15); + + return 0; +} + +static int dell_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, dell_debugfs_show, inode->i_private); +} + +static const struct file_operations dell_debugfs_fops = { + .owner = THIS_MODULE, + .open = dell_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void dell_update_rfkill(struct work_struct *ignored) { if (wifi_rfkill) @@ -556,6 +627,11 @@ static int __init dell_init(void) goto fail_filter; } + dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); + if (dell_laptop_dir != NULL) + debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, + &dell_debugfs_fops); + #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't * register the platform controller. @@ -615,6 +691,7 @@ fail_platform_driver: static void __exit dell_exit(void) { + debugfs_remove_recursive(dell_laptop_dir); i8042_remove_filter(dell_laptop_i8042_filter); cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 08fb70f6d9b..77f1d55414c 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <acpi/acpi_drivers.h> #include <linux/acpi.h> #include <linux/string.h> @@ -44,78 +45,70 @@ static int acpi_video; MODULE_ALIAS("wmi:"DELL_EVENT_GUID); -struct key_entry { - char type; /* See KE_* below */ - u16 code; - u16 keycode; -}; - -enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; - /* * Certain keys are flagged as KE_IGNORE. All of these are either * notifications (rather than requests for change) or are also sent * via the keyboard controller so should not be sent again. */ -static struct key_entry dell_legacy_wmi_keymap[] = { - {KE_KEY, 0xe045, KEY_PROG1}, - {KE_KEY, 0xe009, KEY_EJECTCD}, +static const struct key_entry dell_wmi_legacy_keymap[] __initconst = { + { KE_KEY, 0xe045, { KEY_PROG1 } }, + { KE_KEY, 0xe009, { KEY_EJECTCD } }, /* These also contain the brightness level at offset 6 */ - {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, - {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, + { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, /* Battery health status button */ - {KE_KEY, 0xe007, KEY_BATTERY}, + { KE_KEY, 0xe007, { KEY_BATTERY } }, /* This is actually for all radios. Although physically a * switch, the notification does not provide an indication of * state and so it should be reported as a key */ - {KE_KEY, 0xe008, KEY_WLAN}, + { KE_KEY, 0xe008, { KEY_WLAN } }, /* The next device is at offset 6, the active devices are at offset 8 and the attached devices at offset 10 */ - {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, + { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, - {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, + { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, /* BIOS error detected */ - {KE_IGNORE, 0xe00d, KEY_RESERVED}, + { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, /* Wifi Catcher */ - {KE_KEY, 0xe011, KEY_PROG2}, + { KE_KEY, 0xe011, {KEY_PROG2 } }, /* Ambient light sensor toggle */ - {KE_IGNORE, 0xe013, KEY_RESERVED}, - - {KE_IGNORE, 0xe020, KEY_MUTE}, - {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, - {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, - {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, - {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, - {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, - {KE_IGNORE, 0xe045, KEY_NUMLOCK}, - {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, - {KE_END, 0} + { KE_IGNORE, 0xe013, { KEY_RESERVED } }, + + { KE_IGNORE, 0xe020, { KEY_MUTE } }, + { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, + { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, + { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, + { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, + { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, + { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, + { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, + { KE_END, 0 } }; static bool dell_new_hk_type; -struct dell_new_keymap_entry { +struct dell_bios_keymap_entry { u16 scancode; u16 keycode; }; -struct dell_hotkey_table { +struct dell_bios_hotkey_table { struct dmi_header header; - struct dell_new_keymap_entry keymap[]; + struct dell_bios_keymap_entry keymap[]; }; -static struct key_entry *dell_new_wmi_keymap; +static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; -static u16 bios_to_linux_keycode[256] = { +static const u16 bios_to_linux_keycode[256] __initconst = { KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, @@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = { KEY_PROG3 }; - -static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; - static struct input_dev *dell_wmi_input_dev; -static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code) -{ - struct key_entry *key; - - for (key = dell_wmi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode) -{ - struct key_entry *key; - - for (key = dell_wmi_keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - -static int dell_wmi_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int dell_wmi_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct key_entry *key; - unsigned int old_keycode; - - key = dell_wmi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!dell_wmi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - return -EINVAL; -} - static void dell_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - static struct key_entry *key; union acpi_object *obj; acpi_status status; @@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context) obj = (union acpi_object *)response.pointer; if (obj && obj->type == ACPI_TYPE_BUFFER) { + const struct key_entry *key; int reported_key; u16 *buffer_entry = (u16 *)obj->buffer.pointer; + if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { printk(KERN_INFO "dell-wmi: Received unknown WMI event" " (0x%x)\n", buffer_entry[1]); @@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context) else reported_key = (int)buffer_entry[1] & 0xffff; - key = dell_wmi_get_entry_by_scancode(reported_key); - + key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, + reported_key); if (!key) { printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", reported_key); @@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context) * come via ACPI */ ; } else { - input_report_key(dell_wmi_input_dev, key->keycode, 1); - input_sync(dell_wmi_input_dev); - input_report_key(dell_wmi_input_dev, key->keycode, 0); - input_sync(dell_wmi_input_dev); + sparse_keymap_report_entry(dell_wmi_input_dev, key, + 1, true); } } kfree(obj); } - -static void setup_new_hk_map(const struct dmi_header *dm) +static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) { - + int hotkey_num = (dell_bios_hotkey_table->header.length - 4) / + sizeof(struct dell_bios_keymap_entry); + struct key_entry *keymap; int i; - int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); - struct dell_hotkey_table *table = - container_of(dm, struct dell_hotkey_table, header); - dell_new_wmi_keymap = kzalloc((hotkey_num+1) * - sizeof(struct key_entry), GFP_KERNEL); + keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL); + if (!keymap) + return NULL; for (i = 0; i < hotkey_num; i++) { - dell_new_wmi_keymap[i].type = KE_KEY; - dell_new_wmi_keymap[i].code = table->keymap[i].scancode; - dell_new_wmi_keymap[i].keycode = - (table->keymap[i].keycode > 255) ? 0 : - bios_to_linux_keycode[table->keymap[i].keycode]; + const struct dell_bios_keymap_entry *bios_entry = + &dell_bios_hotkey_table->keymap[i]; + keymap[i].type = KE_KEY; + keymap[i].code = bios_entry->scancode; + keymap[i].keycode = bios_entry->keycode < 256 ? + bios_to_linux_keycode[bios_entry->keycode] : + KEY_RESERVED; } - dell_new_wmi_keymap[i].type = KE_END; - dell_new_wmi_keymap[i].code = 0; - dell_new_wmi_keymap[i].keycode = 0; - - dell_wmi_keymap = dell_new_wmi_keymap; + keymap[hotkey_num].type = KE_END; + return keymap; } - -static void find_hk_type(const struct dmi_header *dm, void *dummy) -{ - - if ((dm->type == 0xb2) && (dm->length > 6)) { - dell_new_hk_type = true; - setup_new_hk_map(dm); - } - -} - - static int __init dell_wmi_input_setup(void) { - struct key_entry *key; int err; dell_wmi_input_dev = input_allocate_device(); - if (!dell_wmi_input_dev) return -ENOMEM; dell_wmi_input_dev->name = "Dell WMI hotkeys"; dell_wmi_input_dev->phys = "wmi/input0"; dell_wmi_input_dev->id.bustype = BUS_HOST; - dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; - dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; - - for (key = dell_wmi_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, dell_wmi_input_dev->evbit); - set_bit(key->keycode, dell_wmi_input_dev->keybit); - break; - case KE_SW: - set_bit(EV_SW, dell_wmi_input_dev->evbit); - set_bit(key->keycode, dell_wmi_input_dev->swbit); - break; + + if (dell_new_hk_type) { + const struct key_entry *keymap = dell_wmi_prepare_new_keymap(); + if (!keymap) { + err = -ENOMEM; + goto err_free_dev; } - } - err = input_register_device(dell_wmi_input_dev); + err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); - if (err) { - input_free_device(dell_wmi_input_dev); - return err; + /* + * Sparse keymap library makes a copy of keymap so we + * don't need the original one that was allocated. + */ + kfree(keymap); + } else { + err = sparse_keymap_setup(dell_wmi_input_dev, + dell_wmi_legacy_keymap, NULL); } + if (err) + goto err_free_dev; + + err = input_register_device(dell_wmi_input_dev); + if (err) + goto err_free_keymap; return 0; + + err_free_keymap: + sparse_keymap_free(dell_wmi_input_dev); + err_free_dev: + input_free_device(dell_wmi_input_dev); + return err; +} + +static void dell_wmi_input_destroy(void) +{ + sparse_keymap_free(dell_wmi_input_dev); + input_unregister_device(dell_wmi_input_dev); +} + +static void __init find_hk_type(const struct dmi_header *dm, void *dummy) +{ + if (dm->type == 0xb2 && dm->length > 6) { + dell_new_hk_type = true; + dell_bios_hotkey_table = + container_of(dm, struct dell_bios_hotkey_table, header); + } } static int __init dell_wmi_init(void) @@ -339,18 +283,13 @@ static int __init dell_wmi_init(void) acpi_video = acpi_video_backlight_support(); err = dell_wmi_input_setup(); - if (err) { - if (dell_new_hk_type) - kfree(dell_wmi_keymap); + if (err) return err; - } status = wmi_install_notify_handler(DELL_EVENT_GUID, dell_wmi_notify, NULL); if (ACPI_FAILURE(status)) { - input_unregister_device(dell_wmi_input_dev); - if (dell_new_hk_type) - kfree(dell_wmi_keymap); + dell_wmi_input_destroy(); printk(KERN_ERR "dell-wmi: Unable to register notify handler - %d\n", status); @@ -359,14 +298,11 @@ static int __init dell_wmi_init(void) return 0; } +module_init(dell_wmi_init); static void __exit dell_wmi_exit(void) { wmi_remove_notify_handler(DELL_EVENT_GUID); - input_unregister_device(dell_wmi_input_dev); - if (dell_new_hk_type) - kfree(dell_wmi_keymap); + dell_wmi_input_destroy(); } - -module_init(dell_wmi_init); module_exit(dell_wmi_exit); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 6b8e06206c4..b2edfdcdcb8 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -165,6 +165,7 @@ struct eeepc_laptop { u16 event_count[128]; /* count for each event */ struct platform_device *platform_device; + struct acpi_device *device; /* the device we are in */ struct device *hwmon_device; struct backlight_device *backlight_device; @@ -1193,9 +1194,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) eeepc->inputdev = input; return 0; - err_free_keymap: +err_free_keymap: sparse_keymap_free(input); - err_free_dev: +err_free_dev: input_free_device(input); return error; } @@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) sparse_keymap_free(eeepc->inputdev); input_unregister_device(eeepc->inputdev); } + eeepc->inputdev = NULL; } /* @@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc) cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); } -static int eeepc_acpi_init(struct eeepc_laptop *eeepc, - struct acpi_device *device) +static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc) { unsigned int init_flags; int result; - result = acpi_bus_get_status(device); + result = acpi_bus_get_status(eeepc->device); if (result) return result; - if (!device->status.present) { + if (!eeepc->device->status.present) { pr_err("Hotkey device not present, aborting\n"); return -ENODEV; } @@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); device->driver_data = eeepc; + eeepc->device = device; eeepc->hotplug_disabled = hotplug_disabled; eeepc_dmi_check(eeepc); - result = eeepc_acpi_init(eeepc, device); + result = eeepc_acpi_init(eeepc); if (result) goto fail_platform; eeepc_enable_camera(eeepc); diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9dc50fbf3d0..462ceab93f8 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_METHODID_DEVS 0x53564544 #define EEEPC_WMI_METHODID_DSTS 0x53544344 +#define EEEPC_WMI_METHODID_CFVS 0x53564643 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 @@ -69,6 +70,11 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0xe1, { KEY_F14 } }, + { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, + { KE_KEY, 0x5c, { KEY_F15 } }, { KE_END, 0}, }; @@ -292,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context) kfree(obj); } +static int store_cpufv(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + struct acpi_buffer input = { (acpi_size)sizeof(value), &value }; + acpi_status status; + + if (!count || sscanf(buf, "%i", &value) != 1) + return -EINVAL; + if (value < 0 || value > 2) + return -EINVAL; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, + 1, EEEPC_WMI_METHODID_CFVS, &input, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + else + return count; +} + +static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); + +static void eeepc_wmi_sysfs_exit(struct platform_device *device) +{ + device_remove_file(&device->dev, &dev_attr_cpufv); +} + +static int eeepc_wmi_sysfs_init(struct platform_device *device) +{ + int retval = -ENOMEM; + + retval = device_create_file(&device->dev, &dev_attr_cpufv); + if (retval) + goto error_sysfs; + + return 0; + +error_sysfs: + eeepc_wmi_sysfs_exit(platform_device); + return retval; +} + static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -387,8 +436,14 @@ static int __init eeepc_wmi_init(void) goto del_dev; } + err = eeepc_wmi_sysfs_init(platform_device); + if (err) + goto del_sysfs; + return 0; +del_sysfs: + eeepc_wmi_sysfs_exit(platform_device); del_dev: platform_device_del(platform_device); put_dev: @@ -403,6 +458,7 @@ static void __exit eeepc_wmi_exit(void) { struct eeepc_wmi *eeepc; + eeepc_wmi_sysfs_exit(platform_device); eeepc = platform_get_drvdata(platform_device); platform_driver_unregister(&platform_driver); platform_device_unregister(platform_device); diff --git a/drivers/hwmon/hdaps.c b/drivers/platform/x86/hdaps.c index bfd42f18924..067bf36d32f 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -1,5 +1,5 @@ /* - * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System + * hdaps.c - driver for IBM's Hard Drive Active Protection System * * Copyright (C) 2005 Robert Love <rml@novell.com> * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c1741142a4c..1dac659b5e0 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/rfkill.h> @@ -88,24 +89,16 @@ struct bios_return { u32 value; }; -struct key_entry { - char type; /* See KE_* below */ - u16 code; - u16 keycode; -}; - -enum { KE_KEY, KE_END }; - -static struct key_entry hp_wmi_keymap[] = { - {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, - {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, - {KE_KEY, 0x20e6, KEY_PROG1}, - {KE_KEY, 0x20e8, KEY_MEDIA}, - {KE_KEY, 0x2142, KEY_MEDIA}, - {KE_KEY, 0x213b, KEY_INFO}, - {KE_KEY, 0x2169, KEY_DIRECTION}, - {KE_KEY, 0x231b, KEY_HELP}, - {KE_END, 0} +static const struct key_entry hp_wmi_keymap[] = { + { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x20e6, { KEY_PROG1 } }, + { KE_KEY, 0x20e8, { KEY_MEDIA } }, + { KE_KEY, 0x2142, { KEY_MEDIA } }, + { KE_KEY, 0x213b, { KEY_INFO } }, + { KE_KEY, 0x2169, { KEY_DIRECTION } }, + { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_END, 0 } }; static struct input_dev *hp_wmi_input_dev; @@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); -static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) -{ - struct key_entry *key; - - for (key = hp_wmi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) -{ - struct key_entry *key; - - for (key = hp_wmi_keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - -static int hp_wmi_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int hp_wmi_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct key_entry *key; - unsigned int old_keycode; - - key = hp_wmi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!hp_wmi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - - return -EINVAL; -} - static void hp_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - static struct key_entry *key; union acpi_object *obj; u32 event_id, event_data; int key_code = 0, ret; @@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context) sizeof(key_code)); if (ret) break; - key = hp_wmi_get_entry_by_scancode(key_code); - if (key) { - switch (key->type) { - case KE_KEY: - input_report_key(hp_wmi_input_dev, - key->keycode, 1); - input_sync(hp_wmi_input_dev); - input_report_key(hp_wmi_input_dev, - key->keycode, 0); - input_sync(hp_wmi_input_dev); - break; - } - } else + + if (!sparse_keymap_report_event(hp_wmi_input_dev, + key_code, 1, true)) printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", key_code); break; @@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context) static int __init hp_wmi_input_setup(void) { - struct key_entry *key; + acpi_status status; int err; hp_wmi_input_dev = input_allocate_device(); @@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void) hp_wmi_input_dev->name = "HP WMI hotkeys"; hp_wmi_input_dev->phys = "wmi/input0"; hp_wmi_input_dev->id.bustype = BUS_HOST; - hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; - hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; - - for (key = hp_wmi_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, hp_wmi_input_dev->evbit); - set_bit(key->keycode, hp_wmi_input_dev->keybit); - break; - } - } - set_bit(EV_SW, hp_wmi_input_dev->evbit); - set_bit(SW_DOCK, hp_wmi_input_dev->swbit); - set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + __set_bit(EV_SW, hp_wmi_input_dev->evbit); + __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + + err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); + if (err) + goto err_free_dev; /* Set initial hardware state */ input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); @@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void) hp_wmi_tablet_state()); input_sync(hp_wmi_input_dev); - err = input_register_device(hp_wmi_input_dev); - - if (err) { - input_free_device(hp_wmi_input_dev); - return err; + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_keymap; } + err = input_register_device(hp_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + return 0; + + err_uninstall_notifier: + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + err_free_keymap: + sparse_keymap_free(hp_wmi_input_dev); + err_free_dev: + input_free_device(hp_wmi_input_dev); + return err; +} + +static void hp_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + sparse_keymap_free(hp_wmi_input_dev); + input_unregister_device(hp_wmi_input_dev); } static void cleanup_sysfs(struct platform_device *device) @@ -704,15 +643,9 @@ static int __init hp_wmi_init(void) int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); if (event_capable) { - err = wmi_install_notify_handler(HPWMI_EVENT_GUID, - hp_wmi_notify, NULL); - if (ACPI_FAILURE(err)) - return -EINVAL; err = hp_wmi_input_setup(); - if (err) { - wmi_remove_notify_handler(HPWMI_EVENT_GUID); + if (err) return err; - } } if (bios_capable) { @@ -739,20 +672,17 @@ err_device_add: err_device_alloc: platform_driver_unregister(&hp_wmi_driver); err_driver_reg: - if (wmi_has_guid(HPWMI_EVENT_GUID)) { - input_unregister_device(hp_wmi_input_dev); - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - } + if (event_capable) + hp_wmi_input_destroy(); return err; } static void __exit hp_wmi_exit(void) { - if (wmi_has_guid(HPWMI_EVENT_GUID)) { - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - input_unregister_device(hp_wmi_input_dev); - } + if (wmi_has_guid(HPWMI_EVENT_GUID)) + hp_wmi_input_destroy(); + if (hp_wmi_platform_dev) { platform_device_unregister(hp_wmi_platform_dev); platform_driver_unregister(&hp_wmi_driver); diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c new file mode 100644 index 00000000000..3c2c6b91ecb --- /dev/null +++ b/drivers/platform/x86/ibm_rtl.c @@ -0,0 +1,341 @@ +/* + * IBM Real-Time Linux driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2010 + * + * Author: Keith Mannthey <kmannth@us.ibm.com> + * Vernon Mauery <vernux@us.ibm.com> + * + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/sysdev.h> +#include <linux/dmi.h> +#include <linux/mutex.h> +#include <asm/bios_ebda.h> + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Show debug output"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>"); +MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>"); + +#define RTL_ADDR_TYPE_IO 1 +#define RTL_ADDR_TYPE_MMIO 2 + +#define RTL_CMD_ENTER_PRTM 1 +#define RTL_CMD_EXIT_PRTM 2 + +/* The RTL table as presented by the EBDA: */ +struct ibm_rtl_table { + char signature[5]; /* signature should be "_RTL_" */ + u8 version; + u8 rt_status; + u8 command; + u8 command_status; + u8 cmd_address_type; + u8 cmd_granularity; + u8 cmd_offset; + u16 reserve1; + u32 cmd_port_address; /* platform dependent address */ + u32 cmd_port_value; /* platform dependent value */ +} __attribute__((packed)); + +/* to locate "_RTL_" signature do a masked 5-byte integer compare */ +#define RTL_SIGNATURE 0x0000005f4c54525fULL +#define RTL_MASK 0x000000ffffffffffULL + +#define RTL_DEBUG(A, ...) do { \ + if (debug) \ + pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \ +} while (0) + +static DEFINE_MUTEX(rtl_lock); +static struct ibm_rtl_table __iomem *rtl_table; +static void __iomem *ebda_map; +static void __iomem *rtl_cmd_addr; +static u8 rtl_cmd_type; +static u8 rtl_cmd_width; + +static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) +{ + if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + return ioremap(addr, len); + return ioport_map(addr, len); +} + +static void rtl_port_unmap(void __iomem *addr) +{ + if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + iounmap(addr); + else + ioport_unmap(addr); +} + +static int ibm_rtl_write(u8 value) +{ + int ret = 0, count = 0; + static u32 cmd_port_val; + + RTL_DEBUG("%s(%d)\n", __FUNCTION__, value); + + value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; + + mutex_lock(&rtl_lock); + + if (ioread8(&rtl_table->rt_status) != value) { + iowrite8(value, &rtl_table->command); + + switch (rtl_cmd_width) { + case 8: + cmd_port_val = ioread8(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite8((u8)cmd_port_val, rtl_cmd_addr); + break; + case 16: + cmd_port_val = ioread16(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite16((u16)cmd_port_val, rtl_cmd_addr); + break; + case 32: + cmd_port_val = ioread32(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite32(cmd_port_val, rtl_cmd_addr); + break; + } + + while (ioread8(&rtl_table->command)) { + msleep(10); + if (count++ > 500) { + pr_err("ibm-rtl: Hardware not responding to " + "mode switch request\n"); + ret = -EIO; + break; + } + + } + + if (ioread8(&rtl_table->command_status)) { + RTL_DEBUG("command_status reports failed command\n"); + ret = -EIO; + } + } + + mutex_unlock(&rtl_lock); + return ret; +} + +static ssize_t rtl_show_version(struct sysdev_class * dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); +} + +static ssize_t rtl_show_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); +} + +static ssize_t rtl_set_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + const char *buf, + size_t count) +{ + ssize_t ret; + + if (count < 1 || count > 2) + return -EINVAL; + + switch (buf[0]) { + case '0': + ret = ibm_rtl_write(0); + break; + case '1': + ret = ibm_rtl_write(1); + break; + default: + ret = -EINVAL; + } + if (ret >= 0) + ret = count; + + return ret; +} + +static struct sysdev_class class_rtl = { + .name = "ibm_rtl", +}; + +static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); +static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); + +static struct sysdev_class_attribute *rtl_attributes[] = { + &attr_version, + &attr_state, + NULL +}; + + +static int rtl_setup_sysfs(void) { + int ret, i; + ret = sysdev_class_register(&class_rtl); + + if (!ret) { + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_create_file(&class_rtl, rtl_attributes[i]); + } + return ret; +} + +static void rtl_teardown_sysfs(void) { + int i; + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); + sysdev_class_unregister(&class_rtl); +} + +static int dmi_check_cb(const struct dmi_system_id *id) +{ + RTL_DEBUG("found IBM server '%s'\n", id->ident); + return 0; +} + +#define ibm_dmi_entry(NAME, TYPE) \ +{ \ + .ident = NAME, \ + .matches = { \ + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \ + }, \ + .callback = dmi_check_cb \ +} + +static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { + ibm_dmi_entry("BladeCenter LS21", "7971"), + ibm_dmi_entry("BladeCenter LS22", "7901"), + ibm_dmi_entry("BladeCenter HS21 XM", "7995"), + ibm_dmi_entry("BladeCenter HS22", "7870"), + ibm_dmi_entry("BladeCenter HS22V", "7871"), + ibm_dmi_entry("System x3550 M2", "7946"), + ibm_dmi_entry("System x3650 M2", "7947"), + ibm_dmi_entry("System x3550 M3", "7944"), + ibm_dmi_entry("System x3650 M3", "7945"), + { } +}; + +static int __init ibm_rtl_init(void) { + unsigned long ebda_addr, ebda_size; + unsigned int ebda_kb; + int ret = -ENODEV, i; + + if (force) + pr_warning("ibm-rtl: module loaded by force\n"); + /* first ensure that we are running on IBM HW */ + else if (!dmi_check_system(ibm_rtl_dmi_table)) + return -ENODEV; + + /* Get the address for the Extended BIOS Data Area */ + ebda_addr = get_bios_ebda(); + if (!ebda_addr) { + RTL_DEBUG("no BIOS EBDA found\n"); + return -ENODEV; + } + + ebda_map = ioremap(ebda_addr, 4); + if (!ebda_map) + return -ENOMEM; + + /* First word in the EDBA is the Size in KB */ + ebda_kb = ioread16(ebda_map); + RTL_DEBUG("EBDA is %d kB\n", ebda_kb); + + if (ebda_kb == 0) + goto out; + + iounmap(ebda_map); + ebda_size = ebda_kb*1024; + + /* Remap the whole table */ + ebda_map = ioremap(ebda_addr, ebda_size); + if (!ebda_map) + return -ENOMEM; + + /* search for the _RTL_ signature at the start of the table */ + for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { + struct ibm_rtl_table __iomem * tmp; + tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); + if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { + phys_addr_t addr; + unsigned int plen; + RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp); + rtl_table = tmp; + /* The address, value, width and offset are platform + * dependent and found in the ibm_rtl_table */ + rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); + rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); + RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", + rtl_cmd_width, rtl_cmd_type); + addr = ioread32(&rtl_table->cmd_port_address); + RTL_DEBUG("addr = %#llx\n", addr); + plen = rtl_cmd_width/sizeof(char); + rtl_cmd_addr = rtl_port_map(addr, plen); + RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr); + if (!rtl_cmd_addr) { + ret = -ENOMEM; + break; + } + ret = rtl_setup_sysfs(); + break; + } + } + +out: + if (ret) { + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } + + return ret; +} + +static void __exit ibm_rtl_exit(void) +{ + if (rtl_table) { + RTL_DEBUG("cleaning up"); + /* do not leave the machine in SMI-free mode */ + ibm_rtl_write(0); + /* unmap, unlink and remove all traces */ + rtl_teardown_sysfs(); + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } +} + +module_init(ibm_rtl_init); +module_exit(ibm_rtl_exit); diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad-laptop.c index 798496353e8..5ff12205aa6 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -35,112 +35,162 @@ #define IDEAPAD_DEV_KILLSW 4 struct ideapad_private { + acpi_handle handle; struct rfkill *rfk[5]; -}; +} *ideapad_priv; static struct { char *name; + int cfgbit; + int opcode; int type; } ideapad_rfk_data[] = { - /* camera has no rfkill */ - { "ideapad_wlan", RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", RFKILL_TYPE_WWAN }, - { "ideapad_killsw", RFKILL_TYPE_WLAN } + { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, + { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, + { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } }; -static int ideapad_dev_exists(int device) -{ - acpi_status status; - union acpi_object in_param; - struct acpi_object_list input = { 1, &in_param }; - struct acpi_buffer output; - union acpi_object out_obj; +static bool no_bt_rfkill; +module_param(no_bt_rfkill, bool, 0444); +MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); - output.length = sizeof(out_obj); - output.pointer = &out_obj; +/* + * ACPI Helpers + */ +#define IDEAPAD_EC_TIMEOUT (100) /* in ms */ - in_param.type = ACPI_TYPE_INTEGER; - in_param.integer.value = device + 1; +static int read_method_int(acpi_handle handle, const char *method, int *val) +{ + acpi_status status; + unsigned long long result; - status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output); + status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status); - return -ENODEV; - } - if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n"); - return -ENODEV; + *val = -1; + return -1; + } else { + *val = result; + return 0; } - return out_obj.integer.value; } -static int ideapad_dev_get_state(int device) +static int method_vpcr(acpi_handle handle, int cmd, int *ret) { acpi_status status; - union acpi_object in_param; - struct acpi_object_list input = { 1, &in_param }; - struct acpi_buffer output; - union acpi_object out_obj; + unsigned long long result; + struct acpi_object_list params; + union acpi_object in_obj; - output.length = sizeof(out_obj); - output.pointer = &out_obj; + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = cmd; - in_param.type = ACPI_TYPE_INTEGER; - in_param.integer.value = device + 1; + status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); - status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status); - return -ENODEV; - } - if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n"); - return -ENODEV; + *ret = -1; + return -1; + } else { + *ret = result; + return 0; } - return out_obj.integer.value; } -static int ideapad_dev_set_state(int device, int state) +static int method_vpcw(acpi_handle handle, int cmd, int data) { + struct acpi_object_list params; + union acpi_object in_obj[2]; acpi_status status; - union acpi_object in_params[2]; - struct acpi_object_list input = { 2, in_params }; - in_params[0].type = ACPI_TYPE_INTEGER; - in_params[0].integer.value = device + 1; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = state; + params.count = 2; + params.pointer = in_obj; + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = cmd; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = data; - status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL); - if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status); - return -ENODEV; - } + status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); + if (status != AE_OK) + return -1; return 0; } + +static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) +{ + int val; + unsigned long int end_jiffies; + + if (method_vpcw(handle, 1, cmd)) + return -1; + + for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; + time_before(jiffies, end_jiffies);) { + schedule(); + if (method_vpcr(handle, 1, &val)) + return -1; + if (val == 0) { + if (method_vpcr(handle, 0, &val)) + return -1; + *data = val; + return 0; + } + } + pr_err("timeout in read_ec_cmd\n"); + return -1; +} + +static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) +{ + int val; + unsigned long int end_jiffies; + + if (method_vpcw(handle, 0, data)) + return -1; + if (method_vpcw(handle, 1, cmd)) + return -1; + + for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; + time_before(jiffies, end_jiffies);) { + schedule(); + if (method_vpcr(handle, 1, &val)) + return -1; + if (val == 0) + return 0; + } + pr_err("timeout in write_ec_cmd\n"); + return -1; +} +/* the above is ACPI helpers */ + static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) { - int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA); - if (state < 0) - return state; + struct ideapad_private *priv = dev_get_drvdata(dev); + acpi_handle handle = priv->handle; + unsigned long result; - return sprintf(buf, "%d\n", state); + if (read_ec_data(handle, 0x1D, &result)) + return sprintf(buf, "-1\n"); + return sprintf(buf, "%lu\n", result); } static ssize_t store_ideapad_cam(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct ideapad_private *priv = dev_get_drvdata(dev); + acpi_handle handle = priv->handle; int ret, state; if (!count) return 0; if (sscanf(buf, "%i", &state) != 1) return -EINVAL; - ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state); + ret = write_ec_cmd(handle, 0x1E, state); if (ret < 0) return ret; return count; @@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked) if (device == IDEAPAD_DEV_KILLSW) return -EINVAL; - return ideapad_dev_set_state(device, !blocked); + + return write_ec_cmd(ideapad_priv->handle, + ideapad_rfk_data[device].opcode, + !blocked); } static struct rfkill_ops ideapad_rfk_ops = { @@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = { static void ideapad_sync_rfk_state(struct acpi_device *adevice) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW); + acpi_handle handle = priv->handle; + unsigned long hw_blocked; int i; - rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked); - for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) - if (priv->rfk[i]) - rfkill_set_hw_state(priv->rfk[i], hw_blocked); - if (hw_blocked) + if (read_ec_data(handle, 0x23, &hw_blocked)) return; + hw_blocked = !hw_blocked; - for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) if (priv->rfk[i]) - rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i)); + rfkill_set_hw_state(priv->rfk[i], hw_blocked); } static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int ret; + unsigned long sw_blocked; + + if (no_bt_rfkill && + (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { + /* Force to enable bluetooth when no_bt_rfkill=1 */ + write_ec_cmd(ideapad_priv->handle, + ideapad_rfk_data[dev].opcode, 1); + return 0; + } - priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev, - ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops, + priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, + ideapad_rfk_data[dev].type, &ideapad_rfk_ops, (void *)(long)dev); if (!priv->rfk[dev]) return -ENOMEM; + if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, + &sw_blocked)) { + rfkill_init_sw_state(priv->rfk[dev], 0); + } else { + sw_blocked = !sw_blocked; + rfkill_init_sw_state(priv->rfk[dev], sw_blocked); + } + ret = rfkill_register(priv->rfk[dev]); if (ret) { rfkill_destroy(priv->rfk[dev]); @@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static int ideapad_acpi_add(struct acpi_device *adevice) { - int i; + int i, cfg; int devs_present[5]; struct ideapad_private *priv; + if (read_method_int(adevice->handle, "_CFG", &cfg)) + return -ENODEV; + for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { - devs_present[i] = ideapad_dev_exists(i); - if (devs_present[i] < 0) - return devs_present[i]; + if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) + devs_present[i] = 1; + else + devs_present[i] = 0; } /* The hardware switch is always present */ @@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice) } } + priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); + ideapad_priv = priv; for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { if (!devs_present[i]) continue; @@ -270,7 +344,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) { - ideapad_sync_rfk_state(adevice); + acpi_handle handle = adevice->handle; + unsigned long vpc1, vpc2, vpc_bit; + + if (read_ec_data(handle, 0x10, &vpc1)) + return; + if (read_ec_data(handle, 0x1A, &vpc2)) + return; + + vpc1 = (vpc2 << 8) | vpc1; + for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { + if (test_bit(vpc_bit, &vpc1)) { + if (vpc_bit == 9) + ideapad_sync_rfk_state(adevice); + } + } } static struct acpi_driver ideapad_acpi_driver = { diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 5cdcff65391..f540ff96c53 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip, if (offset < 8)/* it is GPIO */ rc = intel_scu_ipc_update_register(GPIO0 + offset, - GPIO_DRV | GPIO_DOU | GPIO_DIR, - GPIO_DRV | (value ? GPIO_DOU : 0)); + GPIO_DRV | (value ? GPIO_DOU : 0), + GPIO_DRV | GPIO_DOU | GPIO_DIR); else if (offset < 16)/* it is GPOSW */ rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, - GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, - GPOSW_DRV | (value ? GPOSW_DOU : 0)); + GPOSW_DRV | (value ? GPOSW_DOU : 0), + GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); else if (offset > 15 && offset < 24)/* it is GPO */ rc = intel_scu_ipc_update_register(GPO, - 1 << (offset - 16), - value ? 1 << (offset - 16) : 0); + value ? 1 << (offset - 16) : 0, + 1 << (offset - 16)); else { printk(KERN_ERR "%s: invalid PMIC GPIO pin %d!\n", __func__, offset); @@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { if (offset < 8)/* it is GPIO */ intel_scu_ipc_update_register(GPIO0 + offset, - GPIO_DRV | GPIO_DOU, - GPIO_DRV | (value ? GPIO_DOU : 0)); + GPIO_DRV | (value ? GPIO_DOU : 0), + GPIO_DRV | GPIO_DOU); else if (offset < 16)/* it is GPOSW */ intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, - GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, - GPOSW_DRV | (value ? GPOSW_DOU : 0)); + GPOSW_DRV | (value ? GPOSW_DOU : 0), + GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); else if (offset > 15 && offset < 24) /* it is GPO */ intel_scu_ipc_update_register(GPO, - 1 << (offset - 16), - value ? 1 << (offset - 16) : 0); + value ? 1 << (offset - 16) : 0, + 1 << (offset - 16)); } static int pmic_irq_type(unsigned irq, unsigned type) @@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type) u32 gpio = irq - pg->irq_base; unsigned long flags; - if (gpio > pg->chip.ngpio) + if (gpio >= pg->chip.ngpio) return -EINVAL; spin_lock_irqsave(&pg->irqtypes.lock, flags); diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 6abe18e638e..41a9e34899a 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -23,6 +23,7 @@ #include <linux/pm.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/sfi.h> #include <asm/mrst.h> #include <asm/intel_scu_ipc.h> diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ec01c3d8fc5..cc1e0ba104d 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -128,6 +128,7 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #ifndef ACPI_HOTKEY_COMPONENT @@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = { }, }; -#define KEYMAP_SIZE 11 -static const unsigned int initial_keymap[KEYMAP_SIZE] = { - /* 0 */ KEY_RESERVED, - /* 1 */ KEY_BRIGHTNESSDOWN, - /* 2 */ KEY_BRIGHTNESSUP, - /* 3 */ KEY_DISPLAYTOGGLE, - /* 4 */ KEY_MUTE, - /* 5 */ KEY_VOLUMEDOWN, - /* 6 */ KEY_VOLUMEUP, - /* 7 */ KEY_SLEEP, - /* 8 */ KEY_PROG1, /* Change CPU boost */ - /* 9 */ KEY_BATTERY, - /* 10 */ KEY_SUSPEND, +static const struct key_entry panasonic_keymap[] = { + { KE_KEY, 0, { KEY_RESERVED } }, + { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 2, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 3, { KEY_DISPLAYTOGGLE } }, + { KE_KEY, 4, { KEY_MUTE } }, + { KE_KEY, 5, { KEY_VOLUMEDOWN } }, + { KE_KEY, 6, { KEY_VOLUMEUP } }, + { KE_KEY, 7, { KEY_SLEEP } }, + { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ + { KE_KEY, 9, { KEY_BATTERY } }, + { KE_KEY, 10, { KEY_SUSPEND } }, + { KE_END, 0 } }; struct pcc_acpi { acpi_handle handle; unsigned long num_sifr; int sticky_mode; - u32 *sinf; + u32 *sinf; struct acpi_device *device; struct input_dev *input_dev; struct backlight_device *backlight; - unsigned int keymap[KEYMAP_SIZE]; }; struct pcc_keyinput { @@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device) } } -static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) +static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) { acpi_status status; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) hkey = buffer.pointer; if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); + status = AE_ERROR; goto end; } @@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) for (i = 0; i < hkey->package.count; i++) { union acpi_object *element = &(hkey->package.elements[i]); if (likely(element->type == ACPI_TYPE_INTEGER)) { - sinf[i] = element->integer.value; + pcc->sinf[i] = element->integer.value; } else ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF data\n")); } - sinf[hkey->package.count] = -1; + pcc->sinf[hkey->package.count] = -1; end: kfree(buffer.pointer); @@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd) { struct pcc_acpi *pcc = bl_get_data(bd); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return pcc->sinf[SINF_AC_CUR_BRIGHT]; @@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd) int bright = bd->props.brightness; int rc; - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) @@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); @@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); @@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); @@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); @@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = { /* hotkey input device driver */ -static int pcc_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct pcc_acpi *pcc = input_get_drvdata(dev); - - if (scancode >= ARRAY_SIZE(pcc->keymap)) - return -EINVAL; - - *keycode = pcc->keymap[scancode]; - - return 0; -} - -static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) { - if (pcc->keymap[i] == keycode) - return i+1; - } - - return 0; -} - -static int pcc_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct pcc_acpi *pcc = input_get_drvdata(dev); - int oldkeycode; - - if (scancode >= ARRAY_SIZE(pcc->keymap)) - return -EINVAL; - - oldkeycode = pcc->keymap[scancode]; - pcc->keymap[scancode] = keycode; - - set_bit(keycode, dev->keybit); - - if (!keymap_get_by_keycode(pcc, oldkeycode)) - clear_bit(oldkeycode, dev->keybit); - - return 0; -} - static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) { struct input_dev *hotk_input_dev = pcc->input_dev; int rc; - int key_code, hkey_num; unsigned long long result; rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, @@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); - hkey_num = result & 0xf; - - if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) { + if (!sparse_keymap_report_event(hotk_input_dev, + result & 0xf, result & 0x80, false)) ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "hotkey number out of range: %d\n", - hkey_num)); - return; - } - - key_code = pcc->keymap[hkey_num]; - - if (key_code != KEY_RESERVED) { - int pushed = (result & 0x80) ? TRUE : FALSE; - - input_report_key(hotk_input_dev, key_code, pushed); - input_sync(hotk_input_dev); - } - - return; + "Unknown hotkey event: %d\n", result)); } static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) @@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) static int acpi_pcc_init_input(struct pcc_acpi *pcc) { - int i, rc; + struct input_dev *input_dev; + int error; - pcc->input_dev = input_allocate_device(); - if (!pcc->input_dev) { + input_dev = input_allocate_device(); + if (!input_dev) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate input device for hotkey")); return -ENOMEM; } - pcc->input_dev->evbit[0] = BIT(EV_KEY); - - pcc->input_dev->name = ACPI_PCC_DRIVER_NAME; - pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS; - pcc->input_dev->id.bustype = BUS_HOST; - pcc->input_dev->id.vendor = 0x0001; - pcc->input_dev->id.product = 0x0001; - pcc->input_dev->id.version = 0x0100; - pcc->input_dev->getkeycode = pcc_getkeycode; - pcc->input_dev->setkeycode = pcc_setkeycode; + input_dev->name = ACPI_PCC_DRIVER_NAME; + input_dev->phys = ACPI_PCC_INPUT_PHYS; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; - /* load initial keymap */ - memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap)); + error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); + if (error) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to setup input device keymap\n")); + goto err_free_dev; + } - for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) - __set_bit(pcc->keymap[i], pcc->input_dev->keybit); - __clear_bit(KEY_RESERVED, pcc->input_dev->keybit); + error = input_register_device(input_dev); + if (error) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to register input device\n")); + goto err_free_keymap; + } - input_set_drvdata(pcc->input_dev, pcc); + pcc->input_dev = input_dev; + return 0; - rc = input_register_device(pcc->input_dev); - if (rc < 0) - input_free_device(pcc->input_dev); + err_free_keymap: + sparse_keymap_free(input_dev); + err_free_dev: + input_free_device(input_dev); + return error; +} - return rc; +static void acpi_pcc_destroy_input(struct pcc_acpi *pcc) +{ + sparse_keymap_free(pcc->input_dev); + input_unregister_device(pcc->input_dev); + /* + * No need to input_free_device() since core input API refcounts + * and free()s the device. + */ } /* kernel module interface */ @@ -636,12 +591,13 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing keyinput handler\n")); - goto out_hotkey; + goto out_sinf; } - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { + if (!acpi_pcc_retrieve_biosdata(pcc)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); + result = -EIO; goto out_input; } /* initialize backlight */ @@ -651,7 +607,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) &pcc_backlight_ops, &props); if (IS_ERR(pcc->backlight)) { result = PTR_ERR(pcc->backlight); - goto out_sinf; + goto out_input; } /* read the initial brightness setting from the hardware */ @@ -669,12 +625,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) out_backlight: backlight_device_unregister(pcc->backlight); +out_input: + acpi_pcc_destroy_input(pcc); out_sinf: kfree(pcc->sinf); -out_input: - input_unregister_device(pcc->input_dev); - /* no need to input_free_device() since core input API refcount and - * free()s the device */ out_hotkey: kfree(pcc); @@ -709,9 +663,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) backlight_device_unregister(pcc->backlight); - input_unregister_device(pcc->input_dev); - /* no need to input_free_device() since core input API refcount and - * free()s the device */ + acpi_pcc_destroy_input(pcc); kfree(pcc->sinf); kfree(pcc); diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index ff4b476f195..1d07d6d09f2 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/acpi.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #define ACPI_TOPSTAR_CLASS "topstar" @@ -26,52 +27,37 @@ struct topstar_hkey { struct input_dev *inputdev; }; -struct tps_key_entry { - u8 code; - u16 keycode; -}; - -static struct tps_key_entry topstar_keymap[] = { - { 0x80, KEY_BRIGHTNESSUP }, - { 0x81, KEY_BRIGHTNESSDOWN }, - { 0x83, KEY_VOLUMEUP }, - { 0x84, KEY_VOLUMEDOWN }, - { 0x85, KEY_MUTE }, - { 0x86, KEY_SWITCHVIDEOMODE }, - { 0x87, KEY_F13 }, /* touchpad enable/disable key */ - { 0x88, KEY_WLAN }, - { 0x8a, KEY_WWW }, - { 0x8b, KEY_MAIL }, - { 0x8c, KEY_MEDIA }, - { 0x96, KEY_F14 }, /* G key? */ - { } -}; - -static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code) -{ - struct tps_key_entry *key; - - for (key = topstar_keymap; key->code; key++) - if (code == key->code) - return key; +static const struct key_entry topstar_keymap[] = { + { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x83, { KEY_VOLUMEUP } }, + { KE_KEY, 0x84, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x85, { KEY_MUTE } }, + { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */ + { KE_KEY, 0x88, { KEY_WLAN } }, + { KE_KEY, 0x8a, { KEY_WWW } }, + { KE_KEY, 0x8b, { KEY_MAIL } }, + { KE_KEY, 0x8c, { KEY_MEDIA } }, - return NULL; -} - -static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) -{ - struct tps_key_entry *key; + /* Known non hotkey events don't handled or that we don't care yet */ + { KE_IGNORE, 0x8e, }, + { KE_IGNORE, 0x8f, }, + { KE_IGNORE, 0x90, }, - for (key = topstar_keymap; key->code; key++) - if (code == key->keycode) - return key; + /* + * 'G key' generate two event codes, convert to only + * one event/key code for now, consider replacing by + * a switch (3G switch - SW_3G?) + */ + { KE_KEY, 0x96, { KEY_F14 } }, + { KE_KEY, 0x97, { KEY_F14 } }, - return NULL; -} + { KE_END, 0 } +}; static void acpi_topstar_notify(struct acpi_device *device, u32 event) { - struct tps_key_entry *key; static bool dup_evnt[2]; bool *dup; struct topstar_hkey *hkey = acpi_driver_data(device); @@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event) *dup = true; } - /* - * 'G key' generate two event codes, convert to only - * one event/key code for now (3G switch?) - */ - if (event == 0x97) - event = 0x96; - - key = tps_get_key_by_scancode(event); - if (key) { - input_report_key(hkey->inputdev, key->keycode, 1); - input_sync(hkey->inputdev); - input_report_key(hkey->inputdev, key->keycode, 0); - input_sync(hkey->inputdev); - return; - } - - /* Known non hotkey events don't handled or that we don't care yet */ - if (event == 0x8e || event == 0x8f || event == 0x90) - return; - - pr_info("unknown event = 0x%02x\n", event); + if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true)) + pr_info("unknown event = 0x%02x\n", event); } static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) @@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) return 0; } -static int topstar_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct tps_key_entry *key = tps_get_key_by_scancode(scancode); - - if (!key) - return -EINVAL; - - *keycode = key->keycode; - return 0; -} - -static int topstar_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct tps_key_entry *key; - int old_keycode; - - key = tps_get_key_by_scancode(scancode); - - if (!key) - return -EINVAL; - - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!tps_get_key_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; -} - static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) { - struct tps_key_entry *key; + struct input_dev *input; + int error; - hkey->inputdev = input_allocate_device(); - if (!hkey->inputdev) { + input = input_allocate_device(); + if (!input) { pr_err("Unable to allocate input device\n"); - return -ENODEV; + return -ENOMEM; } - hkey->inputdev->name = "Topstar Laptop extra buttons"; - hkey->inputdev->phys = "topstar/input0"; - hkey->inputdev->id.bustype = BUS_HOST; - hkey->inputdev->getkeycode = topstar_getkeycode; - hkey->inputdev->setkeycode = topstar_setkeycode; - for (key = topstar_keymap; key->code; key++) { - set_bit(EV_KEY, hkey->inputdev->evbit); - set_bit(key->keycode, hkey->inputdev->keybit); + + input->name = "Topstar Laptop extra buttons"; + input->phys = "topstar/input0"; + input->id.bustype = BUS_HOST; + + error = sparse_keymap_setup(input, topstar_keymap, NULL); + if (error) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; } - if (input_register_device(hkey->inputdev)) { + + error = input_register_device(input); + if (error) { pr_err("Unable to register input device\n"); - input_free_device(hkey->inputdev); - return -ENODEV; + goto err_free_keymap; } + hkey->inputdev = input; return 0; + + err_free_keymap: + sparse_keymap_free(input); + err_free_dev: + input_free_device(input); + return error; } static int acpi_topstar_add(struct acpi_device *device) @@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type) acpi_topstar_fncx_switch(device, false); + sparse_keymap_free(tps_hkey->inputdev); input_unregister_device(tps_hkey->inputdev); kfree(tps_hkey); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 7d67a45bb2b..06f304f46e0 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -48,6 +48,7 @@ #include <linux/platform_device.h> #include <linux/rfkill.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/leds.h> #include <linux/slab.h> @@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); -struct key_entry { - char type; - u16 code; - u16 keycode; -}; - -enum {KE_KEY, KE_END}; - -static struct key_entry toshiba_acpi_keymap[] = { - {KE_KEY, 0x101, KEY_MUTE}, - {KE_KEY, 0x102, KEY_ZOOMOUT}, - {KE_KEY, 0x103, KEY_ZOOMIN}, - {KE_KEY, 0x13b, KEY_COFFEE}, - {KE_KEY, 0x13c, KEY_BATTERY}, - {KE_KEY, 0x13d, KEY_SLEEP}, - {KE_KEY, 0x13e, KEY_SUSPEND}, - {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, - {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, - {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, - {KE_KEY, 0x142, KEY_WLAN}, - {KE_KEY, 0x143, KEY_PROG1}, - {KE_KEY, 0xb05, KEY_PROG2}, - {KE_KEY, 0xb06, KEY_WWW}, - {KE_KEY, 0xb07, KEY_MAIL}, - {KE_KEY, 0xb30, KEY_STOP}, - {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, - {KE_KEY, 0xb32, KEY_NEXTSONG}, - {KE_KEY, 0xb33, KEY_PLAYPAUSE}, - {KE_KEY, 0xb5a, KEY_MEDIA}, - {KE_END, 0, 0}, +static const struct key_entry toshiba_acpi_keymap[] __initconst = { + { KE_KEY, 0x101, { KEY_MUTE } }, + { KE_KEY, 0x102, { KEY_ZOOMOUT } }, + { KE_KEY, 0x103, { KEY_ZOOMIN } }, + { KE_KEY, 0x13b, { KEY_COFFEE } }, + { KE_KEY, 0x13c, { KEY_BATTERY } }, + { KE_KEY, 0x13d, { KEY_SLEEP } }, + { KE_KEY, 0x13e, { KEY_SUSPEND } }, + { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x142, { KEY_WLAN } }, + { KE_KEY, 0x143, { KEY_PROG1 } }, + { KE_KEY, 0xb05, { KEY_PROG2 } }, + { KE_KEY, 0xb06, { KEY_WWW } }, + { KE_KEY, 0xb07, { KEY_MAIL } }, + { KE_KEY, 0xb30, { KEY_STOP } }, + { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, + { KE_KEY, 0xb32, { KEY_NEXTSONG } }, + { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, + { KE_KEY, 0xb5a, { KEY_MEDIA } }, + { KE_END, 0 }, }; /* utility @@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; -static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) -{ - struct key_entry *key; - - for (key = toshiba_acpi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) -{ - struct key_entry *key; - - for (key = toshiba_acpi_keymap; key->type != KE_END; key++) - if (code == key->keycode && key->type == KE_KEY) - return key; - - return NULL; -} - -static int toshiba_acpi_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int toshiba_acpi_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct key_entry *key; - unsigned int old_keycode; - - key = toshiba_acpi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - - return -EINVAL; -} - static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) { u32 hci_result, value; - struct key_entry *key; if (event != 0x80) return; @@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) if (value & 0x80) continue; - key = toshiba_acpi_get_entry_by_scancode - (value); - if (!key) { + if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, + value, 1, true)) { printk(MY_INFO "Unknown key %x\n", value); - continue; } - input_report_key(toshiba_acpi.hotkey_dev, - key->keycode, 1); - input_sync(toshiba_acpi.hotkey_dev); - input_report_key(toshiba_acpi.hotkey_dev, - key->keycode, 0); - input_sync(toshiba_acpi.hotkey_dev); } else if (hci_result == HCI_NOT_SUPPORTED) { /* This is a workaround for an unresolved issue on * some machines where system events sporadically @@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) } while (hci_result != HCI_EMPTY); } -static int toshiba_acpi_setup_keyboard(char *device) +static int __init toshiba_acpi_setup_keyboard(char *device) { acpi_status status; - acpi_handle handle; - int result; - const struct key_entry *key; + int error; - status = acpi_get_handle(NULL, device, &handle); + status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); if (ACPI_FAILURE(status)) { printk(MY_INFO "Unable to get notification device\n"); return -ENODEV; } - toshiba_acpi.handle = handle; - - status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); - if (ACPI_FAILURE(status)) { - printk(MY_INFO "Unable to enable hotkeys\n"); - return -ENODEV; - } - - status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, - toshiba_acpi_notify, NULL); - if (ACPI_FAILURE(status)) { - printk(MY_INFO "Unable to install hotkey notification\n"); - return -ENODEV; - } - toshiba_acpi.hotkey_dev = input_allocate_device(); if (!toshiba_acpi.hotkey_dev) { printk(MY_INFO "Unable to register input device\n"); @@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device) toshiba_acpi.hotkey_dev->name = "Toshiba input device"; toshiba_acpi.hotkey_dev->phys = device; toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; - toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; - toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; - for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { - set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); - set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); + error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, + toshiba_acpi_keymap, NULL); + if (error) + goto err_free_dev; + + status = acpi_install_notify_handler(toshiba_acpi.handle, + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to install hotkey notification\n"); + error = -ENODEV; + goto err_free_keymap; + } + + status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to enable hotkeys\n"); + error = -ENODEV; + goto err_remove_notify; } - result = input_register_device(toshiba_acpi.hotkey_dev); - if (result) { + error = input_register_device(toshiba_acpi.hotkey_dev); + if (error) { printk(MY_INFO "Unable to register input device\n"); - return result; + goto err_remove_notify; } return 0; + + err_remove_notify: + acpi_remove_notify_handler(toshiba_acpi.handle, + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); + err_free_keymap: + sparse_keymap_free(toshiba_acpi.hotkey_dev); + err_free_dev: + input_free_device(toshiba_acpi.hotkey_dev); + toshiba_acpi.hotkey_dev = NULL; + return error; } static void toshiba_acpi_exit(void) { - if (toshiba_acpi.hotkey_dev) + if (toshiba_acpi.hotkey_dev) { + acpi_remove_notify_handler(toshiba_acpi.handle, + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); + sparse_keymap_free(toshiba_acpi.hotkey_dev); input_unregister_device(toshiba_acpi.hotkey_dev); + } if (toshiba_acpi.bt_rfk) { rfkill_unregister(toshiba_acpi.bt_rfk); @@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void) if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); - acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, - toshiba_acpi_notify); - if (toshiba_acpi.illumination_installed) led_classdev_unregister(&toshiba_led); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b2978a04317..104b77c87ef 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -27,6 +27,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> @@ -44,9 +46,8 @@ MODULE_LICENSE("GPL"); #define ACPI_WMI_CLASS "wmi" -#define PREFIX "ACPI: WMI: " - static DEFINE_MUTEX(wmi_data_lock); +static LIST_HEAD(wmi_block_list); struct guid_block { char guid[16]; @@ -67,10 +68,9 @@ struct wmi_block { acpi_handle handle; wmi_notify_handler handler; void *handler_data; - struct device *dev; + struct device dev; }; -static struct wmi_block wmi_blocks; /* * If the GUID data block is marked as expensive, we must enable and @@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { .add = acpi_wmi_add, .remove = acpi_wmi_remove, .notify = acpi_wmi_notify, - }, + }, }; /* @@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = { */ static int wmi_parse_hexbyte(const u8 *src) { - unsigned int x; /* For correct wrapping */ int h; + int value; /* high part */ - x = src[0]; - if (x - '0' <= '9' - '0') { - h = x - '0'; - } else if (x - 'a' <= 'f' - 'a') { - h = x - 'a' + 10; - } else if (x - 'A' <= 'F' - 'A') { - h = x - 'A' + 10; - } else { + h = value = hex_to_bin(src[0]); + if (value < 0) return -1; - } - h <<= 4; /* low part */ - x = src[1]; - if (x - '0' <= '9' - '0') - return h | (x - '0'); - if (x - 'a' <= 'f' - 'a') - return h | (x - 'a' + 10); - if (x - 'A' <= 'F' - 'A') - return h | (x - 'A' + 10); + value = hex_to_bin(src[1]); + if (value >= 0) + return (h << 4) | value; return -1; } @@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out) for (i = 10; i <= 15; i++) out += sprintf(out, "%02X", in[i] & 0xFF); - out = '\0'; + *out = '\0'; return 0; } @@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) wmi_parse_guid(guid_string, tmp); wmi_swap_bytes(tmp, guid_input); - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -487,30 +475,29 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); -static void wmi_dump_wdg(struct guid_block *g) +static void wmi_dump_wdg(const struct guid_block *g) { char guid_string[37]; wmi_gtoa(g->guid, guid_string); - printk(KERN_INFO PREFIX "%s:\n", guid_string); - printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", - g->object_id[0], g->object_id[1]); - printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); - printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); - printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); - printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); + + pr_info("%s:\n", guid_string); + pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); + pr_info("\tnotify_id: %02X\n", g->notify_id); + pr_info("\treserved: %02X\n", g->reserved); + pr_info("\tinstance_count: %d\n", g->instance_count); + pr_info("\tflags: %#x ", g->flags); if (g->flags) { - printk(" "); if (g->flags & ACPI_WMI_EXPENSIVE) - printk("ACPI_WMI_EXPENSIVE "); + pr_cont("ACPI_WMI_EXPENSIVE "); if (g->flags & ACPI_WMI_METHOD) - printk("ACPI_WMI_METHOD "); + pr_cont("ACPI_WMI_METHOD "); if (g->flags & ACPI_WMI_STRING) - printk("ACPI_WMI_STRING "); + pr_cont("ACPI_WMI_STRING "); if (g->flags & ACPI_WMI_EVENT) - printk("ACPI_WMI_EVENT "); + pr_cont("ACPI_WMI_EVENT "); } - printk("\n"); + pr_cont("\n"); } @@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context) status = wmi_get_event_data(value, &response); if (status != AE_OK) { - printk(KERN_INFO "wmi: bad event status 0x%x\n", status); + pr_info("bad event status 0x%x\n", status); return; } @@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context) if (!obj) return; - printk(KERN_INFO PREFIX "DEBUG Event "); + pr_info("DEBUG Event "); switch(obj->type) { case ACPI_TYPE_BUFFER: - printk("BUFFER_TYPE - length %d\n", obj->buffer.length); + pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); break; case ACPI_TYPE_STRING: - printk("STRING_TYPE - %s\n", obj->string.pointer); + pr_cont("STRING_TYPE - %s\n", obj->string.pointer); break; case ACPI_TYPE_INTEGER: - printk("INTEGER_TYPE - %llu\n", obj->integer.value); + pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); break; case ACPI_TYPE_PACKAGE: - printk("PACKAGE_TYPE - %d elements\n", obj->package.count); + pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); break; default: - printk("object type 0x%X\n", obj->type); + pr_cont("object type 0x%X\n", obj->type); } kfree(obj); } @@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = event; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); gblock = &wblock->gblock; @@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); /* * sysfs interface */ -static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { char guid_string[37]; @@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, return sprintf(buf, "wmi:%s\n", guid_string); } -static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct device_attribute wmi_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL +}; static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static void wmi_dev_free(struct device *dev) { - kfree(dev); + struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); + + kfree(wmi_block); } static struct class wmi_class = { .name = "wmi", .dev_release = wmi_dev_free, .dev_uevent = wmi_dev_uevent, + .dev_attrs = wmi_dev_attrs, }; -static int wmi_create_devs(void) +static struct wmi_block *wmi_create_device(const struct guid_block *gblock, + acpi_handle handle) { - int result; - char guid_string[37]; - struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; - struct device *guid_dev; - - /* Create devices for all the GUIDs */ - list_for_each(p, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - - guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!guid_dev) - return -ENOMEM; - - wblock->dev = guid_dev; - - guid_dev->class = &wmi_class; - dev_set_drvdata(guid_dev, wblock); - - gblock = &wblock->gblock; - - wmi_gtoa(gblock->guid, guid_string); - dev_set_name(guid_dev, guid_string); - - result = device_register(guid_dev); - if (result) - return result; + int error; + char guid_string[37]; - result = device_create_file(guid_dev, &dev_attr_modalias); - if (result) - return result; + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) { + error = -ENOMEM; + goto err_out; } - return 0; -} + wblock->handle = handle; + wblock->gblock = *gblock; -static void wmi_remove_devs(void) -{ - struct guid_block *gblock; - struct wmi_block *wblock; - struct list_head *p; - struct device *guid_dev; + wblock->dev.class = &wmi_class; - /* Delete devices for all the GUIDs */ - list_for_each(p, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); + wmi_gtoa(gblock->guid, guid_string); + dev_set_name(&wblock->dev, guid_string); - guid_dev = wblock->dev; - gblock = &wblock->gblock; + dev_set_drvdata(&wblock->dev, wblock); - device_remove_file(guid_dev, &dev_attr_modalias); + error = device_register(&wblock->dev); + if (error) + goto err_free; - device_unregister(guid_dev); - } -} + list_add_tail(&wblock->list, &wmi_block_list); + return wblock; -static void wmi_class_exit(void) -{ - wmi_remove_devs(); - class_unregister(&wmi_class); +err_free: + kfree(wblock); +err_out: + return ERR_PTR(error); } -static int wmi_class_init(void) +static void wmi_free_devices(void) { - int ret; - - ret = class_register(&wmi_class); - if (ret) - return ret; + struct wmi_block *wblock, *next; - ret = wmi_create_devs(); - if (ret) - wmi_class_exit(); - - return ret; + /* Delete devices for all the GUIDs */ + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) + device_unregister(&wblock->dev); } static bool guid_already_parsed(const char *guid_string) { - struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; - list_for_each(p, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - gblock = &wblock->gblock; - - if (strncmp(gblock->guid, guid_string, 16) == 0) + list_for_each_entry(wblock, &wmi_block_list, list) + if (strncmp(wblock->gblock.guid, guid_string, 16) == 0) return true; - } + return false; } @@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; - struct guid_block *gblock; + const struct guid_block *gblock; struct wmi_block *wblock; char guid_string[37]; acpi_status status; + int retval; u32 i, total; status = acpi_evaluate_object(handle, "_WDG", NULL, &out); - if (ACPI_FAILURE(status)) - return status; + return -ENXIO; obj = (union acpi_object *) out.pointer; + if (!obj) + return -ENXIO; - if (obj->type != ACPI_TYPE_BUFFER) - return AE_ERROR; - - total = obj->buffer.length / sizeof(struct guid_block); - - gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); - if (!gblock) { - status = AE_NO_MEMORY; + if (obj->type != ACPI_TYPE_BUFFER) { + retval = -ENXIO; goto out_free_pointer; } + gblock = (const struct guid_block *)obj->buffer.pointer; + total = obj->buffer.length / sizeof(struct guid_block); + for (i = 0; i < total; i++) { /* Some WMI devices, like those for nVidia hooks, have a @@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle) */ if (guid_already_parsed(gblock[i].guid) == true) { wmi_gtoa(gblock[i].guid, guid_string); - printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", - guid_string); + pr_info("Skipping duplicate GUID %s\n", guid_string); continue; } + if (debug_dump_wdg) wmi_dump_wdg(&gblock[i]); - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - status = AE_NO_MEMORY; - goto out_free_gblock; + wblock = wmi_create_device(&gblock[i], handle); + if (IS_ERR(wblock)) { + retval = PTR_ERR(wblock); + wmi_free_devices(); + break; } - wblock->gblock = gblock[i]; - wblock->handle = handle; if (debug_event) { wblock->handler = wmi_notify_debug; - status = wmi_method_enable(wblock, 1); + wmi_method_enable(wblock, 1); } - list_add_tail(&wblock->list, &wmi_blocks.list); } -out_free_gblock: - kfree(gblock); + retval = 0; + out_free_pointer: kfree(out.pointer); - return status; + return retval; } /* @@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) struct list_head *p; char guid_string[37]; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) wblock->handler(event, wblock->handler_data); if (debug_event) { wmi_gtoa(wblock->gblock.guid, guid_string); - printk(KERN_INFO PREFIX "DEBUG Event GUID:" - " %s\n", guid_string); + pr_info("DEBUG Event GUID: %s\n", guid_string); } acpi_bus_generate_netlink_event( @@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); + wmi_free_devices(); return 0; } @@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) static int acpi_wmi_add(struct acpi_device *device) { acpi_status status; - int result = 0; + int error; status = acpi_install_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler, NULL, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; - - status = parse_wdg(device->handle); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + pr_err("Error installing EC region handler\n"); return -ENODEV; } - return result; + error = parse_wdg(device->handle); + if (error) { + acpi_remove_address_space_handler(device->handle, + ACPI_ADR_SPACE_EC, + &acpi_wmi_ec_space_handler); + pr_err("Failed to parse WDG method\n"); + return error; + } + + return 0; } static int __init acpi_wmi_init(void) { - int result; - - INIT_LIST_HEAD(&wmi_blocks.list); + int error; if (acpi_disabled) return -ENODEV; - result = acpi_bus_register_driver(&acpi_wmi_driver); + error = class_register(&wmi_class); + if (error) + return error; - if (result < 0) { - printk(KERN_INFO PREFIX "Error loading mapper\n"); - return -ENODEV; + error = acpi_bus_register_driver(&acpi_wmi_driver); + if (error) { + pr_err("Error loading mapper\n"); + class_unregister(&wmi_class); + return error; } - result = wmi_class_init(); - if (result) { - acpi_bus_unregister_driver(&acpi_wmi_driver); - return result; - } - - printk(KERN_INFO PREFIX "Mapper loaded\n"); - - return result; + pr_info("Mapper loaded\n"); + return 0; } static void __exit acpi_wmi_exit(void) { - struct list_head *p, *tmp; - struct wmi_block *wblock; - - wmi_class_exit(); - acpi_bus_unregister_driver(&acpi_wmi_driver); + class_unregister(&wmi_class); - list_for_each_safe(p, tmp, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - - list_del(p); - kfree(wblock); - } - - printk(KERN_INFO PREFIX "Mapper unloaded\n"); + pr_info("Mapper unloaded\n"); } subsys_initcall(acpi_wmi_init); diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c new file mode 100644 index 00000000000..e549eeeda12 --- /dev/null +++ b/drivers/platform/x86/xo1-rfkill.c @@ -0,0 +1,85 @@ +/* + * Support for rfkill through the OLPC XO-1 laptop embedded controller + * + * Copyright (C) 2010 One Laptop per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rfkill.h> + +#include <asm/olpc.h> + +static int rfkill_set_block(void *data, bool blocked) +{ + unsigned char cmd; + if (blocked) + cmd = EC_WLAN_ENTER_RESET; + else + cmd = EC_WLAN_LEAVE_RESET; + + return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); +} + +static const struct rfkill_ops rfkill_ops = { + .set_block = rfkill_set_block, +}; + +static int __devinit xo1_rfkill_probe(struct platform_device *pdev) +{ + struct rfkill *rfk; + int r; + + rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN, + &rfkill_ops, NULL); + if (!rfk) + return -ENOMEM; + + r = rfkill_register(rfk); + if (r) { + rfkill_destroy(rfk); + return r; + } + + platform_set_drvdata(pdev, rfk); + return 0; +} + +static int __devexit xo1_rfkill_remove(struct platform_device *pdev) +{ + struct rfkill *rfk = platform_get_drvdata(pdev); + rfkill_unregister(rfk); + rfkill_destroy(rfk); + return 0; +} + +static struct platform_driver xo1_rfkill_driver = { + .driver = { + .name = "xo1-rfkill", + .owner = THIS_MODULE, + }, + .probe = xo1_rfkill_probe, + .remove = __devexit_p(xo1_rfkill_remove), +}; + +static int __init xo1_rfkill_init(void) +{ + return platform_driver_register(&xo1_rfkill_driver); +} + +static void __exit xo1_rfkill_exit(void) +{ + platform_driver_unregister(&xo1_rfkill_driver); +} + +MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:xo1-rfkill"); + +module_init(xo1_rfkill_init); +module_exit(xo1_rfkill_exit); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index aa95f100176..fb613d70c2c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1099,16 +1099,30 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, cqr = (struct dasd_ccw_req *) intparm; if (!cqr || ((scsw_cc(&irb->scsw) == 1) && (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && - (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))) { + ((scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND) || + (scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND | + SCSW_STCTL_ALERT_STATUS))))) { if (cqr && cqr->status == DASD_CQR_IN_IO) cqr->status = DASD_CQR_QUEUED; + if (cqr) + memcpy(&cqr->irb, irb, sizeof(*irb)); device = dasd_device_from_cdev_locked(cdev); - if (!IS_ERR(device)) { - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, - irb); + if (IS_ERR(device)) + return; + /* ignore unsolicited interrupts for DIAG discipline */ + if (device->discipline == dasd_diag_discipline_pointer) { dasd_put_device(device); + return; } + device->discipline->dump_sense_dbf(device, irb, + "unsolicited"); + if ((device->features & DASD_FEATURE_ERPLOG)) + device->discipline->dump_sense(device, cqr, + irb); + dasd_device_clear_timer(device); + device->discipline->handle_unsolicited_interrupt(device, + irb); + dasd_put_device(device); return; } diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index e82d427ff5e..968c76cf712 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -221,6 +221,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ccw->cmd_code = CCW_CMD_DCTL; ccw->count = 4; ccw->cda = (__u32)(addr_t) DCTL_data; + dctl_cqr->flags = erp->flags; dctl_cqr->function = dasd_3990_erp_DCTL; dctl_cqr->refers = erp; dctl_cqr->startdev = device; @@ -1710,6 +1711,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) ccw->cda = cpa; /* fill erp related fields */ + erp->flags = default_erp->flags; erp->function = dasd_3990_erp_action_1B_32; erp->refers = default_erp->refers; erp->startdev = device; @@ -2354,6 +2356,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) ccw->cda = (long)(cqr->cpaddr); } + erp->flags = cqr->flags; erp->function = dasd_3990_erp_add_erp; erp->refers = cqr; erp->startdev = device; diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 2b3bc3ec054..266b34b5540 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -228,25 +228,22 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) } /* Handle external interruption. */ -static void -dasd_ext_handler(__u16 code) +static void dasd_ext_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct dasd_ccw_req *cqr, *next; struct dasd_device *device; unsigned long long expires; unsigned long flags; - u8 int_code, status; addr_t ip; int rc; - int_code = *((u8 *) DASD_DIAG_LC_INT_CODE); - status = *((u8 *) DASD_DIAG_LC_INT_STATUS); - switch (int_code) { + switch (ext_int_code >> 24) { case DASD_DIAG_CODE_31BIT: - ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT); + ip = (addr_t) param32; break; case DASD_DIAG_CODE_64BIT: - ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT); + ip = (addr_t) param64; break; default: return; @@ -281,7 +278,7 @@ dasd_ext_handler(__u16 code) cqr->stopclk = get_clock(); expires = 0; - if (status == 0) { + if ((ext_int_code & 0xff0000) == 0) { cqr->status = DASD_CQR_SUCCESS; /* Start first request on queue if possible -> fast_io. */ if (!list_empty(&device->ccw_queue)) { @@ -296,8 +293,8 @@ dasd_ext_handler(__u16 code) } else { cqr->status = DASD_CQR_QUEUED; DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for " - "request %p was %d (%d retries left)", cqr, status, - cqr->retries); + "request %p was %d (%d retries left)", cqr, + (ext_int_code >> 16) & 0xff, cqr->retries); dasd_diag_erp(device); } diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h index b8c78267ff3..4f71fbe60c8 100644 --- a/drivers/s390/block/dasd_diag.h +++ b/drivers/s390/block/dasd_diag.h @@ -18,10 +18,6 @@ #define DEV_CLASS_FBA 0x01 #define DEV_CLASS_ECKD 0x04 -#define DASD_DIAG_LC_INT_CODE 132 -#define DASD_DIAG_LC_INT_STATUS 133 -#define DASD_DIAG_LC_INT_PARM_31BIT 128 -#define DASD_DIAG_LC_INT_PARM_64BIT 4536 #define DASD_DIAG_CODE_31BIT 0x03 #define DASD_DIAG_CODE_64BIT 0x07 diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 59b4ecfb967..50cf96389d2 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1776,13 +1776,13 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, } /* summary unit check */ - if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && - (irb->ecw[7] == 0x0D)) { + sense = dasd_get_sense(irb); + if (sense && (sense[7] == 0x0D) && + (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { dasd_alias_handle_summary_unit_check(device, irb); return; } - sense = dasd_get_sense(irb); /* service information message SIM */ if (sense && !(sense[27] & DASD_SENSE_BIT_0) && ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { @@ -1791,26 +1791,15 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, return; } - if ((scsw_cc(&irb->scsw) == 1) && - (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && - (scsw_actl(&irb->scsw) & SCSW_ACTL_START_PEND) && - (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) { + if ((scsw_cc(&irb->scsw) == 1) && !sense && + (scsw_fctl(&irb->scsw) == SCSW_FCTL_START_FUNC) && + (scsw_actl(&irb->scsw) == SCSW_ACTL_START_PEND) && + (scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND)) { /* fake irb do nothing, they are handled elsewhere */ dasd_schedule_device_bh(device); return; } - if (!sense) { - /* just report other unsolicited interrupts */ - DBF_DEV_EVENT(DBF_ERR, device, "%s", - "unsolicited interrupt received"); - } else { - DBF_DEV_EVENT(DBF_ERR, device, "%s", - "unsolicited interrupt received " - "(sense available)"); - device->discipline->dump_sense_dbf(device, irb, "unsolicited"); - } - dasd_schedule_device_bh(device); return; }; @@ -3093,19 +3082,19 @@ dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb, char *reason) { u64 *sense; + u64 *stat; sense = (u64 *) dasd_get_sense(irb); + stat = (u64 *) &irb->scsw; if (sense) { - DBF_DEV_EVENT(DBF_EMERG, device, - "%s: %s %02x%02x%02x %016llx %016llx %016llx " - "%016llx", reason, - scsw_is_tm(&irb->scsw) ? "t" : "c", - scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), - scsw_dstat(&irb->scsw), sense[0], sense[1], - sense[2], sense[3]); + DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : " + "%016llx %016llx %016llx %016llx", + reason, *stat, *((u32 *) (stat + 1)), + sense[0], sense[1], sense[2], sense[3]); } else { - DBF_DEV_EVENT(DBF_EMERG, device, "%s", - "SORRY - NO VALID SENSE AVAILABLE\n"); + DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s", + reason, *stat, *((u32 *) (stat + 1)), + "NO VALID SENSE"); } } @@ -3131,9 +3120,12 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d\n", - req, scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw), - scsw_cc(&irb->scsw), req ? req->intrc : 0); + " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " + "CS:%02X RC:%d\n", + req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), + scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), + scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), + req ? req->intrc : 0); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " device %s: Failing CCW: %p\n", dev_name(&device->cdev->dev), @@ -3234,11 +3226,13 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d " - "fcxs: 0x%02X schxs: 0x%02X\n", req, - scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw), - scsw_cc(&irb->scsw), req->intrc, - irb->scsw.tm.fcxs, irb->scsw.tm.schxs); + " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " + "CS:%02X fcxs:%02X schxs:%02X RC:%d\n", + req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), + scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), + scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), + irb->scsw.tm.fcxs, irb->scsw.tm.schxs, + req ? req->intrc : 0); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " device %s: Failing TCW: %p\n", dev_name(&device->cdev->dev), @@ -3246,7 +3240,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, tsb = NULL; sense = NULL; - if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs == 0x01)) + if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01)) tsb = tcw_get_tsb( (struct tcw *)(unsigned long)irb->scsw.tm.tcw); @@ -3344,7 +3338,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, static void dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req *req, struct irb *irb) { - if (req && scsw_is_tm(&req->irb.scsw)) + if (scsw_is_tm(&irb->scsw)) dasd_eckd_dump_sense_tcw(device, req, irb); else dasd_eckd_dump_sense_ccw(device, req, irb); diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 2eb02559280..c4a6a31bd9c 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -251,7 +251,6 @@ static ssize_t dasd_stats_proc_write(struct file *file, buffer = dasd_get_user_string(user_buf, user_len); if (IS_ERR(buffer)) return PTR_ERR(buffer); - DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer); /* check for valid verbs */ str = skip_spaces(buffer); diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 5707a80b96b..35cc4686b99 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -395,16 +395,16 @@ __sclp_find_req(u32 sccb) /* Handler for external interruption. Perform request post-processing. * Prepare read event data request if necessary. Start processing of next * request on queue. */ -static void -sclp_interrupt_handler(__u16 code) +static void sclp_interrupt_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct sclp_req *req; u32 finished_sccb; u32 evbuf_pending; spin_lock(&sclp_lock); - finished_sccb = S390_lowcore.ext_params & 0xfffffff8; - evbuf_pending = S390_lowcore.ext_params & 0x3; + finished_sccb = param32 & 0xfffffff8; + evbuf_pending = param32 & 0x3; if (finished_sccb) { del_timer(&sclp_request_timer); sclp_running_state = sclp_running_state_reset_pending; @@ -819,12 +819,12 @@ EXPORT_SYMBOL(sclp_reactivate); /* Handler for external interruption used during initialization. Modify * request state to done. */ -static void -sclp_check_handler(__u16 code) +static void sclp_check_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { u32 finished_sccb; - finished_sccb = S390_lowcore.ext_params & 0xfffffff8; + finished_sccb = param32 & 0xfffffff8; /* Is this the interrupt we are waiting for? */ if (finished_sccb == 0) return; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 0d6dc4b92cc..9f661426e4a 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -215,7 +215,7 @@ static void vmlogrdr_iucv_message_pending(struct iucv_path *path, static int vmlogrdr_get_recording_class_AB(void) { - char cp_command[]="QUERY COMMAND RECORDING "; + static const char cp_command[] = "QUERY COMMAND RECORDING "; char cp_response[80]; char *tail; int len,i; @@ -638,7 +638,7 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) { - char cp_command[] = "QUERY RECORDING "; + static const char cp_command[] = "QUERY RECORDING "; int len; cpcmd(cp_command, buf, 4096, NULL); diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 13cb60162e4..76058a5166e 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -79,17 +79,15 @@ static int pure_hex(char **cp, unsigned int *val, int min_digit, int max_digit, int max_val) { int diff; - unsigned int value; diff = 0; *val = 0; - while (isxdigit(**cp) && (diff <= max_digit)) { + while (diff <= max_digit) { + int value = hex_to_bin(**cp); - if (isdigit(**cp)) - value = **cp - '0'; - else - value = tolower(**cp) - 'a' + 10; + if (value < 0) + break; *val = *val * 16 + value; (*cp)++; diff++; diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 6c9fa15aac7..2d32233943a 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/chp.c * - * Copyright IBM Corp. 1999,2007 + * Copyright IBM Corp. 1999,2010 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> @@ -54,12 +54,6 @@ static struct work_struct cfg_work; /* Wait queue for configure completion events. */ static wait_queue_head_t cfg_wait_queue; -/* Return channel_path struct for given chpid. */ -static inline struct channel_path *chpid_to_chp(struct chp_id chpid) -{ - return channel_subsystems[chpid.cssid]->chps[chpid.id]; -} - /* Set vary state for given chpid. */ static void set_chp_logically_online(struct chp_id chpid, int onoff) { @@ -241,11 +235,13 @@ static ssize_t chp_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct channel_path *chp = to_channelpath(dev); + int status; - if (!chp) - return 0; - return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") : - sprintf(buf, "offline\n")); + mutex_lock(&chp->lock); + status = chp->state; + mutex_unlock(&chp->lock); + + return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n"); } static ssize_t chp_status_write(struct device *dev, @@ -261,15 +257,18 @@ static ssize_t chp_status_write(struct device *dev, if (!num_args) return count; - if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) + if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) { + mutex_lock(&cp->lock); error = s390_vary_chpid(cp->chpid, 1); - else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) + mutex_unlock(&cp->lock); + } else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) { + mutex_lock(&cp->lock); error = s390_vary_chpid(cp->chpid, 0); - else + mutex_unlock(&cp->lock); + } else error = -EINVAL; return error < 0 ? error : count; - } static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); @@ -315,10 +314,12 @@ static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct channel_path *chp = to_channelpath(dev); + u8 type; - if (!chp) - return 0; - return sprintf(buf, "%x\n", chp->desc.desc); + mutex_lock(&chp->lock); + type = chp->desc.desc; + mutex_unlock(&chp->lock); + return sprintf(buf, "%x\n", type); } static DEVICE_ATTR(type, 0444, chp_type_show, NULL); @@ -395,6 +396,7 @@ int chp_new(struct chp_id chpid) chp->state = 1; chp->dev.parent = &channel_subsystems[chpid.cssid]->device; chp->dev.release = chp_release; + mutex_init(&chp->lock); /* Obtain channel path description and fill it in. */ ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); @@ -464,7 +466,10 @@ void *chp_get_chp_desc(struct chp_id chpid) desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); if (!desc) return NULL; + + mutex_lock(&chp->lock); memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); + mutex_unlock(&chp->lock); return desc; } diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 26c3d224617..12b4903d6fe 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -1,7 +1,7 @@ /* * drivers/s390/cio/chp.h * - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007,2010 * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ @@ -10,6 +10,7 @@ #include <linux/types.h> #include <linux/device.h> +#include <linux/mutex.h> #include <asm/chpid.h> #include "chsc.h" #include "css.h" @@ -40,16 +41,23 @@ static inline int chp_test_bit(u8 *bitmap, int num) struct channel_path { + struct device dev; struct chp_id chpid; + struct mutex lock; /* Serialize access to below members. */ int state; struct channel_path_desc desc; /* Channel-measurement related stuff: */ int cmg; int shared; void *cmg_chars; - struct device dev; }; +/* Return channel_path struct for given chpid. */ +static inline struct channel_path *chpid_to_chp(struct chp_id chpid) +{ + return channel_subsystems[chpid.cssid]->chps[chpid.id]; +} + int chp_get_status(struct chp_id chpid); u8 chp_get_sch_opm(struct subchannel *sch); int chp_is_registered(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 4cbb1a6ca33..1aaddea673e 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -2,7 +2,7 @@ * drivers/s390/cio/chsc.c * S/390 common I/O routines -- channel subsystem call * - * Copyright IBM Corp. 1999,2008 + * Copyright IBM Corp. 1999,2010 * Author(s): Ingo Adlung (adlung@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) @@ -29,8 +29,8 @@ #include "chsc.h" static void *sei_page; -static DEFINE_SPINLOCK(siosl_lock); -static DEFINE_SPINLOCK(sda_lock); +static void *chsc_page; +static DEFINE_SPINLOCK(chsc_page_lock); /** * chsc_error_from_response() - convert a chsc response to an error @@ -85,17 +85,15 @@ struct chsc_ssd_area { int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) { - unsigned long page; struct chsc_ssd_area *ssd_area; int ccode; int ret; int i; int mask; - page = get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!page) - return -ENOMEM; - ssd_area = (struct chsc_ssd_area *) page; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + ssd_area = chsc_page; ssd_area->request.length = 0x0010; ssd_area->request.code = 0x0004; ssd_area->ssid = schid.ssid; @@ -106,25 +104,25 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) /* Check response. */ if (ccode > 0) { ret = (ccode == 3) ? -ENODEV : -EBUSY; - goto out_free; + goto out; } ret = chsc_error_from_response(ssd_area->response.code); if (ret != 0) { CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", schid.ssid, schid.sch_no, ssd_area->response.code); - goto out_free; + goto out; } if (!ssd_area->sch_valid) { ret = -ENODEV; - goto out_free; + goto out; } /* Copy data */ ret = 0; memset(ssd, 0, sizeof(struct chsc_ssd_info)); if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && (ssd_area->st != SUBCHANNEL_TYPE_MSG)) - goto out_free; + goto out; ssd->path_mask = ssd_area->path_mask; ssd->fla_valid_mask = ssd_area->fla_valid_mask; for (i = 0; i < 8; i++) { @@ -136,8 +134,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) if (ssd_area->fla_valid_mask & mask) ssd->fla[i] = ssd_area->fla[i]; } -out_free: - free_page(page); +out: + spin_unlock_irq(&chsc_page_lock); return ret; } @@ -497,6 +495,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) */ int chsc_chp_vary(struct chp_id chpid, int on) { + struct channel_path *chp = chpid_to_chp(chpid); struct chp_link link; memset(&link, 0, sizeof(struct chp_link)); @@ -506,11 +505,12 @@ int chsc_chp_vary(struct chp_id chpid, int on) /* * Redo PathVerification on the devices the chpid connects to */ - - if (on) + if (on) { + /* Try to update the channel path descritor. */ + chsc_determine_base_channel_path_desc(chpid, &chp->desc); for_each_subchannel_staged(s390_subchannel_vary_chpid_on, __s390_vary_chpid_on, &link); - else + } else for_each_subchannel_staged(s390_subchannel_vary_chpid_off, NULL, &link); @@ -552,7 +552,7 @@ cleanup: return ret; } -int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) +int __chsc_do_secm(struct channel_subsystem *css, int enable) { struct { struct chsc_header request; @@ -573,7 +573,9 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) } __attribute__ ((packed)) *secm_area; int ret, ccode; - secm_area = page; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + secm_area = chsc_page; secm_area->request.length = 0x0050; secm_area->request.code = 0x0016; @@ -584,8 +586,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) secm_area->operation_code = enable ? 0 : 1; ccode = chsc(secm_area); - if (ccode > 0) - return (ccode == 3) ? -ENODEV : -EBUSY; + if (ccode > 0) { + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out; + } switch (secm_area->response.code) { case 0x0102: @@ -598,37 +602,32 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) if (ret != 0) CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", secm_area->response.code); +out: + spin_unlock_irq(&chsc_page_lock); return ret; } int chsc_secm(struct channel_subsystem *css, int enable) { - void *secm_area; int ret; - secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!secm_area) - return -ENOMEM; - if (enable && !css->cm_enabled) { css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!css->cub_addr1 || !css->cub_addr2) { free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); - free_page((unsigned long)secm_area); return -ENOMEM; } } - ret = __chsc_do_secm(css, enable, secm_area); + ret = __chsc_do_secm(css, enable); if (!ret) { css->cm_enabled = enable; if (css->cm_enabled) { ret = chsc_add_cmg_attr(css); if (ret) { - memset(secm_area, 0, PAGE_SIZE); - __chsc_do_secm(css, 0, secm_area); + __chsc_do_secm(css, 0); css->cm_enabled = 0; } } else @@ -638,44 +637,24 @@ chsc_secm(struct channel_subsystem *css, int enable) free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); } - free_page((unsigned long)secm_area); return ret; } int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, - int c, int m, - struct chsc_response_struct *resp) + int c, int m, void *page) { + struct chsc_scpd *scpd_area; int ccode, ret; - struct { - struct chsc_header request; - u32 : 2; - u32 m : 1; - u32 c : 1; - u32 fmt : 4; - u32 cssid : 8; - u32 : 4; - u32 rfmt : 4; - u32 first_chpid : 8; - u32 : 24; - u32 last_chpid : 8; - u32 zeroes1; - struct chsc_header response; - u8 data[PAGE_SIZE - 20]; - } __attribute__ ((packed)) *scpd_area; - if ((rfmt == 1) && !css_general_characteristics.fcs) return -EINVAL; if ((rfmt == 2) && !css_general_characteristics.cib) return -EINVAL; - scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scpd_area) - return -ENOMEM; + memset(page, 0, PAGE_SIZE); + scpd_area = page; scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; - scpd_area->cssid = chpid.cssid; scpd_area->first_chpid = chpid.id; scpd_area->last_chpid = chpid.id; @@ -685,20 +664,13 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, scpd_area->rfmt = rfmt; ccode = chsc(scpd_area); - if (ccode > 0) { - ret = (ccode == 3) ? -ENODEV : -EBUSY; - goto out; - } + if (ccode > 0) + return (ccode == 3) ? -ENODEV : -EBUSY; ret = chsc_error_from_response(scpd_area->response.code); - if (ret == 0) - /* Success. */ - memcpy(resp, &scpd_area->response, scpd_area->response.length); - else + if (ret) CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", scpd_area->response.code); -out: - free_page((unsigned long)scpd_area); return ret; } EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); @@ -707,17 +679,19 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid, struct channel_path_desc *desc) { struct chsc_response_struct *chsc_resp; + struct chsc_scpd *scpd_area; + unsigned long flags; int ret; - chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); - if (!chsc_resp) - return -ENOMEM; - ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp); + spin_lock_irqsave(&chsc_page_lock, flags); + scpd_area = chsc_page; + ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area); if (ret) - goto out_free; + goto out; + chsc_resp = (void *)&scpd_area->response; memcpy(desc, &chsc_resp->data, sizeof(*desc)); -out_free: - kfree(chsc_resp); +out: + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } @@ -725,33 +699,22 @@ static void chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, struct cmg_chars *chars) { - switch (chp->cmg) { - case 2: - case 3: - chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), - GFP_KERNEL); - if (chp->cmg_chars) { - int i, mask; - struct cmg_chars *cmg_chars; - - cmg_chars = chp->cmg_chars; - for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { - mask = 0x80 >> (i + 3); - if (cmcv & mask) - cmg_chars->values[i] = chars->values[i]; - else - cmg_chars->values[i] = 0; - } - } - break; - default: - /* No cmg-dependent data. */ - break; + struct cmg_chars *cmg_chars; + int i, mask; + + cmg_chars = chp->cmg_chars; + for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { + mask = 0x80 >> (i + 3); + if (cmcv & mask) + cmg_chars->values[i] = chars->values[i]; + else + cmg_chars->values[i] = 0; } } int chsc_get_channel_measurement_chars(struct channel_path *chp) { + struct cmg_chars *cmg_chars; int ccode, ret; struct { @@ -775,13 +738,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) u32 data[NR_MEASUREMENT_CHARS]; } __attribute__ ((packed)) *scmc_area; - scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scmc_area) + chp->cmg_chars = NULL; + cmg_chars = kmalloc(sizeof(*cmg_chars), GFP_KERNEL); + if (!cmg_chars) return -ENOMEM; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + scmc_area = chsc_page; scmc_area->request.length = 0x0010; scmc_area->request.code = 0x0022; - scmc_area->first_chpid = chp->chpid.id; scmc_area->last_chpid = chp->chpid.id; @@ -792,53 +758,65 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) } ret = chsc_error_from_response(scmc_area->response.code); - if (ret == 0) { - /* Success. */ - if (!scmc_area->not_valid) { - chp->cmg = scmc_area->cmg; - chp->shared = scmc_area->shared; - chsc_initialize_cmg_chars(chp, scmc_area->cmcv, - (struct cmg_chars *) - &scmc_area->data); - } else { - chp->cmg = -1; - chp->shared = -1; - } - } else { + if (ret) { CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n", scmc_area->response.code); + goto out; + } + if (scmc_area->not_valid) { + chp->cmg = -1; + chp->shared = -1; + goto out; } + chp->cmg = scmc_area->cmg; + chp->shared = scmc_area->shared; + if (chp->cmg != 2 && chp->cmg != 3) { + /* No cmg-dependent data. */ + goto out; + } + chp->cmg_chars = cmg_chars; + chsc_initialize_cmg_chars(chp, scmc_area->cmcv, + (struct cmg_chars *) &scmc_area->data); out: - free_page((unsigned long)scmc_area); + spin_unlock_irq(&chsc_page_lock); + if (!chp->cmg_chars) + kfree(cmg_chars); + return ret; } -int __init chsc_alloc_sei_area(void) +int __init chsc_init(void) { int ret; sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!sei_page) { - CIO_MSG_EVENT(0, "Can't allocate page for processing of " - "chsc machine checks!\n"); - return -ENOMEM; + chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sei_page || !chsc_page) { + ret = -ENOMEM; + goto out_err; } ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); if (ret) - kfree(sei_page); + goto out_err; + return ret; +out_err: + free_page((unsigned long)chsc_page); + free_page((unsigned long)sei_page); return ret; } -void __init chsc_free_sei_area(void) +void __init chsc_init_cleanup(void) { crw_unregister_handler(CRW_RSC_CSS); - kfree(sei_page); + free_page((unsigned long)chsc_page); + free_page((unsigned long)sei_page); } int chsc_enable_facility(int operation_code) { + unsigned long flags; int ret; - static struct { + struct { struct chsc_header request; u8 reserved1:4; u8 format:4; @@ -851,32 +829,33 @@ int chsc_enable_facility(int operation_code) u32 reserved5:4; u32 format2:4; u32 reserved6:24; - } __attribute__ ((packed, aligned(4096))) sda_area; + } __attribute__ ((packed)) *sda_area; - spin_lock(&sda_lock); - memset(&sda_area, 0, sizeof(sda_area)); - sda_area.request.length = 0x0400; - sda_area.request.code = 0x0031; - sda_area.operation_code = operation_code; + spin_lock_irqsave(&chsc_page_lock, flags); + memset(chsc_page, 0, PAGE_SIZE); + sda_area = chsc_page; + sda_area->request.length = 0x0400; + sda_area->request.code = 0x0031; + sda_area->operation_code = operation_code; - ret = chsc(&sda_area); + ret = chsc(sda_area); if (ret > 0) { ret = (ret == 3) ? -ENODEV : -EBUSY; goto out; } - switch (sda_area.response.code) { + switch (sda_area->response.code) { case 0x0101: ret = -EOPNOTSUPP; break; default: - ret = chsc_error_from_response(sda_area.response.code); + ret = chsc_error_from_response(sda_area->response.code); } if (ret != 0) CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", - operation_code, sda_area.response.code); - out: - spin_unlock(&sda_lock); + operation_code, sda_area->response.code); +out: + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } @@ -895,13 +874,12 @@ chsc_determine_css_characteristics(void) struct chsc_header response; u32 reserved4; u32 general_char[510]; - u32 chsc_char[518]; + u32 chsc_char[508]; } __attribute__ ((packed)) *scsc_area; - scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scsc_area) - return -ENOMEM; - + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + scsc_area = chsc_page; scsc_area->request.length = 0x0010; scsc_area->request.code = 0x0010; @@ -921,7 +899,7 @@ chsc_determine_css_characteristics(void) CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", scsc_area->response.code); exit: - free_page ((unsigned long) scsc_area); + spin_unlock_irq(&chsc_page_lock); return result; } @@ -976,29 +954,29 @@ int chsc_sstpi(void *page, void *result, size_t size) return (rr->response.code == 0x0001) ? 0 : -EIO; } -static struct { - struct chsc_header request; - u32 word1; - struct subchannel_id sid; - u32 word3; - struct chsc_header response; - u32 word[11]; -} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE))); - int chsc_siosl(struct subchannel_id schid) { + struct { + struct chsc_header request; + u32 word1; + struct subchannel_id sid; + u32 word3; + struct chsc_header response; + u32 word[11]; + } __attribute__ ((packed)) *siosl_area; unsigned long flags; int ccode; int rc; - spin_lock_irqsave(&siosl_lock, flags); - memset(&siosl_area, 0, sizeof(siosl_area)); - siosl_area.request.length = 0x0010; - siosl_area.request.code = 0x0046; - siosl_area.word1 = 0x80000000; - siosl_area.sid = schid; + spin_lock_irqsave(&chsc_page_lock, flags); + memset(chsc_page, 0, PAGE_SIZE); + siosl_area = chsc_page; + siosl_area->request.length = 0x0010; + siosl_area->request.code = 0x0046; + siosl_area->word1 = 0x80000000; + siosl_area->sid = schid; - ccode = chsc(&siosl_area); + ccode = chsc(siosl_area); if (ccode > 0) { if (ccode == 3) rc = -ENODEV; @@ -1008,17 +986,16 @@ int chsc_siosl(struct subchannel_id schid) schid.ssid, schid.sch_no, ccode); goto out; } - rc = chsc_error_from_response(siosl_area.response.code); + rc = chsc_error_from_response(siosl_area->response.code); if (rc) CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", schid.ssid, schid.sch_no, - siosl_area.response.code); + siosl_area->response.code); else CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", schid.ssid, schid.sch_no); out: - spin_unlock_irqrestore(&siosl_lock, flags); - + spin_unlock_irqrestore(&chsc_page_lock, flags); return rc; } EXPORT_SYMBOL_GPL(chsc_siosl); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 5453013f094..6693f5e3176 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -57,21 +57,39 @@ struct chsc_ssd_info { struct chp_id chpid[8]; u16 fla[8]; }; + +struct chsc_scpd { + struct chsc_header request; + u32:2; + u32 m:1; + u32 c:1; + u32 fmt:4; + u32 cssid:8; + u32:4; + u32 rfmt:4; + u32 first_chpid:8; + u32:24; + u32 last_chpid:8; + u32 zeroes1; + struct chsc_header response; + u8 data[PAGE_SIZE - 20]; +} __attribute__ ((packed)); + + extern int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); -extern int chsc_alloc_sei_area(void); -extern void chsc_free_sei_area(void); +extern int chsc_init(void); +extern void chsc_init_cleanup(void); extern int chsc_enable_facility(int); struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); -int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page); +int __chsc_do_secm(struct channel_subsystem *css, int enable); int chsc_chp_vary(struct chp_id chpid, int on); int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, - int c, int m, - struct chsc_response_struct *resp); + int c, int m, void *page); int chsc_determine_base_channel_path_desc(struct chp_id chpid, struct channel_path_desc *desc); void chsc_chp_online(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index f2b77e7bfc6..3c3f3ffe217 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -688,25 +688,31 @@ out_free: static int chsc_ioctl_chpd(void __user *user_chpd) { + struct chsc_scpd *scpd_area; struct chsc_cpd_info *chpd; int ret; chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); - if (!chpd) - return -ENOMEM; + scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpd_area || !chpd) { + ret = -ENOMEM; + goto out_free; + } if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) { ret = -EFAULT; goto out_free; } ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt, chpd->rfmt, chpd->c, chpd->m, - &chpd->chpdb); + scpd_area); if (ret) goto out_free; + memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length); if (copy_to_user(user_chpd, chpd, sizeof(*chpd))) ret = -EFAULT; out_free: kfree(chpd); + free_page((unsigned long)scpd_area); return ret; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index ca8e1c240c3..a5050e21715 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1,7 +1,7 @@ /* * driver for channel subsystem * - * Copyright IBM Corp. 2002, 2009 + * Copyright IBM Corp. 2002, 2010 * * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) @@ -577,7 +577,7 @@ static int __unset_registered(struct device *dev, void *data) return 0; } -void css_schedule_eval_all_unreg(void) +static void css_schedule_eval_all_unreg(void) { unsigned long flags; struct idset *unreg_set; @@ -790,7 +790,6 @@ static struct notifier_block css_reboot_notifier = { static int css_power_event(struct notifier_block *this, unsigned long event, void *ptr) { - void *secm_area; int ret, i; switch (event) { @@ -806,15 +805,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event, mutex_unlock(&css->mutex); continue; } - secm_area = (void *)get_zeroed_page(GFP_KERNEL | - GFP_DMA); - if (secm_area) { - if (__chsc_do_secm(css, 0, secm_area)) - ret = NOTIFY_BAD; - free_page((unsigned long)secm_area); - } else + if (__chsc_do_secm(css, 0)) ret = NOTIFY_BAD; - mutex_unlock(&css->mutex); } break; @@ -830,15 +822,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event, mutex_unlock(&css->mutex); continue; } - secm_area = (void *)get_zeroed_page(GFP_KERNEL | - GFP_DMA); - if (secm_area) { - if (__chsc_do_secm(css, 1, secm_area)) - ret = NOTIFY_BAD; - free_page((unsigned long)secm_area); - } else + if (__chsc_do_secm(css, 1)) ret = NOTIFY_BAD; - mutex_unlock(&css->mutex); } /* search for subchannels, which appeared during hibernation */ @@ -863,14 +848,11 @@ static int __init css_bus_init(void) { int ret, i; - ret = chsc_determine_css_characteristics(); - if (ret == -ENOMEM) - goto out; - - ret = chsc_alloc_sei_area(); + ret = chsc_init(); if (ret) - goto out; + return ret; + chsc_determine_css_characteristics(); /* Try to enable MSS. */ ret = chsc_enable_facility(CHSC_SDA_OC_MSS); if (ret) @@ -956,9 +938,9 @@ out_unregister: } bus_unregister(&css_bus_type); out: - crw_unregister_handler(CRW_RSC_CSS); - chsc_free_sei_area(); + crw_unregister_handler(CRW_RSC_SCH); idset_free(slow_subchannel_set); + chsc_init_cleanup(); pr_alert("The CSS device driver initialization failed with " "errno=%d\n", ret); return ret; @@ -978,9 +960,9 @@ static void __init css_bus_cleanup(void) device_unregister(&css->device); } bus_unregister(&css_bus_type); - crw_unregister_handler(CRW_RSC_CSS); - chsc_free_sei_area(); + crw_unregister_handler(CRW_RSC_SCH); idset_free(slow_subchannel_set); + chsc_init_cleanup(); isc_unregister(IO_SCH_ISC); } @@ -1048,7 +1030,16 @@ subsys_initcall_sync(channel_subsystem_init_sync); void channel_subsystem_reinit(void) { + struct channel_path *chp; + struct chp_id chpid; + chsc_enable_facility(CHSC_SDA_OC_MSS); + chp_id_for_each(&chpid) { + chp = chpid_to_chp(chpid); + if (!chp) + continue; + chsc_determine_base_channel_path_desc(chpid, &chp->desc); + } } #ifdef CONFIG_PROC_FS @@ -1200,6 +1191,7 @@ static int css_pm_restore(struct device *dev) struct subchannel *sch = to_subchannel(dev); struct css_driver *drv; + css_update_ssd_info(sch); if (!sch->dev.driver) return 0; drv = to_cssdriver(sch->dev.driver); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 51bd3687d16..2ff8a22d425 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1147,6 +1147,7 @@ err: static int io_subchannel_chp_event(struct subchannel *sch, struct chp_link *link, int event) { + struct ccw_device *cdev = sch_get_cdev(sch); int mask; mask = chp_ssd_get_mask(&sch->ssd_info, link); @@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch, case CHP_VARY_OFF: sch->opm &= ~mask; sch->lpm &= ~mask; + if (cdev) + cdev->private->path_gone_mask |= mask; io_subchannel_terminate_path(sch, mask); break; case CHP_VARY_ON: sch->opm |= mask; sch->lpm |= mask; + if (cdev) + cdev->private->path_new_mask |= mask; io_subchannel_verify(sch); break; case CHP_OFFLINE: if (cio_update_schib(sch)) return -ENODEV; + if (cdev) + cdev->private->path_gone_mask |= mask; io_subchannel_terminate_path(sch, mask); break; case CHP_ONLINE: if (cio_update_schib(sch)) return -ENODEV; sch->lpm |= mask & sch->opm; + if (cdev) + cdev->private->path_new_mask |= mask; io_subchannel_verify(sch); break; } @@ -1196,6 +1205,7 @@ static void io_subchannel_quiesce(struct subchannel *sch) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); while (ret == -EBUSY) { cdev->private->state = DEV_STATE_QUIESCE; + cdev->private->iretry = 255; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, HZ/10); @@ -1468,9 +1478,13 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) goto out; break; case IO_SCH_UNREG_ATTACH: + if (cdev->private->flags.resuming) { + /* Device will be handled later. */ + rc = 0; + goto out; + } /* Unregister ccw device. */ - if (!cdev->private->flags.resuming) - ccw_device_unregister(cdev); + ccw_device_unregister(cdev); break; default: break; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index c9b852647f0..a845695ac31 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -174,7 +174,10 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) ret = cio_clear (sch); return (ret == 0) ? -EBUSY : ret; } - panic("Can't stop i/o on subchannel.\n"); + /* Function was unsuccessful */ + CIO_MSG_EVENT(0, "0.%x.%04x: could not stop I/O\n", + cdev->private->dev_id.ssid, cdev->private->dev_id.devno); + return -EIO; } void ccw_device_update_sense_data(struct ccw_device *cdev) @@ -349,9 +352,13 @@ out: static void ccw_device_oper_notify(struct ccw_device *cdev) { + struct subchannel *sch = to_subchannel(cdev->dev.parent); + if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) { /* Reenable channel measurements, if needed. */ ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); + /* Save indication for new paths. */ + cdev->private->path_new_mask = sch->vpm; return; } /* Driver doesn't want device back. */ @@ -462,6 +469,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e) } } +static void ccw_device_report_path_events(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + int path_event[8]; + int chp, mask; + + for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) { + path_event[chp] = PE_NONE; + if (mask & cdev->private->path_gone_mask & ~(sch->vpm)) + path_event[chp] |= PE_PATH_GONE; + if (mask & cdev->private->path_new_mask & sch->vpm) + path_event[chp] |= PE_PATH_AVAILABLE; + if (mask & cdev->private->pgid_reset_mask & sch->vpm) + path_event[chp] |= PE_PATHGROUP_ESTABLISHED; + } + if (cdev->online && cdev->drv->path_event) + cdev->drv->path_event(cdev, path_event); +} + +static void ccw_device_reset_path_events(struct ccw_device *cdev) +{ + cdev->private->path_gone_mask = 0; + cdev->private->path_new_mask = 0; + cdev->private->pgid_reset_mask = 0; +} + void ccw_device_verify_done(struct ccw_device *cdev, int err) { @@ -498,6 +531,7 @@ callback: &cdev->private->irb); memset(&cdev->private->irb, 0, sizeof(struct irb)); } + ccw_device_report_path_events(cdev); break; case -ETIME: case -EUSERS: @@ -516,6 +550,7 @@ callback: ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } + ccw_device_reset_path_events(cdev); } /* @@ -734,13 +769,14 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) int ret; ccw_device_set_timeout(cdev, 0); + cdev->private->iretry = 255; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); cdev->private->state = DEV_STATE_TIMEOUT_KILL; return; } - if (ret == -ENODEV) + if (ret) dev_fsm_event(cdev, DEV_EVENT_NOTOPER); else if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, @@ -837,6 +873,7 @@ void ccw_device_kill_io(struct ccw_device *cdev) { int ret; + cdev->private->iretry = 255; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 82a5ad0d63f..07a4fd29f09 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev) spid_do(cdev); } +static int pgid_is_reset(struct pgid *p) +{ + char *c; + + for (c = (char *)p + 1; c < (char *)(p + 1); c++) { + if (*c != 0) + return 0; + } + return 1; +} + static int pgid_cmp(struct pgid *p1, struct pgid *p2) { return memcmp((char *) p1 + 1, (char *) p2 + 1, @@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2) * Determine pathgroup state from PGID data. */ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, - int *mismatch, int *reserved, int *reset) + int *mismatch, int *reserved, u8 *reset) { struct pgid *pgid = &cdev->private->pgid[0]; struct pgid *first = NULL; @@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, continue; if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) *reserved = 1; - if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { - /* A PGID was reset. */ - *reset = 1; + if (pgid_is_reset(pgid)) { + *reset |= lpm; continue; } if (!first) { @@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc) struct pgid *pgid; int mismatch = 0; int reserved = 0; - int reset = 0; + u8 reset = 0; u8 donepm; if (rc) @@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc) donepm = pgid_to_donepm(cdev); sch->vpm = donepm & sch->opm; cdev->private->pgid_todo_mask &= ~donepm; + cdev->private->pgid_reset_mask |= reset; pgid_fill(cdev, pgid); } out: CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " - "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid, + "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid, id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, cdev->private->pgid_todo_mask, mismatch, reserved, reset); switch (rc) { diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 469ef93f230..d024d2c2189 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -151,8 +151,11 @@ struct ccw_device_private { struct subchannel_id schid; /* subchannel number */ struct ccw_request req; /* internal I/O request */ int iretry; - u8 pgid_valid_mask; /* mask of valid PGIDs */ - u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ + u8 pgid_valid_mask; /* mask of valid PGIDs */ + u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ + u8 pgid_reset_mask; /* mask of PGIDs which were reset */ + u8 path_gone_mask; /* mask of paths, that became unavailable */ + u8 path_new_mask; /* mask of paths, that became available */ struct { unsigned int fast:1; /* post with "channel end" */ unsigned int repall:1; /* report every interrupt status */ diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 91c6028d7b7..8fd8c62455e 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -154,14 +154,7 @@ static inline int ap_instructions_available(void) */ static int ap_interrupts_available(void) { - unsigned long long facility_bits[2]; - - if (stfle(facility_bits, 2) <= 1) - return 0; - if (!(facility_bits[0] & (1ULL << 61)) || - !(facility_bits[1] & (1ULL << 62))) - return 0; - return 1; + return test_facility(1) && test_facility(2); } /** diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 5a46b8c5d68..375aeeaf9ea 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -372,21 +372,22 @@ static void hotplug_devices(struct work_struct *dummy) /* * we emulate the request_irq behaviour on top of s390 extints */ -static void kvm_extint_handler(u16 code) +static void kvm_extint_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct virtqueue *vq; u16 subcode; u32 param; - subcode = S390_lowcore.cpu_addr; + subcode = ext_int_code >> 16; if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) return; /* The LSB might be overloaded, we have to mask it */ - vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL); + vq = (struct virtqueue *)(param64 & ~1UL); /* We use ext_params to decide what this interrupt means */ - param = S390_lowcore.ext_params & VIRTIO_PARAM_MASK; + param = param32 & VIRTIO_PARAM_MASK; switch (param) { case VIRTIO_PARAM_CONFIG_CHANGED: diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index 13f48e28a1e..a624f5af432 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -461,7 +461,7 @@ static int jsflash_init(void) { int rc; struct jsflash *jsf; - int node; + phandle node; char banner[128]; struct linux_prom_registers reg0; diff --git a/drivers/serial/68328serial.h b/drivers/serial/68328serial.h index 58aa2154655..664ceb0a158 100644 --- a/drivers/serial/68328serial.h +++ b/drivers/serial/68328serial.h @@ -181,13 +181,8 @@ struct m68k_serial { /* * Define the number of ports supported and their irqs. */ -#ifndef CONFIG_68328_SERIAL_UART2 #define NR_PORTS 1 #define UART_IRQ_DEFNS {UART_IRQ_NUM} -#else -#define NR_PORTS 2 -#define UART_IRQ_DEFNS {UART1_IRQ_NUM, UART2_IRQ_NUM} -#endif #endif /* __KERNEL__ */ #endif /* !(_MC683XX_SERIAL_H) */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 92781648439..aff9dcd051c 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1410,6 +1410,33 @@ config SERIAL_OF_PLATFORM Currently, only 8250 compatible ports are supported, but others can easily be added. +config SERIAL_OMAP + tristate "OMAP serial port support" + depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 + select SERIAL_CORE + help + If you have a machine based on an Texas Instruments OMAP CPU you + can enable its onboard serial ports by enabling this option. + + By enabling this option you take advantage of dma feature available + with the omap-serial driver. DMA support can be enabled from platform + data. + +config SERIAL_OMAP_CONSOLE + bool "Console on OMAP serial port" + depends on SERIAL_OMAP + select SERIAL_CORE_CONSOLE + help + Select this option if you would like to use omap serial port as + console. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyOx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + config SERIAL_OF_PLATFORM_NWPSERIAL tristate "NWP serial port driver" depends on PPC_OF && PPC_DCR diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 1ca4fd599ff..c5705765454 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -88,3 +88,4 @@ obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o +obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 2af8fd11312..17849dcb9ad 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -31,8 +31,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, { struct resource resource; struct device_node *np = ofdev->dev.of_node; - const unsigned int *clk, *spd; - const u32 *prop; + const __be32 *clk, *spd; + const __be32 *prop; int ret, prop_size; memset(port, 0, sizeof *port); @@ -55,23 +55,23 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, /* Check for shifted address mapping */ prop = of_get_property(np, "reg-offset", &prop_size); if (prop && (prop_size == sizeof(u32))) - port->mapbase += *prop; + port->mapbase += be32_to_cpup(prop); /* Check for registers offset within the devices address range */ prop = of_get_property(np, "reg-shift", &prop_size); if (prop && (prop_size == sizeof(u32))) - port->regshift = *prop; + port->regshift = be32_to_cpup(prop); port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; port->type = type; - port->uartclk = *clk; + port->uartclk = be32_to_cpup(clk); port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; /* If current-speed was set, then try not to change it. */ if (spd) - port->custom_divisor = *clk / (16 * (*spd)); + port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); return 0; } diff --git a/drivers/serial/omap-serial.c b/drivers/serial/omap-serial.c new file mode 100644 index 00000000000..14365f72b66 --- /dev/null +++ b/drivers/serial/omap-serial.c @@ -0,0 +1,1333 @@ +/* + * Driver for OMAP-UART controller. + * Based on drivers/serial/8250.c + * + * Copyright (C) 2010 Texas Instruments. + * + * Authors: + * Govindraj R <govindraj.raja@ti.com> + * Thara Gopinath <thara@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Note: This driver is made seperate from 8250 driver as we cannot + * over load 8250 driver with omap platform specific configuration for + * features like DMA, it makes easier to implement features like DMA and + * hardware flow control and software flow control configuration with + * this driver as required for the omap-platform. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial_reg.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/serial_core.h> +#include <linux/irq.h> + +#include <plat/dma.h> +#include <plat/dmtimer.h> +#include <plat/omap-serial.h> + +static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; + +/* Forward declaration of functions */ +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data); +static void serial_omap_rx_timeout(unsigned long uart_no); +static int serial_omap_start_rxdma(struct uart_omap_port *up); + +static inline unsigned int serial_in(struct uart_omap_port *up, int offset) +{ + offset <<= up->port.regshift; + return readw(up->port.membase + offset); +} + +static inline void serial_out(struct uart_omap_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + writew(value, up->port.membase + offset); +} + +static inline void serial_omap_clear_fifos(struct uart_omap_port *up) +{ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +/* + * serial_omap_get_divisor - calculate divisor value + * @port: uart port info + * @baud: baudrate for which divisor needs to be calculated. + * + * We have written our own function to get the divisor so as to support + * 13x mode. 3Mbps Baudrate as an different divisor. + * Reference OMAP TRM Chapter 17: + * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates + * referring to oversampling - divisor value + * baudrate 460,800 to 3,686,400 all have divisor 13 + * except 3,000,000 which has divisor value 16 + */ +static unsigned int +serial_omap_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int divisor; + + if (baud > OMAP_MODE13X_SPEED && baud != 3000000) + divisor = 13; + else + divisor = 16; + return port->uartclk/(baud * divisor); +} + +static void serial_omap_stop_rxdma(struct uart_omap_port *up) +{ + if (up->uart_dma.rx_dma_used) { + del_timer(&up->uart_dma.rx_timer); + omap_stop_dma(up->uart_dma.rx_dma_channel); + omap_free_dma(up->uart_dma.rx_dma_channel); + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_used = false; + } +} + +static void serial_omap_enable_ms(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void serial_omap_stop_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma && + up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { + /* + * Check if dma is still active. If yes do nothing, + * return. Else stop dma + */ + if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel)) + return; + omap_stop_dma(up->uart_dma.tx_dma_channel); + omap_free_dma(up->uart_dma.tx_dma_channel); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_stop_rx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma) + serial_omap_stop_rxdma(up); + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static inline void receive_chars(struct uart_omap_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int flag; + unsigned char ch, lsr = *status; + int max_count = 256; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) { + up->port.icount.parity++; + } else if (lsr & UART_LSR_FE) { + up->port.icount.frame++; + } + + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + lsr |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (lsr & UART_LSR_BI) + flag = TTY_BREAK; + else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); +ignore_char: + lsr = serial_in(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); +} + +static void transmit_chars(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_omap_stop_tx(&up->port); + return; + } + count = up->port.fifosize / 4; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_omap_stop_tx(&up->port); +} + +static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) +{ + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_start_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + struct circ_buf *xmit; + unsigned int start; + int ret = 0; + + if (!up->use_dma) { + serial_omap_enable_ier_thri(up); + return; + } + + if (up->uart_dma.tx_dma_used) + return; + + xmit = &up->port.state->xmit; + + if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) { + ret = omap_request_dma(up->uart_dma.uart_dma_tx, + "UART Tx DMA", + (void *)uart_tx_dma_callback, up, + &(up->uart_dma.tx_dma_channel)); + + if (ret < 0) { + serial_omap_enable_ier_thri(up); + return; + } + } + spin_lock(&(up->uart_dma.tx_lock)); + up->uart_dma.tx_dma_used = true; + spin_unlock(&(up->uart_dma.tx_lock)); + + start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + + UART_XMIT_SIZE) - start; + + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static unsigned int check_modem_status(struct uart_omap_port *up) +{ + unsigned int status; + + status = serial_in(up, UART_MSR); + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if ((status & UART_MSR_ANY_DELTA) == 0) + return status; + + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change + (&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change + (&up->port, status & UART_MSR_CTS); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + return status; +} + +/** + * serial_omap_irq() - This handles the interrupt from one port + * @irq: uart port irq number + * @dev_id: uart port info + */ +static inline irqreturn_t serial_omap_irq(int irq, void *dev_id) +{ + struct uart_omap_port *up = dev_id; + unsigned int iir, lsr; + unsigned long flags; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + if (iir & UART_IIR_RLSI) { + if (!up->use_dma) { + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + } else { + up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + if ((serial_omap_start_rxdma(up) != 0) && + (lsr & UART_LSR_DR)) + receive_chars(up, &lsr); + } + } + + check_modem_status(up); + if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI)) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + up->port_activity = jiffies; + return IRQ_HANDLED; +} + +static unsigned int serial_omap_tx_empty(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + unsigned int ret = 0; + + dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_omap_get_mctrl(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char status; + unsigned int ret = 0; + + status = check_modem_status(up); + dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id); + + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char mcr = 0; + + dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->pdev->id); + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr |= up->mcr; + serial_out(up, UART_MCR, mcr); +} + +static void serial_omap_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial_omap_startup(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags, + up->name, up); + if (retval) + return retval; + + dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id); + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_omap_clear_fifos(up); + /* For Hardware flow control */ + serial_out(up, UART_MCR, UART_MCR_RTS); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + spin_lock_irqsave(&up->port.lock, flags); + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + up->port.mctrl |= TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + up->msr_saved_flags = 0; + if (up->use_dma) { + free_page((unsigned long)up->port.state->xmit.buf); + up->port.state->xmit.buf = dma_alloc_coherent(NULL, + UART_XMIT_SIZE, + (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys), + 0); + init_timer(&(up->uart_dma.rx_timer)); + up->uart_dma.rx_timer.function = serial_omap_rx_timeout; + up->uart_dma.rx_timer.data = up->pdev->id; + /* Currently the buffer size is 4KB. Can increase it */ + up->uart_dma.rx_buf = dma_alloc_coherent(NULL, + up->uart_dma.rx_buf_size, + (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0); + } + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + + up->port_activity = jiffies; + return 0; +} + +static void serial_omap_shutdown(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id); + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_omap_clear_fifos(up); + + /* + * Read data port to reset things, and then free the irq + */ + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + if (up->use_dma) { + dma_free_coherent(up->port.dev, + UART_XMIT_SIZE, up->port.state->xmit.buf, + up->uart_dma.tx_buf_dma_phys); + up->port.state->xmit.buf = NULL; + serial_omap_stop_rx(port); + dma_free_coherent(up->port.dev, + up->uart_dma.rx_buf_size, up->uart_dma.rx_buf, + up->uart_dma.rx_buf_dma_phys); + up->uart_dma.rx_buf = NULL; + } + free_irq(up->port.irq, up); +} + +static inline void +serial_omap_configure_xonxoff + (struct uart_omap_port *up, struct ktermios *termios) +{ + unsigned char efr = 0; + + up->lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); + + serial_out(up, UART_XON1, termios->c_cc[VSTART]); + serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); + + /* clear SW control mode bits */ + efr = up->efr; + efr &= OMAP_UART_SW_CLR; + + /* + * IXON Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXON) + efr |= OMAP_UART_SW_TX; + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. + */ + if (termios->c_iflag & IXOFF) + efr |= OMAP_UART_SW_RX; + + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_DLAB); + + up->mcr = serial_in(up, UART_MCR); + + /* + * IXANY Flag: + * Enable any character to restart output. + * Operation resumes after receiving any + * character after recognition of the XOFF character + */ + if (termios->c_iflag & IXANY) + up->mcr |= UART_MCR_XONANY; + + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + /* Enable special char function UARTi.EFR_REG[5] and + * load the new software flow control mode IXON or IXOFF + * and restore the UARTi.EFR_REG[4] ENHANCED_EN value. + */ + serial_out(up, UART_EFR, efr | UART_EFR_SCD); + serial_out(up, UART_LCR, UART_LCR_DLAB); + + serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_LCR, up->lcr); +} + +static void +serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char cval = 0; + unsigned char efr = 0; + unsigned long flags = 0; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13); + quot = serial_omap_get_divisor(port, baud); + + up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 | + UART_FCR_ENABLE_FIFO; + if (up->use_dma) + up->fcr |= UART_FCR_DMA_SELECT; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * Modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, cval); /* reset DLAB */ + + /* FIFOs and DMA Settings */ + + /* FCR can be changed only when the + * baud clock is not running + * DLL_REG and DLH_REG set to 0. + */ + serial_out(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_DLL, 0); + serial_out(up, UART_DLM, 0); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, UART_LCR_DLAB); + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + /* FIFO ENABLE, DMA MODE */ + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + if (up->use_dma) { + serial_out(up, UART_TI752_TLR, 0); + serial_out(up, UART_OMAP_SCR, + (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8)); + } + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_MCR, up->mcr); + + /* Protocol, Baud Rate, and Interrupt Settings */ + + serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_DISABLE); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, 0); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, cval); + + if (baud > 230400 && baud != 3000000) + serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_MODE13X); + else + serial_out(up, UART_OMAP_MDR1, OMAP_MDR1_MODE16X); + + /* Hardware Flow Control Configuration */ + + if (termios->c_cflag & CRTSCTS) { + efr |= (UART_EFR_CTS | UART_EFR_RTS); + serial_out(up, UART_LCR, UART_LCR_DLAB); + + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */ + serial_out(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); + serial_out(up, UART_LCR, cval); + } + + serial_omap_set_mctrl(&up->port, up->port.mctrl); + /* Software Flow Control Configuration */ + if (termios->c_iflag & (IXON | IXOFF)) + serial_omap_configure_xonxoff(up, termios); + + spin_unlock_irqrestore(&up->port.lock, flags); + dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id); +} + +static void +serial_omap_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char efr; + + dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, OMAP_UART_LCR_CONF_MDB); + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); + /* Enable module level wake up */ + serial_out(up, UART_OMAP_WER, + (state != 0) ? OMAP_UART_WER_MOD_WKUP : 0); +} + +static void serial_omap_release_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_release_port+\n"); +} + +static int serial_omap_request_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_request_port+\n"); + return 0; +} + +static void serial_omap_config_port(struct uart_port *port, int flags) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", + up->pdev->id); + up->port.type = PORT_OMAP; +} + +static int +serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + dev_dbg(port->dev, "serial_omap_verify_port+\n"); + return -EINVAL; +} + +static const char * +serial_omap_type(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->pdev->id); + return up->name; +} + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + +static struct uart_omap_port *serial_omap_console_ports[4]; + +static struct uart_driver serial_omap_reg; + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static inline void wait_for_xmitr(struct uart_omap_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) + break; + + udelay(1); + } + } +} + +static void serial_omap_console_putchar(struct uart_port *port, int ch) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +static void +serial_omap_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_omap_port *up = serial_omap_console_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_omap_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + check_modem_status(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init +serial_omap_console_setup(struct console *co, char *options) +{ + struct uart_omap_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (serial_omap_console_ports[co->index] == NULL) + return -ENODEV; + up = serial_omap_console_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_omap_console = { + .name = OMAP_SERIAL_NAME, + .write = serial_omap_console_write, + .device = uart_console_device, + .setup = serial_omap_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_omap_reg, +}; + +static void serial_omap_add_console_port(struct uart_omap_port *up) +{ + serial_omap_console_ports[up->pdev->id] = up; +} + +#define OMAP_CONSOLE (&serial_omap_console) + +#else + +#define OMAP_CONSOLE NULL + +static inline void serial_omap_add_console_port(struct uart_omap_port *up) +{} + +#endif + +static struct uart_ops serial_omap_pops = { + .tx_empty = serial_omap_tx_empty, + .set_mctrl = serial_omap_set_mctrl, + .get_mctrl = serial_omap_get_mctrl, + .stop_tx = serial_omap_stop_tx, + .start_tx = serial_omap_start_tx, + .stop_rx = serial_omap_stop_rx, + .enable_ms = serial_omap_enable_ms, + .break_ctl = serial_omap_break_ctl, + .startup = serial_omap_startup, + .shutdown = serial_omap_shutdown, + .set_termios = serial_omap_set_termios, + .pm = serial_omap_pm, + .type = serial_omap_type, + .release_port = serial_omap_release_port, + .request_port = serial_omap_request_port, + .config_port = serial_omap_config_port, + .verify_port = serial_omap_verify_port, +}; + +static struct uart_driver serial_omap_reg = { + .owner = THIS_MODULE, + .driver_name = "OMAP-SERIAL", + .dev_name = OMAP_SERIAL_NAME, + .nr = OMAP_MAX_HSUART_PORTS, + .cons = OMAP_CONSOLE, +}; + +static int +serial_omap_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct uart_omap_port *up = platform_get_drvdata(pdev); + + if (up) + uart_suspend_port(&serial_omap_reg, &up->port); + return 0; +} + +static int serial_omap_resume(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + if (up) + uart_resume_port(&serial_omap_reg, &up->port); + return 0; +} + +static void serial_omap_rx_timeout(unsigned long uart_no) +{ + struct uart_omap_port *up = ui[uart_no]; + unsigned int curr_dma_pos, curr_transmitted_size; + int ret = 0; + + curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel); + if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) || + (curr_dma_pos == 0)) { + if (jiffies_to_msecs(jiffies - up->port_activity) < + RX_TIMEOUT) { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } else { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + return; + } + + curr_transmitted_size = curr_dma_pos - + up->uart_dma.prev_rx_dma_pos; + up->port.icount.rx += curr_transmitted_size; + tty_insert_flip_string(up->port.state->port.tty, + up->uart_dma.rx_buf + + (up->uart_dma.prev_rx_dma_pos - + up->uart_dma.rx_buf_dma_phys), + curr_transmitted_size); + tty_flip_buffer_push(up->port.state->port.tty); + up->uart_dma.prev_rx_dma_pos = curr_dma_pos; + if (up->uart_dma.rx_buf_size + + up->uart_dma.rx_buf_dma_phys == curr_dma_pos) { + ret = serial_omap_start_rxdma(up); + if (ret < 0) { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + } else { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } + up->port_activity = jiffies; +} + +static void uart_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + return; +} + +static int serial_omap_start_rxdma(struct uart_omap_port *up) +{ + int ret = 0; + + if (up->uart_dma.rx_dma_channel == -1) { + ret = omap_request_dma(up->uart_dma.uart_dma_rx, + "UART Rx DMA", + (void *)uart_rx_dma_callback, up, + &(up->uart_dma.rx_dma_channel)); + if (ret < 0) + return ret; + + omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, + up->uart_dma.rx_buf_dma_phys, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.rx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_rx, 0); + } + up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys; + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.rx_dma_channel); + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + up->uart_dma.rx_dma_used = true; + return ret; +} + +static void serial_omap_continue_tx(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + unsigned int start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + if (uart_circ_empty(xmit)) + return; + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start; + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct uart_omap_port *up = (struct uart_omap_port *)data; + struct circ_buf *xmit = &up->port.state->xmit; + + xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \ + (UART_XMIT_SIZE - 1); + up->port.icount.tx += up->uart_dma.tx_buf_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) { + spin_lock(&(up->uart_dma.tx_lock)); + serial_omap_stop_tx(&up->port); + up->uart_dma.tx_dma_used = false; + spin_unlock(&(up->uart_dma.tx_lock)); + } else { + omap_stop_dma(up->uart_dma.tx_dma_channel); + serial_omap_continue_tx(up); + } + up->port_activity = jiffies; + return; +} + +static int serial_omap_probe(struct platform_device *pdev) +{ + struct uart_omap_port *up; + struct resource *mem, *irq, *dma_tx, *dma_rx; + struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + int ret = -ENOSPC; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->dev.driver->name)) { + dev_err(&pdev->dev, "memory region already claimed\n"); + return -EBUSY; + } + + dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!dma_rx) { + ret = -EINVAL; + goto err; + } + + dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!dma_tx) { + ret = -EINVAL; + goto err; + } + + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (up == NULL) { + ret = -ENOMEM; + goto do_release_region; + } + sprintf(up->name, "OMAP UART%d", pdev->id); + up->pdev = pdev; + up->port.dev = &pdev->dev; + up->port.type = PORT_OMAP; + up->port.iotype = UPIO_MEM; + up->port.irq = irq->start; + + up->port.regshift = 2; + up->port.fifosize = 64; + up->port.ops = &serial_omap_pops; + up->port.line = pdev->id; + + up->port.membase = omap_up_info->membase; + up->port.mapbase = omap_up_info->mapbase; + up->port.flags = omap_up_info->flags; + up->port.irqflags = omap_up_info->irqflags; + up->port.uartclk = omap_up_info->uartclk; + up->uart_dma.uart_base = mem->start; + + if (omap_up_info->dma_enabled) { + up->uart_dma.uart_dma_tx = dma_tx->start; + up->uart_dma.uart_dma_rx = dma_rx->start; + up->use_dma = 1; + up->uart_dma.rx_buf_size = 4096; + up->uart_dma.rx_timeout = 2; + spin_lock_init(&(up->uart_dma.tx_lock)); + spin_lock_init(&(up->uart_dma.rx_lock)); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + ui[pdev->id] = up; + serial_omap_add_console_port(up); + + ret = uart_add_one_port(&serial_omap_reg, &up->port); + if (ret != 0) + goto do_release_region; + + platform_set_drvdata(pdev, up); + return 0; +err: + dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", + pdev->id, __func__, ret); +do_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return ret; +} + +static int serial_omap_remove(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + if (up) { + uart_remove_one_port(&serial_omap_reg, &up->port); + kfree(up); + } + return 0; +} + +static struct platform_driver serial_omap_driver = { + .probe = serial_omap_probe, + .remove = serial_omap_remove, + + .suspend = serial_omap_suspend, + .resume = serial_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init serial_omap_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_omap_reg); + if (ret != 0) + return ret; + ret = platform_driver_register(&serial_omap_driver); + if (ret != 0) + uart_unregister_driver(&serial_omap_reg); + return ret; +} + +static void __exit serial_omap_exit(void) +{ + platform_driver_unregister(&serial_omap_driver); + uart_unregister_driver(&serial_omap_reg); +} + +module_init(serial_omap_init); +module_exit(serial_omap_exit); + +MODULE_DESCRIPTION("OMAP High Speed UART driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 9b52f77a930..d2352ac437c 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -140,7 +140,15 @@ # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ # define SCSPTR1 0xffe10024 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + +#if defined(CONFIG_SH_SH2007) +/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ +# define SCSCR_INIT(port) 0x38 +#else +/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ +# define SCSCR_INIT(port) 0x3a +#endif + #elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) # define SCSPTR0 0xffea0024 /* 16 bit SCIF */ @@ -616,9 +624,10 @@ static inline int sci_rxd_in(struct uart_port *port) * -- Mitch Davis - 15 Jul 2000 */ -#if defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) +#if (defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786)) && \ + !defined(CONFIG_SH_SH2007) #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index a54de0b9b3d..f168a615996 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -1,24 +1,5 @@ -config INTC_USERIMASK - bool "Userspace interrupt masking support" - depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) - help - This enables support for hardware-assisted userspace hardirq - masking. +menu "SuperH / SH-Mobile Driver Options" - SH-4A and newer interrupt blocks all support a special shadowed - page with all non-masking registers obscured when mapped in to - userspace. This is primarily for use by userspace device - drivers that are using special priority levels. +source "drivers/sh/intc/Kconfig" - If in doubt, say N. - -config INTC_BALANCING - bool "Hardware IRQ balancing support" - depends on SMP && SUPERH && CPU_SUBTYPE_SH7786 - help - This enables support for IRQ auto-distribution mode on SH-X3 - SMP parts. All of the balancing and CPU wakeup decisions are - taken care of automatically by hardware for distributed - vectors. - - If in doubt, say N. +endmenu diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 08fc653a825..24e6cec0ae8 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -1,10 +1,9 @@ # # Makefile for the SuperH specific drivers. # -obj-y := clk.o intc.o +obj-y := intc/ -obj-$(CONFIG_SUPERHYWAY) += superhyway/ +obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ - +obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_GENERIC_GPIO) += pfc.o -obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o diff --git a/drivers/sh/clk/Makefile b/drivers/sh/clk/Makefile new file mode 100644 index 00000000000..5d15ebfaa07 --- /dev/null +++ b/drivers/sh/clk/Makefile @@ -0,0 +1,3 @@ +obj-y := core.o + +obj-$(CONFIG_SH_CLK_CPG) += cpg.o diff --git a/drivers/sh/clk.c b/drivers/sh/clk/core.c index 5d84adac9ec..fd0d1b98901 100644 --- a/drivers/sh/clk.c +++ b/drivers/sh/clk/core.c @@ -1,7 +1,7 @@ /* - * drivers/sh/clk.c - SuperH clock framework + * SuperH clock framework * - * Copyright (C) 2005 - 2009 Paul Mundt + * Copyright (C) 2005 - 2010 Paul Mundt * * This clock framework is derived from the OMAP version by: * @@ -14,6 +14,8 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ +#define pr_fmt(fmt) "clock: " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> @@ -23,7 +25,7 @@ #include <linux/sysdev.h> #include <linux/seq_file.h> #include <linux/err.h> -#include <linux/platform_device.h> +#include <linux/io.h> #include <linux/debugfs.h> #include <linux/cpufreq.h> #include <linux/clk.h> @@ -43,6 +45,8 @@ void clk_rate_table_build(struct clk *clk, unsigned long freq; int i; + clk->nr_freqs = nr_freqs; + for (i = 0; i < nr_freqs; i++) { div = 1; mult = 1; @@ -67,29 +71,39 @@ void clk_rate_table_build(struct clk *clk, freq_table[i].frequency = CPUFREQ_TABLE_END; } -long clk_rate_table_round(struct clk *clk, - struct cpufreq_frequency_table *freq_table, - unsigned long rate) +struct clk_rate_round_data; + +struct clk_rate_round_data { + unsigned long rate; + unsigned int min, max; + long (*func)(unsigned int, struct clk_rate_round_data *); + void *arg; +}; + +#define for_each_frequency(pos, r, freq) \ + for (pos = r->min, freq = r->func(pos, r); \ + pos <= r->max; pos++, freq = r->func(pos, r)) \ + if (unlikely(freq == 0)) \ + ; \ + else + +static long clk_rate_round_helper(struct clk_rate_round_data *rounder) { unsigned long rate_error, rate_error_prev = ~0UL; - unsigned long rate_best_fit = rate; - unsigned long highest, lowest; + unsigned long rate_best_fit = rounder->rate; + unsigned long highest, lowest, freq; int i; - highest = lowest = 0; - - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned long freq = freq_table[i].frequency; - - if (freq == CPUFREQ_ENTRY_INVALID) - continue; + highest = 0; + lowest = ~0UL; + for_each_frequency(i, rounder, freq) { if (freq > highest) highest = freq; if (freq < lowest) lowest = freq; - rate_error = abs(freq - rate); + rate_error = abs(freq - rounder->rate); if (rate_error < rate_error_prev) { rate_best_fit = freq; rate_error_prev = rate_error; @@ -99,14 +113,64 @@ long clk_rate_table_round(struct clk *clk, break; } - if (rate >= highest) + if (rounder->rate >= highest) rate_best_fit = highest; - if (rate <= lowest) + if (rounder->rate <= lowest) rate_best_fit = lowest; return rate_best_fit; } +static long clk_rate_table_iter(unsigned int pos, + struct clk_rate_round_data *rounder) +{ + struct cpufreq_frequency_table *freq_table = rounder->arg; + unsigned long freq = freq_table[pos].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + freq = 0; + + return freq; +} + +long clk_rate_table_round(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + unsigned long rate) +{ + struct clk_rate_round_data table_round = { + .min = 0, + .max = clk->nr_freqs - 1, + .func = clk_rate_table_iter, + .arg = freq_table, + .rate = rate, + }; + + if (clk->nr_freqs < 1) + return 0; + + return clk_rate_round_helper(&table_round); +} + +static long clk_rate_div_range_iter(unsigned int pos, + struct clk_rate_round_data *rounder) +{ + return clk_get_rate(rounder->arg) / pos; +} + +long clk_rate_div_range_round(struct clk *clk, unsigned int div_min, + unsigned int div_max, unsigned long rate) +{ + struct clk_rate_round_data div_range_round = { + .min = div_min, + .max = div_max, + .func = clk_rate_div_range_iter, + .arg = clk_get_parent(clk), + .rate = rate, + }; + + return clk_rate_round_helper(&div_range_round); +} + int clk_rate_table_find(struct clk *clk, struct cpufreq_frequency_table *freq_table, unsigned long rate) @@ -160,8 +224,8 @@ void propagate_rate(struct clk *tclk) static void __clk_disable(struct clk *clk) { - if (WARN(!clk->usecount, "Trying to disable clock %s with 0 usecount\n", - clk->name)) + if (WARN(!clk->usecount, "Trying to disable clock %p with 0 usecount\n", + clk)) return; if (!(--clk->usecount)) { @@ -248,8 +312,88 @@ void recalculate_root_clocks(void) } } +static struct clk_mapping dummy_mapping; + +static struct clk *lookup_root_clock(struct clk *clk) +{ + while (clk->parent) + clk = clk->parent; + + return clk; +} + +static int clk_establish_mapping(struct clk *clk) +{ + struct clk_mapping *mapping = clk->mapping; + + /* + * Propagate mappings. + */ + if (!mapping) { + struct clk *clkp; + + /* + * dummy mapping for root clocks with no specified ranges + */ + if (!clk->parent) { + clk->mapping = &dummy_mapping; + return 0; + } + + /* + * If we're on a child clock and it provides no mapping of its + * own, inherit the mapping from its root clock. + */ + clkp = lookup_root_clock(clk); + mapping = clkp->mapping; + BUG_ON(!mapping); + } + + /* + * Establish initial mapping. + */ + if (!mapping->base && mapping->phys) { + kref_init(&mapping->ref); + + mapping->base = ioremap_nocache(mapping->phys, mapping->len); + if (unlikely(!mapping->base)) + return -ENXIO; + } else if (mapping->base) { + /* + * Bump the refcount for an existing mapping + */ + kref_get(&mapping->ref); + } + + clk->mapping = mapping; + return 0; +} + +static void clk_destroy_mapping(struct kref *kref) +{ + struct clk_mapping *mapping; + + mapping = container_of(kref, struct clk_mapping, ref); + + iounmap(mapping->base); +} + +static void clk_teardown_mapping(struct clk *clk) +{ + struct clk_mapping *mapping = clk->mapping; + + /* Nothing to do */ + if (mapping == &dummy_mapping) + return; + + kref_put(&mapping->ref, clk_destroy_mapping); + clk->mapping = NULL; +} + int clk_register(struct clk *clk) { + int ret; + if (clk == NULL || IS_ERR(clk)) return -EINVAL; @@ -264,6 +408,10 @@ int clk_register(struct clk *clk) INIT_LIST_HEAD(&clk->children); clk->usecount = 0; + ret = clk_establish_mapping(clk); + if (unlikely(ret)) + goto out_unlock; + if (clk->parent) list_add(&clk->sibling, &clk->parent->children); else @@ -272,9 +420,11 @@ int clk_register(struct clk *clk) list_add(&clk->node, &clock_list); if (clk->ops && clk->ops->init) clk->ops->init(clk); + +out_unlock: mutex_unlock(&clock_list_sem); - return 0; + return ret; } EXPORT_SYMBOL_GPL(clk_register); @@ -283,6 +433,7 @@ void clk_unregister(struct clk *clk) mutex_lock(&clock_list_sem); list_del(&clk->sibling); list_del(&clk->node); + clk_teardown_mapping(clk); mutex_unlock(&clock_list_sem); } EXPORT_SYMBOL_GPL(clk_unregister); @@ -354,10 +505,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) ret = clk_reparent(clk, parent); if (ret == 0) { - pr_debug("clock: set parent of %s to %s (new rate %ld)\n", - clk->name, clk->parent->name, clk->rate); if (clk->ops->recalc) clk->rate = clk->ops->recalc(clk); + pr_debug("set parent of %p to %p (new rate %ld)\n", + clk, clk->parent, clk->rate); propagate_rate(clk); } } else @@ -469,9 +620,7 @@ static int clk_debugfs_register_one(struct clk *c) char s[255]; char *p = s; - p += sprintf(p, "%s", c->name); - if (c->id >= 0) - sprintf(p, ":%d", c->id); + p += sprintf(p, "%p", c); d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root); if (!d) return -ENOMEM; @@ -513,7 +662,7 @@ static int clk_debugfs_register(struct clk *c) return err; } - if (!c->dentry && c->name) { + if (!c->dentry) { err = clk_debugfs_register_one(c); if (err) return err; diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk/cpg.c index 8c024b984ed..3aea5f0ceb0 100644 --- a/drivers/sh/clk-cpg.c +++ b/drivers/sh/clk/cpg.c @@ -1,3 +1,12 @@ +/* + * Helper routines for SuperH Clock Pulse Generator blocks (CPG). + * + * Copyright (C) 2010 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ #include <linux/clk.h> #include <linux/compiler.h> #include <linux/slab.h> @@ -180,7 +189,6 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; - clkp->id = -1; clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; @@ -319,7 +327,6 @@ static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; - clkp->id = -1; clkp->priv = table; clkp->freq_table = freq_table + (k * freq_table_size); diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c deleted file mode 100644 index e91a23e5ffd..00000000000 --- a/drivers/sh/intc.c +++ /dev/null @@ -1,1390 +0,0 @@ -/* - * Shared interrupt handling code for IPR and INTC2 types of IRQs. - * - * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt - * - * Based on intc2.c and ipr.c - * - * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi - * Copyright (C) 2000 Kazumoto Kojima - * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) - * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> - * Copyright (C) 2005, 2006 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/init.h> -#include <linux/irq.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/sh_intc.h> -#include <linux/sysdev.h> -#include <linux/list.h> -#include <linux/topology.h> -#include <linux/bitmap.h> -#include <linux/cpumask.h> -#include <asm/sizes.h> - -#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ - ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ - ((addr_e) << 16) | ((addr_d << 24))) - -#define _INTC_SHIFT(h) (h & 0x1f) -#define _INTC_WIDTH(h) ((h >> 5) & 0xf) -#define _INTC_FN(h) ((h >> 9) & 0xf) -#define _INTC_MODE(h) ((h >> 13) & 0x7) -#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) -#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) - -struct intc_handle_int { - unsigned int irq; - unsigned long handle; -}; - -struct intc_window { - phys_addr_t phys; - void __iomem *virt; - unsigned long size; -}; - -struct intc_desc_int { - struct list_head list; - struct sys_device sysdev; - pm_message_t state; - unsigned long *reg; -#ifdef CONFIG_SMP - unsigned long *smp; -#endif - unsigned int nr_reg; - struct intc_handle_int *prio; - unsigned int nr_prio; - struct intc_handle_int *sense; - unsigned int nr_sense; - struct intc_window *window; - unsigned int nr_windows; - struct irq_chip chip; -}; - -static LIST_HEAD(intc_list); - -/* - * The intc_irq_map provides a global map of bound IRQ vectors for a - * given platform. Allocation of IRQs are either static through the CPU - * vector map, or dynamic in the case of board mux vectors or MSI. - * - * As this is a central point for all IRQ controllers on the system, - * each of the available sources are mapped out here. This combined with - * sparseirq makes it quite trivial to keep the vector map tightly packed - * when dynamically creating IRQs, as well as tying in to otherwise - * unused irq_desc positions in the sparse array. - */ -static DECLARE_BITMAP(intc_irq_map, NR_IRQS); -static DEFINE_SPINLOCK(vector_lock); - -#ifdef CONFIG_SMP -#define IS_SMP(x) x.smp -#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) -#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) -#else -#define IS_SMP(x) 0 -#define INTC_REG(d, x, c) (d->reg[(x)]) -#define SMP_NR(d, x) 1 -#endif - -static unsigned int intc_prio_level[NR_IRQS]; /* for now */ -static unsigned int default_prio_level = 2; /* 2 - 16 */ -static unsigned long ack_handle[NR_IRQS]; -#ifdef CONFIG_INTC_BALANCING -static unsigned long dist_handle[NR_IRQS]; -#endif - -static inline struct intc_desc_int *get_intc_desc(unsigned int irq) -{ - struct irq_chip *chip = get_irq_chip(irq); - return container_of(chip, struct intc_desc_int, chip); -} - -static unsigned long intc_phys_to_virt(struct intc_desc_int *d, - unsigned long address) -{ - struct intc_window *window; - int k; - - /* scan through physical windows and convert address */ - for (k = 0; k < d->nr_windows; k++) { - window = d->window + k; - - if (address < window->phys) - continue; - - if (address >= (window->phys + window->size)) - continue; - - address -= window->phys; - address += (unsigned long)window->virt; - - return address; - } - - /* no windows defined, register must be 1:1 mapped virt:phys */ - return address; -} - -static unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) -{ - unsigned int k; - - address = intc_phys_to_virt(d, address); - - for (k = 0; k < d->nr_reg; k++) { - if (d->reg[k] == address) - return k; - } - - BUG(); - return 0; -} - -static inline unsigned int set_field(unsigned int value, - unsigned int field_value, - unsigned int handle) -{ - unsigned int width = _INTC_WIDTH(handle); - unsigned int shift = _INTC_SHIFT(handle); - - value &= ~(((1 << width) - 1) << shift); - value |= field_value << shift; - return value; -} - -static void write_8(unsigned long addr, unsigned long h, unsigned long data) -{ - __raw_writeb(set_field(0, data, h), addr); - (void)__raw_readb(addr); /* Defeat write posting */ -} - -static void write_16(unsigned long addr, unsigned long h, unsigned long data) -{ - __raw_writew(set_field(0, data, h), addr); - (void)__raw_readw(addr); /* Defeat write posting */ -} - -static void write_32(unsigned long addr, unsigned long h, unsigned long data) -{ - __raw_writel(set_field(0, data, h), addr); - (void)__raw_readl(addr); /* Defeat write posting */ -} - -static void modify_8(unsigned long addr, unsigned long h, unsigned long data) -{ - unsigned long flags; - local_irq_save(flags); - __raw_writeb(set_field(__raw_readb(addr), data, h), addr); - (void)__raw_readb(addr); /* Defeat write posting */ - local_irq_restore(flags); -} - -static void modify_16(unsigned long addr, unsigned long h, unsigned long data) -{ - unsigned long flags; - local_irq_save(flags); - __raw_writew(set_field(__raw_readw(addr), data, h), addr); - (void)__raw_readw(addr); /* Defeat write posting */ - local_irq_restore(flags); -} - -static void modify_32(unsigned long addr, unsigned long h, unsigned long data) -{ - unsigned long flags; - local_irq_save(flags); - __raw_writel(set_field(__raw_readl(addr), data, h), addr); - (void)__raw_readl(addr); /* Defeat write posting */ - local_irq_restore(flags); -} - -enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; - -static void (*intc_reg_fns[])(unsigned long addr, - unsigned long h, - unsigned long data) = { - [REG_FN_WRITE_BASE + 0] = write_8, - [REG_FN_WRITE_BASE + 1] = write_16, - [REG_FN_WRITE_BASE + 3] = write_32, - [REG_FN_MODIFY_BASE + 0] = modify_8, - [REG_FN_MODIFY_BASE + 1] = modify_16, - [REG_FN_MODIFY_BASE + 3] = modify_32, -}; - -enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ - MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ - MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ - MODE_PRIO_REG, /* Priority value written to enable interrupt */ - MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ -}; - -static void intc_mode_field(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); -} - -static void intc_mode_zero(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - fn(addr, handle, 0); -} - -static void intc_mode_prio(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - fn(addr, handle, intc_prio_level[irq]); -} - -static void (*intc_enable_fns[])(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_field, - [MODE_MASK_REG] = intc_mode_zero, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_prio, - [MODE_PCLR_REG] = intc_mode_prio, -}; - -static void (*intc_disable_fns[])(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_zero, - [MODE_MASK_REG] = intc_mode_field, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_zero, - [MODE_PCLR_REG] = intc_mode_field, -}; - -#ifdef CONFIG_INTC_BALANCING -static inline void intc_balancing_enable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = dist_handle[irq]; - unsigned long addr; - - if (irq_balancing_disabled(irq) || !handle) - return; - - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); -} - -static inline void intc_balancing_disable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = dist_handle[irq]; - unsigned long addr; - - if (irq_balancing_disabled(irq) || !handle) - return; - - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); -} - -static unsigned int intc_dist_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_mask_reg *mr = desc->hw.mask_regs; - unsigned int i, j, fn, mode; - unsigned long reg_e, reg_d; - - for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { - mr = desc->hw.mask_regs + i; - - /* - * Skip this entry if there's no auto-distribution - * register associated with it. - */ - if (!mr->dist_reg) - continue; - - for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { - if (mr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - mode = MODE_ENABLE_REG; - reg_e = mr->dist_reg; - reg_d = mr->dist_reg; - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - j); - } - } - - /* - * It's possible we've gotten here with no distribution options - * available for the IRQ in question, so we just skip over those. - */ - return 0; -} -#else -static inline void intc_balancing_enable(unsigned int irq) -{ -} - -static inline void intc_balancing_disable(unsigned int irq) -{ -} -#endif - -static inline void _intc_enable(unsigned int irq, unsigned long handle) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long addr; - unsigned int cpu; - - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { -#ifdef CONFIG_SMP - if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) - continue; -#endif - addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); - intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ - [_INTC_FN(handle)], irq); - } - - intc_balancing_enable(irq); -} - -static void intc_enable(unsigned int irq) -{ - _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); -} - -static void intc_disable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = (unsigned long)get_irq_chip_data(irq); - unsigned long addr; - unsigned int cpu; - - intc_balancing_disable(irq); - - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { -#ifdef CONFIG_SMP - if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) - continue; -#endif - addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); - intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ - [_INTC_FN(handle)], irq); - } -} - -static void (*intc_enable_noprio_fns[])(unsigned long addr, - unsigned long handle, - void (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_field, - [MODE_MASK_REG] = intc_mode_zero, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_field, - [MODE_PCLR_REG] = intc_mode_field, -}; - -static void intc_enable_disable(struct intc_desc_int *d, - unsigned long handle, int do_enable) -{ - unsigned long addr; - unsigned int cpu; - void (*fn)(unsigned long, unsigned long, - void (*)(unsigned long, unsigned long, unsigned long), - unsigned int); - - if (do_enable) { - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { - addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); - fn = intc_enable_noprio_fns[_INTC_MODE(handle)]; - fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); - } - } else { - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { - addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); - fn = intc_disable_fns[_INTC_MODE(handle)]; - fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); - } - } -} - -static int intc_set_wake(unsigned int irq, unsigned int on) -{ - return 0; /* allow wakeup, but setup hardware in intc_suspend() */ -} - -#ifdef CONFIG_SMP -/* - * This is held with the irq desc lock held, so we don't require any - * additional locking here at the intc desc level. The affinity mask is - * later tested in the enable/disable paths. - */ -static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask) -{ - if (!cpumask_intersects(cpumask, cpu_online_mask)) - return -1; - - cpumask_copy(irq_to_desc(irq)->affinity, cpumask); - - return 0; -} -#endif - -static void intc_mask_ack(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = ack_handle[irq]; - unsigned long addr; - - intc_disable(irq); - - /* read register and write zero only to the associated bit */ - if (handle) { - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - switch (_INTC_FN(handle)) { - case REG_FN_MODIFY_BASE + 0: /* 8bit */ - __raw_readb(addr); - __raw_writeb(0xff ^ set_field(0, 1, handle), addr); - break; - case REG_FN_MODIFY_BASE + 1: /* 16bit */ - __raw_readw(addr); - __raw_writew(0xffff ^ set_field(0, 1, handle), addr); - break; - case REG_FN_MODIFY_BASE + 3: /* 32bit */ - __raw_readl(addr); - __raw_writel(0xffffffff ^ set_field(0, 1, handle), addr); - break; - default: - BUG(); - break; - } - } -} - -static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, - unsigned int nr_hp, - unsigned int irq) -{ - int i; - - /* - * this doesn't scale well, but... - * - * this function should only be used for cerain uncommon - * operations such as intc_set_priority() and intc_set_sense() - * and in those rare cases performance doesn't matter that much. - * keeping the memory footprint low is more important. - * - * one rather simple way to speed this up and still keep the - * memory footprint down is to make sure the array is sorted - * and then perform a bisect to lookup the irq. - */ - for (i = 0; i < nr_hp; i++) { - if ((hp + i)->irq != irq) - continue; - - return hp + i; - } - - return NULL; -} - -int intc_set_priority(unsigned int irq, unsigned int prio) -{ - struct intc_desc_int *d = get_intc_desc(irq); - struct intc_handle_int *ihp; - - if (!intc_prio_level[irq] || prio <= 1) - return -EINVAL; - - ihp = intc_find_irq(d->prio, d->nr_prio, irq); - if (ihp) { - if (prio >= (1 << _INTC_WIDTH(ihp->handle))) - return -EINVAL; - - intc_prio_level[irq] = prio; - - /* - * only set secondary masking method directly - * primary masking method is using intc_prio_level[irq] - * priority level will be set during next enable() - */ - if (_INTC_FN(ihp->handle) != REG_FN_ERR) - _intc_enable(irq, ihp->handle); - } - return 0; -} - -#define VALID(x) (x | 0x80) - -static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { - [IRQ_TYPE_EDGE_FALLING] = VALID(0), - [IRQ_TYPE_EDGE_RISING] = VALID(1), - [IRQ_TYPE_LEVEL_LOW] = VALID(2), - /* SH7706, SH7707 and SH7709 do not support high level triggered */ -#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ - !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ - !defined(CONFIG_CPU_SUBTYPE_SH7709) - [IRQ_TYPE_LEVEL_HIGH] = VALID(3), -#endif -}; - -static int intc_set_sense(unsigned int irq, unsigned int type) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; - struct intc_handle_int *ihp; - unsigned long addr; - - if (!value) - return -EINVAL; - - ihp = intc_find_irq(d->sense, d->nr_sense, irq); - if (ihp) { - addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); - intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); - } - return 0; -} - -static intc_enum __init intc_grp_id(struct intc_desc *desc, - intc_enum enum_id) -{ - struct intc_group *g = desc->hw.groups; - unsigned int i, j; - - for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) { - g = desc->hw.groups + i; - - for (j = 0; g->enum_ids[j]; j++) { - if (g->enum_ids[j] != enum_id) - continue; - - return g->enum_id; - } - } - - return 0; -} - -static unsigned int __init _intc_mask_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int *reg_idx, - unsigned int *fld_idx) -{ - struct intc_mask_reg *mr = desc->hw.mask_regs; - unsigned int fn, mode; - unsigned long reg_e, reg_d; - - while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) { - mr = desc->hw.mask_regs + *reg_idx; - - for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) { - if (mr->enum_ids[*fld_idx] != enum_id) - continue; - - if (mr->set_reg && mr->clr_reg) { - fn = REG_FN_WRITE_BASE; - mode = MODE_DUAL_REG; - reg_e = mr->clr_reg; - reg_d = mr->set_reg; - } else { - fn = REG_FN_MODIFY_BASE; - if (mr->set_reg) { - mode = MODE_ENABLE_REG; - reg_e = mr->set_reg; - reg_d = mr->set_reg; - } else { - mode = MODE_MASK_REG; - reg_e = mr->clr_reg; - reg_d = mr->clr_reg; - } - } - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - *fld_idx); - } - - *fld_idx = 0; - (*reg_idx)++; - } - - return 0; -} - -static unsigned int __init intc_mask_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int do_grps) -{ - unsigned int i = 0; - unsigned int j = 0; - unsigned int ret; - - ret = _intc_mask_data(desc, d, enum_id, &i, &j); - if (ret) - return ret; - - if (do_grps) - return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0); - - return 0; -} - -static unsigned int __init _intc_prio_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int *reg_idx, - unsigned int *fld_idx) -{ - struct intc_prio_reg *pr = desc->hw.prio_regs; - unsigned int fn, n, mode, bit; - unsigned long reg_e, reg_d; - - while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) { - pr = desc->hw.prio_regs + *reg_idx; - - for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) { - if (pr->enum_ids[*fld_idx] != enum_id) - continue; - - if (pr->set_reg && pr->clr_reg) { - fn = REG_FN_WRITE_BASE; - mode = MODE_PCLR_REG; - reg_e = pr->set_reg; - reg_d = pr->clr_reg; - } else { - fn = REG_FN_MODIFY_BASE; - mode = MODE_PRIO_REG; - if (!pr->set_reg) - BUG(); - reg_e = pr->set_reg; - reg_d = pr->set_reg; - } - - fn += (pr->reg_width >> 3) - 1; - n = *fld_idx + 1; - - BUG_ON(n * pr->field_width > pr->reg_width); - - bit = pr->reg_width - (n * pr->field_width); - - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - pr->field_width, bit); - } - - *fld_idx = 0; - (*reg_idx)++; - } - - return 0; -} - -static unsigned int __init intc_prio_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int do_grps) -{ - unsigned int i = 0; - unsigned int j = 0; - unsigned int ret; - - ret = _intc_prio_data(desc, d, enum_id, &i, &j); - if (ret) - return ret; - - if (do_grps) - return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0); - - return 0; -} - -static void __init intc_enable_disable_enum(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int enable) -{ - unsigned int i, j, data; - - /* go through and enable/disable all mask bits */ - i = j = 0; - do { - data = _intc_mask_data(desc, d, enum_id, &i, &j); - if (data) - intc_enable_disable(d, data, enable); - j++; - } while (data); - - /* go through and enable/disable all priority fields */ - i = j = 0; - do { - data = _intc_prio_data(desc, d, enum_id, &i, &j); - if (data) - intc_enable_disable(d, data, enable); - - j++; - } while (data); -} - -static unsigned int __init intc_ack_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_mask_reg *mr = desc->hw.ack_regs; - unsigned int i, j, fn, mode; - unsigned long reg_e, reg_d; - - for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) { - mr = desc->hw.ack_regs + i; - - for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { - if (mr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - mode = MODE_ENABLE_REG; - reg_e = mr->set_reg; - reg_d = mr->set_reg; - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - j); - } - } - - return 0; -} - -static unsigned int __init intc_sense_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_sense_reg *sr = desc->hw.sense_regs; - unsigned int i, j, fn, bit; - - for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) { - sr = desc->hw.sense_regs + i; - - for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { - if (sr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - fn += (sr->reg_width >> 3) - 1; - - BUG_ON((j + 1) * sr->field_width > sr->reg_width); - - bit = sr->reg_width - ((j + 1) * sr->field_width); - - return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), - 0, sr->field_width, bit); - } - } - - return 0; -} - -static void __init intc_register_irq(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int irq) -{ - struct intc_handle_int *hp; - unsigned int data[2], primary; - - /* - * Register the IRQ position with the global IRQ map - */ - set_bit(irq, intc_irq_map); - - /* - * Prefer single interrupt source bitmap over other combinations: - * - * 1. bitmap, single interrupt source - * 2. priority, single interrupt source - * 3. bitmap, multiple interrupt sources (groups) - * 4. priority, multiple interrupt sources (groups) - */ - data[0] = intc_mask_data(desc, d, enum_id, 0); - data[1] = intc_prio_data(desc, d, enum_id, 0); - - primary = 0; - if (!data[0] && data[1]) - primary = 1; - - if (!data[0] && !data[1]) - pr_warning("missing unique irq mask for irq %d (vect 0x%04x)\n", - irq, irq2evt(irq)); - - data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1); - data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1); - - if (!data[primary]) - primary ^= 1; - - BUG_ON(!data[primary]); /* must have primary masking method */ - - disable_irq_nosync(irq); - set_irq_chip_and_handler_name(irq, &d->chip, - handle_level_irq, "level"); - set_irq_chip_data(irq, (void *)data[primary]); - - /* - * set priority level - * - this needs to be at least 2 for 5-bit priorities on 7780 - */ - intc_prio_level[irq] = default_prio_level; - - /* enable secondary masking method if present */ - if (data[!primary]) - _intc_enable(irq, data[!primary]); - - /* add irq to d->prio list if priority is available */ - if (data[1]) { - hp = d->prio + d->nr_prio; - hp->irq = irq; - hp->handle = data[1]; - - if (primary) { - /* - * only secondary priority should access registers, so - * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() - */ - hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); - hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); - } - d->nr_prio++; - } - - /* add irq to d->sense list if sense is available */ - data[0] = intc_sense_data(desc, d, enum_id); - if (data[0]) { - (d->sense + d->nr_sense)->irq = irq; - (d->sense + d->nr_sense)->handle = data[0]; - d->nr_sense++; - } - - /* irq should be disabled by default */ - d->chip.mask(irq); - - if (desc->hw.ack_regs) - ack_handle[irq] = intc_ack_data(desc, d, enum_id); - -#ifdef CONFIG_INTC_BALANCING - if (desc->hw.mask_regs) - dist_handle[irq] = intc_dist_data(desc, d, enum_id); -#endif - -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ -#endif -} - -static unsigned int __init save_reg(struct intc_desc_int *d, - unsigned int cnt, - unsigned long value, - unsigned int smp) -{ - if (value) { - value = intc_phys_to_virt(d, value); - - d->reg[cnt] = value; -#ifdef CONFIG_SMP - d->smp[cnt] = smp; -#endif - return 1; - } - - return 0; -} - -static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) -{ - generic_handle_irq((unsigned int)get_irq_data(irq)); -} - -int __init register_intc_controller(struct intc_desc *desc) -{ - unsigned int i, k, smp; - struct intc_hw_desc *hw = &desc->hw; - struct intc_desc_int *d; - struct resource *res; - - pr_info("Registered controller '%s' with %u IRQs\n", - desc->name, hw->nr_vectors); - - d = kzalloc(sizeof(*d), GFP_NOWAIT); - if (!d) - goto err0; - - INIT_LIST_HEAD(&d->list); - list_add(&d->list, &intc_list); - - if (desc->num_resources) { - d->nr_windows = desc->num_resources; - d->window = kzalloc(d->nr_windows * sizeof(*d->window), - GFP_NOWAIT); - if (!d->window) - goto err1; - - for (k = 0; k < d->nr_windows; k++) { - res = desc->resource + k; - WARN_ON(resource_type(res) != IORESOURCE_MEM); - d->window[k].phys = res->start; - d->window[k].size = resource_size(res); - d->window[k].virt = ioremap_nocache(res->start, - resource_size(res)); - if (!d->window[k].virt) - goto err2; - } - } - - d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; -#ifdef CONFIG_INTC_BALANCING - if (d->nr_reg) - d->nr_reg += hw->nr_mask_regs; -#endif - d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; - d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; - d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; - - d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); - if (!d->reg) - goto err2; - -#ifdef CONFIG_SMP - d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); - if (!d->smp) - goto err3; -#endif - k = 0; - - if (hw->mask_regs) { - for (i = 0; i < hw->nr_mask_regs; i++) { - smp = IS_SMP(hw->mask_regs[i]); - k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); - k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); -#ifdef CONFIG_INTC_BALANCING - k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); -#endif - } - } - - if (hw->prio_regs) { - d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), - GFP_NOWAIT); - if (!d->prio) - goto err4; - - for (i = 0; i < hw->nr_prio_regs; i++) { - smp = IS_SMP(hw->prio_regs[i]); - k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); - k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); - } - } - - if (hw->sense_regs) { - d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), - GFP_NOWAIT); - if (!d->sense) - goto err5; - - for (i = 0; i < hw->nr_sense_regs; i++) - k += save_reg(d, k, hw->sense_regs[i].reg, 0); - } - - d->chip.name = desc->name; - d->chip.mask = intc_disable; - d->chip.unmask = intc_enable; - d->chip.mask_ack = intc_disable; - d->chip.enable = intc_enable; - d->chip.disable = intc_disable; - d->chip.shutdown = intc_disable; - d->chip.set_type = intc_set_sense; - d->chip.set_wake = intc_set_wake; -#ifdef CONFIG_SMP - d->chip.set_affinity = intc_set_affinity; -#endif - - if (hw->ack_regs) { - for (i = 0; i < hw->nr_ack_regs; i++) - k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); - - d->chip.mask_ack = intc_mask_ack; - } - - /* disable bits matching force_disable before registering irqs */ - if (desc->force_disable) - intc_enable_disable_enum(desc, d, desc->force_disable, 0); - - /* disable bits matching force_enable before registering irqs */ - if (desc->force_enable) - intc_enable_disable_enum(desc, d, desc->force_enable, 0); - - BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ - - /* register the vectors one by one */ - for (i = 0; i < hw->nr_vectors; i++) { - struct intc_vect *vect = hw->vectors + i; - unsigned int irq = evt2irq(vect->vect); - struct irq_desc *irq_desc; - - if (!vect->enum_id) - continue; - - irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); - if (unlikely(!irq_desc)) { - pr_err("can't get irq_desc for %d\n", irq); - continue; - } - - intc_register_irq(desc, d, vect->enum_id, irq); - - for (k = i + 1; k < hw->nr_vectors; k++) { - struct intc_vect *vect2 = hw->vectors + k; - unsigned int irq2 = evt2irq(vect2->vect); - - if (vect->enum_id != vect2->enum_id) - continue; - - /* - * In the case of multi-evt handling and sparse - * IRQ support, each vector still needs to have - * its own backing irq_desc. - */ - irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); - if (unlikely(!irq_desc)) { - pr_err("can't get irq_desc for %d\n", irq2); - continue; - } - - vect2->enum_id = 0; - - /* redirect this interrupts to the first one */ - set_irq_chip(irq2, &dummy_irq_chip); - set_irq_chained_handler(irq2, intc_redirect_irq); - set_irq_data(irq2, (void *)irq); - } - } - - /* enable bits matching force_enable after registering irqs */ - if (desc->force_enable) - intc_enable_disable_enum(desc, d, desc->force_enable, 1); - - return 0; -err5: - kfree(d->prio); -err4: -#ifdef CONFIG_SMP - kfree(d->smp); -err3: -#endif - kfree(d->reg); -err2: - for (k = 0; k < d->nr_windows; k++) - if (d->window[k].virt) - iounmap(d->window[k].virt); - - kfree(d->window); -err1: - kfree(d); -err0: - pr_err("unable to allocate INTC memory\n"); - - return -ENOMEM; -} - -#ifdef CONFIG_INTC_USERIMASK -static void __iomem *uimask; - -int register_intc_userimask(unsigned long addr) -{ - if (unlikely(uimask)) - return -EBUSY; - - uimask = ioremap_nocache(addr, SZ_4K); - if (unlikely(!uimask)) - return -ENOMEM; - - pr_info("userimask support registered for levels 0 -> %d\n", - default_prio_level - 1); - - return 0; -} - -static ssize_t -show_intc_userimask(struct sysdev_class *cls, - struct sysdev_class_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); -} - -static ssize_t -store_intc_userimask(struct sysdev_class *cls, - struct sysdev_class_attribute *attr, - const char *buf, size_t count) -{ - unsigned long level; - - level = simple_strtoul(buf, NULL, 10); - - /* - * Minimal acceptable IRQ levels are in the 2 - 16 range, but - * these are chomped so as to not interfere with normal IRQs. - * - * Level 1 is a special case on some CPUs in that it's not - * directly settable, but given that USERIMASK cuts off below a - * certain level, we don't care about this limitation here. - * Level 0 on the other hand equates to user masking disabled. - * - * We use default_prio_level as a cut off so that only special - * case opt-in IRQs can be mangled. - */ - if (level >= default_prio_level) - return -EINVAL; - - __raw_writel(0xa5 << 24 | level << 4, uimask); - - return count; -} - -static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, - show_intc_userimask, store_intc_userimask); -#endif - -static ssize_t -show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) -{ - struct intc_desc_int *d; - - d = container_of(dev, struct intc_desc_int, sysdev); - - return sprintf(buf, "%s\n", d->chip.name); -} - -static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); - -static int intc_suspend(struct sys_device *dev, pm_message_t state) -{ - struct intc_desc_int *d; - struct irq_desc *desc; - int irq; - - /* get intc controller associated with this sysdev */ - d = container_of(dev, struct intc_desc_int, sysdev); - - switch (state.event) { - case PM_EVENT_ON: - if (d->state.event != PM_EVENT_FREEZE) - break; - for_each_irq_desc(irq, desc) { - if (desc->handle_irq == intc_redirect_irq) - continue; - if (desc->chip != &d->chip) - continue; - if (desc->status & IRQ_DISABLED) - intc_disable(irq); - else - intc_enable(irq); - } - break; - case PM_EVENT_FREEZE: - /* nothing has to be done */ - break; - case PM_EVENT_SUSPEND: - /* enable wakeup irqs belonging to this intc controller */ - for_each_irq_desc(irq, desc) { - if ((desc->status & IRQ_WAKEUP) && (desc->chip == &d->chip)) - intc_enable(irq); - } - break; - } - d->state = state; - - return 0; -} - -static int intc_resume(struct sys_device *dev) -{ - return intc_suspend(dev, PMSG_ON); -} - -static struct sysdev_class intc_sysdev_class = { - .name = "intc", - .suspend = intc_suspend, - .resume = intc_resume, -}; - -/* register this intc as sysdev to allow suspend/resume */ -static int __init register_intc_sysdevs(void) -{ - struct intc_desc_int *d; - int error; - int id = 0; - - error = sysdev_class_register(&intc_sysdev_class); -#ifdef CONFIG_INTC_USERIMASK - if (!error && uimask) - error = sysdev_class_create_file(&intc_sysdev_class, - &attr_userimask); -#endif - if (!error) { - list_for_each_entry(d, &intc_list, list) { - d->sysdev.id = id; - d->sysdev.cls = &intc_sysdev_class; - error = sysdev_register(&d->sysdev); - if (error == 0) - error = sysdev_create_file(&d->sysdev, - &attr_name); - if (error) - break; - - id++; - } - } - - if (error) - pr_err("sysdev registration error\n"); - - return error; -} -device_initcall(register_intc_sysdevs); - -/* - * Dynamic IRQ allocation and deallocation - */ -unsigned int create_irq_nr(unsigned int irq_want, int node) -{ - unsigned int irq = 0, new; - unsigned long flags; - struct irq_desc *desc; - - spin_lock_irqsave(&vector_lock, flags); - - /* - * First try the wanted IRQ - */ - if (test_and_set_bit(irq_want, intc_irq_map) == 0) { - new = irq_want; - } else { - /* .. then fall back to scanning. */ - new = find_first_zero_bit(intc_irq_map, nr_irqs); - if (unlikely(new == nr_irqs)) - goto out_unlock; - - __set_bit(new, intc_irq_map); - } - - desc = irq_to_desc_alloc_node(new, node); - if (unlikely(!desc)) { - pr_err("can't get irq_desc for %d\n", new); - goto out_unlock; - } - - desc = move_irq_desc(desc, node); - irq = new; - -out_unlock: - spin_unlock_irqrestore(&vector_lock, flags); - - if (irq > 0) { - dynamic_irq_init(irq); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ -#endif - } - - return irq; -} - -int create_irq(void) -{ - int nid = cpu_to_node(smp_processor_id()); - int irq; - - irq = create_irq_nr(NR_IRQS_LEGACY, nid); - if (irq == 0) - irq = -1; - - return irq; -} - -void destroy_irq(unsigned int irq) -{ - unsigned long flags; - - dynamic_irq_cleanup(irq); - - spin_lock_irqsave(&vector_lock, flags); - __clear_bit(irq, intc_irq_map); - spin_unlock_irqrestore(&vector_lock, flags); -} - -int reserve_irq_vector(unsigned int irq) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vector_lock, flags); - if (test_and_set_bit(irq, intc_irq_map)) - ret = -EBUSY; - spin_unlock_irqrestore(&vector_lock, flags); - - return ret; -} - -void reserve_irq_legacy(void) -{ - unsigned long flags; - int i, j; - - spin_lock_irqsave(&vector_lock, flags); - j = find_first_bit(intc_irq_map, nr_irqs); - for (i = 0; i < j; i++) - __set_bit(i, intc_irq_map); - spin_unlock_irqrestore(&vector_lock, flags); -} diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig new file mode 100644 index 00000000000..c88cbccc62b --- /dev/null +++ b/drivers/sh/intc/Kconfig @@ -0,0 +1,35 @@ +comment "Interrupt controller options" + +config INTC_USERIMASK + bool "Userspace interrupt masking support" + depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) + help + This enables support for hardware-assisted userspace hardirq + masking. + + SH-4A and newer interrupt blocks all support a special shadowed + page with all non-masking registers obscured when mapped in to + userspace. This is primarily for use by userspace device + drivers that are using special priority levels. + + If in doubt, say N. + +config INTC_BALANCING + bool "Hardware IRQ balancing support" + depends on SMP && SUPERH && CPU_SHX3 + help + This enables support for IRQ auto-distribution mode on SH-X3 + SMP parts. All of the balancing and CPU wakeup decisions are + taken care of automatically by hardware for distributed + vectors. + + If in doubt, say N. + +config INTC_MAPPING_DEBUG + bool "Expose IRQ to per-controller id mapping via debugfs" + depends on DEBUG_FS + help + This will create a debugfs entry for showing the relationship + between system IRQs and the per-controller id tables. + + If in doubt, say N. diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile new file mode 100644 index 00000000000..bb5df868d77 --- /dev/null +++ b/drivers/sh/intc/Makefile @@ -0,0 +1,5 @@ +obj-y := access.o chip.o core.o dynamic.o handle.o virq.o + +obj-$(CONFIG_INTC_BALANCING) += balancing.o +obj-$(CONFIG_INTC_USERIMASK) += userimask.o +obj-$(CONFIG_INTC_MAPPING_DEBUG) += virq-debugfs.o diff --git a/drivers/sh/intc/access.c b/drivers/sh/intc/access.c new file mode 100644 index 00000000000..f892ae1d212 --- /dev/null +++ b/drivers/sh/intc/access.c @@ -0,0 +1,237 @@ +/* + * Common INTC2 register accessors + * + * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009, 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/io.h> +#include "internals.h" + +unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address) +{ + struct intc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < d->nr_windows; k++) { + window = d->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + address -= window->phys; + address += (unsigned long)window->virt; + + return address; + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return address; +} + +unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) +{ + unsigned int k; + + address = intc_phys_to_virt(d, address); + + for (k = 0; k < d->nr_reg; k++) { + if (d->reg[k] == address) + return k; + } + + BUG(); + return 0; +} + +unsigned int intc_set_field_from_handle(unsigned int value, + unsigned int field_value, + unsigned int handle) +{ + unsigned int width = _INTC_WIDTH(handle); + unsigned int shift = _INTC_SHIFT(handle); + + value &= ~(((1 << width) - 1) << shift); + value |= field_value << shift; + return value; +} + +unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle) +{ + unsigned int width = _INTC_WIDTH(handle); + unsigned int shift = _INTC_SHIFT(handle); + unsigned int mask = ((1 << width) - 1) << shift; + + return (value & mask) >> shift; +} + +static unsigned long test_8(unsigned long addr, unsigned long h, + unsigned long ignore) +{ + return intc_get_field_from_handle(__raw_readb(addr), h); +} + +static unsigned long test_16(unsigned long addr, unsigned long h, + unsigned long ignore) +{ + return intc_get_field_from_handle(__raw_readw(addr), h); +} + +static unsigned long test_32(unsigned long addr, unsigned long h, + unsigned long ignore) +{ + return intc_get_field_from_handle(__raw_readl(addr), h); +} + +static unsigned long write_8(unsigned long addr, unsigned long h, + unsigned long data) +{ + __raw_writeb(intc_set_field_from_handle(0, data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ + return 0; +} + +static unsigned long write_16(unsigned long addr, unsigned long h, + unsigned long data) +{ + __raw_writew(intc_set_field_from_handle(0, data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ + return 0; +} + +static unsigned long write_32(unsigned long addr, unsigned long h, + unsigned long data) +{ + __raw_writel(intc_set_field_from_handle(0, data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ + return 0; +} + +static unsigned long modify_8(unsigned long addr, unsigned long h, + unsigned long data) +{ + unsigned long flags; + unsigned int value; + local_irq_save(flags); + value = intc_set_field_from_handle(__raw_readb(addr), data, h); + __raw_writeb(value, addr); + (void)__raw_readb(addr); /* Defeat write posting */ + local_irq_restore(flags); + return 0; +} + +static unsigned long modify_16(unsigned long addr, unsigned long h, + unsigned long data) +{ + unsigned long flags; + unsigned int value; + local_irq_save(flags); + value = intc_set_field_from_handle(__raw_readw(addr), data, h); + __raw_writew(value, addr); + (void)__raw_readw(addr); /* Defeat write posting */ + local_irq_restore(flags); + return 0; +} + +static unsigned long modify_32(unsigned long addr, unsigned long h, + unsigned long data) +{ + unsigned long flags; + unsigned int value; + local_irq_save(flags); + value = intc_set_field_from_handle(__raw_readl(addr), data, h); + __raw_writel(value, addr); + (void)__raw_readl(addr); /* Defeat write posting */ + local_irq_restore(flags); + return 0; +} + +static unsigned long intc_mode_field(unsigned long addr, + unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); +} + +static unsigned long intc_mode_zero(unsigned long addr, + unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + return fn(addr, handle, 0); +} + +static unsigned long intc_mode_prio(unsigned long addr, + unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + return fn(addr, handle, intc_get_prio_level(irq)); +} + +unsigned long (*intc_reg_fns[])(unsigned long addr, + unsigned long h, + unsigned long data) = { + [REG_FN_TEST_BASE + 0] = test_8, + [REG_FN_TEST_BASE + 1] = test_16, + [REG_FN_TEST_BASE + 3] = test_32, + [REG_FN_WRITE_BASE + 0] = write_8, + [REG_FN_WRITE_BASE + 1] = write_16, + [REG_FN_WRITE_BASE + 3] = write_32, + [REG_FN_MODIFY_BASE + 0] = modify_8, + [REG_FN_MODIFY_BASE + 1] = modify_16, + [REG_FN_MODIFY_BASE + 3] = modify_32, +}; + +unsigned long (*intc_enable_fns[])(unsigned long addr, + unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_field, + [MODE_MASK_REG] = intc_mode_zero, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_prio, + [MODE_PCLR_REG] = intc_mode_prio, +}; + +unsigned long (*intc_disable_fns[])(unsigned long addr, + unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_zero, + [MODE_MASK_REG] = intc_mode_field, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_zero, + [MODE_PCLR_REG] = intc_mode_field, +}; + +unsigned long (*intc_enable_noprio_fns[])(unsigned long addr, + unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_field, + [MODE_MASK_REG] = intc_mode_zero, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_field, + [MODE_PCLR_REG] = intc_mode_field, +}; diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c new file mode 100644 index 00000000000..cec7a96f2c0 --- /dev/null +++ b/drivers/sh/intc/balancing.c @@ -0,0 +1,97 @@ +/* + * Support for hardware-managed IRQ auto-distribution. + * + * Copyright (C) 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include "internals.h" + +static unsigned long dist_handle[NR_IRQS]; + +void intc_balancing_enable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); +} + +void intc_balancing_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); +} + +static unsigned int intc_dist_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->hw.mask_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { + mr = desc->hw.mask_regs + i; + + /* + * Skip this entry if there's no auto-distribution + * register associated with it. + */ + if (!mr->dist_reg) + continue; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->dist_reg; + reg_d = mr->dist_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + /* + * It's possible we've gotten here with no distribution options + * available for the IRQ in question, so we just skip over those. + */ + return 0; +} + +void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, + struct intc_desc_int *d, intc_enum id) +{ + unsigned long flags; + + /* + * Nothing to do for this IRQ. + */ + if (!desc->hw.mask_regs) + return; + + raw_spin_lock_irqsave(&intc_big_lock, flags); + dist_handle[irq] = intc_dist_data(desc, d, id); + raw_spin_unlock_irqrestore(&intc_big_lock, flags); +} diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c new file mode 100644 index 00000000000..35c03706cc2 --- /dev/null +++ b/drivers/sh/intc/chip.c @@ -0,0 +1,215 @@ +/* + * IRQ chip definitions for INTC IRQs. + * + * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009, 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/cpumask.h> +#include <linux/io.h> +#include "internals.h" + +void _intc_enable(unsigned int irq, unsigned long handle) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long addr; + unsigned int cpu; + + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { +#ifdef CONFIG_SMP + if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) + continue; +#endif + addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); + intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ + [_INTC_FN(handle)], irq); + } + + intc_balancing_enable(irq); +} + +static void intc_enable(unsigned int irq) +{ + _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); +} + +static void intc_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = (unsigned long)get_irq_chip_data(irq); + unsigned long addr; + unsigned int cpu; + + intc_balancing_disable(irq); + + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { +#ifdef CONFIG_SMP + if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) + continue; +#endif + addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); + intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ + [_INTC_FN(handle)], irq); + } +} + +static int intc_set_wake(unsigned int irq, unsigned int on) +{ + return 0; /* allow wakeup, but setup hardware in intc_suspend() */ +} + +#ifdef CONFIG_SMP +/* + * This is held with the irq desc lock held, so we don't require any + * additional locking here at the intc desc level. The affinity mask is + * later tested in the enable/disable paths. + */ +static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask) +{ + if (!cpumask_intersects(cpumask, cpu_online_mask)) + return -1; + + cpumask_copy(irq_to_desc(irq)->affinity, cpumask); + + return 0; +} +#endif + +static void intc_mask_ack(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = intc_get_ack_handle(irq); + unsigned long addr; + + intc_disable(irq); + + /* read register and write zero only to the associated bit */ + if (handle) { + unsigned int value; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + value = intc_set_field_from_handle(0, 1, handle); + + switch (_INTC_FN(handle)) { + case REG_FN_MODIFY_BASE + 0: /* 8bit */ + __raw_readb(addr); + __raw_writeb(0xff ^ value, addr); + break; + case REG_FN_MODIFY_BASE + 1: /* 16bit */ + __raw_readw(addr); + __raw_writew(0xffff ^ value, addr); + break; + case REG_FN_MODIFY_BASE + 3: /* 32bit */ + __raw_readl(addr); + __raw_writel(0xffffffff ^ value, addr); + break; + default: + BUG(); + break; + } + } +} + +static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, + unsigned int nr_hp, + unsigned int irq) +{ + int i; + + /* + * this doesn't scale well, but... + * + * this function should only be used for cerain uncommon + * operations such as intc_set_priority() and intc_set_type() + * and in those rare cases performance doesn't matter that much. + * keeping the memory footprint low is more important. + * + * one rather simple way to speed this up and still keep the + * memory footprint down is to make sure the array is sorted + * and then perform a bisect to lookup the irq. + */ + for (i = 0; i < nr_hp; i++) { + if ((hp + i)->irq != irq) + continue; + + return hp + i; + } + + return NULL; +} + +int intc_set_priority(unsigned int irq, unsigned int prio) +{ + struct intc_desc_int *d = get_intc_desc(irq); + struct intc_handle_int *ihp; + + if (!intc_get_prio_level(irq) || prio <= 1) + return -EINVAL; + + ihp = intc_find_irq(d->prio, d->nr_prio, irq); + if (ihp) { + if (prio >= (1 << _INTC_WIDTH(ihp->handle))) + return -EINVAL; + + intc_set_prio_level(irq, prio); + + /* + * only set secondary masking method directly + * primary masking method is using intc_prio_level[irq] + * priority level will be set during next enable() + */ + if (_INTC_FN(ihp->handle) != REG_FN_ERR) + _intc_enable(irq, ihp->handle); + } + return 0; +} + +#define VALID(x) (x | 0x80) + +static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_EDGE_FALLING] = VALID(0), + [IRQ_TYPE_EDGE_RISING] = VALID(1), + [IRQ_TYPE_LEVEL_LOW] = VALID(2), + /* SH7706, SH7707 and SH7709 do not support high level triggered */ +#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ + !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ + !defined(CONFIG_CPU_SUBTYPE_SH7709) + [IRQ_TYPE_LEVEL_HIGH] = VALID(3), +#endif +}; + +static int intc_set_type(unsigned int irq, unsigned int type) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; + struct intc_handle_int *ihp; + unsigned long addr; + + if (!value) + return -EINVAL; + + ihp = intc_find_irq(d->sense, d->nr_sense, irq); + if (ihp) { + addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); + intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); + } + + return 0; +} + +struct irq_chip intc_irq_chip = { + .mask = intc_disable, + .unmask = intc_enable, + .mask_ack = intc_mask_ack, + .enable = intc_enable, + .disable = intc_disable, + .shutdown = intc_disable, + .set_type = intc_set_type, + .set_wake = intc_set_wake, +#ifdef CONFIG_SMP + .set_affinity = intc_set_affinity, +#endif +}; diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c new file mode 100644 index 00000000000..306ed287077 --- /dev/null +++ b/drivers/sh/intc/core.c @@ -0,0 +1,469 @@ +/* + * Shared interrupt handling code for IPR and INTC2 types of IRQs. + * + * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009, 2010 Paul Mundt + * + * Based on intc2.c and ipr.c + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) + * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> + * Copyright (C) 2005, 2006 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) "intc: " fmt + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/sh_intc.h> +#include <linux/sysdev.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/radix-tree.h> +#include "internals.h" + +LIST_HEAD(intc_list); +DEFINE_RAW_SPINLOCK(intc_big_lock); +unsigned int nr_intc_controllers; + +/* + * Default priority level + * - this needs to be at least 2 for 5-bit priorities on 7780 + */ +static unsigned int default_prio_level = 2; /* 2 - 16 */ +static unsigned int intc_prio_level[NR_IRQS]; /* for now */ + +unsigned int intc_get_dfl_prio_level(void) +{ + return default_prio_level; +} + +unsigned int intc_get_prio_level(unsigned int irq) +{ + return intc_prio_level[irq]; +} + +void intc_set_prio_level(unsigned int irq, unsigned int level) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&intc_big_lock, flags); + intc_prio_level[irq] = level; + raw_spin_unlock_irqrestore(&intc_big_lock, flags); +} + +static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) +{ + generic_handle_irq((unsigned int)get_irq_data(irq)); +} + +static void __init intc_register_irq(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int irq) +{ + struct intc_handle_int *hp; + unsigned int data[2], primary; + unsigned long flags; + + /* + * Register the IRQ position with the global IRQ map, then insert + * it in to the radix tree. + */ + reserve_irq_vector(irq); + + raw_spin_lock_irqsave(&intc_big_lock, flags); + radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); + raw_spin_unlock_irqrestore(&intc_big_lock, flags); + + /* + * Prefer single interrupt source bitmap over other combinations: + * + * 1. bitmap, single interrupt source + * 2. priority, single interrupt source + * 3. bitmap, multiple interrupt sources (groups) + * 4. priority, multiple interrupt sources (groups) + */ + data[0] = intc_get_mask_handle(desc, d, enum_id, 0); + data[1] = intc_get_prio_handle(desc, d, enum_id, 0); + + primary = 0; + if (!data[0] && data[1]) + primary = 1; + + if (!data[0] && !data[1]) + pr_warning("missing unique irq mask for irq %d (vect 0x%04x)\n", + irq, irq2evt(irq)); + + data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, 1); + data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1); + + if (!data[primary]) + primary ^= 1; + + BUG_ON(!data[primary]); /* must have primary masking method */ + + disable_irq_nosync(irq); + set_irq_chip_and_handler_name(irq, &d->chip, + handle_level_irq, "level"); + set_irq_chip_data(irq, (void *)data[primary]); + + /* + * set priority level + */ + intc_set_prio_level(irq, intc_get_dfl_prio_level()); + + /* enable secondary masking method if present */ + if (data[!primary]) + _intc_enable(irq, data[!primary]); + + /* add irq to d->prio list if priority is available */ + if (data[1]) { + hp = d->prio + d->nr_prio; + hp->irq = irq; + hp->handle = data[1]; + + if (primary) { + /* + * only secondary priority should access registers, so + * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() + */ + hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); + hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); + } + d->nr_prio++; + } + + /* add irq to d->sense list if sense is available */ + data[0] = intc_get_sense_handle(desc, d, enum_id); + if (data[0]) { + (d->sense + d->nr_sense)->irq = irq; + (d->sense + d->nr_sense)->handle = data[0]; + d->nr_sense++; + } + + /* irq should be disabled by default */ + d->chip.mask(irq); + + intc_set_ack_handle(irq, desc, d, enum_id); + intc_set_dist_handle(irq, desc, d, enum_id); + + activate_irq(irq); +} + +static unsigned int __init save_reg(struct intc_desc_int *d, + unsigned int cnt, + unsigned long value, + unsigned int smp) +{ + if (value) { + value = intc_phys_to_virt(d, value); + + d->reg[cnt] = value; +#ifdef CONFIG_SMP + d->smp[cnt] = smp; +#endif + return 1; + } + + return 0; +} + +int __init register_intc_controller(struct intc_desc *desc) +{ + unsigned int i, k, smp; + struct intc_hw_desc *hw = &desc->hw; + struct intc_desc_int *d; + struct resource *res; + + pr_info("Registered controller '%s' with %u IRQs\n", + desc->name, hw->nr_vectors); + + d = kzalloc(sizeof(*d), GFP_NOWAIT); + if (!d) + goto err0; + + INIT_LIST_HEAD(&d->list); + list_add_tail(&d->list, &intc_list); + + raw_spin_lock_init(&d->lock); + + d->index = nr_intc_controllers; + + if (desc->num_resources) { + d->nr_windows = desc->num_resources; + d->window = kzalloc(d->nr_windows * sizeof(*d->window), + GFP_NOWAIT); + if (!d->window) + goto err1; + + for (k = 0; k < d->nr_windows; k++) { + res = desc->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + d->window[k].phys = res->start; + d->window[k].size = resource_size(res); + d->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!d->window[k].virt) + goto err2; + } + } + + d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; +#ifdef CONFIG_INTC_BALANCING + if (d->nr_reg) + d->nr_reg += hw->nr_mask_regs; +#endif + d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; + d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; + d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; + d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; + + d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); + if (!d->reg) + goto err2; + +#ifdef CONFIG_SMP + d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); + if (!d->smp) + goto err3; +#endif + k = 0; + + if (hw->mask_regs) { + for (i = 0; i < hw->nr_mask_regs; i++) { + smp = IS_SMP(hw->mask_regs[i]); + k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); + k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); +#ifdef CONFIG_INTC_BALANCING + k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); +#endif + } + } + + if (hw->prio_regs) { + d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), + GFP_NOWAIT); + if (!d->prio) + goto err4; + + for (i = 0; i < hw->nr_prio_regs; i++) { + smp = IS_SMP(hw->prio_regs[i]); + k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); + k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); + } + } + + if (hw->sense_regs) { + d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), + GFP_NOWAIT); + if (!d->sense) + goto err5; + + for (i = 0; i < hw->nr_sense_regs; i++) + k += save_reg(d, k, hw->sense_regs[i].reg, 0); + } + + if (hw->subgroups) + for (i = 0; i < hw->nr_subgroups; i++) + if (hw->subgroups[i].reg) + k+= save_reg(d, k, hw->subgroups[i].reg, 0); + + memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip)); + d->chip.name = desc->name; + + if (hw->ack_regs) + for (i = 0; i < hw->nr_ack_regs; i++) + k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); + else + d->chip.mask_ack = d->chip.disable; + + /* disable bits matching force_disable before registering irqs */ + if (desc->force_disable) + intc_enable_disable_enum(desc, d, desc->force_disable, 0); + + /* disable bits matching force_enable before registering irqs */ + if (desc->force_enable) + intc_enable_disable_enum(desc, d, desc->force_enable, 0); + + BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ + + /* register the vectors one by one */ + for (i = 0; i < hw->nr_vectors; i++) { + struct intc_vect *vect = hw->vectors + i; + unsigned int irq = evt2irq(vect->vect); + struct irq_desc *irq_desc; + + if (!vect->enum_id) + continue; + + irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); + if (unlikely(!irq_desc)) { + pr_err("can't get irq_desc for %d\n", irq); + continue; + } + + intc_irq_xlate_set(irq, vect->enum_id, d); + intc_register_irq(desc, d, vect->enum_id, irq); + + for (k = i + 1; k < hw->nr_vectors; k++) { + struct intc_vect *vect2 = hw->vectors + k; + unsigned int irq2 = evt2irq(vect2->vect); + + if (vect->enum_id != vect2->enum_id) + continue; + + /* + * In the case of multi-evt handling and sparse + * IRQ support, each vector still needs to have + * its own backing irq_desc. + */ + irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); + if (unlikely(!irq_desc)) { + pr_err("can't get irq_desc for %d\n", irq2); + continue; + } + + vect2->enum_id = 0; + + /* redirect this interrupts to the first one */ + set_irq_chip(irq2, &dummy_irq_chip); + set_irq_chained_handler(irq2, intc_redirect_irq); + set_irq_data(irq2, (void *)irq); + } + } + + intc_subgroup_init(desc, d); + + /* enable bits matching force_enable after registering irqs */ + if (desc->force_enable) + intc_enable_disable_enum(desc, d, desc->force_enable, 1); + + nr_intc_controllers++; + + return 0; +err5: + kfree(d->prio); +err4: +#ifdef CONFIG_SMP + kfree(d->smp); +err3: +#endif + kfree(d->reg); +err2: + for (k = 0; k < d->nr_windows; k++) + if (d->window[k].virt) + iounmap(d->window[k].virt); + + kfree(d->window); +err1: + kfree(d); +err0: + pr_err("unable to allocate INTC memory\n"); + + return -ENOMEM; +} + +static ssize_t +show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +{ + struct intc_desc_int *d; + + d = container_of(dev, struct intc_desc_int, sysdev); + + return sprintf(buf, "%s\n", d->chip.name); +} + +static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); + +static int intc_suspend(struct sys_device *dev, pm_message_t state) +{ + struct intc_desc_int *d; + struct irq_desc *desc; + int irq; + + /* get intc controller associated with this sysdev */ + d = container_of(dev, struct intc_desc_int, sysdev); + + switch (state.event) { + case PM_EVENT_ON: + if (d->state.event != PM_EVENT_FREEZE) + break; + + for_each_irq_desc(irq, desc) { + /* + * This will catch the redirect and VIRQ cases + * due to the dummy_irq_chip being inserted. + */ + if (desc->chip != &d->chip) + continue; + if (desc->status & IRQ_DISABLED) + desc->chip->disable(irq); + else + desc->chip->enable(irq); + } + break; + case PM_EVENT_FREEZE: + /* nothing has to be done */ + break; + case PM_EVENT_SUSPEND: + /* enable wakeup irqs belonging to this intc controller */ + for_each_irq_desc(irq, desc) { + if (desc->chip != &d->chip) + continue; + if ((desc->status & IRQ_WAKEUP)) + desc->chip->enable(irq); + } + break; + } + + d->state = state; + + return 0; +} + +static int intc_resume(struct sys_device *dev) +{ + return intc_suspend(dev, PMSG_ON); +} + +struct sysdev_class intc_sysdev_class = { + .name = "intc", + .suspend = intc_suspend, + .resume = intc_resume, +}; + +/* register this intc as sysdev to allow suspend/resume */ +static int __init register_intc_sysdevs(void) +{ + struct intc_desc_int *d; + int error; + + error = sysdev_class_register(&intc_sysdev_class); + if (!error) { + list_for_each_entry(d, &intc_list, list) { + d->sysdev.id = d->index; + d->sysdev.cls = &intc_sysdev_class; + error = sysdev_register(&d->sysdev); + if (error == 0) + error = sysdev_create_file(&d->sysdev, + &attr_name); + if (error) + break; + } + } + + if (error) + pr_err("sysdev registration error\n"); + + return error; +} +device_initcall(register_intc_sysdevs); diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c new file mode 100644 index 00000000000..6caecdffe20 --- /dev/null +++ b/drivers/sh/intc/dynamic.c @@ -0,0 +1,135 @@ +/* + * Dynamic IRQ management + * + * Copyright (C) 2010 Paul Mundt + * + * Modelled after arch/x86/kernel/apic/io_apic.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) "intc: " fmt + +#include <linux/irq.h> +#include <linux/bitmap.h> +#include <linux/spinlock.h> +#include "internals.h" /* only for activate_irq() damage.. */ + +/* + * The intc_irq_map provides a global map of bound IRQ vectors for a + * given platform. Allocation of IRQs are either static through the CPU + * vector map, or dynamic in the case of board mux vectors or MSI. + * + * As this is a central point for all IRQ controllers on the system, + * each of the available sources are mapped out here. This combined with + * sparseirq makes it quite trivial to keep the vector map tightly packed + * when dynamically creating IRQs, as well as tying in to otherwise + * unused irq_desc positions in the sparse array. + */ +static DECLARE_BITMAP(intc_irq_map, NR_IRQS); +static DEFINE_RAW_SPINLOCK(vector_lock); + +/* + * Dynamic IRQ allocation and deallocation + */ +unsigned int create_irq_nr(unsigned int irq_want, int node) +{ + unsigned int irq = 0, new; + unsigned long flags; + struct irq_desc *desc; + + raw_spin_lock_irqsave(&vector_lock, flags); + + /* + * First try the wanted IRQ + */ + if (test_and_set_bit(irq_want, intc_irq_map) == 0) { + new = irq_want; + } else { + /* .. then fall back to scanning. */ + new = find_first_zero_bit(intc_irq_map, nr_irqs); + if (unlikely(new == nr_irqs)) + goto out_unlock; + + __set_bit(new, intc_irq_map); + } + + desc = irq_to_desc_alloc_node(new, node); + if (unlikely(!desc)) { + pr_err("can't get irq_desc for %d\n", new); + goto out_unlock; + } + + desc = move_irq_desc(desc, node); + irq = new; + +out_unlock: + raw_spin_unlock_irqrestore(&vector_lock, flags); + + if (irq > 0) { + dynamic_irq_init(irq); + activate_irq(irq); + } + + return irq; +} + +int create_irq(void) +{ + int nid = cpu_to_node(smp_processor_id()); + int irq; + + irq = create_irq_nr(NR_IRQS_LEGACY, nid); + if (irq == 0) + irq = -1; + + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + raw_spin_lock_irqsave(&vector_lock, flags); + __clear_bit(irq, intc_irq_map); + raw_spin_unlock_irqrestore(&vector_lock, flags); +} + +int reserve_irq_vector(unsigned int irq) +{ + unsigned long flags; + int ret = 0; + + raw_spin_lock_irqsave(&vector_lock, flags); + if (test_and_set_bit(irq, intc_irq_map)) + ret = -EBUSY; + raw_spin_unlock_irqrestore(&vector_lock, flags); + + return ret; +} + +void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) +{ + unsigned long flags; + int i; + + raw_spin_lock_irqsave(&vector_lock, flags); + for (i = 0; i < nr_vecs; i++) + __set_bit(evt2irq(vectors[i].vect), intc_irq_map); + raw_spin_unlock_irqrestore(&vector_lock, flags); +} + +void reserve_irq_legacy(void) +{ + unsigned long flags; + int i, j; + + raw_spin_lock_irqsave(&vector_lock, flags); + j = find_first_bit(intc_irq_map, nr_irqs); + for (i = 0; i < j; i++) + __set_bit(i, intc_irq_map); + raw_spin_unlock_irqrestore(&vector_lock, flags); +} diff --git a/drivers/sh/intc/handle.c b/drivers/sh/intc/handle.c new file mode 100644 index 00000000000..057ce56829b --- /dev/null +++ b/drivers/sh/intc/handle.c @@ -0,0 +1,307 @@ +/* + * Shared interrupt handling code for IPR and INTC2 types of IRQs. + * + * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009, 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/spinlock.h> +#include "internals.h" + +static unsigned long ack_handle[NR_IRQS]; + +static intc_enum __init intc_grp_id(struct intc_desc *desc, + intc_enum enum_id) +{ + struct intc_group *g = desc->hw.groups; + unsigned int i, j; + + for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) { + g = desc->hw.groups + i; + + for (j = 0; g->enum_ids[j]; j++) { + if (g->enum_ids[j] != enum_id) + continue; + + return g->enum_id; + } + } + + return 0; +} + +static unsigned int __init _intc_mask_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int *reg_idx, + unsigned int *fld_idx) +{ + struct intc_mask_reg *mr = desc->hw.mask_regs; + unsigned int fn, mode; + unsigned long reg_e, reg_d; + + while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) { + mr = desc->hw.mask_regs + *reg_idx; + + for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) { + if (mr->enum_ids[*fld_idx] != enum_id) + continue; + + if (mr->set_reg && mr->clr_reg) { + fn = REG_FN_WRITE_BASE; + mode = MODE_DUAL_REG; + reg_e = mr->clr_reg; + reg_d = mr->set_reg; + } else { + fn = REG_FN_MODIFY_BASE; + if (mr->set_reg) { + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + } else { + mode = MODE_MASK_REG; + reg_e = mr->clr_reg; + reg_d = mr->clr_reg; + } + } + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - *fld_idx); + } + + *fld_idx = 0; + (*reg_idx)++; + } + + return 0; +} + +unsigned int __init +intc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d, + intc_enum enum_id, int do_grps) +{ + unsigned int i = 0; + unsigned int j = 0; + unsigned int ret; + + ret = _intc_mask_data(desc, d, enum_id, &i, &j); + if (ret) + return ret; + + if (do_grps) + return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0); + + return 0; +} + +static unsigned int __init _intc_prio_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int *reg_idx, + unsigned int *fld_idx) +{ + struct intc_prio_reg *pr = desc->hw.prio_regs; + unsigned int fn, n, mode, bit; + unsigned long reg_e, reg_d; + + while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) { + pr = desc->hw.prio_regs + *reg_idx; + + for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) { + if (pr->enum_ids[*fld_idx] != enum_id) + continue; + + if (pr->set_reg && pr->clr_reg) { + fn = REG_FN_WRITE_BASE; + mode = MODE_PCLR_REG; + reg_e = pr->set_reg; + reg_d = pr->clr_reg; + } else { + fn = REG_FN_MODIFY_BASE; + mode = MODE_PRIO_REG; + if (!pr->set_reg) + BUG(); + reg_e = pr->set_reg; + reg_d = pr->set_reg; + } + + fn += (pr->reg_width >> 3) - 1; + n = *fld_idx + 1; + + BUG_ON(n * pr->field_width > pr->reg_width); + + bit = pr->reg_width - (n * pr->field_width); + + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + pr->field_width, bit); + } + + *fld_idx = 0; + (*reg_idx)++; + } + + return 0; +} + +unsigned int __init +intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d, + intc_enum enum_id, int do_grps) +{ + unsigned int i = 0; + unsigned int j = 0; + unsigned int ret; + + ret = _intc_prio_data(desc, d, enum_id, &i, &j); + if (ret) + return ret; + + if (do_grps) + return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0); + + return 0; +} + +static unsigned int __init intc_ack_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->hw.ack_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) { + mr = desc->hw.ack_regs + i; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + return 0; +} + +static void intc_enable_disable(struct intc_desc_int *d, + unsigned long handle, int do_enable) +{ + unsigned long addr; + unsigned int cpu; + unsigned long (*fn)(unsigned long, unsigned long, + unsigned long (*)(unsigned long, unsigned long, + unsigned long), + unsigned int); + + if (do_enable) { + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { + addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); + fn = intc_enable_noprio_fns[_INTC_MODE(handle)]; + fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); + } + } else { + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { + addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); + fn = intc_disable_fns[_INTC_MODE(handle)]; + fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); + } + } +} + +void __init intc_enable_disable_enum(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int enable) +{ + unsigned int i, j, data; + + /* go through and enable/disable all mask bits */ + i = j = 0; + do { + data = _intc_mask_data(desc, d, enum_id, &i, &j); + if (data) + intc_enable_disable(d, data, enable); + j++; + } while (data); + + /* go through and enable/disable all priority fields */ + i = j = 0; + do { + data = _intc_prio_data(desc, d, enum_id, &i, &j); + if (data) + intc_enable_disable(d, data, enable); + + j++; + } while (data); +} + +unsigned int __init +intc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_sense_reg *sr = desc->hw.sense_regs; + unsigned int i, j, fn, bit; + + for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) { + sr = desc->hw.sense_regs + i; + + for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { + if (sr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + fn += (sr->reg_width >> 3) - 1; + + BUG_ON((j + 1) * sr->field_width > sr->reg_width); + + bit = sr->reg_width - ((j + 1) * sr->field_width); + + return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), + 0, sr->field_width, bit); + } + } + + return 0; +} + + +void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc, + struct intc_desc_int *d, intc_enum id) +{ + unsigned long flags; + + /* + * Nothing to do for this IRQ. + */ + if (!desc->hw.ack_regs) + return; + + raw_spin_lock_irqsave(&intc_big_lock, flags); + ack_handle[irq] = intc_ack_data(desc, d, id); + raw_spin_unlock_irqrestore(&intc_big_lock, flags); +} + +unsigned long intc_get_ack_handle(unsigned int irq) +{ + return ack_handle[irq]; +} diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h new file mode 100644 index 00000000000..d49482c623f --- /dev/null +++ b/drivers/sh/intc/internals.h @@ -0,0 +1,186 @@ +#include <linux/sh_intc.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/radix-tree.h> +#include <linux/sysdev.h> + +#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ + ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ + ((addr_e) << 16) | ((addr_d << 24))) + +#define _INTC_SHIFT(h) (h & 0x1f) +#define _INTC_WIDTH(h) ((h >> 5) & 0xf) +#define _INTC_FN(h) ((h >> 9) & 0xf) +#define _INTC_MODE(h) ((h >> 13) & 0x7) +#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) +#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) + +#ifdef CONFIG_SMP +#define IS_SMP(x) (x.smp) +#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) +#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) +#else +#define IS_SMP(x) 0 +#define INTC_REG(d, x, c) (d->reg[(x)]) +#define SMP_NR(d, x) 1 +#endif + +struct intc_handle_int { + unsigned int irq; + unsigned long handle; +}; + +struct intc_window { + phys_addr_t phys; + void __iomem *virt; + unsigned long size; +}; + +struct intc_map_entry { + intc_enum enum_id; + struct intc_desc_int *desc; +}; + +struct intc_subgroup_entry { + unsigned int pirq; + intc_enum enum_id; + unsigned long handle; +}; + +struct intc_desc_int { + struct list_head list; + struct sys_device sysdev; + struct radix_tree_root tree; + pm_message_t state; + raw_spinlock_t lock; + unsigned int index; + unsigned long *reg; +#ifdef CONFIG_SMP + unsigned long *smp; +#endif + unsigned int nr_reg; + struct intc_handle_int *prio; + unsigned int nr_prio; + struct intc_handle_int *sense; + unsigned int nr_sense; + struct intc_window *window; + unsigned int nr_windows; + struct irq_chip chip; +}; + + +enum { + REG_FN_ERR = 0, + REG_FN_TEST_BASE = 1, + REG_FN_WRITE_BASE = 5, + REG_FN_MODIFY_BASE = 9 +}; + +enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ + MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ + MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ + MODE_PRIO_REG, /* Priority value written to enable interrupt */ + MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ +}; + +static inline struct intc_desc_int *get_intc_desc(unsigned int irq) +{ + struct irq_chip *chip = get_irq_chip(irq); + + return container_of(chip, struct intc_desc_int, chip); +} + +/* + * Grumble. + */ +static inline void activate_irq(int irq) +{ +#ifdef CONFIG_ARM + /* ARM requires an extra step to clear IRQ_NOREQUEST, which it + * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. + */ + set_irq_flags(irq, IRQF_VALID); +#else + /* same effect on other architectures */ + set_irq_noprobe(irq); +#endif +} + +/* access.c */ +extern unsigned long +(*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data); + +extern unsigned long +(*intc_enable_fns[])(unsigned long addr, unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, unsigned long), + unsigned int irq); +extern unsigned long +(*intc_disable_fns[])(unsigned long addr, unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, unsigned long), + unsigned int irq); +extern unsigned long +(*intc_enable_noprio_fns[])(unsigned long addr, unsigned long handle, + unsigned long (*fn)(unsigned long, + unsigned long, unsigned long), + unsigned int irq); + +unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address); +unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address); +unsigned int intc_set_field_from_handle(unsigned int value, + unsigned int field_value, + unsigned int handle); +unsigned long intc_get_field_from_handle(unsigned int value, + unsigned int handle); + +/* balancing.c */ +#ifdef CONFIG_INTC_BALANCING +void intc_balancing_enable(unsigned int irq); +void intc_balancing_disable(unsigned int irq); +void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, + struct intc_desc_int *d, intc_enum id); +#else +static inline void intc_balancing_enable(unsigned int irq) { } +static inline void intc_balancing_disable(unsigned int irq) { } +static inline void +intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, + struct intc_desc_int *d, intc_enum id) { } +#endif + +/* chip.c */ +extern struct irq_chip intc_irq_chip; +void _intc_enable(unsigned int irq, unsigned long handle); + +/* core.c */ +extern struct list_head intc_list; +extern raw_spinlock_t intc_big_lock; +extern unsigned int nr_intc_controllers; +extern struct sysdev_class intc_sysdev_class; + +unsigned int intc_get_dfl_prio_level(void); +unsigned int intc_get_prio_level(unsigned int irq); +void intc_set_prio_level(unsigned int irq, unsigned int level); + +/* handle.c */ +unsigned int intc_get_mask_handle(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int do_grps); +unsigned int intc_get_prio_handle(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int do_grps); +unsigned int intc_get_sense_handle(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id); +void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc, + struct intc_desc_int *d, intc_enum id); +unsigned long intc_get_ack_handle(unsigned int irq); +void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d, + intc_enum enum_id, int enable); + +/* virq.c */ +void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d); +void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d); +struct intc_map_entry *intc_irq_xlate_get(unsigned int irq); diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c new file mode 100644 index 00000000000..e32304b66cf --- /dev/null +++ b/drivers/sh/intc/userimask.c @@ -0,0 +1,83 @@ +/* + * Support for hardware-assisted userspace interrupt masking. + * + * Copyright (C) 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) "intc: " fmt + +#include <linux/errno.h> +#include <linux/sysdev.h> +#include <linux/init.h> +#include <linux/io.h> +#include <asm/sizes.h> +#include "internals.h" + +static void __iomem *uimask; + +static ssize_t +show_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); +} + +static ssize_t +store_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + unsigned long level; + + level = simple_strtoul(buf, NULL, 10); + + /* + * Minimal acceptable IRQ levels are in the 2 - 16 range, but + * these are chomped so as to not interfere with normal IRQs. + * + * Level 1 is a special case on some CPUs in that it's not + * directly settable, but given that USERIMASK cuts off below a + * certain level, we don't care about this limitation here. + * Level 0 on the other hand equates to user masking disabled. + * + * We use the default priority level as a cut off so that only + * special case opt-in IRQs can be mangled. + */ + if (level >= intc_get_dfl_prio_level()) + return -EINVAL; + + __raw_writel(0xa5 << 24 | level << 4, uimask); + + return count; +} + +static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, + show_intc_userimask, store_intc_userimask); + + +static int __init userimask_sysdev_init(void) +{ + if (unlikely(!uimask)) + return -ENXIO; + + return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask); +} +late_initcall(userimask_sysdev_init); + +int register_intc_userimask(unsigned long addr) +{ + if (unlikely(uimask)) + return -EBUSY; + + uimask = ioremap_nocache(addr, SZ_4K); + if (unlikely(!uimask)) + return -ENOMEM; + + pr_info("userimask support registered for levels 0 -> %d\n", + intc_get_dfl_prio_level() - 1); + + return 0; +} diff --git a/drivers/sh/intc/virq-debugfs.c b/drivers/sh/intc/virq-debugfs.c new file mode 100644 index 00000000000..9e62ba9311f --- /dev/null +++ b/drivers/sh/intc/virq-debugfs.c @@ -0,0 +1,64 @@ +/* + * Support for virtual IRQ subgroups debugfs mapping. + * + * Copyright (C) 2010 Paul Mundt + * + * Modelled after arch/powerpc/kernel/irq.c. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/seq_file.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/debugfs.h> +#include "internals.h" + +static int intc_irq_xlate_debug(struct seq_file *m, void *priv) +{ + int i; + + seq_printf(m, "%-5s %-7s %-15s\n", "irq", "enum", "chip name"); + + for (i = 1; i < nr_irqs; i++) { + struct intc_map_entry *entry = intc_irq_xlate_get(i); + struct intc_desc_int *desc = entry->desc; + + if (!desc) + continue; + + seq_printf(m, "%5d ", i); + seq_printf(m, "0x%05x ", entry->enum_id); + seq_printf(m, "%-15s\n", desc->chip.name); + } + + return 0; +} + +static int intc_irq_xlate_open(struct inode *inode, struct file *file) +{ + return single_open(file, intc_irq_xlate_debug, inode->i_private); +} + +static const struct file_operations intc_irq_xlate_fops = { + .open = intc_irq_xlate_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init intc_irq_xlate_init(void) +{ + /* + * XXX.. use arch_debugfs_dir here when all of the intc users are + * converted. + */ + if (debugfs_create_file("intc_irq_xlate", S_IRUGO, NULL, NULL, + &intc_irq_xlate_fops) == NULL) + return -ENOMEM; + + return 0; +} +fs_initcall(intc_irq_xlate_init); diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c new file mode 100644 index 00000000000..643dfd4d205 --- /dev/null +++ b/drivers/sh/intc/virq.c @@ -0,0 +1,255 @@ +/* + * Support for virtual IRQ subgroups. + * + * Copyright (C) 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) "intc: " fmt + +#include <linux/slab.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/radix-tree.h> +#include <linux/spinlock.h> +#include "internals.h" + +static struct intc_map_entry intc_irq_xlate[NR_IRQS]; + +struct intc_virq_list { + unsigned int irq; + struct intc_virq_list *next; +}; + +#define for_each_virq(entry, head) \ + for (entry = head; entry; entry = entry->next) + +/* + * Tags for the radix tree + */ +#define INTC_TAG_VIRQ_NEEDS_ALLOC 0 + +void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&intc_big_lock, flags); + intc_irq_xlate[irq].enum_id = id; + intc_irq_xlate[irq].desc = d; + raw_spin_unlock_irqrestore(&intc_big_lock, flags); +} + +struct intc_map_entry *intc_irq_xlate_get(unsigned int irq) +{ + return intc_irq_xlate + irq; +} + +int intc_irq_lookup(const char *chipname, intc_enum enum_id) +{ + struct intc_map_entry *ptr; + struct intc_desc_int *d; + int irq = -1; + + list_for_each_entry(d, &intc_list, list) { + int tagged; + + if (strcmp(d->chip.name, chipname) != 0) + continue; + + /* + * Catch early lookups for subgroup VIRQs that have not + * yet been allocated an IRQ. This already includes a + * fast-path out if the tree is untagged, so there is no + * need to explicitly test the root tree. + */ + tagged = radix_tree_tag_get(&d->tree, enum_id, + INTC_TAG_VIRQ_NEEDS_ALLOC); + if (unlikely(tagged)) + break; + + ptr = radix_tree_lookup(&d->tree, enum_id); + if (ptr) { + irq = ptr - intc_irq_xlate; + break; + } + } + + return irq; +} +EXPORT_SYMBOL_GPL(intc_irq_lookup); + +static int add_virq_to_pirq(unsigned int irq, unsigned int virq) +{ + struct intc_virq_list **last, *entry; + struct irq_desc *desc = irq_to_desc(irq); + + /* scan for duplicates */ + last = (struct intc_virq_list **)&desc->handler_data; + for_each_virq(entry, desc->handler_data) { + if (entry->irq == virq) + return 0; + last = &entry->next; + } + + entry = kzalloc(sizeof(struct intc_virq_list), GFP_ATOMIC); + if (!entry) { + pr_err("can't allocate VIRQ mapping for %d\n", virq); + return -ENOMEM; + } + + entry->irq = virq; + + *last = entry; + + return 0; +} + +static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct intc_virq_list *entry, *vlist = get_irq_data(irq); + struct intc_desc_int *d = get_intc_desc(irq); + + desc->chip->mask_ack(irq); + + for_each_virq(entry, vlist) { + unsigned long addr, handle; + + handle = (unsigned long)get_irq_data(entry->irq); + addr = INTC_REG(d, _INTC_ADDR_E(handle), 0); + + if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) + generic_handle_irq(entry->irq); + } + + desc->chip->unmask(irq); +} + +static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup, + struct intc_desc_int *d, + unsigned int index) +{ + unsigned int fn = REG_FN_TEST_BASE + (subgroup->reg_width >> 3) - 1; + + return _INTC_MK(fn, MODE_ENABLE_REG, intc_get_reg(d, subgroup->reg), + 0, 1, (subgroup->reg_width - 1) - index); +} + +static void __init intc_subgroup_init_one(struct intc_desc *desc, + struct intc_desc_int *d, + struct intc_subgroup *subgroup) +{ + struct intc_map_entry *mapped; + unsigned int pirq; + unsigned long flags; + int i; + + mapped = radix_tree_lookup(&d->tree, subgroup->parent_id); + if (!mapped) { + WARN_ON(1); + return; + } + + pirq = mapped - intc_irq_xlate; + + raw_spin_lock_irqsave(&d->lock, flags); + + for (i = 0; i < ARRAY_SIZE(subgroup->enum_ids); i++) { + struct intc_subgroup_entry *entry; + int err; + + if (!subgroup->enum_ids[i]) + continue; + + entry = kmalloc(sizeof(*entry), GFP_NOWAIT); + if (!entry) + break; + + entry->pirq = pirq; + entry->enum_id = subgroup->enum_ids[i]; + entry->handle = intc_subgroup_data(subgroup, d, i); + + err = radix_tree_insert(&d->tree, entry->enum_id, entry); + if (unlikely(err < 0)) + break; + + radix_tree_tag_set(&d->tree, entry->enum_id, + INTC_TAG_VIRQ_NEEDS_ALLOC); + } + + raw_spin_unlock_irqrestore(&d->lock, flags); +} + +void __init intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d) +{ + int i; + + if (!desc->hw.subgroups) + return; + + for (i = 0; i < desc->hw.nr_subgroups; i++) + intc_subgroup_init_one(desc, d, desc->hw.subgroups + i); +} + +static void __init intc_subgroup_map(struct intc_desc_int *d) +{ + struct intc_subgroup_entry *entries[32]; + unsigned long flags; + unsigned int nr_found; + int i; + + raw_spin_lock_irqsave(&d->lock, flags); + +restart: + nr_found = radix_tree_gang_lookup_tag_slot(&d->tree, + (void ***)entries, 0, ARRAY_SIZE(entries), + INTC_TAG_VIRQ_NEEDS_ALLOC); + + for (i = 0; i < nr_found; i++) { + struct intc_subgroup_entry *entry; + int irq; + + entry = radix_tree_deref_slot((void **)entries[i]); + if (unlikely(!entry)) + continue; + if (unlikely(entry == RADIX_TREE_RETRY)) + goto restart; + + irq = create_irq(); + if (unlikely(irq < 0)) { + pr_err("no more free IRQs, bailing..\n"); + break; + } + + pr_info("Setting up a chained VIRQ from %d -> %d\n", + irq, entry->pirq); + + intc_irq_xlate_set(irq, entry->enum_id, d); + + set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq), + handle_simple_irq, "virq"); + set_irq_chip_data(irq, get_irq_chip_data(entry->pirq)); + + set_irq_data(irq, (void *)entry->handle); + + set_irq_chained_handler(entry->pirq, intc_virq_handler); + add_virq_to_pirq(entry->pirq, irq); + + radix_tree_tag_clear(&d->tree, entry->enum_id, + INTC_TAG_VIRQ_NEEDS_ALLOC); + radix_tree_replace_slot((void **)entries[i], + &intc_irq_xlate[irq]); + } + + raw_spin_unlock_irqrestore(&d->lock, flags); +} + +void __init intc_finalize(void) +{ + struct intc_desc_int *d; + + list_for_each_entry(d, &intc_list, list) + if (radix_tree_tagged(&d->tree, INTC_TAG_VIRQ_NEEDS_ALLOC)) + intc_subgroup_map(d); +} diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index cf0303acab8..75934e3ea34 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -7,6 +7,8 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/errno.h> #include <linux/kernel.h> #include <linux/list.h> @@ -559,10 +561,8 @@ static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) struct pinmux_data_reg *dr = NULL; int bit = 0; - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) { - BUG(); - return 0; - } + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) + return -EINVAL; return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); } @@ -581,7 +581,7 @@ int register_pinmux(struct pinmux_info *pip) { struct gpio_chip *chip = &pip->chip; - pr_info("sh pinmux: %s handling gpio %d -> %d\n", + pr_info("%s handling gpio %d -> %d\n", pip->name, pip->first_gpio, pip->last_gpio); setup_data_regs(pip); @@ -602,3 +602,10 @@ int register_pinmux(struct pinmux_info *pip) return gpiochip_add(chip); } + +int unregister_pinmux(struct pinmux_info *pip) +{ + pr_info("%s deregistering\n", pip->name); + + return gpiochip_remove(&pip->chip); +} diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 335311a98fd..8e03e760023 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -139,8 +139,6 @@ source "drivers/staging/adis16255/Kconfig" source "drivers/staging/xgifb/Kconfig" -source "drivers/staging/mrst-touchscreen/Kconfig" - source "drivers/staging/msm/Kconfig" source "drivers/staging/lirc/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e3f1e1b6095..0e7d7559d37 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -51,7 +51,6 @@ obj-$(CONFIG_CXT1E1) += cxt1e1/ obj-$(CONFIG_TI_ST) += ti-st/ obj-$(CONFIG_ADIS16255) += adis16255/ obj-$(CONFIG_FB_XGI) += xgifb/ -obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/ obj-$(CONFIG_MSM_STAGING) += msm/ obj-$(CONFIG_EASYCAP) += easycap/ obj-$(CONFIG_SOLO6X10) += solo6x10/ diff --git a/drivers/staging/mrst-touchscreen/Kconfig b/drivers/staging/mrst-touchscreen/Kconfig deleted file mode 100644 index c2af4921708..00000000000 --- a/drivers/staging/mrst-touchscreen/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config TOUCHSCREEN_INTEL_MID - tristate "Intel MID platform resistive touchscreen" - depends on INTEL_SCU_IPC - default y - help - Say Y here if you have a Intel MID based touchscreen - If unsure, say N. diff --git a/drivers/staging/mrst-touchscreen/Makefile b/drivers/staging/mrst-touchscreen/Makefile deleted file mode 100644 index 2d638b0d70b..00000000000 --- a/drivers/staging/mrst-touchscreen/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) := intel_mid_touch.o - - diff --git a/drivers/staging/mrst-touchscreen/TODO b/drivers/staging/mrst-touchscreen/TODO deleted file mode 100644 index 7157028d634..00000000000 --- a/drivers/staging/mrst-touchscreen/TODO +++ /dev/null @@ -1,2 +0,0 @@ -- Move the driver to not think it is SPI (requires fixing some of the SFI - and firmware side) diff --git a/drivers/staging/mrst-touchscreen/intel-mid-touch.c b/drivers/staging/mrst-touchscreen/intel-mid-touch.c deleted file mode 100644 index abba22f921b..00000000000 --- a/drivers/staging/mrst-touchscreen/intel-mid-touch.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * intel_mid_touch.c - Intel MID Resistive Touch Screen Driver - * - * Copyright (C) 2008 Intel Corp - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; ifnot, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) - * Ramesh Agarwal (ramesh.agarwal@intel.com) - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * TODO: - * kill off mrstouch_debug eventually - * review conversion of r/m/w sequences - * Replace interrupt mutex abuse - * Kill of mrstouchdevp pointer - * - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/err.h> -#include <linux/param.h> -#include <linux/spi/spi.h> -#include <linux/irq.h> -#include <linux/delay.h> -#include <linux/kthread.h> -#include <asm/intel_scu_ipc.h> - - -#if defined(MRSTOUCH_DEBUG) -#define mrstouch_debug(fmt, args...)\ - do { \ - printk(KERN_DEBUG "\n[MRSTOUCH(%d)] - ", __LINE__); \ - printk(KERN_DEBUG fmt, ##args); \ - } while (0); -#else -#define mrstouch_debug(fmt, args...) -#endif - -/* PMIC Interrupt registers */ -#define PMIC_REG_ID1 0x00 /*PMIC ID1 register */ - -/* PMIC Interrupt registers */ -#define PMIC_REG_INT 0x04 /*PMIC interrupt register */ -#define PMIC_REG_MINT 0x05 /*PMIC interrupt mask register */ - -/* ADC Interrupt registers */ -#define PMIC_REG_ADCINT 0x5F /*ADC interrupt register */ -#define PMIC_REG_MADCINT 0x60 /*ADC interrupt mask register */ - -/* ADC Control registers */ -#define PMIC_REG_ADCCNTL1 0x61 /*ADC control register */ - -/* ADC Channel Selection registers */ -#define PMICADDR0 0xA4 -#define END_OF_CHANNEL 0x1F - -/* ADC Result register */ -#define PMIC_REG_ADCSNS0H 0x64 - -/* ADC channels for touch screen */ -#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ -#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ -#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ -#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ - -/* Touch screen coordinate constants */ -#define TOUCH_PRESSURE 50 -#define TOUCH_PRESSURE_FS 100 - -#define XMOVE_LIMIT 5 -#define YMOVE_LIMIT 5 -#define XYMOVE_CNT 3 - -#define MAX_10BIT ((1<<10)-1) - -/* Touch screen channel BIAS constants */ -#define XBIAS 0x20 -#define YBIAS 0x40 -#define ZBIAS 0x80 - -/* Touch screen coordinates */ -#define MIN_X 10 -#define MAX_X 1024 -#define MIN_Y 10 -#define MAX_Y 1024 -#define WAIT_ADC_COMPLETION 10 - -/* PMIC ADC round robin delays */ -#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ -#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ - -/* PMIC Vendor Identifiers */ -#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ -#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ -#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ -#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ - -/* Touch screen device structure */ -struct mrstouch_dev { - struct spi_device *spi; /* SPI device associated with touch screen */ - struct input_dev *input; /* input device for touchscreen*/ - char phys[32]; /* Device name */ - struct task_struct *pendet_thrd; /* PENDET interrupt handler */ - struct mutex lock; /* Sync between interrupt and PENDET handler */ - bool busy; /* Busy flag */ - u16 asr; /* Address selection register */ - int irq; /* Touch screen IRQ # */ - uint vendor; /* PMIC vendor */ - uint rev; /* PMIC revision */ - bool suspended; /* Device suspended status */ - bool disabled; /* Device disabled status */ - u16 x; /* X coordinate */ - u16 y; /* Y coordinate */ - bool pendown; /* PEN position */ -} ; - - -/* Global Pointer to Touch screen device */ -static struct mrstouch_dev *mrstouchdevp; - -/* Utility to read PMIC ID */ -static int mrstouch_pmic_id(uint *vendor, uint *rev) -{ - int err; - u8 r; - - err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); - if (err) - return err; - - *vendor = r & 0x7; - *rev = (r >> 3) & 0x7; - - return 0; -} - -/* - * Parse ADC channels to find end of the channel configured by other ADC user - * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels - */ -static int mrstouch_chan_parse(struct mrstouch_dev *tsdev) -{ - int err, i, j, found; - u32 r32; - - found = -1; - - for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { - if (found >= 0) - break; - - err = intel_scu_ipc_ioread32(PMICADDR0, &r32); - if (err) - return err; - - for (j = 0; j < 32; j+= 8) { - if (((r32 >> j) & 0xFF) == END_OF_CHANNEL) { - found = i; - break; - } - } - } - if (found < 0) - return 0; - - if (tsdev->vendor == PMIC_VENDOR_FS) { - if (found && found > (MRSTOUCH_MAX_CHANNELS - 18)) - return -ENOSPC; - } else { - if (found && found > (MRSTOUCH_MAX_CHANNELS - 4)) - return -ENOSPC; - } - return found; -} - -/* Utility to enable/disable pendet. - * pendet set to true enables PENDET interrupt - * pendet set to false disables PENDET interrupt - * Also clears RND mask bit -*/ -static int pendet_enable(struct mrstouch_dev *tsdev, bool pendet) -{ - u16 reg; - u8 r; - u8 pendet_enabled = 0; - int retry = 0; - int err; - - err = intel_scu_ipc_ioread16(PMIC_REG_MADCINT, ®); - if (err) - return err; - - if (pendet) { - reg &= ~0x0005; - reg |= 0x2000; /* Enable pendet */ - } else - reg &= 0xDFFF; /* Disable pendet */ - - /* Set MADCINT and update ADCCNTL1 (next reg byte) */ - err = intel_scu_ipc_iowrite16(PMIC_REG_MADCINT, reg); - if (!pendet || err) - return err; - - /* - * Sometimes even after the register write succeeds - * the PMIC register value is not updated. Retry few iterations - * to enable pendet. - */ - - err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r); - pendet_enabled = (r >> 5) & 0x01; - - retry = 0; - while (!err && !pendet_enabled) { - retry++; - msleep(10); - err = intel_scu_ipc_iowrite8(PMIC_REG_ADCCNTL1, reg >> 8); - if (err) - break; - err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r); - if (err == 0) - pendet_enabled = (r >> 5) & 0x01; - if (retry >= 10) { - dev_err(&tsdev->spi->dev, "Touch screen disabled.\n"); - return -EIO; - } - } - return 0; -} - -/* To read PMIC ADC touch screen result - * Reads ADC storage registers for higher 7 and lower 3 bits - * converts the two readings to single value and turns off gain bit - */ -static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) -{ - int err; - u16 result; - u32 res; - - result = PMIC_REG_ADCSNS0H + offset; - - if (chan == MRST_TS_CHAN12) - result += 4; - - err = intel_scu_ipc_ioread32(result, &res); - if (err) - return err; - - /* Mash the bits up */ - - *vp = (res & 0xFF) << 3; /* Highest 7 bits */ - *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ - *vp &= 0x3FF; - - res >>= 16; - - *vm = (res & 0xFF) << 3; /* Highest 7 bits */ - *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ - *vm &= 0x3FF; - - return 0; -} - -/* To configure touch screen channels - * Writes touch screen channels to ADC address selection registers - */ -static int mrstouch_ts_chan_set(uint offset) -{ - int count; - u16 chan; - u16 reg[5]; - u8 data[5]; - - chan = PMICADDR0 + offset; - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = MRST_TS_CHAN10 + count; - } - reg[count] = chan; - data[count] = END_OF_CHANNEL; - - return intel_scu_ipc_writev(reg, data, 5); -} - -/* Initialize ADC */ -static int mrstouch_adc_init(struct mrstouch_dev *tsdev) -{ - int err, start; - u8 ra, rm; - - err = mrstouch_pmic_id(&tsdev->vendor, &tsdev->rev); - if (err) { - dev_err(&tsdev->spi->dev, "Unable to read PMIC id\n"); - return err; - } - - start = mrstouch_chan_parse(tsdev); - if (start < 0) { - dev_err(&tsdev->spi->dev, "Unable to parse channels\n"); - return start; - } - - tsdev->asr = start; - - mrstouch_debug("Channel offset(%d): 0x%X\n", tsdev->asr, tsdev->vendor); - - /* ADC power on, start, enable PENDET and set loop delay - * ADC loop delay is set to 4.5 ms approximately - * Loop delay more than this results in jitter in adc readings - * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET - * interrupt generation sometimes. - */ - - if (tsdev->vendor == PMIC_VENDOR_FS) { - ra = 0xE0 | ADC_LOOP_DELAY0; - rm = 0x5; - } else { - /* NEC and MAXIm not consistent with loop delay 0 */ - ra = 0xE0 | ADC_LOOP_DELAY1; - rm = 0x0; - - /* configure touch screen channels */ - err = mrstouch_ts_chan_set(tsdev->asr); - if (err) - return err; - } - err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); - if (err == 0) - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); - return err; -} - -/* Reports x,y coordinates to event subsystem */ -static void mrstouch_report_xy(struct mrstouch_dev *tsdev, u16 x, u16 y, u16 z) -{ - int xdiff, ydiff; - - if (tsdev->pendown && z <= TOUCH_PRESSURE) { - /* Pen removed, report button release */ - mrstouch_debug("BTN REL(%d)", z); - input_report_key(tsdev->input, BTN_TOUCH, 0); - tsdev->pendown = false; - } - - xdiff = abs(x - tsdev->x); - ydiff = abs(y - tsdev->y); - - /* - if x and y values changes for XYMOVE_CNT readings it is considered - as stylus is moving. This is required to differentiate between stylus - movement and jitter - */ - if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) { - /* Spurious values, release button if touched and return */ - if (tsdev->pendown) { - mrstouch_debug("BTN REL(%d)", z); - input_report_key(tsdev->input, BTN_TOUCH, 0); - tsdev->pendown = false; - } - return; - } else if (xdiff >= XMOVE_LIMIT || ydiff >= YMOVE_LIMIT) { - tsdev->x = x; - tsdev->y = y; - - input_report_abs(tsdev->input, ABS_X, x); - input_report_abs(tsdev->input, ABS_Y, y); - input_sync(tsdev->input); - } - - - if (!tsdev->pendown && z > TOUCH_PRESSURE) { - /* Pen touched, report button touch */ - mrstouch_debug("BTN TCH(%d, %d, %d)", x, y, z); - input_report_key(tsdev->input, BTN_TOUCH, 1); - tsdev->pendown = true; - } -} - - -/* Utility to start ADC, used by freescale handler */ -static int pendet_mask(void) -{ - return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); -} - -/* Utility to stop ADC, used by freescale handler */ -static int pendet_umask(void) -{ - return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); -} - -/* Utility to read ADC, used by freescale handler */ -static int mrstouch_pmic_fs_adc_read(struct mrstouch_dev *tsdev) -{ - int err; - u16 x, y, z, result; - u16 reg[4]; - u8 data[4]; - - result = PMIC_REG_ADCSNS0H + tsdev->asr; - - reg[0] = result + 4; - reg[1] = result + 5; - reg[2] = result + 16; - reg[3] = result + 17; - - err = intel_scu_ipc_readv(reg, data, 4); - if (err) - goto ipc_error; - - x = data[0] << 3; /* Higher 7 bits */ - x |= data[1] & 0x7; /* Lower 3 bits */ - x &= 0x3FF; - - y = data[2] << 3; /* Higher 7 bits */ - y |= data[3] & 0x7; /* Lower 3 bits */ - y &= 0x3FF; - - /* Read Z value */ - reg[0] = result + 28; - reg[1] = result + 29; - - err = intel_scu_ipc_readv(reg, data, 4); - if (err) - goto ipc_error; - - z = data[0] << 3; /* Higher 7 bits */ - z |= data[1] & 0x7; /* Lower 3 bits */ - z &= 0x3FF; - -#if defined(MRSTOUCH_PRINT_XYZP) - mrstouch_debug("X: %d, Y: %d, Z: %d", x, y, z); -#endif - - if (z >= TOUCH_PRESSURE_FS) { - mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE - 1); /* Pen Removed */ - return TOUCH_PRESSURE - 1; - } else { - mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE + 1); /* Pen Touched */ - return TOUCH_PRESSURE + 1; - } - - return 0; - -ipc_error: - dev_err(&tsdev->spi->dev, "ipc error during fs_adc read\n"); - return err; -} - -/* To handle free scale pmic pendet interrupt */ -static int pmic0_pendet(void *dev_id) -{ - int err, count; - u16 chan; - unsigned int touched; - struct mrstouch_dev *tsdev = (struct mrstouch_dev *)dev_id; - u16 reg[5]; - u8 data[5]; - - chan = PMICADDR0 + tsdev->asr; - - /* Set X BIAS */ - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = 0x2A; - } - reg[count] = chan++; /* Dummy */ - data[count] = 0; - - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* Set Y BIAS */ - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = 0x4A; - } - reg[count] = chan++; /* Dummy */ - data[count] = 0; - - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* Set Z BIAS */ - err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /*Read touch screen channels till pen removed - * Freescale reports constant value of z for all points - * z is high when screen is not touched and low when touched - * Map high z value to not touched and low z value to pen touched - */ - touched = mrstouch_pmic_fs_adc_read(tsdev); - while (touched > TOUCH_PRESSURE) { - touched = mrstouch_pmic_fs_adc_read(tsdev); - msleep(WAIT_ADC_COMPLETION); - } - - /* Clear all TS channels */ - chan = PMICADDR0 + tsdev->asr; - for (count = 0; count <= 4; count++) { - reg[count] = chan++; - data[count] = 0; - } - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - for (count = 0; count <= 4; count++) { - reg[count] = chan++; - data[count] = 0; - } - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); - if (err) - goto ipc_error; - - return 0; - -ipc_error: - dev_err(&tsdev->spi->dev, "ipc error during pendet\n"); - return err; -} - - -/* To enable X, Y and Z bias values - * Enables YPYM for X channels and XPXM for Y channels - */ -static int mrstouch_ts_bias_set(uint offset, uint bias) -{ - int count; - u16 chan, start; - u16 reg[4]; - u8 data[4]; - - chan = PMICADDR0 + offset; - start = MRST_TS_CHAN10; - - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = bias | (start + count); - } - return intel_scu_ipc_writev(reg, data, 4); -} - -/* To read touch screen channel values */ -static int mrstouch_adc_read(struct mrstouch_dev *tsdev) -{ - int err; - u16 xp, xm, yp, ym, zp, zm; - - /* configure Y bias for X channels */ - err = mrstouch_ts_bias_set(tsdev->asr, YBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read x+ and x- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &xp, &xm); - if (err) - goto ipc_error; - - /* configure x bias for y channels */ - err = mrstouch_ts_bias_set(tsdev->asr, XBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read y+ and y- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, &yp, &ym); - if (err) - goto ipc_error; - - /* configure z bias for x and y channels */ - err = mrstouch_ts_bias_set(tsdev->asr, ZBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read z+ and z- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &zp, &zm); - if (err) - goto ipc_error; - -#if defined(MRSTOUCH_PRINT_XYZP) - printk(KERN_INFO "X+: %d, Y+: %d, Z+: %d\n", xp, yp, zp); -#endif - -#if defined(MRSTOUCH_PRINT_XYZM) - printk(KERN_INFO "X-: %d, Y-: %d, Z-: %d\n", xm, ym, zm); -#endif - - mrstouch_report_xy(tsdev, xp, yp, zp); /* report x and y to eventX */ - - return zp; - -ipc_error: - dev_err(&tsdev->spi->dev, "ipc error during adc read\n"); - return err; -} - -/* PENDET interrupt handler function for NEC and MAXIM */ -static void pmic12_pendet(void *data) -{ - unsigned int touched; - struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data; - - /* read touch screen channels till pen removed */ - do { - touched = mrstouch_adc_read(tsdev); - } while (touched > TOUCH_PRESSURE); -} - -/* Handler to process PENDET interrupt */ -int mrstouch_pendet(void *data) -{ - struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data; - while (1) { - /* Wait for PENDET interrupt */ - if (mutex_lock_interruptible(&tsdev->lock)) { - msleep(WAIT_ADC_COMPLETION); - continue; - } - - if (tsdev->busy) - return 0; - - tsdev->busy = true; - - if (tsdev->vendor == PMIC_VENDOR_NEC || - tsdev->vendor == PMIC_VENDOR_MAXIM) { - /* PENDET must be disabled in NEC before reading ADC */ - pendet_enable(tsdev,false); /* Disbale PENDET */ - pmic12_pendet(tsdev); - pendet_enable(tsdev, true); /*Enable PENDET */ - } else if (tsdev->vendor == PMIC_VENDOR_FS) { - pendet_umask(); /* Stop ADC */ - pmic0_pendet(tsdev); - pendet_mask(); /* Stop ADC */ - } else - dev_err(&tsdev->spi->dev, "Unsupported touchscreen: %d\n", - tsdev->vendor); - - tsdev->busy = false; - - } - return 0; -} - -/* PENDET interrupt handler */ -static irqreturn_t pendet_intr_handler(int irq, void *handle) -{ - struct mrstouch_dev *tsdev = (struct mrstouch_dev *)handle; - - mutex_unlock(&tsdev->lock); - return IRQ_HANDLED; -} - -/* Intializes input device and registers with input subsystem */ -static int ts_input_dev_init(struct mrstouch_dev *tsdev, struct spi_device *spi) -{ - int err = 0; - - mrstouch_debug("%s", __func__); - - tsdev->input = input_allocate_device(); - if (!tsdev->input) { - dev_err(&tsdev->spi->dev, "Unable to allocate input device.\n"); - return -EINVAL; - } - - tsdev->input->name = "mrst_touchscreen"; - snprintf(tsdev->phys, sizeof(tsdev->phys), - "%s/input0", dev_name(&spi->dev)); - tsdev->input->phys = tsdev->phys; - tsdev->input->dev.parent = &spi->dev; - - tsdev->input->id.vendor = tsdev->vendor; - tsdev->input->id.version = tsdev->rev; - - tsdev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - tsdev->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(tsdev->input, ABS_X, MIN_X, MIN_Y, 0, 0); - input_set_abs_params(tsdev->input, ABS_Y, MIN_X, MIN_Y, 0, 0); - - err = input_register_device(tsdev->input); - if (err) { - dev_err(&tsdev->spi->dev, "unable to register input device\n"); - input_free_device(tsdev->input); - return err; - } - - mrstouch_debug("%s", "mrstouch initialized"); - - return 0; - -} - -/* Probe function for touch screen driver */ -static int __devinit mrstouch_probe(struct spi_device *mrstouch_spi) -{ - int err; - unsigned int myirq; - struct mrstouch_dev *tsdev; - - mrstouch_debug("%s(%p)", __func__, mrstouch_spi); - - mrstouchdevp = NULL; - myirq = mrstouch_spi->irq; - - if (!mrstouch_spi->irq) { - dev_err(&mrstouch_spi->dev, "no interrupt assigned\n"); - return -EINVAL; - } - - tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); - if (!tsdev) { - dev_err(&mrstouch_spi->dev, "unable to allocate memory\n"); - return -ENOMEM; - } - - tsdev->irq = myirq; - mrstouchdevp = tsdev; - - err = mrstouch_adc_init(tsdev); - if (err) { - dev_err(&mrstouch_spi->dev, "ADC init failed\n"); - goto mrstouch_err_free_mem; - } - - dev_set_drvdata(&mrstouch_spi->dev, tsdev); - tsdev->spi = mrstouch_spi; - - err = ts_input_dev_init(tsdev, mrstouch_spi); - if (err) { - dev_err(&tsdev->spi->dev, "ts_input_dev_init failed"); - goto mrstouch_err_free_mem; - } - - mutex_init(&tsdev->lock); - mutex_lock(&tsdev->lock) - - mrstouch_debug("Requesting IRQ-%d", myirq); - err = request_irq(myirq, pendet_intr_handler, - 0, "mrstouch", tsdev); - if (err) { - dev_err(&tsdev->spi->dev, "unable to allocate irq\n"); - goto mrstouch_err_free_mem; - } - - tsdev->pendet_thrd = kthread_run(mrstouch_pendet, - (void *)tsdev, "pendet handler"); - if (IS_ERR(tsdev->pendet_thrd)) { - dev_err(&tsdev->spi->dev, "kthread_run failed\n"); - err = PTR_ERR(tsdev->pendet_thrd); - goto mrstouch_err_free_mem; - } - mrstouch_debug("%s", "Driver initialized"); - return 0; - -mrstouch_err_free_mem: - kfree(tsdev); - return err; -} - -static int mrstouch_suspend(struct spi_device *spi, pm_message_t msg) -{ - mrstouch_debug("%s", __func__); - mrstouchdevp->suspended = 1; - return 0; -} - -static int mrstouch_resume(struct spi_device *spi) -{ - mrstouch_debug("%s", __func__); - mrstouchdevp->suspended = 0; - return 0; -} - -static int mrstouch_remove(struct spi_device *spi) -{ - mrstouch_debug("%s", __func__); - free_irq(mrstouchdevp->irq, mrstouchdevp); - input_unregister_device(mrstouchdevp->input); - input_free_device(mrstouchdevp->input); - if (mrstouchdevp->pendet_thrd) - kthread_stop(mrstouchdevp->pendet_thrd); - kfree(mrstouchdevp); - return 0; -} - -static struct spi_driver mrstouch_driver = { - .driver = { - .name = "pmic_touch", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = mrstouch_probe, - .suspend = mrstouch_suspend, - .resume = mrstouch_resume, - .remove = mrstouch_remove, -}; - -static int __init mrstouch_module_init(void) -{ - int err; - - mrstouch_debug("%s", __func__); - err = spi_register_driver(&mrstouch_driver); - if (err) { - mrstouch_debug("%s(%d)", "SPI PENDET failed", err); - return -1; - } - - return 0; -} - -static void __exit mrstouch_module_exit(void) -{ - mrstouch_debug("%s", __func__); - spi_unregister_driver(&mrstouch_driver); - return; -} - -module_init(mrstouch_module_init); -module_exit(mrstouch_module_exit); - -MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); -MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/xgifb/TODO b/drivers/staging/xgifb/TODO index 7d71019b84c..c85ff5e9e70 100644 --- a/drivers/staging/xgifb/TODO +++ b/drivers/staging/xgifb/TODO @@ -12,4 +12,4 @@ TODO: - get rid of non-linux related stuff Please send patches to: -Arnaud Patard <apatard@mandriva.com> +Arnaud Patard <arnaud.patard@rtp-net.org> diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 61d3ca6619b..cb5cd422f3f 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -54,7 +54,6 @@ #include <plat/dma.h> #include <plat/usb.h> -#include <plat/control.h> #include "omap_udc.h" @@ -2309,21 +2308,12 @@ static char *trx_mode(unsigned m, int enabled) static int proc_otg_show(struct seq_file *s) { u32 tmp; - u32 trans; - char *ctrl_name; + u32 trans = 0; + char *ctrl_name = "(UNKNOWN)"; + /* XXX This needs major revision for OMAP2+ */ tmp = omap_readl(OTG_REV); - if (cpu_is_omap24xx()) { - /* - * REVISIT: Not clear how this works on OMAP2. trans - * is ANDed to produce bits 7 and 8, which might make - * sense for USB_TRANSCEIVER_CTRL on OMAP1, - * but with CONTROL_DEVCONF, these bits have something to - * do with the frame adjustment counter and McBSP2. - */ - ctrl_name = "control_devconf"; - trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); - } else { + if (cpu_class_is_omap1()) { ctrl_name = "tranceiver_ctrl"; trans = omap_readw(USB_TRANSCEIVER_CTRL); } diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 77be3c24a42..3076b1cc05d 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2397,7 +2397,7 @@ static const struct dev_pm_ops r8a66597_dev_pm_ops = { #define R8A66597_DEV_PM_OPS NULL #endif -static int __init_or_module r8a66597_remove(struct platform_device *pdev) +static int __devexit r8a66597_remove(struct platform_device *pdev) { struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); @@ -2542,7 +2542,7 @@ clean_up: static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove = r8a66597_remove, + .remove = __devexit_p(r8a66597_remove), .driver = { .name = (char *) hcd_name, .owner = THIS_MODULE, diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig index bac8e7a6f17..d100f54ed65 100644 --- a/drivers/uwb/Kconfig +++ b/drivers/uwb/Kconfig @@ -12,8 +12,7 @@ menuconfig UWB technology using a wide spectrum (3.1-10.6GHz). It is optimized for in-room use (480Mbps at 2 meters, 110Mbps at 10m). It serves as the transport layer for other protocols, - such as Wireless USB (WUSB), IP (WLP) and upcoming - Bluetooth and 1394 + such as Wireless USB (WUSB). The topology is peer to peer; however, higher level protocols (such as WUSB) might impose a master/slave @@ -58,13 +57,6 @@ config UWB_WHCI To compile this driver select Y (built in) or M (module). It is safe to select any even if you do not have the hardware. -config UWB_WLP - tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)" - depends on UWB && NET - help - This is a common library for drivers that implement - networking over UWB. - config UWB_I1480U tristate "Support for Intel Wireless UWB Link 1480 HWA" depends on UWB_HWA @@ -77,14 +69,4 @@ config UWB_I1480U To compile this driver select Y (built in) or M (module). It is safe to select any even if you do not have the hardware. -config UWB_I1480U_WLP - tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface" - depends on UWB_I1480U && UWB_WLP && NET - help - This driver enables WLP support for the i1480 when connected via - USB. WLP is the WiMedia Link Protocol, or IP over UWB. - - To compile this driver select Y (built in) or M (module). It - is safe to select any even if you don't have the hardware. - endif # UWB diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile index 2f98d080fe7..d47dd6e2942 100644 --- a/drivers/uwb/Makefile +++ b/drivers/uwb/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_UWB) += uwb.o -obj-$(CONFIG_UWB_WLP) += wlp/ obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o obj-$(CONFIG_UWB_HWA) += hwa-rc.o obj-$(CONFIG_UWB_I1480U) += i1480/ diff --git a/drivers/uwb/i1480/Makefile b/drivers/uwb/i1480/Makefile index 212bbc7d4c3..d69da1684cf 100644 --- a/drivers/uwb/i1480/Makefile +++ b/drivers/uwb/i1480/Makefile @@ -1,2 +1 @@ obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o -obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/ diff --git a/drivers/uwb/i1480/i1480-wlp.h b/drivers/uwb/i1480/i1480-wlp.h deleted file mode 100644 index 18a8b0e4567..00000000000 --- a/drivers/uwb/i1480/i1480-wlp.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Intel 1480 Wireless UWB Link - * WLP specific definitions - * - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - */ - -#ifndef __i1480_wlp_h__ -#define __i1480_wlp_h__ - -#include <linux/spinlock.h> -#include <linux/list.h> -#include <linux/uwb.h> -#include <linux/if_ether.h> -#include <asm/byteorder.h> - -/* New simplified header format? */ -#undef WLP_HDR_FMT_2 /* FIXME: rename */ - -/** - * Values of the Delivery ID & Type field when PCA or DRP - * - * The Delivery ID & Type field in the WLP TX header indicates whether - * the frame is PCA or DRP. This is done based on the high level bit of - * this field. - * We use this constant to test if the traffic is PCA or DRP as follows: - * if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP) - * this is DRP traffic - * else - * this is PCA traffic - */ -enum deliver_id_type_bit { - WLP_DRP = 8, -}; - -/** - * WLP TX header - * - * Indicates UWB/WLP-specific transmission parameters for a network - * packet. - */ -struct wlp_tx_hdr { - /* dword 0 */ - struct uwb_dev_addr dstaddr; - u8 key_index; - u8 mac_params; - /* dword 1 */ - u8 phy_params; -#ifndef WLP_HDR_FMT_2 - u8 reserved; - __le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */ - /* dword 2 */ - u8 oui2; /* if all LE, it could be merged */ - __le16 prid; -#endif -} __attribute__((packed)); - -static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr) -{ - return hdr->mac_params & 0x0f; -} - -static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr) -{ - return (hdr->mac_params >> 4) & 0x07; -} - -static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr) -{ - return (hdr->mac_params >> 7) & 0x01; -} - -static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id) -{ - hdr->mac_params = (hdr->mac_params & ~0x0f) | id; -} - -static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr, - enum uwb_ack_pol policy) -{ - hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4); -} - -static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts) -{ - hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7); -} - -static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr) -{ - return hdr->phy_params & 0x0f; -} - -static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr) -{ - return (hdr->phy_params >> 4) & 0x0f; -} - -static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate) -{ - hdr->phy_params = (hdr->phy_params & ~0x0f) | rate; -} - -static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr) -{ - hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4); -} - - -/** - * WLP RX header - * - * Provides UWB/WLP-specific transmission data for a received - * network packet. - */ -struct wlp_rx_hdr { - /* dword 0 */ - struct uwb_dev_addr dstaddr; - struct uwb_dev_addr srcaddr; - /* dword 1 */ - u8 LQI; - s8 RSSI; - u8 reserved3; -#ifndef WLP_HDR_FMT_2 - u8 oui0; - /* dword 2 */ - __le16 oui12; - __le16 prid; -#endif -} __attribute__((packed)); - - -/** User configurable options for WLP */ -struct wlp_options { - struct mutex mutex; /* access to user configurable options*/ - struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */ - u8 pca_base_priority; - u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/ -}; - - -static inline -void wlp_options_init(struct wlp_options *options) -{ - mutex_init(&options->mutex); - wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM); - wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1); - /* FIXME: default to phy caps */ - wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480); -#ifndef WLP_HDR_FMT_2 - options->def_tx_hdr.prid = cpu_to_le16(0x0000); -#endif -} - - -/* sysfs helpers */ - -extern ssize_t uwb_pca_base_priority_store(struct wlp_options *, - const char *, size_t); -extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *); -extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t); -extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *); -extern ssize_t uwb_ack_policy_store(struct wlp_options *, - const char *, size_t); -extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *); -extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t); -extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *); -extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t); -extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *); - - -/** Simple bandwidth allocation (temporary and too simple) */ -struct wlp_bw_allocs { - const char *name; - struct { - u8 mask, stream; - } tx, rx; -}; - - -#endif /* #ifndef __i1480_wlp_h__ */ diff --git a/drivers/uwb/i1480/i1480u-wlp/Makefile b/drivers/uwb/i1480/i1480u-wlp/Makefile deleted file mode 100644 index fe6709b8e68..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o - -i1480u-wlp-objs := \ - lc.o \ - netdev.o \ - rx.o \ - sysfs.o \ - tx.o diff --git a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h deleted file mode 100644 index 2e31f536a34..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Intel 1480 Wireless UWB Link USB - * Header formats, constants, general internal interfaces - * - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * This is not an standard interface. - * - * FIXME: docs - * - * i1480u-wlp is pretty simple: two endpoints, one for tx, one for - * rx. rx is polled. Network packets (ethernet, whatever) are wrapped - * in i1480 TX or RX headers (for sending over the air), and these - * packets are wrapped in UNTD headers (for sending to the WLP UWB - * controller). - * - * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets - * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the - * i1480 packet is broken in chunks/packets: - * - * UNTD-1st.hdr + i1480.hdr + payload - * UNTD-next.hdr + payload - * ... - * UNTD-last.hdr + payload - * - * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE. - * - * All HW structures and bitmaps are little endian, so we need to play - * ugly tricks when defining bitfields. Hoping for the day GCC - * implements __attribute__((endian(1234))). - * - * FIXME: ROADMAP to the whole implementation - */ - -#ifndef __i1480u_wlp_h__ -#define __i1480u_wlp_h__ - -#include <linux/usb.h> -#include <linux/netdevice.h> -#include <linux/uwb.h> /* struct uwb_rc, struct uwb_notifs_handler */ -#include <linux/wlp.h> -#include "../i1480-wlp.h" - -#undef i1480u_FLOW_CONTROL /* Enable flow control code */ - -/** - * Basic flow control - */ -enum { - i1480u_TX_INFLIGHT_MAX = 1000, - i1480u_TX_INFLIGHT_THRESHOLD = 100, -}; - -/** Maximum size of a transaction that we can tx/rx */ -enum { - /* Maximum packet size computed as follows: max UNTD header (8) + - * i1480 RX header (8) + max Ethernet header and payload (4096) + - * Padding added by skb_reserve (2) to make post Ethernet payload - * start on 16 byte boundary*/ - i1480u_MAX_RX_PKT_SIZE = 4114, - i1480u_MAX_FRG_SIZE = 512, - i1480u_RX_BUFS = 9, -}; - - -/** - * UNTD packet type - * - * We need to fragment any payload whose UNTD packet is going to be - * bigger than i1480u_MAX_FRG_SIZE. - */ -enum i1480u_pkt_type { - i1480u_PKT_FRAG_1ST = 0x1, - i1480u_PKT_FRAG_NXT = 0x0, - i1480u_PKT_FRAG_LST = 0x2, - i1480u_PKT_FRAG_CMP = 0x3 -}; -enum { - i1480u_PKT_NONE = 0x4, -}; - -/** USB Network Transfer Descriptor - common */ -struct untd_hdr { - u8 type; - __le16 len; -} __attribute__((packed)); - -static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr) -{ - return hdr->type & 0x03; -} - -static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr) -{ - return (hdr->type >> 2) & 0x01; -} - -static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type) -{ - hdr->type = (hdr->type & ~0x03) | type; -} - -static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx) -{ - hdr->type = (hdr->type & ~0x04) | (rx_tx << 2); -} - - -/** - * USB Network Transfer Descriptor - Complete Packet - * - * This is for a packet that is smaller (header + payload) than - * i1480u_MAX_FRG_SIZE. - * - * @hdr.total_len is the size of the payload; the payload doesn't - * count this header nor the padding, but includes the size of i1480 - * header. - */ -struct untd_hdr_cmp { - struct untd_hdr hdr; - u8 padding; -} __attribute__((packed)); - - -/** - * USB Network Transfer Descriptor - First fragment - * - * @hdr.len is the size of the *whole packet* (excluding UNTD - * headers); @fragment_len is the size of the payload (excluding UNTD - * headers, but including i1480 headers). - */ -struct untd_hdr_1st { - struct untd_hdr hdr; - __le16 fragment_len; - u8 padding[3]; -} __attribute__((packed)); - - -/** - * USB Network Transfer Descriptor - Next / Last [Rest] - * - * @hdr.len is the size of the payload, not including headrs. - */ -struct untd_hdr_rst { - struct untd_hdr hdr; - u8 padding; -} __attribute__((packed)); - - -/** - * Transmission context - * - * Wraps all the stuff needed to track a pending/active tx - * operation. - */ -struct i1480u_tx { - struct list_head list_node; - struct i1480u *i1480u; - struct urb *urb; - - struct sk_buff *skb; - struct wlp_tx_hdr *wlp_tx_hdr; - - void *buf; /* if NULL, no new buf was used */ - size_t buf_size; -}; - -/** - * Basic flow control - * - * We maintain a basic flow control counter. "count" how many TX URBs are - * outstanding. Only allow "max" - * TX URBs to be outstanding. If this value is reached the queue will be - * stopped. The queue will be restarted when there are - * "threshold" URBs outstanding. - * Maintain a counter of how many time the TX queue needed to be restarted - * due to the "max" being exceeded and the "threshold" reached again. The - * timestamp "restart_ts" is to keep track from when the counter was last - * queried (see sysfs handling of file wlp_tx_inflight). - */ -struct i1480u_tx_inflight { - atomic_t count; - unsigned long max; - unsigned long threshold; - unsigned long restart_ts; - atomic_t restart_count; -}; - -/** - * Instance of a i1480u WLP interface - * - * Keeps references to the USB device that wraps it, as well as it's - * interface and associated UWB host controller. As well, it also - * keeps a link to the netdevice for integration into the networking - * stack. - * We maintian separate error history for the tx and rx endpoints because - * the implementation does not rely on locking - having one shared - * structure between endpoints may cause problems. Adding locking to the - * implementation will have higher cost than adding a separate structure. - */ -struct i1480u { - struct usb_device *usb_dev; - struct usb_interface *usb_iface; - struct net_device *net_dev; - - spinlock_t lock; - - /* RX context handling */ - struct sk_buff *rx_skb; - struct uwb_dev_addr rx_srcaddr; - size_t rx_untd_pkt_size; - struct i1480u_rx_buf { - struct i1480u *i1480u; /* back pointer */ - struct urb *urb; - struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */ - } rx_buf[i1480u_RX_BUFS]; /* N bufs */ - - spinlock_t tx_list_lock; /* TX context */ - struct list_head tx_list; - u8 tx_stream; - - struct stats lqe_stats, rssi_stats; /* radio statistics */ - - /* Options we can set from sysfs */ - struct wlp_options options; - struct uwb_notifs_handler uwb_notifs_handler; - struct edc tx_errors; - struct edc rx_errors; - struct wlp wlp; -#ifdef i1480u_FLOW_CONTROL - struct urb *notif_urb; - struct edc notif_edc; /* error density counter */ - u8 notif_buffer[1]; -#endif - struct i1480u_tx_inflight tx_inflight; -}; - -/* Internal interfaces */ -extern void i1480u_rx_cb(struct urb *urb); -extern int i1480u_rx_setup(struct i1480u *); -extern void i1480u_rx_release(struct i1480u *); -extern void i1480u_tx_release(struct i1480u *); -extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *, - struct uwb_dev_addr *); -extern void i1480u_stop_queue(struct wlp *); -extern void i1480u_start_queue(struct wlp *); -extern int i1480u_sysfs_setup(struct i1480u *); -extern void i1480u_sysfs_release(struct i1480u *); - -/* netdev interface */ -extern int i1480u_open(struct net_device *); -extern int i1480u_stop(struct net_device *); -extern netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *, - struct net_device *); -extern void i1480u_tx_timeout(struct net_device *); -extern int i1480u_set_config(struct net_device *, struct ifmap *); -extern int i1480u_change_mtu(struct net_device *, int); -extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs); - -/* bandwidth allocation callback */ -extern void i1480u_bw_alloc_cb(struct uwb_rsv *); - -/* Sys FS */ -extern struct attribute_group i1480u_wlp_attr_group; - -#endif /* #ifndef __i1480u_wlp_h__ */ diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c deleted file mode 100644 index def778cf221..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/lc.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * WUSB Wire Adapter: WLP interface - * Driver for the Linux Network stack. - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - * - * This implements a very simple network driver for the WLP USB - * device that is associated to a UWB (Ultra Wide Band) host. - * - * This is seen as an interface of a composite device. Once the UWB - * host has an association to another WLP capable device, the - * networking interface (aka WLP) can start to send packets back and - * forth. - * - * Limitations: - * - * - Hand cranked; can't ifup the interface until there is an association - * - * - BW allocation very simplistic [see i1480u_mas_set() and callees]. - * - * - * ROADMAP: - * - * ENTRY POINTS (driver model): - * - * i1480u_driver_{exit,init}(): initialization of the driver. - * - * i1480u_probe(): called by the driver code when a device - * matching 'i1480u_id_table' is connected. - * - * This allocs a netdev instance, inits with - * i1480u_add(), then registers_netdev(). - * i1480u_init() - * i1480u_add() - * - * i1480u_disconnect(): device has been disconnected/module - * is being removed. - * i1480u_rm() - */ -#include <linux/gfp.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> - -#include "i1480u-wlp.h" - - - -static inline -void i1480u_init(struct i1480u *i1480u) -{ - /* nothing so far... doesn't it suck? */ - spin_lock_init(&i1480u->lock); - INIT_LIST_HEAD(&i1480u->tx_list); - spin_lock_init(&i1480u->tx_list_lock); - wlp_options_init(&i1480u->options); - edc_init(&i1480u->tx_errors); - edc_init(&i1480u->rx_errors); -#ifdef i1480u_FLOW_CONTROL - edc_init(&i1480u->notif_edc); -#endif - stats_init(&i1480u->lqe_stats); - stats_init(&i1480u->rssi_stats); - wlp_init(&i1480u->wlp); -} - -/** - * Fill WLP device information structure - * - * The structure will contain a few character arrays, each ending with a - * null terminated string. Each string has to fit (excluding terminating - * character) into a specified range obtained from the WLP substack. - * - * It is still not clear exactly how this device information should be - * obtained. Until we find out we use the USB device descriptor as backup, some - * information elements have intuitive mappings, other not. - */ -static -void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info) -{ - struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); - struct usb_device *usb_dev = i1480u->usb_dev; - /* Treat device name and model name the same */ - if (usb_dev->descriptor.iProduct) { - usb_string(usb_dev, usb_dev->descriptor.iProduct, - dev_info->name, sizeof(dev_info->name)); - usb_string(usb_dev, usb_dev->descriptor.iProduct, - dev_info->model_name, sizeof(dev_info->model_name)); - } - if (usb_dev->descriptor.iManufacturer) - usb_string(usb_dev, usb_dev->descriptor.iManufacturer, - dev_info->manufacturer, - sizeof(dev_info->manufacturer)); - scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x", - __le16_to_cpu(usb_dev->descriptor.bcdDevice)); - if (usb_dev->descriptor.iSerialNumber) - usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, - dev_info->serial, sizeof(dev_info->serial)); - /* FIXME: where should we obtain category? */ - dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER); - /* FIXME: Complete OUI and OUIsubdiv attributes */ -} - -#ifdef i1480u_FLOW_CONTROL -/** - * Callback for the notification endpoint - * - * This mostly controls the xon/xoff protocol. In case of hard error, - * we stop the queue. If not, we always retry. - */ -static -void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs) -{ - struct i1480u *i1480u = urb->context; - struct usb_interface *usb_iface = i1480u->usb_iface; - struct device *dev = &usb_iface->dev; - int result; - - switch (urb->status) { - case 0: /* Got valid data, do xon/xoff */ - switch (i1480u->notif_buffer[0]) { - case 'N': - dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies); - netif_stop_queue(i1480u->net_dev); - break; - case 'A': - dev_err(dev, "XON STARTING queue at %lu\n", jiffies); - netif_start_queue(i1480u->net_dev); - break; - default: - dev_err(dev, "NEP: unknown data 0x%02hhx\n", - i1480u->notif_buffer[0]); - } - break; - case -ECONNRESET: /* Controlled situation ... */ - case -ENOENT: /* we killed the URB... */ - dev_err(dev, "NEP: URB reset/noent %d\n", urb->status); - goto error; - case -ESHUTDOWN: /* going away! */ - dev_err(dev, "NEP: URB down %d\n", urb->status); - goto error; - default: /* Retry unless it gets ugly */ - if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "NEP: URB max acceptable errors " - "exceeded; resetting device\n"); - goto error_reset; - } - dev_err(dev, "NEP: URB error %d\n", urb->status); - break; - } - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n", - result); - goto error_reset; - } - return; - -error_reset: - wlp_reset_all(&i1480-wlp); -error: - netif_stop_queue(i1480u->net_dev); - return; -} -#endif - -static const struct net_device_ops i1480u_netdev_ops = { - .ndo_open = i1480u_open, - .ndo_stop = i1480u_stop, - .ndo_start_xmit = i1480u_hard_start_xmit, - .ndo_tx_timeout = i1480u_tx_timeout, - .ndo_set_config = i1480u_set_config, - .ndo_change_mtu = i1480u_change_mtu, -}; - -static -int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) -{ - int result = -ENODEV; - struct wlp *wlp = &i1480u->wlp; - struct usb_device *usb_dev = interface_to_usbdev(iface); - struct net_device *net_dev = i1480u->net_dev; - struct uwb_rc *rc; - struct uwb_dev *uwb_dev; -#ifdef i1480u_FLOW_CONTROL - struct usb_endpoint_descriptor *epd; -#endif - - i1480u->usb_dev = usb_get_dev(usb_dev); - i1480u->usb_iface = iface; - rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev); - if (rc == NULL) { - dev_err(&iface->dev, "Cannot get associated UWB Radio " - "Controller\n"); - goto out; - } - wlp->xmit_frame = i1480u_xmit_frame; - wlp->fill_device_info = i1480u_fill_device_info; - wlp->stop_queue = i1480u_stop_queue; - wlp->start_queue = i1480u_start_queue; - result = wlp_setup(wlp, rc, net_dev); - if (result < 0) { - dev_err(&iface->dev, "Cannot setup WLP\n"); - goto error_wlp_setup; - } - result = 0; - ether_setup(net_dev); /* make it an etherdevice */ - uwb_dev = &rc->uwb_dev; - /* FIXME: hookup address change notifications? */ - - memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data, - sizeof(net_dev->dev_addr)); - - net_dev->hard_header_len = sizeof(struct untd_hdr_cmp) - + sizeof(struct wlp_tx_hdr) - + WLP_DATA_HLEN - + ETH_HLEN; - net_dev->mtu = 3500; - net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */ - -/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */ - /* FIXME: multicast disabled */ - net_dev->flags &= ~IFF_MULTICAST; - net_dev->features &= ~NETIF_F_SG; - net_dev->features &= ~NETIF_F_FRAGLIST; - /* All NETIF_F_*_CSUM disabled */ - net_dev->features |= NETIF_F_HIGHDMA; - net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */ - - net_dev->netdev_ops = &i1480u_netdev_ops; - -#ifdef i1480u_FLOW_CONTROL - /* Notification endpoint setup (submitted when we open the device) */ - i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL); - if (i1480u->notif_urb == NULL) { - dev_err(&iface->dev, "Unable to allocate notification URB\n"); - result = -ENOMEM; - goto error_urb_alloc; - } - epd = &iface->cur_altsetting->endpoint[0].desc; - usb_fill_int_urb(i1480u->notif_urb, usb_dev, - usb_rcvintpipe(usb_dev, epd->bEndpointAddress), - i1480u->notif_buffer, sizeof(i1480u->notif_buffer), - i1480u_notif_cb, i1480u, epd->bInterval); - -#endif - - i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX; - i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; - i1480u->tx_inflight.restart_ts = jiffies; - usb_set_intfdata(iface, i1480u); - return result; - -#ifdef i1480u_FLOW_CONTROL -error_urb_alloc: -#endif - wlp_remove(wlp); -error_wlp_setup: - uwb_rc_put(rc); -out: - usb_put_dev(i1480u->usb_dev); - return result; -} - -static void i1480u_rm(struct i1480u *i1480u) -{ - struct uwb_rc *rc = i1480u->wlp.rc; - usb_set_intfdata(i1480u->usb_iface, NULL); -#ifdef i1480u_FLOW_CONTROL - usb_kill_urb(i1480u->notif_urb); - usb_free_urb(i1480u->notif_urb); -#endif - wlp_remove(&i1480u->wlp); - uwb_rc_put(rc); - usb_put_dev(i1480u->usb_dev); -} - -/** Just setup @net_dev's i1480u private data */ -static void i1480u_netdev_setup(struct net_device *net_dev) -{ - struct i1480u *i1480u = netdev_priv(net_dev); - /* Initialize @i1480u */ - memset(i1480u, 0, sizeof(*i1480u)); - i1480u_init(i1480u); -} - -/** - * Probe a i1480u interface and register it - * - * @iface: USB interface to link to - * @id: USB class/subclass/protocol id - * @returns: 0 if ok, < 0 errno code on error. - * - * Does basic housekeeping stuff and then allocs a netdev with space - * for the i1480u data. Initializes, registers in i1480u, registers in - * netdev, ready to go. - */ -static int i1480u_probe(struct usb_interface *iface, - const struct usb_device_id *id) -{ - int result; - struct net_device *net_dev; - struct device *dev = &iface->dev; - struct i1480u *i1480u; - - /* Allocate instance [calls i1480u_netdev_setup() on it] */ - result = -ENOMEM; - net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup); - if (net_dev == NULL) { - dev_err(dev, "no memory for network device instance\n"); - goto error_alloc_netdev; - } - SET_NETDEV_DEV(net_dev, dev); - i1480u = netdev_priv(net_dev); - i1480u->net_dev = net_dev; - result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */ - if (result < 0) { - dev_err(dev, "cannot add i1480u device: %d\n", result); - goto error_i1480u_add; - } - result = register_netdev(net_dev); /* Okey dokey, bring it up */ - if (result < 0) { - dev_err(dev, "cannot register network device: %d\n", result); - goto error_register_netdev; - } - i1480u_sysfs_setup(i1480u); - if (result < 0) - goto error_sysfs_init; - return 0; - -error_sysfs_init: - unregister_netdev(net_dev); -error_register_netdev: - i1480u_rm(i1480u); -error_i1480u_add: - free_netdev(net_dev); -error_alloc_netdev: - return result; -} - - -/** - * Disconect a i1480u from the system. - * - * i1480u_stop() has been called before, so al the rx and tx contexts - * have been taken down already. Make sure the queue is stopped, - * unregister netdev and i1480u, free and kill. - */ -static void i1480u_disconnect(struct usb_interface *iface) -{ - struct i1480u *i1480u; - struct net_device *net_dev; - - i1480u = usb_get_intfdata(iface); - net_dev = i1480u->net_dev; - netif_stop_queue(net_dev); -#ifdef i1480u_FLOW_CONTROL - usb_kill_urb(i1480u->notif_urb); -#endif - i1480u_sysfs_release(i1480u); - unregister_netdev(net_dev); - i1480u_rm(i1480u); - free_netdev(net_dev); -} - -static struct usb_device_id i1480u_id_table[] = { - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE \ - | USB_DEVICE_ID_MATCH_DEV_INFO \ - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x8086, - .idProduct = 0x0c3b, - .bDeviceClass = 0xef, - .bDeviceSubClass = 0x02, - .bDeviceProtocol = 0x02, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0xff, - .bInterfaceProtocol = 0xff, - }, - {}, -}; -MODULE_DEVICE_TABLE(usb, i1480u_id_table); - -static struct usb_driver i1480u_driver = { - .name = KBUILD_MODNAME, - .probe = i1480u_probe, - .disconnect = i1480u_disconnect, - .id_table = i1480u_id_table, -}; - -static int __init i1480u_driver_init(void) -{ - return usb_register(&i1480u_driver); -} -module_init(i1480u_driver_init); - - -static void __exit i1480u_driver_exit(void) -{ - usb_deregister(&i1480u_driver); -} -module_exit(i1480u_driver_exit); - -MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); -MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB"); -MODULE_LICENSE("GPL"); diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c deleted file mode 100644 index f98f6ce8b9e..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/netdev.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * WUSB Wire Adapter: WLP interface - * Driver for the Linux Network stack. - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - * - * Implementation of the netdevice linkage (except tx and rx related stuff). - * - * ROADMAP: - * - * ENTRY POINTS (Net device): - * - * i1480u_open(): Called when we ifconfig up the interface; - * associates to a UWB host controller, reserves - * bandwidth (MAS), sets up RX USB URB and starts - * the queue. - * - * i1480u_stop(): Called when we ifconfig down a interface; - * reverses _open(). - * - * i1480u_set_config(): - */ - -#include <linux/slab.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> - -#include "i1480u-wlp.h" - -struct i1480u_cmd_set_ip_mas { - struct uwb_rccb rccb; - struct uwb_dev_addr addr; - u8 stream; - u8 owner; - u8 type; /* enum uwb_drp_type */ - u8 baMAS[32]; -} __attribute__((packed)); - - -static -int i1480u_set_ip_mas( - struct uwb_rc *rc, - const struct uwb_dev_addr *dstaddr, - u8 stream, u8 owner, u8 type, unsigned long *mas) -{ - - int result; - struct i1480u_cmd_set_ip_mas *cmd; - struct uwb_rc_evt_confirm reply; - - result = -ENOMEM; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - goto error_kzalloc; - cmd->rccb.bCommandType = 0xfd; - cmd->rccb.wCommand = cpu_to_le16(0x000e); - cmd->addr = *dstaddr; - cmd->stream = stream; - cmd->owner = owner; - cmd->type = type; - if (mas == NULL) - memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS)); - else - memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS)); - reply.rceb.bEventType = 0xfd; - reply.rceb.wEvent = cpu_to_le16(0x000e); - result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd), - &reply.rceb, sizeof(reply)); - if (result < 0) - goto error_cmd; - if (reply.bResultCode != UWB_RC_RES_FAIL) { - dev_err(&rc->uwb_dev.dev, - "SET-IP-MAS: command execution failed: %d\n", - reply.bResultCode); - result = -EIO; - } -error_cmd: - kfree(cmd); -error_kzalloc: - return result; -} - -/* - * Inform a WLP interface of a MAS reservation - * - * @rc is assumed refcnted. - */ -/* FIXME: detect if remote device is WLP capable? */ -static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc, - u8 stream, u8 owner, u8 type, unsigned long *mas) -{ - int result = 0; - struct device *dev = &rc->uwb_dev.dev; - - result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner, - type, mas); - if (result < 0) { - char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE]; - uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf), - &rc->uwb_dev.dev_addr); - uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf), - &uwb_dev->dev_addr); - dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n", - rcaddrbuf, devaddrbuf, result); - } - return result; -} - -/** - * Called by bandwidth allocator when change occurs in reservation. - * - * @rsv: The reservation that is being established, modified, or - * terminated. - * - * When a reservation is established, modified, or terminated the upper layer - * (WLP here) needs set/update the currently available Media Access Slots - * that can be use for IP traffic. - * - * Our action taken during failure depends on how the reservation is being - * changed: - * - if reservation is being established we do nothing if we cannot set the - * new MAS to be used - * - if reservation is being terminated we revert back to PCA whether the - * SET IP MAS command succeeds or not. - */ -void i1480u_bw_alloc_cb(struct uwb_rsv *rsv) -{ - int result = 0; - struct i1480u *i1480u = rsv->pal_priv; - struct device *dev = &i1480u->usb_iface->dev; - struct uwb_dev *target_dev = rsv->target.dev; - struct uwb_rc *rc = i1480u->wlp.rc; - u8 stream = rsv->stream; - int type = rsv->type; - int is_owner = rsv->owner == &rc->uwb_dev; - unsigned long *bmp = rsv->mas.bm; - - dev_err(dev, "WLP callback called - sending set ip mas\n"); - /*user cannot change options while setting configuration*/ - mutex_lock(&i1480u->options.mutex); - switch (rsv->state) { - case UWB_RSV_STATE_T_ACCEPTED: - case UWB_RSV_STATE_O_ESTABLISHED: - result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, - type, bmp); - if (result < 0) { - dev_err(dev, "MAS reservation failed: %d\n", result); - goto out; - } - if (is_owner) { - wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, - WLP_DRP | stream); - wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0); - } - break; - case UWB_RSV_STATE_NONE: - /* revert back to PCA */ - result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, - type, bmp); - if (result < 0) - dev_err(dev, "MAS reservation failed: %d\n", result); - /* Revert to PCA even though SET IP MAS failed. */ - wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, - i1480u->options.pca_base_priority); - wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1); - break; - default: - dev_err(dev, "unexpected WLP reservation state: %s (%d).\n", - uwb_rsv_state_str(rsv->state), rsv->state); - break; - } -out: - mutex_unlock(&i1480u->options.mutex); - return; -} - -/** - * - * Called on 'ifconfig up' - */ -int i1480u_open(struct net_device *net_dev) -{ - int result; - struct i1480u *i1480u = netdev_priv(net_dev); - struct wlp *wlp = &i1480u->wlp; - struct uwb_rc *rc; - struct device *dev = &i1480u->usb_iface->dev; - - rc = wlp->rc; - result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */ - if (result < 0) - goto error_rx_setup; - - result = uwb_radio_start(&wlp->pal); - if (result < 0) - goto error_radio_start; - - netif_wake_queue(net_dev); -#ifdef i1480u_FLOW_CONTROL - result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "Can't submit notification URB: %d\n", result); - goto error_notif_urb_submit; - } -#endif - /* Interface is up with an address, now we can create WSS */ - result = wlp_wss_setup(net_dev, &wlp->wss); - if (result < 0) { - dev_err(dev, "Can't create WSS: %d. \n", result); - goto error_wss_setup; - } - return 0; -error_wss_setup: -#ifdef i1480u_FLOW_CONTROL - usb_kill_urb(i1480u->notif_urb); -error_notif_urb_submit: -#endif - uwb_radio_stop(&wlp->pal); -error_radio_start: - netif_stop_queue(net_dev); - i1480u_rx_release(i1480u); -error_rx_setup: - return result; -} - - -/** - * Called on 'ifconfig down' - */ -int i1480u_stop(struct net_device *net_dev) -{ - struct i1480u *i1480u = netdev_priv(net_dev); - struct wlp *wlp = &i1480u->wlp; - - BUG_ON(wlp->rc == NULL); - wlp_wss_remove(&wlp->wss); - netif_carrier_off(net_dev); -#ifdef i1480u_FLOW_CONTROL - usb_kill_urb(i1480u->notif_urb); -#endif - netif_stop_queue(net_dev); - uwb_radio_stop(&wlp->pal); - i1480u_rx_release(i1480u); - i1480u_tx_release(i1480u); - return 0; -} - -/** - * - * Change the interface config--we probably don't have to do anything. - */ -int i1480u_set_config(struct net_device *net_dev, struct ifmap *map) -{ - int result; - struct i1480u *i1480u = netdev_priv(net_dev); - BUG_ON(i1480u->wlp.rc == NULL); - result = 0; - return result; -} - -/** - * Change the MTU of the interface - */ -int i1480u_change_mtu(struct net_device *net_dev, int mtu) -{ - static union { - struct wlp_tx_hdr tx; - struct wlp_rx_hdr rx; - } i1480u_all_hdrs; - - if (mtu < ETH_HLEN) /* We encap eth frames */ - return -ERANGE; - if (mtu > 4000 - sizeof(i1480u_all_hdrs)) - return -ERANGE; - net_dev->mtu = mtu; - return 0; -} - -/** - * Stop the network queue - * - * Enable WLP substack to stop network queue. We also set the flow control - * threshold at this time to prevent the flow control from restarting the - * queue. - * - * we are loosing the current threshold value here ... FIXME? - */ -void i1480u_stop_queue(struct wlp *wlp) -{ - struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); - struct net_device *net_dev = i1480u->net_dev; - i1480u->tx_inflight.threshold = 0; - netif_stop_queue(net_dev); -} - -/** - * Start the network queue - * - * Enable WLP substack to start network queue. Also re-enable the flow - * control to manage the queue again. - * - * We re-enable the flow control by storing the default threshold in the - * flow control threshold. This means that if the user modified the - * threshold before the queue was stopped and restarted that information - * will be lost. FIXME? - */ -void i1480u_start_queue(struct wlp *wlp) -{ - struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); - struct net_device *net_dev = i1480u->net_dev; - i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; - netif_start_queue(net_dev); -} diff --git a/drivers/uwb/i1480/i1480u-wlp/rx.c b/drivers/uwb/i1480/i1480u-wlp/rx.c deleted file mode 100644 index d4e51e108aa..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/rx.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * WUSB Wire Adapter: WLP interface - * Driver for the Linux Network stack. - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * i1480u's RX handling is simple. i1480u will send the received - * network packets broken up in fragments; 1 to N fragments make a - * packet, we assemble them together and deliver the packet with netif_rx(). - * - * Beacuse each USB transfer is a *single* fragment (except when the - * transfer contains a first fragment), each URB called thus - * back contains one or two fragments. So we queue N URBs, each with its own - * fragment buffer. When a URB is done, we process it (adding to the - * current skb from the fragment buffer until complete). Once - * processed, we requeue the URB. There is always a bunch of URBs - * ready to take data, so the intergap should be minimal. - * - * An URB's transfer buffer is the data field of a socket buffer. This - * reduces copying as data can be passed directly to network layer. If a - * complete packet or 1st fragment is received the URB's transfer buffer is - * taken away from it and used to send data to the network layer. In this - * case a new transfer buffer is allocated to the URB before being requeued. - * If a "NEXT" or "LAST" fragment is received, the fragment contents is - * appended to the RX packet under construction and the transfer buffer - * is reused. To be able to use this buffer to assemble complete packets - * we set each buffer's size to that of the MAX ethernet packet that can - * be received. There is thus room for improvement in memory usage. - * - * When the max tx fragment size increases, we should be able to read - * data into the skbs directly with very simple code. - * - * ROADMAP: - * - * ENTRY POINTS: - * - * i1480u_rx_setup(): setup RX context [from i1480u_open()] - * - * i1480u_rx_release(): release RX context [from i1480u_stop()] - * - * i1480u_rx_cb(): called when the RX USB URB receives a - * packet. It removes the header and pushes it up - * the Linux netdev stack with netif_rx(). - * - * i1480u_rx_buffer() - * i1480u_drop() and i1480u_fix() - * i1480u_skb_deliver - * - */ - -#include <linux/gfp.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include "i1480u-wlp.h" - -/* - * Setup the RX context - * - * Each URB is provided with a transfer_buffer that is the data field - * of a new socket buffer. - */ -int i1480u_rx_setup(struct i1480u *i1480u) -{ - int result, cnt; - struct device *dev = &i1480u->usb_iface->dev; - struct net_device *net_dev = i1480u->net_dev; - struct usb_endpoint_descriptor *epd; - struct sk_buff *skb; - - /* Alloc RX stuff */ - i1480u->rx_skb = NULL; /* not in process of receiving packet */ - result = -ENOMEM; - epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc; - for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { - struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt]; - rx_buf->i1480u = i1480u; - skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE); - if (!skb) { - dev_err(dev, - "RX: cannot allocate RX buffer %d\n", cnt); - result = -ENOMEM; - goto error; - } - skb->dev = net_dev; - skb->ip_summed = CHECKSUM_NONE; - skb_reserve(skb, 2); - rx_buf->data = skb; - rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); - if (unlikely(rx_buf->urb == NULL)) { - dev_err(dev, "RX: cannot allocate URB %d\n", cnt); - result = -ENOMEM; - goto error; - } - usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev, - usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress), - rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2, - i1480u_rx_cb, rx_buf); - result = usb_submit_urb(rx_buf->urb, GFP_NOIO); - if (unlikely(result < 0)) { - dev_err(dev, "RX: cannot submit URB %d: %d\n", - cnt, result); - goto error; - } - } - return 0; - -error: - i1480u_rx_release(i1480u); - return result; -} - - -/* Release resources associated to the rx context */ -void i1480u_rx_release(struct i1480u *i1480u) -{ - int cnt; - for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { - if (i1480u->rx_buf[cnt].data) - dev_kfree_skb(i1480u->rx_buf[cnt].data); - if (i1480u->rx_buf[cnt].urb) { - usb_kill_urb(i1480u->rx_buf[cnt].urb); - usb_free_urb(i1480u->rx_buf[cnt].urb); - } - } - if (i1480u->rx_skb != NULL) - dev_kfree_skb(i1480u->rx_skb); -} - -static -void i1480u_rx_unlink_urbs(struct i1480u *i1480u) -{ - int cnt; - for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { - if (i1480u->rx_buf[cnt].urb) - usb_unlink_urb(i1480u->rx_buf[cnt].urb); - } -} - -/* Fix an out-of-sequence packet */ -#define i1480u_fix(i1480u, msg...) \ -do { \ - if (printk_ratelimit()) \ - dev_err(&i1480u->usb_iface->dev, msg); \ - dev_kfree_skb_irq(i1480u->rx_skb); \ - i1480u->rx_skb = NULL; \ - i1480u->rx_untd_pkt_size = 0; \ -} while (0) - - -/* Drop an out-of-sequence packet */ -#define i1480u_drop(i1480u, msg...) \ -do { \ - if (printk_ratelimit()) \ - dev_err(&i1480u->usb_iface->dev, msg); \ - i1480u->net_dev->stats.rx_dropped++; \ -} while (0) - - - - -/* Finalizes setting up the SKB and delivers it - * - * We first pass the incoming frame to WLP substack for verification. It - * may also be a WLP association frame in which case WLP will take over the - * processing. If WLP does not take it over it will still verify it, if the - * frame is invalid the skb will be freed by WLP and we will not continue - * parsing. - * */ -static -void i1480u_skb_deliver(struct i1480u *i1480u) -{ - int should_parse; - struct net_device *net_dev = i1480u->net_dev; - struct device *dev = &i1480u->usb_iface->dev; - - should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb, - &i1480u->rx_srcaddr); - if (!should_parse) - goto out; - i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev); - net_dev->stats.rx_packets++; - net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size; - - netif_rx(i1480u->rx_skb); /* deliver */ -out: - i1480u->rx_skb = NULL; - i1480u->rx_untd_pkt_size = 0; -} - - -/* - * Process a buffer of data received from the USB RX endpoint - * - * First fragment arrives with next or last fragment. All other fragments - * arrive alone. - * - * /me hates long functions. - */ -static -void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf) -{ - unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */ - size_t untd_hdr_size, untd_frg_size; - size_t i1480u_hdr_size; - struct wlp_rx_hdr *i1480u_hdr = NULL; - - struct i1480u *i1480u = rx_buf->i1480u; - struct sk_buff *skb = rx_buf->data; - int size_left = rx_buf->urb->actual_length; - void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */ - struct untd_hdr *untd_hdr; - - struct net_device *net_dev = i1480u->net_dev; - struct device *dev = &i1480u->usb_iface->dev; - struct sk_buff *new_skb; - -#if 0 - dev_fnstart(dev, - "(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left); - dev_err(dev, "RX packet, %zu bytes\n", size_left); - dump_bytes(dev, ptr, size_left); -#endif - i1480u_hdr_size = sizeof(struct wlp_rx_hdr); - - while (size_left > 0) { - if (pkt_completed) { - i1480u_drop(i1480u, "RX: fragment follows completed" - "packet in same buffer. Dropping\n"); - break; - } - untd_hdr = ptr; - if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */ - i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n"); - goto out; - } - if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */ - i1480u_drop(i1480u, "RX: TX bit set! Dropping\n"); - goto out; - } - switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */ - case i1480u_PKT_FRAG_1ST: { - struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr; - dev_dbg(dev, "1st fragment\n"); - untd_hdr_size = sizeof(struct untd_hdr_1st); - if (i1480u->rx_skb != NULL) - i1480u_fix(i1480u, "RX: 1st fragment out of " - "sequence! Fixing\n"); - if (size_left < untd_hdr_size + i1480u_hdr_size) { - i1480u_drop(i1480u, "RX: short 1st fragment! " - "Dropping\n"); - goto out; - } - i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len) - - i1480u_hdr_size; - untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len); - if (size_left < untd_hdr_size + untd_frg_size) { - i1480u_drop(i1480u, - "RX: short payload! Dropping\n"); - goto out; - } - i1480u->rx_skb = skb; - i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size; - i1480u->rx_srcaddr = i1480u_hdr->srcaddr; - skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size); - skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size); - stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7); - stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18); - rx_buf->data = NULL; /* need to create new buffer */ - break; - } - case i1480u_PKT_FRAG_NXT: { - dev_dbg(dev, "nxt fragment\n"); - untd_hdr_size = sizeof(struct untd_hdr_rst); - if (i1480u->rx_skb == NULL) { - i1480u_drop(i1480u, "RX: next fragment out of " - "sequence! Dropping\n"); - goto out; - } - if (size_left < untd_hdr_size) { - i1480u_drop(i1480u, "RX: short NXT fragment! " - "Dropping\n"); - goto out; - } - untd_frg_size = le16_to_cpu(untd_hdr->len); - if (size_left < untd_hdr_size + untd_frg_size) { - i1480u_drop(i1480u, - "RX: short payload! Dropping\n"); - goto out; - } - memmove(skb_put(i1480u->rx_skb, untd_frg_size), - ptr + untd_hdr_size, untd_frg_size); - break; - } - case i1480u_PKT_FRAG_LST: { - dev_dbg(dev, "Lst fragment\n"); - untd_hdr_size = sizeof(struct untd_hdr_rst); - if (i1480u->rx_skb == NULL) { - i1480u_drop(i1480u, "RX: last fragment out of " - "sequence! Dropping\n"); - goto out; - } - if (size_left < untd_hdr_size) { - i1480u_drop(i1480u, "RX: short LST fragment! " - "Dropping\n"); - goto out; - } - untd_frg_size = le16_to_cpu(untd_hdr->len); - if (size_left < untd_frg_size + untd_hdr_size) { - i1480u_drop(i1480u, - "RX: short payload! Dropping\n"); - goto out; - } - memmove(skb_put(i1480u->rx_skb, untd_frg_size), - ptr + untd_hdr_size, untd_frg_size); - pkt_completed = 1; - break; - } - case i1480u_PKT_FRAG_CMP: { - dev_dbg(dev, "cmp fragment\n"); - untd_hdr_size = sizeof(struct untd_hdr_cmp); - if (i1480u->rx_skb != NULL) - i1480u_fix(i1480u, "RX: fix out-of-sequence CMP" - " fragment!\n"); - if (size_left < untd_hdr_size + i1480u_hdr_size) { - i1480u_drop(i1480u, "RX: short CMP fragment! " - "Dropping\n"); - goto out; - } - i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len); - untd_frg_size = i1480u->rx_untd_pkt_size; - if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) { - i1480u_drop(i1480u, - "RX: short payload! Dropping\n"); - goto out; - } - i1480u->rx_skb = skb; - i1480u_hdr = (void *) untd_hdr + untd_hdr_size; - i1480u->rx_srcaddr = i1480u_hdr->srcaddr; - stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7); - stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18); - skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size); - skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size); - rx_buf->data = NULL; /* for hand off skb to network stack */ - pkt_completed = 1; - i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */ - break; - } - default: - i1480u_drop(i1480u, "RX: unknown packet type %u! " - "Dropping\n", untd_hdr_type(untd_hdr)); - goto out; - } - size_left -= untd_hdr_size + untd_frg_size; - if (size_left > 0) - ptr += untd_hdr_size + untd_frg_size; - } - if (pkt_completed) - i1480u_skb_deliver(i1480u); -out: - /* recreate needed RX buffers*/ - if (rx_buf->data == NULL) { - /* buffer is being used to receive packet, create new */ - new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE); - if (!new_skb) { - if (printk_ratelimit()) - dev_err(dev, - "RX: cannot allocate RX buffer\n"); - } else { - new_skb->dev = net_dev; - new_skb->ip_summed = CHECKSUM_NONE; - skb_reserve(new_skb, 2); - rx_buf->data = new_skb; - } - } - return; -} - - -/* - * Called when an RX URB has finished receiving or has found some kind - * of error condition. - * - * LIMITATIONS: - * - * - We read USB-transfers, each transfer contains a SINGLE fragment - * (can contain a complete packet, or a 1st, next, or last fragment - * of a packet). - * Looks like a transfer can contain more than one fragment (07/18/06) - * - * - Each transfer buffer is the size of the maximum packet size (minus - * headroom), i1480u_MAX_PKT_SIZE - 2 - * - * - We always read the full USB-transfer, no partials. - * - * - Each transfer is read directly into a skb. This skb will be used to - * send data to the upper layers if it is the first fragment or a complete - * packet. In the other cases the data will be copied from the skb to - * another skb that is being prepared for the upper layers from a prev - * first fragment. - * - * It is simply too much of a pain. Gosh, there should be a unified - * SG infrastructure for *everything* [so that I could declare a SG - * buffer, pass it to USB for receiving, append some space to it if - * I wish, receive more until I have the whole chunk, adapt - * pointers on each fragment to remove hardware headers and then - * attach that to an skbuff and netif_rx()]. - */ -void i1480u_rx_cb(struct urb *urb) -{ - int result; - int do_parse_buffer = 1; - struct i1480u_rx_buf *rx_buf = urb->context; - struct i1480u *i1480u = rx_buf->i1480u; - struct device *dev = &i1480u->usb_iface->dev; - unsigned long flags; - u8 rx_buf_idx = rx_buf - i1480u->rx_buf; - - switch (urb->status) { - case 0: - break; - case -ECONNRESET: /* Not an error, but a controlled situation; */ - case -ENOENT: /* (we killed the URB)...so, no broadcast */ - case -ESHUTDOWN: /* going away! */ - dev_err(dev, "RX URB[%u]: goind down %d\n", - rx_buf_idx, urb->status); - goto error; - default: - dev_err(dev, "RX URB[%u]: unknown status %d\n", - rx_buf_idx, urb->status); - if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "RX: max acceptable errors exceeded," - " resetting device.\n"); - i1480u_rx_unlink_urbs(i1480u); - wlp_reset_all(&i1480u->wlp); - goto error; - } - do_parse_buffer = 0; - break; - } - spin_lock_irqsave(&i1480u->lock, flags); - /* chew the data fragments, extract network packets */ - if (do_parse_buffer) { - i1480u_rx_buffer(rx_buf); - if (rx_buf->data) { - rx_buf->urb->transfer_buffer = rx_buf->data->data; - result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "RX URB[%u]: cannot submit %d\n", - rx_buf_idx, result); - } - } - } - spin_unlock_irqrestore(&i1480u->lock, flags); -error: - return; -} - diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c deleted file mode 100644 index 4ffaf546cc6..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/sysfs.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * WUSB Wire Adapter: WLP interface - * Sysfs interfaces - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - */ - -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/device.h> - -#include "i1480u-wlp.h" - - -/** - * - * @dev: Class device from the net_device; assumed refcnted. - * - * Yes, I don't lock--we assume it is refcounted and I am getting a - * single byte value that is kind of atomic to read. - */ -ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf) -{ - return sprintf(buf, "%u\n", - wlp_tx_hdr_phy_rate(&options->def_tx_hdr)); -} -EXPORT_SYMBOL_GPL(uwb_phy_rate_show); - - -ssize_t uwb_phy_rate_store(struct wlp_options *options, - const char *buf, size_t size) -{ - ssize_t result; - unsigned rate; - - result = sscanf(buf, "%u\n", &rate); - if (result != 1) { - result = -EINVAL; - goto out; - } - result = -EINVAL; - if (rate >= UWB_PHY_RATE_INVALID) - goto out; - wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate); - result = 0; -out: - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(uwb_phy_rate_store); - - -ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf) -{ - return sprintf(buf, "%u\n", - wlp_tx_hdr_rts_cts(&options->def_tx_hdr)); -} -EXPORT_SYMBOL_GPL(uwb_rts_cts_show); - - -ssize_t uwb_rts_cts_store(struct wlp_options *options, - const char *buf, size_t size) -{ - ssize_t result; - unsigned value; - - result = sscanf(buf, "%u\n", &value); - if (result != 1) { - result = -EINVAL; - goto out; - } - result = -EINVAL; - wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value); - result = 0; -out: - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(uwb_rts_cts_store); - - -ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf) -{ - return sprintf(buf, "%u\n", - wlp_tx_hdr_ack_policy(&options->def_tx_hdr)); -} -EXPORT_SYMBOL_GPL(uwb_ack_policy_show); - - -ssize_t uwb_ack_policy_store(struct wlp_options *options, - const char *buf, size_t size) -{ - ssize_t result; - unsigned value; - - result = sscanf(buf, "%u\n", &value); - if (result != 1 || value > UWB_ACK_B_REQ) { - result = -EINVAL; - goto out; - } - wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value); - result = 0; -out: - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(uwb_ack_policy_store); - - -/** - * Show the PCA base priority. - * - * We can access without locking, as the value is (for now) orthogonal - * to other values. - */ -ssize_t uwb_pca_base_priority_show(const struct wlp_options *options, - char *buf) -{ - return sprintf(buf, "%u\n", - options->pca_base_priority); -} -EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show); - - -/** - * Set the PCA base priority. - * - * We can access without locking, as the value is (for now) orthogonal - * to other values. - */ -ssize_t uwb_pca_base_priority_store(struct wlp_options *options, - const char *buf, size_t size) -{ - ssize_t result = -EINVAL; - u8 pca_base_priority; - - result = sscanf(buf, "%hhu\n", &pca_base_priority); - if (result != 1) { - result = -EINVAL; - goto out; - } - result = -EINVAL; - if (pca_base_priority >= 8) - goto out; - options->pca_base_priority = pca_base_priority; - /* Update TX header if we are currently using PCA. */ - if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0) - wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority); - result = 0; -out: - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store); - -/** - * Show current inflight values - * - * Will print the current MAX and THRESHOLD values for the basic flow - * control. In addition it will report how many times the TX queue needed - * to be restarted since the last time this query was made. - */ -static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight, - char *buf) -{ - ssize_t result; - unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ; - unsigned long restart_count = atomic_read(&inflight->restart_count); - - result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n" - "#read: threshold max inflight_count restarts " - "seconds restarts/sec\n" - "#write: threshold max\n", - inflight->threshold, inflight->max, - atomic_read(&inflight->count), - restart_count, sec_elapsed, - sec_elapsed == 0 ? 0 : restart_count/sec_elapsed); - inflight->restart_ts = jiffies; - atomic_set(&inflight->restart_count, 0); - return result; -} - -static -ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight, - const char *buf, size_t size) -{ - unsigned long in_threshold, in_max; - ssize_t result; - result = sscanf(buf, "%lu %lu", &in_threshold, &in_max); - if (result != 2) - return -EINVAL; - if (in_max <= in_threshold) - return -EINVAL; - inflight->max = in_max; - inflight->threshold = in_threshold; - return size; -} -/* - * Glue (or function adaptors) for accesing info on sysfs - * - * [we need this indirection because the PCI driver does almost the - * same] - * - * Linux 2.6.21 changed how 'struct netdevice' does attributes (from - * having a 'struct class_dev' to having a 'struct device'). That is - * quite of a pain. - * - * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE() - * create adaptors for extracting the 'struct i1480u' from a 'struct - * dev' and calling a function for doing a sysfs operation (as we have - * them factorized already). i1480u_ATTR creates the attribute file - * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a - * class_device_attr_NAME or device_attr_NAME (for group registration). - */ - -#define i1480u_SHOW(name, fn, param) \ -static ssize_t i1480u_show_##name(struct device *dev, \ - struct device_attribute *attr,\ - char *buf) \ -{ \ - struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ - return fn(&i1480u->param, buf); \ -} - -#define i1480u_STORE(name, fn, param) \ -static ssize_t i1480u_store_##name(struct device *dev, \ - struct device_attribute *attr,\ - const char *buf, size_t size)\ -{ \ - struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ - return fn(&i1480u->param, buf, size); \ -} - -#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \ - i1480u_show_##name,\ - i1480u_store_##name) - -#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \ - S_IRUGO, \ - i1480u_show_##name, NULL) - -#define i1480u_ATTR_NAME(a) (dev_attr_##a) - - -/* - * Sysfs adaptors - */ -i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options); -i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options); -i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR); - -i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options); -i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options); -i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR); - -i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options); -i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options); -i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR); - -i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options); -i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options); -i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_eda, wlp_eda_show, wlp); -i1480u_STORE(wlp_eda, wlp_eda_store, wlp); -i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp); -i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp); -i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp); -i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp); -i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp); -i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp); -i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp); -i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp); -i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp); -i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp); -i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp); -i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp); -i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp); -i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp); -i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp); -i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp); -i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp); -i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp); -i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp); -i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp); -i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR); - -i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp); -i1480u_ATTR_SHOW(wlp_neighborhood); - -i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss); -i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss); -i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR); - -/* - * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over - * the last 256 received WLP frames (ECMA-368 13.3). - * - * [the -7dB that have to be substracted from the LQI to make the LQE - * are already taken into account]. - */ -i1480u_SHOW(wlp_lqe, stats_show, lqe_stats); -i1480u_STORE(wlp_lqe, stats_store, lqe_stats); -i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR); - -/* - * Show the Receive Signal Strength Indicator averaged over all the - * received WLP frames (ECMA-368 13.3). Still is not clear what - * this value is, but is kind of a percentage of the signal strength - * at the antenna. - */ -i1480u_SHOW(wlp_rssi, stats_show, rssi_stats); -i1480u_STORE(wlp_rssi, stats_store, rssi_stats); -i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR); - -/** - * We maintain a basic flow control counter. "count" how many TX URBs are - * outstanding. Only allow "max" - * TX URBs to be outstanding. If this value is reached the queue will be - * stopped. The queue will be restarted when there are - * "threshold" URBs outstanding. - */ -i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight); -i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight); -i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR); - -static struct attribute *i1480u_attrs[] = { - &i1480u_ATTR_NAME(uwb_phy_rate).attr, - &i1480u_ATTR_NAME(uwb_rts_cts).attr, - &i1480u_ATTR_NAME(uwb_ack_policy).attr, - &i1480u_ATTR_NAME(uwb_pca_base_priority).attr, - &i1480u_ATTR_NAME(wlp_lqe).attr, - &i1480u_ATTR_NAME(wlp_rssi).attr, - &i1480u_ATTR_NAME(wlp_eda).attr, - &i1480u_ATTR_NAME(wlp_uuid).attr, - &i1480u_ATTR_NAME(wlp_dev_name).attr, - &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr, - &i1480u_ATTR_NAME(wlp_dev_model_name).attr, - &i1480u_ATTR_NAME(wlp_dev_model_nr).attr, - &i1480u_ATTR_NAME(wlp_dev_serial).attr, - &i1480u_ATTR_NAME(wlp_dev_prim_category).attr, - &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr, - &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr, - &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr, - &i1480u_ATTR_NAME(wlp_neighborhood).attr, - &i1480u_ATTR_NAME(wss_activate).attr, - &i1480u_ATTR_NAME(wlp_tx_inflight).attr, - NULL, -}; - -static struct attribute_group i1480u_attr_group = { - .name = NULL, /* we want them in the same directory */ - .attrs = i1480u_attrs, -}; - -int i1480u_sysfs_setup(struct i1480u *i1480u) -{ - int result; - struct device *dev = &i1480u->usb_iface->dev; - result = sysfs_create_group(&i1480u->net_dev->dev.kobj, - &i1480u_attr_group); - if (result < 0) - dev_err(dev, "cannot initialize sysfs attributes: %d\n", - result); - return result; -} - - -void i1480u_sysfs_release(struct i1480u *i1480u) -{ - sysfs_remove_group(&i1480u->net_dev->dev.kobj, - &i1480u_attr_group); -} diff --git a/drivers/uwb/i1480/i1480u-wlp/tx.c b/drivers/uwb/i1480/i1480u-wlp/tx.c deleted file mode 100644 index 3c117a36456..00000000000 --- a/drivers/uwb/i1480/i1480u-wlp/tx.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - * WUSB Wire Adapter: WLP interface - * Deal with TX (massaging data to transmit, handling it) - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * Transmission engine. Get an skb, create from that a WLP transmit - * context, add a WLP TX header (which we keep prefilled in the - * device's instance), fill out the target-specific fields and - * fire it. - * - * ROADMAP: - * - * Entry points: - * - * i1480u_tx_release(): called by i1480u_disconnect() to release - * pending tx contexts. - * - * i1480u_tx_cb(): callback for TX contexts (USB URBs) - * i1480u_tx_destroy(): - * - * i1480u_tx_timeout(): called for timeout handling from the - * network stack. - * - * i1480u_hard_start_xmit(): called for transmitting an skb from - * the network stack. Will interact with WLP - * substack to verify and prepare frame. - * i1480u_xmit_frame(): actual transmission on hardware - * - * i1480u_tx_create() Creates TX context - * i1480u_tx_create_1() For packets in 1 fragment - * i1480u_tx_create_n() For packets in >1 fragments - * - * TODO: - * - * - FIXME: rewrite using usb_sg_*(), add asynch support to - * usb_sg_*(). It might not make too much sense as most of - * the times the MTU will be smaller than one page... - */ - -#include <linux/slab.h> -#include "i1480u-wlp.h" - -enum { - /* This is only for Next and Last TX packets */ - i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE - - sizeof(struct untd_hdr_rst), -}; - -/* Free resources allocated to a i1480u tx context. */ -static -void i1480u_tx_free(struct i1480u_tx *wtx) -{ - kfree(wtx->buf); - if (wtx->skb) - dev_kfree_skb_irq(wtx->skb); - usb_free_urb(wtx->urb); - kfree(wtx); -} - -static -void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx) -{ - unsigned long flags; - spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */ - list_del(&wtx->list_node); - i1480u_tx_free(wtx); - spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); -} - -static -void i1480u_tx_unlink_urbs(struct i1480u *i1480u) -{ - unsigned long flags; - struct i1480u_tx *wtx, *next; - - spin_lock_irqsave(&i1480u->tx_list_lock, flags); - list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) { - usb_unlink_urb(wtx->urb); - } - spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); -} - - -/* - * Callback for a completed tx USB URB. - * - * TODO: - * - * - FIXME: recover errors more gracefully - * - FIXME: handle NAKs (I dont think they come here) for flow ctl - */ -static -void i1480u_tx_cb(struct urb *urb) -{ - struct i1480u_tx *wtx = urb->context; - struct i1480u *i1480u = wtx->i1480u; - struct net_device *net_dev = i1480u->net_dev; - struct device *dev = &i1480u->usb_iface->dev; - unsigned long flags; - - switch (urb->status) { - case 0: - spin_lock_irqsave(&i1480u->lock, flags); - net_dev->stats.tx_packets++; - net_dev->stats.tx_bytes += urb->actual_length; - spin_unlock_irqrestore(&i1480u->lock, flags); - break; - case -ECONNRESET: /* Not an error, but a controlled situation; */ - case -ENOENT: /* (we killed the URB)...so, no broadcast */ - dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status); - netif_stop_queue(net_dev); - break; - case -ESHUTDOWN: /* going away! */ - dev_dbg(dev, "notif endp: down %d\n", urb->status); - netif_stop_queue(net_dev); - break; - default: - dev_err(dev, "TX: unknown URB status %d\n", urb->status); - if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "TX: max acceptable errors exceeded." - "Reset device.\n"); - netif_stop_queue(net_dev); - i1480u_tx_unlink_urbs(i1480u); - wlp_reset_all(&i1480u->wlp); - } - break; - } - i1480u_tx_destroy(i1480u, wtx); - if (atomic_dec_return(&i1480u->tx_inflight.count) - <= i1480u->tx_inflight.threshold - && netif_queue_stopped(net_dev) - && i1480u->tx_inflight.threshold != 0) { - netif_start_queue(net_dev); - atomic_inc(&i1480u->tx_inflight.restart_count); - } - return; -} - - -/* - * Given a buffer that doesn't fit in a single fragment, create an - * scatter/gather structure for delivery to the USB pipe. - * - * Implements functionality of i1480u_tx_create(). - * - * @wtx: tx descriptor - * @skb: skb to send - * @gfp_mask: gfp allocation mask - * @returns: Pointer to @wtx if ok, NULL on error. - * - * Sorry, TOO LONG a function, but breaking it up is kind of hard - * - * This will break the buffer in chunks smaller than - * i1480u_MAX_FRG_SIZE (including the header) and add proper headers - * to each: - * - * 1st header \ - * i1480 tx header | fragment 1 - * fragment data / - * nxt header \ fragment 2 - * fragment data / - * .. - * .. - * last header \ fragment 3 - * last fragment data / - * - * This does not fill the i1480 TX header, it is left up to the - * caller to do that; you can get it from @wtx->wlp_tx_hdr. - * - * This function consumes the skb unless there is an error. - */ -static -int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb, - gfp_t gfp_mask) -{ - int result; - void *pl; - size_t pl_size; - - void *pl_itr, *buf_itr; - size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0; - struct untd_hdr_1st *untd_hdr_1st; - struct wlp_tx_hdr *wlp_tx_hdr; - struct untd_hdr_rst *untd_hdr_rst; - - wtx->skb = NULL; - pl = skb->data; - pl_itr = pl; - pl_size = skb->len; - pl_size_left = pl_size; /* payload size */ - /* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus - * the headers */ - pl_size_1st = i1480u_MAX_FRG_SIZE - - sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr); - BUG_ON(pl_size_1st > pl_size); - pl_size_left -= pl_size_1st; - /* The rest have an smaller header (no i1480 TX header). We - * need to break up the payload in blocks smaller than - * i1480u_MAX_PL_SIZE (payload excluding header). */ - frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE; - /* Allocate space for the new buffer. In this new buffer we'll - * place the headers followed by the data fragment, headers, - * data fragments, etc.. - */ - result = -ENOMEM; - wtx->buf_size = sizeof(*untd_hdr_1st) - + sizeof(*wlp_tx_hdr) - + frgs * sizeof(*untd_hdr_rst) - + pl_size; - wtx->buf = kmalloc(wtx->buf_size, gfp_mask); - if (wtx->buf == NULL) - goto error_buf_alloc; - - buf_itr = wtx->buf; /* We got the space, let's fill it up */ - /* Fill 1st fragment */ - untd_hdr_1st = buf_itr; - buf_itr += sizeof(*untd_hdr_1st); - untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST); - untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0); - untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr)); - untd_hdr_1st->fragment_len = - cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr)); - memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding)); - /* Set up i1480 header info */ - wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr; - buf_itr += sizeof(*wlp_tx_hdr); - /* Copy the first fragment */ - memcpy(buf_itr, pl_itr, pl_size_1st); - pl_itr += pl_size_1st; - buf_itr += pl_size_1st; - - /* Now do each remaining fragment */ - result = -EINVAL; - while (pl_size_left > 0) { - if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf - > wtx->buf_size) { - printk(KERN_ERR "BUG: no space for header\n"); - goto error_bug; - } - untd_hdr_rst = buf_itr; - buf_itr += sizeof(*untd_hdr_rst); - if (pl_size_left > i1480u_MAX_PL_SIZE) { - frg_pl_size = i1480u_MAX_PL_SIZE; - untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT); - } else { - frg_pl_size = pl_size_left; - untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST); - } - untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0); - untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size); - untd_hdr_rst->padding = 0; - if (buf_itr + frg_pl_size - wtx->buf - > wtx->buf_size) { - printk(KERN_ERR "BUG: no space for payload\n"); - goto error_bug; - } - memcpy(buf_itr, pl_itr, frg_pl_size); - buf_itr += frg_pl_size; - pl_itr += frg_pl_size; - pl_size_left -= frg_pl_size; - } - dev_kfree_skb_irq(skb); - return 0; - -error_bug: - printk(KERN_ERR - "BUG: skb %u bytes\n" - "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n" - "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n", - skb->len, - frg_pl_size, i1480u_MAX_FRG_SIZE, - buf_itr - wtx->buf, wtx->buf_size, pl_size_left); - - kfree(wtx->buf); -error_buf_alloc: - return result; -} - - -/* - * Given a buffer that fits in a single fragment, fill out a @wtx - * struct for transmitting it down the USB pipe. - * - * Uses the fact that we have space reserved in front of the skbuff - * for hardware headers :] - * - * This does not fill the i1480 TX header, it is left up to the - * caller to do that; you can get it from @wtx->wlp_tx_hdr. - * - * @pl: pointer to payload data - * @pl_size: size of the payuload - * - * This function does not consume the @skb. - */ -static -int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb, - gfp_t gfp_mask) -{ - struct untd_hdr_cmp *untd_hdr_cmp; - struct wlp_tx_hdr *wlp_tx_hdr; - - wtx->buf = NULL; - wtx->skb = skb; - BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr)); - wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr)); - wtx->wlp_tx_hdr = wlp_tx_hdr; - BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp)); - untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp)); - - untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP); - untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0); - untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp)); - untd_hdr_cmp->padding = 0; - return 0; -} - - -/* - * Given a skb to transmit, massage it to become palatable for the TX pipe - * - * This will break the buffer in chunks smaller than - * i1480u_MAX_FRG_SIZE and add proper headers to each. - * - * 1st header \ - * i1480 tx header | fragment 1 - * fragment data / - * nxt header \ fragment 2 - * fragment data / - * .. - * .. - * last header \ fragment 3 - * last fragment data / - * - * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE. - * - * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the - * following is composed: - * - * complete header \ - * i1480 tx header | single fragment - * packet data / - * - * We were going to use s/g support, but because the interface is - * synch and at the end there is plenty of overhead to do it, it - * didn't seem that worth for data that is going to be smaller than - * one page. - */ -static -struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u, - struct sk_buff *skb, gfp_t gfp_mask) -{ - int result; - struct usb_endpoint_descriptor *epd; - int usb_pipe; - unsigned long flags; - - struct i1480u_tx *wtx; - const size_t pl_max_size = - i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp) - - sizeof(struct wlp_tx_hdr); - - wtx = kmalloc(sizeof(*wtx), gfp_mask); - if (wtx == NULL) - goto error_wtx_alloc; - wtx->urb = usb_alloc_urb(0, gfp_mask); - if (wtx->urb == NULL) - goto error_urb_alloc; - epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc; - usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress); - /* Fits in a single complete packet or need to split? */ - if (skb->len > pl_max_size) { - result = i1480u_tx_create_n(wtx, skb, gfp_mask); - if (result < 0) - goto error_create; - usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe, - wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx); - } else { - result = i1480u_tx_create_1(wtx, skb, gfp_mask); - if (result < 0) - goto error_create; - usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe, - skb->data, skb->len, i1480u_tx_cb, wtx); - } - spin_lock_irqsave(&i1480u->tx_list_lock, flags); - list_add(&wtx->list_node, &i1480u->tx_list); - spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); - return wtx; - -error_create: - kfree(wtx->urb); -error_urb_alloc: - kfree(wtx); -error_wtx_alloc: - return NULL; -} - -/* - * Actual fragmentation and transmission of frame - * - * @wlp: WLP substack data structure - * @skb: To be transmitted - * @dst: Device address of destination - * @returns: 0 on success, <0 on failure - * - * This function can also be called directly (not just from - * hard_start_xmit), so we also check here if the interface is up before - * taking sending anything. - */ -int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb, - struct uwb_dev_addr *dst) -{ - int result = -ENXIO; - struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); - struct device *dev = &i1480u->usb_iface->dev; - struct net_device *net_dev = i1480u->net_dev; - struct i1480u_tx *wtx; - struct wlp_tx_hdr *wlp_tx_hdr; - static unsigned char dev_bcast[2] = { 0xff, 0xff }; - - BUG_ON(i1480u->wlp.rc == NULL); - if ((net_dev->flags & IFF_UP) == 0) - goto out; - result = -EBUSY; - if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) { - netif_stop_queue(net_dev); - goto error_max_inflight; - } - result = -ENOMEM; - wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC); - if (unlikely(wtx == NULL)) { - if (printk_ratelimit()) - dev_err(dev, "TX: no memory for WLP TX URB," - "dropping packet (in flight %d)\n", - atomic_read(&i1480u->tx_inflight.count)); - netif_stop_queue(net_dev); - goto error_wtx_alloc; - } - wtx->i1480u = i1480u; - /* Fill out the i1480 header; @i1480u->def_tx_hdr read without - * locking. We do so because they are kind of orthogonal to - * each other (and thus not changed in an atomic batch). - * The ETH header is right after the WLP TX header. */ - wlp_tx_hdr = wtx->wlp_tx_hdr; - *wlp_tx_hdr = i1480u->options.def_tx_hdr; - wlp_tx_hdr->dstaddr = *dst; - if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast)) - && (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) { - /*Broadcast message directed to DRP host. Send as best effort - * on PCA. */ - wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority); - } - - result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */ - if (result < 0) { - dev_err(dev, "TX: cannot submit URB: %d\n", result); - /* We leave the freeing of skb to calling function */ - wtx->skb = NULL; - goto error_tx_urb_submit; - } - atomic_inc(&i1480u->tx_inflight.count); - net_dev->trans_start = jiffies; - return result; - -error_tx_urb_submit: - i1480u_tx_destroy(i1480u, wtx); -error_wtx_alloc: -error_max_inflight: -out: - return result; -} - - -/* - * Transmit an skb Called when an skbuf has to be transmitted - * - * The skb is first passed to WLP substack to ensure this is a valid - * frame. If valid the device address of destination will be filled and - * the WLP header prepended to the skb. If this step fails we fake sending - * the frame, if we return an error the network stack will just keep trying. - * - * Broadcast frames inside a WSS needs to be treated special as multicast is - * not supported. A broadcast frame is sent as unicast to each member of the - * WSS - this is done by the WLP substack when it finds a broadcast frame. - * So, we test if the WLP substack took over the skb and only transmit it - * if it has not (been taken over). - * - * @net_dev->xmit_lock is held - */ -netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *skb, - struct net_device *net_dev) -{ - int result; - struct i1480u *i1480u = netdev_priv(net_dev); - struct device *dev = &i1480u->usb_iface->dev; - struct uwb_dev_addr dst; - - if ((net_dev->flags & IFF_UP) == 0) - goto error; - result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst); - if (result < 0) { - dev_err(dev, "WLP verification of TX frame failed (%d). " - "Dropping packet.\n", result); - goto error; - } else if (result == 1) { - /* trans_start time will be set when WLP actually transmits - * the frame */ - goto out; - } - result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst); - if (result < 0) { - dev_err(dev, "Frame TX failed (%d).\n", result); - goto error; - } - return NETDEV_TX_OK; -error: - dev_kfree_skb_any(skb); - net_dev->stats.tx_dropped++; -out: - return NETDEV_TX_OK; -} - - -/* - * Called when a pkt transmission doesn't complete in a reasonable period - * Device reset may sleep - do it outside of interrupt context (delayed) - */ -void i1480u_tx_timeout(struct net_device *net_dev) -{ - struct i1480u *i1480u = netdev_priv(net_dev); - - wlp_reset_all(&i1480u->wlp); -} - - -void i1480u_tx_release(struct i1480u *i1480u) -{ - unsigned long flags; - struct i1480u_tx *wtx, *next; - int count = 0, empty; - - spin_lock_irqsave(&i1480u->tx_list_lock, flags); - list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) { - count++; - usb_unlink_urb(wtx->urb); - } - spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); - count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */ - /* - * We don't like this sollution too much (dirty as it is), but - * it is cheaper than putting a refcount on each i1480u_tx and - * i1480uting for all of them to go away... - * - * Called when no more packets can be added to tx_list - * so can i1480ut for it to be empty. - */ - while (1) { - spin_lock_irqsave(&i1480u->tx_list_lock, flags); - empty = list_empty(&i1480u->tx_list); - spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); - if (empty) - break; - count--; - BUG_ON(count == 0); - msleep(20); - } -} diff --git a/drivers/uwb/wlp/Makefile b/drivers/uwb/wlp/Makefile deleted file mode 100644 index c72c11db5b1..00000000000 --- a/drivers/uwb/wlp/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -obj-$(CONFIG_UWB_WLP) := wlp.o - -wlp-objs := \ - driver.o \ - eda.o \ - messages.o \ - sysfs.o \ - txrx.o \ - wlp-lc.o \ - wss-lc.o diff --git a/drivers/uwb/wlp/driver.c b/drivers/uwb/wlp/driver.c deleted file mode 100644 index cb8d699b6a6..00000000000 --- a/drivers/uwb/wlp/driver.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * - * Copyright (C) 2007 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * Life cycle of WLP substack - * - * FIXME: Docs - */ - -#include <linux/module.h> - -static int __init wlp_subsys_init(void) -{ - return 0; -} -module_init(wlp_subsys_init); - -static void __exit wlp_subsys_exit(void) -{ - return; -} -module_exit(wlp_subsys_exit); - -MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>"); -MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)"); -MODULE_LICENSE("GPL"); diff --git a/drivers/uwb/wlp/eda.c b/drivers/uwb/wlp/eda.c deleted file mode 100644 index 086fc0cf940..00000000000 --- a/drivers/uwb/wlp/eda.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * WUSB Wire Adapter: WLP interface - * Ethernet to device address cache - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * We need to be able to map ethernet addresses to device addresses - * and back because there is not explicit relationship between the eth - * addresses used in the ETH frames and the device addresses (no, it - * would not have been simpler to force as ETH address the MBOA MAC - * address...no, not at all :). - * - * A device has one MBOA MAC address and one device address. It is possible - * for a device to have more than one virtual MAC address (although a - * virtual address can be the same as the MBOA MAC address). The device - * address is guaranteed to be unique among the devices in the extended - * beacon group (see ECMA 17.1.1). We thus use the device address as index - * to this cache. We do allow searching based on virtual address as this - * is how Ethernet frames will be addressed. - * - * We need to support virtual EUI-48. Although, right now the virtual - * EUI-48 will always be the same as the MAC SAP address. The EDA cache - * entry thus contains a MAC SAP address as well as the virtual address - * (used to map the network stack address to a neighbor). When we move - * to support more than one virtual MAC on a host then this organization - * will have to change. Perhaps a neighbor has a list of WSSs, each with a - * tag and virtual EUI-48. - * - * On data transmission - * it is used to determine if the neighbor is connected and what WSS it - * belongs to. With this we know what tag to add to the WLP frame. Storing - * the WSS in the EDA cache may be overkill because we only support one - * WSS. Hopefully we will support more than one WSS at some point. - * On data reception it is used to determine the WSS based on - * the tag and address of the transmitting neighbor. - */ - -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <linux/wlp.h> -#include "wlp-internal.h" - - -/* FIXME: cache is not purged, only on device close */ - -/* FIXME: does not scale, change to dynamic array */ - -/* - * Initialize the EDA cache - * - * @returns 0 if ok, < 0 errno code on error - * - * Call when the interface is being brought up - * - * NOTE: Keep it as a separate function as the implementation will - * change and be more complex. - */ -void wlp_eda_init(struct wlp_eda *eda) -{ - INIT_LIST_HEAD(&eda->cache); - spin_lock_init(&eda->lock); -} - -/* - * Release the EDA cache - * - * @returns 0 if ok, < 0 errno code on error - * - * Called when the interface is brought down - */ -void wlp_eda_release(struct wlp_eda *eda) -{ - unsigned long flags; - struct wlp_eda_node *itr, *next; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry_safe(itr, next, &eda->cache, list_node) { - list_del(&itr->list_node); - kfree(itr); - } - spin_unlock_irqrestore(&eda->lock, flags); -} - -/* - * Add an address mapping - * - * @returns 0 if ok, < 0 errno code on error - * - * An address mapping is initially created when the neighbor device is seen - * for the first time (it is "onair"). At this time the neighbor is not - * connected or associated with a WSS so we only populate the Ethernet and - * Device address fields. - * - */ -int wlp_eda_create_node(struct wlp_eda *eda, - const unsigned char eth_addr[ETH_ALEN], - const struct uwb_dev_addr *dev_addr) -{ - int result = 0; - struct wlp_eda_node *itr; - unsigned long flags; - - BUG_ON(dev_addr == NULL || eth_addr == NULL); - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry(itr, &eda->cache, list_node) { - if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { - printk(KERN_ERR "EDA cache already contains entry " - "for neighbor %02x:%02x\n", - dev_addr->data[1], dev_addr->data[0]); - result = -EEXIST; - goto out_unlock; - } - } - itr = kzalloc(sizeof(*itr), GFP_ATOMIC); - if (itr != NULL) { - memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr)); - itr->dev_addr = *dev_addr; - list_add(&itr->list_node, &eda->cache); - } else - result = -ENOMEM; -out_unlock: - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} - -/* - * Remove entry from EDA cache - * - * This is done when the device goes off air. - */ -void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr) -{ - struct wlp_eda_node *itr, *next; - unsigned long flags; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry_safe(itr, next, &eda->cache, list_node) { - if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { - list_del(&itr->list_node); - kfree(itr); - break; - } - } - spin_unlock_irqrestore(&eda->lock, flags); -} - -/* - * Update an address mapping - * - * @returns 0 if ok, < 0 errno code on error - */ -int wlp_eda_update_node(struct wlp_eda *eda, - const struct uwb_dev_addr *dev_addr, - struct wlp_wss *wss, - const unsigned char virt_addr[ETH_ALEN], - const u8 tag, const enum wlp_wss_connect state) -{ - int result = -ENOENT; - struct wlp_eda_node *itr; - unsigned long flags; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry(itr, &eda->cache, list_node) { - if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { - /* Found it, update it */ - itr->wss = wss; - memcpy(itr->virt_addr, virt_addr, - sizeof(itr->virt_addr)); - itr->tag = tag; - itr->state = state; - result = 0; - goto out_unlock; - } - } - /* Not found */ -out_unlock: - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} - -/* - * Update only state field of an address mapping - * - * @returns 0 if ok, < 0 errno code on error - */ -int wlp_eda_update_node_state(struct wlp_eda *eda, - const struct uwb_dev_addr *dev_addr, - const enum wlp_wss_connect state) -{ - int result = -ENOENT; - struct wlp_eda_node *itr; - unsigned long flags; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry(itr, &eda->cache, list_node) { - if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { - /* Found it, update it */ - itr->state = state; - result = 0; - goto out_unlock; - } - } - /* Not found */ -out_unlock: - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} - -/* - * Return contents of EDA cache entry - * - * @dev_addr: index to EDA cache - * @eda_entry: pointer to where contents of EDA cache will be copied - */ -int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr, - struct wlp_eda_node *eda_entry) -{ - int result = -ENOENT; - struct wlp_eda_node *itr; - unsigned long flags; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry(itr, &eda->cache, list_node) { - if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { - *eda_entry = *itr; - result = 0; - goto out_unlock; - } - } - /* Not found */ -out_unlock: - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} - -/* - * Execute function for every element in the cache - * - * @function: function to execute on element of cache (must be atomic) - * @priv: private data of function - * @returns: result of first function that failed, or last function - * executed if no function failed. - * - * Stop executing when function returns error for any element in cache. - * - * IMPORTANT: We are using a spinlock here: the function executed on each - * element has to be atomic. - */ -int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function, - void *priv) -{ - int result = 0; - struct wlp *wlp = container_of(eda, struct wlp, eda); - struct wlp_eda_node *entry; - unsigned long flags; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry(entry, &eda->cache, list_node) { - result = (*function)(wlp, entry, priv); - if (result < 0) - break; - } - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} - -/* - * Execute function for single element in the cache (return dev addr) - * - * @virt_addr: index into EDA cache used to determine which element to - * execute the function on - * @dev_addr: device address of element in cache will be returned using - * @dev_addr - * @function: function to execute on element of cache (must be atomic) - * @priv: private data of function - * @returns: result of function - * - * IMPORTANT: We are using a spinlock here: the function executed on the - * element has to be atomic. - */ -int wlp_eda_for_virtual(struct wlp_eda *eda, - const unsigned char virt_addr[ETH_ALEN], - struct uwb_dev_addr *dev_addr, - wlp_eda_for_each_f function, - void *priv) -{ - int result = 0; - struct wlp *wlp = container_of(eda, struct wlp, eda); - struct wlp_eda_node *itr; - unsigned long flags; - int found = 0; - - spin_lock_irqsave(&eda->lock, flags); - list_for_each_entry(itr, &eda->cache, list_node) { - if (!memcmp(itr->virt_addr, virt_addr, - sizeof(itr->virt_addr))) { - result = (*function)(wlp, itr, priv); - *dev_addr = itr->dev_addr; - found = 1; - break; - } - } - if (!found) - result = -ENODEV; - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} - -static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED", - "WLP_WSS_CONNECTED", - "WLP_WSS_CONNECT_FAILED", -}; - -static const char *wlp_wss_connect_state_str(unsigned id) -{ - if (id >= ARRAY_SIZE(__wlp_wss_connect_state)) - return "unknown WSS connection state"; - return __wlp_wss_connect_state[id]; -} - -/* - * View EDA cache from user space - * - * A debugging feature to give user visibility into the EDA cache. Also - * used to display members of WSS to user (called from wlp_wss_members_show()) - */ -ssize_t wlp_eda_show(struct wlp *wlp, char *buf) -{ - ssize_t result = 0; - struct wlp_eda_node *entry; - unsigned long flags; - struct wlp_eda *eda = &wlp->eda; - spin_lock_irqsave(&eda->lock, flags); - result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr " - "tag state virt_addr\n"); - list_for_each_entry(entry, &eda->cache, list_node) { - result += scnprintf(buf + result, PAGE_SIZE - result, - "%pM %02x:%02x %p 0x%02x %s %pM\n", - entry->eth_addr, - entry->dev_addr.data[1], - entry->dev_addr.data[0], entry->wss, - entry->tag, - wlp_wss_connect_state_str(entry->state), - entry->virt_addr); - if (result >= PAGE_SIZE) - break; - } - spin_unlock_irqrestore(&eda->lock, flags); - return result; -} -EXPORT_SYMBOL_GPL(wlp_eda_show); - -/* - * Add new EDA cache entry based on user input in sysfs - * - * Should only be used for debugging. - * - * The WSS is assumed to be the only WSS supported. This needs to be - * redesigned when we support more than one WSS. - */ -ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size) -{ - ssize_t result; - struct wlp_eda *eda = &wlp->eda; - u8 eth_addr[6]; - struct uwb_dev_addr dev_addr; - u8 tag; - unsigned state; - - result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx " - "%02hhx:%02hhx %02hhx %u\n", - ð_addr[0], ð_addr[1], - ð_addr[2], ð_addr[3], - ð_addr[4], ð_addr[5], - &dev_addr.data[1], &dev_addr.data[0], &tag, &state); - switch (result) { - case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */ - /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/ - result = -ENOSYS; - break; - case 10: - state = state >= 1 ? 1 : 0; - result = wlp_eda_create_node(eda, eth_addr, &dev_addr); - if (result < 0 && result != -EEXIST) - goto error; - /* Set virtual addr to be same as MAC */ - result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss, - eth_addr, tag, state); - if (result < 0) - goto error; - break; - default: /* bad format */ - result = -EINVAL; - } -error: - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_eda_store); diff --git a/drivers/uwb/wlp/messages.c b/drivers/uwb/wlp/messages.c deleted file mode 100644 index 3a8e033dce2..00000000000 --- a/drivers/uwb/wlp/messages.c +++ /dev/null @@ -1,1798 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * Message construction and parsing - * - * Copyright (C) 2007 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - */ - -#include <linux/wlp.h> -#include <linux/slab.h> - -#include "wlp-internal.h" - -static -const char *__wlp_assoc_frame[] = { - [WLP_ASSOC_D1] = "WLP_ASSOC_D1", - [WLP_ASSOC_D2] = "WLP_ASSOC_D2", - [WLP_ASSOC_M1] = "WLP_ASSOC_M1", - [WLP_ASSOC_M2] = "WLP_ASSOC_M2", - [WLP_ASSOC_M3] = "WLP_ASSOC_M3", - [WLP_ASSOC_M4] = "WLP_ASSOC_M4", - [WLP_ASSOC_M5] = "WLP_ASSOC_M5", - [WLP_ASSOC_M6] = "WLP_ASSOC_M6", - [WLP_ASSOC_M7] = "WLP_ASSOC_M7", - [WLP_ASSOC_M8] = "WLP_ASSOC_M8", - [WLP_ASSOC_F0] = "WLP_ASSOC_F0", - [WLP_ASSOC_E1] = "WLP_ASSOC_E1", - [WLP_ASSOC_E2] = "WLP_ASSOC_E2", - [WLP_ASSOC_C1] = "WLP_ASSOC_C1", - [WLP_ASSOC_C2] = "WLP_ASSOC_C2", - [WLP_ASSOC_C3] = "WLP_ASSOC_C3", - [WLP_ASSOC_C4] = "WLP_ASSOC_C4", -}; - -static const char *wlp_assoc_frame_str(unsigned id) -{ - if (id >= ARRAY_SIZE(__wlp_assoc_frame)) - return "unknown association frame"; - return __wlp_assoc_frame[id]; -} - -static const char *__wlp_assc_error[] = { - "none", - "Authenticator Failure", - "Rogue activity suspected", - "Device busy", - "Setup Locked", - "Registrar not ready", - "Invalid WSS selection", - "Message timeout", - "Enrollment session timeout", - "Device password invalid", - "Unsupported version", - "Internal error", - "Undefined error", - "Numeric comparison failure", - "Waiting for user input", -}; - -static const char *wlp_assc_error_str(unsigned id) -{ - if (id >= ARRAY_SIZE(__wlp_assc_error)) - return "unknown WLP association error"; - return __wlp_assc_error[id]; -} - -static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type, - size_t len) -{ - hdr->type = cpu_to_le16(type); - hdr->length = cpu_to_le16(len); -} - -/* - * Populate fields of a constant sized attribute - * - * @returns: total size of attribute including size of new value - * - * We have two instances of this function (wlp_pset and wlp_set): one takes - * the value as a parameter, the other takes a pointer to the value as - * parameter. They thus only differ in how the value is assigned to the - * attribute. - * - * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of - * sizeof(type) to be able to use this same code for the structures that - * contain 8bit enum values and be able to deal with pointer types. - */ -#define wlp_set(type, type_code, name) \ -static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \ -{ \ - wlp_set_attr_hdr(&attr->hdr, type_code, \ - sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \ - attr->name = value; \ - return sizeof(*attr); \ -} - -#define wlp_pset(type, type_code, name) \ -static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \ -{ \ - wlp_set_attr_hdr(&attr->hdr, type_code, \ - sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \ - attr->name = *value; \ - return sizeof(*attr); \ -} - -/** - * Populate fields of a variable attribute - * - * @returns: total size of attribute including size of new value - * - * Provided with a pointer to the memory area reserved for the - * attribute structure, the field is populated with the value. The - * reserved memory has to contain enough space for the value. - */ -#define wlp_vset(type, type_code, name) \ -static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value, \ - size_t len) \ -{ \ - wlp_set_attr_hdr(&attr->hdr, type_code, len); \ - memcpy(attr->name, value, len); \ - return sizeof(*attr) + len; \ -} - -wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name) -wlp_vset(char *, WLP_ATTR_MANUF, manufacturer) -wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type) -wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name) -wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr) -wlp_vset(char *, WLP_ATTR_SERIAL, serial) -wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name) -wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e) -wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r) -wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid) -wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type) -/*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/ -wlp_set(u8, WLP_ATTR_WLP_VER, version) -wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err) -wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd) -wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl) -wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status) -wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast) -wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce) -wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce) -wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag) -wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt) - -/** - * Fill in the WSS information attributes - * - * We currently only support one WSS, and this is assumed in this function - * that can populate only one WSS information attribute. - */ -static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr, - struct wlp_wss *wss) -{ - size_t datalen; - void *ptr = attr->wss_info; - size_t used = sizeof(*attr); - - datalen = sizeof(struct wlp_wss_info) + strlen(wss->name); - wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen); - used = wlp_set_wssid(ptr, &wss->wssid); - used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name)); - used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll); - used += wlp_set_wss_sec_status(ptr + used, wss->secure_status); - used += wlp_set_wss_bcast(ptr + used, &wss->bcast); - return sizeof(*attr) + used; -} - -/** - * Verify attribute header - * - * @hdr: Pointer to attribute header that will be verified. - * @type: Expected attribute type. - * @len: Expected length of attribute value (excluding header). - * - * Most attribute values have a known length even when they do have a - * length field. This knowledge can be used via this function to verify - * that the length field matches the expected value. - */ -static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr, - enum wlp_attr_type type, unsigned len) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - - if (le16_to_cpu(hdr->type) != type) { - dev_err(dev, "WLP: unexpected header type. Expected " - "%u, got %u.\n", type, le16_to_cpu(hdr->type)); - return -EINVAL; - } - if (le16_to_cpu(hdr->length) != len) { - dev_err(dev, "WLP: unexpected length in header. Expected " - "%u, got %u.\n", len, le16_to_cpu(hdr->length)); - return -EINVAL; - } - return 0; -} - -/** - * Check if header of WSS information attribute valid - * - * @returns: length of WSS attributes (value of length attribute field) if - * valid WSS information attribute found - * -ENODATA if no WSS information attribute found - * -EIO other error occured - * - * The WSS information attribute is optional. The function will be provided - * with a pointer to data that could _potentially_ be a WSS information - * attribute. If a valid WSS information attribute is found it will return - * 0, if no WSS information attribute is found it will return -ENODATA, and - * another error will be returned if it is a WSS information attribute, but - * some parsing failure occured. - */ -static int wlp_check_wss_info_attr_hdr(struct wlp *wlp, - struct wlp_attr_hdr *hdr, size_t buflen) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - size_t len; - int result = 0; - - if (buflen < sizeof(*hdr)) { - dev_err(dev, "WLP: Not enough space in buffer to parse" - " WSS information attribute header.\n"); - result = -EIO; - goto out; - } - if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) { - /* WSS information is optional */ - result = -ENODATA; - goto out; - } - len = le16_to_cpu(hdr->length); - if (buflen < sizeof(*hdr) + len) { - dev_err(dev, "WLP: Not enough space in buffer to parse " - "variable data. Got %d, expected %d.\n", - (int)buflen, (int)(sizeof(*hdr) + len)); - result = -EIO; - goto out; - } - result = len; -out: - return result; -} - - -static ssize_t wlp_get_attribute(struct wlp *wlp, u16 type_code, - struct wlp_attr_hdr *attr_hdr, void *value, ssize_t value_len, - ssize_t buflen) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - ssize_t attr_len = sizeof(*attr_hdr) + value_len; - if (buflen < 0) - return -EINVAL; - if (buflen < attr_len) { - dev_err(dev, "WLP: Not enough space in buffer to parse" - " attribute field. Need %d, received %zu\n", - (int)attr_len, buflen); - return -EIO; - } - if (wlp_check_attr_hdr(wlp, attr_hdr, type_code, value_len) < 0) { - dev_err(dev, "WLP: Header verification failed. \n"); - return -EINVAL; - } - memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), value_len); - return attr_len; -} - -static ssize_t wlp_vget_attribute(struct wlp *wlp, u16 type_code, - struct wlp_attr_hdr *attr_hdr, void *value, ssize_t max_value_len, - ssize_t buflen) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - size_t len; - if (buflen < 0) - return -EINVAL; - if (buflen < sizeof(*attr_hdr)) { - dev_err(dev, "WLP: Not enough space in buffer to parse" - " header.\n"); - return -EIO; - } - if (le16_to_cpu(attr_hdr->type) != type_code) { - dev_err(dev, "WLP: Unexpected attribute type. Got %u, " - "expected %u.\n", le16_to_cpu(attr_hdr->type), - type_code); - return -EINVAL; - } - len = le16_to_cpu(attr_hdr->length); - if (len > max_value_len) { - dev_err(dev, "WLP: Attribute larger than maximum " - "allowed. Received %zu, max is %d.\n", len, - (int)max_value_len); - return -EFBIG; - } - if (buflen < sizeof(*attr_hdr) + len) { - dev_err(dev, "WLP: Not enough space in buffer to parse " - "variable data.\n"); - return -EIO; - } - memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), len); - return sizeof(*attr_hdr) + len; -} - -/** - * Get value of attribute from fixed size attribute field. - * - * @attr: Pointer to attribute field. - * @value: Pointer to variable in which attribute value will be placed. - * @buflen: Size of buffer in which attribute field (including header) - * can be found. - * @returns: Amount of given buffer consumed by parsing for this attribute. - * - * The size and type of the value is known by the type of the attribute. - */ -#define wlp_get(type, type_code, name) \ -ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \ - type *value, ssize_t buflen) \ -{ \ - return wlp_get_attribute(wlp, (type_code), &attr->hdr, \ - value, sizeof(*value), buflen); \ -} - -#define wlp_get_sparse(type, type_code, name) \ - static wlp_get(type, type_code, name) - -/** - * Get value of attribute from variable sized attribute field. - * - * @max: The maximum size of this attribute. This value is dictated by - * the maximum value from the WLP specification. - * - * @attr: Pointer to attribute field. - * @value: Pointer to variable that will contain the value. The memory - * must already have been allocated for this value. - * @buflen: Size of buffer in which attribute field (including header) - * can be found. - * @returns: Amount of given bufferconsumed by parsing for this attribute. - */ -#define wlp_vget(type_val, type_code, name, max) \ -static ssize_t wlp_get_##name(struct wlp *wlp, \ - struct wlp_attr_##name *attr, \ - type_val *value, ssize_t buflen) \ -{ \ - return wlp_vget_attribute(wlp, (type_code), &attr->hdr, \ - value, (max), buflen); \ -} - -wlp_get(u8, WLP_ATTR_WLP_VER, version) -wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd) -wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type) -wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err) -wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e) -wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r) -wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid) -wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl) -wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status) -wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast) -wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag) -wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt) -wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce) -wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce) - -/* The buffers for the device info attributes can be found in the - * wlp_device_info struct. These buffers contain one byte more than the - * max allowed by the spec - this is done to be able to add the - * terminating \0 for user display. This terminating byte is not required - * in the actual attribute field (because it has a length field) so the - * maximum allowed for this value is one less than its size in the - * structure. - */ -wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name, - FIELD_SIZEOF(struct wlp_wss, name) - 1) -wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name, - FIELD_SIZEOF(struct wlp_device_info, name) - 1) -wlp_vget(char, WLP_ATTR_MANUF, manufacturer, - FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1) -wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name, - FIELD_SIZEOF(struct wlp_device_info, model_name) - 1) -wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr, - FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1) -wlp_vget(char, WLP_ATTR_SERIAL, serial, - FIELD_SIZEOF(struct wlp_device_info, serial) - 1) - -/** - * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info - * - * @attr: pointer to WSS name attribute in WSS information attribute field - * @info: structure that will be populated with data from WSS information - * field (WSS name, Accept enroll, secure status, broadcast address) - * @buflen: size of buffer - * - * Although the WSSID attribute forms part of the WSS info attribute it is - * retrieved separately and stored in a different location. - */ -static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp, - struct wlp_attr_hdr *attr, - struct wlp_wss_tmp_info *info, - ssize_t buflen) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - void *ptr = attr; - size_t used = 0; - ssize_t result = -EINVAL; - - result = wlp_get_wss_name(wlp, ptr, info->name, buflen); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS name from " - "WSS info in D2 message.\n"); - goto error_parse; - } - used += result; - - result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll, - buflen - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain accepting " - "enrollment from WSS info in D2 message.\n"); - goto error_parse; - } - if (info->accept_enroll != 0 && info->accept_enroll != 1) { - dev_err(dev, "WLP: invalid value for accepting " - "enrollment in D2 message.\n"); - result = -EINVAL; - goto error_parse; - } - used += result; - - result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status, - buflen - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain secure " - "status from WSS info in D2 message.\n"); - goto error_parse; - } - if (info->sec_status != 0 && info->sec_status != 1) { - dev_err(dev, "WLP: invalid value for secure " - "status in D2 message.\n"); - result = -EINVAL; - goto error_parse; - } - used += result; - - result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast, - buflen - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain broadcast " - "address from WSS info in D2 message.\n"); - goto error_parse; - } - used += result; - result = used; -error_parse: - return result; -} - -/** - * Create a new WSSID entry for the neighbor, allocate temporary storage - * - * Each neighbor can have many WSS active. We maintain a list of WSSIDs - * advertised by neighbor. During discovery we also cache information about - * these WSS in temporary storage. - * - * The temporary storage will be removed after it has been used (eg. - * displayed to user), the wssid element will be removed from the list when - * the neighbor is rediscovered or when it disappears. - */ -static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp, - struct wlp_neighbor_e *neighbor) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_wssid_e *wssid_e; - - wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL); - if (wssid_e == NULL) { - dev_err(dev, "WLP: unable to allocate memory " - "for WSS information.\n"); - goto error_alloc; - } - wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL); - if (wssid_e->info == NULL) { - dev_err(dev, "WLP: unable to allocate memory " - "for temporary WSS information.\n"); - kfree(wssid_e); - wssid_e = NULL; - goto error_alloc; - } - list_add(&wssid_e->node, &neighbor->wssid); -error_alloc: - return wssid_e; -} - -/** - * Parse WSS information attribute - * - * @attr: pointer to WSS information attribute header - * @buflen: size of buffer in which WSS information attribute appears - * @wssid: will place wssid from WSS info attribute in this location - * @wss_info: will place other information from WSS information attribute - * in this location - * - * memory for @wssid and @wss_info must be allocated when calling this - */ -static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr, - size_t buflen, struct wlp_uuid *wssid, - struct wlp_wss_tmp_info *wss_info) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - ssize_t result; - size_t len; - size_t used = 0; - void *ptr; - - result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr, - buflen); - if (result < 0) - goto out; - len = result; - used = sizeof(*attr); - ptr = attr; - - result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n"); - goto out; - } - used += result; - result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info, - buflen - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS information " - "from WSS information attributes. \n"); - goto out; - } - used += result; - if (len + sizeof(*attr) != used) { - dev_err(dev, "WLP: Amount of data parsed does not " - "match length field. Parsed %zu, length " - "field %zu. \n", used, len); - result = -EINVAL; - goto out; - } - result = used; -out: - return result; -} - -/** - * Retrieve WSS info from association frame - * - * @attr: pointer to WSS information attribute - * @neighbor: ptr to neighbor being discovered, NULL if enrollment in - * progress - * @wss: ptr to WSS being enrolled in, NULL if discovery in progress - * @buflen: size of buffer in which WSS information appears - * - * The WSS information attribute appears in the D2 association message. - * This message is used in two ways: to discover all neighbors or to enroll - * into a WSS activated by a neighbor. During discovery we only want to - * store the WSS info in a cache, to be deleted right after it has been - * used (eg. displayed to the user). During enrollment we store the WSS - * information for the lifetime of enrollment. - * - * During discovery we are interested in all WSS information, during - * enrollment we are only interested in the WSS being enrolled in. Even so, - * when in enrollment we keep parsing the message after finding the WSS of - * interest, this simplifies the calling routine in that it can be sure - * that all WSS information attributes have been parsed out of the message. - * - * Association frame is process with nbmutex held. The list access is safe. - */ -static ssize_t wlp_get_all_wss_info(struct wlp *wlp, - struct wlp_attr_wss_info *attr, - struct wlp_neighbor_e *neighbor, - struct wlp_wss *wss, ssize_t buflen) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - size_t used = 0; - ssize_t result = -EINVAL; - struct wlp_attr_wss_info *cur; - struct wlp_uuid wssid; - struct wlp_wss_tmp_info wss_info; - unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */ - struct wlp_wssid_e *wssid_e; - char buf[WLP_WSS_UUID_STRSIZE]; - - if (buflen < 0) - goto out; - - if (neighbor != NULL && wss == NULL) - enroll = 0; /* discovery */ - else if (wss != NULL && neighbor == NULL) - enroll = 1; /* enrollment */ - else - goto out; - - cur = attr; - while (buflen - used > 0) { - memset(&wss_info, 0, sizeof(wss_info)); - cur = (void *)cur + used; - result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid, - &wss_info); - if (result == -ENODATA) { - result = used; - goto out; - } else if (result < 0) { - dev_err(dev, "WLP: Unable to parse WSS information " - "from WSS information attribute. \n"); - result = -EINVAL; - goto error_parse; - } - if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) { - if (wss_info.accept_enroll != 1) { - dev_err(dev, "WLP: Requested WSS does " - "not accept enrollment.\n"); - result = -EINVAL; - goto out; - } - memcpy(wss->name, wss_info.name, sizeof(wss->name)); - wss->bcast = wss_info.bcast; - wss->secure_status = wss_info.sec_status; - wss->accept_enroll = wss_info.accept_enroll; - wss->state = WLP_WSS_STATE_PART_ENROLLED; - wlp_wss_uuid_print(buf, sizeof(buf), &wssid); - dev_dbg(dev, "WLP: Found WSS %s. Enrolling.\n", buf); - } else { - wssid_e = wlp_create_wssid_e(wlp, neighbor); - if (wssid_e == NULL) { - dev_err(dev, "WLP: Cannot create new WSSID " - "entry for neighbor %02x:%02x.\n", - neighbor->uwb_dev->dev_addr.data[1], - neighbor->uwb_dev->dev_addr.data[0]); - result = -ENOMEM; - goto out; - } - wssid_e->wssid = wssid; - *wssid_e->info = wss_info; - } - used += result; - } - result = used; -error_parse: - if (result < 0 && !enroll) /* this was a discovery */ - wlp_remove_neighbor_tmp_info(neighbor); -out: - return result; - -} - -/** - * Parse WSS information attributes into cache for discovery - * - * @attr: the first WSS information attribute in message - * @neighbor: the neighbor whose cache will be populated - * @buflen: size of the input buffer - */ -static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp, - struct wlp_attr_wss_info *attr, - struct wlp_neighbor_e *neighbor, - ssize_t buflen) -{ - return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen); -} - -/** - * Parse WSS information attributes into WSS struct for enrollment - * - * @attr: the first WSS information attribute in message - * @wss: the WSS that will be enrolled - * @buflen: size of the input buffer - */ -static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp, - struct wlp_attr_wss_info *attr, - struct wlp_wss *wss, ssize_t buflen) -{ - return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen); -} - -/** - * Construct a D1 association frame - * - * We use the radio control functions to determine the values of the device - * properties. These are of variable length and the total space needed is - * tallied first before we start constructing the message. The radio - * control functions return strings that are terminated with \0. This - * character should not be included in the message (there is a length field - * accompanying it in the attribute). - */ -static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb) -{ - - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - struct wlp_device_info *info; - size_t used = 0; - struct wlp_frame_assoc *_d1; - struct sk_buff *_skb; - void *d1_itr; - - if (wlp->dev_info == NULL) { - result = __wlp_setup_device_info(wlp); - if (result < 0) { - dev_err(dev, "WLP: Unable to setup device " - "information for D1 message.\n"); - goto error; - } - } - info = wlp->dev_info; - _skb = dev_alloc_skb(sizeof(*_d1) - + sizeof(struct wlp_attr_uuid_e) - + sizeof(struct wlp_attr_wss_sel_mthd) - + sizeof(struct wlp_attr_dev_name) - + strlen(info->name) - + sizeof(struct wlp_attr_manufacturer) - + strlen(info->manufacturer) - + sizeof(struct wlp_attr_model_name) - + strlen(info->model_name) - + sizeof(struct wlp_attr_model_nr) - + strlen(info->model_nr) - + sizeof(struct wlp_attr_serial) - + strlen(info->serial) - + sizeof(struct wlp_attr_prim_dev_type) - + sizeof(struct wlp_attr_wlp_assc_err)); - if (_skb == NULL) { - dev_err(dev, "WLP: Cannot allocate memory for association " - "message.\n"); - result = -ENOMEM; - goto error; - } - _d1 = (void *) _skb->data; - _d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); - _d1->hdr.type = WLP_FRAME_ASSOCIATION; - _d1->type = WLP_ASSOC_D1; - - wlp_set_version(&_d1->version, WLP_VERSION); - wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1); - d1_itr = _d1->attr; - used = wlp_set_uuid_e(d1_itr, &wlp->uuid); - used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT); - used += wlp_set_dev_name(d1_itr + used, info->name, - strlen(info->name)); - used += wlp_set_manufacturer(d1_itr + used, info->manufacturer, - strlen(info->manufacturer)); - used += wlp_set_model_name(d1_itr + used, info->model_name, - strlen(info->model_name)); - used += wlp_set_model_nr(d1_itr + used, info->model_nr, - strlen(info->model_nr)); - used += wlp_set_serial(d1_itr + used, info->serial, - strlen(info->serial)); - used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type); - used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE); - skb_put(_skb, sizeof(*_d1) + used); - *skb = _skb; -error: - return result; -} - -/** - * Construct a D2 association frame - * - * We use the radio control functions to determine the values of the device - * properties. These are of variable length and the total space needed is - * tallied first before we start constructing the message. The radio - * control functions return strings that are terminated with \0. This - * character should not be included in the message (there is a length field - * accompanying it in the attribute). - */ -static -int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb, struct wlp_uuid *uuid_e) -{ - - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - struct wlp_device_info *info; - size_t used = 0; - struct wlp_frame_assoc *_d2; - struct sk_buff *_skb; - void *d2_itr; - size_t mem_needed; - - if (wlp->dev_info == NULL) { - result = __wlp_setup_device_info(wlp); - if (result < 0) { - dev_err(dev, "WLP: Unable to setup device " - "information for D2 message.\n"); - goto error; - } - } - info = wlp->dev_info; - mem_needed = sizeof(*_d2) - + sizeof(struct wlp_attr_uuid_e) - + sizeof(struct wlp_attr_uuid_r) - + sizeof(struct wlp_attr_dev_name) - + strlen(info->name) - + sizeof(struct wlp_attr_manufacturer) - + strlen(info->manufacturer) - + sizeof(struct wlp_attr_model_name) - + strlen(info->model_name) - + sizeof(struct wlp_attr_model_nr) - + strlen(info->model_nr) - + sizeof(struct wlp_attr_serial) - + strlen(info->serial) - + sizeof(struct wlp_attr_prim_dev_type) - + sizeof(struct wlp_attr_wlp_assc_err); - if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE) - mem_needed += sizeof(struct wlp_attr_wss_info) - + sizeof(struct wlp_wss_info) - + strlen(wlp->wss.name); - _skb = dev_alloc_skb(mem_needed); - if (_skb == NULL) { - dev_err(dev, "WLP: Cannot allocate memory for association " - "message.\n"); - result = -ENOMEM; - goto error; - } - _d2 = (void *) _skb->data; - _d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); - _d2->hdr.type = WLP_FRAME_ASSOCIATION; - _d2->type = WLP_ASSOC_D2; - - wlp_set_version(&_d2->version, WLP_VERSION); - wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2); - d2_itr = _d2->attr; - used = wlp_set_uuid_e(d2_itr, uuid_e); - used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid); - if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE) - used += wlp_set_wss_info(d2_itr + used, &wlp->wss); - used += wlp_set_dev_name(d2_itr + used, info->name, - strlen(info->name)); - used += wlp_set_manufacturer(d2_itr + used, info->manufacturer, - strlen(info->manufacturer)); - used += wlp_set_model_name(d2_itr + used, info->model_name, - strlen(info->model_name)); - used += wlp_set_model_nr(d2_itr + used, info->model_nr, - strlen(info->model_nr)); - used += wlp_set_serial(d2_itr + used, info->serial, - strlen(info->serial)); - used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type); - used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE); - skb_put(_skb, sizeof(*_d2) + used); - *skb = _skb; -error: - return result; -} - -/** - * Allocate memory for and populate fields of F0 association frame - * - * Currently (while focusing on unsecure enrollment) we ignore the - * nonce's that could be placed in the message. Only the error field is - * populated by the value provided by the caller. - */ -static -int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb, - enum wlp_assc_error error) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = -ENOMEM; - struct { - struct wlp_frame_assoc f0_hdr; - struct wlp_attr_enonce enonce; - struct wlp_attr_rnonce rnonce; - struct wlp_attr_wlp_assc_err assc_err; - } *f0; - struct sk_buff *_skb; - struct wlp_nonce tmp; - - _skb = dev_alloc_skb(sizeof(*f0)); - if (_skb == NULL) { - dev_err(dev, "WLP: Unable to allocate memory for F0 " - "association frame. \n"); - goto error_alloc; - } - f0 = (void *) _skb->data; - f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); - f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION; - f0->f0_hdr.type = WLP_ASSOC_F0; - wlp_set_version(&f0->f0_hdr.version, WLP_VERSION); - wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0); - memset(&tmp, 0, sizeof(tmp)); - wlp_set_enonce(&f0->enonce, &tmp); - wlp_set_rnonce(&f0->rnonce, &tmp); - wlp_set_wlp_assc_err(&f0->assc_err, error); - skb_put(_skb, sizeof(*f0)); - *skb = _skb; - result = 0; -error_alloc: - return result; -} - -/** - * Parse F0 frame - * - * We just retrieve the values and print it as an error to the user. - * Calling function already knows an error occured (F0 indicates error), so - * we just parse the content as debug for higher layers. - */ -int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_frame_assoc *f0 = (void *) skb->data; - void *ptr = skb->data; - size_t len = skb->len; - size_t used; - ssize_t result; - struct wlp_nonce enonce, rnonce; - enum wlp_assc_error assc_err; - char enonce_buf[WLP_WSS_NONCE_STRSIZE]; - char rnonce_buf[WLP_WSS_NONCE_STRSIZE]; - - used = sizeof(*f0); - result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Enrollee nonce " - "attribute from F0 message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Registrar nonce " - "attribute from F0 message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WLP Association error " - "attribute from F0 message.\n"); - goto error_parse; - } - wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce); - wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce); - dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee " - "nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n", - enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err)); - result = 0; -error_parse: - return result; -} - -/** - * Retrieve variable device information from association message - * - * The device information parsed is not required in any message. This - * routine will thus not fail if an attribute is not present. - * The attributes are expected in a certain order, even if all are not - * present. The "attribute type" value is used to ensure the attributes - * are parsed in the correct order. - * - * If an error is encountered during parsing the function will return an - * error code, when this happens the given device_info structure may be - * partially filled. - */ -static -int wlp_get_variable_info(struct wlp *wlp, void *data, - struct wlp_device_info *dev_info, ssize_t len) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - size_t used = 0; - struct wlp_attr_hdr *hdr; - ssize_t result = 0; - unsigned last = 0; - - while (len - used > 0) { - if (len - used < sizeof(*hdr)) { - dev_err(dev, "WLP: Partial data in frame, cannot " - "parse. \n"); - goto error_parse; - } - hdr = data + used; - switch (le16_to_cpu(hdr->type)) { - case WLP_ATTR_MANUF: - if (last >= WLP_ATTR_MANUF) { - dev_err(dev, "WLP: Incorrect order of " - "attribute values in D1 msg.\n"); - goto error_parse; - } - result = wlp_get_manufacturer(wlp, data + used, - dev_info->manufacturer, - len - used); - if (result < 0) { - dev_err(dev, "WLP: Unable to obtain " - "Manufacturer attribute from D1 " - "message.\n"); - goto error_parse; - } - last = WLP_ATTR_MANUF; - used += result; - break; - case WLP_ATTR_MODEL_NAME: - if (last >= WLP_ATTR_MODEL_NAME) { - dev_err(dev, "WLP: Incorrect order of " - "attribute values in D1 msg.\n"); - goto error_parse; - } - result = wlp_get_model_name(wlp, data + used, - dev_info->model_name, - len - used); - if (result < 0) { - dev_err(dev, "WLP: Unable to obtain Model " - "name attribute from D1 message.\n"); - goto error_parse; - } - last = WLP_ATTR_MODEL_NAME; - used += result; - break; - case WLP_ATTR_MODEL_NR: - if (last >= WLP_ATTR_MODEL_NR) { - dev_err(dev, "WLP: Incorrect order of " - "attribute values in D1 msg.\n"); - goto error_parse; - } - result = wlp_get_model_nr(wlp, data + used, - dev_info->model_nr, - len - used); - if (result < 0) { - dev_err(dev, "WLP: Unable to obtain Model " - "number attribute from D1 message.\n"); - goto error_parse; - } - last = WLP_ATTR_MODEL_NR; - used += result; - break; - case WLP_ATTR_SERIAL: - if (last >= WLP_ATTR_SERIAL) { - dev_err(dev, "WLP: Incorrect order of " - "attribute values in D1 msg.\n"); - goto error_parse; - } - result = wlp_get_serial(wlp, data + used, - dev_info->serial, len - used); - if (result < 0) { - dev_err(dev, "WLP: Unable to obtain Serial " - "number attribute from D1 message.\n"); - goto error_parse; - } - last = WLP_ATTR_SERIAL; - used += result; - break; - case WLP_ATTR_PRI_DEV_TYPE: - if (last >= WLP_ATTR_PRI_DEV_TYPE) { - dev_err(dev, "WLP: Incorrect order of " - "attribute values in D1 msg.\n"); - goto error_parse; - } - result = wlp_get_prim_dev_type(wlp, data + used, - &dev_info->prim_dev_type, - len - used); - if (result < 0) { - dev_err(dev, "WLP: Unable to obtain Primary " - "device type attribute from D1 " - "message.\n"); - goto error_parse; - } - dev_info->prim_dev_type.category = - le16_to_cpu(dev_info->prim_dev_type.category); - dev_info->prim_dev_type.subID = - le16_to_cpu(dev_info->prim_dev_type.subID); - last = WLP_ATTR_PRI_DEV_TYPE; - used += result; - break; - default: - /* This is not variable device information. */ - goto out; - break; - } - } -out: - return used; -error_parse: - return -EINVAL; -} - -/** - * Parse incoming D1 frame, populate attribute values - * - * Caller provides pointers to memory already allocated for attributes - * expected in the D1 frame. These variables will be populated. - */ -static -int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb, - struct wlp_uuid *uuid_e, - enum wlp_wss_sel_mthd *sel_mthd, - struct wlp_device_info *dev_info, - enum wlp_assc_error *assc_err) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_frame_assoc *d1 = (void *) skb->data; - void *ptr = skb->data; - size_t len = skb->len; - size_t used; - ssize_t result; - - used = sizeof(*d1); - result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 " - "message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS selection method " - "from D1 message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_dev_name(wlp, ptr + used, dev_info->name, - len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Device Name from D1 " - "message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Device Information from " - "D1 message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WLP Association Error " - "Information from D1 message.\n"); - goto error_parse; - } - result = 0; -error_parse: - return result; -} -/** - * Handle incoming D1 frame - * - * The frame has already been verified to contain an Association header with - * the correct version number. Parse the incoming frame, construct and send - * a D2 frame in response. - * - * It is not clear what to do with most fields in the incoming D1 frame. We - * retrieve and discard the information here for now. - */ -void wlp_handle_d1_frame(struct work_struct *ws) -{ - struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, - struct wlp_assoc_frame_ctx, - ws); - struct wlp *wlp = frame_ctx->wlp; - struct wlp_wss *wss = &wlp->wss; - struct sk_buff *skb = frame_ctx->skb; - struct uwb_dev_addr *src = &frame_ctx->src; - int result; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_uuid uuid_e; - enum wlp_wss_sel_mthd sel_mthd = 0; - struct wlp_device_info dev_info; - enum wlp_assc_error assc_err; - struct sk_buff *resp = NULL; - - /* Parse D1 frame */ - mutex_lock(&wss->mutex); - mutex_lock(&wlp->mutex); /* to access wlp->uuid */ - memset(&dev_info, 0, sizeof(dev_info)); - result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info, - &assc_err); - if (result < 0) { - dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n"); - kfree_skb(skb); - goto out; - } - - kfree_skb(skb); - if (!wlp_uuid_is_set(&wlp->uuid)) { - dev_err(dev, "WLP: UUID is not set. Set via sysfs to " - "proceed. Respong to D1 message with error F0.\n"); - result = wlp_build_assoc_f0(wlp, &resp, - WLP_ASSOC_ERROR_NOT_READY); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct F0 message.\n"); - goto out; - } - } else { - /* Construct D2 frame */ - result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct D2 message.\n"); - goto out; - } - } - /* Send D2 frame */ - BUG_ON(wlp->xmit_frame == NULL); - result = wlp->xmit_frame(wlp, resp, src); - if (result < 0) { - dev_err(dev, "WLP: Unable to transmit D2 association " - "message: %d\n", result); - if (result == -ENXIO) - dev_err(dev, "WLP: Is network interface up? \n"); - /* We could try again ... */ - dev_kfree_skb_any(resp); /* we need to free if tx fails */ - } -out: - kfree(frame_ctx); - mutex_unlock(&wlp->mutex); - mutex_unlock(&wss->mutex); -} - -/** - * Parse incoming D2 frame, create and populate temporary cache - * - * @skb: socket buffer in which D2 frame can be found - * @neighbor: the neighbor that sent the D2 frame - * - * Will allocate memory for temporary storage of information learned during - * discovery. - */ -int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb, - struct wlp_neighbor_e *neighbor) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_frame_assoc *d2 = (void *) skb->data; - void *ptr = skb->data; - size_t len = skb->len; - size_t used; - ssize_t result; - struct wlp_uuid uuid_e; - struct wlp_device_info *nb_info; - enum wlp_assc_error assc_err; - - used = sizeof(*d2); - result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 " - "message.\n"); - goto error_parse; - } - if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) { - dev_err(dev, "WLP: UUID-E in incoming D2 does not match " - "local UUID sent in D1. \n"); - goto error_parse; - } - used += result; - result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 " - "message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor, - len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS information " - "from D2 message.\n"); - goto error_parse; - } - used += result; - neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); - if (neighbor->info == NULL) { - dev_err(dev, "WLP: cannot allocate memory to store device " - "info.\n"); - result = -ENOMEM; - goto error_parse; - } - nb_info = neighbor->info; - result = wlp_get_dev_name(wlp, ptr + used, nb_info->name, - len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Device Name from D2 " - "message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Device Information from " - "D2 message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WLP Association Error " - "Information from D2 message.\n"); - goto error_parse; - } - if (assc_err != WLP_ASSOC_ERROR_NONE) { - dev_err(dev, "WLP: neighbor device returned association " - "error %d\n", assc_err); - result = -EINVAL; - goto error_parse; - } - result = 0; -error_parse: - if (result < 0) - wlp_remove_neighbor_tmp_info(neighbor); - return result; -} - -/** - * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in - * - * @wss: our WSS that will be enrolled - * @skb: socket buffer in which D2 frame can be found - * @neighbor: the neighbor that sent the D2 frame - * @wssid: the wssid of the WSS in which we want to enroll - * - * Forms part of enrollment sequence. We are trying to enroll in WSS with - * @wssid by using @neighbor as registrar. A D1 message was sent to - * @neighbor and now we need to parse the D2 response. The neighbor's - * response is searched for the requested WSS and if found (and it accepts - * enrollment), we store the information. - */ -int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb, - struct wlp_neighbor_e *neighbor, - struct wlp_uuid *wssid) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - void *ptr = skb->data; - size_t len = skb->len; - size_t used; - ssize_t result; - struct wlp_uuid uuid_e; - struct wlp_uuid uuid_r; - struct wlp_device_info nb_info; - enum wlp_assc_error assc_err; - char uuid_bufA[WLP_WSS_UUID_STRSIZE]; - char uuid_bufB[WLP_WSS_UUID_STRSIZE]; - - used = sizeof(struct wlp_frame_assoc); - result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 " - "message.\n"); - goto error_parse; - } - if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) { - dev_err(dev, "WLP: UUID-E in incoming D2 does not match " - "local UUID sent in D1. \n"); - goto error_parse; - } - used += result; - result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 " - "message.\n"); - goto error_parse; - } - if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) { - wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA), - &neighbor->uuid); - wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r); - dev_err(dev, "WLP: UUID of neighbor does not match UUID " - "learned during discovery. Originally discovered: %s, " - "now from D2 message: %s\n", uuid_bufA, uuid_bufB); - result = -EINVAL; - goto error_parse; - } - used += result; - wss->wssid = *wssid; - result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS information " - "from D2 message.\n"); - goto error_parse; - } - if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { - dev_err(dev, "WLP: D2 message did not contain information " - "for successful enrollment. \n"); - result = -EINVAL; - goto error_parse; - } - used += result; - /* Place device information on stack to continue parsing of message */ - result = wlp_get_dev_name(wlp, ptr + used, nb_info.name, - len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Device Name from D2 " - "message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain Device Information from " - "D2 message.\n"); - goto error_parse; - } - used += result; - result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WLP Association Error " - "Information from D2 message.\n"); - goto error_parse; - } - if (assc_err != WLP_ASSOC_ERROR_NONE) { - dev_err(dev, "WLP: neighbor device returned association " - "error %d\n", assc_err); - if (wss->state == WLP_WSS_STATE_PART_ENROLLED) { - dev_err(dev, "WLP: Enrolled in WSS (should not " - "happen according to spec). Undoing. \n"); - wlp_wss_reset(wss); - } - result = -EINVAL; - goto error_parse; - } - result = 0; -error_parse: - return result; -} - -/** - * Parse C3/C4 frame into provided variables - * - * @wssid: will point to copy of wssid retrieved from C3/C4 frame - * @tag: will point to copy of tag retrieved from C3/C4 frame - * @virt_addr: will point to copy of virtual address retrieved from C3/C4 - * frame. - * - * Calling function has to allocate memory for these values. - * - * skb contains a valid C3/C4 frame, return the individual fields of this - * frame in the provided variables. - */ -int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb, - struct wlp_uuid *wssid, u8 *tag, - struct uwb_mac_addr *virt_addr) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result; - void *ptr = skb->data; - size_t len = skb->len; - size_t used; - struct wlp_frame_assoc *assoc = ptr; - - used = sizeof(*assoc); - result = wlp_get_wssid(wlp, ptr + used, wssid, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSSID attribute from " - "%s message.\n", wlp_assoc_frame_str(assoc->type)); - goto error_parse; - } - used += result; - result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS tag attribute from " - "%s message.\n", wlp_assoc_frame_str(assoc->type)); - goto error_parse; - } - used += result; - result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSS virtual address " - "attribute from %s message.\n", - wlp_assoc_frame_str(assoc->type)); - goto error_parse; - } -error_parse: - return result; -} - -/** - * Allocate memory for and populate fields of C1 or C2 association frame - * - * The C1 and C2 association frames appear identical - except for the type. - */ -static -int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb, enum wlp_assoc_type type) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = -ENOMEM; - struct { - struct wlp_frame_assoc c_hdr; - struct wlp_attr_wssid wssid; - } *c; - struct sk_buff *_skb; - - _skb = dev_alloc_skb(sizeof(*c)); - if (_skb == NULL) { - dev_err(dev, "WLP: Unable to allocate memory for C1/C2 " - "association frame. \n"); - goto error_alloc; - } - c = (void *) _skb->data; - c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); - c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION; - c->c_hdr.type = type; - wlp_set_version(&c->c_hdr.version, WLP_VERSION); - wlp_set_msg_type(&c->c_hdr.msg_type, type); - wlp_set_wssid(&c->wssid, &wss->wssid); - skb_put(_skb, sizeof(*c)); - *skb = _skb; - result = 0; -error_alloc: - return result; -} - - -static -int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb) -{ - return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1); -} - -static -int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb) -{ - return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2); -} - - -/** - * Allocate memory for and populate fields of C3 or C4 association frame - * - * The C3 and C4 association frames appear identical - except for the type. - */ -static -int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb, enum wlp_assoc_type type) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = -ENOMEM; - struct { - struct wlp_frame_assoc c_hdr; - struct wlp_attr_wssid wssid; - struct wlp_attr_wss_tag wss_tag; - struct wlp_attr_wss_virt wss_virt; - } *c; - struct sk_buff *_skb; - - _skb = dev_alloc_skb(sizeof(*c)); - if (_skb == NULL) { - dev_err(dev, "WLP: Unable to allocate memory for C3/C4 " - "association frame. \n"); - goto error_alloc; - } - c = (void *) _skb->data; - c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); - c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION; - c->c_hdr.type = type; - wlp_set_version(&c->c_hdr.version, WLP_VERSION); - wlp_set_msg_type(&c->c_hdr.msg_type, type); - wlp_set_wssid(&c->wssid, &wss->wssid); - wlp_set_wss_tag(&c->wss_tag, wss->tag); - wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr); - skb_put(_skb, sizeof(*c)); - *skb = _skb; - result = 0; -error_alloc: - return result; -} - -static -int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb) -{ - return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3); -} - -static -int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss, - struct sk_buff **skb) -{ - return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4); -} - - -#define wlp_send_assoc(type, id) \ -static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss, \ - struct uwb_dev_addr *dev_addr) \ -{ \ - struct device *dev = &wlp->rc->uwb_dev.dev; \ - int result; \ - struct sk_buff *skb = NULL; \ - \ - /* Build the frame */ \ - result = wlp_build_assoc_##type(wlp, wss, &skb); \ - if (result < 0) { \ - dev_err(dev, "WLP: Unable to construct %s association " \ - "frame: %d\n", wlp_assoc_frame_str(id), result);\ - goto error_build_assoc; \ - } \ - /* Send the frame */ \ - BUG_ON(wlp->xmit_frame == NULL); \ - result = wlp->xmit_frame(wlp, skb, dev_addr); \ - if (result < 0) { \ - dev_err(dev, "WLP: Unable to transmit %s association " \ - "message: %d\n", wlp_assoc_frame_str(id), \ - result); \ - if (result == -ENXIO) \ - dev_err(dev, "WLP: Is network interface " \ - "up? \n"); \ - goto error_xmit; \ - } \ - return 0; \ -error_xmit: \ - /* We could try again ... */ \ - dev_kfree_skb_any(skb);/*we need to free if tx fails*/ \ -error_build_assoc: \ - return result; \ -} - -wlp_send_assoc(d1, WLP_ASSOC_D1) -wlp_send_assoc(c1, WLP_ASSOC_C1) -wlp_send_assoc(c3, WLP_ASSOC_C3) - -int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss, - struct uwb_dev_addr *dev_addr, - enum wlp_assoc_type type) -{ - int result = 0; - struct device *dev = &wlp->rc->uwb_dev.dev; - switch (type) { - case WLP_ASSOC_D1: - result = wlp_send_assoc_d1(wlp, wss, dev_addr); - break; - case WLP_ASSOC_C1: - result = wlp_send_assoc_c1(wlp, wss, dev_addr); - break; - case WLP_ASSOC_C3: - result = wlp_send_assoc_c3(wlp, wss, dev_addr); - break; - default: - dev_err(dev, "WLP: Received request to send unknown " - "association message.\n"); - result = -EINVAL; - break; - } - return result; -} - -/** - * Handle incoming C1 frame - * - * The frame has already been verified to contain an Association header with - * the correct version number. Parse the incoming frame, construct and send - * a C2 frame in response. - */ -void wlp_handle_c1_frame(struct work_struct *ws) -{ - struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, - struct wlp_assoc_frame_ctx, - ws); - struct wlp *wlp = frame_ctx->wlp; - struct wlp_wss *wss = &wlp->wss; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data; - unsigned int len = frame_ctx->skb->len; - struct uwb_dev_addr *src = &frame_ctx->src; - int result; - struct wlp_uuid wssid; - struct sk_buff *resp = NULL; - - /* Parse C1 frame */ - mutex_lock(&wss->mutex); - result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid, - len - sizeof(*c1)); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n"); - goto out; - } - if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)) - && wss->state == WLP_WSS_STATE_ACTIVE) { - /* Construct C2 frame */ - result = wlp_build_assoc_c2(wlp, wss, &resp); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct C2 message.\n"); - goto out; - } - } else { - /* Construct F0 frame */ - result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct F0 message.\n"); - goto out; - } - } - /* Send C2 frame */ - BUG_ON(wlp->xmit_frame == NULL); - result = wlp->xmit_frame(wlp, resp, src); - if (result < 0) { - dev_err(dev, "WLP: Unable to transmit response association " - "message: %d\n", result); - if (result == -ENXIO) - dev_err(dev, "WLP: Is network interface up? \n"); - /* We could try again ... */ - dev_kfree_skb_any(resp); /* we need to free if tx fails */ - } -out: - kfree_skb(frame_ctx->skb); - kfree(frame_ctx); - mutex_unlock(&wss->mutex); -} - -/** - * Handle incoming C3 frame - * - * The frame has already been verified to contain an Association header with - * the correct version number. Parse the incoming frame, construct and send - * a C4 frame in response. If the C3 frame identifies a WSS that is locally - * active then we connect to this neighbor (add it to our EDA cache). - */ -void wlp_handle_c3_frame(struct work_struct *ws) -{ - struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, - struct wlp_assoc_frame_ctx, - ws); - struct wlp *wlp = frame_ctx->wlp; - struct wlp_wss *wss = &wlp->wss; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct sk_buff *skb = frame_ctx->skb; - struct uwb_dev_addr *src = &frame_ctx->src; - int result; - struct sk_buff *resp = NULL; - struct wlp_uuid wssid; - u8 tag; - struct uwb_mac_addr virt_addr; - - /* Parse C3 frame */ - mutex_lock(&wss->mutex); - result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain values from C3 frame.\n"); - goto out; - } - if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)) - && wss->state >= WLP_WSS_STATE_ACTIVE) { - result = wlp_eda_update_node(&wlp->eda, src, wss, - (void *) virt_addr.data, tag, - WLP_WSS_CONNECTED); - if (result < 0) { - dev_err(dev, "WLP: Unable to update EDA cache " - "with new connected neighbor information.\n"); - result = wlp_build_assoc_f0(wlp, &resp, - WLP_ASSOC_ERROR_INT); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct F0 " - "message.\n"); - goto out; - } - } else { - wss->state = WLP_WSS_STATE_CONNECTED; - /* Construct C4 frame */ - result = wlp_build_assoc_c4(wlp, wss, &resp); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct C4 " - "message.\n"); - goto out; - } - } - } else { - /* Construct F0 frame */ - result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV); - if (result < 0) { - dev_err(dev, "WLP: Unable to construct F0 message.\n"); - goto out; - } - } - /* Send C4 frame */ - BUG_ON(wlp->xmit_frame == NULL); - result = wlp->xmit_frame(wlp, resp, src); - if (result < 0) { - dev_err(dev, "WLP: Unable to transmit response association " - "message: %d\n", result); - if (result == -ENXIO) - dev_err(dev, "WLP: Is network interface up? \n"); - /* We could try again ... */ - dev_kfree_skb_any(resp); /* we need to free if tx fails */ - } -out: - kfree_skb(frame_ctx->skb); - kfree(frame_ctx); - mutex_unlock(&wss->mutex); -} - - diff --git a/drivers/uwb/wlp/sysfs.c b/drivers/uwb/wlp/sysfs.c deleted file mode 100644 index 6627c94cc85..00000000000 --- a/drivers/uwb/wlp/sysfs.c +++ /dev/null @@ -1,708 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * sysfs functions - * - * Copyright (C) 2007 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: Docs - * - */ -#include <linux/wlp.h> - -#include "wlp-internal.h" - -static -size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize, - struct wlp_wssid_e *wssid_e) -{ - size_t used = 0; - used += scnprintf(buf, bufsize, " WSS: "); - used += wlp_wss_uuid_print(buf + used, bufsize - used, - &wssid_e->wssid); - - if (wssid_e->info != NULL) { - used += scnprintf(buf + used, bufsize - used, " "); - used += uwb_mac_addr_print(buf + used, bufsize - used, - &wssid_e->info->bcast); - used += scnprintf(buf + used, bufsize - used, " %u %u %s\n", - wssid_e->info->accept_enroll, - wssid_e->info->sec_status, - wssid_e->info->name); - } - return used; -} - -/** - * Print out information learned from neighbor discovery - * - * Some fields being printed may not be included in the device discovery - * information (it is not mandatory). We are thus careful how the - * information is printed to ensure it is clear to the user what field is - * being referenced. - * The information being printed is for one time use - temporary storage is - * cleaned after it is printed. - * - * Ideally sysfs output should be on one line. The information printed here - * contain a few strings so it will be hard to parse if they are all - * printed on the same line - without agreeing on a standard field - * separator. - */ -static -ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf, - size_t bufsize) -{ - size_t used = 0; - struct wlp_neighbor_e *neighb; - struct wlp_wssid_e *wssid_e; - - mutex_lock(&wlp->nbmutex); - used = scnprintf(buf, bufsize, "#Neighbor information\n" - "#uuid dev_addr\n" - "# Device Name:\n# Model Name:\n# Manufacturer:\n" - "# Model Nr:\n# Serial:\n" - "# Pri Dev type: CategoryID OUI OUISubdiv " - "SubcategoryID\n" - "# WSS: WSSID WSS_name accept_enroll sec_status " - "bcast\n" - "# WSS: WSSID WSS_name accept_enroll sec_status " - "bcast\n\n"); - list_for_each_entry(neighb, &wlp->neighbors, node) { - if (bufsize - used <= 0) - goto out; - used += wlp_wss_uuid_print(buf + used, bufsize - used, - &neighb->uuid); - buf[used++] = ' '; - used += uwb_dev_addr_print(buf + used, bufsize - used, - &neighb->uwb_dev->dev_addr); - if (neighb->info != NULL) - used += scnprintf(buf + used, bufsize - used, - "\n Device Name: %s\n" - " Model Name: %s\n" - " Manufacturer:%s \n" - " Model Nr: %s\n" - " Serial: %s\n" - " Pri Dev type: " - "%u %02x:%02x:%02x %u %u\n", - neighb->info->name, - neighb->info->model_name, - neighb->info->manufacturer, - neighb->info->model_nr, - neighb->info->serial, - neighb->info->prim_dev_type.category, - neighb->info->prim_dev_type.OUI[0], - neighb->info->prim_dev_type.OUI[1], - neighb->info->prim_dev_type.OUI[2], - neighb->info->prim_dev_type.OUIsubdiv, - neighb->info->prim_dev_type.subID); - list_for_each_entry(wssid_e, &neighb->wssid, node) { - used += wlp_wss_wssid_e_print(buf + used, - bufsize - used, - wssid_e); - } - buf[used++] = '\n'; - wlp_remove_neighbor_tmp_info(neighb); - } - - -out: - mutex_unlock(&wlp->nbmutex); - return used; -} - - -/** - * Show properties of all WSS in neighborhood. - * - * Will trigger a complete discovery of WSS activated by this device and - * its neighbors. - */ -ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf) -{ - wlp_discover(wlp); - return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE); -} -EXPORT_SYMBOL_GPL(wlp_neighborhood_show); - -static -ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf, - size_t bufsize) -{ - ssize_t result; - - result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid); - result += scnprintf(buf + result, bufsize - result, " "); - result += uwb_mac_addr_print(buf + result, bufsize - result, - &wss->bcast); - result += scnprintf(buf + result, bufsize - result, - " 0x%02x %u ", wss->hash, wss->secure_status); - result += wlp_wss_key_print(buf + result, bufsize - result, - wss->master_key); - result += scnprintf(buf + result, bufsize - result, " 0x%02x ", - wss->tag); - result += uwb_mac_addr_print(buf + result, bufsize - result, - &wss->virtual_addr); - result += scnprintf(buf + result, bufsize - result, " %s", wss->name); - result += scnprintf(buf + result, bufsize - result, - "\n\n#WSSID\n#WSS broadcast address\n" - "#WSS hash\n#WSS secure status\n" - "#WSS master key\n#WSS local tag\n" - "#WSS local virtual EUI-48\n#WSS name\n"); - return result; -} - -/** - * Show which WSS is activated. - */ -ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf) -{ - int result = 0; - - if (mutex_lock_interruptible(&wss->mutex)) - goto out; - if (wss->state >= WLP_WSS_STATE_ACTIVE) - result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); - else - result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n"); - result += scnprintf(buf + result, PAGE_SIZE - result, - "\n\n" - "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT " - "NAME #create new WSS\n" - "# echo WSSID [DEV ADDR] #enroll in and activate " - "existing WSS, can request registrar\n" - "#\n" - "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n" - "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n" - "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n" - "# NAME is the text string identifying the WSS\n" - "# DEV ADDR is the device address of neighbor " - "that should be registrar. Eg. 32:AB\n"); - - mutex_unlock(&wss->mutex); -out: - return result; - -} -EXPORT_SYMBOL_GPL(wlp_wss_activate_show); - -/** - * Create/activate a new WSS or enroll/activate in neighboring WSS - * - * The user can provide the WSSID of a WSS in which it wants to enroll. - * Only the WSSID is necessary if the WSS have been discovered before. If - * the WSS has not been discovered before, or the user wants to use a - * particular neighbor as its registrar, then the user can also provide a - * device address or the neighbor that will be used as registrar. - * - * A new WSS is created when the user provides a WSSID, secure status, and - * WSS name. - */ -ssize_t wlp_wss_activate_store(struct wlp_wss *wss, - const char *buf, size_t size) -{ - ssize_t result = -EINVAL; - struct wlp_uuid wssid; - struct uwb_dev_addr dev; - struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; - char name[65]; - unsigned sec_status, accept; - memset(name, 0, sizeof(name)); - result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx:%02hhx", - &wssid.data[0] , &wssid.data[1], - &wssid.data[2] , &wssid.data[3], - &wssid.data[4] , &wssid.data[5], - &wssid.data[6] , &wssid.data[7], - &wssid.data[8] , &wssid.data[9], - &wssid.data[10], &wssid.data[11], - &wssid.data[12], &wssid.data[13], - &wssid.data[14], &wssid.data[15], - &dev.data[1], &dev.data[0]); - if (result == 16 || result == 17) { - result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%u %u %64c", - &wssid.data[0] , &wssid.data[1], - &wssid.data[2] , &wssid.data[3], - &wssid.data[4] , &wssid.data[5], - &wssid.data[6] , &wssid.data[7], - &wssid.data[8] , &wssid.data[9], - &wssid.data[10], &wssid.data[11], - &wssid.data[12], &wssid.data[13], - &wssid.data[14], &wssid.data[15], - &sec_status, &accept, name); - if (result == 16) - result = wlp_wss_enroll_activate(wss, &wssid, &bcast); - else if (result == 19) { - sec_status = sec_status == 0 ? 0 : 1; - accept = accept == 0 ? 0 : 1; - /* We read name using %c, so the newline needs to be - * removed */ - if (strlen(name) != sizeof(name) - 1) - name[strlen(name) - 1] = '\0'; - result = wlp_wss_create_activate(wss, &wssid, name, - sec_status, accept); - } else - result = -EINVAL; - } else if (result == 18) - result = wlp_wss_enroll_activate(wss, &wssid, &dev); - else - result = -EINVAL; - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_wss_activate_store); - -/** - * Show the UUID of this host - */ -ssize_t wlp_uuid_show(struct wlp *wlp, char *buf) -{ - ssize_t result = 0; - - mutex_lock(&wlp->mutex); - result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid); - buf[result++] = '\n'; - mutex_unlock(&wlp->mutex); - return result; -} -EXPORT_SYMBOL_GPL(wlp_uuid_show); - -/** - * Store a new UUID for this host - * - * According to the spec this should be encoded as an octet string in the - * order the octets are shown in string representation in RFC 4122 (WLP - * 0.99 [Table 6]) - * - * We do not check value provided by user. - */ -ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size) -{ - ssize_t result; - struct wlp_uuid uuid; - - mutex_lock(&wlp->mutex); - result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx ", - &uuid.data[0] , &uuid.data[1], - &uuid.data[2] , &uuid.data[3], - &uuid.data[4] , &uuid.data[5], - &uuid.data[6] , &uuid.data[7], - &uuid.data[8] , &uuid.data[9], - &uuid.data[10], &uuid.data[11], - &uuid.data[12], &uuid.data[13], - &uuid.data[14], &uuid.data[15]); - if (result != 16) { - result = -EINVAL; - goto error; - } - wlp->uuid = uuid; -error: - mutex_unlock(&wlp->mutex); - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_uuid_store); - -/** - * Show contents of members of device information structure - */ -#define wlp_dev_info_show(type) \ -ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \ -{ \ - ssize_t result = 0; \ - mutex_lock(&wlp->mutex); \ - if (wlp->dev_info == NULL) { \ - result = __wlp_setup_device_info(wlp); \ - if (result < 0) \ - goto out; \ - } \ - result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\ -out: \ - mutex_unlock(&wlp->mutex); \ - return result; \ -} \ -EXPORT_SYMBOL_GPL(wlp_dev_##type##_show); - -wlp_dev_info_show(name) -wlp_dev_info_show(model_name) -wlp_dev_info_show(model_nr) -wlp_dev_info_show(manufacturer) -wlp_dev_info_show(serial) - -/** - * Store contents of members of device information structure - */ -#define wlp_dev_info_store(type, len) \ -ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\ -{ \ - ssize_t result; \ - char format[10]; \ - mutex_lock(&wlp->mutex); \ - if (wlp->dev_info == NULL) { \ - result = __wlp_alloc_device_info(wlp); \ - if (result < 0) \ - goto out; \ - } \ - memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \ - sprintf(format, "%%%uc", len); \ - result = sscanf(buf, format, wlp->dev_info->type); \ -out: \ - mutex_unlock(&wlp->mutex); \ - return result < 0 ? result : size; \ -} \ -EXPORT_SYMBOL_GPL(wlp_dev_##type##_store); - -wlp_dev_info_store(name, 32) -wlp_dev_info_store(manufacturer, 64) -wlp_dev_info_store(model_name, 32) -wlp_dev_info_store(model_nr, 32) -wlp_dev_info_store(serial, 32) - -static -const char *__wlp_dev_category[] = { - [WLP_DEV_CAT_COMPUTER] = "Computer", - [WLP_DEV_CAT_INPUT] = "Input device", - [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or " - "Copier", - [WLP_DEV_CAT_CAMERA] = "Camera", - [WLP_DEV_CAT_STORAGE] = "Storage Network", - [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure", - [WLP_DEV_CAT_DISPLAY] = "Display", - [WLP_DEV_CAT_MULTIM] = "Multimedia device", - [WLP_DEV_CAT_GAMING] = "Gaming device", - [WLP_DEV_CAT_TELEPHONE] = "Telephone", - [WLP_DEV_CAT_OTHER] = "Other", -}; - -static -const char *wlp_dev_category_str(unsigned cat) -{ - if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) - || cat == WLP_DEV_CAT_OTHER) - return __wlp_dev_category[cat]; - return "unknown category"; -} - -ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf) -{ - ssize_t result = 0; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_setup_device_info(wlp); - if (result < 0) - goto out; - } - result = scnprintf(buf, PAGE_SIZE, "%s\n", - wlp_dev_category_str(wlp->dev_info->prim_dev_type.category)); -out: - mutex_unlock(&wlp->mutex); - return result; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show); - -ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf, - size_t size) -{ - ssize_t result; - u16 cat; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_alloc_device_info(wlp); - if (result < 0) - goto out; - } - result = sscanf(buf, "%hu", &cat); - if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) - || cat == WLP_DEV_CAT_OTHER) - wlp->dev_info->prim_dev_type.category = cat; - else - result = -EINVAL; -out: - mutex_unlock(&wlp->mutex); - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store); - -ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf) -{ - ssize_t result = 0; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_setup_device_info(wlp); - if (result < 0) - goto out; - } - result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n", - wlp->dev_info->prim_dev_type.OUI[0], - wlp->dev_info->prim_dev_type.OUI[1], - wlp->dev_info->prim_dev_type.OUI[2]); -out: - mutex_unlock(&wlp->mutex); - return result; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show); - -ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size) -{ - ssize_t result; - u8 OUI[3]; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_alloc_device_info(wlp); - if (result < 0) - goto out; - } - result = sscanf(buf, "%hhx:%hhx:%hhx", - &OUI[0], &OUI[1], &OUI[2]); - if (result != 3) { - result = -EINVAL; - goto out; - } else - memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI)); -out: - mutex_unlock(&wlp->mutex); - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store); - - -ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf) -{ - ssize_t result = 0; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_setup_device_info(wlp); - if (result < 0) - goto out; - } - result = scnprintf(buf, PAGE_SIZE, "%u\n", - wlp->dev_info->prim_dev_type.OUIsubdiv); -out: - mutex_unlock(&wlp->mutex); - return result; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show); - -ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf, - size_t size) -{ - ssize_t result; - unsigned sub; - u8 max_sub = ~0; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_alloc_device_info(wlp); - if (result < 0) - goto out; - } - result = sscanf(buf, "%u", &sub); - if (sub <= max_sub) - wlp->dev_info->prim_dev_type.OUIsubdiv = sub; - else - result = -EINVAL; -out: - mutex_unlock(&wlp->mutex); - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store); - -ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf) -{ - ssize_t result = 0; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_setup_device_info(wlp); - if (result < 0) - goto out; - } - result = scnprintf(buf, PAGE_SIZE, "%u\n", - wlp->dev_info->prim_dev_type.subID); -out: - mutex_unlock(&wlp->mutex); - return result; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show); - -ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf, - size_t size) -{ - ssize_t result; - unsigned sub; - __le16 max_sub = ~0; - mutex_lock(&wlp->mutex); - if (wlp->dev_info == NULL) { - result = __wlp_alloc_device_info(wlp); - if (result < 0) - goto out; - } - result = sscanf(buf, "%u", &sub); - if (sub <= max_sub) - wlp->dev_info->prim_dev_type.subID = sub; - else - result = -EINVAL; -out: - mutex_unlock(&wlp->mutex); - return result < 0 ? result : size; -} -EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store); - -/** - * Subsystem implementation for interaction with individual WSS via sysfs - * - * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt - */ - -#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj) -#define attr_to_wlp_wss_attr(_attr) \ - container_of(_attr, struct wlp_wss_attribute, attr) - -/** - * Sysfs subsystem: forward read calls - * - * Sysfs operation for forwarding read call to the show method of the - * attribute owner - */ -static -ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); - struct wlp_wss *wss = kobj_to_wlp_wss(kobj); - ssize_t ret = -EIO; - - if (wss_attr->show) - ret = wss_attr->show(wss, buf); - return ret; -} -/** - * Sysfs subsystem: forward write calls - * - * Sysfs operation for forwarding write call to the store method of the - * attribute owner - */ -static -ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); - struct wlp_wss *wss = kobj_to_wlp_wss(kobj); - ssize_t ret = -EIO; - - if (wss_attr->store) - ret = wss_attr->store(wss, buf, count); - return ret; -} - -static const struct sysfs_ops wss_sysfs_ops = { - .show = wlp_wss_attr_show, - .store = wlp_wss_attr_store, -}; - -struct kobj_type wss_ktype = { - .release = wlp_wss_release, - .sysfs_ops = &wss_sysfs_ops, -}; - - -/** - * Sysfs files for individual WSS - */ - -/** - * Print static properties of this WSS - * - * The name of a WSS may not be null teminated. It's max size is 64 bytes - * so we copy it to a larger array just to make sure we print sane data. - */ -static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf) -{ - int result = 0; - - if (mutex_lock_interruptible(&wss->mutex)) - goto out; - result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); - mutex_unlock(&wss->mutex); -out: - return result; -} -WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL); - -/** - * Print all connected members of this WSS - * The EDA cache contains all members of WSS neighborhood. - */ -static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - return wlp_eda_show(wlp, buf); -} -WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL); - -static -const char *__wlp_strstate[] = { - "none", - "partially enrolled", - "enrolled", - "active", - "connected", -}; - -static const char *wlp_wss_strstate(unsigned state) -{ - if (state >= ARRAY_SIZE(__wlp_strstate)) - return "unknown state"; - return __wlp_strstate[state]; -} - -/* - * Print current state of this WSS - */ -static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf) -{ - int result = 0; - - if (mutex_lock_interruptible(&wss->mutex)) - goto out; - result = scnprintf(buf, PAGE_SIZE, "%s\n", - wlp_wss_strstate(wss->state)); - mutex_unlock(&wss->mutex); -out: - return result; -} -WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL); - - -static -struct attribute *wss_attrs[] = { - &wss_attr_properties.attr, - &wss_attr_members.attr, - &wss_attr_state.attr, - NULL, -}; - -struct attribute_group wss_attr_group = { - .name = NULL, /* we want them in the same directory */ - .attrs = wss_attrs, -}; diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c deleted file mode 100644 index 05dde44b359..00000000000 --- a/drivers/uwb/wlp/txrx.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * Message exchange infrastructure - * - * Copyright (C) 2007 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: Docs - * - */ - -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <linux/wlp.h> - -#include "wlp-internal.h" - -/* - * Direct incoming association msg to correct parsing routine - * - * We only expect D1, E1, C1, C3 messages as new. All other incoming - * association messages should form part of an established session that is - * handled elsewhere. - * The handling of these messages often require calling sleeping functions - * - this cannot be done in interrupt context. We use the kernel's - * workqueue to handle these messages. - */ -static -void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb, - struct uwb_dev_addr *src) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_frame_assoc *assoc = (void *) skb->data; - struct wlp_assoc_frame_ctx *frame_ctx; - - frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC); - if (frame_ctx == NULL) { - dev_err(dev, "WLP: Unable to allocate memory for association " - "frame handling.\n"); - kfree_skb(skb); - return; - } - frame_ctx->wlp = wlp; - frame_ctx->skb = skb; - frame_ctx->src = *src; - switch (assoc->type) { - case WLP_ASSOC_D1: - INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame); - schedule_work(&frame_ctx->ws); - break; - case WLP_ASSOC_E1: - kfree_skb(skb); /* Temporary until we handle it */ - kfree(frame_ctx); /* Temporary until we handle it */ - break; - case WLP_ASSOC_C1: - INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame); - schedule_work(&frame_ctx->ws); - break; - case WLP_ASSOC_C3: - INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame); - schedule_work(&frame_ctx->ws); - break; - default: - dev_err(dev, "Received unexpected association frame. " - "Type = %d \n", assoc->type); - kfree_skb(skb); - kfree(frame_ctx); - break; - } -} - -/* - * Process incoming association frame - * - * Although it could be possible to deal with some incoming association - * messages without creating a new session we are keeping things simple. We - * do not accept new association messages if there is a session in progress - * and the messages do not belong to that session. - * - * If an association message arrives that causes the creation of a session - * (WLP_ASSOC_E1) while we are in the process of creating a session then we - * rely on the neighbor mutex to protect the data. That is, the new session - * will not be started until the previous is completed. - */ -static -void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb, - struct uwb_dev_addr *src) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_frame_assoc *assoc = (void *) skb->data; - struct wlp_session *session = wlp->session; - u8 version; - - if (wlp_get_version(wlp, &assoc->version, &version, - sizeof(assoc->version)) < 0) - goto error; - if (version != WLP_VERSION) { - dev_err(dev, "Unsupported WLP version in association " - "message.\n"); - goto error; - } - if (session != NULL) { - /* Function that created this session is still holding the - * &wlp->mutex to protect this session. */ - if (assoc->type == session->exp_message || - assoc->type == WLP_ASSOC_F0) { - if (!memcmp(&session->neighbor_addr, src, - sizeof(*src))) { - session->data = skb; - (session->cb)(wlp); - } else { - dev_err(dev, "Received expected message from " - "unexpected source. Expected message " - "%d or F0 from %02x:%02x, but received " - "it from %02x:%02x. Dropping.\n", - session->exp_message, - session->neighbor_addr.data[1], - session->neighbor_addr.data[0], - src->data[1], src->data[0]); - goto error; - } - } else { - dev_err(dev, "Association already in progress. " - "Dropping.\n"); - goto error; - } - } else { - wlp_direct_assoc_frame(wlp, skb, src); - } - return; -error: - kfree_skb(skb); -} - -/* - * Verify incoming frame is from connected neighbor, prep to pass to WLP client - * - * Verification proceeds according to WLP 0.99 [7.3.1]. The source address - * is used to determine which neighbor is sending the frame and the WSS tag - * is used to know to which WSS the frame belongs (we only support one WSS - * so this test is straight forward). - * With the WSS found we need to ensure that we are connected before - * allowing the exchange of data frames. - */ -static -int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb, - struct uwb_dev_addr *src) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = -EINVAL; - struct wlp_eda_node eda_entry; - struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data; - - /*verify*/ - result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Incoming frame is from unknown " - "neighbor %02x:%02x.\n", src->data[1], - src->data[0]); - goto out; - } - if (hdr->tag != eda_entry.tag) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Tag of incoming frame from " - "%02x:%02x does not match expected tag. " - "Received 0x%02x, expected 0x%02x. \n", - src->data[1], src->data[0], hdr->tag, - eda_entry.tag); - result = -EINVAL; - goto out; - } - if (eda_entry.state != WLP_WSS_CONNECTED) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Incoming frame from " - "%02x:%02x does is not from connected WSS.\n", - src->data[1], src->data[0]); - result = -EINVAL; - goto out; - } - /*prep*/ - skb_pull(skb, sizeof(*hdr)); -out: - return result; -} - -/* - * Receive a WLP frame from device - * - * @returns: 1 if calling function should free the skb - * 0 if it successfully handled skb and freed it - * 0 if error occured, will free skb in this case - */ -int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb, - struct uwb_dev_addr *src) -{ - unsigned len = skb->len; - void *ptr = skb->data; - struct wlp_frame_hdr *hdr; - int result = 0; - - if (len < sizeof(*hdr)) { - dev_err(dev, "Not enough data to parse WLP header.\n"); - result = -EINVAL; - goto out; - } - hdr = ptr; - if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) { - dev_err(dev, "Not a WLP frame type.\n"); - result = -EINVAL; - goto out; - } - switch (hdr->type) { - case WLP_FRAME_STANDARD: - if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) { - dev_err(dev, "Not enough data to parse Standard " - "WLP header.\n"); - goto out; - } - result = wlp_verify_prep_rx_frame(wlp, skb, src); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Verification of frame " - "from neighbor %02x:%02x failed.\n", - src->data[1], src->data[0]); - goto out; - } - result = 1; - break; - case WLP_FRAME_ABBREVIATED: - dev_err(dev, "Abbreviated frame received. FIXME?\n"); - kfree_skb(skb); - break; - case WLP_FRAME_CONTROL: - dev_err(dev, "Control frame received. FIXME?\n"); - kfree_skb(skb); - break; - case WLP_FRAME_ASSOCIATION: - if (len < sizeof(struct wlp_frame_assoc)) { - dev_err(dev, "Not enough data to parse Association " - "WLP header.\n"); - goto out; - } - wlp_receive_assoc_frame(wlp, skb, src); - break; - default: - dev_err(dev, "Invalid frame received.\n"); - result = -EINVAL; - break; - } -out: - if (result < 0) { - kfree_skb(skb); - result = 0; - } - return result; -} -EXPORT_SYMBOL_GPL(wlp_receive_frame); - - -/* - * Verify frame from network stack, prepare for further transmission - * - * @skb: the socket buffer that needs to be prepared for transmission (it - * is in need of a WLP header). If this is a broadcast frame we take - * over the entire transmission. - * If it is a unicast the WSS connection should already be established - * and transmission will be done by the calling function. - * @dst: On return this will contain the device address to which the - * frame is destined. - * @returns: 0 on success no tx : WLP header successfully applied to skb buffer, - * calling function can proceed with tx - * 1 on success with tx : WLP will take over transmission of this - * frame - * <0 on error - * - * The network stack (WLP client) is attempting to transmit a frame. We can - * only transmit data if a local WSS is at least active (connection will be - * done here if this is a broadcast frame and neighbor also has the WSS - * active). - * - * The frame can be either broadcast or unicast. Broadcast in a WSS is - * supported via multicast, but we don't support multicast yet (until - * devices start to support MAB IEs). If a broadcast frame needs to be - * transmitted it is treated as a unicast frame to each neighbor. In this - * case the WLP takes over transmission of the skb and returns 1 - * to the caller to indicate so. Also, in this case, if a neighbor has the - * same WSS activated but is not connected then the WSS connection will be - * done at this time. The neighbor's virtual address will be learned at - * this time. - * - * The destination address in a unicast frame is the virtual address of the - * neighbor. This address only becomes known when a WSS connection is - * established. We thus rely on a broadcast frame to trigger the setup of - * WSS connections to all neighbors before we are able to send unicast - * frames to them. This seems reasonable as IP would usually use ARP first - * before any unicast frames are sent. - * - * If we are already connected to the neighbor (neighbor's virtual address - * is known) we just prepare the WLP header and the caller will continue to - * send the frame. - * - * A failure in this function usually indicates something that cannot be - * fixed automatically. So, if this function fails (@return < 0) the calling - * function should not retry to send the frame as it will very likely keep - * failing. - * - */ -int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp, - struct sk_buff *skb, struct uwb_dev_addr *dst) -{ - int result = -EINVAL; - struct ethhdr *eth_hdr = (void *) skb->data; - - if (is_multicast_ether_addr(eth_hdr->h_dest)) { - result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "Unable to handle broadcast " - "frame from WLP client.\n"); - goto out; - } - dev_kfree_skb_irq(skb); - result = 1; - /* Frame will be transmitted by WLP. */ - } else { - result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst, - wlp_wss_prep_hdr, skb); - if (unlikely(result < 0)) { - if (printk_ratelimit()) - dev_err(dev, "Unable to prepare " - "skb for transmission. \n"); - goto out; - } - } -out: - return result; -} -EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame); diff --git a/drivers/uwb/wlp/wlp-internal.h b/drivers/uwb/wlp/wlp-internal.h deleted file mode 100644 index 3e8d5de7c5b..00000000000 --- a/drivers/uwb/wlp/wlp-internal.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * Internal API - * - * Copyright (C) 2007 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#ifndef __WLP_INTERNAL_H__ -#define __WLP_INTERNAL_H__ - -/** - * State of WSS connection - * - * A device needs to connect to a neighbor in an activated WSS before data - * can be transmitted. The spec also distinguishes between a new connection - * attempt and a connection attempt after previous connection attempts. The - * state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99 - * [7.2.6] - */ -enum wlp_wss_connect { - WLP_WSS_UNCONNECTED = 0, - WLP_WSS_CONNECTED, - WLP_WSS_CONNECT_FAILED, -}; - -extern struct kobj_type wss_ktype; -extern struct attribute_group wss_attr_group; - -/* This should be changed to a dynamic array where entries are sorted - * by eth_addr and search is done in a binary form - * - * Although thinking twice about it: this technologie's maximum reach - * is 10 meters...unless you want to pack too much stuff in around - * your radio controller/WLP device, the list will probably not be - * too big. - * - * In any case, there is probably some data structure in the kernel - * than we could reused for that already. - * - * The below structure is really just good while we support one WSS per - * host. - */ -struct wlp_eda_node { - struct list_head list_node; - unsigned char eth_addr[ETH_ALEN]; - struct uwb_dev_addr dev_addr; - struct wlp_wss *wss; - unsigned char virt_addr[ETH_ALEN]; - u8 tag; - enum wlp_wss_connect state; -}; - -typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *); - -extern void wlp_eda_init(struct wlp_eda *); -extern void wlp_eda_release(struct wlp_eda *); -extern int wlp_eda_create_node(struct wlp_eda *, - const unsigned char eth_addr[ETH_ALEN], - const struct uwb_dev_addr *); -extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *); -extern int wlp_eda_update_node(struct wlp_eda *, - const struct uwb_dev_addr *, - struct wlp_wss *, - const unsigned char virt_addr[ETH_ALEN], - const u8, const enum wlp_wss_connect); -extern int wlp_eda_update_node_state(struct wlp_eda *, - const struct uwb_dev_addr *, - const enum wlp_wss_connect); - -extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *, - struct wlp_eda_node *); -extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *); -extern int wlp_eda_for_virtual(struct wlp_eda *, - const unsigned char eth_addr[ETH_ALEN], - struct uwb_dev_addr *, - wlp_eda_for_each_f , void *); - - -extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *); - -extern size_t wlp_wss_key_print(char *, size_t, u8 *); - -/* Function called when no more references to WSS exists */ -extern void wlp_wss_release(struct kobject *); - -extern void wlp_wss_reset(struct wlp_wss *); -extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *, - char *, unsigned, unsigned); -extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *, - struct uwb_dev_addr *); -extern ssize_t wlp_discover(struct wlp *); - -extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *, - struct wlp_wss *, struct wlp_uuid *); -extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *, - struct uwb_dev_addr *); - -struct wlp_assoc_conn_ctx { - struct work_struct ws; - struct wlp *wlp; - struct sk_buff *skb; - struct wlp_eda_node eda_entry; -}; - - -extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *); -extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *); - - -/* Message handling */ -struct wlp_assoc_frame_ctx { - struct work_struct ws; - struct wlp *wlp; - struct sk_buff *skb; - struct uwb_dev_addr src; -}; - -extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *); -extern void wlp_handle_d1_frame(struct work_struct *); -extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *, - struct wlp_neighbor_e *); -extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *, - struct wlp_neighbor_e *, - struct wlp_uuid *); -extern void wlp_handle_c1_frame(struct work_struct *); -extern void wlp_handle_c3_frame(struct work_struct *); -extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *, - struct wlp_uuid *, u8 *, - struct uwb_mac_addr *); -extern int wlp_parse_f0(struct wlp *, struct sk_buff *); -extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *, - struct uwb_dev_addr *, enum wlp_assoc_type); -extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *, - u8 *, ssize_t); -extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *, - struct wlp_uuid *, ssize_t); -extern int __wlp_alloc_device_info(struct wlp *); -extern int __wlp_setup_device_info(struct wlp *); - -extern struct wlp_wss_attribute wss_attribute_properties; -extern struct wlp_wss_attribute wss_attribute_members; -extern struct wlp_wss_attribute wss_attribute_state; - -static inline -size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid) -{ - size_t result; - - result = scnprintf(buf, bufsize, - "%02x:%02x:%02x:%02x:%02x:%02x:" - "%02x:%02x:%02x:%02x:%02x:%02x:" - "%02x:%02x:%02x:%02x", - uuid->data[0], uuid->data[1], - uuid->data[2], uuid->data[3], - uuid->data[4], uuid->data[5], - uuid->data[6], uuid->data[7], - uuid->data[8], uuid->data[9], - uuid->data[10], uuid->data[11], - uuid->data[12], uuid->data[13], - uuid->data[14], uuid->data[15]); - return result; -} - -/** - * FIXME: How should a nonce be displayed? - */ -static inline -size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce) -{ - size_t result; - - result = scnprintf(buf, bufsize, - "%02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x", - nonce->data[0], nonce->data[1], - nonce->data[2], nonce->data[3], - nonce->data[4], nonce->data[5], - nonce->data[6], nonce->data[7], - nonce->data[8], nonce->data[9], - nonce->data[10], nonce->data[11], - nonce->data[12], nonce->data[13], - nonce->data[14], nonce->data[15]); - return result; -} - - -static inline -void wlp_session_cb(struct wlp *wlp) -{ - struct completion *completion = wlp->session->cb_priv; - complete(completion); -} - -static inline -int wlp_uuid_is_set(struct wlp_uuid *uuid) -{ - struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} }; - - if (!memcmp(uuid, &zero_uuid, sizeof(*uuid))) - return 0; - return 1; -} - -#endif /* __WLP_INTERNAL_H__ */ diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c deleted file mode 100644 index 7f6a630bf26..00000000000 --- a/drivers/uwb/wlp/wlp-lc.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * - * Copyright (C) 2005-2006 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - */ -#include <linux/wlp.h> -#include <linux/slab.h> - -#include "wlp-internal.h" - -static -void wlp_neighbor_init(struct wlp_neighbor_e *neighbor) -{ - INIT_LIST_HEAD(&neighbor->wssid); -} - -/** - * Create area for device information storage - * - * wlp->mutex must be held - */ -int __wlp_alloc_device_info(struct wlp *wlp) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - BUG_ON(wlp->dev_info != NULL); - wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); - if (wlp->dev_info == NULL) { - dev_err(dev, "WLP: Unable to allocate memory for " - "device information.\n"); - return -ENOMEM; - } - return 0; -} - - -/** - * Fill in device information using function provided by driver - * - * wlp->mutex must be held - */ -static -void __wlp_fill_device_info(struct wlp *wlp) -{ - wlp->fill_device_info(wlp, wlp->dev_info); -} - -/** - * Setup device information - * - * Allocate area for device information and populate it. - * - * wlp->mutex must be held - */ -int __wlp_setup_device_info(struct wlp *wlp) -{ - int result; - struct device *dev = &wlp->rc->uwb_dev.dev; - - result = __wlp_alloc_device_info(wlp); - if (result < 0) { - dev_err(dev, "WLP: Unable to allocate area for " - "device information.\n"); - return result; - } - __wlp_fill_device_info(wlp); - return 0; -} - -/** - * Remove information about neighbor stored temporarily - * - * Information learned during discovey should only be stored when the - * device enrolls in the neighbor's WSS. We do need to store this - * information temporarily in order to present it to the user. - * - * We are only interested in keeping neighbor WSS information if that - * neighbor is accepting enrollment. - * - * should be called with wlp->nbmutex held - */ -void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor) -{ - struct wlp_wssid_e *wssid_e, *next; - u8 keep; - if (!list_empty(&neighbor->wssid)) { - list_for_each_entry_safe(wssid_e, next, &neighbor->wssid, - node) { - if (wssid_e->info != NULL) { - keep = wssid_e->info->accept_enroll; - kfree(wssid_e->info); - wssid_e->info = NULL; - if (!keep) { - list_del(&wssid_e->node); - kfree(wssid_e); - } - } - } - } - if (neighbor->info != NULL) { - kfree(neighbor->info); - neighbor->info = NULL; - } -} - -/* - * Populate WLP neighborhood cache with neighbor information - * - * A new neighbor is found. If it is discoverable then we add it to the - * neighborhood cache. - * - */ -static -int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev) -{ - int result = 0; - int discoverable; - struct wlp_neighbor_e *neighbor; - - /* - * FIXME: - * Use contents of WLP IE found in beacon cache to determine if - * neighbor is discoverable. - * The device does not support WLP IE yet so this still needs to be - * done. Until then we assume all devices are discoverable. - */ - discoverable = 1; /* will be changed when FIXME disappears */ - if (discoverable) { - /* Add neighbor to cache for discovery */ - neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL); - if (neighbor == NULL) { - dev_err(&dev->dev, "Unable to create memory for " - "new neighbor. \n"); - result = -ENOMEM; - goto error_no_mem; - } - wlp_neighbor_init(neighbor); - uwb_dev_get(dev); - neighbor->uwb_dev = dev; - list_add(&neighbor->node, &wlp->neighbors); - } -error_no_mem: - return result; -} - -/** - * Remove one neighbor from cache - */ -static -void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor) -{ - struct wlp_wssid_e *wssid_e, *next_wssid_e; - - list_for_each_entry_safe(wssid_e, next_wssid_e, - &neighbor->wssid, node) { - list_del(&wssid_e->node); - kfree(wssid_e); - } - uwb_dev_put(neighbor->uwb_dev); - list_del(&neighbor->node); - kfree(neighbor); -} - -/** - * Clear entire neighborhood cache. - */ -static -void __wlp_neighbors_release(struct wlp *wlp) -{ - struct wlp_neighbor_e *neighbor, *next; - if (list_empty(&wlp->neighbors)) - return; - list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { - __wlp_neighbor_release(neighbor); - } -} - -static -void wlp_neighbors_release(struct wlp *wlp) -{ - mutex_lock(&wlp->nbmutex); - __wlp_neighbors_release(wlp); - mutex_unlock(&wlp->nbmutex); -} - - - -/** - * Send D1 message to neighbor, receive D2 message - * - * @neighbor: neighbor to which D1 message will be sent - * @wss: if not NULL, it is an enrollment request for this WSS - * @wssid: if wss not NULL, this is the wssid of the WSS in which we - * want to enroll - * - * A D1/D2 exchange is done for one of two reasons: discovery or - * enrollment. If done for discovery the D1 message is sent to the neighbor - * and the contents of the D2 response is stored in a temporary cache. - * If done for enrollment the @wss and @wssid are provided also. In this - * case the D1 message is sent to the neighbor, the D2 response is parsed - * for enrollment of the WSS with wssid. - * - * &wss->mutex is held - */ -static -int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor, - struct wlp_wss *wss, struct wlp_uuid *wssid) -{ - int result; - struct device *dev = &wlp->rc->uwb_dev.dev; - DECLARE_COMPLETION_ONSTACK(completion); - struct wlp_session session; - struct sk_buff *skb; - struct wlp_frame_assoc *resp; - struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; - - mutex_lock(&wlp->mutex); - if (!wlp_uuid_is_set(&wlp->uuid)) { - dev_err(dev, "WLP: UUID is not set. Set via sysfs to " - "proceed.\n"); - result = -ENXIO; - goto out; - } - /* Send D1 association frame */ - result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1); - if (result < 0) { - dev_err(dev, "Unable to send D1 frame to neighbor " - "%02x:%02x (%d)\n", dev_addr->data[1], - dev_addr->data[0], result); - goto out; - } - /* Create session, wait for response */ - session.exp_message = WLP_ASSOC_D2; - session.cb = wlp_session_cb; - session.cb_priv = &completion; - session.neighbor_addr = *dev_addr; - BUG_ON(wlp->session != NULL); - wlp->session = &session; - /* Wait for D2/F0 frame */ - result = wait_for_completion_interruptible_timeout(&completion, - WLP_PER_MSG_TIMEOUT * HZ); - if (result == 0) { - result = -ETIMEDOUT; - dev_err(dev, "Timeout while sending D1 to neighbor " - "%02x:%02x.\n", dev_addr->data[1], - dev_addr->data[0]); - goto error_session; - } - if (result < 0) { - dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n", - dev_addr->data[1], dev_addr->data[0]); - goto error_session; - } - /* Parse message in session->data: it will be either D2 or F0 */ - skb = session.data; - resp = (void *) skb->data; - - if (resp->type == WLP_ASSOC_F0) { - result = wlp_parse_f0(wlp, skb); - if (result < 0) - dev_err(dev, "WLP: Unable to parse F0 from neighbor " - "%02x:%02x.\n", dev_addr->data[1], - dev_addr->data[0]); - result = -EINVAL; - goto error_resp_parse; - } - if (wss == NULL) { - /* Discovery */ - result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor); - if (result < 0) { - dev_err(dev, "WLP: Unable to parse D2 message from " - "neighbor %02x:%02x for discovery.\n", - dev_addr->data[1], dev_addr->data[0]); - goto error_resp_parse; - } - } else { - /* Enrollment */ - result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor, - wssid); - if (result < 0) { - dev_err(dev, "WLP: Unable to parse D2 message from " - "neighbor %02x:%02x for enrollment.\n", - dev_addr->data[1], dev_addr->data[0]); - goto error_resp_parse; - } - } -error_resp_parse: - kfree_skb(skb); -error_session: - wlp->session = NULL; -out: - mutex_unlock(&wlp->mutex); - return result; -} - -/** - * Enroll into WSS of provided WSSID by using neighbor as registrar - * - * &wss->mutex is held - */ -int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor, - struct wlp_wss *wss, struct wlp_uuid *wssid) -{ - int result = 0; - struct device *dev = &wlp->rc->uwb_dev.dev; - char buf[WLP_WSS_UUID_STRSIZE]; - struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; - - wlp_wss_uuid_print(buf, sizeof(buf), wssid); - - result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid); - if (result < 0) { - dev_err(dev, "WLP: D1/D2 message exchange for enrollment " - "failed. result = %d \n", result); - goto out; - } - if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { - dev_err(dev, "WLP: Unable to enroll into WSS %s using " - "neighbor %02x:%02x. \n", buf, - dev_addr->data[1], dev_addr->data[0]); - result = -EINVAL; - goto out; - } - if (wss->secure_status == WLP_WSS_SECURE) { - dev_err(dev, "FIXME: need to complete secure enrollment.\n"); - result = -EINVAL; - goto error; - } else { - wss->state = WLP_WSS_STATE_ENROLLED; - dev_dbg(dev, "WLP: Success Enrollment into unsecure WSS " - "%s using neighbor %02x:%02x. \n", - buf, dev_addr->data[1], dev_addr->data[0]); - } -out: - return result; -error: - wlp_wss_reset(wss); - return result; -} - -/** - * Discover WSS information of neighbor's active WSS - */ -static -int wlp_discover_neighbor(struct wlp *wlp, - struct wlp_neighbor_e *neighbor) -{ - return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL); -} - - -/** - * Each neighbor in the neighborhood cache is discoverable. Discover it. - * - * Discovery is done through sending of D1 association frame and parsing - * the D2 association frame response. Only wssid from D2 will be included - * in neighbor cache, rest is just displayed to user and forgotten. - * - * The discovery is not done in parallel. This is simple and enables us to - * maintain only one association context. - * - * The discovery of one neighbor does not affect the other, but if the - * discovery of a neighbor fails it is removed from the neighborhood cache. - */ -static -int wlp_discover_all_neighbors(struct wlp *wlp) -{ - int result = 0; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_neighbor_e *neighbor, *next; - - list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { - result = wlp_discover_neighbor(wlp, neighbor); - if (result < 0) { - dev_err(dev, "WLP: Unable to discover neighbor " - "%02x:%02x, removing from neighborhood. \n", - neighbor->uwb_dev->dev_addr.data[1], - neighbor->uwb_dev->dev_addr.data[0]); - __wlp_neighbor_release(neighbor); - } - } - return result; -} - -static int wlp_add_neighbor_helper(struct device *dev, void *priv) -{ - struct wlp *wlp = priv; - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - - return wlp_add_neighbor(wlp, uwb_dev); -} - -/** - * Discover WLP neighborhood - * - * Will send D1 association frame to all devices in beacon group that have - * discoverable bit set in WLP IE. D2 frames will be received, information - * displayed to user in @buf. Partial information (from D2 association - * frame) will be cached to assist with future association - * requests. - * - * The discovery of the WLP neighborhood is triggered by the user. This - * should occur infrequently and we thus free current cache and re-allocate - * memory if needed. - * - * If one neighbor fails during initial discovery (determining if it is a - * neighbor or not), we fail all - note that interaction with neighbor has - * not occured at this point so if a failure occurs we know something went wrong - * locally. We thus undo everything. - */ -ssize_t wlp_discover(struct wlp *wlp) -{ - int result = 0; - struct device *dev = &wlp->rc->uwb_dev.dev; - - mutex_lock(&wlp->nbmutex); - /* Clear current neighborhood cache. */ - __wlp_neighbors_release(wlp); - /* Determine which devices in neighborhood. Repopulate cache. */ - result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp); - if (result < 0) { - /* May have partial neighbor information, release all. */ - __wlp_neighbors_release(wlp); - goto error_dev_for_each; - } - /* Discover the properties of devices in neighborhood. */ - result = wlp_discover_all_neighbors(wlp); - /* In case of failure we still print our partial results. */ - if (result < 0) { - dev_err(dev, "Unable to fully discover neighborhood. \n"); - result = 0; - } -error_dev_for_each: - mutex_unlock(&wlp->nbmutex); - return result; -} - -/** - * Handle events from UWB stack - * - * We handle events conservatively. If a neighbor goes off the air we - * remove it from the neighborhood. If an association process is in - * progress this function will block waiting for the nbmutex to become - * free. The association process will thus be allowed to complete before it - * is removed. - */ -static -void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev, - enum uwb_notifs event) -{ - struct wlp *wlp = _wlp; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_neighbor_e *neighbor, *next; - int result; - switch (event) { - case UWB_NOTIF_ONAIR: - result = wlp_eda_create_node(&wlp->eda, - uwb_dev->mac_addr.data, - &uwb_dev->dev_addr); - if (result < 0) - dev_err(dev, "WLP: Unable to add new neighbor " - "%02x:%02x to EDA cache.\n", - uwb_dev->dev_addr.data[1], - uwb_dev->dev_addr.data[0]); - break; - case UWB_NOTIF_OFFAIR: - wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr); - mutex_lock(&wlp->nbmutex); - list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { - if (neighbor->uwb_dev == uwb_dev) - __wlp_neighbor_release(neighbor); - } - mutex_unlock(&wlp->nbmutex); - break; - default: - dev_err(dev, "don't know how to handle event %d from uwb\n", - event); - } -} - -static void wlp_channel_changed(struct uwb_pal *pal, int channel) -{ - struct wlp *wlp = container_of(pal, struct wlp, pal); - - if (channel < 0) - netif_carrier_off(wlp->ndev); - else - netif_carrier_on(wlp->ndev); -} - -int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev) -{ - int result; - - BUG_ON(wlp->fill_device_info == NULL); - BUG_ON(wlp->xmit_frame == NULL); - BUG_ON(wlp->stop_queue == NULL); - BUG_ON(wlp->start_queue == NULL); - - wlp->rc = rc; - wlp->ndev = ndev; - wlp_eda_init(&wlp->eda);/* Set up address cache */ - wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb; - wlp->uwb_notifs_handler.data = wlp; - uwb_notifs_register(rc, &wlp->uwb_notifs_handler); - - uwb_pal_init(&wlp->pal); - wlp->pal.rc = rc; - wlp->pal.channel_changed = wlp_channel_changed; - result = uwb_pal_register(&wlp->pal); - if (result < 0) - uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); - - return result; -} -EXPORT_SYMBOL_GPL(wlp_setup); - -void wlp_remove(struct wlp *wlp) -{ - wlp_neighbors_release(wlp); - uwb_pal_unregister(&wlp->pal); - uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); - wlp_eda_release(&wlp->eda); - mutex_lock(&wlp->mutex); - if (wlp->dev_info != NULL) - kfree(wlp->dev_info); - mutex_unlock(&wlp->mutex); - wlp->rc = NULL; -} -EXPORT_SYMBOL_GPL(wlp_remove); - -/** - * wlp_reset_all - reset the WLP hardware - * @wlp: the WLP device to reset. - * - * This schedules a full hardware reset of the WLP device. The radio - * controller and any other PALs will also be reset. - */ -void wlp_reset_all(struct wlp *wlp) -{ - uwb_rc_reset_all(wlp->rc); -} -EXPORT_SYMBOL_GPL(wlp_reset_all); diff --git a/drivers/uwb/wlp/wss-lc.c b/drivers/uwb/wlp/wss-lc.c deleted file mode 100644 index 67872c83b67..00000000000 --- a/drivers/uwb/wlp/wss-lc.c +++ /dev/null @@ -1,959 +0,0 @@ -/* - * WiMedia Logical Link Control Protocol (WLP) - * - * Copyright (C) 2007 Intel Corporation - * Reinette Chatre <reinette.chatre@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * Implementation of the WLP association protocol. - * - * FIXME: Docs - * - * A UWB network interface will configure a WSS through wlp_wss_setup() after - * the interface has been assigned a MAC address, typically after - * "ifconfig" has been called. When the interface goes down it should call - * wlp_wss_remove(). - * - * When the WSS is ready for use the user interacts via sysfs to create, - * discover, and activate WSS. - * - * wlp_wss_enroll_activate() - * - * wlp_wss_create_activate() - * wlp_wss_set_wssid_hash() - * wlp_wss_comp_wssid_hash() - * wlp_wss_sel_bcast_addr() - * wlp_wss_sysfs_add() - * - * Called when no more references to WSS exist: - * wlp_wss_release() - * wlp_wss_reset() - */ -#include <linux/etherdevice.h> /* for is_valid_ether_addr */ -#include <linux/skbuff.h> -#include <linux/slab.h> -#include <linux/wlp.h> - -#include "wlp-internal.h" - -size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key) -{ - size_t result; - - result = scnprintf(buf, bufsize, - "%02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x", - key[0], key[1], key[2], key[3], - key[4], key[5], key[6], key[7], - key[8], key[9], key[10], key[11], - key[12], key[13], key[14], key[15]); - return result; -} - -/** - * Compute WSSID hash - * WLP Draft 0.99 [7.2.1] - * - * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR - * of all octets in the WSSID. - */ -static -u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid) -{ - return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2] - ^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5] - ^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8] - ^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11] - ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14] - ^ wssid->data[15]; -} - -/** - * Select a multicast EUI-48 for the WSS broadcast address. - * WLP Draft 0.99 [7.2.1] - * - * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP - * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive. - * - * This address is currently hardcoded. - * FIXME? - */ -static -struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss) -{ - struct uwb_mac_addr bcast = { - .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 } - }; - return bcast; -} - -/** - * Clear the contents of the WSS structure - all except kobj, mutex, virtual - * - * We do not want to reinitialize - the internal kobj should not change as - * it still points to the parent received during setup. The mutex should - * remain also. We thus just reset values individually. - * The virutal address assigned to WSS will remain the same for the - * lifetime of the WSS. We only reset the fields that can change during its - * lifetime. - */ -void wlp_wss_reset(struct wlp_wss *wss) -{ - memset(&wss->wssid, 0, sizeof(wss->wssid)); - wss->hash = 0; - memset(&wss->name[0], 0, sizeof(wss->name)); - memset(&wss->bcast, 0, sizeof(wss->bcast)); - wss->secure_status = WLP_WSS_UNSECURE; - memset(&wss->master_key[0], 0, sizeof(wss->master_key)); - wss->tag = 0; - wss->state = WLP_WSS_STATE_NONE; -} - -/** - * Create sysfs infrastructure for WSS - * - * The WSS is configured to have the interface as parent (see wlp_wss_setup()) - * a new sysfs directory that includes wssid as its name is created in the - * interface's sysfs directory. The group of files interacting with WSS are - * created also. - */ -static -int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - int result; - - result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str); - if (result < 0) - return result; - wss->kobj.ktype = &wss_ktype; - result = kobject_init_and_add(&wss->kobj, - &wss_ktype, wss->kobj.parent, "wlp"); - if (result < 0) { - dev_err(dev, "WLP: Cannot register WSS kobject.\n"); - goto error_kobject_register; - } - result = sysfs_create_group(&wss->kobj, &wss_attr_group); - if (result < 0) { - dev_err(dev, "WLP: Cannot register WSS attributes: %d\n", - result); - goto error_sysfs_create_group; - } - return 0; -error_sysfs_create_group: - - kobject_put(&wss->kobj); /* will free name if needed */ - return result; -error_kobject_register: - kfree(wss->kobj.name); - wss->kobj.name = NULL; - wss->kobj.ktype = NULL; - return result; -} - - -/** - * Release WSS - * - * No more references exist to this WSS. We should undo everything that was - * done in wlp_wss_create_activate() except removing the group. The group - * is not removed because an object can be unregistered before the group is - * created. We also undo any additional operations on the WSS after this - * (addition of members). - * - * If memory was allocated for the kobject's name then it will - * be freed by the kobject system during this time. - * - * The EDA cache is removed and reinitialized when the WSS is removed. We - * thus loose knowledge of members of this WSS at that time and need not do - * it here. - */ -void wlp_wss_release(struct kobject *kobj) -{ - struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj); - - wlp_wss_reset(wss); -} - -/** - * Enroll into a WSS using provided neighbor as registrar - * - * First search the neighborhood information to learn which neighbor is - * referred to, next proceed with enrollment. - * - * &wss->mutex is held - */ -static -int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid, - struct uwb_dev_addr *dest) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_neighbor_e *neighbor; - int result = -ENXIO; - struct uwb_dev_addr *dev_addr; - - mutex_lock(&wlp->nbmutex); - list_for_each_entry(neighbor, &wlp->neighbors, node) { - dev_addr = &neighbor->uwb_dev->dev_addr; - if (!memcmp(dest, dev_addr, sizeof(*dest))) { - result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid); - break; - } - } - if (result == -ENXIO) - dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n", - dest->data[1], dest->data[0]); - mutex_unlock(&wlp->nbmutex); - return result; -} - -/** - * Enroll into a WSS previously discovered - * - * User provides WSSID of WSS, search for neighbor that has this WSS - * activated and attempt to enroll. - * - * &wss->mutex is held - */ -static -int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_neighbor_e *neighbor; - struct wlp_wssid_e *wssid_e; - char buf[WLP_WSS_UUID_STRSIZE]; - int result = -ENXIO; - - - mutex_lock(&wlp->nbmutex); - list_for_each_entry(neighbor, &wlp->neighbors, node) { - list_for_each_entry(wssid_e, &neighbor->wssid, node) { - if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) { - result = wlp_enroll_neighbor(wlp, neighbor, - wss, wssid); - if (result == 0) /* enrollment success */ - goto out; - break; - } - } - } -out: - if (result == -ENXIO) { - wlp_wss_uuid_print(buf, sizeof(buf), wssid); - dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf); - } - mutex_unlock(&wlp->nbmutex); - return result; -} - -/** - * Enroll into WSS with provided WSSID, registrar may be provided - * - * @wss: out WSS that will be enrolled - * @wssid: wssid of neighboring WSS that we want to enroll in - * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any - * neighbor can be used as registrar. - * - * &wss->mutex is held - */ -static -int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid, - struct uwb_dev_addr *devaddr) -{ - int result; - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - char buf[WLP_WSS_UUID_STRSIZE]; - struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; - - wlp_wss_uuid_print(buf, sizeof(buf), wssid); - - if (wss->state != WLP_WSS_STATE_NONE) { - dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf); - result = -EEXIST; - goto error; - } - if (!memcmp(&bcast, devaddr, sizeof(bcast))) - result = wlp_wss_enroll_discovered(wss, wssid); - else - result = wlp_wss_enroll_target(wss, wssid, devaddr); - if (result < 0) { - dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n", - buf, result); - goto error; - } - dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf); - result = wlp_wss_sysfs_add(wss, buf); - if (result < 0) { - dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n"); - wlp_wss_reset(wss); - } -error: - return result; - -} - -/** - * Activate given WSS - * - * Prior to activation a WSS must be enrolled. To activate a WSS a device - * includes the WSS hash in the WLP IE in its beacon in each superframe. - * WLP 0.99 [7.2.5]. - * - * The WSS tag is also computed at this time. We only support one activated - * WSS so we can use the hash as a tag - there will never be a conflict. - * - * We currently only support one activated WSS so only one WSS hash is - * included in the WLP IE. - */ -static -int wlp_wss_activate(struct wlp_wss *wss) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - struct uwb_rc *uwb_rc = wlp->rc; - int result; - struct { - struct wlp_ie wlp_ie; - u8 hash; /* only include one hash */ - } ie_data; - - BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED); - wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid); - wss->tag = wss->hash; - memset(&ie_data, 0, sizeof(ie_data)); - ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP; - ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr); - wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash)); - ie_data.hash = wss->hash; - result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr, - sizeof(ie_data)); - if (result < 0) { - dev_err(dev, "WLP: Unable to add WLP IE to beacon. " - "result = %d.\n", result); - goto error_wlp_ie; - } - wss->state = WLP_WSS_STATE_ACTIVE; - result = 0; -error_wlp_ie: - return result; -} - -/** - * Enroll in and activate WSS identified by provided WSSID - * - * The neighborhood cache should contain a list of all neighbors and the - * WSS they have activated. Based on that cache we search which neighbor we - * can perform the association process with. The user also has option to - * specify which neighbor it prefers as registrar. - * Successful enrollment is followed by activation. - * Successful activation will create the sysfs directory containing - * specific information regarding this WSS. - */ -int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, - struct uwb_dev_addr *devaddr) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - char buf[WLP_WSS_UUID_STRSIZE]; - - mutex_lock(&wss->mutex); - result = wlp_wss_enroll(wss, wssid, devaddr); - if (result < 0) { - wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid); - dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf); - goto error_enroll; - } - result = wlp_wss_activate(wss); - if (result < 0) { - dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment " - "result = %d \n", result); - /* Undo enrollment */ - wlp_wss_reset(wss); - goto error_activate; - } -error_activate: -error_enroll: - mutex_unlock(&wss->mutex); - return result; -} - -/** - * Create, enroll, and activate a new WSS - * - * @wssid: new wssid provided by user - * @name: WSS name requested by used. - * @sec_status: security status requested by user - * - * A user requested the creation of a new WSS. All operations are done - * locally. The new WSS will be stored locally, the hash will be included - * in the WLP IE, and the sysfs infrastructure for this WSS will be - * created. - */ -int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, - char *name, unsigned sec_status, unsigned accept) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - char buf[WLP_WSS_UUID_STRSIZE]; - - result = wlp_wss_uuid_print(buf, sizeof(buf), wssid); - - if (!mutex_trylock(&wss->mutex)) { - dev_err(dev, "WLP: WLP association session in progress.\n"); - return -EBUSY; - } - if (wss->state != WLP_WSS_STATE_NONE) { - dev_err(dev, "WLP: WSS already exists. Not creating new.\n"); - result = -EEXIST; - goto out; - } - if (wss->kobj.parent == NULL) { - dev_err(dev, "WLP: WSS parent not ready. Is network interface " - "up?\n"); - result = -ENXIO; - goto out; - } - if (sec_status == WLP_WSS_SECURE) { - dev_err(dev, "WLP: FIXME Creation of secure WSS not " - "supported yet.\n"); - result = -EINVAL; - goto out; - } - wss->wssid = *wssid; - memcpy(wss->name, name, sizeof(wss->name)); - wss->bcast = wlp_wss_sel_bcast_addr(wss); - wss->secure_status = sec_status; - wss->accept_enroll = accept; - /*wss->virtual_addr is initialized in call to wlp_wss_setup*/ - /* sysfs infrastructure */ - result = wlp_wss_sysfs_add(wss, buf); - if (result < 0) { - dev_err(dev, "Cannot set up sysfs for WSS kobject.\n"); - wlp_wss_reset(wss); - goto out; - } else - result = 0; - wss->state = WLP_WSS_STATE_ENROLLED; - result = wlp_wss_activate(wss); - if (result < 0) { - dev_err(dev, "WLP: Unable to activate WSS. Undoing " - "enrollment\n"); - wlp_wss_reset(wss); - goto out; - } - result = 0; -out: - mutex_unlock(&wss->mutex); - return result; -} - -/** - * Determine if neighbor has WSS activated - * - * @returns: 1 if neighbor has WSS activated, zero otherwise - * - * This can be done in two ways: - * - send a C1 frame, parse C2/F0 response - * - examine the WLP IE sent by the neighbor - * - * The WLP IE is not fully supported in hardware so we use the C1/C2 frame - * exchange to determine if a WSS is activated. Using the WLP IE should be - * faster and should be used when it becomes possible. - */ -int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss, - struct uwb_dev_addr *dev_addr) -{ - int result = 0; - struct device *dev = &wlp->rc->uwb_dev.dev; - DECLARE_COMPLETION_ONSTACK(completion); - struct wlp_session session; - struct sk_buff *skb; - struct wlp_frame_assoc *resp; - struct wlp_uuid wssid; - - mutex_lock(&wlp->mutex); - /* Send C1 association frame */ - result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1); - if (result < 0) { - dev_err(dev, "Unable to send C1 frame to neighbor " - "%02x:%02x (%d)\n", dev_addr->data[1], - dev_addr->data[0], result); - result = 0; - goto out; - } - /* Create session, wait for response */ - session.exp_message = WLP_ASSOC_C2; - session.cb = wlp_session_cb; - session.cb_priv = &completion; - session.neighbor_addr = *dev_addr; - BUG_ON(wlp->session != NULL); - wlp->session = &session; - /* Wait for C2/F0 frame */ - result = wait_for_completion_interruptible_timeout(&completion, - WLP_PER_MSG_TIMEOUT * HZ); - if (result == 0) { - dev_err(dev, "Timeout while sending C1 to neighbor " - "%02x:%02x.\n", dev_addr->data[1], - dev_addr->data[0]); - goto out; - } - if (result < 0) { - dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n", - dev_addr->data[1], dev_addr->data[0]); - result = 0; - goto out; - } - /* Parse message in session->data: it will be either C2 or F0 */ - skb = session.data; - resp = (void *) skb->data; - if (resp->type == WLP_ASSOC_F0) { - result = wlp_parse_f0(wlp, skb); - if (result < 0) - dev_err(dev, "WLP: unable to parse incoming F0 " - "frame from neighbor %02x:%02x.\n", - dev_addr->data[1], dev_addr->data[0]); - result = 0; - goto error_resp_parse; - } - /* WLP version and message type fields have already been parsed */ - result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid, - skb->len - sizeof(*resp)); - if (result < 0) { - dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n"); - result = 0; - goto error_resp_parse; - } - if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))) - result = 1; - else { - dev_err(dev, "WLP: Received a C2 frame without matching " - "WSSID.\n"); - result = 0; - } -error_resp_parse: - kfree_skb(skb); -out: - wlp->session = NULL; - mutex_unlock(&wlp->mutex); - return result; -} - -/** - * Activate connection with neighbor by updating EDA cache - * - * @wss: local WSS to which neighbor wants to connect - * @dev_addr: neighbor's address - * @wssid: neighbor's WSSID - must be same as our WSS's WSSID - * @tag: neighbor's WSS tag used to identify frames transmitted by it - * @virt_addr: neighbor's virtual EUI-48 - */ -static -int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss, - struct uwb_dev_addr *dev_addr, - struct wlp_uuid *wssid, u8 *tag, - struct uwb_mac_addr *virt_addr) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - - if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) { - /* Update EDA cache */ - result = wlp_eda_update_node(&wlp->eda, dev_addr, wss, - (void *) virt_addr->data, *tag, - WLP_WSS_CONNECTED); - if (result < 0) - dev_err(dev, "WLP: Unable to update EDA cache " - "with new connected neighbor information.\n"); - } else { - dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n"); - result = -EINVAL; - } - return result; -} - -/** - * Connect to WSS neighbor - * - * Use C3/C4 exchange to determine if neighbor has WSS activated and - * retrieve the WSS tag and virtual EUI-48 of the neighbor. - */ -static -int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss, - struct uwb_dev_addr *dev_addr) -{ - int result; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct wlp_uuid wssid; - u8 tag; - struct uwb_mac_addr virt_addr; - DECLARE_COMPLETION_ONSTACK(completion); - struct wlp_session session; - struct wlp_frame_assoc *resp; - struct sk_buff *skb; - - mutex_lock(&wlp->mutex); - /* Send C3 association frame */ - result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3); - if (result < 0) { - dev_err(dev, "Unable to send C3 frame to neighbor " - "%02x:%02x (%d)\n", dev_addr->data[1], - dev_addr->data[0], result); - goto out; - } - /* Create session, wait for response */ - session.exp_message = WLP_ASSOC_C4; - session.cb = wlp_session_cb; - session.cb_priv = &completion; - session.neighbor_addr = *dev_addr; - BUG_ON(wlp->session != NULL); - wlp->session = &session; - /* Wait for C4/F0 frame */ - result = wait_for_completion_interruptible_timeout(&completion, - WLP_PER_MSG_TIMEOUT * HZ); - if (result == 0) { - dev_err(dev, "Timeout while sending C3 to neighbor " - "%02x:%02x.\n", dev_addr->data[1], - dev_addr->data[0]); - result = -ETIMEDOUT; - goto out; - } - if (result < 0) { - dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n", - dev_addr->data[1], dev_addr->data[0]); - goto out; - } - /* Parse message in session->data: it will be either C4 or F0 */ - skb = session.data; - resp = (void *) skb->data; - if (resp->type == WLP_ASSOC_F0) { - result = wlp_parse_f0(wlp, skb); - if (result < 0) - dev_err(dev, "WLP: unable to parse incoming F0 " - "frame from neighbor %02x:%02x.\n", - dev_addr->data[1], dev_addr->data[0]); - result = -EINVAL; - goto error_resp_parse; - } - result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); - if (result < 0) { - dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n"); - goto error_resp_parse; - } - result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag, - &virt_addr); - if (result < 0) { - dev_err(dev, "WLP: Unable to activate connection to " - "neighbor %02x:%02x.\n", dev_addr->data[1], - dev_addr->data[0]); - goto error_resp_parse; - } -error_resp_parse: - kfree_skb(skb); -out: - /* Record that we unsuccessfully tried to connect to this neighbor */ - if (result < 0) - wlp_eda_update_node_state(&wlp->eda, dev_addr, - WLP_WSS_CONNECT_FAILED); - wlp->session = NULL; - mutex_unlock(&wlp->mutex); - return result; -} - -/** - * Connect to neighbor with common WSS, send pending frame - * - * This function is scheduled when a frame is destined to a neighbor with - * which we do not have a connection. A copy of the EDA cache entry is - * provided - not the actual cache entry (because it is protected by a - * spinlock). - * - * First determine if neighbor has the same WSS activated, connect if it - * does. The C3/C4 exchange is dual purpose to determine if neighbor has - * WSS activated and proceed with the connection. - * - * The frame that triggered the connection setup is sent after connection - * setup. - * - * network queue is stopped - we need to restart when done - * - */ -static -void wlp_wss_connect_send(struct work_struct *ws) -{ - struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws, - struct wlp_assoc_conn_ctx, - ws); - struct wlp *wlp = conn_ctx->wlp; - struct sk_buff *skb = conn_ctx->skb; - struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry; - struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; - struct wlp_wss *wss = &wlp->wss; - int result; - struct device *dev = &wlp->rc->uwb_dev.dev; - - mutex_lock(&wss->mutex); - if (wss->state < WLP_WSS_STATE_ACTIVE) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Attempting to connect with " - "WSS that is not active or connected.\n"); - dev_kfree_skb(skb); - goto out; - } - /* Establish connection - send C3 rcv C4 */ - result = wlp_wss_connect_neighbor(wlp, wss, dev_addr); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to establish connection " - "with neighbor %02x:%02x.\n", - dev_addr->data[1], dev_addr->data[0]); - dev_kfree_skb(skb); - goto out; - } - /* EDA entry changed, update the local copy being used */ - result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Cannot find EDA entry for " - "neighbor %02x:%02x \n", - dev_addr->data[1], dev_addr->data[0]); - } - result = wlp_wss_prep_hdr(wlp, eda_entry, skb); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to prepare frame header for " - "transmission (neighbor %02x:%02x). \n", - dev_addr->data[1], dev_addr->data[0]); - dev_kfree_skb(skb); - goto out; - } - BUG_ON(wlp->xmit_frame == NULL); - result = wlp->xmit_frame(wlp, skb, dev_addr); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to transmit frame: %d\n", - result); - if (result == -ENXIO) - dev_err(dev, "WLP: Is network interface up? \n"); - /* We could try again ... */ - dev_kfree_skb(skb);/*we need to free if tx fails */ - } -out: - kfree(conn_ctx); - BUG_ON(wlp->start_queue == NULL); - wlp->start_queue(wlp); - mutex_unlock(&wss->mutex); -} - -/** - * Add WLP header to outgoing skb - * - * @eda_entry: pointer to neighbor's entry in the EDA cache - * @_skb: skb containing data destined to the neighbor - */ -int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry, - void *_skb) -{ - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - unsigned char *eth_addr = eda_entry->eth_addr; - struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; - struct sk_buff *skb = _skb; - struct wlp_frame_std_abbrv_hdr *std_hdr; - - if (eda_entry->state == WLP_WSS_CONNECTED) { - /* Add WLP header */ - BUG_ON(skb_headroom(skb) < sizeof(*std_hdr)); - std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr)); - std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); - std_hdr->hdr.type = WLP_FRAME_STANDARD; - std_hdr->tag = eda_entry->wss->tag; - } else { - if (printk_ratelimit()) - dev_err(dev, "WLP: Destination neighbor (Ethernet: " - "%pM, Dev: %02x:%02x) is not connected.\n", - eth_addr, dev_addr->data[1], dev_addr->data[0]); - result = -EINVAL; - } - return result; -} - - -/** - * Prepare skb for neighbor: connect if not already and prep WLP header - * - * This function is called in interrupt context, but it needs to sleep. We - * temporarily stop the net queue to establish the WLP connection. - * Setup of the WLP connection and restart of queue is scheduled - * on the default work queue. - * - * run with eda->lock held (spinlock) - */ -int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry, - void *_skb) -{ - int result = 0; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct sk_buff *skb = _skb; - struct wlp_assoc_conn_ctx *conn_ctx; - - if (eda_entry->state == WLP_WSS_UNCONNECTED) { - /* We don't want any more packets while we set up connection */ - BUG_ON(wlp->stop_queue == NULL); - wlp->stop_queue(wlp); - conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC); - if (conn_ctx == NULL) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to allocate memory " - "for connection handling.\n"); - result = -ENOMEM; - goto out; - } - conn_ctx->wlp = wlp; - conn_ctx->skb = skb; - conn_ctx->eda_entry = *eda_entry; - INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send); - schedule_work(&conn_ctx->ws); - result = 1; - } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) { - /* Previous connection attempts failed, don't retry - see - * conditions for connection in WLP 0.99 [7.6.2] */ - if (printk_ratelimit()) - dev_err(dev, "Could not connect to neighbor " - "previously. Not retrying. \n"); - result = -ENONET; - goto out; - } else /* eda_entry->state == WLP_WSS_CONNECTED */ - result = wlp_wss_prep_hdr(wlp, eda_entry, skb); -out: - return result; -} - -/** - * Emulate broadcast: copy skb, send copy to neighbor (connect if not already) - * - * We need to copy skbs in the case where we emulate broadcast through - * unicast. We copy instead of clone because we are modifying the data of - * the frame after copying ... clones share data so we cannot emulate - * broadcast using clones. - * - * run with eda->lock held (spinlock) - */ -int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry, - void *_skb) -{ - int result = -ENOMEM; - struct device *dev = &wlp->rc->uwb_dev.dev; - struct sk_buff *skb = _skb; - struct sk_buff *copy; - struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; - - copy = skb_copy(skb, GFP_ATOMIC); - if (copy == NULL) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to copy skb for " - "transmission.\n"); - goto out; - } - result = wlp_wss_connect_prep(wlp, eda_entry, copy); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to connect/send skb " - "to neighbor.\n"); - dev_kfree_skb_irq(copy); - goto out; - } else if (result == 1) - /* Frame will be transmitted separately */ - goto out; - BUG_ON(wlp->xmit_frame == NULL); - result = wlp->xmit_frame(wlp, copy, dev_addr); - if (result < 0) { - if (printk_ratelimit()) - dev_err(dev, "WLP: Unable to transmit frame: %d\n", - result); - if ((result == -ENXIO) && printk_ratelimit()) - dev_err(dev, "WLP: Is network interface up? \n"); - /* We could try again ... */ - dev_kfree_skb_irq(copy);/*we need to free if tx fails */ - } -out: - return result; -} - - -/** - * Setup WSS - * - * Should be called by network driver after the interface has been given a - * MAC address. - */ -int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - struct device *dev = &wlp->rc->uwb_dev.dev; - int result = 0; - - mutex_lock(&wss->mutex); - wss->kobj.parent = &net_dev->dev.kobj; - if (!is_valid_ether_addr(net_dev->dev_addr)) { - dev_err(dev, "WLP: Invalid MAC address. Cannot use for" - "virtual.\n"); - result = -EINVAL; - goto out; - } - memcpy(wss->virtual_addr.data, net_dev->dev_addr, - sizeof(wss->virtual_addr.data)); -out: - mutex_unlock(&wss->mutex); - return result; -} -EXPORT_SYMBOL_GPL(wlp_wss_setup); - -/** - * Remove WSS - * - * Called by client that configured WSS through wlp_wss_setup(). This - * function is called when client no longer needs WSS, eg. client shuts - * down. - * - * We remove the WLP IE from the beacon before initiating local cleanup. - */ -void wlp_wss_remove(struct wlp_wss *wss) -{ - struct wlp *wlp = container_of(wss, struct wlp, wss); - - mutex_lock(&wss->mutex); - if (wss->state == WLP_WSS_STATE_ACTIVE) - uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP); - if (wss->state != WLP_WSS_STATE_NONE) { - sysfs_remove_group(&wss->kobj, &wss_attr_group); - kobject_put(&wss->kobj); - } - wss->kobj.parent = NULL; - memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr)); - /* Cleanup EDA cache */ - wlp_eda_release(&wlp->eda); - wlp_eda_init(&wlp->eda); - mutex_unlock(&wss->mutex); -} -EXPORT_SYMBOL_GPL(wlp_wss_remove); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index dc06ff13455..596ef6b922b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1919,6 +1919,9 @@ config FB_SH_MOBILE_HDMI tristate "SuperH Mobile HDMI controller support" depends on FB_SH_MOBILE_LCDC select FB_MODE_HELPERS + select SOUND + select SND + select SND_SOC ---help--- Driver for the on-chip SH-Mobile HDMI controller. diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index f8d69ad3683..5bf91236c70 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2970,7 +2970,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, struct atyfb_par *par = info->par; struct device_node *dp; char prop[128]; - int node, len, i, j, ret; + phandle node; + int len, i, j, ret; u32 mem, chip_id; /* diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 2fde08cc66b..ef989d94511 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -22,6 +22,8 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> #include <video/sh_mobile_hdmi.h> #include <video/sh_mobile_lcdc.h> @@ -222,6 +224,58 @@ static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) return ioread8(hdmi->base + reg); } +/* + * HDMI sound + */ +static unsigned int sh_hdmi_snd_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct sh_hdmi *hdmi = snd_soc_codec_get_drvdata(codec); + + return hdmi_read(hdmi, reg); +} + +static int sh_hdmi_snd_write(struct snd_soc_codec *codec, + unsigned int reg, + unsigned int value) +{ + struct sh_hdmi *hdmi = snd_soc_codec_get_drvdata(codec); + + hdmi_write(hdmi, value, reg); + return 0; +} + +static struct snd_soc_dai_driver sh_hdmi_dai = { + .name = "sh_mobile_hdmi-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}; + +static int sh_hdmi_snd_probe(struct snd_soc_codec *codec) +{ + dev_info(codec->dev, "SH Mobile HDMI Audio Codec"); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { + .probe = sh_hdmi_snd_probe, + .read = sh_hdmi_snd_read, + .write = sh_hdmi_snd_write, +}; + +/* + * HDMI video + */ + /* External video parameter settings */ static void hdmi_external_video_param(struct sh_hdmi *hdmi) { @@ -318,6 +372,9 @@ static void sh_hdmi_video_config(struct sh_hdmi *hdmi) */ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) { + u8 data; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + /* * [7:4] L/R data swap control * [3:0] appropriate N[19:16] @@ -335,7 +392,23 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) * [6:5] set required down sampling rate if required * [4:3] set required audio source */ - hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1); + switch (pdata->flags & HDMI_SND_SRC_MASK) { + default: + /* fall through */ + case HDMI_SND_SRC_I2S: + data = 0x0 << 3; + break; + case HDMI_SND_SRC_SPDIF: + data = 0x1 << 3; + break; + case HDMI_SND_SRC_DSD: + data = 0x2 << 3; + break; + case HDMI_SND_SRC_HBR: + data = 0x3 << 3; + break; + } + hdmi_write(hdmi, data, HDMI_AUDIO_SETTING_1); /* [3:0] set sending channel number for channel status */ hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2); @@ -891,6 +964,11 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) return -ENOMEM; } + ret = snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1); + if (ret < 0) + goto esndreg; + hdmi->dev = &pdev->dev; hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); @@ -976,6 +1054,8 @@ eclkenable: erate: clk_put(hdmi->hdmi_clk); egetclk: + snd_soc_unregister_codec(&pdev->dev); +esndreg: kfree(hdmi); return ret; @@ -988,6 +1068,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); + snd_soc_unregister_codec(&pdev->dev); + pdata->lcd_chan->board_cfg.display_on = NULL; pdata->lcd_chan->board_cfg.display_off = NULL; pdata->lcd_chan->board_cfg.board_data = NULL; diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index d72075a9f01..7a1419279c8 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -1243,8 +1243,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) if (priv->ch[i].sglist) vfree(priv->ch[i].sglist); - dma_free_coherent(&pdev->dev, info->fix.smem_len, - info->screen_base, priv->ch[i].dma_handle); + if (info->screen_base) + dma_free_coherent(&pdev->dev, info->fix.smem_len, + info->screen_base, + priv->ch[i].dma_handle); fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 0c9ce88e95e..68bd23476c6 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -32,10 +32,14 @@ #include <linux/dma-mapping.h> #include <linux/of_device.h> #include <linux/of_platform.h> +#include <linux/of_address.h> #include <linux/io.h> #include <linux/xilinxfb.h> #include <linux/slab.h> + +#ifdef CONFIG_PPC_DCR #include <asm/dcr.h> +#endif #define DRIVER_NAME "xilinxfb" @@ -123,10 +127,10 @@ struct xilinxfb_drvdata { registers */ void __iomem *regs; /* virt. address of the control registers */ - +#ifdef CONFIG_PPC_DCR dcr_host_t dcr_host; unsigned int dcr_len; - +#endif void *fb_virt; /* virt. address of the frame buffer */ dma_addr_t fb_phys; /* phys. address of the frame buffer */ int fb_alloced; /* Flag, was the fb memory alloced? */ @@ -152,9 +156,10 @@ static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset, { if (drvdata->flags & PLB_ACCESS_FLAG) out_be32(drvdata->regs + (offset << 2), val); +#ifdef CONFIG_PPC_DCR else dcr_write(drvdata->dcr_host, offset, val); - +#endif } static int @@ -383,8 +388,11 @@ static int xilinxfb_release(struct device *dev) if (drvdata->flags & PLB_ACCESS_FLAG) { iounmap(drvdata->regs); release_mem_region(drvdata->regs_phys, 8); - } else + } +#ifdef CONFIG_PPC_DCR + else dcr_unmap(drvdata->dcr_host, drvdata->dcr_len); +#endif kfree(drvdata); dev_set_drvdata(dev, NULL); @@ -404,7 +412,7 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) u32 tft_access; struct xilinxfb_platform_data pdata; struct resource res; - int size, rc, start; + int size, rc; struct xilinxfb_drvdata *drvdata; /* Copy with the default pdata (not a ptr reference!) */ @@ -437,7 +445,10 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) dev_err(&op->dev, "invalid address\n"); goto err; } - } else { + } +#ifdef CONFIG_PPC_DCR + else { + int start; res.start = 0; start = dcr_resource_start(op->dev.of_node, 0); drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0); @@ -447,6 +458,7 @@ xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) goto err; } } +#endif prop = of_get_property(op->dev.of_node, "phys-size", &size); if ((prop) && (size >= sizeof(u32)*2)) { diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 81e3d610089..3dd4971160e 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -38,11 +38,11 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/moduleparam.h> -#include <linux/clk.h> #include <linux/bitops.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <mach/hardware.h> #include <plat/prcm.h> @@ -61,8 +61,6 @@ struct omap_wdt_dev { void __iomem *base; /* physical */ struct device *dev; int omap_wdt_users; - struct clk *ick; - struct clk *fck; struct resource *mem; struct miscdevice omap_wdt_miscdev; }; @@ -146,8 +144,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file) if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) return -EBUSY; - clk_enable(wdev->ick); /* Enable the interface clock */ - clk_enable(wdev->fck); /* Enable the functional clock */ + pm_runtime_get_sync(wdev->dev); /* initialize prescaler */ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) @@ -177,8 +174,7 @@ static int omap_wdt_release(struct inode *inode, struct file *file) omap_wdt_disable(wdev); - clk_disable(wdev->ick); - clk_disable(wdev->fck); + pm_runtime_put_sync(wdev->dev); #else printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); #endif @@ -293,19 +289,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) wdev->omap_wdt_users = 0; wdev->mem = mem; - - wdev->ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(wdev->ick)) { - ret = PTR_ERR(wdev->ick); - wdev->ick = NULL; - goto err_clk; - } - wdev->fck = clk_get(&pdev->dev, "fck"); - if (IS_ERR(wdev->fck)) { - ret = PTR_ERR(wdev->fck); - wdev->fck = NULL; - goto err_clk; - } + wdev->dev = &pdev->dev; wdev->base = ioremap(res->start, resource_size(res)); if (!wdev->base) { @@ -315,8 +299,8 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdev); - clk_enable(wdev->ick); - clk_enable(wdev->fck); + pm_runtime_enable(wdev->dev); + pm_runtime_get_sync(wdev->dev); omap_wdt_disable(wdev); omap_wdt_adjust_timeout(timer_margin); @@ -334,11 +318,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, timer_margin); - /* autogate OCP interface clock */ - __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); - - clk_disable(wdev->ick); - clk_disable(wdev->fck); + pm_runtime_put_sync(wdev->dev); omap_wdt_dev = pdev; @@ -350,12 +330,6 @@ err_misc: err_ioremap: wdev->base = NULL; - -err_clk: - if (wdev->ick) - clk_put(wdev->ick); - if (wdev->fck) - clk_put(wdev->fck); kfree(wdev); err_kzalloc: @@ -387,8 +361,6 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - clk_put(wdev->ick); - clk_put(wdev->fck); iounmap(wdev->base); kfree(wdev); |