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