xref: /linux/drivers/char/tlclk.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
11a80ba88SMark Gross /*
21a80ba88SMark Gross  * Telecom Clock driver for Intel NetStructure(tm) MPCBL0010
31a80ba88SMark Gross  *
41a80ba88SMark Gross  * Copyright (C) 2005 Kontron Canada
51a80ba88SMark Gross  *
61a80ba88SMark Gross  * All rights reserved.
71a80ba88SMark Gross  *
81a80ba88SMark Gross  * This program is free software; you can redistribute it and/or modify
91a80ba88SMark Gross  * it under the terms of the GNU General Public License as published by
101a80ba88SMark Gross  * the Free Software Foundation; either version 2 of the License, or (at
111a80ba88SMark Gross  * your option) any later version.
121a80ba88SMark Gross  *
131a80ba88SMark Gross  * This program is distributed in the hope that it will be useful, but
141a80ba88SMark Gross  * WITHOUT ANY WARRANTY; without even the implied warranty of
151a80ba88SMark Gross  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
161a80ba88SMark Gross  * NON INFRINGEMENT.  See the GNU General Public License for more
171a80ba88SMark Gross  * details.
181a80ba88SMark Gross  *
191a80ba88SMark Gross  * You should have received a copy of the GNU General Public License
201a80ba88SMark Gross  * along with this program; if not, write to the Free Software
211a80ba88SMark Gross  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
221a80ba88SMark Gross  *
231a80ba88SMark Gross  * Send feedback to <sebastien.bouchard@ca.kontron.com> and the current
241a80ba88SMark Gross  * Maintainer  <mark.gross@intel.com>
251a80ba88SMark Gross  *
261a80ba88SMark Gross  * Description : This is the TELECOM CLOCK module driver for the ATCA
271a80ba88SMark Gross  * MPCBL0010 ATCA computer.
281a80ba88SMark Gross  */
291a80ba88SMark Gross 
301a80ba88SMark Gross #include <linux/module.h>
311a80ba88SMark Gross #include <linux/init.h>
321a80ba88SMark Gross #include <linux/kernel.h>	/* printk() */
331a80ba88SMark Gross #include <linux/fs.h>		/* everything... */
341a80ba88SMark Gross #include <linux/errno.h>	/* error codes */
35d43c36dcSAlexey Dobriyan #include <linux/sched.h>
361a80ba88SMark Gross #include <linux/slab.h>
371a80ba88SMark Gross #include <linux/ioport.h>
381a80ba88SMark Gross #include <linux/interrupt.h>
391a80ba88SMark Gross #include <linux/spinlock.h>
40efbec1cdSArnd Bergmann #include <linux/mutex.h>
411a80ba88SMark Gross #include <linux/timer.h>
421a80ba88SMark Gross #include <linux/sysfs.h>
431a80ba88SMark Gross #include <linux/device.h>
441a80ba88SMark Gross #include <linux/miscdevice.h>
45ce463370SAndrew Morton #include <linux/platform_device.h>
461a80ba88SMark Gross #include <asm/io.h>		/* inb/outb */
477c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
481a80ba88SMark Gross 
491a80ba88SMark Gross MODULE_AUTHOR("Sebastien Bouchard <sebastien.bouchard@ca.kontron.com>");
50*a4648228SJeff Johnson MODULE_DESCRIPTION("Telecom Clock driver for Intel NetStructure(tm) MPCBL0010");
511a80ba88SMark Gross MODULE_LICENSE("GPL");
521a80ba88SMark Gross 
531a80ba88SMark Gross /*Hardware Reset of the PLL */
541a80ba88SMark Gross #define RESET_ON	0x00
551a80ba88SMark Gross #define RESET_OFF	0x01
561a80ba88SMark Gross 
571a80ba88SMark Gross /* MODE SELECT */
581a80ba88SMark Gross #define NORMAL_MODE 	0x00
591a80ba88SMark Gross #define HOLDOVER_MODE	0x10
601a80ba88SMark Gross #define FREERUN_MODE	0x20
611a80ba88SMark Gross 
621a80ba88SMark Gross /* FILTER SELECT */
631a80ba88SMark Gross #define FILTER_6HZ	0x04
641a80ba88SMark Gross #define FILTER_12HZ	0x00
651a80ba88SMark Gross 
661a80ba88SMark Gross /* SELECT REFERENCE FREQUENCY */
671a80ba88SMark Gross #define REF_CLK1_8kHz		0x00
681a80ba88SMark Gross #define REF_CLK2_19_44MHz	0x02
691a80ba88SMark Gross 
701a80ba88SMark Gross /* Select primary or secondary redundant clock */
711a80ba88SMark Gross #define PRIMARY_CLOCK	0x00
721a80ba88SMark Gross #define SECONDARY_CLOCK	0x01
731a80ba88SMark Gross 
741a80ba88SMark Gross /* CLOCK TRANSMISSION DEFINE */
751a80ba88SMark Gross #define CLK_8kHz	0xff
761a80ba88SMark Gross #define CLK_16_384MHz	0xfb
771a80ba88SMark Gross 
781a80ba88SMark Gross #define CLK_1_544MHz	0x00
791a80ba88SMark Gross #define CLK_2_048MHz	0x01
801a80ba88SMark Gross #define CLK_4_096MHz	0x02
811a80ba88SMark Gross #define CLK_6_312MHz	0x03
821a80ba88SMark Gross #define CLK_8_192MHz	0x04
831a80ba88SMark Gross #define CLK_19_440MHz	0x06
841a80ba88SMark Gross 
851a80ba88SMark Gross #define CLK_8_592MHz	0x08
861a80ba88SMark Gross #define CLK_11_184MHz	0x09
871a80ba88SMark Gross #define CLK_34_368MHz	0x0b
881a80ba88SMark Gross #define CLK_44_736MHz	0x0a
891a80ba88SMark Gross 
901a80ba88SMark Gross /* RECEIVED REFERENCE */
911a80ba88SMark Gross #define AMC_B1 0
921a80ba88SMark Gross #define AMC_B2 1
931a80ba88SMark Gross 
941a80ba88SMark Gross /* HARDWARE SWITCHING DEFINE */
951a80ba88SMark Gross #define HW_ENABLE	0x80
961a80ba88SMark Gross #define HW_DISABLE	0x00
971a80ba88SMark Gross 
981a80ba88SMark Gross /* HARDWARE SWITCHING MODE DEFINE */
991a80ba88SMark Gross #define PLL_HOLDOVER	0x40
1001a80ba88SMark Gross #define LOST_CLOCK	0x00
1011a80ba88SMark Gross 
1021a80ba88SMark Gross /* ALARMS DEFINE */
1031a80ba88SMark Gross #define UNLOCK_MASK	0x10
1041a80ba88SMark Gross #define HOLDOVER_MASK	0x20
1051a80ba88SMark Gross #define SEC_LOST_MASK	0x40
1061a80ba88SMark Gross #define PRI_LOST_MASK	0x80
1071a80ba88SMark Gross 
1081a80ba88SMark Gross /* INTERRUPT CAUSE DEFINE */
1091a80ba88SMark Gross 
1101a80ba88SMark Gross #define PRI_LOS_01_MASK		0x01
1111a80ba88SMark Gross #define PRI_LOS_10_MASK		0x02
1121a80ba88SMark Gross 
1131a80ba88SMark Gross #define SEC_LOS_01_MASK		0x04
1141a80ba88SMark Gross #define SEC_LOS_10_MASK		0x08
1151a80ba88SMark Gross 
1161a80ba88SMark Gross #define HOLDOVER_01_MASK	0x10
1171a80ba88SMark Gross #define HOLDOVER_10_MASK	0x20
1181a80ba88SMark Gross 
1191a80ba88SMark Gross #define UNLOCK_01_MASK		0x40
1201a80ba88SMark Gross #define UNLOCK_10_MASK		0x80
1211a80ba88SMark Gross 
1221a80ba88SMark Gross struct tlclk_alarms {
1231a80ba88SMark Gross 	__u32 lost_clocks;
1241a80ba88SMark Gross 	__u32 lost_primary_clock;
1251a80ba88SMark Gross 	__u32 lost_secondary_clock;
1261a80ba88SMark Gross 	__u32 primary_clock_back;
1271a80ba88SMark Gross 	__u32 secondary_clock_back;
1281a80ba88SMark Gross 	__u32 switchover_primary;
1291a80ba88SMark Gross 	__u32 switchover_secondary;
1301a80ba88SMark Gross 	__u32 pll_holdover;
1311a80ba88SMark Gross 	__u32 pll_end_holdover;
1321a80ba88SMark Gross 	__u32 pll_lost_sync;
1331a80ba88SMark Gross 	__u32 pll_sync;
1341a80ba88SMark Gross };
1351a80ba88SMark Gross /* Telecom clock I/O register definition */
1361a80ba88SMark Gross #define TLCLK_BASE 0xa08
1371a80ba88SMark Gross #define TLCLK_REG0 TLCLK_BASE
1381a80ba88SMark Gross #define TLCLK_REG1 (TLCLK_BASE+1)
1391a80ba88SMark Gross #define TLCLK_REG2 (TLCLK_BASE+2)
1401a80ba88SMark Gross #define TLCLK_REG3 (TLCLK_BASE+3)
1411a80ba88SMark Gross #define TLCLK_REG4 (TLCLK_BASE+4)
1421a80ba88SMark Gross #define TLCLK_REG5 (TLCLK_BASE+5)
1431a80ba88SMark Gross #define TLCLK_REG6 (TLCLK_BASE+6)
1441a80ba88SMark Gross #define TLCLK_REG7 (TLCLK_BASE+7)
1451a80ba88SMark Gross 
1461a80ba88SMark Gross #define SET_PORT_BITS(port, mask, val) outb(((inb(port) & mask) | val), port)
1471a80ba88SMark Gross 
1481a80ba88SMark Gross /* 0 = Dynamic allocation of the major device number */
1491a80ba88SMark Gross #define TLCLK_MAJOR 0
1501a80ba88SMark Gross 
1511a80ba88SMark Gross /* sysfs interface definition:
1521a80ba88SMark Gross Upon loading the driver will create a sysfs directory under
1531a80ba88SMark Gross /sys/devices/platform/telco_clock.
1541a80ba88SMark Gross 
1551a80ba88SMark Gross This directory exports the following interfaces.  There operation is
1561a80ba88SMark Gross documented in the MCPBL0010 TPS under the Telecom Clock API section, 11.4.
1571a80ba88SMark Gross alarms				:
1581a80ba88SMark Gross current_ref			:
159648bf4fbSmark gross received_ref_clk3a		:
160648bf4fbSmark gross received_ref_clk3b		:
1611a80ba88SMark Gross enable_clk3a_output		:
1621a80ba88SMark Gross enable_clk3b_output		:
1631a80ba88SMark Gross enable_clka0_output		:
1641a80ba88SMark Gross enable_clka1_output		:
1651a80ba88SMark Gross enable_clkb0_output		:
1661a80ba88SMark Gross enable_clkb1_output		:
1671a80ba88SMark Gross filter_select			:
1681a80ba88SMark Gross hardware_switching		:
1691a80ba88SMark Gross hardware_switching_mode		:
170648bf4fbSmark gross telclock_version		:
1711a80ba88SMark Gross mode_select			:
1721a80ba88SMark Gross refalign			:
1731a80ba88SMark Gross reset				:
1741a80ba88SMark Gross select_amcb1_transmit_clock	:
1751a80ba88SMark Gross select_amcb2_transmit_clock	:
1761a80ba88SMark Gross select_redundant_clock		:
1771a80ba88SMark Gross select_ref_frequency		:
1781a80ba88SMark Gross 
1791a80ba88SMark Gross All sysfs interfaces are integers in hex format, i.e echo 99 > refalign
1801a80ba88SMark Gross has the same effect as echo 0x99 > refalign.
1811a80ba88SMark Gross */
1821a80ba88SMark Gross 
1831a80ba88SMark Gross static unsigned int telclk_interrupt;
1841a80ba88SMark Gross 
1851a80ba88SMark Gross static int int_events;		/* Event that generate a interrupt */
1861a80ba88SMark Gross static int got_event;		/* if events processing have been done */
1871a80ba88SMark Gross 
18851487d9eSKees Cook static void switchover_timeout(struct timer_list *t);
18951487d9eSKees Cook static struct timer_list switchover_timer;
19079603a35SMark Gross static unsigned long tlclk_timer_data;
1911a80ba88SMark Gross 
1921a80ba88SMark Gross static struct tlclk_alarms *alarm_events;
1931a80ba88SMark Gross 
1941a80ba88SMark Gross static DEFINE_SPINLOCK(event_lock);
1951a80ba88SMark Gross 
1961a80ba88SMark Gross static int tlclk_major = TLCLK_MAJOR;
1971a80ba88SMark Gross 
1987d12e780SDavid Howells static irqreturn_t tlclk_interrupt(int irq, void *dev_id);
1991a80ba88SMark Gross 
2001a80ba88SMark Gross static DECLARE_WAIT_QUEUE_HEAD(wq);
2011a80ba88SMark Gross 
20279603a35SMark Gross static unsigned long useflags;
20379603a35SMark Gross static DEFINE_MUTEX(tlclk_mutex);
20479603a35SMark Gross 
tlclk_open(struct inode * inode,struct file * filp)2051a80ba88SMark Gross static int tlclk_open(struct inode *inode, struct file *filp)
2061a80ba88SMark Gross {
2071a80ba88SMark Gross 	int result;
2081a80ba88SMark Gross 
209efbec1cdSArnd Bergmann 	mutex_lock(&tlclk_mutex);
210b8c71d7aSJonathan Corbet 	if (test_and_set_bit(0, &useflags)) {
211b8c71d7aSJonathan Corbet 		result = -EBUSY;
21279603a35SMark Gross 		/* this legacy device is always one per system and it doesn't
21379603a35SMark Gross 		 * know how to handle multiple concurrent clients.
21479603a35SMark Gross 		 */
215b8c71d7aSJonathan Corbet 		goto out;
216b8c71d7aSJonathan Corbet 	}
21779603a35SMark Gross 
2181a80ba88SMark Gross 	/* Make sure there is no interrupt pending while
2191a80ba88SMark Gross 	 * initialising interrupt handler */
2201a80ba88SMark Gross 	inb(TLCLK_REG6);
2211a80ba88SMark Gross 
2221a80ba88SMark Gross 	/* This device is wired through the FPGA IO space of the ATCA blade
2231a80ba88SMark Gross 	 * we can't share this IRQ */
2241a80ba88SMark Gross 	result = request_irq(telclk_interrupt, &tlclk_interrupt,
2259de14f1cSMichael Opdenacker 			     0, "telco_clock", tlclk_interrupt);
226b8c71d7aSJonathan Corbet 	if (result == -EBUSY)
2274ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");
228b8c71d7aSJonathan Corbet 	else
2291a80ba88SMark Gross 		inb(TLCLK_REG6);	/* Clear interrupt events */
2301a80ba88SMark Gross 
231b8c71d7aSJonathan Corbet out:
232efbec1cdSArnd Bergmann 	mutex_unlock(&tlclk_mutex);
233b8c71d7aSJonathan Corbet 	return result;
2341a80ba88SMark Gross }
2351a80ba88SMark Gross 
tlclk_release(struct inode * inode,struct file * filp)2361a80ba88SMark Gross static int tlclk_release(struct inode *inode, struct file *filp)
2371a80ba88SMark Gross {
2381a80ba88SMark Gross 	free_irq(telclk_interrupt, tlclk_interrupt);
23979603a35SMark Gross 	clear_bit(0, &useflags);
2401a80ba88SMark Gross 
2411a80ba88SMark Gross 	return 0;
2421a80ba88SMark Gross }
2431a80ba88SMark Gross 
tlclk_read(struct file * filp,char __user * buf,size_t count,loff_t * f_pos)244648bf4fbSmark gross static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count,
2451a80ba88SMark Gross 		loff_t *f_pos)
2461a80ba88SMark Gross {
2471a80ba88SMark Gross 	if (count < sizeof(struct tlclk_alarms))
2481a80ba88SMark Gross 		return -EIO;
24979603a35SMark Gross 	if (mutex_lock_interruptible(&tlclk_mutex))
25079603a35SMark Gross 		return -EINTR;
25179603a35SMark Gross 
2521a80ba88SMark Gross 
2531a80ba88SMark Gross 	wait_event_interruptible(wq, got_event);
25479603a35SMark Gross 	if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms))) {
25579603a35SMark Gross 		mutex_unlock(&tlclk_mutex);
2561a80ba88SMark Gross 		return -EFAULT;
25779603a35SMark Gross 	}
2581a80ba88SMark Gross 
2591a80ba88SMark Gross 	memset(alarm_events, 0, sizeof(struct tlclk_alarms));
2601a80ba88SMark Gross 	got_event = 0;
2611a80ba88SMark Gross 
26279603a35SMark Gross 	mutex_unlock(&tlclk_mutex);
2631a80ba88SMark Gross 	return  sizeof(struct tlclk_alarms);
2641a80ba88SMark Gross }
2651a80ba88SMark Gross 
26662322d25SArjan van de Ven static const struct file_operations tlclk_fops = {
2671a80ba88SMark Gross 	.read = tlclk_read,
2681a80ba88SMark Gross 	.open = tlclk_open,
2691a80ba88SMark Gross 	.release = tlclk_release,
2706038f373SArnd Bergmann 	.llseek = noop_llseek,
2711a80ba88SMark Gross 
2721a80ba88SMark Gross };
2731a80ba88SMark Gross 
2741a80ba88SMark Gross static struct miscdevice tlclk_miscdev = {
2751a80ba88SMark Gross 	.minor = MISC_DYNAMIC_MINOR,
2761a80ba88SMark Gross 	.name = "telco_clock",
2771a80ba88SMark Gross 	.fops = &tlclk_fops,
2781a80ba88SMark Gross };
2791a80ba88SMark Gross 
show_current_ref(struct device * d,struct device_attribute * attr,char * buf)2801a80ba88SMark Gross static ssize_t show_current_ref(struct device *d,
2811a80ba88SMark Gross 		struct device_attribute *attr, char *buf)
2821a80ba88SMark Gross {
2831a80ba88SMark Gross 	unsigned long ret_val;
2841a80ba88SMark Gross 	unsigned long flags;
2851a80ba88SMark Gross 
2861a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
2871a80ba88SMark Gross 	ret_val = ((inb(TLCLK_REG1) & 0x08) >> 3);
2881a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
2891a80ba88SMark Gross 
2901a80ba88SMark Gross 	return sprintf(buf, "0x%lX\n", ret_val);
2911a80ba88SMark Gross }
2921a80ba88SMark Gross 
2931a80ba88SMark Gross static DEVICE_ATTR(current_ref, S_IRUGO, show_current_ref, NULL);
2941a80ba88SMark Gross 
2951a80ba88SMark Gross 
show_telclock_version(struct device * d,struct device_attribute * attr,char * buf)296648bf4fbSmark gross static ssize_t show_telclock_version(struct device *d,
2971a80ba88SMark Gross 		struct device_attribute *attr, char *buf)
2981a80ba88SMark Gross {
2991a80ba88SMark Gross 	unsigned long ret_val;
3001a80ba88SMark Gross 	unsigned long flags;
3011a80ba88SMark Gross 
3021a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
303648bf4fbSmark gross 	ret_val = inb(TLCLK_REG5);
3041a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
3051a80ba88SMark Gross 
3061a80ba88SMark Gross 	return sprintf(buf, "0x%lX\n", ret_val);
3071a80ba88SMark Gross }
3081a80ba88SMark Gross 
309648bf4fbSmark gross static DEVICE_ATTR(telclock_version, S_IRUGO,
310648bf4fbSmark gross 		show_telclock_version, NULL);
3111a80ba88SMark Gross 
show_alarms(struct device * d,struct device_attribute * attr,char * buf)3121a80ba88SMark Gross static ssize_t show_alarms(struct device *d,
3131a80ba88SMark Gross 		struct device_attribute *attr,  char *buf)
3141a80ba88SMark Gross {
3151a80ba88SMark Gross 	unsigned long ret_val;
3161a80ba88SMark Gross 	unsigned long flags;
3171a80ba88SMark Gross 
3181a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
3191a80ba88SMark Gross 	ret_val = (inb(TLCLK_REG2) & 0xf0);
3201a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
3211a80ba88SMark Gross 
3221a80ba88SMark Gross 	return sprintf(buf, "0x%lX\n", ret_val);
3231a80ba88SMark Gross }
3241a80ba88SMark Gross 
3251a80ba88SMark Gross static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
3261a80ba88SMark Gross 
store_received_ref_clk3a(struct device * d,struct device_attribute * attr,const char * buf,size_t count)327648bf4fbSmark gross static ssize_t store_received_ref_clk3a(struct device *d,
328648bf4fbSmark gross 		 struct device_attribute *attr, const char *buf, size_t count)
329648bf4fbSmark gross {
330648bf4fbSmark gross 	unsigned long tmp;
331648bf4fbSmark gross 	unsigned char val;
332648bf4fbSmark gross 	unsigned long flags;
333648bf4fbSmark gross 
334648bf4fbSmark gross 	sscanf(buf, "%lX", &tmp);
335648bf4fbSmark gross 	dev_dbg(d, ": tmp = 0x%lX\n", tmp);
336648bf4fbSmark gross 
337648bf4fbSmark gross 	val = (unsigned char)tmp;
338648bf4fbSmark gross 	spin_lock_irqsave(&event_lock, flags);
339648bf4fbSmark gross 	SET_PORT_BITS(TLCLK_REG1, 0xef, val);
340648bf4fbSmark gross 	spin_unlock_irqrestore(&event_lock, flags);
341648bf4fbSmark gross 
342648bf4fbSmark gross 	return strnlen(buf, count);
343648bf4fbSmark gross }
344648bf4fbSmark gross 
34531cc48bfSMark Bellon static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,
346648bf4fbSmark gross 		store_received_ref_clk3a);
347648bf4fbSmark gross 
348648bf4fbSmark gross 
store_received_ref_clk3b(struct device * d,struct device_attribute * attr,const char * buf,size_t count)349648bf4fbSmark gross static ssize_t store_received_ref_clk3b(struct device *d,
350648bf4fbSmark gross 		 struct device_attribute *attr, const char *buf, size_t count)
351648bf4fbSmark gross {
352648bf4fbSmark gross 	unsigned long tmp;
353648bf4fbSmark gross 	unsigned char val;
354648bf4fbSmark gross 	unsigned long flags;
355648bf4fbSmark gross 
356648bf4fbSmark gross 	sscanf(buf, "%lX", &tmp);
357648bf4fbSmark gross 	dev_dbg(d, ": tmp = 0x%lX\n", tmp);
358648bf4fbSmark gross 
359648bf4fbSmark gross 	val = (unsigned char)tmp;
360648bf4fbSmark gross 	spin_lock_irqsave(&event_lock, flags);
361a09ab7e2Smark gross 	SET_PORT_BITS(TLCLK_REG1, 0xdf, val << 1);
362648bf4fbSmark gross 	spin_unlock_irqrestore(&event_lock, flags);
363648bf4fbSmark gross 
364648bf4fbSmark gross 	return strnlen(buf, count);
365648bf4fbSmark gross }
366648bf4fbSmark gross 
36731cc48bfSMark Bellon static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,
368648bf4fbSmark gross 		store_received_ref_clk3b);
369648bf4fbSmark gross 
370648bf4fbSmark gross 
store_enable_clk3b_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)3711a80ba88SMark Gross static ssize_t store_enable_clk3b_output(struct device *d,
3721a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
3731a80ba88SMark Gross {
3741a80ba88SMark Gross 	unsigned long tmp;
3751a80ba88SMark Gross 	unsigned char val;
3761a80ba88SMark Gross 	unsigned long flags;
3771a80ba88SMark Gross 
3781a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
3791a80ba88SMark Gross 	dev_dbg(d, ": tmp = 0x%lX\n", tmp);
3801a80ba88SMark Gross 
3811a80ba88SMark Gross 	val = (unsigned char)tmp;
3821a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
3831a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG3, 0x7f, val << 7);
3841a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
3851a80ba88SMark Gross 
3861a80ba88SMark Gross 	return strnlen(buf, count);
3871a80ba88SMark Gross }
3881a80ba88SMark Gross 
38931cc48bfSMark Bellon static DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL,
3901a80ba88SMark Gross 		store_enable_clk3b_output);
3911a80ba88SMark Gross 
store_enable_clk3a_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)3921a80ba88SMark Gross static ssize_t store_enable_clk3a_output(struct device *d,
3931a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
3941a80ba88SMark Gross {
3951a80ba88SMark Gross 	unsigned long flags;
3961a80ba88SMark Gross 	unsigned long tmp;
3971a80ba88SMark Gross 	unsigned char val;
3981a80ba88SMark Gross 
3991a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4001a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4011a80ba88SMark Gross 
4021a80ba88SMark Gross 	val = (unsigned char)tmp;
4031a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4041a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG3, 0xbf, val << 6);
4051a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4061a80ba88SMark Gross 
4071a80ba88SMark Gross 	return strnlen(buf, count);
4081a80ba88SMark Gross }
4091a80ba88SMark Gross 
41031cc48bfSMark Bellon static DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL,
4111a80ba88SMark Gross 		store_enable_clk3a_output);
4121a80ba88SMark Gross 
store_enable_clkb1_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4131a80ba88SMark Gross static ssize_t store_enable_clkb1_output(struct device *d,
4141a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4151a80ba88SMark Gross {
4161a80ba88SMark Gross 	unsigned long flags;
4171a80ba88SMark Gross 	unsigned long tmp;
4181a80ba88SMark Gross 	unsigned char val;
4191a80ba88SMark Gross 
4201a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4211a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4221a80ba88SMark Gross 
4231a80ba88SMark Gross 	val = (unsigned char)tmp;
4241a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4251a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xf7, val << 3);
4261a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4271a80ba88SMark Gross 
4281a80ba88SMark Gross 	return strnlen(buf, count);
4291a80ba88SMark Gross }
4301a80ba88SMark Gross 
43131cc48bfSMark Bellon static DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL,
4321a80ba88SMark Gross 		store_enable_clkb1_output);
4331a80ba88SMark Gross 
4341a80ba88SMark Gross 
store_enable_clka1_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4351a80ba88SMark Gross static ssize_t store_enable_clka1_output(struct device *d,
4361a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4371a80ba88SMark Gross {
4381a80ba88SMark Gross 	unsigned long flags;
4391a80ba88SMark Gross 	unsigned long tmp;
4401a80ba88SMark Gross 	unsigned char val;
4411a80ba88SMark Gross 
4421a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4431a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4441a80ba88SMark Gross 
4451a80ba88SMark Gross 	val = (unsigned char)tmp;
4461a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4471a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xfb, val << 2);
4481a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4491a80ba88SMark Gross 
4501a80ba88SMark Gross 	return strnlen(buf, count);
4511a80ba88SMark Gross }
4521a80ba88SMark Gross 
45331cc48bfSMark Bellon static DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL,
4541a80ba88SMark Gross 		store_enable_clka1_output);
4551a80ba88SMark Gross 
store_enable_clkb0_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4561a80ba88SMark Gross static ssize_t store_enable_clkb0_output(struct device *d,
4571a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4581a80ba88SMark Gross {
4591a80ba88SMark Gross 	unsigned long flags;
4601a80ba88SMark Gross 	unsigned long tmp;
4611a80ba88SMark Gross 	unsigned char val;
4621a80ba88SMark Gross 
4631a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4641a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4651a80ba88SMark Gross 
4661a80ba88SMark Gross 	val = (unsigned char)tmp;
4671a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4681a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xfd, val << 1);
4691a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4701a80ba88SMark Gross 
4711a80ba88SMark Gross 	return strnlen(buf, count);
4721a80ba88SMark Gross }
4731a80ba88SMark Gross 
47431cc48bfSMark Bellon static DEVICE_ATTR(enable_clkb0_output, (S_IWUSR|S_IWGRP), NULL,
4751a80ba88SMark Gross 		store_enable_clkb0_output);
4761a80ba88SMark Gross 
store_enable_clka0_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4771a80ba88SMark Gross static ssize_t store_enable_clka0_output(struct device *d,
4781a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4791a80ba88SMark Gross {
4801a80ba88SMark Gross 	unsigned long flags;
4811a80ba88SMark Gross 	unsigned long tmp;
4821a80ba88SMark Gross 	unsigned char val;
4831a80ba88SMark Gross 
4841a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4851a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4861a80ba88SMark Gross 
4871a80ba88SMark Gross 	val = (unsigned char)tmp;
4881a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4891a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xfe, val);
4901a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4911a80ba88SMark Gross 
4921a80ba88SMark Gross 	return strnlen(buf, count);
4931a80ba88SMark Gross }
4941a80ba88SMark Gross 
49531cc48bfSMark Bellon static DEVICE_ATTR(enable_clka0_output, (S_IWUSR|S_IWGRP), NULL,
4961a80ba88SMark Gross 		store_enable_clka0_output);
4971a80ba88SMark Gross 
store_select_amcb2_transmit_clock(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4981a80ba88SMark Gross static ssize_t store_select_amcb2_transmit_clock(struct device *d,
4991a80ba88SMark Gross 		struct device_attribute *attr, const char *buf, size_t count)
5001a80ba88SMark Gross {
5011a80ba88SMark Gross 	unsigned long flags;
5021a80ba88SMark Gross 	unsigned long tmp;
5031a80ba88SMark Gross 	unsigned char val;
5041a80ba88SMark Gross 
5051a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
5061a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
5071a80ba88SMark Gross 
5081a80ba88SMark Gross 	val = (unsigned char)tmp;
5091a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
5101a80ba88SMark Gross 	if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) {
5111a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x28);
5121a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val);
5131a80ba88SMark Gross 	} else if (val >= CLK_8_592MHz) {
5141a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x38);
5151a80ba88SMark Gross 		switch (val) {
5161a80ba88SMark Gross 		case CLK_8_592MHz:
517648bf4fbSmark gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 2);
5181a80ba88SMark Gross 			break;
5191a80ba88SMark Gross 		case CLK_11_184MHz:
5201a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 0);
5211a80ba88SMark Gross 			break;
5221a80ba88SMark Gross 		case CLK_34_368MHz:
5231a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 3);
5241a80ba88SMark Gross 			break;
5251a80ba88SMark Gross 		case CLK_44_736MHz:
526648bf4fbSmark gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
5271a80ba88SMark Gross 			break;
5281a80ba88SMark Gross 		}
529bc702adeSColin Ian King 	} else {
5301a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xc7, val << 3);
531bc702adeSColin Ian King 	}
5321a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
5331a80ba88SMark Gross 
5341a80ba88SMark Gross 	return strnlen(buf, count);
5351a80ba88SMark Gross }
5361a80ba88SMark Gross 
53731cc48bfSMark Bellon static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
5381a80ba88SMark Gross 	store_select_amcb2_transmit_clock);
5391a80ba88SMark Gross 
store_select_amcb1_transmit_clock(struct device * d,struct device_attribute * attr,const char * buf,size_t count)5401a80ba88SMark Gross static ssize_t store_select_amcb1_transmit_clock(struct device *d,
5411a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
5421a80ba88SMark Gross {
5431a80ba88SMark Gross 	unsigned long tmp;
5441a80ba88SMark Gross 	unsigned char val;
5451a80ba88SMark Gross 	unsigned long flags;
5461a80ba88SMark Gross 
5471a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
5481a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
5491a80ba88SMark Gross 
5501a80ba88SMark Gross 	val = (unsigned char)tmp;
5511a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
5521a80ba88SMark Gross 	if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) {
5531a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x5);
5541a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val);
5551a80ba88SMark Gross 	} else if (val >= CLK_8_592MHz) {
5561a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x7);
5571a80ba88SMark Gross 		switch (val) {
5581a80ba88SMark Gross 		case CLK_8_592MHz:
55979603a35SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 2);
5601a80ba88SMark Gross 			break;
5611a80ba88SMark Gross 		case CLK_11_184MHz:
5621a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 0);
5631a80ba88SMark Gross 			break;
5641a80ba88SMark Gross 		case CLK_34_368MHz:
5651a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 3);
5661a80ba88SMark Gross 			break;
5671a80ba88SMark Gross 		case CLK_44_736MHz:
56879603a35SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
5691a80ba88SMark Gross 			break;
5701a80ba88SMark Gross 		}
571bc702adeSColin Ian King 	} else {
5721a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xf8, val);
573bc702adeSColin Ian King 	}
5741a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
5751a80ba88SMark Gross 
5761a80ba88SMark Gross 	return strnlen(buf, count);
5771a80ba88SMark Gross }
5781a80ba88SMark Gross 
57931cc48bfSMark Bellon static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
5801a80ba88SMark Gross 		store_select_amcb1_transmit_clock);
5811a80ba88SMark Gross 
store_select_redundant_clock(struct device * d,struct device_attribute * attr,const char * buf,size_t count)5821a80ba88SMark Gross static ssize_t store_select_redundant_clock(struct device *d,
5831a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
5841a80ba88SMark Gross {
5851a80ba88SMark Gross 	unsigned long tmp;
5861a80ba88SMark Gross 	unsigned char val;
5871a80ba88SMark Gross 	unsigned long flags;
5881a80ba88SMark Gross 
5891a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
5901a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
5911a80ba88SMark Gross 
5921a80ba88SMark Gross 	val = (unsigned char)tmp;
5931a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
5941a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG1, 0xfe, val);
5951a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
5961a80ba88SMark Gross 
5971a80ba88SMark Gross 	return strnlen(buf, count);
5981a80ba88SMark Gross }
5991a80ba88SMark Gross 
60031cc48bfSMark Bellon static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL,
6011a80ba88SMark Gross 		store_select_redundant_clock);
6021a80ba88SMark Gross 
store_select_ref_frequency(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6031a80ba88SMark Gross static ssize_t store_select_ref_frequency(struct device *d,
6041a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6051a80ba88SMark Gross {
6061a80ba88SMark Gross 	unsigned long tmp;
6071a80ba88SMark Gross 	unsigned char val;
6081a80ba88SMark Gross 	unsigned long flags;
6091a80ba88SMark Gross 
6101a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6111a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6121a80ba88SMark Gross 
6131a80ba88SMark Gross 	val = (unsigned char)tmp;
6141a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6151a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG1, 0xfd, val);
6161a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6171a80ba88SMark Gross 
6181a80ba88SMark Gross 	return strnlen(buf, count);
6191a80ba88SMark Gross }
6201a80ba88SMark Gross 
62131cc48bfSMark Bellon static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL,
6221a80ba88SMark Gross 		store_select_ref_frequency);
6231a80ba88SMark Gross 
store_filter_select(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6241a80ba88SMark Gross static ssize_t store_filter_select(struct device *d,
6251a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6261a80ba88SMark Gross {
6271a80ba88SMark Gross 	unsigned long tmp;
6281a80ba88SMark Gross 	unsigned char val;
6291a80ba88SMark Gross 	unsigned long flags;
6301a80ba88SMark Gross 
6311a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6321a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6331a80ba88SMark Gross 
6341a80ba88SMark Gross 	val = (unsigned char)tmp;
6351a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6361a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xfb, val);
6371a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6381a80ba88SMark Gross 
6391a80ba88SMark Gross 	return strnlen(buf, count);
6401a80ba88SMark Gross }
6411a80ba88SMark Gross 
64231cc48bfSMark Bellon static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select);
6431a80ba88SMark Gross 
store_hardware_switching_mode(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6441a80ba88SMark Gross static ssize_t store_hardware_switching_mode(struct device *d,
6451a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6461a80ba88SMark Gross {
6471a80ba88SMark Gross 	unsigned long tmp;
6481a80ba88SMark Gross 	unsigned char val;
6491a80ba88SMark Gross 	unsigned long flags;
6501a80ba88SMark Gross 
6511a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6521a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6531a80ba88SMark Gross 
6541a80ba88SMark Gross 	val = (unsigned char)tmp;
6551a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6561a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xbf, val);
6571a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6581a80ba88SMark Gross 
6591a80ba88SMark Gross 	return strnlen(buf, count);
6601a80ba88SMark Gross }
6611a80ba88SMark Gross 
66231cc48bfSMark Bellon static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL,
6631a80ba88SMark Gross 		store_hardware_switching_mode);
6641a80ba88SMark Gross 
store_hardware_switching(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6651a80ba88SMark Gross static ssize_t store_hardware_switching(struct device *d,
6661a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6671a80ba88SMark Gross {
6681a80ba88SMark Gross 	unsigned long tmp;
6691a80ba88SMark Gross 	unsigned char val;
6701a80ba88SMark Gross 	unsigned long flags;
6711a80ba88SMark Gross 
6721a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6731a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6741a80ba88SMark Gross 
6751a80ba88SMark Gross 	val = (unsigned char)tmp;
6761a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6771a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0x7f, val);
6781a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6791a80ba88SMark Gross 
6801a80ba88SMark Gross 	return strnlen(buf, count);
6811a80ba88SMark Gross }
6821a80ba88SMark Gross 
68331cc48bfSMark Bellon static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL,
6841a80ba88SMark Gross 		store_hardware_switching);
6851a80ba88SMark Gross 
store_refalign(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6861a80ba88SMark Gross static ssize_t store_refalign (struct device *d,
6871a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6881a80ba88SMark Gross {
6891a80ba88SMark Gross 	unsigned long tmp;
6901a80ba88SMark Gross 	unsigned long flags;
6911a80ba88SMark Gross 
6921a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6931a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6941a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6951a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xf7, 0);
6961a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xf7, 0x08);
6971a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xf7, 0);
6981a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6991a80ba88SMark Gross 
7001a80ba88SMark Gross 	return strnlen(buf, count);
7011a80ba88SMark Gross }
7021a80ba88SMark Gross 
70331cc48bfSMark Bellon static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign);
7041a80ba88SMark Gross 
store_mode_select(struct device * d,struct device_attribute * attr,const char * buf,size_t count)7051a80ba88SMark Gross static ssize_t store_mode_select (struct device *d,
7061a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
7071a80ba88SMark Gross {
7081a80ba88SMark Gross 	unsigned long tmp;
7091a80ba88SMark Gross 	unsigned char val;
7101a80ba88SMark Gross 	unsigned long flags;
7111a80ba88SMark Gross 
7121a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
7131a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
7141a80ba88SMark Gross 
7151a80ba88SMark Gross 	val = (unsigned char)tmp;
7161a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
7171a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xcf, val);
7181a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
7191a80ba88SMark Gross 
7201a80ba88SMark Gross 	return strnlen(buf, count);
7211a80ba88SMark Gross }
7221a80ba88SMark Gross 
72331cc48bfSMark Bellon static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select);
7241a80ba88SMark Gross 
store_reset(struct device * d,struct device_attribute * attr,const char * buf,size_t count)7251a80ba88SMark Gross static ssize_t store_reset (struct device *d,
7261a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
7271a80ba88SMark Gross {
7281a80ba88SMark Gross 	unsigned long tmp;
7291a80ba88SMark Gross 	unsigned char val;
7301a80ba88SMark Gross 	unsigned long flags;
7311a80ba88SMark Gross 
7321a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
7331a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
7341a80ba88SMark Gross 
7351a80ba88SMark Gross 	val = (unsigned char)tmp;
7361a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
7371a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG4, 0xfd, val);
7381a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
7391a80ba88SMark Gross 
7401a80ba88SMark Gross 	return strnlen(buf, count);
7411a80ba88SMark Gross }
7421a80ba88SMark Gross 
74331cc48bfSMark Bellon static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset);
7441a80ba88SMark Gross 
7451a80ba88SMark Gross static struct attribute *tlclk_sysfs_entries[] = {
7461a80ba88SMark Gross 	&dev_attr_current_ref.attr,
747648bf4fbSmark gross 	&dev_attr_telclock_version.attr,
7481a80ba88SMark Gross 	&dev_attr_alarms.attr,
749648bf4fbSmark gross 	&dev_attr_received_ref_clk3a.attr,
750648bf4fbSmark gross 	&dev_attr_received_ref_clk3b.attr,
7511a80ba88SMark Gross 	&dev_attr_enable_clk3a_output.attr,
7521a80ba88SMark Gross 	&dev_attr_enable_clk3b_output.attr,
7531a80ba88SMark Gross 	&dev_attr_enable_clkb1_output.attr,
7541a80ba88SMark Gross 	&dev_attr_enable_clka1_output.attr,
7551a80ba88SMark Gross 	&dev_attr_enable_clkb0_output.attr,
7561a80ba88SMark Gross 	&dev_attr_enable_clka0_output.attr,
7571a80ba88SMark Gross 	&dev_attr_select_amcb1_transmit_clock.attr,
7581a80ba88SMark Gross 	&dev_attr_select_amcb2_transmit_clock.attr,
7591a80ba88SMark Gross 	&dev_attr_select_redundant_clock.attr,
7601a80ba88SMark Gross 	&dev_attr_select_ref_frequency.attr,
7611a80ba88SMark Gross 	&dev_attr_filter_select.attr,
7621a80ba88SMark Gross 	&dev_attr_hardware_switching_mode.attr,
7631a80ba88SMark Gross 	&dev_attr_hardware_switching.attr,
7641a80ba88SMark Gross 	&dev_attr_refalign.attr,
7651a80ba88SMark Gross 	&dev_attr_mode_select.attr,
7661a80ba88SMark Gross 	&dev_attr_reset.attr,
7671a80ba88SMark Gross 	NULL
7681a80ba88SMark Gross };
7691a80ba88SMark Gross 
7700faef109SArvind Yadav static const struct attribute_group tlclk_attribute_group = {
7711a80ba88SMark Gross 	.name = NULL,		/* put in device directory */
7721a80ba88SMark Gross 	.attrs = tlclk_sysfs_entries,
7731a80ba88SMark Gross };
7741a80ba88SMark Gross 
7751a80ba88SMark Gross static struct platform_device *tlclk_device;
7761a80ba88SMark Gross 
tlclk_init(void)7771a80ba88SMark Gross static int __init tlclk_init(void)
7781a80ba88SMark Gross {
7791a80ba88SMark Gross 	int ret;
7801a80ba88SMark Gross 
78144b8fb6eSMadhuparna Bhowmik 	telclk_interrupt = (inb(TLCLK_REG7) & 0x0f);
78244b8fb6eSMadhuparna Bhowmik 
7831a80ba88SMark Gross 	alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL);
784e045907cSJulia Lawall 	if (!alarm_events) {
785e045907cSJulia Lawall 		ret = -ENOMEM;
7861a80ba88SMark Gross 		goto out1;
787e045907cSJulia Lawall 	}
7881a80ba88SMark Gross 
78944b8fb6eSMadhuparna Bhowmik 	ret = register_chrdev(tlclk_major, "telco_clock", &tlclk_fops);
79044b8fb6eSMadhuparna Bhowmik 	if (ret < 0) {
79144b8fb6eSMadhuparna Bhowmik 		printk(KERN_ERR "tlclk: can't get major %d.\n", tlclk_major);
79244b8fb6eSMadhuparna Bhowmik 		kfree(alarm_events);
79344b8fb6eSMadhuparna Bhowmik 		return ret;
79444b8fb6eSMadhuparna Bhowmik 	}
79544b8fb6eSMadhuparna Bhowmik 	tlclk_major = ret;
79644b8fb6eSMadhuparna Bhowmik 
7971a80ba88SMark Gross 	/* Read telecom clock IRQ number (Set by BIOS) */
7981a80ba88SMark Gross 	if (!request_region(TLCLK_BASE, 8, "telco_clock")) {
7994ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: request_region 0x%X failed.\n",
8001a80ba88SMark Gross 			TLCLK_BASE);
8011a80ba88SMark Gross 		ret = -EBUSY;
8021a80ba88SMark Gross 		goto out2;
8031a80ba88SMark Gross 	}
8041a80ba88SMark Gross 
8051a80ba88SMark Gross 	if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */
806838d51bfSMasanari Iida 		printk(KERN_ERR "telclk_interrupt = 0x%x non-mcpbl0010 hw.\n",
8071a80ba88SMark Gross 			telclk_interrupt);
8081a80ba88SMark Gross 		ret = -ENXIO;
8091a80ba88SMark Gross 		goto out3;
8101a80ba88SMark Gross 	}
8111a80ba88SMark Gross 
81251487d9eSKees Cook 	timer_setup(&switchover_timer, switchover_timeout, 0);
8131a80ba88SMark Gross 
8141a80ba88SMark Gross 	ret = misc_register(&tlclk_miscdev);
8151a80ba88SMark Gross 	if (ret < 0) {
8164ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: misc_register returns %d.\n", ret);
8171a80ba88SMark Gross 		goto out3;
8181a80ba88SMark Gross 	}
8191a80ba88SMark Gross 
8201a80ba88SMark Gross 	tlclk_device = platform_device_register_simple("telco_clock",
8211a80ba88SMark Gross 				-1, NULL, 0);
8225e66b0b5SAkinobu Mita 	if (IS_ERR(tlclk_device)) {
8234ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: platform_device_register failed.\n");
8245e66b0b5SAkinobu Mita 		ret = PTR_ERR(tlclk_device);
8251a80ba88SMark Gross 		goto out4;
8261a80ba88SMark Gross 	}
8271a80ba88SMark Gross 
8281a80ba88SMark Gross 	ret = sysfs_create_group(&tlclk_device->dev.kobj,
8291a80ba88SMark Gross 			&tlclk_attribute_group);
8301a80ba88SMark Gross 	if (ret) {
8314ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n");
8321a80ba88SMark Gross 		goto out5;
8331a80ba88SMark Gross 	}
8341a80ba88SMark Gross 
8351a80ba88SMark Gross 	return 0;
8361a80ba88SMark Gross out5:
8371a80ba88SMark Gross 	platform_device_unregister(tlclk_device);
8381a80ba88SMark Gross out4:
8391a80ba88SMark Gross 	misc_deregister(&tlclk_miscdev);
8401a80ba88SMark Gross out3:
8411a80ba88SMark Gross 	release_region(TLCLK_BASE, 8);
8421a80ba88SMark Gross out2:
8431a80ba88SMark Gross 	kfree(alarm_events);
8441a80ba88SMark Gross 	unregister_chrdev(tlclk_major, "telco_clock");
84544b8fb6eSMadhuparna Bhowmik out1:
8461a80ba88SMark Gross 	return ret;
8471a80ba88SMark Gross }
8481a80ba88SMark Gross 
tlclk_cleanup(void)8491a80ba88SMark Gross static void __exit tlclk_cleanup(void)
8501a80ba88SMark Gross {
8511a80ba88SMark Gross 	sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group);
8521a80ba88SMark Gross 	platform_device_unregister(tlclk_device);
8531a80ba88SMark Gross 	misc_deregister(&tlclk_miscdev);
8541a80ba88SMark Gross 	unregister_chrdev(tlclk_major, "telco_clock");
8551a80ba88SMark Gross 
8561a80ba88SMark Gross 	release_region(TLCLK_BASE, 8);
8571a80ba88SMark Gross 	del_timer_sync(&switchover_timer);
8581a80ba88SMark Gross 	kfree(alarm_events);
8591a80ba88SMark Gross 
8601a80ba88SMark Gross }
8611a80ba88SMark Gross 
switchover_timeout(struct timer_list * unused)86251487d9eSKees Cook static void switchover_timeout(struct timer_list *unused)
8631a80ba88SMark Gross {
86451487d9eSKees Cook 	unsigned long flags = tlclk_timer_data;
86579603a35SMark Gross 
86679603a35SMark Gross 	if ((flags & 1)) {
86779603a35SMark Gross 		if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08))
8681a80ba88SMark Gross 			alarm_events->switchover_primary++;
8691a80ba88SMark Gross 	} else {
87079603a35SMark Gross 		if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08))
8711a80ba88SMark Gross 			alarm_events->switchover_secondary++;
8721a80ba88SMark Gross 	}
8731a80ba88SMark Gross 
8741a80ba88SMark Gross 	/* Alarm processing is done, wake up read task */
8751a80ba88SMark Gross 	del_timer(&switchover_timer);
8761a80ba88SMark Gross 	got_event = 1;
8771a80ba88SMark Gross 	wake_up(&wq);
8781a80ba88SMark Gross }
8791a80ba88SMark Gross 
tlclk_interrupt(int irq,void * dev_id)8807d12e780SDavid Howells static irqreturn_t tlclk_interrupt(int irq, void *dev_id)
8811a80ba88SMark Gross {
8821a80ba88SMark Gross 	unsigned long flags;
8831a80ba88SMark Gross 
8841a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
8851a80ba88SMark Gross 	/* Read and clear interrupt events */
8861a80ba88SMark Gross 	int_events = inb(TLCLK_REG6);
8871a80ba88SMark Gross 
8881a80ba88SMark Gross 	/* Primary_Los changed from 0 to 1 ? */
8891a80ba88SMark Gross 	if (int_events & PRI_LOS_01_MASK) {
8901a80ba88SMark Gross 		if (inb(TLCLK_REG2) & SEC_LOST_MASK)
8911a80ba88SMark Gross 			alarm_events->lost_clocks++;
8921a80ba88SMark Gross 		else
8931a80ba88SMark Gross 			alarm_events->lost_primary_clock++;
8941a80ba88SMark Gross 	}
8951a80ba88SMark Gross 
8961a80ba88SMark Gross 	/* Primary_Los changed from 1 to 0 ? */
8971a80ba88SMark Gross 	if (int_events & PRI_LOS_10_MASK) {
8981a80ba88SMark Gross 		alarm_events->primary_clock_back++;
8991a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xFE, 1);
9001a80ba88SMark Gross 	}
9011a80ba88SMark Gross 	/* Secondary_Los changed from 0 to 1 ? */
9021a80ba88SMark Gross 	if (int_events & SEC_LOS_01_MASK) {
9031a80ba88SMark Gross 		if (inb(TLCLK_REG2) & PRI_LOST_MASK)
9041a80ba88SMark Gross 			alarm_events->lost_clocks++;
9051a80ba88SMark Gross 		else
9061a80ba88SMark Gross 			alarm_events->lost_secondary_clock++;
9071a80ba88SMark Gross 	}
9081a80ba88SMark Gross 	/* Secondary_Los changed from 1 to 0 ? */
9091a80ba88SMark Gross 	if (int_events & SEC_LOS_10_MASK) {
9101a80ba88SMark Gross 		alarm_events->secondary_clock_back++;
9111a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xFE, 0);
9121a80ba88SMark Gross 	}
9131a80ba88SMark Gross 	if (int_events & HOLDOVER_10_MASK)
9141a80ba88SMark Gross 		alarm_events->pll_end_holdover++;
9151a80ba88SMark Gross 
9161a80ba88SMark Gross 	if (int_events & UNLOCK_01_MASK)
9171a80ba88SMark Gross 		alarm_events->pll_lost_sync++;
9181a80ba88SMark Gross 
9191a80ba88SMark Gross 	if (int_events & UNLOCK_10_MASK)
9201a80ba88SMark Gross 		alarm_events->pll_sync++;
9211a80ba88SMark Gross 
9221a80ba88SMark Gross 	/* Holdover changed from 0 to 1 ? */
9231a80ba88SMark Gross 	if (int_events & HOLDOVER_01_MASK) {
9241a80ba88SMark Gross 		alarm_events->pll_holdover++;
9251a80ba88SMark Gross 
9261a80ba88SMark Gross 		/* TIMEOUT in ~10ms */
9271a80ba88SMark Gross 		switchover_timer.expires = jiffies + msecs_to_jiffies(10);
92879603a35SMark Gross 		tlclk_timer_data = inb(TLCLK_REG1);
92979603a35SMark Gross 		mod_timer(&switchover_timer, switchover_timer.expires);
9301a80ba88SMark Gross 	} else {
9311a80ba88SMark Gross 		got_event = 1;
9321a80ba88SMark Gross 		wake_up(&wq);
9331a80ba88SMark Gross 	}
9341a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
9351a80ba88SMark Gross 
9361a80ba88SMark Gross 	return IRQ_HANDLED;
9371a80ba88SMark Gross }
9381a80ba88SMark Gross 
9391a80ba88SMark Gross module_init(tlclk_init);
9401a80ba88SMark Gross module_exit(tlclk_cleanup);
941