blob: f58ad7f6826738846e606aab135161b92cc83dcc [file] [log] [blame]
Mark Gross1a80ba82005-10-30 15:02:55 -08001/*
2 * Telecom Clock driver for Intel NetStructure(tm) MPCBL0010
3 *
4 * Copyright (C) 2005 Kontron Canada
5 *
6 * All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
16 * NON INFRINGEMENT. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 * Send feedback to <sebastien.bouchard@ca.kontron.com> and the current
24 * Maintainer <mark.gross@intel.com>
25 *
26 * Description : This is the TELECOM CLOCK module driver for the ATCA
27 * MPCBL0010 ATCA computer.
28 */
29
30#include <linux/config.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/sched.h>
34#include <linux/kernel.h> /* printk() */
35#include <linux/fs.h> /* everything... */
36#include <linux/errno.h> /* error codes */
Mark Gross1a80ba82005-10-30 15:02:55 -080037#include <linux/slab.h>
38#include <linux/ioport.h>
39#include <linux/interrupt.h>
40#include <linux/spinlock.h>
41#include <linux/timer.h>
42#include <linux/sysfs.h>
43#include <linux/device.h>
44#include <linux/miscdevice.h>
Andrew Mortonce463372005-10-31 23:44:30 -080045#include <linux/platform_device.h>
Mark Gross1a80ba82005-10-30 15:02:55 -080046#include <asm/io.h> /* inb/outb */
47#include <asm/uaccess.h>
48
49MODULE_AUTHOR("Sebastien Bouchard <sebastien.bouchard@ca.kontron.com>");
50MODULE_LICENSE("GPL");
51
52/*Hardware Reset of the PLL */
53#define RESET_ON 0x00
54#define RESET_OFF 0x01
55
56/* MODE SELECT */
57#define NORMAL_MODE 0x00
58#define HOLDOVER_MODE 0x10
59#define FREERUN_MODE 0x20
60
61/* FILTER SELECT */
62#define FILTER_6HZ 0x04
63#define FILTER_12HZ 0x00
64
65/* SELECT REFERENCE FREQUENCY */
66#define REF_CLK1_8kHz 0x00
67#define REF_CLK2_19_44MHz 0x02
68
69/* Select primary or secondary redundant clock */
70#define PRIMARY_CLOCK 0x00
71#define SECONDARY_CLOCK 0x01
72
73/* CLOCK TRANSMISSION DEFINE */
74#define CLK_8kHz 0xff
75#define CLK_16_384MHz 0xfb
76
77#define CLK_1_544MHz 0x00
78#define CLK_2_048MHz 0x01
79#define CLK_4_096MHz 0x02
80#define CLK_6_312MHz 0x03
81#define CLK_8_192MHz 0x04
82#define CLK_19_440MHz 0x06
83
84#define CLK_8_592MHz 0x08
85#define CLK_11_184MHz 0x09
86#define CLK_34_368MHz 0x0b
87#define CLK_44_736MHz 0x0a
88
89/* RECEIVED REFERENCE */
90#define AMC_B1 0
91#define AMC_B2 1
92
93/* HARDWARE SWITCHING DEFINE */
94#define HW_ENABLE 0x80
95#define HW_DISABLE 0x00
96
97/* HARDWARE SWITCHING MODE DEFINE */
98#define PLL_HOLDOVER 0x40
99#define LOST_CLOCK 0x00
100
101/* ALARMS DEFINE */
102#define UNLOCK_MASK 0x10
103#define HOLDOVER_MASK 0x20
104#define SEC_LOST_MASK 0x40
105#define PRI_LOST_MASK 0x80
106
107/* INTERRUPT CAUSE DEFINE */
108
109#define PRI_LOS_01_MASK 0x01
110#define PRI_LOS_10_MASK 0x02
111
112#define SEC_LOS_01_MASK 0x04
113#define SEC_LOS_10_MASK 0x08
114
115#define HOLDOVER_01_MASK 0x10
116#define HOLDOVER_10_MASK 0x20
117
118#define UNLOCK_01_MASK 0x40
119#define UNLOCK_10_MASK 0x80
120
121struct tlclk_alarms {
122 __u32 lost_clocks;
123 __u32 lost_primary_clock;
124 __u32 lost_secondary_clock;
125 __u32 primary_clock_back;
126 __u32 secondary_clock_back;
127 __u32 switchover_primary;
128 __u32 switchover_secondary;
129 __u32 pll_holdover;
130 __u32 pll_end_holdover;
131 __u32 pll_lost_sync;
132 __u32 pll_sync;
133};
134/* Telecom clock I/O register definition */
135#define TLCLK_BASE 0xa08
136#define TLCLK_REG0 TLCLK_BASE
137#define TLCLK_REG1 (TLCLK_BASE+1)
138#define TLCLK_REG2 (TLCLK_BASE+2)
139#define TLCLK_REG3 (TLCLK_BASE+3)
140#define TLCLK_REG4 (TLCLK_BASE+4)
141#define TLCLK_REG5 (TLCLK_BASE+5)
142#define TLCLK_REG6 (TLCLK_BASE+6)
143#define TLCLK_REG7 (TLCLK_BASE+7)
144
145#define SET_PORT_BITS(port, mask, val) outb(((inb(port) & mask) | val), port)
146
147/* 0 = Dynamic allocation of the major device number */
148#define TLCLK_MAJOR 0
149
150/* sysfs interface definition:
151Upon loading the driver will create a sysfs directory under
152/sys/devices/platform/telco_clock.
153
154This directory exports the following interfaces. There operation is
155documented in the MCPBL0010 TPS under the Telecom Clock API section, 11.4.
156alarms :
157current_ref :
mark gross648bf4f2006-01-15 17:37:30 -0800158received_ref_clk3a :
159received_ref_clk3b :
Mark Gross1a80ba82005-10-30 15:02:55 -0800160enable_clk3a_output :
161enable_clk3b_output :
162enable_clka0_output :
163enable_clka1_output :
164enable_clkb0_output :
165enable_clkb1_output :
166filter_select :
167hardware_switching :
168hardware_switching_mode :
mark gross648bf4f2006-01-15 17:37:30 -0800169telclock_version :
Mark Gross1a80ba82005-10-30 15:02:55 -0800170mode_select :
171refalign :
172reset :
173select_amcb1_transmit_clock :
174select_amcb2_transmit_clock :
175select_redundant_clock :
176select_ref_frequency :
Mark Gross1a80ba82005-10-30 15:02:55 -0800177
178All sysfs interfaces are integers in hex format, i.e echo 99 > refalign
179has the same effect as echo 0x99 > refalign.
180*/
181
182static unsigned int telclk_interrupt;
183
184static int int_events; /* Event that generate a interrupt */
185static int got_event; /* if events processing have been done */
186
187static void switchover_timeout(unsigned long data);
188static struct timer_list switchover_timer =
189 TIMER_INITIALIZER(switchover_timeout , 0, 0);
190
191static struct tlclk_alarms *alarm_events;
192
193static DEFINE_SPINLOCK(event_lock);
194
195static int tlclk_major = TLCLK_MAJOR;
196
197static irqreturn_t tlclk_interrupt(int irq, void *dev_id, struct pt_regs *regs);
198
199static DECLARE_WAIT_QUEUE_HEAD(wq);
200
201static int tlclk_open(struct inode *inode, struct file *filp)
202{
203 int result;
204
205 /* Make sure there is no interrupt pending while
206 * initialising interrupt handler */
207 inb(TLCLK_REG6);
208
209 /* This device is wired through the FPGA IO space of the ATCA blade
210 * we can't share this IRQ */
211 result = request_irq(telclk_interrupt, &tlclk_interrupt,
212 SA_INTERRUPT, "telco_clock", tlclk_interrupt);
213 if (result == -EBUSY) {
Alan Cox4ab24952006-01-11 12:17:33 -0800214 printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");
Mark Gross1a80ba82005-10-30 15:02:55 -0800215 return -EBUSY;
216 }
217 inb(TLCLK_REG6); /* Clear interrupt events */
218
219 return 0;
220}
221
222static int tlclk_release(struct inode *inode, struct file *filp)
223{
224 free_irq(telclk_interrupt, tlclk_interrupt);
225
226 return 0;
227}
228
mark gross648bf4f2006-01-15 17:37:30 -0800229static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count,
Mark Gross1a80ba82005-10-30 15:02:55 -0800230 loff_t *f_pos)
231{
232 if (count < sizeof(struct tlclk_alarms))
233 return -EIO;
234
235 wait_event_interruptible(wq, got_event);
236 if (copy_to_user(buf, alarm_events, sizeof(struct tlclk_alarms)))
237 return -EFAULT;
238
239 memset(alarm_events, 0, sizeof(struct tlclk_alarms));
240 got_event = 0;
241
242 return sizeof(struct tlclk_alarms);
243}
244
mark gross648bf4f2006-01-15 17:37:30 -0800245static ssize_t tlclk_write(struct file *filp, const char __user *buf, size_t count,
Mark Gross1a80ba82005-10-30 15:02:55 -0800246 loff_t *f_pos)
247{
248 return 0;
249}
250
251static struct file_operations tlclk_fops = {
252 .read = tlclk_read,
253 .write = tlclk_write,
254 .open = tlclk_open,
255 .release = tlclk_release,
256
257};
258
259static struct miscdevice tlclk_miscdev = {
260 .minor = MISC_DYNAMIC_MINOR,
261 .name = "telco_clock",
262 .fops = &tlclk_fops,
263};
264
265static ssize_t show_current_ref(struct device *d,
266 struct device_attribute *attr, char *buf)
267{
268 unsigned long ret_val;
269 unsigned long flags;
270
271 spin_lock_irqsave(&event_lock, flags);
272 ret_val = ((inb(TLCLK_REG1) & 0x08) >> 3);
273 spin_unlock_irqrestore(&event_lock, flags);
274
275 return sprintf(buf, "0x%lX\n", ret_val);
276}
277
278static DEVICE_ATTR(current_ref, S_IRUGO, show_current_ref, NULL);
279
280
mark gross648bf4f2006-01-15 17:37:30 -0800281static ssize_t show_telclock_version(struct device *d,
Mark Gross1a80ba82005-10-30 15:02:55 -0800282 struct device_attribute *attr, char *buf)
283{
284 unsigned long ret_val;
285 unsigned long flags;
286
287 spin_lock_irqsave(&event_lock, flags);
mark gross648bf4f2006-01-15 17:37:30 -0800288 ret_val = inb(TLCLK_REG5);
Mark Gross1a80ba82005-10-30 15:02:55 -0800289 spin_unlock_irqrestore(&event_lock, flags);
290
291 return sprintf(buf, "0x%lX\n", ret_val);
292}
293
mark gross648bf4f2006-01-15 17:37:30 -0800294static DEVICE_ATTR(telclock_version, S_IRUGO,
295 show_telclock_version, NULL);
Mark Gross1a80ba82005-10-30 15:02:55 -0800296
297static ssize_t show_alarms(struct device *d,
298 struct device_attribute *attr, char *buf)
299{
300 unsigned long ret_val;
301 unsigned long flags;
302
303 spin_lock_irqsave(&event_lock, flags);
304 ret_val = (inb(TLCLK_REG2) & 0xf0);
305 spin_unlock_irqrestore(&event_lock, flags);
306
307 return sprintf(buf, "0x%lX\n", ret_val);
308}
309
310static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
311
mark gross648bf4f2006-01-15 17:37:30 -0800312static ssize_t store_received_ref_clk3a(struct device *d,
313 struct device_attribute *attr, const char *buf, size_t count)
314{
315 unsigned long tmp;
316 unsigned char val;
317 unsigned long flags;
318
319 sscanf(buf, "%lX", &tmp);
320 dev_dbg(d, ": tmp = 0x%lX\n", tmp);
321
322 val = (unsigned char)tmp;
323 spin_lock_irqsave(&event_lock, flags);
324 SET_PORT_BITS(TLCLK_REG1, 0xef, val);
325 spin_unlock_irqrestore(&event_lock, flags);
326
327 return strnlen(buf, count);
328}
329
Mark Bellon31cc48b2006-04-10 22:54:20 -0700330static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL,
mark gross648bf4f2006-01-15 17:37:30 -0800331 store_received_ref_clk3a);
332
333
334static ssize_t store_received_ref_clk3b(struct device *d,
335 struct device_attribute *attr, const char *buf, size_t count)
336{
337 unsigned long tmp;
338 unsigned char val;
339 unsigned long flags;
340
341 sscanf(buf, "%lX", &tmp);
342 dev_dbg(d, ": tmp = 0x%lX\n", tmp);
343
344 val = (unsigned char)tmp;
345 spin_lock_irqsave(&event_lock, flags);
346 SET_PORT_BITS(TLCLK_REG1, 0xef, val << 1);
347 spin_unlock_irqrestore(&event_lock, flags);
348
349 return strnlen(buf, count);
350}
351
Mark Bellon31cc48b2006-04-10 22:54:20 -0700352static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL,
mark gross648bf4f2006-01-15 17:37:30 -0800353 store_received_ref_clk3b);
354
355
Mark Gross1a80ba82005-10-30 15:02:55 -0800356static ssize_t store_enable_clk3b_output(struct device *d,
357 struct device_attribute *attr, const char *buf, size_t count)
358{
359 unsigned long tmp;
360 unsigned char val;
361 unsigned long flags;
362
363 sscanf(buf, "%lX", &tmp);
364 dev_dbg(d, ": tmp = 0x%lX\n", tmp);
365
366 val = (unsigned char)tmp;
367 spin_lock_irqsave(&event_lock, flags);
368 SET_PORT_BITS(TLCLK_REG3, 0x7f, val << 7);
369 spin_unlock_irqrestore(&event_lock, flags);
370
371 return strnlen(buf, count);
372}
373
Mark Bellon31cc48b2006-04-10 22:54:20 -0700374static DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800375 store_enable_clk3b_output);
376
377static ssize_t store_enable_clk3a_output(struct device *d,
378 struct device_attribute *attr, const char *buf, size_t count)
379{
380 unsigned long flags;
381 unsigned long tmp;
382 unsigned char val;
383
384 sscanf(buf, "%lX", &tmp);
385 dev_dbg(d, "tmp = 0x%lX\n", tmp);
386
387 val = (unsigned char)tmp;
388 spin_lock_irqsave(&event_lock, flags);
389 SET_PORT_BITS(TLCLK_REG3, 0xbf, val << 6);
390 spin_unlock_irqrestore(&event_lock, flags);
391
392 return strnlen(buf, count);
393}
394
Mark Bellon31cc48b2006-04-10 22:54:20 -0700395static DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800396 store_enable_clk3a_output);
397
398static ssize_t store_enable_clkb1_output(struct device *d,
399 struct device_attribute *attr, const char *buf, size_t count)
400{
401 unsigned long flags;
402 unsigned long tmp;
403 unsigned char val;
404
405 sscanf(buf, "%lX", &tmp);
406 dev_dbg(d, "tmp = 0x%lX\n", tmp);
407
408 val = (unsigned char)tmp;
409 spin_lock_irqsave(&event_lock, flags);
410 SET_PORT_BITS(TLCLK_REG2, 0xf7, val << 3);
411 spin_unlock_irqrestore(&event_lock, flags);
412
413 return strnlen(buf, count);
414}
415
Mark Bellon31cc48b2006-04-10 22:54:20 -0700416static DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800417 store_enable_clkb1_output);
418
419
420static ssize_t store_enable_clka1_output(struct device *d,
421 struct device_attribute *attr, const char *buf, size_t count)
422{
423 unsigned long flags;
424 unsigned long tmp;
425 unsigned char val;
426
427 sscanf(buf, "%lX", &tmp);
428 dev_dbg(d, "tmp = 0x%lX\n", tmp);
429
430 val = (unsigned char)tmp;
431 spin_lock_irqsave(&event_lock, flags);
432 SET_PORT_BITS(TLCLK_REG2, 0xfb, val << 2);
433 spin_unlock_irqrestore(&event_lock, flags);
434
435 return strnlen(buf, count);
436}
437
Mark Bellon31cc48b2006-04-10 22:54:20 -0700438static DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800439 store_enable_clka1_output);
440
441static ssize_t store_enable_clkb0_output(struct device *d,
442 struct device_attribute *attr, const char *buf, size_t count)
443{
444 unsigned long flags;
445 unsigned long tmp;
446 unsigned char val;
447
448 sscanf(buf, "%lX", &tmp);
449 dev_dbg(d, "tmp = 0x%lX\n", tmp);
450
451 val = (unsigned char)tmp;
452 spin_lock_irqsave(&event_lock, flags);
453 SET_PORT_BITS(TLCLK_REG2, 0xfd, val << 1);
454 spin_unlock_irqrestore(&event_lock, flags);
455
456 return strnlen(buf, count);
457}
458
Mark Bellon31cc48b2006-04-10 22:54:20 -0700459static DEVICE_ATTR(enable_clkb0_output, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800460 store_enable_clkb0_output);
461
462static ssize_t store_enable_clka0_output(struct device *d,
463 struct device_attribute *attr, const char *buf, size_t count)
464{
465 unsigned long flags;
466 unsigned long tmp;
467 unsigned char val;
468
469 sscanf(buf, "%lX", &tmp);
470 dev_dbg(d, "tmp = 0x%lX\n", tmp);
471
472 val = (unsigned char)tmp;
473 spin_lock_irqsave(&event_lock, flags);
474 SET_PORT_BITS(TLCLK_REG2, 0xfe, val);
475 spin_unlock_irqrestore(&event_lock, flags);
476
477 return strnlen(buf, count);
478}
479
Mark Bellon31cc48b2006-04-10 22:54:20 -0700480static DEVICE_ATTR(enable_clka0_output, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800481 store_enable_clka0_output);
482
Mark Gross1a80ba82005-10-30 15:02:55 -0800483static ssize_t store_select_amcb2_transmit_clock(struct device *d,
484 struct device_attribute *attr, const char *buf, size_t count)
485{
486 unsigned long flags;
487 unsigned long tmp;
488 unsigned char val;
489
490 sscanf(buf, "%lX", &tmp);
491 dev_dbg(d, "tmp = 0x%lX\n", tmp);
492
493 val = (unsigned char)tmp;
494 spin_lock_irqsave(&event_lock, flags);
495 if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) {
496 SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x28);
497 SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val);
498 } else if (val >= CLK_8_592MHz) {
499 SET_PORT_BITS(TLCLK_REG3, 0xc7, 0x38);
500 switch (val) {
501 case CLK_8_592MHz:
mark gross648bf4f2006-01-15 17:37:30 -0800502 SET_PORT_BITS(TLCLK_REG0, 0xfc, 2);
Mark Gross1a80ba82005-10-30 15:02:55 -0800503 break;
504 case CLK_11_184MHz:
505 SET_PORT_BITS(TLCLK_REG0, 0xfc, 0);
506 break;
507 case CLK_34_368MHz:
508 SET_PORT_BITS(TLCLK_REG0, 0xfc, 3);
509 break;
510 case CLK_44_736MHz:
mark gross648bf4f2006-01-15 17:37:30 -0800511 SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
Mark Gross1a80ba82005-10-30 15:02:55 -0800512 break;
513 }
514 } else
515 SET_PORT_BITS(TLCLK_REG3, 0xc7, val << 3);
516
517 spin_unlock_irqrestore(&event_lock, flags);
518
519 return strnlen(buf, count);
520}
521
Mark Bellon31cc48b2006-04-10 22:54:20 -0700522static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800523 store_select_amcb2_transmit_clock);
524
525static ssize_t store_select_amcb1_transmit_clock(struct device *d,
526 struct device_attribute *attr, const char *buf, size_t count)
527{
528 unsigned long tmp;
529 unsigned char val;
530 unsigned long flags;
531
532 sscanf(buf, "%lX", &tmp);
533 dev_dbg(d, "tmp = 0x%lX\n", tmp);
534
535 val = (unsigned char)tmp;
536 spin_lock_irqsave(&event_lock, flags);
537 if ((val == CLK_8kHz) || (val == CLK_16_384MHz)) {
538 SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x5);
539 SET_PORT_BITS(TLCLK_REG1, 0xfb, ~val);
540 } else if (val >= CLK_8_592MHz) {
541 SET_PORT_BITS(TLCLK_REG3, 0xf8, 0x7);
542 switch (val) {
543 case CLK_8_592MHz:
544 SET_PORT_BITS(TLCLK_REG0, 0xfc, 1);
545 break;
546 case CLK_11_184MHz:
547 SET_PORT_BITS(TLCLK_REG0, 0xfc, 0);
548 break;
549 case CLK_34_368MHz:
550 SET_PORT_BITS(TLCLK_REG0, 0xfc, 3);
551 break;
552 case CLK_44_736MHz:
553 SET_PORT_BITS(TLCLK_REG0, 0xfc, 2);
554 break;
555 }
556 } else
557 SET_PORT_BITS(TLCLK_REG3, 0xf8, val);
558 spin_unlock_irqrestore(&event_lock, flags);
559
560 return strnlen(buf, count);
561}
562
Mark Bellon31cc48b2006-04-10 22:54:20 -0700563static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800564 store_select_amcb1_transmit_clock);
565
566static ssize_t store_select_redundant_clock(struct device *d,
567 struct device_attribute *attr, const char *buf, size_t count)
568{
569 unsigned long tmp;
570 unsigned char val;
571 unsigned long flags;
572
573 sscanf(buf, "%lX", &tmp);
574 dev_dbg(d, "tmp = 0x%lX\n", tmp);
575
576 val = (unsigned char)tmp;
577 spin_lock_irqsave(&event_lock, flags);
578 SET_PORT_BITS(TLCLK_REG1, 0xfe, val);
579 spin_unlock_irqrestore(&event_lock, flags);
580
581 return strnlen(buf, count);
582}
583
Mark Bellon31cc48b2006-04-10 22:54:20 -0700584static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800585 store_select_redundant_clock);
586
587static ssize_t store_select_ref_frequency(struct device *d,
588 struct device_attribute *attr, const char *buf, size_t count)
589{
590 unsigned long tmp;
591 unsigned char val;
592 unsigned long flags;
593
594 sscanf(buf, "%lX", &tmp);
595 dev_dbg(d, "tmp = 0x%lX\n", tmp);
596
597 val = (unsigned char)tmp;
598 spin_lock_irqsave(&event_lock, flags);
599 SET_PORT_BITS(TLCLK_REG1, 0xfd, val);
600 spin_unlock_irqrestore(&event_lock, flags);
601
602 return strnlen(buf, count);
603}
604
Mark Bellon31cc48b2006-04-10 22:54:20 -0700605static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800606 store_select_ref_frequency);
607
608static ssize_t store_filter_select(struct device *d,
609 struct device_attribute *attr, const char *buf, size_t count)
610{
611 unsigned long tmp;
612 unsigned char val;
613 unsigned long flags;
614
615 sscanf(buf, "%lX", &tmp);
616 dev_dbg(d, "tmp = 0x%lX\n", tmp);
617
618 val = (unsigned char)tmp;
619 spin_lock_irqsave(&event_lock, flags);
620 SET_PORT_BITS(TLCLK_REG0, 0xfb, val);
621 spin_unlock_irqrestore(&event_lock, flags);
622
623 return strnlen(buf, count);
624}
625
Mark Bellon31cc48b2006-04-10 22:54:20 -0700626static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select);
Mark Gross1a80ba82005-10-30 15:02:55 -0800627
628static ssize_t store_hardware_switching_mode(struct device *d,
629 struct device_attribute *attr, const char *buf, size_t count)
630{
631 unsigned long tmp;
632 unsigned char val;
633 unsigned long flags;
634
635 sscanf(buf, "%lX", &tmp);
636 dev_dbg(d, "tmp = 0x%lX\n", tmp);
637
638 val = (unsigned char)tmp;
639 spin_lock_irqsave(&event_lock, flags);
640 SET_PORT_BITS(TLCLK_REG0, 0xbf, val);
641 spin_unlock_irqrestore(&event_lock, flags);
642
643 return strnlen(buf, count);
644}
645
Mark Bellon31cc48b2006-04-10 22:54:20 -0700646static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800647 store_hardware_switching_mode);
648
649static ssize_t store_hardware_switching(struct device *d,
650 struct device_attribute *attr, const char *buf, size_t count)
651{
652 unsigned long tmp;
653 unsigned char val;
654 unsigned long flags;
655
656 sscanf(buf, "%lX", &tmp);
657 dev_dbg(d, "tmp = 0x%lX\n", tmp);
658
659 val = (unsigned char)tmp;
660 spin_lock_irqsave(&event_lock, flags);
661 SET_PORT_BITS(TLCLK_REG0, 0x7f, val);
662 spin_unlock_irqrestore(&event_lock, flags);
663
664 return strnlen(buf, count);
665}
666
Mark Bellon31cc48b2006-04-10 22:54:20 -0700667static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL,
Mark Gross1a80ba82005-10-30 15:02:55 -0800668 store_hardware_switching);
669
670static ssize_t store_refalign (struct device *d,
671 struct device_attribute *attr, const char *buf, size_t count)
672{
673 unsigned long tmp;
674 unsigned long flags;
675
676 sscanf(buf, "%lX", &tmp);
677 dev_dbg(d, "tmp = 0x%lX\n", tmp);
678 spin_lock_irqsave(&event_lock, flags);
679 SET_PORT_BITS(TLCLK_REG0, 0xf7, 0);
Mark Gross1a80ba82005-10-30 15:02:55 -0800680 SET_PORT_BITS(TLCLK_REG0, 0xf7, 0x08);
Mark Gross1a80ba82005-10-30 15:02:55 -0800681 SET_PORT_BITS(TLCLK_REG0, 0xf7, 0);
682 spin_unlock_irqrestore(&event_lock, flags);
683
684 return strnlen(buf, count);
685}
686
Mark Bellon31cc48b2006-04-10 22:54:20 -0700687static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign);
Mark Gross1a80ba82005-10-30 15:02:55 -0800688
689static ssize_t store_mode_select (struct device *d,
690 struct device_attribute *attr, const char *buf, size_t count)
691{
692 unsigned long tmp;
693 unsigned char val;
694 unsigned long flags;
695
696 sscanf(buf, "%lX", &tmp);
697 dev_dbg(d, "tmp = 0x%lX\n", tmp);
698
699 val = (unsigned char)tmp;
700 spin_lock_irqsave(&event_lock, flags);
701 SET_PORT_BITS(TLCLK_REG0, 0xcf, val);
702 spin_unlock_irqrestore(&event_lock, flags);
703
704 return strnlen(buf, count);
705}
706
Mark Bellon31cc48b2006-04-10 22:54:20 -0700707static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select);
Mark Gross1a80ba82005-10-30 15:02:55 -0800708
709static ssize_t store_reset (struct device *d,
710 struct device_attribute *attr, const char *buf, size_t count)
711{
712 unsigned long tmp;
713 unsigned char val;
714 unsigned long flags;
715
716 sscanf(buf, "%lX", &tmp);
717 dev_dbg(d, "tmp = 0x%lX\n", tmp);
718
719 val = (unsigned char)tmp;
720 spin_lock_irqsave(&event_lock, flags);
721 SET_PORT_BITS(TLCLK_REG4, 0xfd, val);
722 spin_unlock_irqrestore(&event_lock, flags);
723
724 return strnlen(buf, count);
725}
726
Mark Bellon31cc48b2006-04-10 22:54:20 -0700727static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset);
Mark Gross1a80ba82005-10-30 15:02:55 -0800728
729static struct attribute *tlclk_sysfs_entries[] = {
730 &dev_attr_current_ref.attr,
mark gross648bf4f2006-01-15 17:37:30 -0800731 &dev_attr_telclock_version.attr,
Mark Gross1a80ba82005-10-30 15:02:55 -0800732 &dev_attr_alarms.attr,
mark gross648bf4f2006-01-15 17:37:30 -0800733 &dev_attr_received_ref_clk3a.attr,
734 &dev_attr_received_ref_clk3b.attr,
Mark Gross1a80ba82005-10-30 15:02:55 -0800735 &dev_attr_enable_clk3a_output.attr,
736 &dev_attr_enable_clk3b_output.attr,
737 &dev_attr_enable_clkb1_output.attr,
738 &dev_attr_enable_clka1_output.attr,
739 &dev_attr_enable_clkb0_output.attr,
740 &dev_attr_enable_clka0_output.attr,
Mark Gross1a80ba82005-10-30 15:02:55 -0800741 &dev_attr_select_amcb1_transmit_clock.attr,
742 &dev_attr_select_amcb2_transmit_clock.attr,
743 &dev_attr_select_redundant_clock.attr,
744 &dev_attr_select_ref_frequency.attr,
745 &dev_attr_filter_select.attr,
746 &dev_attr_hardware_switching_mode.attr,
747 &dev_attr_hardware_switching.attr,
748 &dev_attr_refalign.attr,
749 &dev_attr_mode_select.attr,
750 &dev_attr_reset.attr,
751 NULL
752};
753
754static struct attribute_group tlclk_attribute_group = {
755 .name = NULL, /* put in device directory */
756 .attrs = tlclk_sysfs_entries,
757};
758
759static struct platform_device *tlclk_device;
760
761static int __init tlclk_init(void)
762{
763 int ret;
764
765 ret = register_chrdev(tlclk_major, "telco_clock", &tlclk_fops);
766 if (ret < 0) {
Alan Cox4ab24952006-01-11 12:17:33 -0800767 printk(KERN_ERR "tlclk: can't get major %d.\n", tlclk_major);
Mark Gross1a80ba82005-10-30 15:02:55 -0800768 return ret;
769 }
Andrew Morton222b9f92006-03-26 01:37:31 -0800770 tlclk_major = ret;
Mark Gross1a80ba82005-10-30 15:02:55 -0800771 alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL);
772 if (!alarm_events)
773 goto out1;
774
775 /* Read telecom clock IRQ number (Set by BIOS) */
776 if (!request_region(TLCLK_BASE, 8, "telco_clock")) {
Alan Cox4ab24952006-01-11 12:17:33 -0800777 printk(KERN_ERR "tlclk: request_region 0x%X failed.\n",
Mark Gross1a80ba82005-10-30 15:02:55 -0800778 TLCLK_BASE);
779 ret = -EBUSY;
780 goto out2;
781 }
782 telclk_interrupt = (inb(TLCLK_REG7) & 0x0f);
783
784 if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */
Alan Cox4ab24952006-01-11 12:17:33 -0800785 printk(KERN_ERR "telclk_interrup = 0x%x non-mcpbl0010 hw.\n",
Mark Gross1a80ba82005-10-30 15:02:55 -0800786 telclk_interrupt);
787 ret = -ENXIO;
788 goto out3;
789 }
790
791 init_timer(&switchover_timer);
792
793 ret = misc_register(&tlclk_miscdev);
794 if (ret < 0) {
Alan Cox4ab24952006-01-11 12:17:33 -0800795 printk(KERN_ERR "tlclk: misc_register returns %d.\n", ret);
Mark Gross1a80ba82005-10-30 15:02:55 -0800796 ret = -EBUSY;
797 goto out3;
798 }
799
800 tlclk_device = platform_device_register_simple("telco_clock",
801 -1, NULL, 0);
802 if (!tlclk_device) {
Alan Cox4ab24952006-01-11 12:17:33 -0800803 printk(KERN_ERR "tlclk: platform_device_register failed.\n");
Mark Gross1a80ba82005-10-30 15:02:55 -0800804 ret = -EBUSY;
805 goto out4;
806 }
807
808 ret = sysfs_create_group(&tlclk_device->dev.kobj,
809 &tlclk_attribute_group);
810 if (ret) {
Alan Cox4ab24952006-01-11 12:17:33 -0800811 printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n");
Mark Gross1a80ba82005-10-30 15:02:55 -0800812 sysfs_remove_group(&tlclk_device->dev.kobj,
813 &tlclk_attribute_group);
814 goto out5;
815 }
816
817 return 0;
818out5:
819 platform_device_unregister(tlclk_device);
820out4:
821 misc_deregister(&tlclk_miscdev);
822out3:
823 release_region(TLCLK_BASE, 8);
824out2:
825 kfree(alarm_events);
826out1:
827 unregister_chrdev(tlclk_major, "telco_clock");
828 return ret;
829}
830
831static void __exit tlclk_cleanup(void)
832{
833 sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group);
834 platform_device_unregister(tlclk_device);
835 misc_deregister(&tlclk_miscdev);
836 unregister_chrdev(tlclk_major, "telco_clock");
837
838 release_region(TLCLK_BASE, 8);
839 del_timer_sync(&switchover_timer);
840 kfree(alarm_events);
841
842}
843
844static void switchover_timeout(unsigned long data)
845{
846 if ((data & 1)) {
847 if ((inb(TLCLK_REG1) & 0x08) != (data & 0x08))
848 alarm_events->switchover_primary++;
849 } else {
850 if ((inb(TLCLK_REG1) & 0x08) != (data & 0x08))
851 alarm_events->switchover_secondary++;
852 }
853
854 /* Alarm processing is done, wake up read task */
855 del_timer(&switchover_timer);
856 got_event = 1;
857 wake_up(&wq);
858}
859
860static irqreturn_t tlclk_interrupt(int irq, void *dev_id, struct pt_regs *regs)
861{
862 unsigned long flags;
863
864 spin_lock_irqsave(&event_lock, flags);
865 /* Read and clear interrupt events */
866 int_events = inb(TLCLK_REG6);
867
868 /* Primary_Los changed from 0 to 1 ? */
869 if (int_events & PRI_LOS_01_MASK) {
870 if (inb(TLCLK_REG2) & SEC_LOST_MASK)
871 alarm_events->lost_clocks++;
872 else
873 alarm_events->lost_primary_clock++;
874 }
875
876 /* Primary_Los changed from 1 to 0 ? */
877 if (int_events & PRI_LOS_10_MASK) {
878 alarm_events->primary_clock_back++;
879 SET_PORT_BITS(TLCLK_REG1, 0xFE, 1);
880 }
881 /* Secondary_Los changed from 0 to 1 ? */
882 if (int_events & SEC_LOS_01_MASK) {
883 if (inb(TLCLK_REG2) & PRI_LOST_MASK)
884 alarm_events->lost_clocks++;
885 else
886 alarm_events->lost_secondary_clock++;
887 }
888 /* Secondary_Los changed from 1 to 0 ? */
889 if (int_events & SEC_LOS_10_MASK) {
890 alarm_events->secondary_clock_back++;
891 SET_PORT_BITS(TLCLK_REG1, 0xFE, 0);
892 }
893 if (int_events & HOLDOVER_10_MASK)
894 alarm_events->pll_end_holdover++;
895
896 if (int_events & UNLOCK_01_MASK)
897 alarm_events->pll_lost_sync++;
898
899 if (int_events & UNLOCK_10_MASK)
900 alarm_events->pll_sync++;
901
902 /* Holdover changed from 0 to 1 ? */
903 if (int_events & HOLDOVER_01_MASK) {
904 alarm_events->pll_holdover++;
905
906 /* TIMEOUT in ~10ms */
907 switchover_timer.expires = jiffies + msecs_to_jiffies(10);
908 switchover_timer.data = inb(TLCLK_REG1);
909 add_timer(&switchover_timer);
910 } else {
911 got_event = 1;
912 wake_up(&wq);
913 }
914 spin_unlock_irqrestore(&event_lock, flags);
915
916 return IRQ_HANDLED;
917}
918
919module_init(tlclk_init);
920module_exit(tlclk_cleanup);