From: Oliver Amft <oam@gmx.net>
To: "Johnson, Stephen" <stevebj@artesyncp.com>
Cc: Wolfgang Denk <wd@denx.de>,
linuxppc-embedded@lists.linuxppc.org, ebs@ebshome.net
Subject: RE: Opps...with ELDK-2.1.0 (ppc_4xx)
Date: Wed, 23 Apr 2003 20:05:06 +0200 [thread overview]
Message-ID: <3EA6D5D2.3080005@gmx.net> (raw)
[-- 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__
next reply other threads:[~2003-04-23 18:05 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-04-23 18:05 Oliver Amft [this message]
[not found] <F9102D41F595D311ACA7009027DE2C8406AB8214@c3po.heurikon.com >
2003-04-23 17:24 ` Opps...with ELDK-2.1.0 (ppc_4xx) 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3EA6D5D2.3080005@gmx.net \
--to=oam@gmx.net \
--cc=ebs@ebshome.net \
--cc=linuxppc-embedded@lists.linuxppc.org \
--cc=stevebj@artesyncp.com \
--cc=wd@denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).