⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tlclk.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Telecom Clock driver for Intel NetStructure(tm) MPCBL0010 * * Copyright (C) 2005 Kontron Canada * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT.  See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Send feedback to <sebastien.bouchard@ca.kontron.com> and the current * Maintainer  <mark.gross@intel.com> * * Description : This is the TELECOM CLOCK module driver for the ATCA * MPCBL0010 ATCA computer. */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>	/* printk() */#include <linux/fs.h>		/* everything... */#include <linux/errno.h>	/* error codes */#include <linux/slab.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <linux/sysfs.h>#include <linux/device.h>#include <linux/miscdevice.h>#include <linux/platform_device.h>#include <asm/io.h>		/* inb/outb */#include <asm/uaccess.h>MODULE_AUTHOR("Sebastien Bouchard <sebastien.bouchard@ca.kontron.com>");MODULE_LICENSE("GPL");/*Hardware Reset of the PLL */#define RESET_ON	0x00#define RESET_OFF	0x01/* MODE SELECT */#define NORMAL_MODE 	0x00#define HOLDOVER_MODE	0x10#define FREERUN_MODE	0x20/* FILTER SELECT */#define FILTER_6HZ	0x04#define FILTER_12HZ	0x00/* SELECT REFERENCE FREQUENCY */#define REF_CLK1_8kHz		0x00#define REF_CLK2_19_44MHz	0x02/* Select primary or secondary redundant clock */#define PRIMARY_CLOCK	0x00#define SECONDARY_CLOCK	0x01/* CLOCK TRANSMISSION DEFINE */#define CLK_8kHz	0xff#define CLK_16_384MHz	0xfb#define CLK_1_544MHz	0x00#define CLK_2_048MHz	0x01#define CLK_4_096MHz	0x02#define CLK_6_312MHz	0x03#define CLK_8_192MHz	0x04#define CLK_19_440MHz	0x06#define CLK_8_592MHz	0x08#define CLK_11_184MHz	0x09#define CLK_34_368MHz	0x0b#define CLK_44_736MHz	0x0a/* RECEIVED REFERENCE */#define AMC_B1 0#define AMC_B2 1/* HARDWARE SWITCHING DEFINE */#define HW_ENABLE	0x80#define HW_DISABLE	0x00/* HARDWARE SWITCHING MODE DEFINE */#define PLL_HOLDOVER	0x40#define LOST_CLOCK	0x00/* ALARMS DEFINE */#define UNLOCK_MASK	0x10#define HOLDOVER_MASK	0x20#define SEC_LOST_MASK	0x40#define PRI_LOST_MASK	0x80/* INTERRUPT CAUSE DEFINE */#define PRI_LOS_01_MASK		0x01#define PRI_LOS_10_MASK		0x02#define SEC_LOS_01_MASK		0x04#define SEC_LOS_10_MASK		0x08#define HOLDOVER_01_MASK	0x10#define HOLDOVER_10_MASK	0x20#define UNLOCK_01_MASK		0x40#define UNLOCK_10_MASK		0x80struct tlclk_alarms {	__u32 lost_clocks;	__u32 lost_primary_clock;	__u32 lost_secondary_clock;	__u32 primary_clock_back;	__u32 secondary_clock_back;	__u32 switchover_primary;	__u32 switchover_secondary;	__u32 pll_holdover;	__u32 pll_end_holdover;	__u32 pll_lost_sync;	__u32 pll_sync;};/* Telecom clock I/O register definition */#define TLCLK_BASE 0xa08#define TLCLK_REG0 TLCLK_BASE#define TLCLK_REG1 (TLCLK_BASE+1)#define TLCLK_REG2 (TLCLK_BASE+2)#define TLCLK_REG3 (TLCLK_BASE+3)#define TLCLK_REG4 (TLCLK_BASE+4)#define TLCLK_REG5 (TLCLK_BASE+5)#define TLCLK_REG6 (TLCLK_BASE+6)#define TLCLK_REG7 (TLCLK_BASE+7)#define SET_PORT_BITS(port, mask, val) outb(((inb(port) & mask) | val), port)/* 0 = Dynamic allocation of the major device number */#define TLCLK_MAJOR 0/* sysfs interface definition:Upon loading the driver will create a sysfs directory under/sys/devices/platform/telco_clock.This directory exports the following interfaces.  There operation isdocumented in the MCPBL0010 TPS under the Telecom Clock API section, 11.4.alarms				:current_ref			:received_ref_clk3a		:received_ref_clk3b		:enable_clk3a_output		:enable_clk3b_output		:enable_clka0_output		:enable_clka1_output		:enable_clkb0_output		:enable_clkb1_output		:filter_select			:hardware_switching		:hardware_switching_mode		:telclock_version		:mode_select			:refalign			:reset				:select_amcb1_transmit_clock	:select_amcb2_transmit_clock	:select_redundant_clock		:select_ref_frequency		:All sysfs interfaces are integers in hex format, i.e echo 99 > refalignhas the same effect as echo 0x99 > refalign.*/static unsigned int telclk_interrupt;static int int_events;		/* Event that generate a interrupt */static int got_event;		/* if events processing have been done */static void switchover_timeout(unsigned long data);static struct timer_list switchover_timer =	TIMER_INITIALIZER(switchover_timeout , 0, 0);static unsigned long tlclk_timer_data;static struct tlclk_alarms *alarm_events;static DEFINE_SPINLOCK(event_lock);static int tlclk_major = TLCLK_MAJOR;static irqreturn_t tlclk_interrupt(int irq, void *dev_id);static DECLARE_WAIT_QUEUE_HEAD(wq);static unsigned long useflags;static DEFINE_MUTEX(tlclk_mutex);static int tlclk_open(struct inode *inode, struct file *filp){	int result;	if (test_and_set_bit(0, &useflags))		return -EBUSY;		/* this legacy device is always one per system and it doesn't		 * know how to handle multiple concurrent clients.		 */	/* Make sure there is no interrupt pending while	 * initialising interrupt handler */	inb(TLCLK_REG6);	/* This device is wired through the FPGA IO space of the ATCA blade	 * we can't share this IRQ */	result = request_irq(telclk_interrupt, &tlclk_interrupt,			     IRQF_DISABLED, "telco_clock", tlclk_interrupt);	if (result == -EBUSY) {		printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");		return -EBUSY;	}	inb(TLCLK_REG6);	/* Clear interrupt events */	return 0;}static int tlclk_release(struct inode *inode, struct file *filp){	free_irq(telclk_interrupt, tlclk_interrupt);	clear_bit(0, &useflags);	return 0;}static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count,		loff_t *f_pos){	if (count < sizeof(struct tlclk_alarms))		return -EIO;	if (mutex_lock_interruptible(&tlclk_mutex))		return -EINTR;	wait_event_interruptible(wq, got_event);	if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms))) {		mutex_unlock(&tlclk_mutex);		return -EFAULT;	}	memset(alarm_events, 0, sizeof(struct tlclk_alarms));	got_event = 0;	mutex_unlock(&tlclk_mutex);	return  sizeof(struct tlclk_alarms);}static const struct file_operations tlclk_fops = {	.read = tlclk_read,	.open = tlclk_open,	.release = tlclk_release,};static struct miscdevice tlclk_miscdev = {	.minor = MISC_DYNAMIC_MINOR,	.name = "telco_clock",	.fops = &tlclk_fops,};static ssize_t show_current_ref(struct device *d,		struct device_attribute *attr, char *buf){	unsigned long ret_val;	unsigned long flags;	spin_lock_irqsave(&event_lock, flags);	ret_val = ((inb(TLCLK_REG1) & 0x08) >> 3);	spin_unlock_irqrestore(&event_lock, flags);	return sprintf(buf, "0x%lX\n", ret_val);}static DEVICE_ATTR(current_ref, S_IRUGO, show_current_ref, NULL);static ssize_t show_telclock_version(struct device *d,		struct device_attribute *attr, char *buf){	unsigned long ret_val;	unsigned long flags;	spin_lock_irqsave(&event_lock, flags);	ret_val = inb(TLCLK_REG5);	spin_unlock_irqrestore(&event_lock, flags);	return sprintf(buf, "0x%lX\n", ret_val);}static DEVICE_ATTR(telclock_version, S_IRUGO,		show_telclock_version, NULL);static ssize_t show_alarms(struct device *d,		struct device_attribute *attr,  char *buf){	unsigned long ret_val;	unsigned long flags;	spin_lock_irqsave(&event_lock, flags);	ret_val = (inb(TLCLK_REG2) & 0xf0);	spin_unlock_irqrestore(&event_lock, flags);	return sprintf(buf, "0x%lX\n", ret_val);}static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);static ssize_t store_received_ref_clk3a(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long tmp;	unsigned char val;	unsigned long flags;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, ": tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG1, 0xef, val);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,		store_received_ref_clk3a);static ssize_t store_received_ref_clk3b(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long tmp;	unsigned char val;	unsigned long flags;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, ": tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG1, 0xdf, val << 1);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,		store_received_ref_clk3b);static ssize_t store_enable_clk3b_output(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long tmp;	unsigned char val;	unsigned long flags;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, ": tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG3, 0x7f, val << 7);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}static DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL,		store_enable_clk3b_output);static ssize_t store_enable_clk3a_output(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long flags;	unsigned long tmp;	unsigned char val;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, "tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG3, 0xbf, val << 6);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}static DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL,		store_enable_clk3a_output);static ssize_t store_enable_clkb1_output(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long flags;	unsigned long tmp;	unsigned char val;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, "tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG2, 0xf7, val << 3);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}static DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL,		store_enable_clkb1_output);static ssize_t store_enable_clka1_output(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long flags;	unsigned long tmp;	unsigned char val;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, "tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG2, 0xfb, val << 2);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}static DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL,		store_enable_clka1_output);static ssize_t store_enable_clkb0_output(struct device *d,		 struct device_attribute *attr, const char *buf, size_t count){	unsigned long flags;	unsigned long tmp;	unsigned char val;	sscanf(buf, "%lX", &tmp);	dev_dbg(d, "tmp = 0x%lX\n", tmp);	val = (unsigned char)tmp;	spin_lock_irqsave(&event_lock, flags);	SET_PORT_BITS(TLCLK_REG2, 0xfd, val << 1);	spin_unlock_irqrestore(&event_lock, flags);	return strnlen(buf, count);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -