xref: /openbmc/linux/drivers/char/tlclk.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
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>");
501a80ba88SMark Gross MODULE_LICENSE("GPL");
511a80ba88SMark Gross 
521a80ba88SMark Gross /*Hardware Reset of the PLL */
531a80ba88SMark Gross #define RESET_ON	0x00
541a80ba88SMark Gross #define RESET_OFF	0x01
551a80ba88SMark Gross 
561a80ba88SMark Gross /* MODE SELECT */
571a80ba88SMark Gross #define NORMAL_MODE 	0x00
581a80ba88SMark Gross #define HOLDOVER_MODE	0x10
591a80ba88SMark Gross #define FREERUN_MODE	0x20
601a80ba88SMark Gross 
611a80ba88SMark Gross /* FILTER SELECT */
621a80ba88SMark Gross #define FILTER_6HZ	0x04
631a80ba88SMark Gross #define FILTER_12HZ	0x00
641a80ba88SMark Gross 
651a80ba88SMark Gross /* SELECT REFERENCE FREQUENCY */
661a80ba88SMark Gross #define REF_CLK1_8kHz		0x00
671a80ba88SMark Gross #define REF_CLK2_19_44MHz	0x02
681a80ba88SMark Gross 
691a80ba88SMark Gross /* Select primary or secondary redundant clock */
701a80ba88SMark Gross #define PRIMARY_CLOCK	0x00
711a80ba88SMark Gross #define SECONDARY_CLOCK	0x01
721a80ba88SMark Gross 
731a80ba88SMark Gross /* CLOCK TRANSMISSION DEFINE */
741a80ba88SMark Gross #define CLK_8kHz	0xff
751a80ba88SMark Gross #define CLK_16_384MHz	0xfb
761a80ba88SMark Gross 
771a80ba88SMark Gross #define CLK_1_544MHz	0x00
781a80ba88SMark Gross #define CLK_2_048MHz	0x01
791a80ba88SMark Gross #define CLK_4_096MHz	0x02
801a80ba88SMark Gross #define CLK_6_312MHz	0x03
811a80ba88SMark Gross #define CLK_8_192MHz	0x04
821a80ba88SMark Gross #define CLK_19_440MHz	0x06
831a80ba88SMark Gross 
841a80ba88SMark Gross #define CLK_8_592MHz	0x08
851a80ba88SMark Gross #define CLK_11_184MHz	0x09
861a80ba88SMark Gross #define CLK_34_368MHz	0x0b
871a80ba88SMark Gross #define CLK_44_736MHz	0x0a
881a80ba88SMark Gross 
891a80ba88SMark Gross /* RECEIVED REFERENCE */
901a80ba88SMark Gross #define AMC_B1 0
911a80ba88SMark Gross #define AMC_B2 1
921a80ba88SMark Gross 
931a80ba88SMark Gross /* HARDWARE SWITCHING DEFINE */
941a80ba88SMark Gross #define HW_ENABLE	0x80
951a80ba88SMark Gross #define HW_DISABLE	0x00
961a80ba88SMark Gross 
971a80ba88SMark Gross /* HARDWARE SWITCHING MODE DEFINE */
981a80ba88SMark Gross #define PLL_HOLDOVER	0x40
991a80ba88SMark Gross #define LOST_CLOCK	0x00
1001a80ba88SMark Gross 
1011a80ba88SMark Gross /* ALARMS DEFINE */
1021a80ba88SMark Gross #define UNLOCK_MASK	0x10
1031a80ba88SMark Gross #define HOLDOVER_MASK	0x20
1041a80ba88SMark Gross #define SEC_LOST_MASK	0x40
1051a80ba88SMark Gross #define PRI_LOST_MASK	0x80
1061a80ba88SMark Gross 
1071a80ba88SMark Gross /* INTERRUPT CAUSE DEFINE */
1081a80ba88SMark Gross 
1091a80ba88SMark Gross #define PRI_LOS_01_MASK		0x01
1101a80ba88SMark Gross #define PRI_LOS_10_MASK		0x02
1111a80ba88SMark Gross 
1121a80ba88SMark Gross #define SEC_LOS_01_MASK		0x04
1131a80ba88SMark Gross #define SEC_LOS_10_MASK		0x08
1141a80ba88SMark Gross 
1151a80ba88SMark Gross #define HOLDOVER_01_MASK	0x10
1161a80ba88SMark Gross #define HOLDOVER_10_MASK	0x20
1171a80ba88SMark Gross 
1181a80ba88SMark Gross #define UNLOCK_01_MASK		0x40
1191a80ba88SMark Gross #define UNLOCK_10_MASK		0x80
1201a80ba88SMark Gross 
1211a80ba88SMark Gross struct tlclk_alarms {
1221a80ba88SMark Gross 	__u32 lost_clocks;
1231a80ba88SMark Gross 	__u32 lost_primary_clock;
1241a80ba88SMark Gross 	__u32 lost_secondary_clock;
1251a80ba88SMark Gross 	__u32 primary_clock_back;
1261a80ba88SMark Gross 	__u32 secondary_clock_back;
1271a80ba88SMark Gross 	__u32 switchover_primary;
1281a80ba88SMark Gross 	__u32 switchover_secondary;
1291a80ba88SMark Gross 	__u32 pll_holdover;
1301a80ba88SMark Gross 	__u32 pll_end_holdover;
1311a80ba88SMark Gross 	__u32 pll_lost_sync;
1321a80ba88SMark Gross 	__u32 pll_sync;
1331a80ba88SMark Gross };
1341a80ba88SMark Gross /* Telecom clock I/O register definition */
1351a80ba88SMark Gross #define TLCLK_BASE 0xa08
1361a80ba88SMark Gross #define TLCLK_REG0 TLCLK_BASE
1371a80ba88SMark Gross #define TLCLK_REG1 (TLCLK_BASE+1)
1381a80ba88SMark Gross #define TLCLK_REG2 (TLCLK_BASE+2)
1391a80ba88SMark Gross #define TLCLK_REG3 (TLCLK_BASE+3)
1401a80ba88SMark Gross #define TLCLK_REG4 (TLCLK_BASE+4)
1411a80ba88SMark Gross #define TLCLK_REG5 (TLCLK_BASE+5)
1421a80ba88SMark Gross #define TLCLK_REG6 (TLCLK_BASE+6)
1431a80ba88SMark Gross #define TLCLK_REG7 (TLCLK_BASE+7)
1441a80ba88SMark Gross 
1451a80ba88SMark Gross #define SET_PORT_BITS(port, mask, val) outb(((inb(port) & mask) | val), port)
1461a80ba88SMark Gross 
1471a80ba88SMark Gross /* 0 = Dynamic allocation of the major device number */
1481a80ba88SMark Gross #define TLCLK_MAJOR 0
1491a80ba88SMark Gross 
1501a80ba88SMark Gross /* sysfs interface definition:
1511a80ba88SMark Gross Upon loading the driver will create a sysfs directory under
1521a80ba88SMark Gross /sys/devices/platform/telco_clock.
1531a80ba88SMark Gross 
1541a80ba88SMark Gross This directory exports the following interfaces.  There operation is
1551a80ba88SMark Gross documented in the MCPBL0010 TPS under the Telecom Clock API section, 11.4.
1561a80ba88SMark Gross alarms				:
1571a80ba88SMark Gross current_ref			:
158648bf4fbSmark gross received_ref_clk3a		:
159648bf4fbSmark gross received_ref_clk3b		:
1601a80ba88SMark Gross enable_clk3a_output		:
1611a80ba88SMark Gross enable_clk3b_output		:
1621a80ba88SMark Gross enable_clka0_output		:
1631a80ba88SMark Gross enable_clka1_output		:
1641a80ba88SMark Gross enable_clkb0_output		:
1651a80ba88SMark Gross enable_clkb1_output		:
1661a80ba88SMark Gross filter_select			:
1671a80ba88SMark Gross hardware_switching		:
1681a80ba88SMark Gross hardware_switching_mode		:
169648bf4fbSmark gross telclock_version		:
1701a80ba88SMark Gross mode_select			:
1711a80ba88SMark Gross refalign			:
1721a80ba88SMark Gross reset				:
1731a80ba88SMark Gross select_amcb1_transmit_clock	:
1741a80ba88SMark Gross select_amcb2_transmit_clock	:
1751a80ba88SMark Gross select_redundant_clock		:
1761a80ba88SMark Gross select_ref_frequency		:
1771a80ba88SMark Gross 
1781a80ba88SMark Gross All sysfs interfaces are integers in hex format, i.e echo 99 > refalign
1791a80ba88SMark Gross has the same effect as echo 0x99 > refalign.
1801a80ba88SMark Gross */
1811a80ba88SMark Gross 
1821a80ba88SMark Gross static unsigned int telclk_interrupt;
1831a80ba88SMark Gross 
1841a80ba88SMark Gross static int int_events;		/* Event that generate a interrupt */
1851a80ba88SMark Gross static int got_event;		/* if events processing have been done */
1861a80ba88SMark Gross 
18751487d9eSKees Cook static void switchover_timeout(struct timer_list *t);
18851487d9eSKees Cook static struct timer_list switchover_timer;
18979603a35SMark Gross static unsigned long tlclk_timer_data;
1901a80ba88SMark Gross 
1911a80ba88SMark Gross static struct tlclk_alarms *alarm_events;
1921a80ba88SMark Gross 
1931a80ba88SMark Gross static DEFINE_SPINLOCK(event_lock);
1941a80ba88SMark Gross 
1951a80ba88SMark Gross static int tlclk_major = TLCLK_MAJOR;
1961a80ba88SMark Gross 
1977d12e780SDavid Howells static irqreturn_t tlclk_interrupt(int irq, void *dev_id);
1981a80ba88SMark Gross 
1991a80ba88SMark Gross static DECLARE_WAIT_QUEUE_HEAD(wq);
2001a80ba88SMark Gross 
20179603a35SMark Gross static unsigned long useflags;
20279603a35SMark Gross static DEFINE_MUTEX(tlclk_mutex);
20379603a35SMark Gross 
tlclk_open(struct inode * inode,struct file * filp)2041a80ba88SMark Gross static int tlclk_open(struct inode *inode, struct file *filp)
2051a80ba88SMark Gross {
2061a80ba88SMark Gross 	int result;
2071a80ba88SMark Gross 
208efbec1cdSArnd Bergmann 	mutex_lock(&tlclk_mutex);
209b8c71d7aSJonathan Corbet 	if (test_and_set_bit(0, &useflags)) {
210b8c71d7aSJonathan Corbet 		result = -EBUSY;
21179603a35SMark Gross 		/* this legacy device is always one per system and it doesn't
21279603a35SMark Gross 		 * know how to handle multiple concurrent clients.
21379603a35SMark Gross 		 */
214b8c71d7aSJonathan Corbet 		goto out;
215b8c71d7aSJonathan Corbet 	}
21679603a35SMark Gross 
2171a80ba88SMark Gross 	/* Make sure there is no interrupt pending while
2181a80ba88SMark Gross 	 * initialising interrupt handler */
2191a80ba88SMark Gross 	inb(TLCLK_REG6);
2201a80ba88SMark Gross 
2211a80ba88SMark Gross 	/* This device is wired through the FPGA IO space of the ATCA blade
2221a80ba88SMark Gross 	 * we can't share this IRQ */
2231a80ba88SMark Gross 	result = request_irq(telclk_interrupt, &tlclk_interrupt,
2249de14f1cSMichael Opdenacker 			     0, "telco_clock", tlclk_interrupt);
225b8c71d7aSJonathan Corbet 	if (result == -EBUSY)
2264ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");
227b8c71d7aSJonathan Corbet 	else
2281a80ba88SMark Gross 		inb(TLCLK_REG6);	/* Clear interrupt events */
2291a80ba88SMark Gross 
230b8c71d7aSJonathan Corbet out:
231efbec1cdSArnd Bergmann 	mutex_unlock(&tlclk_mutex);
232b8c71d7aSJonathan Corbet 	return result;
2331a80ba88SMark Gross }
2341a80ba88SMark Gross 
tlclk_release(struct inode * inode,struct file * filp)2351a80ba88SMark Gross static int tlclk_release(struct inode *inode, struct file *filp)
2361a80ba88SMark Gross {
2371a80ba88SMark Gross 	free_irq(telclk_interrupt, tlclk_interrupt);
23879603a35SMark Gross 	clear_bit(0, &useflags);
2391a80ba88SMark Gross 
2401a80ba88SMark Gross 	return 0;
2411a80ba88SMark Gross }
2421a80ba88SMark Gross 
tlclk_read(struct file * filp,char __user * buf,size_t count,loff_t * f_pos)243648bf4fbSmark gross static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count,
2441a80ba88SMark Gross 		loff_t *f_pos)
2451a80ba88SMark Gross {
2461a80ba88SMark Gross 	if (count < sizeof(struct tlclk_alarms))
2471a80ba88SMark Gross 		return -EIO;
24879603a35SMark Gross 	if (mutex_lock_interruptible(&tlclk_mutex))
24979603a35SMark Gross 		return -EINTR;
25079603a35SMark Gross 
2511a80ba88SMark Gross 
2521a80ba88SMark Gross 	wait_event_interruptible(wq, got_event);
25379603a35SMark Gross 	if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms))) {
25479603a35SMark Gross 		mutex_unlock(&tlclk_mutex);
2551a80ba88SMark Gross 		return -EFAULT;
25679603a35SMark Gross 	}
2571a80ba88SMark Gross 
2581a80ba88SMark Gross 	memset(alarm_events, 0, sizeof(struct tlclk_alarms));
2591a80ba88SMark Gross 	got_event = 0;
2601a80ba88SMark Gross 
26179603a35SMark Gross 	mutex_unlock(&tlclk_mutex);
2621a80ba88SMark Gross 	return  sizeof(struct tlclk_alarms);
2631a80ba88SMark Gross }
2641a80ba88SMark Gross 
26562322d25SArjan van de Ven static const struct file_operations tlclk_fops = {
2661a80ba88SMark Gross 	.read = tlclk_read,
2671a80ba88SMark Gross 	.open = tlclk_open,
2681a80ba88SMark Gross 	.release = tlclk_release,
2696038f373SArnd Bergmann 	.llseek = noop_llseek,
2701a80ba88SMark Gross 
2711a80ba88SMark Gross };
2721a80ba88SMark Gross 
2731a80ba88SMark Gross static struct miscdevice tlclk_miscdev = {
2741a80ba88SMark Gross 	.minor = MISC_DYNAMIC_MINOR,
2751a80ba88SMark Gross 	.name = "telco_clock",
2761a80ba88SMark Gross 	.fops = &tlclk_fops,
2771a80ba88SMark Gross };
2781a80ba88SMark Gross 
show_current_ref(struct device * d,struct device_attribute * attr,char * buf)2791a80ba88SMark Gross static ssize_t show_current_ref(struct device *d,
2801a80ba88SMark Gross 		struct device_attribute *attr, char *buf)
2811a80ba88SMark Gross {
2821a80ba88SMark Gross 	unsigned long ret_val;
2831a80ba88SMark Gross 	unsigned long flags;
2841a80ba88SMark Gross 
2851a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
2861a80ba88SMark Gross 	ret_val = ((inb(TLCLK_REG1) & 0x08) >> 3);
2871a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
2881a80ba88SMark Gross 
2891a80ba88SMark Gross 	return sprintf(buf, "0x%lX\n", ret_val);
2901a80ba88SMark Gross }
2911a80ba88SMark Gross 
2921a80ba88SMark Gross static DEVICE_ATTR(current_ref, S_IRUGO, show_current_ref, NULL);
2931a80ba88SMark Gross 
2941a80ba88SMark Gross 
show_telclock_version(struct device * d,struct device_attribute * attr,char * buf)295648bf4fbSmark gross static ssize_t show_telclock_version(struct device *d,
2961a80ba88SMark Gross 		struct device_attribute *attr, char *buf)
2971a80ba88SMark Gross {
2981a80ba88SMark Gross 	unsigned long ret_val;
2991a80ba88SMark Gross 	unsigned long flags;
3001a80ba88SMark Gross 
3011a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
302648bf4fbSmark gross 	ret_val = inb(TLCLK_REG5);
3031a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
3041a80ba88SMark Gross 
3051a80ba88SMark Gross 	return sprintf(buf, "0x%lX\n", ret_val);
3061a80ba88SMark Gross }
3071a80ba88SMark Gross 
308648bf4fbSmark gross static DEVICE_ATTR(telclock_version, S_IRUGO,
309648bf4fbSmark gross 		show_telclock_version, NULL);
3101a80ba88SMark Gross 
show_alarms(struct device * d,struct device_attribute * attr,char * buf)3111a80ba88SMark Gross static ssize_t show_alarms(struct device *d,
3121a80ba88SMark Gross 		struct device_attribute *attr,  char *buf)
3131a80ba88SMark Gross {
3141a80ba88SMark Gross 	unsigned long ret_val;
3151a80ba88SMark Gross 	unsigned long flags;
3161a80ba88SMark Gross 
3171a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
3181a80ba88SMark Gross 	ret_val = (inb(TLCLK_REG2) & 0xf0);
3191a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
3201a80ba88SMark Gross 
3211a80ba88SMark Gross 	return sprintf(buf, "0x%lX\n", ret_val);
3221a80ba88SMark Gross }
3231a80ba88SMark Gross 
3241a80ba88SMark Gross static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
3251a80ba88SMark Gross 
store_received_ref_clk3a(struct device * d,struct device_attribute * attr,const char * buf,size_t count)326648bf4fbSmark gross static ssize_t store_received_ref_clk3a(struct device *d,
327648bf4fbSmark gross 		 struct device_attribute *attr, const char *buf, size_t count)
328648bf4fbSmark gross {
329648bf4fbSmark gross 	unsigned long tmp;
330648bf4fbSmark gross 	unsigned char val;
331648bf4fbSmark gross 	unsigned long flags;
332648bf4fbSmark gross 
333648bf4fbSmark gross 	sscanf(buf, "%lX", &tmp);
334648bf4fbSmark gross 	dev_dbg(d, ": tmp = 0x%lX\n", tmp);
335648bf4fbSmark gross 
336648bf4fbSmark gross 	val = (unsigned char)tmp;
337648bf4fbSmark gross 	spin_lock_irqsave(&event_lock, flags);
338648bf4fbSmark gross 	SET_PORT_BITS(TLCLK_REG1, 0xef, val);
339648bf4fbSmark gross 	spin_unlock_irqrestore(&event_lock, flags);
340648bf4fbSmark gross 
341648bf4fbSmark gross 	return strnlen(buf, count);
342648bf4fbSmark gross }
343648bf4fbSmark gross 
34431cc48bfSMark Bellon static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,
345648bf4fbSmark gross 		store_received_ref_clk3a);
346648bf4fbSmark gross 
347648bf4fbSmark gross 
store_received_ref_clk3b(struct device * d,struct device_attribute * attr,const char * buf,size_t count)348648bf4fbSmark gross static ssize_t store_received_ref_clk3b(struct device *d,
349648bf4fbSmark gross 		 struct device_attribute *attr, const char *buf, size_t count)
350648bf4fbSmark gross {
351648bf4fbSmark gross 	unsigned long tmp;
352648bf4fbSmark gross 	unsigned char val;
353648bf4fbSmark gross 	unsigned long flags;
354648bf4fbSmark gross 
355648bf4fbSmark gross 	sscanf(buf, "%lX", &tmp);
356648bf4fbSmark gross 	dev_dbg(d, ": tmp = 0x%lX\n", tmp);
357648bf4fbSmark gross 
358648bf4fbSmark gross 	val = (unsigned char)tmp;
359648bf4fbSmark gross 	spin_lock_irqsave(&event_lock, flags);
360a09ab7e2Smark gross 	SET_PORT_BITS(TLCLK_REG1, 0xdf, val << 1);
361648bf4fbSmark gross 	spin_unlock_irqrestore(&event_lock, flags);
362648bf4fbSmark gross 
363648bf4fbSmark gross 	return strnlen(buf, count);
364648bf4fbSmark gross }
365648bf4fbSmark gross 
36631cc48bfSMark Bellon static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,
367648bf4fbSmark gross 		store_received_ref_clk3b);
368648bf4fbSmark gross 
369648bf4fbSmark gross 
store_enable_clk3b_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)3701a80ba88SMark Gross static ssize_t store_enable_clk3b_output(struct device *d,
3711a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
3721a80ba88SMark Gross {
3731a80ba88SMark Gross 	unsigned long tmp;
3741a80ba88SMark Gross 	unsigned char val;
3751a80ba88SMark Gross 	unsigned long flags;
3761a80ba88SMark Gross 
3771a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
3781a80ba88SMark Gross 	dev_dbg(d, ": tmp = 0x%lX\n", tmp);
3791a80ba88SMark Gross 
3801a80ba88SMark Gross 	val = (unsigned char)tmp;
3811a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
3821a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG3, 0x7f, val << 7);
3831a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
3841a80ba88SMark Gross 
3851a80ba88SMark Gross 	return strnlen(buf, count);
3861a80ba88SMark Gross }
3871a80ba88SMark Gross 
38831cc48bfSMark Bellon static DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL,
3891a80ba88SMark Gross 		store_enable_clk3b_output);
3901a80ba88SMark Gross 
store_enable_clk3a_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)3911a80ba88SMark Gross static ssize_t store_enable_clk3a_output(struct device *d,
3921a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
3931a80ba88SMark Gross {
3941a80ba88SMark Gross 	unsigned long flags;
3951a80ba88SMark Gross 	unsigned long tmp;
3961a80ba88SMark Gross 	unsigned char val;
3971a80ba88SMark Gross 
3981a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
3991a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4001a80ba88SMark Gross 
4011a80ba88SMark Gross 	val = (unsigned char)tmp;
4021a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4031a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG3, 0xbf, val << 6);
4041a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4051a80ba88SMark Gross 
4061a80ba88SMark Gross 	return strnlen(buf, count);
4071a80ba88SMark Gross }
4081a80ba88SMark Gross 
40931cc48bfSMark Bellon static DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL,
4101a80ba88SMark Gross 		store_enable_clk3a_output);
4111a80ba88SMark Gross 
store_enable_clkb1_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4121a80ba88SMark Gross static ssize_t store_enable_clkb1_output(struct device *d,
4131a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4141a80ba88SMark Gross {
4151a80ba88SMark Gross 	unsigned long flags;
4161a80ba88SMark Gross 	unsigned long tmp;
4171a80ba88SMark Gross 	unsigned char val;
4181a80ba88SMark Gross 
4191a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4201a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4211a80ba88SMark Gross 
4221a80ba88SMark Gross 	val = (unsigned char)tmp;
4231a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4241a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xf7, val << 3);
4251a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4261a80ba88SMark Gross 
4271a80ba88SMark Gross 	return strnlen(buf, count);
4281a80ba88SMark Gross }
4291a80ba88SMark Gross 
43031cc48bfSMark Bellon static DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL,
4311a80ba88SMark Gross 		store_enable_clkb1_output);
4321a80ba88SMark Gross 
4331a80ba88SMark Gross 
store_enable_clka1_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4341a80ba88SMark Gross static ssize_t store_enable_clka1_output(struct device *d,
4351a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4361a80ba88SMark Gross {
4371a80ba88SMark Gross 	unsigned long flags;
4381a80ba88SMark Gross 	unsigned long tmp;
4391a80ba88SMark Gross 	unsigned char val;
4401a80ba88SMark Gross 
4411a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4421a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4431a80ba88SMark Gross 
4441a80ba88SMark Gross 	val = (unsigned char)tmp;
4451a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4461a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xfb, val << 2);
4471a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4481a80ba88SMark Gross 
4491a80ba88SMark Gross 	return strnlen(buf, count);
4501a80ba88SMark Gross }
4511a80ba88SMark Gross 
45231cc48bfSMark Bellon static DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL,
4531a80ba88SMark Gross 		store_enable_clka1_output);
4541a80ba88SMark Gross 
store_enable_clkb0_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4551a80ba88SMark Gross static ssize_t store_enable_clkb0_output(struct device *d,
4561a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4571a80ba88SMark Gross {
4581a80ba88SMark Gross 	unsigned long flags;
4591a80ba88SMark Gross 	unsigned long tmp;
4601a80ba88SMark Gross 	unsigned char val;
4611a80ba88SMark Gross 
4621a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4631a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4641a80ba88SMark Gross 
4651a80ba88SMark Gross 	val = (unsigned char)tmp;
4661a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4671a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xfd, val << 1);
4681a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4691a80ba88SMark Gross 
4701a80ba88SMark Gross 	return strnlen(buf, count);
4711a80ba88SMark Gross }
4721a80ba88SMark Gross 
47331cc48bfSMark Bellon static DEVICE_ATTR(enable_clkb0_output, (S_IWUSR|S_IWGRP), NULL,
4741a80ba88SMark Gross 		store_enable_clkb0_output);
4751a80ba88SMark Gross 
store_enable_clka0_output(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4761a80ba88SMark Gross static ssize_t store_enable_clka0_output(struct device *d,
4771a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
4781a80ba88SMark Gross {
4791a80ba88SMark Gross 	unsigned long flags;
4801a80ba88SMark Gross 	unsigned long tmp;
4811a80ba88SMark Gross 	unsigned char val;
4821a80ba88SMark Gross 
4831a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
4841a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
4851a80ba88SMark Gross 
4861a80ba88SMark Gross 	val = (unsigned char)tmp;
4871a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
4881a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG2, 0xfe, val);
4891a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
4901a80ba88SMark Gross 
4911a80ba88SMark Gross 	return strnlen(buf, count);
4921a80ba88SMark Gross }
4931a80ba88SMark Gross 
49431cc48bfSMark Bellon static DEVICE_ATTR(enable_clka0_output, (S_IWUSR|S_IWGRP), NULL,
4951a80ba88SMark Gross 		store_enable_clka0_output);
4961a80ba88SMark Gross 
store_select_amcb2_transmit_clock(struct device * d,struct device_attribute * attr,const char * buf,size_t count)4971a80ba88SMark Gross static ssize_t store_select_amcb2_transmit_clock(struct device *d,
4981a80ba88SMark Gross 		struct device_attribute *attr, const char *buf, size_t count)
4991a80ba88SMark Gross {
5001a80ba88SMark Gross 	unsigned long flags;
5011a80ba88SMark Gross 	unsigned long tmp;
5021a80ba88SMark Gross 	unsigned char val;
5031a80ba88SMark Gross 
5041a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
5051a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
5061a80ba88SMark Gross 
5071a80ba88SMark Gross 	val = (unsigned char)tmp;
5081a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
5091a80ba88SMark Gross 	if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) {
5101a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x28);
5111a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val);
5121a80ba88SMark Gross 	} else if (val >= CLK_8_592MHz) {
5131a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x38);
5141a80ba88SMark Gross 		switch (val) {
5151a80ba88SMark Gross 		case CLK_8_592MHz:
516648bf4fbSmark gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 2);
5171a80ba88SMark Gross 			break;
5181a80ba88SMark Gross 		case CLK_11_184MHz:
5191a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 0);
5201a80ba88SMark Gross 			break;
5211a80ba88SMark Gross 		case CLK_34_368MHz:
5221a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 3);
5231a80ba88SMark Gross 			break;
5241a80ba88SMark Gross 		case CLK_44_736MHz:
525648bf4fbSmark gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
5261a80ba88SMark Gross 			break;
5271a80ba88SMark Gross 		}
528bc702adeSColin Ian King 	} else {
5291a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xc7, val << 3);
530bc702adeSColin Ian King 	}
5311a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
5321a80ba88SMark Gross 
5331a80ba88SMark Gross 	return strnlen(buf, count);
5341a80ba88SMark Gross }
5351a80ba88SMark Gross 
53631cc48bfSMark Bellon static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
5371a80ba88SMark Gross 	store_select_amcb2_transmit_clock);
5381a80ba88SMark Gross 
store_select_amcb1_transmit_clock(struct device * d,struct device_attribute * attr,const char * buf,size_t count)5391a80ba88SMark Gross static ssize_t store_select_amcb1_transmit_clock(struct device *d,
5401a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
5411a80ba88SMark Gross {
5421a80ba88SMark Gross 	unsigned long tmp;
5431a80ba88SMark Gross 	unsigned char val;
5441a80ba88SMark Gross 	unsigned long flags;
5451a80ba88SMark Gross 
5461a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
5471a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
5481a80ba88SMark Gross 
5491a80ba88SMark Gross 	val = (unsigned char)tmp;
5501a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
5511a80ba88SMark Gross 	if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) {
5521a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x5);
5531a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val);
5541a80ba88SMark Gross 	} else if (val >= CLK_8_592MHz) {
5551a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x7);
5561a80ba88SMark Gross 		switch (val) {
5571a80ba88SMark Gross 		case CLK_8_592MHz:
55879603a35SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 2);
5591a80ba88SMark Gross 			break;
5601a80ba88SMark Gross 		case CLK_11_184MHz:
5611a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 0);
5621a80ba88SMark Gross 			break;
5631a80ba88SMark Gross 		case CLK_34_368MHz:
5641a80ba88SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 3);
5651a80ba88SMark Gross 			break;
5661a80ba88SMark Gross 		case CLK_44_736MHz:
56779603a35SMark Gross 			SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
5681a80ba88SMark Gross 			break;
5691a80ba88SMark Gross 		}
570bc702adeSColin Ian King 	} else {
5711a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG3, 0xf8, val);
572bc702adeSColin Ian King 	}
5731a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
5741a80ba88SMark Gross 
5751a80ba88SMark Gross 	return strnlen(buf, count);
5761a80ba88SMark Gross }
5771a80ba88SMark Gross 
57831cc48bfSMark Bellon static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
5791a80ba88SMark Gross 		store_select_amcb1_transmit_clock);
5801a80ba88SMark Gross 
store_select_redundant_clock(struct device * d,struct device_attribute * attr,const char * buf,size_t count)5811a80ba88SMark Gross static ssize_t store_select_redundant_clock(struct device *d,
5821a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
5831a80ba88SMark Gross {
5841a80ba88SMark Gross 	unsigned long tmp;
5851a80ba88SMark Gross 	unsigned char val;
5861a80ba88SMark Gross 	unsigned long flags;
5871a80ba88SMark Gross 
5881a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
5891a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
5901a80ba88SMark Gross 
5911a80ba88SMark Gross 	val = (unsigned char)tmp;
5921a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
5931a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG1, 0xfe, val);
5941a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
5951a80ba88SMark Gross 
5961a80ba88SMark Gross 	return strnlen(buf, count);
5971a80ba88SMark Gross }
5981a80ba88SMark Gross 
59931cc48bfSMark Bellon static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL,
6001a80ba88SMark Gross 		store_select_redundant_clock);
6011a80ba88SMark Gross 
store_select_ref_frequency(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6021a80ba88SMark Gross static ssize_t store_select_ref_frequency(struct device *d,
6031a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6041a80ba88SMark Gross {
6051a80ba88SMark Gross 	unsigned long tmp;
6061a80ba88SMark Gross 	unsigned char val;
6071a80ba88SMark Gross 	unsigned long flags;
6081a80ba88SMark Gross 
6091a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6101a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6111a80ba88SMark Gross 
6121a80ba88SMark Gross 	val = (unsigned char)tmp;
6131a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6141a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG1, 0xfd, val);
6151a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6161a80ba88SMark Gross 
6171a80ba88SMark Gross 	return strnlen(buf, count);
6181a80ba88SMark Gross }
6191a80ba88SMark Gross 
62031cc48bfSMark Bellon static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL,
6211a80ba88SMark Gross 		store_select_ref_frequency);
6221a80ba88SMark Gross 
store_filter_select(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6231a80ba88SMark Gross static ssize_t store_filter_select(struct device *d,
6241a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6251a80ba88SMark Gross {
6261a80ba88SMark Gross 	unsigned long tmp;
6271a80ba88SMark Gross 	unsigned char val;
6281a80ba88SMark Gross 	unsigned long flags;
6291a80ba88SMark Gross 
6301a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6311a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6321a80ba88SMark Gross 
6331a80ba88SMark Gross 	val = (unsigned char)tmp;
6341a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6351a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xfb, val);
6361a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6371a80ba88SMark Gross 
6381a80ba88SMark Gross 	return strnlen(buf, count);
6391a80ba88SMark Gross }
6401a80ba88SMark Gross 
64131cc48bfSMark Bellon static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select);
6421a80ba88SMark Gross 
store_hardware_switching_mode(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6431a80ba88SMark Gross static ssize_t store_hardware_switching_mode(struct device *d,
6441a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6451a80ba88SMark Gross {
6461a80ba88SMark Gross 	unsigned long tmp;
6471a80ba88SMark Gross 	unsigned char val;
6481a80ba88SMark Gross 	unsigned long flags;
6491a80ba88SMark Gross 
6501a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6511a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6521a80ba88SMark Gross 
6531a80ba88SMark Gross 	val = (unsigned char)tmp;
6541a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6551a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xbf, val);
6561a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6571a80ba88SMark Gross 
6581a80ba88SMark Gross 	return strnlen(buf, count);
6591a80ba88SMark Gross }
6601a80ba88SMark Gross 
66131cc48bfSMark Bellon static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL,
6621a80ba88SMark Gross 		store_hardware_switching_mode);
6631a80ba88SMark Gross 
store_hardware_switching(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6641a80ba88SMark Gross static ssize_t store_hardware_switching(struct device *d,
6651a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6661a80ba88SMark Gross {
6671a80ba88SMark Gross 	unsigned long tmp;
6681a80ba88SMark Gross 	unsigned char val;
6691a80ba88SMark Gross 	unsigned long flags;
6701a80ba88SMark Gross 
6711a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6721a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6731a80ba88SMark Gross 
6741a80ba88SMark Gross 	val = (unsigned char)tmp;
6751a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6761a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0x7f, val);
6771a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6781a80ba88SMark Gross 
6791a80ba88SMark Gross 	return strnlen(buf, count);
6801a80ba88SMark Gross }
6811a80ba88SMark Gross 
68231cc48bfSMark Bellon static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL,
6831a80ba88SMark Gross 		store_hardware_switching);
6841a80ba88SMark Gross 
store_refalign(struct device * d,struct device_attribute * attr,const char * buf,size_t count)6851a80ba88SMark Gross static ssize_t store_refalign (struct device *d,
6861a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
6871a80ba88SMark Gross {
6881a80ba88SMark Gross 	unsigned long tmp;
6891a80ba88SMark Gross 	unsigned long flags;
6901a80ba88SMark Gross 
6911a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
6921a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
6931a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
6941a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xf7, 0);
6951a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xf7, 0x08);
6961a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xf7, 0);
6971a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
6981a80ba88SMark Gross 
6991a80ba88SMark Gross 	return strnlen(buf, count);
7001a80ba88SMark Gross }
7011a80ba88SMark Gross 
70231cc48bfSMark Bellon static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign);
7031a80ba88SMark Gross 
store_mode_select(struct device * d,struct device_attribute * attr,const char * buf,size_t count)7041a80ba88SMark Gross static ssize_t store_mode_select (struct device *d,
7051a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
7061a80ba88SMark Gross {
7071a80ba88SMark Gross 	unsigned long tmp;
7081a80ba88SMark Gross 	unsigned char val;
7091a80ba88SMark Gross 	unsigned long flags;
7101a80ba88SMark Gross 
7111a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
7121a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
7131a80ba88SMark Gross 
7141a80ba88SMark Gross 	val = (unsigned char)tmp;
7151a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
7161a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG0, 0xcf, val);
7171a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
7181a80ba88SMark Gross 
7191a80ba88SMark Gross 	return strnlen(buf, count);
7201a80ba88SMark Gross }
7211a80ba88SMark Gross 
72231cc48bfSMark Bellon static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select);
7231a80ba88SMark Gross 
store_reset(struct device * d,struct device_attribute * attr,const char * buf,size_t count)7241a80ba88SMark Gross static ssize_t store_reset (struct device *d,
7251a80ba88SMark Gross 		 struct device_attribute *attr, const char *buf, size_t count)
7261a80ba88SMark Gross {
7271a80ba88SMark Gross 	unsigned long tmp;
7281a80ba88SMark Gross 	unsigned char val;
7291a80ba88SMark Gross 	unsigned long flags;
7301a80ba88SMark Gross 
7311a80ba88SMark Gross 	sscanf(buf, "%lX", &tmp);
7321a80ba88SMark Gross 	dev_dbg(d, "tmp = 0x%lX\n", tmp);
7331a80ba88SMark Gross 
7341a80ba88SMark Gross 	val = (unsigned char)tmp;
7351a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
7361a80ba88SMark Gross 	SET_PORT_BITS(TLCLK_REG4, 0xfd, val);
7371a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
7381a80ba88SMark Gross 
7391a80ba88SMark Gross 	return strnlen(buf, count);
7401a80ba88SMark Gross }
7411a80ba88SMark Gross 
74231cc48bfSMark Bellon static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset);
7431a80ba88SMark Gross 
7441a80ba88SMark Gross static struct attribute *tlclk_sysfs_entries[] = {
7451a80ba88SMark Gross 	&dev_attr_current_ref.attr,
746648bf4fbSmark gross 	&dev_attr_telclock_version.attr,
7471a80ba88SMark Gross 	&dev_attr_alarms.attr,
748648bf4fbSmark gross 	&dev_attr_received_ref_clk3a.attr,
749648bf4fbSmark gross 	&dev_attr_received_ref_clk3b.attr,
7501a80ba88SMark Gross 	&dev_attr_enable_clk3a_output.attr,
7511a80ba88SMark Gross 	&dev_attr_enable_clk3b_output.attr,
7521a80ba88SMark Gross 	&dev_attr_enable_clkb1_output.attr,
7531a80ba88SMark Gross 	&dev_attr_enable_clka1_output.attr,
7541a80ba88SMark Gross 	&dev_attr_enable_clkb0_output.attr,
7551a80ba88SMark Gross 	&dev_attr_enable_clka0_output.attr,
7561a80ba88SMark Gross 	&dev_attr_select_amcb1_transmit_clock.attr,
7571a80ba88SMark Gross 	&dev_attr_select_amcb2_transmit_clock.attr,
7581a80ba88SMark Gross 	&dev_attr_select_redundant_clock.attr,
7591a80ba88SMark Gross 	&dev_attr_select_ref_frequency.attr,
7601a80ba88SMark Gross 	&dev_attr_filter_select.attr,
7611a80ba88SMark Gross 	&dev_attr_hardware_switching_mode.attr,
7621a80ba88SMark Gross 	&dev_attr_hardware_switching.attr,
7631a80ba88SMark Gross 	&dev_attr_refalign.attr,
7641a80ba88SMark Gross 	&dev_attr_mode_select.attr,
7651a80ba88SMark Gross 	&dev_attr_reset.attr,
7661a80ba88SMark Gross 	NULL
7671a80ba88SMark Gross };
7681a80ba88SMark Gross 
7690faef109SArvind Yadav static const struct attribute_group tlclk_attribute_group = {
7701a80ba88SMark Gross 	.name = NULL,		/* put in device directory */
7711a80ba88SMark Gross 	.attrs = tlclk_sysfs_entries,
7721a80ba88SMark Gross };
7731a80ba88SMark Gross 
7741a80ba88SMark Gross static struct platform_device *tlclk_device;
7751a80ba88SMark Gross 
tlclk_init(void)7761a80ba88SMark Gross static int __init tlclk_init(void)
7771a80ba88SMark Gross {
7781a80ba88SMark Gross 	int ret;
7791a80ba88SMark Gross 
780*44b8fb6eSMadhuparna Bhowmik 	telclk_interrupt = (inb(TLCLK_REG7) & 0x0f);
781*44b8fb6eSMadhuparna Bhowmik 
7821a80ba88SMark Gross 	alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL);
783e045907cSJulia Lawall 	if (!alarm_events) {
784e045907cSJulia Lawall 		ret = -ENOMEM;
7851a80ba88SMark Gross 		goto out1;
786e045907cSJulia Lawall 	}
7871a80ba88SMark Gross 
788*44b8fb6eSMadhuparna Bhowmik 	ret = register_chrdev(tlclk_major, "telco_clock", &tlclk_fops);
789*44b8fb6eSMadhuparna Bhowmik 	if (ret < 0) {
790*44b8fb6eSMadhuparna Bhowmik 		printk(KERN_ERR "tlclk: can't get major %d.\n", tlclk_major);
791*44b8fb6eSMadhuparna Bhowmik 		kfree(alarm_events);
792*44b8fb6eSMadhuparna Bhowmik 		return ret;
793*44b8fb6eSMadhuparna Bhowmik 	}
794*44b8fb6eSMadhuparna Bhowmik 	tlclk_major = ret;
795*44b8fb6eSMadhuparna Bhowmik 
7961a80ba88SMark Gross 	/* Read telecom clock IRQ number (Set by BIOS) */
7971a80ba88SMark Gross 	if (!request_region(TLCLK_BASE, 8, "telco_clock")) {
7984ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: request_region 0x%X failed.\n",
7991a80ba88SMark Gross 			TLCLK_BASE);
8001a80ba88SMark Gross 		ret = -EBUSY;
8011a80ba88SMark Gross 		goto out2;
8021a80ba88SMark Gross 	}
8031a80ba88SMark Gross 
8041a80ba88SMark Gross 	if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */
805838d51bfSMasanari Iida 		printk(KERN_ERR "telclk_interrupt = 0x%x non-mcpbl0010 hw.\n",
8061a80ba88SMark Gross 			telclk_interrupt);
8071a80ba88SMark Gross 		ret = -ENXIO;
8081a80ba88SMark Gross 		goto out3;
8091a80ba88SMark Gross 	}
8101a80ba88SMark Gross 
81151487d9eSKees Cook 	timer_setup(&switchover_timer, switchover_timeout, 0);
8121a80ba88SMark Gross 
8131a80ba88SMark Gross 	ret = misc_register(&tlclk_miscdev);
8141a80ba88SMark Gross 	if (ret < 0) {
8154ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: misc_register returns %d.\n", ret);
8161a80ba88SMark Gross 		goto out3;
8171a80ba88SMark Gross 	}
8181a80ba88SMark Gross 
8191a80ba88SMark Gross 	tlclk_device = platform_device_register_simple("telco_clock",
8201a80ba88SMark Gross 				-1, NULL, 0);
8215e66b0b5SAkinobu Mita 	if (IS_ERR(tlclk_device)) {
8224ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: platform_device_register failed.\n");
8235e66b0b5SAkinobu Mita 		ret = PTR_ERR(tlclk_device);
8241a80ba88SMark Gross 		goto out4;
8251a80ba88SMark Gross 	}
8261a80ba88SMark Gross 
8271a80ba88SMark Gross 	ret = sysfs_create_group(&tlclk_device->dev.kobj,
8281a80ba88SMark Gross 			&tlclk_attribute_group);
8291a80ba88SMark Gross 	if (ret) {
8304ab2495aSAlan Cox 		printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n");
8311a80ba88SMark Gross 		goto out5;
8321a80ba88SMark Gross 	}
8331a80ba88SMark Gross 
8341a80ba88SMark Gross 	return 0;
8351a80ba88SMark Gross out5:
8361a80ba88SMark Gross 	platform_device_unregister(tlclk_device);
8371a80ba88SMark Gross out4:
8381a80ba88SMark Gross 	misc_deregister(&tlclk_miscdev);
8391a80ba88SMark Gross out3:
8401a80ba88SMark Gross 	release_region(TLCLK_BASE, 8);
8411a80ba88SMark Gross out2:
8421a80ba88SMark Gross 	kfree(alarm_events);
8431a80ba88SMark Gross 	unregister_chrdev(tlclk_major, "telco_clock");
844*44b8fb6eSMadhuparna Bhowmik out1:
8451a80ba88SMark Gross 	return ret;
8461a80ba88SMark Gross }
8471a80ba88SMark Gross 
tlclk_cleanup(void)8481a80ba88SMark Gross static void __exit tlclk_cleanup(void)
8491a80ba88SMark Gross {
8501a80ba88SMark Gross 	sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group);
8511a80ba88SMark Gross 	platform_device_unregister(tlclk_device);
8521a80ba88SMark Gross 	misc_deregister(&tlclk_miscdev);
8531a80ba88SMark Gross 	unregister_chrdev(tlclk_major, "telco_clock");
8541a80ba88SMark Gross 
8551a80ba88SMark Gross 	release_region(TLCLK_BASE, 8);
8561a80ba88SMark Gross 	del_timer_sync(&switchover_timer);
8571a80ba88SMark Gross 	kfree(alarm_events);
8581a80ba88SMark Gross 
8591a80ba88SMark Gross }
8601a80ba88SMark Gross 
switchover_timeout(struct timer_list * unused)86151487d9eSKees Cook static void switchover_timeout(struct timer_list *unused)
8621a80ba88SMark Gross {
86351487d9eSKees Cook 	unsigned long flags = tlclk_timer_data;
86479603a35SMark Gross 
86579603a35SMark Gross 	if ((flags & 1)) {
86679603a35SMark Gross 		if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08))
8671a80ba88SMark Gross 			alarm_events->switchover_primary++;
8681a80ba88SMark Gross 	} else {
86979603a35SMark Gross 		if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08))
8701a80ba88SMark Gross 			alarm_events->switchover_secondary++;
8711a80ba88SMark Gross 	}
8721a80ba88SMark Gross 
8731a80ba88SMark Gross 	/* Alarm processing is done, wake up read task */
8741a80ba88SMark Gross 	del_timer(&switchover_timer);
8751a80ba88SMark Gross 	got_event = 1;
8761a80ba88SMark Gross 	wake_up(&wq);
8771a80ba88SMark Gross }
8781a80ba88SMark Gross 
tlclk_interrupt(int irq,void * dev_id)8797d12e780SDavid Howells static irqreturn_t tlclk_interrupt(int irq, void *dev_id)
8801a80ba88SMark Gross {
8811a80ba88SMark Gross 	unsigned long flags;
8821a80ba88SMark Gross 
8831a80ba88SMark Gross 	spin_lock_irqsave(&event_lock, flags);
8841a80ba88SMark Gross 	/* Read and clear interrupt events */
8851a80ba88SMark Gross 	int_events = inb(TLCLK_REG6);
8861a80ba88SMark Gross 
8871a80ba88SMark Gross 	/* Primary_Los changed from 0 to 1 ? */
8881a80ba88SMark Gross 	if (int_events & PRI_LOS_01_MASK) {
8891a80ba88SMark Gross 		if (inb(TLCLK_REG2) & SEC_LOST_MASK)
8901a80ba88SMark Gross 			alarm_events->lost_clocks++;
8911a80ba88SMark Gross 		else
8921a80ba88SMark Gross 			alarm_events->lost_primary_clock++;
8931a80ba88SMark Gross 	}
8941a80ba88SMark Gross 
8951a80ba88SMark Gross 	/* Primary_Los changed from 1 to 0 ? */
8961a80ba88SMark Gross 	if (int_events & PRI_LOS_10_MASK) {
8971a80ba88SMark Gross 		alarm_events->primary_clock_back++;
8981a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xFE, 1);
8991a80ba88SMark Gross 	}
9001a80ba88SMark Gross 	/* Secondary_Los changed from 0 to 1 ? */
9011a80ba88SMark Gross 	if (int_events & SEC_LOS_01_MASK) {
9021a80ba88SMark Gross 		if (inb(TLCLK_REG2) & PRI_LOST_MASK)
9031a80ba88SMark Gross 			alarm_events->lost_clocks++;
9041a80ba88SMark Gross 		else
9051a80ba88SMark Gross 			alarm_events->lost_secondary_clock++;
9061a80ba88SMark Gross 	}
9071a80ba88SMark Gross 	/* Secondary_Los changed from 1 to 0 ? */
9081a80ba88SMark Gross 	if (int_events & SEC_LOS_10_MASK) {
9091a80ba88SMark Gross 		alarm_events->secondary_clock_back++;
9101a80ba88SMark Gross 		SET_PORT_BITS(TLCLK_REG1, 0xFE, 0);
9111a80ba88SMark Gross 	}
9121a80ba88SMark Gross 	if (int_events & HOLDOVER_10_MASK)
9131a80ba88SMark Gross 		alarm_events->pll_end_holdover++;
9141a80ba88SMark Gross 
9151a80ba88SMark Gross 	if (int_events & UNLOCK_01_MASK)
9161a80ba88SMark Gross 		alarm_events->pll_lost_sync++;
9171a80ba88SMark Gross 
9181a80ba88SMark Gross 	if (int_events & UNLOCK_10_MASK)
9191a80ba88SMark Gross 		alarm_events->pll_sync++;
9201a80ba88SMark Gross 
9211a80ba88SMark Gross 	/* Holdover changed from 0 to 1 ? */
9221a80ba88SMark Gross 	if (int_events & HOLDOVER_01_MASK) {
9231a80ba88SMark Gross 		alarm_events->pll_holdover++;
9241a80ba88SMark Gross 
9251a80ba88SMark Gross 		/* TIMEOUT in ~10ms */
9261a80ba88SMark Gross 		switchover_timer.expires = jiffies + msecs_to_jiffies(10);
92779603a35SMark Gross 		tlclk_timer_data = inb(TLCLK_REG1);
92879603a35SMark Gross 		mod_timer(&switchover_timer, switchover_timer.expires);
9291a80ba88SMark Gross 	} else {
9301a80ba88SMark Gross 		got_event = 1;
9311a80ba88SMark Gross 		wake_up(&wq);
9321a80ba88SMark Gross 	}
9331a80ba88SMark Gross 	spin_unlock_irqrestore(&event_lock, flags);
9341a80ba88SMark Gross 
9351a80ba88SMark Gross 	return IRQ_HANDLED;
9361a80ba88SMark Gross }
9371a80ba88SMark Gross 
9381a80ba88SMark Gross module_init(tlclk_init);
9391a80ba88SMark Gross module_exit(tlclk_cleanup);
940