diff options
Diffstat (limited to 'drivers/char/tlclk.c')
| -rw-r--r-- | drivers/char/tlclk.c | 122 |
1 files changed, 70 insertions, 52 deletions
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 4c272189cd4..100cd1de993 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -27,17 +27,17 @@ * MPCBL0010 ATCA computer. */ -#include <linux/config.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/sched.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ +#include <linux/sched.h> #include <linux/slab.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/mutex.h> #include <linux/timer.h> #include <linux/sysfs.h> #include <linux/device.h> @@ -187,6 +187,7 @@ static int got_event; /* if events processing have been done */ static void switchover_timeout(unsigned long data); static struct timer_list switchover_timer = TIMER_INITIALIZER(switchover_timeout , 0, 0); +static unsigned long tlclk_timer_data; static struct tlclk_alarms *alarm_events; @@ -194,14 +195,26 @@ static DEFINE_SPINLOCK(event_lock); static int tlclk_major = TLCLK_MAJOR; -static irqreturn_t tlclk_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t tlclk_interrupt(int irq, void *dev_id); static DECLARE_WAIT_QUEUE_HEAD(wq); +static unsigned long useflags; +static DEFINE_MUTEX(tlclk_mutex); + static int tlclk_open(struct inode *inode, struct file *filp) { int result; + mutex_lock(&tlclk_mutex); + if (test_and_set_bit(0, &useflags)) { + result = -EBUSY; + /* this legacy device is always one per system and it doesn't + * know how to handle multiple concurrent clients. + */ + goto out; + } + /* Make sure there is no interrupt pending while * initialising interrupt handler */ inb(TLCLK_REG6); @@ -209,19 +222,21 @@ static int tlclk_open(struct inode *inode, struct file *filp) /* This device is wired through the FPGA IO space of the ATCA blade * we can't share this IRQ */ result = request_irq(telclk_interrupt, &tlclk_interrupt, - SA_INTERRUPT, "telco_clock", tlclk_interrupt); - if (result == -EBUSY) { + 0, "telco_clock", tlclk_interrupt); + if (result == -EBUSY) printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n"); - return -EBUSY; - } - inb(TLCLK_REG6); /* Clear interrupt events */ + else + inb(TLCLK_REG6); /* Clear interrupt events */ - return 0; +out: + mutex_unlock(&tlclk_mutex); + return result; } static int tlclk_release(struct inode *inode, struct file *filp) { free_irq(telclk_interrupt, tlclk_interrupt); + clear_bit(0, &useflags); return 0; } @@ -231,28 +246,28 @@ static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count, { if (count < sizeof(struct tlclk_alarms)) return -EIO; + if (mutex_lock_interruptible(&tlclk_mutex)) + return -EINTR; + wait_event_interruptible(wq, got_event); - if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms))) + if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms))) { + mutex_unlock(&tlclk_mutex); return -EFAULT; + } memset(alarm_events, 0, sizeof(struct tlclk_alarms)); got_event = 0; + mutex_unlock(&tlclk_mutex); return sizeof(struct tlclk_alarms); } -static ssize_t tlclk_write(struct file *filp, const char __user *buf, size_t count, - loff_t *f_pos) -{ - return 0; -} - -static struct file_operations tlclk_fops = { +static const struct file_operations tlclk_fops = { .read = tlclk_read, - .write = tlclk_write, .open = tlclk_open, .release = tlclk_release, + .llseek = noop_llseek, }; @@ -327,7 +342,7 @@ static ssize_t store_received_ref_clk3a(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(received_ref_clk3a, S_IWUGO, NULL, +static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL, store_received_ref_clk3a); @@ -343,13 +358,13 @@ static ssize_t store_received_ref_clk3b(struct device *d, val = (unsigned char)tmp; spin_lock_irqsave(&event_lock, flags); - SET_PORT_BITS(TLCLK_REG1, 0xef, val << 1); + SET_PORT_BITS(TLCLK_REG1, 0xdf, val << 1); spin_unlock_irqrestore(&event_lock, flags); return strnlen(buf, count); } -static DEVICE_ATTR(received_ref_clk3b, S_IWUGO, NULL, +static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL, store_received_ref_clk3b); @@ -371,7 +386,7 @@ static ssize_t store_enable_clk3b_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clk3b_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clk3b_output); static ssize_t store_enable_clk3a_output(struct device *d, @@ -392,7 +407,7 @@ static ssize_t store_enable_clk3a_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clk3a_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clk3a_output); static ssize_t store_enable_clkb1_output(struct device *d, @@ -413,7 +428,7 @@ static ssize_t store_enable_clkb1_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clkb1_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clkb1_output); @@ -435,7 +450,7 @@ static ssize_t store_enable_clka1_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clka1_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clka1_output); static ssize_t store_enable_clkb0_output(struct device *d, @@ -456,7 +471,7 @@ static ssize_t store_enable_clkb0_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clkb0_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clkb0_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clkb0_output); static ssize_t store_enable_clka0_output(struct device *d, @@ -477,7 +492,7 @@ static ssize_t store_enable_clka0_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clka0_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clka0_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clka0_output); static ssize_t store_select_amcb2_transmit_clock(struct device *d, @@ -519,7 +534,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_amcb2_transmit_clock, S_IWUGO, NULL, +static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL, store_select_amcb2_transmit_clock); static ssize_t store_select_amcb1_transmit_clock(struct device *d, @@ -541,7 +556,7 @@ static ssize_t store_select_amcb1_transmit_clock(struct device *d, SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x7); switch (val) { case CLK_8_592MHz: - SET_PORT_BITS(TLCLK_REG0, 0xfc, 1); + SET_PORT_BITS(TLCLK_REG0, 0xfc, 2); break; case CLK_11_184MHz: SET_PORT_BITS(TLCLK_REG0, 0xfc, 0); @@ -550,7 +565,7 @@ static ssize_t store_select_amcb1_transmit_clock(struct device *d, SET_PORT_BITS(TLCLK_REG0, 0xfc, 3); break; case CLK_44_736MHz: - SET_PORT_BITS(TLCLK_REG0, 0xfc, 2); + SET_PORT_BITS(TLCLK_REG0, 0xfc, 1); break; } } else @@ -560,7 +575,7 @@ static ssize_t store_select_amcb1_transmit_clock(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_amcb1_transmit_clock, S_IWUGO, NULL, +static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL, store_select_amcb1_transmit_clock); static ssize_t store_select_redundant_clock(struct device *d, @@ -581,7 +596,7 @@ static ssize_t store_select_redundant_clock(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_redundant_clock, S_IWUGO, NULL, +static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL, store_select_redundant_clock); static ssize_t store_select_ref_frequency(struct device *d, @@ -602,7 +617,7 @@ static ssize_t store_select_ref_frequency(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_ref_frequency, S_IWUGO, NULL, +static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL, store_select_ref_frequency); static ssize_t store_filter_select(struct device *d, @@ -623,7 +638,7 @@ static ssize_t store_filter_select(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(filter_select, S_IWUGO, NULL, store_filter_select); +static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select); static ssize_t store_hardware_switching_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -643,7 +658,7 @@ static ssize_t store_hardware_switching_mode(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(hardware_switching_mode, S_IWUGO, NULL, +static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL, store_hardware_switching_mode); static ssize_t store_hardware_switching(struct device *d, @@ -664,7 +679,7 @@ static ssize_t store_hardware_switching(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(hardware_switching, S_IWUGO, NULL, +static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL, store_hardware_switching); static ssize_t store_refalign (struct device *d, @@ -684,7 +699,7 @@ static ssize_t store_refalign (struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(refalign, S_IWUGO, NULL, store_refalign); +static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign); static ssize_t store_mode_select (struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -704,7 +719,7 @@ static ssize_t store_mode_select (struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(mode_select, S_IWUGO, NULL, store_mode_select); +static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select); static ssize_t store_reset (struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -724,7 +739,7 @@ static ssize_t store_reset (struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(reset, S_IWUGO, NULL, store_reset); +static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset); static struct attribute *tlclk_sysfs_entries[] = { &dev_attr_current_ref.attr, @@ -767,9 +782,12 @@ static int __init tlclk_init(void) printk(KERN_ERR "tlclk: can't get major %d.\n", tlclk_major); return ret; } + tlclk_major = ret; alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); - if (!alarm_events) + if (!alarm_events) { + ret = -ENOMEM; goto out1; + } /* Read telecom clock IRQ number (Set by BIOS) */ if (!request_region(TLCLK_BASE, 8, "telco_clock")) { @@ -781,7 +799,7 @@ static int __init tlclk_init(void) telclk_interrupt = (inb(TLCLK_REG7) & 0x0f); if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */ - printk(KERN_ERR "telclk_interrup = 0x%x non-mcpbl0010 hw.\n", + printk(KERN_ERR "telclk_interrupt = 0x%x non-mcpbl0010 hw.\n", telclk_interrupt); ret = -ENXIO; goto out3; @@ -792,15 +810,14 @@ static int __init tlclk_init(void) ret = misc_register(&tlclk_miscdev); if (ret < 0) { printk(KERN_ERR "tlclk: misc_register returns %d.\n", ret); - ret = -EBUSY; goto out3; } tlclk_device = platform_device_register_simple("telco_clock", -1, NULL, 0); - if (!tlclk_device) { + if (IS_ERR(tlclk_device)) { printk(KERN_ERR "tlclk: platform_device_register failed.\n"); - ret = -EBUSY; + ret = PTR_ERR(tlclk_device); goto out4; } @@ -808,8 +825,6 @@ static int __init tlclk_init(void) &tlclk_attribute_group); if (ret) { printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n"); - sysfs_remove_group(&tlclk_device->dev.kobj, - &tlclk_attribute_group); goto out5; } @@ -842,11 +857,13 @@ static void __exit tlclk_cleanup(void) static void switchover_timeout(unsigned long data) { - if ((data & 1)) { - if ((inb(TLCLK_REG1) & 0x08) != (data & 0x08)) + unsigned long flags = *(unsigned long *) data; + + if ((flags & 1)) { + if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08)) alarm_events->switchover_primary++; } else { - if ((inb(TLCLK_REG1) & 0x08) != (data & 0x08)) + if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08)) alarm_events->switchover_secondary++; } @@ -856,7 +873,7 @@ static void switchover_timeout(unsigned long data) wake_up(&wq); } -static irqreturn_t tlclk_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t tlclk_interrupt(int irq, void *dev_id) { unsigned long flags; @@ -904,8 +921,9 @@ static irqreturn_t tlclk_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* TIMEOUT in ~10ms */ switchover_timer.expires = jiffies + msecs_to_jiffies(10); - switchover_timer.data = inb(TLCLK_REG1); - add_timer(&switchover_timer); + tlclk_timer_data = inb(TLCLK_REG1); + switchover_timer.data = (unsigned long) &tlclk_timer_data; + mod_timer(&switchover_timer, switchover_timer.expires); } else { got_event = 1; wake_up(&wq); |
