* Opps...with ELDK-2.1.0 (ppc_4xx)
@ 2003-04-22 15:59 Johnson, Stephen
2003-04-22 16:58 ` Wolfgang Denk
0 siblings, 1 reply; 11+ messages in thread
From: Johnson, Stephen @ 2003-04-22 15:59 UTC (permalink / raw)
To: linuxppc-embedded
[background]
I've been using the ELDK-2.0.2 with the same kernel (2.4.21) and it has been very stable (uptime of ~4 months). I installed the ELDK-2.1.0 and mounted it (using the same kernel) and I'm observing an "Oops" after several minutes (~10 minutes or so).
I'll be digging through the logs for clues.
----
Oops: Exception in kernel mode, sig: 4
NIP: C000F2DC XER: 00000000 LR: C000F2DC SP: C0170C00 REGS: c0170b50 TRAP: 0700
Not tainted
MSR: 00001030 EE: 0 PR: 0 FP: 0 ME: 1 IR/DR: 11
TASK = c016f070[0] 'swapper' Last syscall: 120
last math 00000000 last altivec 00000000
PLB0: bear= 0x00002e28 acr= 0x9b000000 besr= 0x00000000
GPR00: C000F2DC C0170C00 C016F070 0000001B 00001030 00000001 00000020 C01C0000
GPR08: 00000E4F 00000000 00000000 C0170B20 C0170D98 100B21AC 07FA8300 007FFF9C
GPR16: 00000000 00000001 007FFF00 FFFFFFFF 00001032 C0170F30 00000000 00000006
GPR24: C0170D68 C01DBA3C 00000000 C01C0000 0001B9FF C0170C38 00000000 C0170C00
Call backtrace:
C000F2DC C000F1EC C000FCEC C00DC510 C00DC860 C00DCF74 C00DA7A0
C00DB5CC C00DB720 C00DB110 C00DD664 C00DD7E4 C0004F04 C000376C
C00228C8 C0004D04 C0004D2C C00011D0 C0181598 C0000224
Kernel panic: Aiee, killing interrupt handler!
In interrupt handler - not syncing
<0>Rebooting in 180 seconds..
Stephen B. Johnson
Senior Applications Engineer - Global Accounts
Artesyn Communication Products
8310 Excelsior Drive
Madison, Wisconsin 53717
(800) 356-9602 [work]
(608) 215-6997 [cell]
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Opps...with ELDK-2.1.0 (ppc_4xx)
2003-04-22 15:59 Johnson, Stephen
@ 2003-04-22 16:58 ` Wolfgang Denk
0 siblings, 0 replies; 11+ messages in thread
From: Wolfgang Denk @ 2003-04-22 16:58 UTC (permalink / raw)
To: Johnson, Stephen; +Cc: linuxppc-embedded
In message <F9102D41F595D311ACA7009027DE2C840527B190@c3po.heurikon.com> you wrote:
>
> I've been using the ELDK-2.0.2 with the same kernel (2.4.21) and it has been very stable (uptime of ~4 months). I installed the ELDK-2.1.0 and mounted it (using the same kernel) and I'm observing an "Oops" after several minutes (~10 minutes or so).
Is this really the same kernel source tree, using the same
configuration? The only part of the ELDK which gets involved with
building the kernel is the compiler, and there were no changes in
that area.
> I'll be digging through the logs for clues.
>
> ----
> Oops: Exception in kernel mode, sig: 4
What was the last you did before that? Are you running from root over
NFS, or ramdisk, or what?
> Call backtrace:
> C000F2DC C000F1EC C000FCEC C00DC510 C00DC860 C00DCF74 C00DA7A0
> C00DB5CC C00DB720 C00DB110 C00DD664 C00DD7E4 C0004F04 C000376C
> C00228C8 C0004D04 C0004D2C C00011D0 C0181598 C0000224
Maybe you could try and decode the backtrace?
See Documentation/oops-tracing.txt, or simply pipe the backtrace
output through our backtrace script (ftp://ftp.denx.de/pub/tools/backtrace)
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
I think there's a world market for about five computers.
-- attr. Thomas J. Watson (Chairman of the Board, IBM), 1943
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Opps...with ELDK-2.1.0 (ppc_4xx)
[not found] <F9102D41F595D311ACA7009027DE2C840527B192@c3po.heurikon.com>
@ 2003-04-22 19:16 ` Wolfgang Denk
0 siblings, 0 replies; 11+ messages in thread
From: Wolfgang Denk @ 2003-04-22 19:16 UTC (permalink / raw)
To: Johnson, Stephen; +Cc: linuxppc-embedded
In message <F9102D41F595D311ACA7009027DE2C840527B192@c3po.heurikon.com> you wrote:
> Stopping ntpd seems to have eliminated the "Oops"
>
> I'll still look at the Oops with ksymoops for completeness and then look to see what ntpd is doing to cause it.
I am aware of the NTPD problems. We are working on a fix. This
problem is the reason why there hasn't been an official release of
the new version of the ELDK yet.
I'm sorry...
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
I program, therefore I am.
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: Opps...with ELDK-2.1.0 (ppc_4xx)
@ 2003-04-22 19:38 Johnson, Stephen
2003-04-22 19:50 ` Wolfgang Denk
0 siblings, 1 reply; 11+ messages in thread
From: Johnson, Stephen @ 2003-04-22 19:38 UTC (permalink / raw)
To: Johnson, Stephen, 'Wolfgang Denk'; +Cc: linuxppc-embedded
Here's the results of ksymoops for the ppc_4xx kernel. Hopefully this is consistent with what has been observed.
----------------------------------------------------------------------
ksymoops < oops.txt
ksymoops 2.4.8 on ppc 2.4.21-pmppc440. Options used
-V (default)
-k /proc/ksyms (default)
-l /proc/modules (default)
-o /lib/modules/2.4.21-pmppc440/ (default)
-m /usr/src/linux/System.map (default)
Warning: You did not tell me where to find symbol information. I will
assume that the log matches the kernel and modules that are running
right now and I'll use the default options above for symbol resolution.
If the current kernel and/or modules do not match the log, you can get
more accurate output by telling me the kernel version and where to find
map, modules, ksyms etc. ksymoops -h explains the options.
No modules in ksyms, skipping objects
Warning (read_lsmod): no symbols in lsmod, is /proc/modules a valid lsmod file?
Warning (compare_maps): mismatch on symbol xchg_u32 , ksyms_base says c0009f0c,
System.map says c00059dc. Ignoring ksyms_base entry
NIP: C000F2DC XER: 00000000 LR: C000F2DC SP: C0170C00 REGS: c0170b50 TRAP: 0700
Not tainted
Using defaults from ksymoops -t elf32-powerpc -a powerpc:common
MSR: 00001030 EE: 0 PR: 0 FP: 0 ME: 1 IR/DR: 11
TASK = c016f070[0] 'swapper' Last syscall: 120
last math 00000000 last altivec 00000000
GPR00: C000F2DC C0170C00 C016F070 0000001B 00001030 00000001 00000020 C01C0000
GPR08: 00000E4F 00000000 00000000 C0170B20 C0170D98 100215B0 07FA8300 007FFF9C
GPR16: 00000000 00000001 007FFF00 FFFFFFFF 00001032 C0170F30 00000000 00000009
GPR24: C0170D68 C01DBA3C 00000000 C01C0000 0001D333 C0170C38 00000000 C0170C00
Call backtrace:
C000F2DC C000F1EC C000FCEC C00DC510 C00DC860 C00DCF74 C00DA7A0
C00DB5CC C00DB720 C00DB110 C00DD664 C00DD7E4 C0004F04 C000376C
C00228C8 C0004D04 C0004D2C C00011D0 C0181598 C0000224
Kernel panic: Aiee, killing interrupt handler!
Warning (Oops_read): Code line not seen, dumping what data is available
>>NIP; c000f2dc <schedule+94/57c> <=====
>>GPR0; c000f2dc <schedule+94/57c>
>>GPR1; c0170c00 <init_task_union+1b90/2000>
>>GPR2; c016f070 <init_task_union+0/2000>
>>GPR7; c01c0000 <log_buf+282c/4000>
>>GPR11; c0170b20 <init_task_union+1ab0/2000>
>>GPR12; c0170d98 <init_task_union+1d28/2000>
>>GPR21; c0170f30 <init_task_union+1ec0/2000>
>>GPR24; c0170d68 <init_task_union+1cf8/2000>
>>GPR25; c01dba3c <iic_wait+0/24>
>>GPR27; c01c0000 <log_buf+282c/4000>
>>GPR29; c0170c38 <init_task_union+1bc8/2000>
>>GPR31; c0170c00 <init_task_union+1b90/2000>
Trace; c000f2dc <schedule+94/57c>
Trace; c000f1ec <schedule_timeout+98/cc>
Trace; c000fcec <interruptible_sleep_on_timeout+68/ac>
Trace; c00dc510 <iic_wait_for_irq+88/1b0>
Trace; c00dc860 <iic_sendbytes+228/29c>
Trace; c00dcf74 <iic_xfer+1bc/1d0>
Trace; c00da7a0 <i2c_transfer+8c/e0>
Trace; c00db5cc <i2c_smbus_xfer_emulated+218/298>
Trace; c00db720 <i2c_smbus_xfer+d4/f0>
Trace; c00db110 <i2c_smbus_write_byte_data+34/44>
Trace; c00dd664 <rtc_write+28/58>
Trace; c00dd7e4 <m41t00_set_rtc_time+150/1b0>
Trace; c0004f04 <timer_interrupt+1c4/254>
Trace; c000376c <ret_from_intercept+0/8>
Trace; c00228c8 <check_pgt_cache+20/30>
Trace; c0004d04 <idled+58/70>
Trace; c0004d2c <cpu_idle+10/24>
Trace; c00011d0 <rest_init+30/40>
Trace; c0181598 <start_kernel+168/17c>
Trace; c0000224 <skpinv+1b8/1f0>
4 warnings issued. Results may not be reliable.
-----Original Message-----
From: Johnson, Stephen
Sent: Tuesday, April 22, 2003 2:26 PM
To: 'Wolfgang Denk'
Cc: linuxppc-embedded@lists.linuxppc.org
Subject: RE: Opps...with ELDK-2.1.0 (ppc_4xx)
Thanks for the help. I was happy to see the Oops go away by shutting down ntpd.
The ELDK has been awesome to use... I've recommended it to many of my customers. Keep up the good work and you have a large community of developers out here working and testing it. I'll still be trying 2.1.0 on the different platforms and post anything that I find.
Actually this Oops will be a good training exercise for some of our support engineers. It's not often that they get an Oops to debug.
-----Original Message-----
From: Wolfgang Denk [mailto:wd@denx.de]
Sent: Tuesday, April 22, 2003 2:16 PM
To: Johnson, Stephen
Cc: linuxppc-embedded@lists.linuxppc.org
Subject: Re: Opps...with ELDK-2.1.0 (ppc_4xx)
In message <F9102D41F595D311ACA7009027DE2C840527B192@c3po.heurikon.com> you wrote:
> Stopping ntpd seems to have eliminated the "Oops"
>
> I'll still look at the Oops with ksymoops for completeness and then look to see what ntpd is doing to cause it.
I am aware of the NTPD problems. We are working on a fix. This
problem is the reason why there hasn't been an official release of
the new version of the ELDK yet.
I'm sorry...
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
I program, therefore I am.
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Opps...with ELDK-2.1.0 (ppc_4xx)
2003-04-22 19:38 Johnson, Stephen
@ 2003-04-22 19:50 ` Wolfgang Denk
2003-04-22 20:33 ` Eugene Surovegin
0 siblings, 1 reply; 11+ messages in thread
From: Wolfgang Denk @ 2003-04-22 19:50 UTC (permalink / raw)
To: Johnson, Stephen; +Cc: linuxppc-embedded
Dear Steve,
in message <F9102D41F595D311ACA7009027DE2C840527B194@c3po.heurikon.com> you wrote:
> Here's the results of ksymoops for the ppc_4xx kernel. Hopefully this is consistent with what has been observed.
It is pretty much consisten with what we're seeing here...
> Trace; c000f2dc <schedule+94/57c>
> Trace; c000f1ec <schedule_timeout+98/cc>
> Trace; c000fcec <interruptible_sleep_on_timeout+68/ac>
> Trace; c00dc510 <iic_wait_for_irq+88/1b0>
> Trace; c00dc860 <iic_sendbytes+228/29c>
> Trace; c00dcf74 <iic_xfer+1bc/1d0>
> Trace; c00da7a0 <i2c_transfer+8c/e0>
> Trace; c00db5cc <i2c_smbus_xfer_emulated+218/298>
> Trace; c00db720 <i2c_smbus_xfer+d4/f0>
> Trace; c00db110 <i2c_smbus_write_byte_data+34/44>
> Trace; c00dd664 <rtc_write+28/58>
> Trace; c00dd7e4 <m41t00_set_rtc_time+150/1b0>
> Trace; c0004f04 <timer_interrupt+1c4/254>
> Trace; c000376c <ret_from_intercept+0/8>
> Trace; c00228c8 <check_pgt_cache+20/30>
> Trace; c0004d04 <idled+58/70>
> Trace; c0004d2c <cpu_idle+10/24>
> Trace; c00011d0 <rest_init+30/40>
> Trace; c0181598 <start_kernel+168/17c>
> Trace; c0000224 <skpinv+1b8/1f0>
This means you have a RTC on the I2C bus, right? Same here. It works
fine on all baords except those with a I2C based RTC. Which is why we
detected the problem so late.
Thanks a lot for the feedback!
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
It all seemed, he thought, to be rather a lot of trouble to go to
just sharpen a razor blade. - Terry Pratchett, _The Light Fantastic_
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Opps...with ELDK-2.1.0 (ppc_4xx)
2003-04-22 19:50 ` Wolfgang Denk
@ 2003-04-22 20:33 ` Eugene Surovegin
0 siblings, 0 replies; 11+ messages in thread
From: Eugene Surovegin @ 2003-04-22 20:33 UTC (permalink / raw)
To: Wolfgang Denk; +Cc: Johnson, Stephen, linuxppc-embedded
At 12:50 PM 4/22/2003, Wolfgang Denk wrote:
>It is pretty much consisten with what we're seeing here...
>
> > Trace; c000f2dc <schedule+94/57c>
> > Trace; c000f1ec <schedule_timeout+98/cc>
> > Trace; c000fcec <interruptible_sleep_on_timeout+68/ac>
> > Trace; c00dc510 <iic_wait_for_irq+88/1b0>
> > Trace; c00dc860 <iic_sendbytes+228/29c>
> > Trace; c00dcf74 <iic_xfer+1bc/1d0>
> > Trace; c00da7a0 <i2c_transfer+8c/e0>
> > Trace; c00db5cc <i2c_smbus_xfer_emulated+218/298>
> > Trace; c00db720 <i2c_smbus_xfer+d4/f0>
> > Trace; c00db110 <i2c_smbus_write_byte_data+34/44>
> > Trace; c00dd664 <rtc_write+28/58>
> > Trace; c00dd7e4 <m41t00_set_rtc_time+150/1b0>
> > Trace; c0004f04 <timer_interrupt+1c4/254>
> > Trace; c000376c <ret_from_intercept+0/8>
> > Trace; c00228c8 <check_pgt_cache+20/30>
> > Trace; c0004d04 <idled+58/70>
> > Trace; c0004d2c <cpu_idle+10/24>
> > Trace; c00011d0 <rest_init+30/40>
> > Trace; c0181598 <start_kernel+168/17c>
> > Trace; c0000224 <skpinv+1b8/1f0>
>
>This means you have a RTC on the I2C bus, right? Same here. It works
>fine on all baords except those with a I2C based RTC. Which is why we
>detected the problem so late.
It looks like a bug in m41t00_set_rtc_time.
Generic I2C layer is very high-level subsystem and can not be used from the
interrupt context.
Eugene.
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Opps...with ELDK-2.1.0 (ppc_4xx)
[not found] <F9102D41F595D311ACA7009027DE2C840527B19B@c3po.heurikon.com>
@ 2003-04-23 17:14 ` Wolfgang Denk
0 siblings, 0 replies; 11+ messages in thread
From: Wolfgang Denk @ 2003-04-23 17:14 UTC (permalink / raw)
To: Johnson, Stephen; +Cc: linuxppc-embedded
In message <F9102D41F595D311ACA7009027DE2C840527B19B@c3po.heurikon.com> you wrote:
>
> i2c is somewhat slow...and I looked at the driver to see it using spin_lock() around the write to the rtc. This could potentially be the problem...and it most likely would not be observed with a memory mapped rtc device (because it is much faster than i2c).
Ummm... can you please try using shorter lines? Some 70...80 chars or
so? Thanks.
> I just changed spin_lock() to spin_lock_irq() and I'm currently testing it.
>
> Wolfgang: Is the driver that you're seeing the problem with using spin_lock().
No, it is not. We see the problem for example on the LWMON board,
which uses drivers/char/pcf8563_rtc.c and drivers/i2c/i2c-simple.c.
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
I'm what passes for a Unix guru in my office. This is a frightening
concept. - Lee Ann Goldstein, in <3k55ba$c43@butch.lmsc.lockheed.com>
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: Opps...with ELDK-2.1.0 (ppc_4xx)
@ 2003-04-23 17:18 Montgomery, Tim
2003-04-23 19:18 ` Wolfgang Denk
0 siblings, 1 reply; 11+ messages in thread
From: Montgomery, Tim @ 2003-04-23 17:18 UTC (permalink / raw)
To: 'Eugene Surovegin', Wolfgang Denk
Cc: Johnson, Stephen, linuxppc-embedded
[Wolfgang]
> >
> >This means you have a RTC on the I2C bus, right? Same here. It works
> >fine on all baords except those with a I2C based RTC. Which is why we
> >detected the problem so late.
>
[Eugene]
> It looks like a bug in m41t00_set_rtc_time.
>
> Generic I2C layer is very high-level subsystem and can not be
> used from the interrupt context.
>
If the i2c layer cannot be used in the (e.g. timer) interrupt context, wouldn't that preclude the use of the i2c layer to provide support (i.e. clock updates) to an i2c-based rtc?
Any suggestions on how to work around this?
Wolfgang:
I know you had some examples of i2c-based rtc support in your tree. Assuming the above is the cause behind the oops, does your driver preclude rtc updates from the timer interrupt context?
Some time ago I queried the list on the best way to support an i2c-based RTC; at that time, there was no consensus.
Any more thoughts along these lines?
Regards,
Tim
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: Opps...with ELDK-2.1.0 (ppc_4xx)
[not found] <F9102D41F595D311ACA7009027DE2C8406AB8214@c3po.heurikon.com >
@ 2003-04-23 17:24 ` Eugene Surovegin
0 siblings, 0 replies; 11+ messages in thread
From: Eugene Surovegin @ 2003-04-23 17:24 UTC (permalink / raw)
To: Montgomery, Tim; +Cc: Wolfgang Denk, Johnson, Stephen, linuxppc-embedded
At 10:18 AM 4/23/2003, Montgomery, Tim wrote:
>[Wolfgang]
> > >
> > >This means you have a RTC on the I2C bus, right? Same here. It works
> > >fine on all baords except those with a I2C based RTC. Which is why we
> > >detected the problem so late.
> >
>
>[Eugene]
> > It looks like a bug in m41t00_set_rtc_time.
> >
> > Generic I2C layer is very high-level subsystem and can not be
> > used from the interrupt context.
> >
>
>If the i2c layer cannot be used in the (e.g. timer) interrupt context,
>wouldn't that preclude the use of the i2c layer to provide support (i.e.
>clock updates) to an i2c-based rtc?
Unfortunately, it looks so
>Any suggestions on how to work around this?
We can start kernel thread and call i2c from it (or maybe use one which is
already available in the kernel?) .
<foo>_set_rtc_time will notify this thread when it needs to update RTC from
interrupt.
If you worry about accuracy, we may also remember "jiffies" at the time of
<foo>_set_rtc_time and adjust time accordingly when calling i2c (but I
doubt it's worth it).
Eugene
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: Opps...with ELDK-2.1.0 (ppc_4xx)
@ 2003-04-23 18:05 Oliver Amft
0 siblings, 0 replies; 11+ messages in thread
From: Oliver Amft @ 2003-04-23 18:05 UTC (permalink / raw)
To: Johnson, Stephen; +Cc: Wolfgang Denk, linuxppc-embedded, ebs
[-- Attachment #1: Type: text/plain, Size: 1352 bytes --]
Having missed some of the messages before, I am not sure if i'm correct
but i think this is what you need ;-)
You will have to adapt the includes, e.g. i2c.h
Is there any official tree where this driver is included? (I havn't
found it in ELDK 2.0.2)
Cheers,
Oliver
>
> >[Wolfgang]
> > > >
> > > >This means you have a RTC on the I2C bus, right? Same here. It works
> > > >fine on all baords except those with a I2C based RTC. Which is why we
> > > >detected the problem so late.
> > >
> >
> >[Eugene]
> > > It looks like a bug in m41t00_set_rtc_time.
> > >
> > > Generic I2C layer is very high-level subsystem and can not be
> > > used from the interrupt context.
> > >
> >
> >If the i2c layer cannot be used in the (e.g. timer) interrupt context,
> >wouldn't that preclude the use of the i2c layer to provide support (i.e.
> >clock updates) to an i2c-based rtc?
>
> Unfortunately, it looks so
>
> >Any suggestions on how to work around this?
>
> We can start kernel thread and call i2c from it (or maybe use one which is
> already available in the kernel?) .
> <foo>_set_rtc_time will notify this thread when it needs to update RTC
> from
> interrupt.
>
> If you worry about accuracy, we may also remember "jiffies" at the time of
> <foo>_set_rtc_time and adjust time accordingly when calling i2c (but I
> doubt it's worth it).
>
> Eugene
>
>
>
[-- Attachment #2: m41t00.c --]
[-- Type: text/plain, Size: 24464 bytes --]
/*
* /linux/drivers/i2c/m41t00.c - driver module
*
* Copyright (c) 2002 Christoph Suter fels@datacomm.ch
* Copyright (c) 2002 ABB Switzerland Ltd. All rights reserved.
*
* This code is free software; you can redistribute it and/or
* modify it under the terms of the GNU *Library* General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This code 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. See the GNU
* *Library* General Public License for more details.
*
* You should have received a copy of the GNU *Library* General Public
* License along with this program (see file COPYING.LIB); if not,
* write to the Free Software Foundation, Inc., 675 Mass Ave,
* Cambridge, MA 02139, USA.
*
* $Id$
*
*/
#include <linux/modversions.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/init.h>
#include "../i2c/i2c-2.6.3/kernel/i2c.h" //import our version of i2c.h. UGLY
#include <linux/rtc.h>
#include <linux/proc_fs.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/machdep.h> /* for ppc.md routines */
#include <asm/uaccess.h> /* for copy to / from userspace */
#include <asm/hardirq.h> /* for checking if in interupt (see function set_rtc_time_interrupt */
/* better include own definitions last... (multiple macro definitions) */
#include "m41t00.h"
/*
* Function prototypes
*/
extern void to_tm(int tim, struct rtc_time *tm);
#define M41T00_NAME "m41t00"
#define M41T00_VERSION "1.00"
/*
* Global variables
*/
devfs_handle_t m41t00_handle;
/* number of times /dev/rtc has been opened */
static int rtc_open = 0;
/* year corresponding to 0x00 (epoch). */
static unsigned long epoch = 2000;
int globerr = 0;
/* Number of Days per Month */
static const unsigned char days_in_mo[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/*don't ignore any addresses */
static unsigned short ignore[] = { I2C_CLIENT_END };
/* the clock has always this address */
static unsigned short normal_addr[] = { 0xD0 / 2, I2C_CLIENT_END };
/*Structures */
extern struct machdep_calls ppc_md;
struct clientdata { /* Keep time from set_time call. */
unsigned long time;
} jiq_data;
/* task queue to write time outside the interrupt */
struct tq_struct jiq_task;
static struct i2c_client *this_client;
static struct i2c_client_address_data addr_data = {
normal_i2c:normal_addr,
normal_i2c_range:ignore,
probe:ignore,
probe_range:ignore,
ignore:ignore,
ignore_range:ignore,
force:ignore,
};
static struct i2c_driver m41t00_driver = {
name:"M41T00",
id:I2C_DRIVERID_M41T00,
flags:I2C_DF_NOTIFY,
attach_adapter:m41t00_attach,
detach_client:m41t00_detach,
command:m41t00_command
};
static struct file_operations m41t00_fops = { /* we only need iotl, open and release here */
owner:THIS_MODULE,
llseek:NULL,
read:NULL,
poll:NULL,
ioctl:m41t00_ioctl,
open:m41t00_open,
release:m41t00_release,
fasync:NULL,
};
/* used for device-numbering */
static struct miscdevice m41t00_miscdev = {
RTC_MINOR,
"rtc",
&m41t00_fops
};
/* End of Global Variables */
/* Global functions */
static int
m41t00_detect_client(struct i2c_adapter *adap, int addr,
unsigned short flags, int kind)
/* When probing for an adapter, i2c_probe calls back this function. To be sure
* that a found device is really our rtc, we try to read the time once and do
* some basic check of the returnvalue. We also check if the RTC is stopped.
* If so, we turn it on.
*
* Reading from rtc is performed in two steps: write to rtc to set the
* rtc's internal adresspointer, 2. read from rtc.
*/
{
struct rtc_registers_cs rtc;
unsigned char addrdata[2];
int errsnd, errrcv = 0;
PDEBUG("rtc.o: Examining address %#02x...\n", 2 * addr);
/* Allocate some memory for Client: */
this_client = kmalloc(sizeof(*this_client), GFP_KERNEL);
if (!this_client) {
return -ENOMEM; /*Memory Allocation failed */
}
strcpy(this_client->name, "M41T00");
this_client->id = m41t00_driver.id;
this_client->flags = 0;
this_client->addr = addr;
this_client->adapter = adap;
this_client->driver = &m41t00_driver;
this_client->data = NULL; /* no driver specific data */
PDEBUG("sending request to RTC...\n");
/* Set the adress pointer in the clock */
addrdata[0] = 0; /*Read from adress 0 */
errsnd = i2c_master_send(this_client, addrdata, 1);
/* Read clock */
errrcv = i2c_master_recv(this_client, (char *) &rtc, sizeof(rtc));
if (errsnd + errrcv != sizeof(addrdata[0]) + sizeof(rtc))
goto error;
/* enable century enable bit and disable century bit in rtc: */
addrdata[0] = 2;
addrdata[1] = (rtc.hours | 0x80) & 0xBF;
if (i2c_master_send(this_client, addrdata, sizeof(addrdata)) != sizeof(addrdata)) goto error;
/* clock stopped? */
if (rtc.secs & CTRL_STOP) {
printk(KERN_INFO
"rtc.o: real-time-clock was stopped.\nNow starting...\n");
addrdata[0] = 0; /* store the following data at address 0 */
addrdata[1] = rtc.secs & ~CTRL_STOP;
if (i2c_master_send( /* restart clock */
this_client, addrdata,
sizeof(addrdata)) !=
sizeof(addrdata))
goto error;
}
i2c_attach_client(this_client);
/* modules can be unloaded and our function pointer could point */
/* to anywhere. Assumption: There is only one RTC on the bus... */
# if(defined CONFIG_PPC) && (defined M41T00_PATCH_KERNEL)
if (ppc_md.set_rtc_time == NULL) {
/* init structure for routine outside interrupt */
jiq_task.sync = 0;
jiq_task.routine = set_rtc_time_task;
jiq_task.data = (void *) &jiq_data;
/* register our function */
ppc_md.set_rtc_time = set_rtc_time_interrupt;
PDEBUG("RTC pointer in kernel modified!\n");
} else {
printk(KERN_WARNING
"rtc.o: RTC pointers in kernel already set!\n");
}
# endif(defined CONFIG_PPC) && (defined M41T00_PATCH_KERNEL)
printk(KERN_INFO
"rtc.o: I2C based RTC detected at address %#02x.\n",
2 * addr);
return 0;
error:
globerr = -1; /* Set global error, so that we do not even try to register the device / attach the driver */
PDEBUG("Summed bus transfer: %u. Should be: %u. \n",
errsnd + errrcv, 1 + sizeof(rtc));
kfree(this_client);
this_client = NULL;
printk(KERN_ERR "rtc.o: I2C bus problem or another
device than RTC is responding at this address... \n ");
return -EIO;
}
/*
* Search for our RTC on the bus. Called by i2c_add_driver.
*
* Whenever a new adapter is inserted, or for all adapters if the driver is
* being registered, the callback m41t00_attach is called. Now is the
* time to determine what devices are present on the adapter, and to register
* a client for each of them.
*
* The attach_adapter callback is really easy: we just call the generic
* detection function. This function will scan the bus for us, using the
* information as defined in the list addr_data. If a device is
* detected at a specific address, another callback "m41t00_detect_client" is
* called.
*
* Return value will be ignored...
*/
static int m41t00_attach(struct i2c_adapter *adap)
{
int err;
PDEBUG(KERN_INFO "rtc.o: searching for RTC...\n");
err = i2c_probe(adap, &addr_data, m41t00_detect_client);
if (err) {
printk(KERN_ERR "rtc.o: error while scanning for RTC.\n");
globerr = -1; /* Set global error to one */
}
return err;
}
/*
* Detach the RTC from the adapter
*/
static int m41t00_detach(struct i2c_client *client)
{
int err = 0;
#if (defined CONFIG_PPC) && (defined M41T00_PATCH_KERNEL)
ppc_md.set_rtc_time = NULL; /* no rtc anymore.. */
#endif /* (defined CONFIG_PPC) && (defined M41T00_PATCH_KERNEL) */
err = i2c_detach_client(client);
if (err) {
printk(KERN_ERR
"rtc.o: Client deregistration failed, client not \
detached. \n");
} else
kfree(this_client);
this_client = NULL;
return 0;
}
/*
* Misc commands exported over i2c driver
*/
static int
m41t00_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
switch (cmd) {
case RTC_RD_TIME:
return m41t00_get_datetime(client, arg);
case RTC_SET_TIME:
return m41t00_set_datetime(client, arg);
default: /* May not happen */
return -EINVAL;
}
}
/*
* Commands
*/
static inline int rtc_command(int cmd, void *data)
{
int ret = -EIO;
if (this_client) {
ret = this_client->driver->command(this_client, cmd, data);
}
return ret;
}
/*
* ioctl-routine
*/
static int m41t00_ioctl(struct inode *inode,
struct file *file,
unsigned int cmd, unsigned long arg)
{
struct rtc_time wtime;
int err = 0;
switch (cmd) {
case RTC_RD_TIME:
err = m41t00_get_datetime(this_client, &wtime);
if (err) {
PDEBUG("Error while getting time: %i \n", err);
return err;
}
PDEBUG("Processing time...\n");
/* copy from kernel address space to user address space */
return copy_to_user((void *) arg, &wtime,
sizeof(wtime)) ? -EFAULT : 0;
case RTC_SET_TIME: /* Set the RTC */
if (!capable(CAP_SYS_TIME))
return -EACCES;
/* copy from user address space to kernel address space */
if (copy_from_user(&wtime, (struct rtc_time *) arg,
sizeof(struct rtc_time))) {
return -EFAULT;
}
if (m41t00_set_datetime(this_client, &wtime)) {
printk(KERN_WARNING
"Could not set time in rtc!\n");
return -EFAULT;
}
return 0;
default: /* May not happen */
PDEBUG("No valid flag given to ioctrl!!\n");
return -EINVAL;
}
}
/*
* Read RTC
*/
static int
m41t00_get_datetime(struct i2c_client *client, struct rtc_time *dt)
{
unsigned char addr[1] = { 0 }; /* read from address 0 */
struct rtc_registers_cs rtc;
int err, err1;
err = i2c_master_send(client, addr, 1); /* set adresspointer */
err1 = i2c_master_recv(this_client, (char *) &rtc, sizeof(rtc)); /*read time */
m41t00_print_info(&rtc);
m41t00_rtc_to_tm(&rtc, dt);
PDEBUG("Read: year-value: %u \n",dt->tm_year);
/*
* Account for differences between how the RTC uses the values
* and how they are defined in a struct rtc_time;
*/
if (dt->tm_year >= 0 && dt->tm_year <= 37)
dt->tm_year += 100;
else if (dt->tm_year >=69 && dt->tm_year <=99)
dt->tm_year += 0;
else return -EIO;
PDEBUG ("Year value now: %u\n",dt->tm_year);
dt->tm_mon--;
if ((err != 1) || (err1 != sizeof(rtc))) {
printk(KERN_ERR
"rtc.o: I2C bus problem! (get datetime)\n");
return -EIO;
} else
return 0;
}
/*
* Set date & time
*
*
* note: we must transfer the address and the time in one transaction
* (no repeated slave address or start bit after we wrote the "word address")
*/
static int
m41t00_set_datetime(struct i2c_client *client, struct rtc_time *dt)
{
int ret = 0;
/* buf[0] is reserved for the adress to write to */
u8 buf[1 + sizeof(struct rtc_registers_cs)];
struct rtc_registers_cs *rtc;
buf[0] = 0; /* write to address 0 */
rtc = (struct rtc_registers_cs *) &buf[1];
PDEBUG("Year before checkdate: %u\n",dt->tm_year);
/* check if date makes sense */
/* Here we still need the "long" year, since leapyear checking is done here */
if (checkdate(dt)) {
PDEBUG("Date invalid!");
return -EINVAL;
}
/* now reduce year to two places */
if (dt->tm_year >99) {
PDEBUG("Need to corect year. Year is now: %u\n",dt->tm_year);
dt->tm_year = dt->tm_year % 100;
}
PDEBUG("Year after Checkdate: %u \n",dt->tm_year);
dt->tm_mon++; // month counting start with zero
PDEBUG("Date ok.\n");
/* convert date to BCD */
m41t00_tm_to_rtc(dt, rtc);
PDEBUG("Setting Date:\n");
PDEBUG("rtc_time: %02d:%02d:%02d\n", dt->tm_hour,
dt->tm_min, dt->tm_sec);
PDEBUG("rtc_date: %04d-%02d-%02d\n",
(dt->tm_year + (int) epoch), dt->tm_mon, dt->tm_mday);
PDEBUG("rtc_epoch: %04lu\n", epoch);
#ifdef M41T00_DEBUG
m41t00_print_content(rtc);
#endif
/* set time & date */
/*if (dt->tm_year > 2000) {
PDEBUG("Subtracting 2000 from year!\n");
dt->tm_year -=2000;
}*/
/* # of transfered bytes */
if (i2c_master_send(client, buf, sizeof(buf)) != sizeof(buf)) {
printk(KERN_ERR "rtc.o: I2C bus problem!\n");
ret = -EIO;
}
#ifdef M41T00_DEBUG
m41t00_print_content(rtc);
#endif
return ret;
}
/*
* Check date, returns -1 if date & time makes no sense
*/
static int checkdate(struct rtc_time *dt)
{
unsigned char leap_yr;
/* Ntpd seems to call us with 2002 for the year-value... */
/*if (dt->tm_year > 2000) {
PDEBUG("Year was greater than 2000!\n");
dt->tm_year -= 2000;
}*/
leap_yr = ((!(dt->tm_year % 4) && (dt->tm_year % 100))
|| !(dt->tm_year % 400));
if ((dt->tm_sec < 0) || (dt->tm_sec > 59)) {
PDEBUG("Seconds out of range! Seconds: %u\n", dt->tm_sec);
return (-1);
}
if ((dt->tm_min < 0) || (dt->tm_min > 59)) {
PDEBUG("Minutes out of range! Minutes: %u\n", dt->tm_min);
return (-1);
}
if ((dt->tm_hour < 0) || (dt->tm_hour > 23)) {
PDEBUG("Hours out of range! Hours: %u\n", dt->tm_hour);
return (0);
}
if ((dt->tm_mon < 1) || (dt->tm_mon > 12)) {
PDEBUG("Months out of range! Months: %u\n", dt->tm_mon);
return (0);
}
if (dt->tm_mday < 1) {
PDEBUG("Days out of range! mday: %u\n", dt->tm_mday);
return (-1);
}
if (dt->tm_mday > days_in_mo[dt->tm_mon]) { /* we must have leap year and 29.2. */
if (!leap_yr) { /* no leapyear, wrong date */
PDEBUG("Days out of range! mday: %u\n",
dt->tm_mday);
return (-1);
} else if (!(dt->tm_mon == 2) && (dt->tm_mday == 29)) { /*leap year, must have 29.2 */
PDEBUG("Days out of range! mday: %u\n",
dt->tm_mday);
return (-1);
}
}
if ((dt->tm_year < 0) || (dt->tm_year > 199)) {
PDEBUG("Years out of range! Years: %u\n", dt->tm_year);
return (-1);
}
/* No checking for weekdays done here, because we may not have
* got a valid parameter for them */
PDEBUG("Checkdate passed!\n");
return 0;
}
/*
* Open..
*/
static int m41t00_open(struct inode *minode, struct file *mfile)
{
if (rtc_open) { /* Allow only one opening at a time */
printk(KERN_WARNING "Device already opened!\n");
return -EBUSY;
}
rtc_open++;
return 0;
}
/*
* Release
*/
static int m41t00_release(struct inode *minode, struct file *mfile)
{
rtc_open--;
return 0;
}
/*
* Info exported via "/proc/rtc".
*/
static int m41t00_proc_output(char *buf)
{
int len = 0, err, err1;
struct rtc_time tm;
unsigned char addr[1] = { 0 }; /* read from address 0 */
struct rtc_registers_cs rtc;
err = i2c_master_send(this_client, addr, 1);
err1 = i2c_master_recv(this_client, (char *) &rtc, sizeof(rtc));
if ((err == 1) && (err1 == sizeof(rtc))) {
PDEBUG("Time successfully read!\n");
len += sprintf(buf + len,
"Stop Bit\t\t: %d\n"
"Century Enable Bit\t: %d\n"
"Century Bit\t\t: %d\n"
"Output Level\t\t: %d\n"
"Frequency Test Bit\t: %d\n"
"Sign Bit\t\t: %d\n"
"Calibration\t\t: %d\n",
(rtc.secs & 0x80) ? 1 : 0,
(rtc.hours & 0x80) ? 1 : 0,
(rtc.hours & 0x40) ? 1 : 0,
(rtc.cs & 0x80) ? 1 : 0,
(rtc.cs & 0x40) ? 1 : 0,
(rtc.cs & 0x20) ? 1 : 0, (rtc.cs & 0x1f));
/* convert date */
m41t00_rtc_to_tm((struct rtc_registers_cs *) &rtc, &tm);
/* Make output deal with years the same way hwclock does: */
if (tm.tm_year >= 0 && tm.tm_year <= 37)
tm.tm_year += 2000;
else if (tm.tm_year >=69 && tm.tm_year <=99)
tm.tm_year += 1900;
/*
* There is no way to tell if the user has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
len += sprintf(buf + len,
"rtc_time\t\t: %02d:%02d:%02d\n"
"rtc_date\t\t: %04d-%02d-%02d\n"
"rtc_epoch\t\t: %04lu\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year, tm.tm_mon,
tm.tm_mday, epoch);
} else {
len += sprintf(buf + len, "Error reading date!\n");
printk(KERN_ERR "rtc.o: I2C bus problem!\n");
}
return len;
}
/**
* helper function for proc output.
*
* page: buffer to print
* start: start page (NULL)
* off: offset
* count: maximal length
*/
static int m41t00_read_proc(char *page, char **start,
off_t off, int count, int *eof, void *data)
{
int len = m41t00_proc_output(page); /* m41t00_proc_output may not */
/*generate more than 1 page of data! */
if (len <= off + count)
*eof = 1; /* 1 = this is last output */
*start = page + off;
len -= off;
if (len > count)
len = count;
if (len < 0)
len = 0;
return len;
}
/**
* Prints time during init() call
*/
static void m41t00_print_info(struct rtc_registers_cs *rtc)
{
struct rtc_time tm;
/* convert date */
m41t00_rtc_to_tm((struct rtc_registers_cs *) rtc, &tm);
/* Make output deal with years the same way hwclock does: */
if (tm.tm_year >= 0 && tm.tm_year <= 37)
tm.tm_year += 2000;
else if (tm.tm_year >=69 && tm.tm_year <=99)
tm.tm_year += 1900;
/*
* There is no way to tell if the user has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
printk(KERN_INFO
"rtc.o: rtc_time: %02d:%02d:%02d\n"
"rtc.o: rtc_date: %04d-%02d-%02d\n"
"rtc.o: rtc_epoch: %04lu\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year, tm.tm_mon, tm.tm_mday, epoch);
}
/*
* prints rtc content via printk
* used for debugging only
*/
#ifdef M41T00_DEBUG
static void m41t00_print_content(struct rtc_registers_cs
*rtc)
{
struct rtc_time tm;
printk(KERN_DEBUG "rtc content:\n");
printk(KERN_DEBUG "Stop Bit\t\t: %d\n"
"Century Enable Bit\t: %d\n"
"Century Bit\t\t: %d\n" "Output Level\t\t: %d\n"
"Frequency Test Bit\t: %d\n" "Sign Bit\t\t: %d\n"
"Calibration\t\t: %d\n",
(rtc->secs & 0x80) ? 1 : 0,
(rtc->hours & 0x80) ? 1 : 0,
(rtc->hours & 0x40) ? 1 : 0,
(rtc->cs & 0x80) ? 1 : 0,
(rtc->cs & 0x40) ? 1 : 0,
(rtc->cs & 0x20) ? 1 : 0, (rtc->cs & 0x1f));
/* convert date */
m41t00_rtc_to_tm((struct rtc_registers_cs *) rtc, &tm);
/* Make output deal with years the same way hwclock does: */
if (tm.tm_year >= 0 && tm.tm_year <= 37)
tm.tm_year += 2000;
else if (tm.tm_year >=69 && tm.tm_year <=99)
tm.tm_year += 1900;
/*
* There is no way to tell if the user has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
printk(KERN_DEBUG
"rtc_time\t\t: %02d:%02d:%02d\n"
"rtc_date\t\t: %04d-%02d-%02d\n"
"rtc_epoch\t\t: %04lu\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year, tm.tm_mon, tm.tm_mday, epoch);
}
#endif /* M41T00_DEBUG */
/*
* Converts date and time from BCD to DEC
*/
static void m41t00_rtc_to_tm(struct rtc_registers_cs
*rtc, struct rtc_time *dt)
{
dt->tm_sec = BCD_TO_BIN(rtc->secs & 0x7f);
dt->tm_min = BCD_TO_BIN(rtc->mins & 0x7f);
dt->tm_hour = BCD_TO_BIN(rtc->hours & 0x3f);
dt->tm_wday = BCD_TO_BIN(rtc->wday & 0x07);
dt->tm_mday = BCD_TO_BIN(rtc->mday & 0x3f);
dt->tm_mon = BCD_TO_BIN(rtc->mon & 0x1f);
dt->tm_year = BCD_TO_BIN(rtc->year & 0xff);
dt->tm_year += (rtc->hours & 0x40) ? 100 : 0;
}
/*
* Converts date and time from DEC to BCD
*/
static void m41t00_tm_to_rtc(struct rtc_time *dt,
struct rtc_registers_cs *rtc)
{
PDEBUG("Year: %d (should be 0-200)\n", dt->tm_year);
rtc->secs = BIN_TO_BCD(dt->tm_sec);
rtc->mins = BIN_TO_BCD(dt->tm_min);
rtc->hours = BIN_TO_BCD(dt->tm_hour);
rtc->hours |= 0x80; /* Set Century enable Bit */
PDEBUG("tm_to_rtc: year: %u\n",dt->tm_year);
rtc->year = BIN_TO_BCD(dt->tm_year);
rtc->mday = BIN_TO_BCD(dt->tm_mday);
rtc->mon = BIN_TO_BCD(dt->tm_mon);
rtc->wday = BIN_TO_BCD(dt->tm_wday);
}
/*
* Driver entry. Register our driver at the I2C-core driver.
* If successful register at the kernel and install the proc read
* routine.
*/
static int __init m41t00_init(void)
{
int err;
err = i2c_add_driver(&m41t00_driver);
if (err || globerr) {
printk(KERN_ERR
"rtc.o: Register I2C driver failed, errno is % d \n ",
err);
return err;
}
err = 0;
m41t00_handle = /* Register device for devfs, fails silently if devfs not supported */
devfs_register(NULL, "rtc", DEVFS_FL_AUTO_DEVNUM,
0, 0, S_IFCHR | S_IRUGO | S_IWGRP,
&m41t00_fops, NULL);
if (m41t00_handle == NULL) { /* we have no devfs, use dev */
err = misc_register(&m41t00_miscdev);
if (!err) { /* registering /dev/rtc ok */
printk(KERN_INFO
"/dev/rtc registered as chardevice major 10, min %u.\n",
RTC_MINOR);
} else { /* no devfs and /dev/rtc register failed: abort */
printk(KERN_ERR
"rtc.o: Could not register device rtc, errno is %u(%d)\n",
err, err);
err = i2c_del_driver(&m41t00_driver);
if (err) { /*unregistering driver failed */
printk(KERN_ERR
"rtc.o: Unregister I2C driver failed, errno \
is % d \n ", err);
}
return err;
}
} else { /*devfs ok , continue */
PDEBUG(KERN_INFO "Chardevice rtc successfully registered.\n");
}
if (!create_proc_read_entry("rtc", 0, NULL, m41t00_read_proc, NULL)) { /* proc support */
printk(KERN_ERR "rtc.o: can't create /proc/rtc\n");
}
/* we have no proc, but we still can access the rtc */
printk(KERN_INFO
"rtc.o: I2C based RTC driver version %s initialized.\n",
M41T00_VERSION);
return 0;
}
/*
* Driver exit: unregister device and driver
*/
static void __exit m41t00_exit(void)
{
remove_proc_entry("rtc", NULL);
devfs_unregister(m41t00_handle);
printk(KERN_INFO "rtc.o: Device unregistered.\n");
if (misc_deregister(&m41t00_miscdev))
printk(KERN_ERR
"rtc.o: Unregistering chardevice failed!\n");
else
printk(KERN_INFO "rtc.o: Chardevice unregistered.\n");
if (i2c_del_driver(&m41t00_driver)) {
printk(KERN_ERR "rtc.o: Unregister I2C driver failed, \
module not removed. \n ");
} else {
printk(KERN_INFO "RTC driver successfully unloaded!\n");
}
}
EXPORT_NO_SYMBOLS;
/*
* If modules is NOT defined when this file is compiled, then the MODULE_*
* macros will resolve to nothing
*/
MODULE_AUTHOR("Christoph Suter <fels@datacomm.ch>");
MODULE_DESCRIPTION("M41T00 Real Time Clock Driver for I2C-Bus");
MODULE_SUPPORTED_DEVICE("rtc");
MODULE_LICENSE("GPL");
/*
* Description: Called when module is loaded or when kernel is intialized.
* If MODULES is defined when this file is compiled, then this function will
* resolve to init_module (the function called when insmod is invoked for a
* module). Otherwise, this function is called early in the boot, when the
* kernel is intialized. Check out /include/init.h to see how this works.
*/
module_init(m41t00_init);
/*
* Description: Resolves to module_cleanup when MODULES is defined.
*/
module_exit(m41t00_exit);
#if (defined CONFIG_PPC) && (defined M41T00_PATCH_KERNEL)
/*
* Set the RTC time.
*
* A Pointer to this function will be set in the machine dependent code.
*So, if we are synching our system time with an extrnal clock, the
* kernel will call this routine every 11 minutes to sync the rtc to the systime
*
* Important: This routine is called in interrupt mode!
* Since the I2C driver also uses interrupts we *must*
* set the RTC after the interrupt has been completed.
* Otherwise we would wait for an interrupt to occur when
* we are in another interrupt.
*/
static int set_rtc_time_interrupt(unsigned
long nowtime)
{
jiq_data.time = nowtime; // save time for delayed set
if (!schedule_task(&jiq_task)) { /* ready to run */
printk(KERN_WARNING
"rtc.o: set_rtc_time calls to close!\n");
}
/* I'm not sure about this... */
PDEBUG("really in interrupt: %s", in_interrupt()? "yes" : "no");
return 0;
}
/*
* Set the RTC time outside the interrupt.
*/
static void set_rtc_time_task(void
*p)
{
struct rtc_time tm;
struct clientdata *data = (struct clientdata *) p;
PDEBUG("Task running\n");
to_tm(data->time, &tm);
tm.tm_mon--; /* for compliance with hwclock */
//tm.tm_hour++; /* compensate kernel bug */
if (tm.tm_year > 2000) {
PDEBUG("Adjusting year!\n");
tm.tm_year -= 2000;
}
rtc_command(RTC_SET_TIME, &tm);
}
#endif /* (defined CONFIG_PPC) && (defined M41T00_PATCH_KERNEL) */
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/
[-- Attachment #3: m41t00.h --]
[-- Type: text/plain, Size: 3383 bytes --]
/*
* linux/include/linux/m41t00.h
*
* Copyright (C) 2002 ABB
*
* Author: Christoph Suter
* fels@datacomm.ch
*
* Based on the work of
* Gérard Basler
* gbasler@ee.ethz.ch
*
* Based on m41t11 driver by George Davis
*
* Copyright (C) 2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define EXPORT_SYMTAB
#define M41T00_DEBUG // turn debug messages on / off
#define M41T00_PATCH_KERNEL
#include <linux/devfs_fs_kernel.h>
#define M41T00_DEBUG
#undef PDEBUG
#if defined M41T00_DEBUG
# define PDEBUG(fmt, args...) printk(KERN_DEBUG "%s, %s [%d]: " fmt, __FILE__, __FUNCTION__ , __LINE__ , ## args)
#else
# define PDEBUG(fmt, args...)
#endif
/* internal definitions not for use in user space programs */
#ifdef __KERNEL__
/* undefined similar functions from other headers... */
#undef BCD_TO_BIN(val)
#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
#undef BIN_TO_BCD(val)
#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
#undef IS_LOWNIBBLE_BCD(x)
#define IS_LOWNIBBLE_BCD(x) ( (x & 0x0f) < 10 )
#undef IS_BCD(x)
#define IS_BCD(x) ( ( (x & 0x0f) < 10) && ( (x & 0xf0) <10) ) )
#undef DAT(x)
#define DAT(x) ((unsigned int)(x->data)) /* x: i2c_client->data */
#define M41T00_IOC_MAGIC 0xcc
#define CTRL_STOP 0x80
/* structure used for reading */
struct rtc_registers_cs {
unsigned char secs __attribute__ ((packed));
unsigned char mins __attribute__ ((packed));
unsigned char hours __attribute__ ((packed));
unsigned char wday __attribute__ ((packed));
unsigned char mday __attribute__ ((packed));
unsigned char mon __attribute__ ((packed));
unsigned char year __attribute__ ((packed));
unsigned char cs __attribute__ ((packed));
};
/*
* Function prototypes
*/
static int __init m41t00_init(void);
static void __exit m41t00_exit(void);
static int m41t00_detect_client(struct i2c_adapter *adap, int addr, unsigned short flags, int kind);
static int m41t00_attach(struct i2c_adapter *adap);
static int m41t00_detach(struct i2c_client *client);
static int m41t00_get_datetime(struct i2c_client *client, struct rtc_time *dt);
static int m41t00_set_datetime(struct i2c_client *client, struct rtc_time *dt);
static int checkdate(struct rtc_time *dt);
static int m41t00_command(struct i2c_client *client, unsigned int cmd, void *arg);
static int m41t00_open(struct inode *minode, struct file *mfile);
static int m41t00_release(struct inode *minode, struct file *mfile);
static int m41t00_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_PROC_FS
static int m41t00_proc_output (char *buf);
#endif // CONFIG_PROC_FS
static void m41t00_print_info(struct rtc_registers_cs *rtc);
#ifdef M41T00_DEBUG
static void m41t00_print_content(struct rtc_registers_cs *rtc);
#endif // M41T00_DEBUG
static int m41t00_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
static void m41t00_rtc_to_tm(struct rtc_registers_cs *rtc, struct rtc_time *dt);
static void m41t00_tm_to_rtc(struct rtc_time *dt, struct rtc_registers_cs *rtc);
#if defined CONFIG_PPC
static int set_rtc_time_interrupt(unsigned long nowtime);
static void set_rtc_time_task(void *p);
#endif // CONFIG_PPC
#endif // __KERNEL__
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Opps...with ELDK-2.1.0 (ppc_4xx)
2003-04-23 17:18 Montgomery, Tim
@ 2003-04-23 19:18 ` Wolfgang Denk
0 siblings, 0 replies; 11+ messages in thread
From: Wolfgang Denk @ 2003-04-23 19:18 UTC (permalink / raw)
To: Montgomery, Tim
Cc: 'Eugene Surovegin', Johnson, Stephen,
linuxppc-embedded
In message <F9102D41F595D311ACA7009027DE2C8406AB8214@c3po.heurikon.com> you wrote:
>
> Wolfgang:
> I know you had some examples of i2c-based rtc support in your tree. Assuming the above is the cause behind the oops, does your driver preclude rtc updates from the timer interrupt context?
So far we used the RTC just to set the system date&time when booting.
No interrupt involved here.
Last year we added support for NTP to the ELDK, but missed to test
this on systems with a I2C based RTC (another lesson learned). It was
only now when I was about to officially announce the new ELDK (main
new feature: suppoort for MIPS) when we ran into the problem.
> Some time ago I queried the list on the best way to support an i2c-based RTC; at that time, there was no consensus.
> Any more thoughts along these lines?
I still feel the whole I2C layer is overkill for embedded systems. In
some projects we decided to ignore it in favour of smaller code with
a finer granularity of control.
Best regards,
Wolfgang Denk
--
Software Engineering: Embedded and Realtime Systems, Embedded Linux
Phone: (+49)-8142-4596-87 Fax: (+49)-8142-4596-88 Email: wd@denx.de
Nobody goes to that restaurant anymore. It's too crowded.
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2003-04-23 19:18 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-04-23 18:05 Opps...with ELDK-2.1.0 (ppc_4xx) Oliver Amft
[not found] <F9102D41F595D311ACA7009027DE2C8406AB8214@c3po.heurikon.com >
2003-04-23 17:24 ` Eugene Surovegin
-- strict thread matches above, loose matches on Subject: below --
2003-04-23 17:18 Montgomery, Tim
2003-04-23 19:18 ` Wolfgang Denk
[not found] <F9102D41F595D311ACA7009027DE2C840527B19B@c3po.heurikon.com>
2003-04-23 17:14 ` Wolfgang Denk
2003-04-22 19:38 Johnson, Stephen
2003-04-22 19:50 ` Wolfgang Denk
2003-04-22 20:33 ` Eugene Surovegin
[not found] <F9102D41F595D311ACA7009027DE2C840527B192@c3po.heurikon.com>
2003-04-22 19:16 ` Wolfgang Denk
2003-04-22 15:59 Johnson, Stephen
2003-04-22 16:58 ` Wolfgang Denk
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).