Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2117

kernel-2.6.18-238.el5.src.rpm

From: Geoff Gustafson <grgustaf@redhat.com>
Date: Mon, 3 Dec 2007 18:27:29 -0500
Subject: [misc] tlclk driver for telco blade systems
Message-id: 20071203232729.GA16326@samurai.boston.redhat.com
O-Subject: [RHEL5.2 PATCH KFLAG] tlclk driver for telco blade systems (bz 233512)
Bugzilla: 233512

Intel requests that this kflag be enabled in RHEL5.2:

CONFIG_TELCLOCK=m

This just builds a driver tlclk.c that provides userspace configuration of a
telecom clock device found on MPCBL0010 and MPCBL0050 ATCA blade systems.

I found three patches with important bug fixes that had gone in upstream since
2.6.18 and they apply and build cleanly and appear to be valid for RHEL5. They
are combined in the attached patch. (Two other changes had been made as part
of kernel-wide code cleanup and I didn't bother with those.)

We don't have the hardware in-house, but I am told there is hardware at the
joint RH/Intel "center of excellence" that could be used.

Booted with the patch on another box, and couldn't load the module because the
device wasn't found. So at least I'm pretty sure it won't screw up other boxes.
:)

- Geoff

Acked-by: Prarit Bhargava <prarit@redhat.com>

diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index d2c5ba4..f307424 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -186,6 +186,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;
 
@@ -197,10 +198,19 @@ static irqreturn_t tlclk_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 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;
 
+	if (test_and_set_bit(0, &useflags))
+		return -EBUSY;
+		/* this legacy device is always one per system and it doesn't
+		 * know how to handle multiple concurrent clients.
+		 */
+
 	/* Make sure there is no interrupt pending while
 	 * initialising interrupt handler */
 	inb(TLCLK_REG6);
@@ -221,6 +231,7 @@ static int tlclk_open(struct inode *inode, struct file *filp)
 static int tlclk_release(struct inode *inode, struct file *filp)
 {
 	free_irq(telclk_interrupt, tlclk_interrupt);
+	clear_bit(0, &useflags);
 
 	return 0;
 }
@@ -230,26 +241,25 @@ 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 const struct file_operations tlclk_fops = {
 	.read = tlclk_read,
-	.write = tlclk_write,
 	.open = tlclk_open,
 	.release = tlclk_release,
 
@@ -540,7 +550,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);
@@ -549,7 +559,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
@@ -792,15 +802,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 +817,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 +849,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++;
 	}
 
@@ -904,8 +913,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);