162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Telecom Clock driver for Intel NetStructure(tm) MPCBL0010 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005 Kontron Canada 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * All rights reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 962306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1062306a36Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or (at 1162306a36Sopenharmony_ci * your option) any later version. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1462306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1562306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1662306a36Sopenharmony_ci * NON INFRINGEMENT. See the GNU General Public License for more 1762306a36Sopenharmony_ci * details. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 2062306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 2162306a36Sopenharmony_ci * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Send feedback to <sebastien.bouchard@ca.kontron.com> and the current 2462306a36Sopenharmony_ci * Maintainer <mark.gross@intel.com> 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Description : This is the TELECOM CLOCK module driver for the ATCA 2762306a36Sopenharmony_ci * MPCBL0010 ATCA computer. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/init.h> 3262306a36Sopenharmony_ci#include <linux/kernel.h> /* printk() */ 3362306a36Sopenharmony_ci#include <linux/fs.h> /* everything... */ 3462306a36Sopenharmony_ci#include <linux/errno.h> /* error codes */ 3562306a36Sopenharmony_ci#include <linux/sched.h> 3662306a36Sopenharmony_ci#include <linux/slab.h> 3762306a36Sopenharmony_ci#include <linux/ioport.h> 3862306a36Sopenharmony_ci#include <linux/interrupt.h> 3962306a36Sopenharmony_ci#include <linux/spinlock.h> 4062306a36Sopenharmony_ci#include <linux/mutex.h> 4162306a36Sopenharmony_ci#include <linux/timer.h> 4262306a36Sopenharmony_ci#include <linux/sysfs.h> 4362306a36Sopenharmony_ci#include <linux/device.h> 4462306a36Sopenharmony_ci#include <linux/miscdevice.h> 4562306a36Sopenharmony_ci#include <linux/platform_device.h> 4662306a36Sopenharmony_ci#include <asm/io.h> /* inb/outb */ 4762306a36Sopenharmony_ci#include <linux/uaccess.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciMODULE_AUTHOR("Sebastien Bouchard <sebastien.bouchard@ca.kontron.com>"); 5062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/*Hardware Reset of the PLL */ 5362306a36Sopenharmony_ci#define RESET_ON 0x00 5462306a36Sopenharmony_ci#define RESET_OFF 0x01 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* MODE SELECT */ 5762306a36Sopenharmony_ci#define NORMAL_MODE 0x00 5862306a36Sopenharmony_ci#define HOLDOVER_MODE 0x10 5962306a36Sopenharmony_ci#define FREERUN_MODE 0x20 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* FILTER SELECT */ 6262306a36Sopenharmony_ci#define FILTER_6HZ 0x04 6362306a36Sopenharmony_ci#define FILTER_12HZ 0x00 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* SELECT REFERENCE FREQUENCY */ 6662306a36Sopenharmony_ci#define REF_CLK1_8kHz 0x00 6762306a36Sopenharmony_ci#define REF_CLK2_19_44MHz 0x02 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Select primary or secondary redundant clock */ 7062306a36Sopenharmony_ci#define PRIMARY_CLOCK 0x00 7162306a36Sopenharmony_ci#define SECONDARY_CLOCK 0x01 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* CLOCK TRANSMISSION DEFINE */ 7462306a36Sopenharmony_ci#define CLK_8kHz 0xff 7562306a36Sopenharmony_ci#define CLK_16_384MHz 0xfb 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define CLK_1_544MHz 0x00 7862306a36Sopenharmony_ci#define CLK_2_048MHz 0x01 7962306a36Sopenharmony_ci#define CLK_4_096MHz 0x02 8062306a36Sopenharmony_ci#define CLK_6_312MHz 0x03 8162306a36Sopenharmony_ci#define CLK_8_192MHz 0x04 8262306a36Sopenharmony_ci#define CLK_19_440MHz 0x06 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define CLK_8_592MHz 0x08 8562306a36Sopenharmony_ci#define CLK_11_184MHz 0x09 8662306a36Sopenharmony_ci#define CLK_34_368MHz 0x0b 8762306a36Sopenharmony_ci#define CLK_44_736MHz 0x0a 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* RECEIVED REFERENCE */ 9062306a36Sopenharmony_ci#define AMC_B1 0 9162306a36Sopenharmony_ci#define AMC_B2 1 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* HARDWARE SWITCHING DEFINE */ 9462306a36Sopenharmony_ci#define HW_ENABLE 0x80 9562306a36Sopenharmony_ci#define HW_DISABLE 0x00 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* HARDWARE SWITCHING MODE DEFINE */ 9862306a36Sopenharmony_ci#define PLL_HOLDOVER 0x40 9962306a36Sopenharmony_ci#define LOST_CLOCK 0x00 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* ALARMS DEFINE */ 10262306a36Sopenharmony_ci#define UNLOCK_MASK 0x10 10362306a36Sopenharmony_ci#define HOLDOVER_MASK 0x20 10462306a36Sopenharmony_ci#define SEC_LOST_MASK 0x40 10562306a36Sopenharmony_ci#define PRI_LOST_MASK 0x80 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* INTERRUPT CAUSE DEFINE */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define PRI_LOS_01_MASK 0x01 11062306a36Sopenharmony_ci#define PRI_LOS_10_MASK 0x02 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define SEC_LOS_01_MASK 0x04 11362306a36Sopenharmony_ci#define SEC_LOS_10_MASK 0x08 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define HOLDOVER_01_MASK 0x10 11662306a36Sopenharmony_ci#define HOLDOVER_10_MASK 0x20 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define UNLOCK_01_MASK 0x40 11962306a36Sopenharmony_ci#define UNLOCK_10_MASK 0x80 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistruct tlclk_alarms { 12262306a36Sopenharmony_ci __u32 lost_clocks; 12362306a36Sopenharmony_ci __u32 lost_primary_clock; 12462306a36Sopenharmony_ci __u32 lost_secondary_clock; 12562306a36Sopenharmony_ci __u32 primary_clock_back; 12662306a36Sopenharmony_ci __u32 secondary_clock_back; 12762306a36Sopenharmony_ci __u32 switchover_primary; 12862306a36Sopenharmony_ci __u32 switchover_secondary; 12962306a36Sopenharmony_ci __u32 pll_holdover; 13062306a36Sopenharmony_ci __u32 pll_end_holdover; 13162306a36Sopenharmony_ci __u32 pll_lost_sync; 13262306a36Sopenharmony_ci __u32 pll_sync; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci/* Telecom clock I/O register definition */ 13562306a36Sopenharmony_ci#define TLCLK_BASE 0xa08 13662306a36Sopenharmony_ci#define TLCLK_REG0 TLCLK_BASE 13762306a36Sopenharmony_ci#define TLCLK_REG1 (TLCLK_BASE+1) 13862306a36Sopenharmony_ci#define TLCLK_REG2 (TLCLK_BASE+2) 13962306a36Sopenharmony_ci#define TLCLK_REG3 (TLCLK_BASE+3) 14062306a36Sopenharmony_ci#define TLCLK_REG4 (TLCLK_BASE+4) 14162306a36Sopenharmony_ci#define TLCLK_REG5 (TLCLK_BASE+5) 14262306a36Sopenharmony_ci#define TLCLK_REG6 (TLCLK_BASE+6) 14362306a36Sopenharmony_ci#define TLCLK_REG7 (TLCLK_BASE+7) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define SET_PORT_BITS(port, mask, val) outb(((inb(port) & mask) | val), port) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 0 = Dynamic allocation of the major device number */ 14862306a36Sopenharmony_ci#define TLCLK_MAJOR 0 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* sysfs interface definition: 15162306a36Sopenharmony_ciUpon loading the driver will create a sysfs directory under 15262306a36Sopenharmony_ci/sys/devices/platform/telco_clock. 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciThis directory exports the following interfaces. There operation is 15562306a36Sopenharmony_cidocumented in the MCPBL0010 TPS under the Telecom Clock API section, 11.4. 15662306a36Sopenharmony_cialarms : 15762306a36Sopenharmony_cicurrent_ref : 15862306a36Sopenharmony_cireceived_ref_clk3a : 15962306a36Sopenharmony_cireceived_ref_clk3b : 16062306a36Sopenharmony_cienable_clk3a_output : 16162306a36Sopenharmony_cienable_clk3b_output : 16262306a36Sopenharmony_cienable_clka0_output : 16362306a36Sopenharmony_cienable_clka1_output : 16462306a36Sopenharmony_cienable_clkb0_output : 16562306a36Sopenharmony_cienable_clkb1_output : 16662306a36Sopenharmony_cifilter_select : 16762306a36Sopenharmony_cihardware_switching : 16862306a36Sopenharmony_cihardware_switching_mode : 16962306a36Sopenharmony_citelclock_version : 17062306a36Sopenharmony_cimode_select : 17162306a36Sopenharmony_cirefalign : 17262306a36Sopenharmony_cireset : 17362306a36Sopenharmony_ciselect_amcb1_transmit_clock : 17462306a36Sopenharmony_ciselect_amcb2_transmit_clock : 17562306a36Sopenharmony_ciselect_redundant_clock : 17662306a36Sopenharmony_ciselect_ref_frequency : 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciAll sysfs interfaces are integers in hex format, i.e echo 99 > refalign 17962306a36Sopenharmony_cihas the same effect as echo 0x99 > refalign. 18062306a36Sopenharmony_ci*/ 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic unsigned int telclk_interrupt; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int int_events; /* Event that generate a interrupt */ 18562306a36Sopenharmony_cistatic int got_event; /* if events processing have been done */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void switchover_timeout(struct timer_list *t); 18862306a36Sopenharmony_cistatic struct timer_list switchover_timer; 18962306a36Sopenharmony_cistatic unsigned long tlclk_timer_data; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic struct tlclk_alarms *alarm_events; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(event_lock); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int tlclk_major = TLCLK_MAJOR; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic irqreturn_t tlclk_interrupt(int irq, void *dev_id); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(wq); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic unsigned long useflags; 20262306a36Sopenharmony_cistatic DEFINE_MUTEX(tlclk_mutex); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int tlclk_open(struct inode *inode, struct file *filp) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int result; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci mutex_lock(&tlclk_mutex); 20962306a36Sopenharmony_ci if (test_and_set_bit(0, &useflags)) { 21062306a36Sopenharmony_ci result = -EBUSY; 21162306a36Sopenharmony_ci /* this legacy device is always one per system and it doesn't 21262306a36Sopenharmony_ci * know how to handle multiple concurrent clients. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Make sure there is no interrupt pending while 21862306a36Sopenharmony_ci * initialising interrupt handler */ 21962306a36Sopenharmony_ci inb(TLCLK_REG6); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* This device is wired through the FPGA IO space of the ATCA blade 22262306a36Sopenharmony_ci * we can't share this IRQ */ 22362306a36Sopenharmony_ci result = request_irq(telclk_interrupt, &tlclk_interrupt, 22462306a36Sopenharmony_ci 0, "telco_clock", tlclk_interrupt); 22562306a36Sopenharmony_ci if (result == -EBUSY) 22662306a36Sopenharmony_ci printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n"); 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci inb(TLCLK_REG6); /* Clear interrupt events */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciout: 23162306a36Sopenharmony_ci mutex_unlock(&tlclk_mutex); 23262306a36Sopenharmony_ci return result; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int tlclk_release(struct inode *inode, struct file *filp) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci free_irq(telclk_interrupt, tlclk_interrupt); 23862306a36Sopenharmony_ci clear_bit(0, &useflags); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count, 24462306a36Sopenharmony_ci loff_t *f_pos) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (count < sizeof(struct tlclk_alarms)) 24762306a36Sopenharmony_ci return -EIO; 24862306a36Sopenharmony_ci if (mutex_lock_interruptible(&tlclk_mutex)) 24962306a36Sopenharmony_ci return -EINTR; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci wait_event_interruptible(wq, got_event); 25362306a36Sopenharmony_ci if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms))) { 25462306a36Sopenharmony_ci mutex_unlock(&tlclk_mutex); 25562306a36Sopenharmony_ci return -EFAULT; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci memset(alarm_events, 0, sizeof(struct tlclk_alarms)); 25962306a36Sopenharmony_ci got_event = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci mutex_unlock(&tlclk_mutex); 26262306a36Sopenharmony_ci return sizeof(struct tlclk_alarms); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic const struct file_operations tlclk_fops = { 26662306a36Sopenharmony_ci .read = tlclk_read, 26762306a36Sopenharmony_ci .open = tlclk_open, 26862306a36Sopenharmony_ci .release = tlclk_release, 26962306a36Sopenharmony_ci .llseek = noop_llseek, 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic struct miscdevice tlclk_miscdev = { 27462306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 27562306a36Sopenharmony_ci .name = "telco_clock", 27662306a36Sopenharmony_ci .fops = &tlclk_fops, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t show_current_ref(struct device *d, 28062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci unsigned long ret_val; 28362306a36Sopenharmony_ci unsigned long flags; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 28662306a36Sopenharmony_ci ret_val = ((inb(TLCLK_REG1) & 0x08) >> 3); 28762306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return sprintf(buf, "0x%lX\n", ret_val); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic DEVICE_ATTR(current_ref, S_IRUGO, show_current_ref, NULL); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic ssize_t show_telclock_version(struct device *d, 29662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci unsigned long ret_val; 29962306a36Sopenharmony_ci unsigned long flags; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 30262306a36Sopenharmony_ci ret_val = inb(TLCLK_REG5); 30362306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return sprintf(buf, "0x%lX\n", ret_val); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic DEVICE_ATTR(telclock_version, S_IRUGO, 30962306a36Sopenharmony_ci show_telclock_version, NULL); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic ssize_t show_alarms(struct device *d, 31262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci unsigned long ret_val; 31562306a36Sopenharmony_ci unsigned long flags; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 31862306a36Sopenharmony_ci ret_val = (inb(TLCLK_REG2) & 0xf0); 31962306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return sprintf(buf, "0x%lX\n", ret_val); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic ssize_t store_received_ref_clk3a(struct device *d, 32762306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci unsigned long tmp; 33062306a36Sopenharmony_ci unsigned char val; 33162306a36Sopenharmony_ci unsigned long flags; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 33462306a36Sopenharmony_ci dev_dbg(d, ": tmp = 0x%lX\n", tmp); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci val = (unsigned char)tmp; 33762306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 33862306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xef, val); 33962306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return strnlen(buf, count); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL, 34562306a36Sopenharmony_ci store_received_ref_clk3a); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic ssize_t store_received_ref_clk3b(struct device *d, 34962306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci unsigned long tmp; 35262306a36Sopenharmony_ci unsigned char val; 35362306a36Sopenharmony_ci unsigned long flags; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 35662306a36Sopenharmony_ci dev_dbg(d, ": tmp = 0x%lX\n", tmp); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci val = (unsigned char)tmp; 35962306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 36062306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xdf, val << 1); 36162306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return strnlen(buf, count); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL, 36762306a36Sopenharmony_ci store_received_ref_clk3b); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic ssize_t store_enable_clk3b_output(struct device *d, 37162306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci unsigned long tmp; 37462306a36Sopenharmony_ci unsigned char val; 37562306a36Sopenharmony_ci unsigned long flags; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 37862306a36Sopenharmony_ci dev_dbg(d, ": tmp = 0x%lX\n", tmp); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci val = (unsigned char)tmp; 38162306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 38262306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0x7f, val << 7); 38362306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return strnlen(buf, count); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL, 38962306a36Sopenharmony_ci store_enable_clk3b_output); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic ssize_t store_enable_clk3a_output(struct device *d, 39262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci unsigned long flags; 39562306a36Sopenharmony_ci unsigned long tmp; 39662306a36Sopenharmony_ci unsigned char val; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 39962306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci val = (unsigned char)tmp; 40262306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 40362306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xbf, val << 6); 40462306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return strnlen(buf, count); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL, 41062306a36Sopenharmony_ci store_enable_clk3a_output); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic ssize_t store_enable_clkb1_output(struct device *d, 41362306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci unsigned long flags; 41662306a36Sopenharmony_ci unsigned long tmp; 41762306a36Sopenharmony_ci unsigned char val; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 42062306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci val = (unsigned char)tmp; 42362306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 42462306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG2, 0xf7, val << 3); 42562306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return strnlen(buf, count); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL, 43162306a36Sopenharmony_ci store_enable_clkb1_output); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic ssize_t store_enable_clka1_output(struct device *d, 43562306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci unsigned long flags; 43862306a36Sopenharmony_ci unsigned long tmp; 43962306a36Sopenharmony_ci unsigned char val; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 44262306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci val = (unsigned char)tmp; 44562306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 44662306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG2, 0xfb, val << 2); 44762306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return strnlen(buf, count); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL, 45362306a36Sopenharmony_ci store_enable_clka1_output); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic ssize_t store_enable_clkb0_output(struct device *d, 45662306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci unsigned long flags; 45962306a36Sopenharmony_ci unsigned long tmp; 46062306a36Sopenharmony_ci unsigned char val; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 46362306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci val = (unsigned char)tmp; 46662306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 46762306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG2, 0xfd, val << 1); 46862306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return strnlen(buf, count); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic DEVICE_ATTR(enable_clkb0_output, (S_IWUSR|S_IWGRP), NULL, 47462306a36Sopenharmony_ci store_enable_clkb0_output); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic ssize_t store_enable_clka0_output(struct device *d, 47762306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci unsigned long flags; 48062306a36Sopenharmony_ci unsigned long tmp; 48162306a36Sopenharmony_ci unsigned char val; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 48462306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci val = (unsigned char)tmp; 48762306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 48862306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG2, 0xfe, val); 48962306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return strnlen(buf, count); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic DEVICE_ATTR(enable_clka0_output, (S_IWUSR|S_IWGRP), NULL, 49562306a36Sopenharmony_ci store_enable_clka0_output); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic ssize_t store_select_amcb2_transmit_clock(struct device *d, 49862306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci unsigned long flags; 50162306a36Sopenharmony_ci unsigned long tmp; 50262306a36Sopenharmony_ci unsigned char val; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 50562306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci val = (unsigned char)tmp; 50862306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 50962306a36Sopenharmony_ci if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) { 51062306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x28); 51162306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val); 51262306a36Sopenharmony_ci } else if (val >= CLK_8_592MHz) { 51362306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x38); 51462306a36Sopenharmony_ci switch (val) { 51562306a36Sopenharmony_ci case CLK_8_592MHz: 51662306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 2); 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci case CLK_11_184MHz: 51962306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 0); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case CLK_34_368MHz: 52262306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 3); 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci case CLK_44_736MHz: 52562306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 1); 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } else { 52962306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xc7, val << 3); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return strnlen(buf, count); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL, 53762306a36Sopenharmony_ci store_select_amcb2_transmit_clock); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic ssize_t store_select_amcb1_transmit_clock(struct device *d, 54062306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci unsigned long tmp; 54362306a36Sopenharmony_ci unsigned char val; 54462306a36Sopenharmony_ci unsigned long flags; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 54762306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci val = (unsigned char)tmp; 55062306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 55162306a36Sopenharmony_ci if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) { 55262306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x5); 55362306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val); 55462306a36Sopenharmony_ci } else if (val >= CLK_8_592MHz) { 55562306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x7); 55662306a36Sopenharmony_ci switch (val) { 55762306a36Sopenharmony_ci case CLK_8_592MHz: 55862306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 2); 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci case CLK_11_184MHz: 56162306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 0); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci case CLK_34_368MHz: 56462306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 3); 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci case CLK_44_736MHz: 56762306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfc, 1); 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } else { 57162306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG3, 0xf8, val); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return strnlen(buf, count); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL, 57962306a36Sopenharmony_ci store_select_amcb1_transmit_clock); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic ssize_t store_select_redundant_clock(struct device *d, 58262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci unsigned long tmp; 58562306a36Sopenharmony_ci unsigned char val; 58662306a36Sopenharmony_ci unsigned long flags; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 58962306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci val = (unsigned char)tmp; 59262306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 59362306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xfe, val); 59462306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return strnlen(buf, count); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL, 60062306a36Sopenharmony_ci store_select_redundant_clock); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic ssize_t store_select_ref_frequency(struct device *d, 60362306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci unsigned long tmp; 60662306a36Sopenharmony_ci unsigned char val; 60762306a36Sopenharmony_ci unsigned long flags; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 61062306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci val = (unsigned char)tmp; 61362306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 61462306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xfd, val); 61562306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return strnlen(buf, count); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL, 62162306a36Sopenharmony_ci store_select_ref_frequency); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic ssize_t store_filter_select(struct device *d, 62462306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci unsigned long tmp; 62762306a36Sopenharmony_ci unsigned char val; 62862306a36Sopenharmony_ci unsigned long flags; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 63162306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci val = (unsigned char)tmp; 63462306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 63562306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xfb, val); 63662306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return strnlen(buf, count); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic ssize_t store_hardware_switching_mode(struct device *d, 64462306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci unsigned long tmp; 64762306a36Sopenharmony_ci unsigned char val; 64862306a36Sopenharmony_ci unsigned long flags; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 65162306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci val = (unsigned char)tmp; 65462306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 65562306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xbf, val); 65662306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return strnlen(buf, count); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL, 66262306a36Sopenharmony_ci store_hardware_switching_mode); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic ssize_t store_hardware_switching(struct device *d, 66562306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci unsigned long tmp; 66862306a36Sopenharmony_ci unsigned char val; 66962306a36Sopenharmony_ci unsigned long flags; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 67262306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci val = (unsigned char)tmp; 67562306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 67662306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0x7f, val); 67762306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return strnlen(buf, count); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL, 68362306a36Sopenharmony_ci store_hardware_switching); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic ssize_t store_refalign (struct device *d, 68662306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci unsigned long tmp; 68962306a36Sopenharmony_ci unsigned long flags; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 69262306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 69362306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 69462306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xf7, 0); 69562306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xf7, 0x08); 69662306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xf7, 0); 69762306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return strnlen(buf, count); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic ssize_t store_mode_select (struct device *d, 70562306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci unsigned long tmp; 70862306a36Sopenharmony_ci unsigned char val; 70962306a36Sopenharmony_ci unsigned long flags; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 71262306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci val = (unsigned char)tmp; 71562306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 71662306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG0, 0xcf, val); 71762306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return strnlen(buf, count); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic ssize_t store_reset (struct device *d, 72562306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci unsigned long tmp; 72862306a36Sopenharmony_ci unsigned char val; 72962306a36Sopenharmony_ci unsigned long flags; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci sscanf(buf, "%lX", &tmp); 73262306a36Sopenharmony_ci dev_dbg(d, "tmp = 0x%lX\n", tmp); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci val = (unsigned char)tmp; 73562306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 73662306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG4, 0xfd, val); 73762306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return strnlen(buf, count); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic struct attribute *tlclk_sysfs_entries[] = { 74562306a36Sopenharmony_ci &dev_attr_current_ref.attr, 74662306a36Sopenharmony_ci &dev_attr_telclock_version.attr, 74762306a36Sopenharmony_ci &dev_attr_alarms.attr, 74862306a36Sopenharmony_ci &dev_attr_received_ref_clk3a.attr, 74962306a36Sopenharmony_ci &dev_attr_received_ref_clk3b.attr, 75062306a36Sopenharmony_ci &dev_attr_enable_clk3a_output.attr, 75162306a36Sopenharmony_ci &dev_attr_enable_clk3b_output.attr, 75262306a36Sopenharmony_ci &dev_attr_enable_clkb1_output.attr, 75362306a36Sopenharmony_ci &dev_attr_enable_clka1_output.attr, 75462306a36Sopenharmony_ci &dev_attr_enable_clkb0_output.attr, 75562306a36Sopenharmony_ci &dev_attr_enable_clka0_output.attr, 75662306a36Sopenharmony_ci &dev_attr_select_amcb1_transmit_clock.attr, 75762306a36Sopenharmony_ci &dev_attr_select_amcb2_transmit_clock.attr, 75862306a36Sopenharmony_ci &dev_attr_select_redundant_clock.attr, 75962306a36Sopenharmony_ci &dev_attr_select_ref_frequency.attr, 76062306a36Sopenharmony_ci &dev_attr_filter_select.attr, 76162306a36Sopenharmony_ci &dev_attr_hardware_switching_mode.attr, 76262306a36Sopenharmony_ci &dev_attr_hardware_switching.attr, 76362306a36Sopenharmony_ci &dev_attr_refalign.attr, 76462306a36Sopenharmony_ci &dev_attr_mode_select.attr, 76562306a36Sopenharmony_ci &dev_attr_reset.attr, 76662306a36Sopenharmony_ci NULL 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic const struct attribute_group tlclk_attribute_group = { 77062306a36Sopenharmony_ci .name = NULL, /* put in device directory */ 77162306a36Sopenharmony_ci .attrs = tlclk_sysfs_entries, 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic struct platform_device *tlclk_device; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int __init tlclk_init(void) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci int ret; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci telclk_interrupt = (inb(TLCLK_REG7) & 0x0f); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); 78362306a36Sopenharmony_ci if (!alarm_events) { 78462306a36Sopenharmony_ci ret = -ENOMEM; 78562306a36Sopenharmony_ci goto out1; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ret = register_chrdev(tlclk_major, "telco_clock", &tlclk_fops); 78962306a36Sopenharmony_ci if (ret < 0) { 79062306a36Sopenharmony_ci printk(KERN_ERR "tlclk: can't get major %d.\n", tlclk_major); 79162306a36Sopenharmony_ci kfree(alarm_events); 79262306a36Sopenharmony_ci return ret; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci tlclk_major = ret; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Read telecom clock IRQ number (Set by BIOS) */ 79762306a36Sopenharmony_ci if (!request_region(TLCLK_BASE, 8, "telco_clock")) { 79862306a36Sopenharmony_ci printk(KERN_ERR "tlclk: request_region 0x%X failed.\n", 79962306a36Sopenharmony_ci TLCLK_BASE); 80062306a36Sopenharmony_ci ret = -EBUSY; 80162306a36Sopenharmony_ci goto out2; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */ 80562306a36Sopenharmony_ci printk(KERN_ERR "telclk_interrupt = 0x%x non-mcpbl0010 hw.\n", 80662306a36Sopenharmony_ci telclk_interrupt); 80762306a36Sopenharmony_ci ret = -ENXIO; 80862306a36Sopenharmony_ci goto out3; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci timer_setup(&switchover_timer, switchover_timeout, 0); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = misc_register(&tlclk_miscdev); 81462306a36Sopenharmony_ci if (ret < 0) { 81562306a36Sopenharmony_ci printk(KERN_ERR "tlclk: misc_register returns %d.\n", ret); 81662306a36Sopenharmony_ci goto out3; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci tlclk_device = platform_device_register_simple("telco_clock", 82062306a36Sopenharmony_ci -1, NULL, 0); 82162306a36Sopenharmony_ci if (IS_ERR(tlclk_device)) { 82262306a36Sopenharmony_ci printk(KERN_ERR "tlclk: platform_device_register failed.\n"); 82362306a36Sopenharmony_ci ret = PTR_ERR(tlclk_device); 82462306a36Sopenharmony_ci goto out4; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = sysfs_create_group(&tlclk_device->dev.kobj, 82862306a36Sopenharmony_ci &tlclk_attribute_group); 82962306a36Sopenharmony_ci if (ret) { 83062306a36Sopenharmony_ci printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n"); 83162306a36Sopenharmony_ci goto out5; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ciout5: 83662306a36Sopenharmony_ci platform_device_unregister(tlclk_device); 83762306a36Sopenharmony_ciout4: 83862306a36Sopenharmony_ci misc_deregister(&tlclk_miscdev); 83962306a36Sopenharmony_ciout3: 84062306a36Sopenharmony_ci release_region(TLCLK_BASE, 8); 84162306a36Sopenharmony_ciout2: 84262306a36Sopenharmony_ci kfree(alarm_events); 84362306a36Sopenharmony_ci unregister_chrdev(tlclk_major, "telco_clock"); 84462306a36Sopenharmony_ciout1: 84562306a36Sopenharmony_ci return ret; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic void __exit tlclk_cleanup(void) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group); 85162306a36Sopenharmony_ci platform_device_unregister(tlclk_device); 85262306a36Sopenharmony_ci misc_deregister(&tlclk_miscdev); 85362306a36Sopenharmony_ci unregister_chrdev(tlclk_major, "telco_clock"); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci release_region(TLCLK_BASE, 8); 85662306a36Sopenharmony_ci del_timer_sync(&switchover_timer); 85762306a36Sopenharmony_ci kfree(alarm_events); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic void switchover_timeout(struct timer_list *unused) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci unsigned long flags = tlclk_timer_data; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if ((flags & 1)) { 86662306a36Sopenharmony_ci if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08)) 86762306a36Sopenharmony_ci alarm_events->switchover_primary++; 86862306a36Sopenharmony_ci } else { 86962306a36Sopenharmony_ci if ((inb(TLCLK_REG1) & 0x08) != (flags & 0x08)) 87062306a36Sopenharmony_ci alarm_events->switchover_secondary++; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Alarm processing is done, wake up read task */ 87462306a36Sopenharmony_ci del_timer(&switchover_timer); 87562306a36Sopenharmony_ci got_event = 1; 87662306a36Sopenharmony_ci wake_up(&wq); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic irqreturn_t tlclk_interrupt(int irq, void *dev_id) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci unsigned long flags; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci spin_lock_irqsave(&event_lock, flags); 88462306a36Sopenharmony_ci /* Read and clear interrupt events */ 88562306a36Sopenharmony_ci int_events = inb(TLCLK_REG6); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* Primary_Los changed from 0 to 1 ? */ 88862306a36Sopenharmony_ci if (int_events & PRI_LOS_01_MASK) { 88962306a36Sopenharmony_ci if (inb(TLCLK_REG2) & SEC_LOST_MASK) 89062306a36Sopenharmony_ci alarm_events->lost_clocks++; 89162306a36Sopenharmony_ci else 89262306a36Sopenharmony_ci alarm_events->lost_primary_clock++; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Primary_Los changed from 1 to 0 ? */ 89662306a36Sopenharmony_ci if (int_events & PRI_LOS_10_MASK) { 89762306a36Sopenharmony_ci alarm_events->primary_clock_back++; 89862306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xFE, 1); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci /* Secondary_Los changed from 0 to 1 ? */ 90162306a36Sopenharmony_ci if (int_events & SEC_LOS_01_MASK) { 90262306a36Sopenharmony_ci if (inb(TLCLK_REG2) & PRI_LOST_MASK) 90362306a36Sopenharmony_ci alarm_events->lost_clocks++; 90462306a36Sopenharmony_ci else 90562306a36Sopenharmony_ci alarm_events->lost_secondary_clock++; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci /* Secondary_Los changed from 1 to 0 ? */ 90862306a36Sopenharmony_ci if (int_events & SEC_LOS_10_MASK) { 90962306a36Sopenharmony_ci alarm_events->secondary_clock_back++; 91062306a36Sopenharmony_ci SET_PORT_BITS(TLCLK_REG1, 0xFE, 0); 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci if (int_events & HOLDOVER_10_MASK) 91362306a36Sopenharmony_ci alarm_events->pll_end_holdover++; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (int_events & UNLOCK_01_MASK) 91662306a36Sopenharmony_ci alarm_events->pll_lost_sync++; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (int_events & UNLOCK_10_MASK) 91962306a36Sopenharmony_ci alarm_events->pll_sync++; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* Holdover changed from 0 to 1 ? */ 92262306a36Sopenharmony_ci if (int_events & HOLDOVER_01_MASK) { 92362306a36Sopenharmony_ci alarm_events->pll_holdover++; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* TIMEOUT in ~10ms */ 92662306a36Sopenharmony_ci switchover_timer.expires = jiffies + msecs_to_jiffies(10); 92762306a36Sopenharmony_ci tlclk_timer_data = inb(TLCLK_REG1); 92862306a36Sopenharmony_ci mod_timer(&switchover_timer, switchover_timer.expires); 92962306a36Sopenharmony_ci } else { 93062306a36Sopenharmony_ci got_event = 1; 93162306a36Sopenharmony_ci wake_up(&wq); 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci spin_unlock_irqrestore(&event_lock, flags); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return IRQ_HANDLED; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cimodule_init(tlclk_init); 93962306a36Sopenharmony_cimodule_exit(tlclk_cleanup); 940