LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Wan ZongShun @ 2010-07-12  8:30 UTC (permalink / raw)
  To: rtc-linux; +Cc: linuxppc-dev, Andrew Morton, B11780, Mingkai.hu
In-Reply-To: <AANLkTikv5coqQM4duVmexbOM6iIh-IG-wiCqSqjPHzV3@mail.gmail.com>

2010/7/12 Tiefei zang <tiefei.zang@gmail.com>:
> On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrote=
:
>> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>>
>>
>> I noted this patch is the second version, maybe you can describe which
>> modifications you have done here.
>> and add [PATCH v2] to mail subject.
> I do not think the modification comparing previous version should go
> into the commit message.
> I buy in your comments in original version patch and update:
> 1. add rtc_valid_tm to check return value.
> 2. remove ioctl function, which do not used and tested.
> 3. add __devinit for ds3232 probe
> 4. put request irq after driver registration.
>

Okay.
Which git tree do you want to merge your patch?

>>
>>> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
>>> Signed-off-by: Jingchang Lu <b22599@freescale.com>
>>> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>> ---
>>> Tested on MPC8536DS and P4080DS board
>>>
>>> =C2=A0drivers/rtc/Kconfig =C2=A0 =C2=A0 =C2=A0| =C2=A0 11 ++
>>> =C2=A0drivers/rtc/Makefile =C2=A0 =C2=A0 | =C2=A0 =C2=A01 +
>>> =C2=A0drivers/rtc/rtc-ds3232.c | =C2=A0427 ++++++++++++++++++++++++++++=
++++++++++++++++++
>>> =C2=A03 files changed, 439 insertions(+), 0 deletions(-)
>>> =C2=A0create mode 100644 drivers/rtc/rtc-ds3232.c
>>>
>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>>> index 6a13037..13c2fdb 100644
>>> --- a/drivers/rtc/Kconfig
>>> +++ b/drivers/rtc/Kconfig
>>> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This driver can also be built as a mo=
dule. If so, the module
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0will be called rtc-ds1672.
>>>
>>> +config RTC_DRV_DS3232
>>> + =C2=A0 =C2=A0 =C2=A0 tristate "Dallas/Maxim DS3232"
>>> + =C2=A0 =C2=A0 =C2=A0 depends on RTC_CLASS && I2C
>>> + =C2=A0 =C2=A0 =C2=A0 help
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 If you say yes here you get support for D=
allas Semiconductor
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 DS3232 real-time clock chips. =C2=A0If an=
 interrupt is associated
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 with the device, the alarm functionality =
is supported.
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 This driver can also be built as a module=
. =C2=A0If so, the module
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 will be called rtc-ds3232.
>>> +
>>> =C2=A0config RTC_DRV_MAX6900
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0tristate "Maxim MAX6900"
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0help
>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>>> index 44ef194..0af190c 100644
>>> --- a/drivers/rtc/Makefile
>>> +++ b/drivers/rtc/Makefile
>>> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =C2=A0+=3D rtc-ds1511.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1553) =C2=A0 +=3D rtc-ds1553.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1672) =C2=A0 +=3D rtc-ds1672.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1742) =C2=A0 +=3D rtc-ds1742.o
>>> +obj-$(CONFIG_RTC_DRV_DS3232) =C2=A0 +=3D rtc-ds3232.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS3234) =C2=A0 +=3D rtc-ds3234.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_EFI) =C2=A0 =C2=A0 =C2=A0+=3D rtc-efi.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_EP93XX) =C2=A0 +=3D rtc-ep93xx.o
>>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>>> new file mode 100644
>>> index 0000000..e36ec1c
>>> --- /dev/null
>>> +++ b/drivers/rtc/rtc-ds3232.c
>>> @@ -0,0 +1,427 @@
>>> +/*
>>> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over =
I2C
>>> + *
>>> + * Copyright (C) 2009-2010 Freescale Semiconductor.
>>> + *
>>> + * This program is free software; you can redistribute =C2=A0it and/or=
 modify it
>>> + * under =C2=A0the terms of =C2=A0the GNU General =C2=A0Public License=
 as published by the
>>> + * Free Software Foundation; =C2=A0either version 2 of the =C2=A0Licen=
se, or (at your
>>> + * option) any later version.
>>> + */
>>> +/*
>>> + * It would be more efficient to use i2c msgs/i2c_transfer directly bu=
t, as
>>> + * recommened in .../Documentation/i2c/writing-clients section
>>> + * "Sending and receiving", using SMBus level communication is preferr=
ed.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/rtc.h>
>>> +#include <linux/bcd.h>
>>> +#include <linux/workqueue.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#define DS3232_REG_SECONDS =C2=A0 =C2=A0 0x00
>>> +#define DS3232_REG_MINUTES =C2=A0 =C2=A0 0x01
>>> +#define DS3232_REG_HOURS =C2=A0 =C2=A0 =C2=A0 0x02
>>> +#define DS3232_REG_AMPM =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x02
>>> +#define DS3232_REG_DAY =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x03
>>> +#define DS3232_REG_DATE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x04
>>> +#define DS3232_REG_MONTH =C2=A0 =C2=A0 =C2=A0 0x05
>>> +#define DS3232_REG_CENTURY =C2=A0 =C2=A0 0x05
>>> +#define DS3232_REG_YEAR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x06
>>> +#define DS3232_REG_ALARM1 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x07 /* Alarm 1 =
BASE */
>>> +#define DS3232_REG_ALARM2 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0B /* Alarm 2 =
BASE */
>>> +#define DS3232_REG_CR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0E =C2=A0 =
=C2=A0/* Control register */
>>> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_CR_nEOSC =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x80
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_INTCN =C2=A0 =C2=A0 =C2=A0=
 =C2=A00x04
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A2IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x02
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A1IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x01
>>> +
>>> +#define DS3232_REG_SR =C2=A00x0F =C2=A0 =C2=A0/* control/status regist=
er */
>>> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_SR_OSF =C2=A0 0x80
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_BSY =C2=A0 0x04
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A2F =C2=A0 0x02
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A1F =C2=A0 0x01
>>> +
>>> +struct ds3232 {
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client;
>>> + =C2=A0 =C2=A0 =C2=A0 struct rtc_device *rtc;
>>> + =C2=A0 =C2=A0 =C2=A0 struct work_struct work;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* The mutex protects alarm operations, and prev=
ents a race
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* between the enable_irq() in the workqueu=
e and the free_irq()
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* in the remove function.
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
>>> + =C2=A0 =C2=A0 =C2=A0 struct mutex mutex;
>>> + =C2=A0 =C2=A0 =C2=A0 int exiting;
>>> +};
>>> +
>>> +static struct i2c_driver ds3232_driver;
>>> +
>>> +static int ds3232_check_rtc_status(struct i2c_client *client)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_OSF)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_warn(&client->de=
v,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "oscillator discontinuity flagged, "
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "time unreliable\n");
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1=
F | DS3232_REG_SR_A2F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* If the alarm is pending, clear it before requ=
esting
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* the interrupt, so an interrupt event isn=
't reported
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* before everything is initialized.
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_read_byte_data(client, DS3=
232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return control;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_C=
R_A2IE);
>>> + =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_REG_CR_INTCN;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_byte_data(client, DS3232_=
REG_CR, control);
>>> +}
>>> +
>>> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int year, month, day, hour, minute, sec=
ond;
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int week, twelve_hr, am_pm;
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int century, add_century =3D 0;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS=
3232_REG_SECONDS, 7, buf);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 7)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EIO;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 second =3D buf[0];
>>> + =C2=A0 =C2=A0 =C2=A0 minute =3D buf[1];
>>> + =C2=A0 =C2=A0 =C2=A0 hour =3D buf[2];
>>> + =C2=A0 =C2=A0 =C2=A0 week =3D buf[3];
>>> + =C2=A0 =C2=A0 =C2=A0 day =3D buf[4];
>>> + =C2=A0 =C2=A0 =C2=A0 month =3D buf[5];
>>> + =C2=A0 =C2=A0 =C2=A0 year =3D buf[6];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Extract additional information for AM/PM and =
century */
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 twelve_hr =3D hour & 0x40;
>>> + =C2=A0 =C2=A0 =C2=A0 am_pm =3D hour & 0x20;
>>> + =C2=A0 =C2=A0 =C2=A0 century =3D month & 0x80;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Write to rtc_time structure */
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_sec =3D bcd2bin(second);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_min =3D bcd2bin(minute);
>>> + =C2=A0 =C2=A0 =C2=A0 if (twelve_hr) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Convert to 24 hr =
*/
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (am_pm)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F) + 12;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F);
>>> + =C2=A0 =C2=A0 =C2=A0 } else {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time->tm_hour =3D bc=
d2bin(hour);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_wday =3D bcd2bin(week);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_mday =3D bcd2bin(day);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_mon =3D bcd2bin(month & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 if (century)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add_century =3D 100;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_year =3D bcd2bin(year) + add_century;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return rtc_valid_tm(time);
>>> +}
>>> +
>>> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Extract time from rtc_time and load into ds32=
32*/
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(time->tm_sec);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(time->tm_min);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(time->tm_hour);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the=
 week */
>>> + =C2=A0 =C2=A0 =C2=A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
>>> + =C2=A0 =C2=A0 =C2=A0 buf[5] =3D bin2bcd(time->tm_mon);
>>> + =C2=A0 =C2=A0 =C2=A0 if (time->tm_year >=3D 100) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[5] |=3D 0x80;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(t=
ime->tm_year - 100);
>>> + =C2=A0 =C2=A0 =C2=A0 } else {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(t=
ime->tm_year);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_i2c_block_data(client,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 DS3232_REG_SECONDS, 7, buf);
>>> +}
>>> +
>>> +/*
>>> + * DS3232 has two alarm, we only use alarm1
>>> + * According to linux specification, only support one-shot alarm
>>> + * no periodic alarm mode
>>> + */
>>> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *al=
arm)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D ret =3D i2c_smbus_read_byte_data(client=
, DS3232_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control =3D ret =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS=
3232_REG_ALARM1, 4, buf);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mon =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_year =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_wday =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_yday =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_isdst =3D -1;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1=
IE);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +}
>>> +
>>> +/*
>>> + * linux rtc-module does not support wday alarm
>>> + * and only 24h time mode supported indeed
>>> + */
>>> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *ala=
rm)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (client->irq <=3D 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* clear alarm interrupt enable bit */
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D control =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_C=
R_A2IE);
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_CR, control);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* clear any pending alarm flag */
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2=
F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_i2c_block_data(client,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 DS3232_REG_ALARM1, 4, buf);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (alarm->enabled) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_=
REG_CR_A1IE;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_wr=
ite_byte_data(client, DS3232_REG_CR, control);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> + =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +}
>>> +
>>> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D dev_id;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 disable_irq_nosync(irq);
>>> + =C2=A0 =C2=A0 =C2=A0 schedule_work(&ds3232->work);
>>> + =C2=A0 =C2=A0 =C2=A0 return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void ds3232_work(struct work_struct *work)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D container_of(work, str=
uct ds3232, work);
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D ds3232->client;
>>> + =C2=A0 =C2=A0 =C2=A0 int stat, control;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto unlock;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_A1F) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbu=
s_read_byte_data(client, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A0 goto out;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* disable alarm1 in=
terrupt */
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS323=
2_REG_CR_A1IE);
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte=
_data(client, DS3232_REG_CR, control);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* clear the alarm p=
end flag */
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stat &=3D ~DS3232_RE=
G_SR_A1F;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte=
_data(client, DS3232_REG_SR, stat);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rtc_update_irq(ds323=
2->rtc, 1, RTC_AF | RTC_IRQF);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232->exiting)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable_irq(client->i=
rq);
>>> +unlock:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> +}
>>> +
>>> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
>>> + =C2=A0 =C2=A0 =C2=A0 .read_time =3D ds3232_read_time,
>>> + =C2=A0 =C2=A0 =C2=A0 .set_time =3D ds3232_set_time,
>>> + =C2=A0 =C2=A0 =C2=A0 .read_alarm =3D ds3232_read_alarm,
>>> + =C2=A0 =C2=A0 =C2=A0 .set_alarm =3D ds3232_set_alarm,
>>> +};
>>
>> I sew that you have discarded the .ioctl function, which is used to
>> enable/disable the alarm irq in previous driver patch.
>> but,at the same time, you didnot implement .alarm_irq_enable function
>> , so is there no need to enable or disable the alarm irq bit in
>> current version patch?
> Right. This is not needed in current patch. Alarm is not used and tested.
> Thanks.
> Roy
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.



--=20
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel@lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

* linux-arm-NUC900 mailing list
mail addr:NUC900@googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com@gmail.com

^ permalink raw reply

* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Tiefei zang @ 2010-07-12  8:12 UTC (permalink / raw)
  To: rtc-linux; +Cc: linuxppc-dev, Andrew Morton, B11780, Mingkai.hu
In-Reply-To: <AANLkTikK7wsnSVa9Ww5swaAR828edz_zuiBHzV4WZjqw@mail.gmail.com>

On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrote:
> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>
>
> I noted this patch is the second version, maybe you can describe which
> modifications you have done here.
> and add [PATCH v2] to mail subject.
I do not think the modification comparing previous version should go
into the commit message.
I buy in your comments in original version patch and update:
1. add rtc_valid_tm to check return value.
2. remove ioctl function, which do not used and tested.
3. add __devinit for ds3232 probe
4. put request irq after driver registration.

>
>> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
>> Signed-off-by: Jingchang Lu <b22599@freescale.com>
>> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>> ---
>> Tested on MPC8536DS and P4080DS board
>>
>> =A0drivers/rtc/Kconfig =A0 =A0 =A0| =A0 11 ++
>> =A0drivers/rtc/Makefile =A0 =A0 | =A0 =A01 +
>> =A0drivers/rtc/rtc-ds3232.c | =A0427 +++++++++++++++++++++++++++++++++++=
+++++++++++
>> =A03 files changed, 439 insertions(+), 0 deletions(-)
>> =A0create mode 100644 drivers/rtc/rtc-ds3232.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index 6a13037..13c2fdb 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
>> =A0 =A0 =A0 =A0 =A0This driver can also be built as a module. If so, the=
 module
>> =A0 =A0 =A0 =A0 =A0will be called rtc-ds1672.
>>
>> +config RTC_DRV_DS3232
>> + =A0 =A0 =A0 tristate "Dallas/Maxim DS3232"
>> + =A0 =A0 =A0 depends on RTC_CLASS && I2C
>> + =A0 =A0 =A0 help
>> + =A0 =A0 =A0 =A0 If you say yes here you get support for Dallas Semicon=
ductor
>> + =A0 =A0 =A0 =A0 DS3232 real-time clock chips. =A0If an interrupt is as=
sociated
>> + =A0 =A0 =A0 =A0 with the device, the alarm functionality is supported.
>> +
>> + =A0 =A0 =A0 =A0 This driver can also be built as a module. =A0If so, t=
he module
>> + =A0 =A0 =A0 =A0 will be called rtc-ds3232.
>> +
>> =A0config RTC_DRV_MAX6900
>> =A0 =A0 =A0 =A0tristate "Maxim MAX6900"
>> =A0 =A0 =A0 =A0help
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 44ef194..0af190c 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =A0+=3D rtc-ds1511.o
>> =A0obj-$(CONFIG_RTC_DRV_DS1553) =A0 +=3D rtc-ds1553.o
>> =A0obj-$(CONFIG_RTC_DRV_DS1672) =A0 +=3D rtc-ds1672.o
>> =A0obj-$(CONFIG_RTC_DRV_DS1742) =A0 +=3D rtc-ds1742.o
>> +obj-$(CONFIG_RTC_DRV_DS3232) =A0 +=3D rtc-ds3232.o
>> =A0obj-$(CONFIG_RTC_DRV_DS3234) =A0 +=3D rtc-ds3234.o
>> =A0obj-$(CONFIG_RTC_DRV_EFI) =A0 =A0 =A0+=3D rtc-efi.o
>> =A0obj-$(CONFIG_RTC_DRV_EP93XX) =A0 +=3D rtc-ep93xx.o
>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>> new file mode 100644
>> index 0000000..e36ec1c
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-ds3232.c
>> @@ -0,0 +1,427 @@
>> +/*
>> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I=
2C
>> + *
>> + * Copyright (C) 2009-2010 Freescale Semiconductor.
>> + *
>> + * This program is free software; you can redistribute =A0it and/or mod=
ify it
>> + * under =A0the terms of =A0the GNU General =A0Public License as publis=
hed by the
>> + * Free Software Foundation; =A0either version 2 of the =A0License, or =
(at your
>> + * option) any later version.
>> + */
>> +/*
>> + * It would be more efficient to use i2c msgs/i2c_transfer directly but=
, as
>> + * recommened in .../Documentation/i2c/writing-clients section
>> + * "Sending and receiving", using SMBus level communication is preferre=
d.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/i2c.h>
>> +#include <linux/rtc.h>
>> +#include <linux/bcd.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/slab.h>
>> +
>> +#define DS3232_REG_SECONDS =A0 =A0 0x00
>> +#define DS3232_REG_MINUTES =A0 =A0 0x01
>> +#define DS3232_REG_HOURS =A0 =A0 =A0 0x02
>> +#define DS3232_REG_AMPM =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x02
>> +#define DS3232_REG_DAY =A0 =A0 =A0 =A0 0x03
>> +#define DS3232_REG_DATE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x04
>> +#define DS3232_REG_MONTH =A0 =A0 =A0 0x05
>> +#define DS3232_REG_CENTURY =A0 =A0 0x05
>> +#define DS3232_REG_YEAR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x06
>> +#define DS3232_REG_ALARM1 =A0 =A0 =A0 =A0 0x07 /* Alarm 1 BASE */
>> +#define DS3232_REG_ALARM2 =A0 =A0 =A0 =A0 0x0B /* Alarm 2 BASE */
>> +#define DS3232_REG_CR =A0 =A0 =A0 =A0 =A00x0E =A0 =A0/* Control registe=
r */
>> +# =A0 =A0 =A0define DS3232_REG_CR_nEOSC =A0 =A0 =A0 =A00x80
>> +# =A0 =A0 =A0 define DS3232_REG_CR_INTCN =A0 =A0 =A0 =A00x04
>> +# =A0 =A0 =A0 define DS3232_REG_CR_A2IE =A0 =A0 =A0 =A00x02
>> +# =A0 =A0 =A0 define DS3232_REG_CR_A1IE =A0 =A0 =A0 =A00x01
>> +
>> +#define DS3232_REG_SR =A00x0F =A0 =A0/* control/status register */
>> +# =A0 =A0 =A0define DS3232_REG_SR_OSF =A0 0x80
>> +# =A0 =A0 =A0 define DS3232_REG_SR_BSY =A0 0x04
>> +# =A0 =A0 =A0 define DS3232_REG_SR_A2F =A0 0x02
>> +# =A0 =A0 =A0 define DS3232_REG_SR_A1F =A0 0x01
>> +
>> +struct ds3232 {
>> + =A0 =A0 =A0 struct i2c_client *client;
>> + =A0 =A0 =A0 struct rtc_device *rtc;
>> + =A0 =A0 =A0 struct work_struct work;
>> +
>> + =A0 =A0 =A0 /* The mutex protects alarm operations, and prevents a rac=
e
>> + =A0 =A0 =A0 =A0* between the enable_irq() in the workqueue and the fre=
e_irq()
>> + =A0 =A0 =A0 =A0* in the remove function.
>> + =A0 =A0 =A0 =A0*/
>> + =A0 =A0 =A0 struct mutex mutex;
>> + =A0 =A0 =A0 int exiting;
>> +};
>> +
>> +static struct i2c_driver ds3232_driver;
>> +
>> +static int ds3232_check_rtc_status(struct i2c_client *client)
>> +{
>> + =A0 =A0 =A0 int ret =3D 0;
>> + =A0 =A0 =A0 int control, stat;
>> +
>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return stat;
>> +
>> + =A0 =A0 =A0 if (stat & DS3232_REG_SR_OSF)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&client->dev,
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "oscillato=
r discontinuity flagged, "
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "time unre=
liable\n");
>> +
>> + =A0 =A0 =A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232=
_REG_SR_A2F);
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_SR, s=
tat);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>> +
>> + =A0 =A0 =A0 /* If the alarm is pending, clear it before requesting
>> + =A0 =A0 =A0 =A0* the interrupt, so an interrupt event isn't reported
>> + =A0 =A0 =A0 =A0* before everything is initialized.
>> + =A0 =A0 =A0 =A0*/
>> +
>> + =A0 =A0 =A0 control =3D i2c_smbus_read_byte_data(client, DS3232_REG_CR=
);
>> + =A0 =A0 =A0 if (control < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return control;
>> +
>> + =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
>> + =A0 =A0 =A0 control |=3D DS3232_REG_CR_INTCN;
>> +
>> + =A0 =A0 =A0 return i2c_smbus_write_byte_data(client, DS3232_REG_CR, co=
ntrol);
>> +}
>> +
>> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 int ret;
>> + =A0 =A0 =A0 u8 buf[7];
>> + =A0 =A0 =A0 unsigned int year, month, day, hour, minute, second;
>> + =A0 =A0 =A0 unsigned int week, twelve_hr, am_pm;
>> + =A0 =A0 =A0 unsigned int century, add_century =3D 0;
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS3232_REG_S=
ECONDS, 7, buf);
>> +
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>> + =A0 =A0 =A0 if (ret < 7)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EIO;
>> +
>> + =A0 =A0 =A0 second =3D buf[0];
>> + =A0 =A0 =A0 minute =3D buf[1];
>> + =A0 =A0 =A0 hour =3D buf[2];
>> + =A0 =A0 =A0 week =3D buf[3];
>> + =A0 =A0 =A0 day =3D buf[4];
>> + =A0 =A0 =A0 month =3D buf[5];
>> + =A0 =A0 =A0 year =3D buf[6];
>> +
>> + =A0 =A0 =A0 /* Extract additional information for AM/PM and century */
>> +
>> + =A0 =A0 =A0 twelve_hr =3D hour & 0x40;
>> + =A0 =A0 =A0 am_pm =3D hour & 0x20;
>> + =A0 =A0 =A0 century =3D month & 0x80;
>> +
>> + =A0 =A0 =A0 /* Write to rtc_time structure */
>> +
>> + =A0 =A0 =A0 time->tm_sec =3D bcd2bin(second);
>> + =A0 =A0 =A0 time->tm_min =3D bcd2bin(minute);
>> + =A0 =A0 =A0 if (twelve_hr) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Convert to 24 hr */
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (am_pm)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(=
hour & 0x1F) + 12;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(=
hour & 0x1F);
>> + =A0 =A0 =A0 } else {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(hour);
>> + =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 time->tm_wday =3D bcd2bin(week);
>> + =A0 =A0 =A0 time->tm_mday =3D bcd2bin(day);
>> + =A0 =A0 =A0 time->tm_mon =3D bcd2bin(month & 0x7F);
>> + =A0 =A0 =A0 if (century)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 add_century =3D 100;
>> +
>> + =A0 =A0 =A0 time->tm_year =3D bcd2bin(year) + add_century;
>> +
>> + =A0 =A0 =A0 return rtc_valid_tm(time);
>> +}
>> +
>> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 u8 buf[7];
>> +
>> + =A0 =A0 =A0 /* Extract time from rtc_time and load into ds3232*/
>> +
>> + =A0 =A0 =A0 buf[0] =3D bin2bcd(time->tm_sec);
>> + =A0 =A0 =A0 buf[1] =3D bin2bcd(time->tm_min);
>> + =A0 =A0 =A0 buf[2] =3D bin2bcd(time->tm_hour);
>> + =A0 =A0 =A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the week */
>> + =A0 =A0 =A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
>> + =A0 =A0 =A0 buf[5] =3D bin2bcd(time->tm_mon);
>> + =A0 =A0 =A0 if (time->tm_year >=3D 100) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[5] |=3D 0x80;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[6] =3D bin2bcd(time->tm_year - 100);
>> + =A0 =A0 =A0 } else {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[6] =3D bin2bcd(time->tm_year);
>> + =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 return i2c_smbus_write_i2c_block_data(client,
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 DS3232_REG_SECONDS, 7, buf);
>> +}
>> +
>> +/*
>> + * DS3232 has two alarm, we only use alarm1
>> + * According to linux specification, only support one-shot alarm
>> + * no periodic alarm mode
>> + */
>> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *ala=
rm)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>> + =A0 =A0 =A0 int control, stat;
>> + =A0 =A0 =A0 int ret =3D 0;
>> + =A0 =A0 =A0 u8 buf[4];
>> +
>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>> + =A0 =A0 =A0 stat =3D ret =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 control =3D ret =3D i2c_smbus_read_byte_data(client, DS323=
2_REG_CR);
>> + =A0 =A0 =A0 if (control < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS3232_REG_A=
LARM1, 4, buf);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
>> + =A0 =A0 =A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
>> + =A0 =A0 =A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
>> + =A0 =A0 =A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
>> +
>> + =A0 =A0 =A0 alarm->time.tm_mon =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_year =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_wday =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_yday =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_isdst =3D -1;
>> +
>> + =A0 =A0 =A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1IE);
>> + =A0 =A0 =A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
>> +out:
>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>> +
>> + =A0 =A0 =A0 return ret;
>> +}
>> +
>> +/*
>> + * linux rtc-module does not support wday alarm
>> + * and only 24h time mode supported indeed
>> + */
>> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alar=
m)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>> + =A0 =A0 =A0 int control, stat;
>> + =A0 =A0 =A0 int ret =3D 0;
>> + =A0 =A0 =A0 u8 buf[4];
>> +
>> + =A0 =A0 =A0 if (client->irq <=3D 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
>> +
>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>> +
>> + =A0 =A0 =A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
>> + =A0 =A0 =A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
>> + =A0 =A0 =A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
>> + =A0 =A0 =A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
>> +
>> + =A0 =A0 =A0 /* clear alarm interrupt enable bit */
>> + =A0 =A0 =A0 ret =3D control =3D i2c_smbus_read_byte_data(client, DS323=
2_REG_CR);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> + =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_CR, c=
ontrol);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 /* clear any pending alarm flag */
>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return stat;
>> +
>> + =A0 =A0 =A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_SR, s=
tat);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_i2c_block_data(client,
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 DS3232_REG_ALARM1, 4, buf);
>> +
>> + =A0 =A0 =A0 if (alarm->enabled) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control |=3D DS3232_REG_CR_A1IE;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, =
DS3232_REG_CR, control);
>> + =A0 =A0 =A0 }
>> +out:
>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>> + =A0 =A0 =A0 return ret;
>> +}
>> +
>> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D dev_id;
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>> +
>> + =A0 =A0 =A0 disable_irq_nosync(irq);
>> + =A0 =A0 =A0 schedule_work(&ds3232->work);
>> + =A0 =A0 =A0 return IRQ_HANDLED;
>> +}
>> +
>> +static void ds3232_work(struct work_struct *work)
>> +{
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D container_of(work, struct ds3232=
, work);
>> + =A0 =A0 =A0 struct i2c_client *client =3D ds3232->client;
>> + =A0 =A0 =A0 int stat, control;
>> +
>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>> +
>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto unlock;
>> +
>> + =A0 =A0 =A0 if (stat & DS3232_REG_SR_A1F) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control =3D i2c_smbus_read_byte_data(clien=
t, DS3232_REG_CR);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (control < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* disable alarm1 interrupt */
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_smbus_write_byte_data(client, DS3232_R=
EG_CR, control);
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* clear the alarm pend flag */
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 stat &=3D ~DS3232_REG_SR_A1F;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_smbus_write_byte_data(client, DS3232_R=
EG_SR, stat);
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rtc_update_irq(ds3232->rtc, 1, RTC_AF | RT=
C_IRQF);
>> + =A0 =A0 =A0 }
>> +
>> +out:
>> + =A0 =A0 =A0 if (!ds3232->exiting)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 enable_irq(client->irq);
>> +unlock:
>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>> +}
>> +
>> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
>> + =A0 =A0 =A0 .read_time =3D ds3232_read_time,
>> + =A0 =A0 =A0 .set_time =3D ds3232_set_time,
>> + =A0 =A0 =A0 .read_alarm =3D ds3232_read_alarm,
>> + =A0 =A0 =A0 .set_alarm =3D ds3232_set_alarm,
>> +};
>
> I sew that you have discarded the .ioctl function, which is used to
> enable/disable the alarm irq in previous driver patch.
> but,at the same time, you didnot implement .alarm_irq_enable function
> , so is there no need to enable or disable the alarm irq bit in
> current version patch?
Right. This is not needed in current patch. Alarm is not used and tested.
Thanks.
Roy

^ permalink raw reply

* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Wan ZongShun @ 2010-07-12  7:08 UTC (permalink / raw)
  To: rtc-linux, Andrew Morton; +Cc: Mingkai.hu, linuxppc-dev, B11780
In-Reply-To: <1278909366-15903-1-git-send-email-tie-fei.zang@freescale.com>

2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
> This patch adds the driver for RTC chip DS3232 via I2C bus.
>

I noted this patch is the second version, maybe you can describe which
modifications you have done here.
and add [PATCH v2] to mail subject.

> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
> Signed-off-by: Jingchang Lu <b22599@freescale.com>
> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> ---
> Tested on MPC8536DS and P4080DS board
>
> =C2=A0drivers/rtc/Kconfig =C2=A0 =C2=A0 =C2=A0| =C2=A0 11 ++
> =C2=A0drivers/rtc/Makefile =C2=A0 =C2=A0 | =C2=A0 =C2=A01 +
> =C2=A0drivers/rtc/rtc-ds3232.c | =C2=A0427 ++++++++++++++++++++++++++++++=
++++++++++++++++
> =C2=A03 files changed, 439 insertions(+), 0 deletions(-)
> =C2=A0create mode 100644 drivers/rtc/rtc-ds3232.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 6a13037..13c2fdb 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This driver can also be built as a modu=
le. If so, the module
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0will be called rtc-ds1672.
>
> +config RTC_DRV_DS3232
> + =C2=A0 =C2=A0 =C2=A0 tristate "Dallas/Maxim DS3232"
> + =C2=A0 =C2=A0 =C2=A0 depends on RTC_CLASS && I2C
> + =C2=A0 =C2=A0 =C2=A0 help
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 If you say yes here you get support for Dal=
las Semiconductor
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 DS3232 real-time clock chips. =C2=A0If an i=
nterrupt is associated
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 with the device, the alarm functionality is=
 supported.
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 This driver can also be built as a module. =
=C2=A0If so, the module
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 will be called rtc-ds3232.
> +
> =C2=A0config RTC_DRV_MAX6900
> =C2=A0 =C2=A0 =C2=A0 =C2=A0tristate "Maxim MAX6900"
> =C2=A0 =C2=A0 =C2=A0 =C2=A0help
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 44ef194..0af190c 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =C2=A0+=3D rtc-ds1511.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS1553) =C2=A0 +=3D rtc-ds1553.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS1672) =C2=A0 +=3D rtc-ds1672.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS1742) =C2=A0 +=3D rtc-ds1742.o
> +obj-$(CONFIG_RTC_DRV_DS3232) =C2=A0 +=3D rtc-ds3232.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS3234) =C2=A0 +=3D rtc-ds3234.o
> =C2=A0obj-$(CONFIG_RTC_DRV_EFI) =C2=A0 =C2=A0 =C2=A0+=3D rtc-efi.o
> =C2=A0obj-$(CONFIG_RTC_DRV_EP93XX) =C2=A0 +=3D rtc-ep93xx.o
> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
> new file mode 100644
> index 0000000..e36ec1c
> --- /dev/null
> +++ b/drivers/rtc/rtc-ds3232.c
> @@ -0,0 +1,427 @@
> +/*
> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2=
C
> + *
> + * Copyright (C) 2009-2010 Freescale Semiconductor.
> + *
> + * This program is free software; you can redistribute =C2=A0it and/or m=
odify it
> + * under =C2=A0the terms of =C2=A0the GNU General =C2=A0Public License a=
s published by the
> + * Free Software Foundation; =C2=A0either version 2 of the =C2=A0License=
, or (at your
> + * option) any later version.
> + */
> +/*
> + * It would be more efficient to use i2c msgs/i2c_transfer directly but,=
 as
> + * recommened in .../Documentation/i2c/writing-clients section
> + * "Sending and receiving", using SMBus level communication is preferred=
.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/rtc.h>
> +#include <linux/bcd.h>
> +#include <linux/workqueue.h>
> +#include <linux/slab.h>
> +
> +#define DS3232_REG_SECONDS =C2=A0 =C2=A0 0x00
> +#define DS3232_REG_MINUTES =C2=A0 =C2=A0 0x01
> +#define DS3232_REG_HOURS =C2=A0 =C2=A0 =C2=A0 0x02
> +#define DS3232_REG_AMPM =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A00x02
> +#define DS3232_REG_DAY =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x03
> +#define DS3232_REG_DATE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A00x04
> +#define DS3232_REG_MONTH =C2=A0 =C2=A0 =C2=A0 0x05
> +#define DS3232_REG_CENTURY =C2=A0 =C2=A0 0x05
> +#define DS3232_REG_YEAR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
 =C2=A00x06
> +#define DS3232_REG_ALARM1 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x07 /* Alarm 1 BA=
SE */
> +#define DS3232_REG_ALARM2 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0B /* Alarm 2 BA=
SE */
> +#define DS3232_REG_CR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0E =C2=A0 =C2=
=A0/* Control register */
> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_CR_nEOSC =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x80
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_INTCN =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x04
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A2IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x02
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A1IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x01
> +
> +#define DS3232_REG_SR =C2=A00x0F =C2=A0 =C2=A0/* control/status register=
 */
> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_SR_OSF =C2=A0 0x80
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_BSY =C2=A0 0x04
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A2F =C2=A0 0x02
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A1F =C2=A0 0x01
> +
> +struct ds3232 {
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client;
> + =C2=A0 =C2=A0 =C2=A0 struct rtc_device *rtc;
> + =C2=A0 =C2=A0 =C2=A0 struct work_struct work;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* The mutex protects alarm operations, and preven=
ts a race
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* between the enable_irq() in the workqueue =
and the free_irq()
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* in the remove function.
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
> + =C2=A0 =C2=A0 =C2=A0 struct mutex mutex;
> + =C2=A0 =C2=A0 =C2=A0 int exiting;
> +};
> +
> +static struct i2c_driver ds3232_driver;
> +
> +static int ds3232_check_rtc_status(struct i2c_client *client)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
> +
> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_OSF)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_warn(&client->dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "oscillator discontinuity flagged, "
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "time unreliable\n");
> +
> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F =
| DS3232_REG_SR_A2F);
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_R=
EG_SR, stat);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* If the alarm is pending, clear it before reques=
ting
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* the interrupt, so an interrupt event isn't=
 reported
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* before everything is initialized.
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
> +
> + =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_read_byte_data(client, DS323=
2_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return control;
> +
> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_=
A2IE);
> + =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_REG_CR_INTCN;
> +
> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_byte_data(client, DS3232_RE=
G_CR, control);
> +}
> +
> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
> + =C2=A0 =C2=A0 =C2=A0 unsigned int year, month, day, hour, minute, secon=
d;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int week, twelve_hr, am_pm;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int century, add_century =3D 0;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS32=
32_REG_SECONDS, 7, buf);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 7)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EIO;
> +
> + =C2=A0 =C2=A0 =C2=A0 second =3D buf[0];
> + =C2=A0 =C2=A0 =C2=A0 minute =3D buf[1];
> + =C2=A0 =C2=A0 =C2=A0 hour =3D buf[2];
> + =C2=A0 =C2=A0 =C2=A0 week =3D buf[3];
> + =C2=A0 =C2=A0 =C2=A0 day =3D buf[4];
> + =C2=A0 =C2=A0 =C2=A0 month =3D buf[5];
> + =C2=A0 =C2=A0 =C2=A0 year =3D buf[6];
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Extract additional information for AM/PM and ce=
ntury */
> +
> + =C2=A0 =C2=A0 =C2=A0 twelve_hr =3D hour & 0x40;
> + =C2=A0 =C2=A0 =C2=A0 am_pm =3D hour & 0x20;
> + =C2=A0 =C2=A0 =C2=A0 century =3D month & 0x80;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Write to rtc_time structure */
> +
> + =C2=A0 =C2=A0 =C2=A0 time->tm_sec =3D bcd2bin(second);
> + =C2=A0 =C2=A0 =C2=A0 time->tm_min =3D bcd2bin(minute);
> + =C2=A0 =C2=A0 =C2=A0 if (twelve_hr) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Convert to 24 hr */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (am_pm)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F) + 12;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F);
> + =C2=A0 =C2=A0 =C2=A0 } else {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time->tm_hour =3D bcd2=
bin(hour);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 time->tm_wday =3D bcd2bin(week);
> + =C2=A0 =C2=A0 =C2=A0 time->tm_mday =3D bcd2bin(day);
> + =C2=A0 =C2=A0 =C2=A0 time->tm_mon =3D bcd2bin(month & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 if (century)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add_century =3D 100;
> +
> + =C2=A0 =C2=A0 =C2=A0 time->tm_year =3D bcd2bin(year) + add_century;
> +
> + =C2=A0 =C2=A0 =C2=A0 return rtc_valid_tm(time);
> +}
> +
> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Extract time from rtc_time and load into ds3232=
*/
> +
> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(time->tm_sec);
> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(time->tm_min);
> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(time->tm_hour);
> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the w=
eek */
> + =C2=A0 =C2=A0 =C2=A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
> + =C2=A0 =C2=A0 =C2=A0 buf[5] =3D bin2bcd(time->tm_mon);
> + =C2=A0 =C2=A0 =C2=A0 if (time->tm_year >=3D 100) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[5] |=3D 0x80;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(tim=
e->tm_year - 100);
> + =C2=A0 =C2=A0 =C2=A0 } else {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(tim=
e->tm_year);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_i2c_block_data(client,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 DS3232_REG_SECONDS, 7, buf);
> +}
> +
> +/*
> + * DS3232 has two alarm, we only use alarm1
> + * According to linux specification, only support one-shot alarm
> + * no periodic alarm mode
> + */
> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alar=
m)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
> + =C2=A0 =C2=A0 =C2=A0 stat =3D ret =3D i2c_smbus_read_byte_data(client, =
DS3232_REG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 control =3D ret =3D i2c_smbus_read_byte_data(clien=
t, DS3232_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS32=
32_REG_ALARM1, 4, buf);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
> +
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mon =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_year =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_wday =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_yday =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_isdst =3D -1;
> +
> + =C2=A0 =C2=A0 =C2=A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1IE=
);
> + =C2=A0 =C2=A0 =C2=A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
> +out:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +/*
> + * linux rtc-module does not support wday alarm
> + * and only 24h time mode supported indeed
> + */
> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm=
)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
> +
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq <=3D 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
> +
> + =C2=A0 =C2=A0 =C2=A0 /* clear alarm interrupt enable bit */
> + =C2=A0 =C2=A0 =C2=A0 ret =3D control =3D i2c_smbus_read_byte_data(clien=
t, DS3232_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_=
A2IE);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_R=
EG_CR, control);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* clear any pending alarm flag */
> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
> +
> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F)=
;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_R=
EG_SR, stat);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_i2c_block_data(client,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 DS3232_REG_ALARM1, 4, buf);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (alarm->enabled) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_RE=
G_CR_A1IE;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_writ=
e_byte_data(client, DS3232_REG_CR, control);
> + =C2=A0 =C2=A0 =C2=A0 }
> +out:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D dev_id;
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> +
> + =C2=A0 =C2=A0 =C2=A0 disable_irq_nosync(irq);
> + =C2=A0 =C2=A0 =C2=A0 schedule_work(&ds3232->work);
> + =C2=A0 =C2=A0 =C2=A0 return IRQ_HANDLED;
> +}
> +
> +static void ds3232_work(struct work_struct *work)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D container_of(work, struc=
t ds3232, work);
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D ds3232->client;
> + =C2=A0 =C2=A0 =C2=A0 int stat, control;
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto unlock;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_A1F) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_=
read_byte_data(client, DS3232_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (control < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 goto out;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* disable alarm1 inte=
rrupt */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_=
REG_CR_A1IE);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte_d=
ata(client, DS3232_REG_CR, control);
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* clear the alarm pen=
d flag */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stat &=3D ~DS3232_REG_=
SR_A1F;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte_d=
ata(client, DS3232_REG_SR, stat);
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rtc_update_irq(ds3232-=
>rtc, 1, RTC_AF | RTC_IRQF);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> +out:
> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232->exiting)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable_irq(client->irq=
);
> +unlock:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
> +}
> +
> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
> + =C2=A0 =C2=A0 =C2=A0 .read_time =3D ds3232_read_time,
> + =C2=A0 =C2=A0 =C2=A0 .set_time =3D ds3232_set_time,
> + =C2=A0 =C2=A0 =C2=A0 .read_alarm =3D ds3232_read_alarm,
> + =C2=A0 =C2=A0 =C2=A0 .set_alarm =3D ds3232_set_alarm,
> +};

I sew that you have discarded the .ioctl function, which is used to
enable/disable the alarm irq in previous driver patch.
but,at the same time, you didnot implement .alarm_irq_enable function
, so is there no need to enable or disable the alarm irq bit in
current version patch?

> +
> +static int __devinit ds3232_probe(struct i2c_client *client,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 const struct i2c_devic=
e_id *id)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232;
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 ds3232 =3D kzalloc(sizeof(struct ds3232), GFP_KERN=
EL);
> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -ENOMEM;
> +
> + =C2=A0 =C2=A0 =C2=A0 ds3232->client =3D client;
> + =C2=A0 =C2=A0 =C2=A0 i2c_set_clientdata(client, ds3232);
> +
> + =C2=A0 =C2=A0 =C2=A0 INIT_WORK(&ds3232->work, ds3232_work);
> + =C2=A0 =C2=A0 =C2=A0 mutex_init(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D ds3232_check_rtc_status(client);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out_free;
> +
> + =C2=A0 =C2=A0 =C2=A0 ds3232->rtc =3D rtc_device_register(client->name, =
&client->dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &ds32=
32_rtc_ops, THIS_MODULE);
> + =C2=A0 =C2=A0 =C2=A0 if (IS_ERR(ds3232->rtc)) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D PTR_ERR(ds3232=
->rtc);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&client->dev, =
"unable to register the class device\n");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out_irq;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq >=3D 0) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D request_irq(cl=
ient->irq, ds3232_irq, 0,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"ds3232", client);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 dev_err(&client->dev, "unable to request IRQ\n");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 goto out_free;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +
> +out_irq:
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq >=3D 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 free_irq(client->irq, =
client);
> +
> +out_free:
> + =C2=A0 =C2=A0 =C2=A0 i2c_set_clientdata(client, NULL);
> + =C2=A0 =C2=A0 =C2=A0 kfree(ds3232);
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static int __devexit ds3232_remove(struct i2c_client *client)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq >=3D 0) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mu=
tex);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ds3232->exiting =3D 1;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->=
mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 free_irq(client->irq, =
client);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flush_scheduled_work()=
;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 rtc_device_unregister(ds3232->rtc);
> + =C2=A0 =C2=A0 =C2=A0 i2c_set_clientdata(client, NULL);
> + =C2=A0 =C2=A0 =C2=A0 kfree(ds3232);
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +}
> +
> +static const struct i2c_device_id ds3232_id[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 { "ds3232", 0 },
> + =C2=A0 =C2=A0 =C2=A0 { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ds3232_id);
> +
> +static struct i2c_driver ds3232_driver =3D {
> + =C2=A0 =C2=A0 =C2=A0 .driver =3D {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =3D "rtc-ds3232"=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .owner =3D THIS_MODULE=
,
> + =C2=A0 =C2=A0 =C2=A0 },
> + =C2=A0 =C2=A0 =C2=A0 .probe =3D ds3232_probe,
> + =C2=A0 =C2=A0 =C2=A0 .remove =3D __devexit_p(ds3232_remove),
> + =C2=A0 =C2=A0 =C2=A0 .id_table =3D ds3232_id,
> +};
> +
> +static int __init ds3232_init(void)
> +{
> + =C2=A0 =C2=A0 =C2=A0 return i2c_add_driver(&ds3232_driver);
> +}
> +
> +static void __exit ds3232_exit(void)
> +{
> + =C2=A0 =C2=A0 =C2=A0 i2c_del_driver(&ds3232_driver);
> +}
> +
> +module_init(ds3232_init);
> +module_exit(ds3232_exit);
> +
> +MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>")=
;
> +MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
> +MODULE_LICENSE("GPL");
> --
> 1.5.6.5
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.



--=20
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel@lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

* linux-arm-NUC900 mailing list
mail addr:NUC900@googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com@gmail.com

^ permalink raw reply

* Re: kernel boot stuck at udbg_putc_cpm()
From: Shawn Jin @ 2010-07-12  6:26 UTC (permalink / raw)
  To: Scott Wood; +Cc: ppcdev
In-Reply-To: <20100709105946.68832b3e@schlenkerla.am.freescale.net>

> You're probably getting to the point where udbg is disabled because the
> real serial driver is trying to take over -- and something's going
> wrong with the real serial port driver. =A0Check to make sure the brg
> config is correct (both the input clock and the baud rate you're trying
> to switch to). =A0Commenting out the call to cpm_set_brg can be
> a quick way of determining if that's the problem.

It seems that the last CP command RESTART_TX never completes in the
cpm_uart_console_setup(). I commented out the writes to brgc1 in
cpm_setbrg() in cpm1.c so that the brgc1 value stays the same as
previously set.

The registers related to SMC1 are dumped below before the last
RESTART_TX command. The CPCR was 0x0090. But after issuing the
RESTART_TX command the CPCR kept at 0x0691. Is there any other obvious
reason for CPM not completing the command? It got to be something
related to the settings.

BDI>rd cpcr
cpcr           : 0x0090      144
BDI>rd rccr
rccr           : 0x0001      1
BDI>rd rmds
rmds           : 0x00        0
BDI>rd rctr1
rctr1          : 0x0000      0
BDI>rd rctr2
rctr2          : 0x0000      0
BDI>rd rctr3
rctr3          : 0x802e      -32722
BDI>rd rctr4
rctr4          : 0x802c      -32724
BDI>rd rter
rter           : 0x0000      0
BDI>rd rtmr
rtmr           : 0x0000      0
BDI>rd brgc1
brgc1          : 0x00010618  67096
BDI>rd smcmr1
smcmr1         : 0x4823      18467
BDI>rd smce1
smce1          : 0x00        0
BDI>rd smcm1
smcm1          : 0x00        0

Thanks,
-Shawn.

^ permalink raw reply

* [PATCH v2] rtc: add support for DS3232 RTC
From: Roy Zang @ 2010-07-12  4:36 UTC (permalink / raw)
  To: rtc-linux; +Cc: Mingkai.hu, linuxppc-dev, B11780

This patch adds the driver for RTC chip DS3232 via I2C bus.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
Signed-off-by: Jingchang Lu <b22599@freescale.com>
Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
---
Tested on MPC8536DS and P4080DS board

 drivers/rtc/Kconfig      |   11 ++
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-ds3232.c |  427 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 439 insertions(+), 0 deletions(-)
 create mode 100644 drivers/rtc/rtc-ds3232.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 6a13037..13c2fdb 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -166,6 +166,17 @@ config RTC_DRV_DS1672
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-ds1672.
 
+config RTC_DRV_DS3232
+	tristate "Dallas/Maxim DS3232"
+	depends on RTC_CLASS && I2C
+	help
+	  If you say yes here you get support for Dallas Semiconductor
+	  DS3232 real-time clock chips.  If an interrupt is associated
+	  with the device, the alarm functionality is supported.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called rtc-ds3232.
+
 config RTC_DRV_MAX6900
 	tristate "Maxim MAX6900"
 	help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 44ef194..0af190c 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511)	+= rtc-ds1511.o
 obj-$(CONFIG_RTC_DRV_DS1553)	+= rtc-ds1553.o
 obj-$(CONFIG_RTC_DRV_DS1672)	+= rtc-ds1672.o
 obj-$(CONFIG_RTC_DRV_DS1742)	+= rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS3232)	+= rtc-ds3232.o
 obj-$(CONFIG_RTC_DRV_DS3234)	+= rtc-ds3234.o
 obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o
 obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 0000000..e36ec1c
--- /dev/null
+++ b/drivers/rtc/rtc-ds3232.c
@@ -0,0 +1,427 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2010 Freescale Semiconductor.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS	0x00
+#define DS3232_REG_MINUTES	0x01
+#define DS3232_REG_HOURS	0x02
+#define DS3232_REG_AMPM		0x02
+#define DS3232_REG_DAY		0x03
+#define DS3232_REG_DATE		0x04
+#define DS3232_REG_MONTH	0x05
+#define DS3232_REG_CENTURY	0x05
+#define DS3232_REG_YEAR		0x06
+#define DS3232_REG_ALARM1         0x07	/* Alarm 1 BASE */
+#define DS3232_REG_ALARM2         0x0B	/* Alarm 2 BASE */
+#define DS3232_REG_CR		0x0E	/* Control register */
+#	define DS3232_REG_CR_nEOSC        0x80
+#       define DS3232_REG_CR_INTCN        0x04
+#       define DS3232_REG_CR_A2IE        0x02
+#       define DS3232_REG_CR_A1IE        0x01
+
+#define DS3232_REG_SR	0x0F	/* control/status register */
+#	define DS3232_REG_SR_OSF   0x80
+#       define DS3232_REG_SR_BSY   0x04
+#       define DS3232_REG_SR_A2F   0x02
+#       define DS3232_REG_SR_A1F   0x01
+
+struct ds3232 {
+	struct i2c_client *client;
+	struct rtc_device *rtc;
+	struct work_struct work;
+
+	/* The mutex protects alarm operations, and prevents a race
+	 * between the enable_irq() in the workqueue and the free_irq()
+	 * in the remove function.
+	 */
+	struct mutex mutex;
+	int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+	int ret = 0;
+	int control, stat;
+
+	stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+	if (stat < 0)
+		return stat;
+
+	if (stat & DS3232_REG_SR_OSF)
+		dev_warn(&client->dev,
+				"oscillator discontinuity flagged, "
+				"time unreliable\n");
+
+	stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+	ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+	if (ret < 0)
+		return ret;
+
+	/* If the alarm is pending, clear it before requesting
+	 * the interrupt, so an interrupt event isn't reported
+	 * before everything is initialized.
+	 */
+
+	control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+	if (control < 0)
+		return control;
+
+	control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+	control |= DS3232_REG_CR_INTCN;
+
+	return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int ret;
+	u8 buf[7];
+	unsigned int year, month, day, hour, minute, second;
+	unsigned int week, twelve_hr, am_pm;
+	unsigned int century, add_century = 0;
+
+	ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+	if (ret < 0)
+		return ret;
+	if (ret < 7)
+		return -EIO;
+
+	second = buf[0];
+	minute = buf[1];
+	hour = buf[2];
+	week = buf[3];
+	day = buf[4];
+	month = buf[5];
+	year = buf[6];
+
+	/* Extract additional information for AM/PM and century */
+
+	twelve_hr = hour & 0x40;
+	am_pm = hour & 0x20;
+	century = month & 0x80;
+
+	/* Write to rtc_time structure */
+
+	time->tm_sec = bcd2bin(second);
+	time->tm_min = bcd2bin(minute);
+	if (twelve_hr) {
+		/* Convert to 24 hr */
+		if (am_pm)
+			time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+		else
+			time->tm_hour = bcd2bin(hour & 0x1F);
+	} else {
+		time->tm_hour = bcd2bin(hour);
+	}
+
+	time->tm_wday = bcd2bin(week);
+	time->tm_mday = bcd2bin(day);
+	time->tm_mon = bcd2bin(month & 0x7F);
+	if (century)
+		add_century = 100;
+
+	time->tm_year = bcd2bin(year) + add_century;
+
+	return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 buf[7];
+
+	/* Extract time from rtc_time and load into ds3232*/
+
+	buf[0] = bin2bcd(time->tm_sec);
+	buf[1] = bin2bcd(time->tm_min);
+	buf[2] = bin2bcd(time->tm_hour);
+	buf[3] = bin2bcd(time->tm_wday); /* Day of the week */
+	buf[4] = bin2bcd(time->tm_mday); /* Date */
+	buf[5] = bin2bcd(time->tm_mon);
+	if (time->tm_year >= 100) {
+		buf[5] |= 0x80;
+		buf[6] = bin2bcd(time->tm_year - 100);
+	} else {
+		buf[6] = bin2bcd(time->tm_year);
+	}
+
+	return i2c_smbus_write_i2c_block_data(client,
+					      DS3232_REG_SECONDS, 7, buf);
+}
+
+/*
+ * DS3232 has two alarm, we only use alarm1
+ * According to linux specification, only support one-shot alarm
+ * no periodic alarm mode
+ */
+static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ds3232 *ds3232 = i2c_get_clientdata(client);
+	int control, stat;
+	int ret = 0;
+	u8 buf[4];
+
+	mutex_lock(&ds3232->mutex);
+	stat = ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+	if (stat < 0)
+		goto out;
+
+	control = ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+	if (control < 0)
+		goto out;
+
+	ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+	if (ret < 0)
+		goto out;
+
+	alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
+	alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
+	alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F);
+	alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F);
+
+	alarm->time.tm_mon = -1;
+	alarm->time.tm_year = -1;
+	alarm->time.tm_wday = -1;
+	alarm->time.tm_yday = -1;
+	alarm->time.tm_isdst = -1;
+
+	alarm->enabled = !!(control & DS3232_REG_CR_A1IE);
+	alarm->pending = !!(stat & DS3232_REG_SR_A1F);
+out:
+	mutex_unlock(&ds3232->mutex);
+
+	return ret;
+}
+
+/*
+ * linux rtc-module does not support wday alarm
+ * and only 24h time mode supported indeed
+ */
+static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ds3232 *ds3232 = i2c_get_clientdata(client);
+	int control, stat;
+	int ret = 0;
+	u8 buf[4];
+
+	if (client->irq <= 0)
+		return -EINVAL;
+
+	mutex_lock(&ds3232->mutex);
+
+	buf[0] = bin2bcd(alarm->time.tm_sec);
+	buf[1] = bin2bcd(alarm->time.tm_min);
+	buf[2] = bin2bcd(alarm->time.tm_hour);
+	buf[3] = bin2bcd(alarm->time.tm_mday);
+
+	/* clear alarm interrupt enable bit */
+	ret = control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+	if (ret < 0)
+		goto out;
+	control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+	ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+	if (ret < 0)
+		goto out;
+
+	/* clear any pending alarm flag */
+	stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+	if (stat < 0)
+		return stat;
+
+	stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+	ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_write_i2c_block_data(client,
+						DS3232_REG_ALARM1, 4, buf);
+
+	if (alarm->enabled) {
+		control |= DS3232_REG_CR_A1IE;
+		ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+	}
+out:
+	mutex_unlock(&ds3232->mutex);
+	return ret;
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+	struct i2c_client *client = dev_id;
+	struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+	disable_irq_nosync(irq);
+	schedule_work(&ds3232->work);
+	return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+	struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+	struct i2c_client *client = ds3232->client;
+	int stat, control;
+
+	mutex_lock(&ds3232->mutex);
+
+	stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+	if (stat < 0)
+		goto unlock;
+
+	if (stat & DS3232_REG_SR_A1F) {
+		control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+		if (control < 0)
+			goto out;
+		/* disable alarm1 interrupt */
+		control &= ~(DS3232_REG_CR_A1IE);
+		i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+		/* clear the alarm pend flag */
+		stat &= ~DS3232_REG_SR_A1F;
+		i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+		rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+	}
+
+out:
+	if (!ds3232->exiting)
+		enable_irq(client->irq);
+unlock:
+	mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+	.read_time = ds3232_read_time,
+	.set_time = ds3232_set_time,
+	.read_alarm = ds3232_read_alarm,
+	.set_alarm = ds3232_set_alarm,
+};
+
+static int __devinit ds3232_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ds3232 *ds3232;
+	int ret;
+
+	ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL);
+	if (!ds3232)
+		return -ENOMEM;
+
+	ds3232->client = client;
+	i2c_set_clientdata(client, ds3232);
+
+	INIT_WORK(&ds3232->work, ds3232_work);
+	mutex_init(&ds3232->mutex);
+
+	ret = ds3232_check_rtc_status(client);
+	if (ret)
+		goto out_free;
+
+	ds3232->rtc = rtc_device_register(client->name, &client->dev,
+					  &ds3232_rtc_ops, THIS_MODULE);
+	if (IS_ERR(ds3232->rtc)) {
+		ret = PTR_ERR(ds3232->rtc);
+		dev_err(&client->dev, "unable to register the class device\n");
+		goto out_irq;
+	}
+
+	if (client->irq >= 0) {
+		ret = request_irq(client->irq, ds3232_irq, 0,
+				 "ds3232", client);
+		if (ret) {
+			dev_err(&client->dev, "unable to request IRQ\n");
+			goto out_free;
+		}
+	}
+
+	return 0;
+
+out_irq:
+	if (client->irq >= 0)
+		free_irq(client->irq, client);
+
+out_free:
+	i2c_set_clientdata(client, NULL);
+	kfree(ds3232);
+	return ret;
+}
+
+static int __devexit ds3232_remove(struct i2c_client *client)
+{
+	struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+	if (client->irq >= 0) {
+		mutex_lock(&ds3232->mutex);
+		ds3232->exiting = 1;
+		mutex_unlock(&ds3232->mutex);
+
+		free_irq(client->irq, client);
+		flush_scheduled_work();
+	}
+
+	rtc_device_unregister(ds3232->rtc);
+	i2c_set_clientdata(client, NULL);
+	kfree(ds3232);
+	return 0;
+}
+
+static const struct i2c_device_id ds3232_id[] = {
+	{ "ds3232", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+	.driver = {
+		.name = "rtc-ds3232",
+		.owner = THIS_MODULE,
+	},
+	.probe = ds3232_probe,
+	.remove = __devexit_p(ds3232_remove),
+	.id_table = ds3232_id,
+};
+
+static int __init ds3232_init(void)
+{
+	return i2c_add_driver(&ds3232_driver);
+}
+
+static void __exit ds3232_exit(void)
+{
+	i2c_del_driver(&ds3232_driver);
+}
+
+module_init(ds3232_init);
+module_exit(ds3232_exit);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
-- 
1.5.6.5

^ permalink raw reply related

* [PATCH 00/12] autoconvert trivial BKL users to private mutex
From: Arnd Bergmann @ 2010-07-11 21:18 UTC (permalink / raw)
  To: linux-kernel
  Cc: devel, Jesper Nilsson, linux-usb, Arnd Bergmann, Corey Minyard,
	linux-cris-kernel, Frederic Weisbecker, Greg Kroah-Hartman,
	Karsten Keil, Mauro Carvalho Chehab, linux-scsi, linuxppc-dev,
	James E.J. Bottomley, linux-mtd, John Kacur, netdev, linux-media,
	openipmi-developer, David S. Miller, David Woodhouse

This is a repost of an earlier patch to remove
those users of the big kernel lock that can be
converted to a mutex using a simple script.

The only use of the BKL is in file operations
that are called without any other lock, so the
new mutex is the top-level serialization
and cannot introduce any AB-BA deadlock.

Please apply to the respective maintainer trees
if the patches look good.

Arnd Bergmann (12):
  staging: autoconvert trivial BKL users to private mutex
  isdn: autoconvert trivial BKL users to private mutex
  scsi: autoconvert trivial BKL users to private mutex
  media: autoconvert trivial BKL users to private mutex
  usb: autoconvert trivial BKL users to private mutex
  net: autoconvert trivial BKL users to private mutex
  cris: autoconvert trivial BKL users to private mutex
  sbus: autoconvert trivial BKL users to private mutex
  mtd: autoconvert trivial BKL users to private mutex
  mac: autoconvert trivial BKL users to private mutex
  ipmi: autoconvert trivial BKL users to private mutex
  drivers: autoconvert trivial BKL users to private mutex

 arch/cris/arch-v10/drivers/eeprom.c        |    2 -
 arch/cris/arch-v10/drivers/i2c.c           |    2 -
 arch/cris/arch-v32/drivers/cryptocop.c     |    2 -
 arch/cris/arch-v32/drivers/i2c.c           |   12 ++++----
 drivers/block/paride/pg.c                  |    7 ++--
 drivers/block/paride/pt.c                  |   19 ++++++------
 drivers/char/apm-emulation.c               |   11 ++++---
 drivers/char/applicom.c                    |    9 +++--
 drivers/char/ds1302.c                      |   15 +++++----
 drivers/char/ds1620.c                      |    8 ++--
 drivers/char/dsp56k.c                      |   27 +++++++++--------
 drivers/char/dtlk.c                        |    8 ++--
 drivers/char/generic_nvram.c               |    7 ++--
 drivers/char/genrtc.c                      |   13 ++++----
 drivers/char/i8k.c                         |    7 ++--
 drivers/char/ip2/ip2main.c                 |    8 ++--
 drivers/char/ipmi/ipmi_devintf.c           |   14 ++++----
 drivers/char/ipmi/ipmi_watchdog.c          |    8 ++--
 drivers/char/lp.c                          |   15 +++++----
 drivers/char/mbcs.c                        |    8 ++--
 drivers/char/mmtimer.c                     |    7 ++--
 drivers/char/mwave/mwavedd.c               |   44 ++++++++++++++--------------
 drivers/char/nvram.c                       |   11 ++++---
 drivers/char/nwflash.c                     |   12 ++++----
 drivers/char/pcmcia/cm4000_cs.c            |   11 ++++---
 drivers/char/pcmcia/cm4040_cs.c            |    7 ++--
 drivers/char/ppdev.c                       |    8 ++--
 drivers/char/rio/rio_linux.c               |    7 ++--
 drivers/char/snsc.c                        |    9 +++--
 drivers/char/toshiba.c                     |    9 +++--
 drivers/char/viotape.c                     |   11 ++++---
 drivers/char/xilinx_hwicap/xilinx_hwicap.c |    6 ++--
 drivers/hwmon/fschmd.c                     |    6 ++--
 drivers/hwmon/w83793.c                     |    6 ++--
 drivers/input/misc/hp_sdc_rtc.c            |    7 ++--
 drivers/isdn/capi/capi.c                   |    6 ++--
 drivers/isdn/divert/divert_procfs.c        |    7 ++--
 drivers/isdn/hardware/eicon/divamnt.c      |    7 ++--
 drivers/isdn/hardware/eicon/divasi.c       |    2 -
 drivers/isdn/hardware/eicon/divasmain.c    |    2 -
 drivers/isdn/hysdn/hysdn_procconf.c        |   21 +++++++------
 drivers/isdn/hysdn/hysdn_proclog.c         |   15 +++++----
 drivers/isdn/i4l/isdn_common.c             |   27 +++++++++--------
 drivers/isdn/mISDN/timerdev.c              |    7 ++--
 drivers/macintosh/adb.c                    |   10 +++---
 drivers/macintosh/smu.c                    |    6 ++--
 drivers/macintosh/via-pmu.c                |   11 ++++---
 drivers/media/dvb/bt8xx/dst_ca.c           |    7 ++--
 drivers/media/video/cx88/cx88-blackbird.c  |   13 ++++----
 drivers/media/video/dabusb.c               |   18 ++++++------
 drivers/media/video/se401.c                |    9 +++--
 drivers/media/video/stradis.c              |    9 +++--
 drivers/media/video/usbvideo/vicam.c       |   14 ++++----
 drivers/message/fusion/mptctl.c            |   15 +++++----
 drivers/message/i2o/i2o_config.c           |   23 +++++++-------
 drivers/misc/phantom.c                     |   11 ++++---
 drivers/mtd/mtdchar.c                      |   15 +++++----
 drivers/net/ppp_generic.c                  |   19 ++++++------
 drivers/net/wan/cosa.c                     |   10 +++---
 drivers/pci/hotplug/cpqphp_sysfs.c         |   13 ++++----
 drivers/rtc/rtc-m41t80.c                   |   13 ++++----
 drivers/sbus/char/display7seg.c            |    8 ++--
 drivers/sbus/char/envctrl.c                |    2 -
 drivers/sbus/char/flash.c                  |   15 +++++----
 drivers/sbus/char/openprom.c               |   15 +++++----
 drivers/sbus/char/uctrl.c                  |    7 ++--
 drivers/scsi/3w-9xxx.c                     |    7 ++--
 drivers/scsi/3w-sas.c                      |    7 ++--
 drivers/scsi/3w-xxxx.c                     |    9 ++---
 drivers/scsi/aacraid/linit.c               |   15 +++++----
 drivers/scsi/ch.c                          |    8 ++--
 drivers/scsi/dpt_i2o.c                     |   18 ++++++------
 drivers/scsi/gdth.c                        |   11 ++++---
 drivers/scsi/megaraid.c                    |    8 ++--
 drivers/scsi/megaraid/megaraid_mm.c        |    8 ++--
 drivers/scsi/megaraid/megaraid_sas.c       |    2 -
 drivers/scsi/mpt2sas/mpt2sas_ctl.c         |   11 ++++---
 drivers/scsi/osst.c                        |   15 +++++----
 drivers/scsi/scsi_tgt_if.c                 |    2 -
 drivers/scsi/sg.c                          |   11 ++++---
 drivers/staging/crystalhd/crystalhd_lnx.c  |    9 +++--
 drivers/staging/dt3155/dt3155_drv.c        |    6 ++-
 drivers/staging/vme/devices/vme_user.c     |    7 ++--
 drivers/telephony/ixj.c                    |    7 ++--
 drivers/usb/gadget/printer.c               |    7 ++--
 drivers/usb/misc/iowarrior.c               |   15 +++++----
 drivers/usb/misc/rio500.c                  |   15 +++++----
 drivers/usb/misc/usblcd.c                  |   16 +++++-----
 drivers/watchdog/cpwd.c                    |   15 +++++----
 fs/hfsplus/ioctl.c                         |   11 ++++---
 net/wanrouter/wanmain.c                    |    7 ++--
 net/wanrouter/wanproc.c                    |    7 ++--
 92 files changed, 505 insertions(+), 469 deletions(-)

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Corey Minyard <minyard@acm.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: David Woodhouse <David.Woodhouse@intel.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: "James E.J. Bottomley" <James.Bottomley@suse.de>
Cc: Jesper Nilsson <jesper.nilsson@axis.com>
Cc: Karsten Keil <isdn@linux-pingi.de>
Cc: Mauro Carvalho Chehab <mchehab@infradead.org>
Cc: netdev@vger.kernel.org
Cc: openipmi-developer@lists.sourceforge.net
Cc: devel@driverdev.osuosl.org
Cc: linux-cris-kernel@axis.com
Cc: linux-media@vger.kernel.org
Cc: linux-mtd@lists.infradead.org
Cc: linuxppc-dev@ozlabs.org
Cc: linux-scsi@vger.kernel.org
Cc: linux-usb@vger.kernel.org

^ permalink raw reply

* [PATCH 10/12] mac: autoconvert trivial BKL users to private mutex
From: Arnd Bergmann @ 2010-07-11 21:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: John Kacur, Frederic Weisbecker, linuxppc-dev, Arnd Bergmann
In-Reply-To: <1278883143-29035-1-git-send-email-arnd@arndb.de>

All these files use the big kernel lock in a trivial
way to serialize their private file operations,
typically resulting from an earlier semi-automatic
pushdown from VFS.

None of these drivers appears to want to lock against
other code, and they all use the BKL as the top-level
lock in their file operations, meaning that there
is no lock-order inversion problem.

Consequently, we can remove the BKL completely,
replacing it with a per-file mutex in every case.
Using a scripted approach means we can avoid
typos.

file=$1
name=$2
if grep -q lock_kernel ${file} ; then
    if grep -q 'include.*linux.mutex.h' ${file} ; then
            sed -i '/include.*<linux\/smp_lock.h>/d' ${file}
    else
            sed -i 's/include.*<linux\/smp_lock.h>.*$/include <linux\/mutex.h>/g' ${file}
    fi
    sed -i ${file} \
        -e "/^#include.*linux.mutex.h/,$ {
                1,/^\(static\|int\|long\)/ {
                     /^\(static\|int\|long\)/istatic DEFINE_MUTEX(${name}_mutex);

} }"  \
    -e "s/\(un\)*lock_kernel\>[ ]*()/mutex_\1lock(\&${name}_mutex)/g" \
    -e '/[      ]*cycle_kernel_lock();/d'
else
    sed -i -e '/include.*\<smp_lock.h\>/d' ${file}  \
                -e '/cycle_kernel_lock()/d'
fi

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: linuxppc-dev@ozlabs.org
---
 drivers/macintosh/adb.c     |   10 +++++-----
 drivers/macintosh/smu.c     |    6 +++---
 drivers/macintosh/via-pmu.c |   11 ++++++-----
 3 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 1c4ee6e..e75e3be 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -24,7 +24,6 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
-#include <linux/smp_lock.h>
 #include <linux/adb.h>
 #include <linux/cuda.h>
 #include <linux/pmu.h>
@@ -55,6 +54,7 @@ extern struct adb_driver adb_iop_driver;
 extern struct adb_driver via_pmu_driver;
 extern struct adb_driver macio_adb_driver;
 
+static DEFINE_MUTEX(adb_mutex);
 static struct adb_driver *adb_driver_list[] = {
 #ifdef CONFIG_ADB_MACII
 	&via_macii_driver,
@@ -647,7 +647,7 @@ static int adb_open(struct inode *inode, struct file *file)
 	struct adbdev_state *state;
 	int ret = 0;
 
-	lock_kernel();
+	mutex_lock(&adb_mutex);
 	if (iminor(inode) > 0 || adb_controller == NULL) {
 		ret = -ENXIO;
 		goto out;
@@ -665,7 +665,7 @@ static int adb_open(struct inode *inode, struct file *file)
 	state->inuse = 1;
 
 out:
-	unlock_kernel();
+	mutex_unlock(&adb_mutex);
 	return ret;
 }
 
@@ -674,7 +674,7 @@ static int adb_release(struct inode *inode, struct file *file)
 	struct adbdev_state *state = file->private_data;
 	unsigned long flags;
 
-	lock_kernel();
+	mutex_lock(&adb_mutex);
 	if (state) {
 		file->private_data = NULL;
 		spin_lock_irqsave(&state->lock, flags);
@@ -687,7 +687,7 @@ static int adb_release(struct inode *inode, struct file *file)
 			spin_unlock_irqrestore(&state->lock, flags);
 		}
 	}
-	unlock_kernel();
+	mutex_unlock(&adb_mutex);
 	return 0;
 }
 
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 2506c95..8775dd4 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -19,7 +19,6 @@
  *    the userland interface
  */
 
-#include <linux/smp_lock.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
@@ -97,6 +96,7 @@ struct smu_device {
  * I don't think there will ever be more than one SMU, so
  * for now, just hard code that
  */
+static DEFINE_MUTEX(smu_mutex);
 static struct smu_device	*smu;
 static DEFINE_MUTEX(smu_part_access);
 static int smu_irq_inited;
@@ -1095,12 +1095,12 @@ static int smu_open(struct inode *inode, struct file *file)
 	pp->mode = smu_file_commands;
 	init_waitqueue_head(&pp->wait);
 
-	lock_kernel();
+	mutex_lock(&smu_mutex);
 	spin_lock_irqsave(&smu_clist_lock, flags);
 	list_add(&pp->list, &smu_clist);
 	spin_unlock_irqrestore(&smu_clist_lock, flags);
 	file->private_data = pp;
-	unlock_kernel();
+	mutex_unlock(&smu_mutex);
 
 	return 0;
 }
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 3d4fc0f..28e4822 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -18,7 +18,7 @@
  *
  */
 #include <stdarg.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -72,6 +72,7 @@
 /* How many iterations between battery polls */
 #define BATTERY_POLLING_COUNT	2
 
+static DEFINE_MUTEX(pmu_info_proc_mutex);
 static volatile unsigned char __iomem *via;
 
 /* VIA registers - spaced 0x200 bytes apart */
@@ -2076,7 +2077,7 @@ pmu_open(struct inode *inode, struct file *file)
 	pp->rb_get = pp->rb_put = 0;
 	spin_lock_init(&pp->lock);
 	init_waitqueue_head(&pp->wait);
-	lock_kernel();
+	mutex_lock(&pmu_info_proc_mutex);
 	spin_lock_irqsave(&all_pvt_lock, flags);
 #if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
 	pp->backlight_locker = 0;
@@ -2084,7 +2085,7 @@ pmu_open(struct inode *inode, struct file *file)
 	list_add(&pp->list, &all_pmu_pvt);
 	spin_unlock_irqrestore(&all_pvt_lock, flags);
 	file->private_data = pp;
-	unlock_kernel();
+	mutex_unlock(&pmu_info_proc_mutex);
 	return 0;
 }
 
@@ -2341,9 +2342,9 @@ static long pmu_unlocked_ioctl(struct file *filp,
 {
 	int ret;
 
-	lock_kernel();
+	mutex_lock(&pmu_info_proc_mutex);
 	ret = pmu_ioctl(filp, cmd, arg);
-	unlock_kernel();
+	mutex_unlock(&pmu_info_proc_mutex);
 
 	return ret;
 }
-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH 1/2] edac: mpc85xx: Fix MPC85xx dependency
From: Kumar Gala @ 2010-07-11 16:20 UTC (permalink / raw)
  To: Anton Vorontsov
  Cc: Peter Tyser, linux-kernel, Dave Jiang, linuxppc-dev,
	Doug Thompson, Andrew Morton
In-Reply-To: <20100708045627.GA32489@oksana.dev.rtsoft.ru>


On Jul 7, 2010, at 11:56 PM, Anton Vorontsov wrote:

> On Wed, Jul 07, 2010 at 02:45:02PM -0700, Andrew Morton wrote:
>> On Fri, 2 Jul 2010 16:41:11 +0400
>> Anton Vorontsov <avorontsov@mvista.com> wrote:
>> 
>>> Since commit 5753c082f66eca5be81f6bda85c1718c5eea6ada ("powerpc/85xx:
>>> Kconfig cleanup"), there is no MPC85xx Kconfig symbol anymore, so the
>>> driver became non-selectable.
>> 
>> hm.  5753c082f66eca5be81f6bda85c1718c5eea6ada got merged into mainline
>> six months ago.  How come nobody noticed?
> 
> Dunno. Well, it's hard to notice these sorts of things until
> somebody actually needs this driver on MPC85xx platform. :-)
> 
>>> This patch fixes the issue by switching to PPC_85xx symbol.
>>> 
>>> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
>>> ---
> [...]
>>> -	depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || MPC85xx)
>>> +	depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || PPC_85xx)
>>> 	help
>>> 	  Support for error detection and correction on the Freescale
>>> 	  MPC8349, MPC8560, MPC8540, MPC8548
>> 
>> I suppose we shold scoot this into 2.6.35 and mark it for -stable
>> backporting.  All very odd.
> 
> Yeah, -stable 2.6.{33,34} sounds good.
> 
> Thanks.

we should add this to mpc85xx_defconfig so we build by default.

- k

^ permalink raw reply

* [git pull] Please pull powerpc.git merge branch
From: Kumar Gala @ 2010-07-11 16:18 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev

* a few build fixes and a bug fix for 2.6.35

The following changes since
commit e467e104bb7482170b79f516d2025e7cfcaaa733:
  Linus Torvalds (1):
        Merge branch 'for-linus' of git://git.kernel.org/.../roland/infiniband

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/galak/powerpc.git merge

Anton Vorontsov (3):
      powerpc/cpm: Reintroduce global spi_pram struct (fixes build issue)
      powerpc/cpm1: Fix build with various CONFIG_*_UCODE_PATCH combinations
      powerpc/cpm1: Mark micropatch code/data static and __init

Matthew McClintock (1):
      powerpc/fsl-booke: Fix address issue when using relocatable kernels

 arch/powerpc/include/asm/cpm.h                |   24 ++++++++++++++++++++
 arch/powerpc/include/asm/cpm1.h               |    3 +-
 arch/powerpc/kernel/fsl_booke_entry_mapping.S |    4 +--
 arch/powerpc/sysdev/micropatch.c              |   30 +++++++++++++++----------
 drivers/spi/spi_mpc8xxx.c                     |   22 ------------------
 5 files changed, 45 insertions(+), 38 deletions(-)

^ permalink raw reply

* Re: [PATCH 3/3] powerpc/cpm1: Mark micropatch code/data static and __init
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: LEROY Christophe, linuxppc-dev, Scott Wood
In-Reply-To: <20100708171616.GC11621@oksana.dev.rtsoft.ru>


On Jul 8, 2010, at 12:16 PM, Anton Vorontsov wrote:

> This saves runtime memory and fixes lots of sparse warnings like this:
> 
>    CHECK   arch/powerpc/sysdev/micropatch.c
>  arch/powerpc/sysdev/micropatch.c:27:6: warning: symbol 'patch_2000'
>  was not declared. Should it be static?
>  arch/powerpc/sysdev/micropatch.c:146:6: warning: symbol 'patch_2f00'
>  was not declared. Should it be static?
>  ...
> 
> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
> ---
> arch/powerpc/include/asm/cpm1.h  |    3 ++-
> arch/powerpc/sysdev/micropatch.c |   18 +++++++++---------
> 2 files changed, 11 insertions(+), 10 deletions(-)

applied to merge

- k

^ permalink raw reply

* Re: [PATCH] powerpc/fsl-booke: Fix address issue when using relocatable kernels
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Matthew McClintock; +Cc: linuxppc-dev
In-Reply-To: <1277840527-18977-1-git-send-email-msm@freescale.com>


On Jun 29, 2010, at 2:42 PM, Matthew McClintock wrote:

> When booting a relocatable kernel it needs to jump to the correct
> start address, which for BookE parts is usually unchanged
> regardless of the physical memory offset.
> 
> Recent changes cause problems with how we calculate the start
> address, it was always adding the RMO into the start address
> which is incorrect. This patch only adds in the RMO offset
> if we are in the kexec code path, as it needs the RMO to work
> correctly.
> 
> Instead of adding the RMO offset in in the common code path, we
> can just set r6 to the RMO offset in the kexec code path instead
> of to zero, and finally perform the masking in the common code
> path
> 
> Signed-off-by: Matthew McClintock <msm@freescale.com>
> ---
> arch/powerpc/kernel/fsl_booke_entry_mapping.S |    4 +---
> 1 files changed, 1 insertions(+), 3 deletions(-)

applied to merge

- k

^ permalink raw reply

* Re: [PATCH 2/3] powerpc/cpm1: Fix build with various CONFIG_*_UCODE_PATCH combinations
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: LEROY Christophe, linuxppc-dev, Scott Wood
In-Reply-To: <20100708171614.GB11621@oksana.dev.rtsoft.ru>


On Jul 8, 2010, at 12:16 PM, Anton Vorontsov wrote:

> Warnings are treated as errors for arch/powerpc code, so build fails
> with CONFIG_I2C_SPI_UCODE_PATCH=y:
> 
>    CC      arch/powerpc/sysdev/micropatch.o
>  cc1: warnings being treated as errors
>  arch/powerpc/sysdev/micropatch.c: In function 'cpm_load_patch':
>  arch/powerpc/sysdev/micropatch.c:630: warning: unused variable 'smp'
>  make[1]: *** [arch/powerpc/sysdev/micropatch.o] Error 1
> 
> And with CONFIG_USB_SOF_UCODE_PATCH=y:
> 
>  CC      arch/powerpc/sysdev/micropatch.o
>  cc1: warnings being treated as errors
>  arch/powerpc/sysdev/micropatch.c: In function 'cpm_load_patch':
>  arch/powerpc/sysdev/micropatch.c:629: warning: unused variable 'spp'
>  arch/powerpc/sysdev/micropatch.c:628: warning: unused variable 'iip'
>  make[1]: *** [arch/powerpc/sysdev/micropatch.o] Error 1
> 
> This patch fixes these issues by introducing proper #ifdefs.
> 
> Cc: <stable@kernel.org> [ .33, .34 ]
> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
> ---
> arch/powerpc/sysdev/micropatch.c |    5 +++++
> 1 files changed, 5 insertions(+), 0 deletions(-)

applied to merge

- k

^ permalink raw reply

* Re: [PATCH 1/3] powerpc/cpm: Reintroduce global spi_pram struct (fixes build issue)
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: LEROY Christophe, linuxppc-dev, Scott Wood
In-Reply-To: <20100708171610.GA11621@oksana.dev.rtsoft.ru>


On Jul 8, 2010, at 12:16 PM, Anton Vorontsov wrote:

> spi_t was removed in commit 644b2a680ccc51a9ec4d6beb12e9d47d2dee98e2
> ("powerpc/cpm: Remove SPI defines and spi structs"), the commit =
assumed
> that spi_t isn't used anywhere outside of the spi_mpc8xxx driver. But
> it appears that the struct is needed for micropatch code. So, let's
> reintroduce the struct.
>=20
> Fixes the following build issue:
>=20
>    CC      arch/powerpc/sysdev/micropatch.o
>  micropatch.c: In function 'cpm_load_patch':
>  micropatch.c:629: error: expected '=3D', ',', ';', 'asm' or =
'__attribute__' before '*' token
>  micropatch.c:629: error: 'spp' undeclared (first use in this =
function)
>  micropatch.c:629: error: (Each undeclared identifier is reported only =
once
>  micropatch.c:629: error: for each function it appears in.)
>=20
> Reported-by: LEROY Christophe <christophe.leroy@c-s.fr>
> Reported-by: Tony Breeds <tony@bakeyournoodle.com>
> Cc: <stable@kernel.org> [ .33, .34 ]
> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
> ---

applied to merge

- k=

^ permalink raw reply

* C67x00 USB Host
From: A C @ 2010-07-11 13:26 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 695 bytes --]

Hello,
I am currently trying to use the C67x00 driver in the vanilla branch of
Linux. I use it in coprocessor hpi mode (GPIO30 and GPIO31 =0) There is
something that I do not understand in the code.
In the probe() function there is a call to the function hpi_reg_init(). In
this function the interrupts are no more routed from HPI.
Then, in the probe function, the ll_reset() is called, there is a send in
the mailbox, and the response should be an interupt. But I do not receveive
any interrupt. I think it is normal because as I said they are no more
routed from the HPI. So I think I miss something.
Someone could tell me where the interrupt are routed towards HPI ?
Thanks by advance
Adrien

[-- Attachment #2: Type: text/html, Size: 742 bytes --]

^ permalink raw reply

* Re: [RT,RFC] Hacks allowing -rt to run on POWER7 / Powerpc.
From: Milton Miller @ 2010-07-11  7:49 UTC (permalink / raw)
  To: Will Schmidt; +Cc: Darren Hart, rt-users, LKML, linuxppc-dev, Thomas Gleixner
In-Reply-To: <1278701701.24737.19.camel@lexx>


On Fri, 09 Jul 2010 about 08:55:01 -0000, Will Schmidt wrote:
> We've been seeing some issues with userspace randomly SIGSEGV'ing while
> running the -RT kernels on POWER7 based systems.   After lots of
> debugging, head scratching, and experimental changes to the code, the
> problem has been narrowed down such that we can avoid the problems by
> disabling the TLB batching.
> 
> After some input from Ben and further debug, we've found that the
> restoration of the batch->active value near the end of __switch_to()
> seems to be the key.    ( The -RT related  changes within
> arch/powerpc/kernel/processor.c __switch_to()  do the equivalent of a
> arch_leave_lazy_mmu_mode() before calling _switch, use a hadbatch flag
> to indicate if batching was active, and then restore that batch->active
> value on the way out after the call to _switch_to.    That particular
> code is in the -RT branch, and not found in mainline  )
> 
> Deferring to Ben (or others in the know) for whether this is the proper
> solution or if there is something deeper, but.. 


I looked at the patch and noticed 2 changes:
	1) the batch is checked and cleared after local_irq_save
	2) enabling the batch is skipped

I talked to Will and had him try moving the local_irq_save above the
check for the active batch.  That alone did not seem to be enough.
However, he confirmed that we are setting batch to active when it is
already active in lazy_mmu_enter, meaning that batching is being turned
on recursively.  I suggested debug to check that irqs are off after the
restore when re-enabling when our debug session timed out.

milton

> 
> diff -aurp linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c
> --- linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c	2010-06-21 11:41:34.402513904 -0500
> +++ linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c	2010-07-09 13:15:13.533269904 -0500
> @@ -304,10 +304,6 @@ struct task_struct *__switch_to(struct t
>  	struct thread_struct *new_thread, *old_thread;
>  	unsigned long flags;
>  	struct task_struct *last;
> -#if defined(CONFIG_PPC64) && defined (CONFIG_PREEMPT_RT)
> -	struct ppc64_tlb_batch *batch;
> -	int hadbatch;
> -#endif
>  
>  #ifdef CONFIG_SMP
>  	/* avoid complexity of lazy save/restore of fpu
> @@ -401,16 +397,6 @@ struct task_struct *__switch_to(struct t
>  		new_thread->start_tb = current_tb;
>  	}
>  
> -#ifdef CONFIG_PREEMPT_RT
> -	batch = &__get_cpu_var(ppc64_tlb_batch);
> -	if (batch->active) {
> -		hadbatch = 1;
> -		if (batch->index) {
> -			__flush_tlb_pending(batch);
> -		}
> -		batch->active = 0;
> -	}
> -#endif /* #ifdef CONFIG_PREEMPT_RT */
>  #endif
>  
>  	local_irq_save(flags);
> @@ -425,16 +411,13 @@ struct task_struct *__switch_to(struct t
>  	 * of sync. Hard disable here.
>  	 */
>  	hard_irq_disable();
> -	last = _switch(old_thread, new_thread);
> -
> -	local_irq_restore(flags);
>  
>  #if defined(CONFIG_PPC64) && defined(CONFIG_PREEMPT_RT)
> -	if (hadbatch) {
> -		batch = &__get_cpu_var(ppc64_tlb_batch);
> -		batch->active = 1;
> -	}
> +	arch_leave_lazy_mmu_mode();
>  #endif
> +	last = _switch(old_thread, new_thread);
> +
> +	local_irq_restore(flags);
>  
>  	return last;
>  }

^ permalink raw reply

* Re: arch/powerpc/lib/copy_32.S: Use alternate memcpy for MPC512x and MPC52xx
From: Milton Miller @ 2010-07-11  7:40 UTC (permalink / raw)
  To: Steve Deiters; +Cc: Albrecht Dreß, linuxppc-dev, David Woodhouse
In-Reply-To: <181804936ABC2349BE503168465576460F272CA4@exchserver.basler.com>


Steve wrote:
> These processors will corrupt data if accessing the local bus with
> unaligned addresses. This version fixes the typical case of copying from
> Flash on the local bus by keeping the source address always aligned.

As Dave said in May of 2008[1], the map driver is advertising xip access
to userspace so jffs2 is taking the XIP path and doing straight memcpy.

My reading of the mtd code says this is because map_is_linear() is true
a cfi cmdset 0001 nor flash will create a point function which jffs2
calls and it finds it will map the whole fs, so it just reads at will
from the direct mapping.

If the point fails to map the whole device, then jffs2 will use the
mtd mapping driver to copy to a buffer, and the default mapping drivers
will use memcpy_fromio to fill this buffer.

Steve, can you verify this by setting phys to NO_XIP in the mapping
driver and setting CONFIG_MTD_COMPLEX_MAPPINGS?  This is just a test,
as it is aparent this driver needs to have a custom mapping driver
that handles the alignment and read/modfiy/write issues -- the existing
memcopy_*io assumes byte writes are always ok which is not the case for
this processor and bus.


[1] http://thread.gmane.org/gmane.linux.drivers.mtd/21521
(found from the link in Albrecht's reply)

milton

^ permalink raw reply

* Re: Problems mapping OCM memory from 460EX to user space
From: Ayman El-Khashab @ 2010-07-11  4:14 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20100707194846.GA724@crust.elkhashab.com>

On 7/7/2010 2:48 PM, ayman@austin.rr.com wrote:
>   rc = io_remap_pfn_range(vma, PLB_OCM_BASE_ADDR>>PAGE_SHIFT, vma->vm_start, len, vma->vm_page_prot);
>
> I am fairly certain the physical address is correct, I've verified that the
> TLB entries in u-boot look ok.  Any tips on how to make this work?
>
>    

I found / fixed the issue.  The first problem is that start and offset 
are swapped in the io_remap_pfn_range invocation.  The 2nd was that
there were some TLB changes that were needed to map the OCM.

^ permalink raw reply

* [PATCH, RT, RFC]  Hacks allowing -rt to run on POWER7 / Powerpc.
From: Will Schmidt @ 2010-07-09 18:55 UTC (permalink / raw)
  To: rt-users, Thomas Gleixner, Benjamin Herrenschmidt
  Cc: linuxppc-dev, Darren Hart, will_schmidt, LKML

[PATCH, RT, RFC]  Hacks allowing -rt to run on POWER7 / Powerpc.

We've been seeing some issues with userspace randomly SIGSEGV'ing while
running the -RT kernels on POWER7 based systems.   After lots of
debugging, head scratching, and experimental changes to the code, the
problem has been narrowed down such that we can avoid the problems by
disabling the TLB batching.

After some input from Ben and further debug, we've found that the
restoration of the batch->active value near the end of __switch_to()
seems to be the key.    ( The -RT related  changes within
arch/powerpc/kernel/processor.c __switch_to()  do the equivalent of a
arch_leave_lazy_mmu_mode() before calling _switch, use a hadbatch flag
to indicate if batching was active, and then restore that batch->active
value on the way out after the call to _switch_to.    That particular
code is in the -RT branch, and not found in mainline  )

Deferring to Ben (or others in the know) for whether this is the proper
solution or if there is something deeper, but.. 
IF the right answer is to simply disable the restoration of
batch->active, the rest of the CONFIG_PREEMPT_RT changes in
__switch_to() should then be replaceable with a single call to
arch_leave_lazy_mmu_mode().

The patch here is what I am currently running with, on both POWER6 and
POWER7 systems, successfully.


Signed-off-by:   Will Schmidt <will_schmidt@vnet.ibm.com>
CC:    Ben Herrenschmidt  <benh@kernel.crashing.org>
CC:    Thomas Gleixner <tglx@linutronix.de>

---
diff -aurp linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c
--- linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c	2010-06-21 11:41:34.402513904 -0500
+++ linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c	2010-07-09 13:15:13.533269904 -0500
@@ -304,10 +304,6 @@ struct task_struct *__switch_to(struct t
 	struct thread_struct *new_thread, *old_thread;
 	unsigned long flags;
 	struct task_struct *last;
-#if defined(CONFIG_PPC64) && defined (CONFIG_PREEMPT_RT)
-	struct ppc64_tlb_batch *batch;
-	int hadbatch;
-#endif
 
 #ifdef CONFIG_SMP
 	/* avoid complexity of lazy save/restore of fpu
@@ -401,16 +397,6 @@ struct task_struct *__switch_to(struct t
 		new_thread->start_tb = current_tb;
 	}
 
-#ifdef CONFIG_PREEMPT_RT
-	batch = &__get_cpu_var(ppc64_tlb_batch);
-	if (batch->active) {
-		hadbatch = 1;
-		if (batch->index) {
-			__flush_tlb_pending(batch);
-		}
-		batch->active = 0;
-	}
-#endif /* #ifdef CONFIG_PREEMPT_RT */
 #endif
 
 	local_irq_save(flags);
@@ -425,16 +411,13 @@ struct task_struct *__switch_to(struct t
 	 * of sync. Hard disable here.
 	 */
 	hard_irq_disable();
-	last = _switch(old_thread, new_thread);
-
-	local_irq_restore(flags);
 
 #if defined(CONFIG_PPC64) && defined(CONFIG_PREEMPT_RT)
-	if (hadbatch) {
-		batch = &__get_cpu_var(ppc64_tlb_batch);
-		batch->active = 1;
-	}
+	arch_leave_lazy_mmu_mode();
 #endif
+	last = _switch(old_thread, new_thread);
+
+	local_irq_restore(flags);
 
 	return last;
 }

^ permalink raw reply

* Re: [PATCH] arch/powerpc/lib/copy_32.S: Use alternate memcpy for MPC512x and MPC52xx
From: Scott Wood @ 2010-07-09 16:18 UTC (permalink / raw)
  To: Segher Boessenkool
  Cc: Albrecht Dreß, Steve Deiters, linuxppc-dev, David Woodhouse
In-Reply-To: <E0D806F7-A109-4AAF-9899-429659E2C3D9@kernel.crashing.org>

On Fri, 9 Jul 2010 14:59:09 +0200
Segher Boessenkool <segher@kernel.crashing.org> wrote:

> >>> Actually, this is something which might need closer attention -
> >>> and maybe some support in the device tree indicating which read or
> >>> write width a device can accept?
> >>
> >> There already is "device-width"; the drivers never should use any
> >> other access width unless they *know* that will work.
> >
> > Wouldn't you want to use "bank-width" instead?
> 
> We were talking about single devices.  But, sure, when you have
> multiple devices in parallel the driver needs to know about that.
> 
> > It would be nice to have a device tree property that can specify
> > that all access widths supported by the CPU will work, though.
> 
> Oh please no.  A device binding should not depend on what CPU there
> is in the system.  There could be multiple CPUs of different
> architectures, even.

What I meant by that was that the flash interface was claiming that it
is not the limiting factor in which access widths are useable -- it
would be a way to claim that it is as flexible as ordinary memory in
that regard.

If there is a transaction size that is capable of being presented to
this component that it cannot handle, it would not present this
property.

> To figure out how to access a device, the driver looks at the device's
> node, and all its parent nodes (or asks generic code to do that, or
> platform code).

"looks" or "should look"? :-)

If there are transaction sizes supported by the CPU that won't work
with a given device through no fault of that device (or the interface
to that device for which we don't have a separate node), then in
theory, yes, it should be described at a higher level.

In reality, device tree parsing code is not AI, so rather than say "the
driver looks at this and figures it out" it would be better to provide
a more specific proposal of how a device tree might express this and
what the driver would look for, if you think the simple solution is
not expressive enough.

-Scott

^ permalink raw reply

* Re: kernel boot stuck at udbg_putc_cpm()
From: Scott Wood @ 2010-07-09 15:59 UTC (permalink / raw)
  To: Shawn Jin; +Cc: ppcdev
In-Reply-To: <AANLkTinNSzP-WvxC2kqZop2kvd1kSUMDuwI01GOBXApE@mail.gmail.com>

On Fri, 9 Jul 2010 00:35:43 -0700
Shawn Jin <shawnxjin@gmail.com> wrote:

> I changed my toolchain and rebuilt the kernel image. This time all the
> messages below magically displayed on the serial port. :-D Are all
> these the early debugging messages?

Yes, it's an alternate output for the regular console (there are
sometimes more messages, if you hook up .progress in your ppc_md, but
that's mainly of interest if you don't get this far).

> Now the kernel stuck at the while loop that waits for transmitter fifo
> to be empty. It seems that the CPM UART stopped working in the middle
> of printing a message. I'm using minicom to connect to the serial
> port. I heard minicom is problematic. Will it be the cause here?

I doubt it...

You're probably getting to the point where udbg is disabled because the
real serial driver is trying to take over -- and something's going
wrong with the real serial port driver.  Check to make sure the brg
config is correct (both the input clock and the baud rate you're trying
to switch to).  Commenting out the call to cpm_set_brg can be
a quick way of determining if that's the problem.

-Scott

^ permalink raw reply

* Re: [PATCH] arch/powerpc/lib/copy_32.S: Use alternate memcpy for MPC512x and MPC52xx
From: Segher Boessenkool @ 2010-07-09 13:03 UTC (permalink / raw)
  To: Albrecht Dreß; +Cc: David Woodhouse, Steve Deiters, linuxppc-dev
In-Reply-To: <1278619791.1801.4@antares>

> Hmm, unfortunately, it's usage is not clearly documented in mtd- 
> physmap.txt,

It's pretty clear I think.  Patches for making it better are welcome  
of course.

> so I never thought of this parameter.  And IMHO the problem goes  
> further - basically *any* chip which is attached to the LPB can be  
> affected by this problem, so it might be better to have a more  
> general approach like a "chip select property".

You cannot treat devices on the LPB as random access, that's all.   
Drivers
that assume they can, cannot be used for devices on the LPB.


Segher

^ permalink raw reply

* Re: [PATCH] arch/powerpc/lib/copy_32.S: Use alternate memcpy for MPC512x and MPC52xx
From: Segher Boessenkool @ 2010-07-09 12:59 UTC (permalink / raw)
  To: Scott Wood
  Cc: Albrecht Dreß, Steve Deiters, linuxppc-dev, David Woodhouse
In-Reply-To: <20100708150904.79feffdd@schlenkerla.am.freescale.net>

>>> Actually, this is something which might need closer attention -
>>> and maybe some support in the device tree indicating which read or
>>> write width a device can accept?
>>
>> There already is "device-width"; the drivers never should use any
>> other access width unless they *know* that will work.
>
> Wouldn't you want to use "bank-width" instead?

We were talking about single devices.  But, sure, when you have
multiple devices in parallel the driver needs to know about that.

> It would be nice to have a device tree property that can specify that
> all access widths supported by the CPU will work, though.

Oh please no.  A device binding should not depend on what CPU there
is in the system.  There could be multiple CPUs of different
architectures, even.

To figure out how to access a device, the driver looks at the device's
node, and all its parent nodes (or asks generic code to do that, or
platform code).


Segher

^ permalink raw reply

* [PATCH] powerpc/40x: Distinguish AMCC PowerPC 405EX and 405EXr correctly
From: Lee Nipper @ 2010-07-09 11:17 UTC (permalink / raw)
  To: jwboyer; +Cc: linuxppc-dev, Lee Nipper

The recent AMCC 405EX Rev D without Security uses a PVR value
that matches the old 405EXr Rev A/B with Security.
The 405EX Rev D without Security would be shown
incorrectly as an 405EXr. The pvr_mask of 0xffff0004
is no longer sufficient to distinguish the 405EX from 405EXr.

This patch replaces 2 entries in the cpu_specs table
and adds 8 more, each using pvr_mask of 0xffff000f
and appropriate pvr_value to distinguish the AMCC
PowerPC 405EX and 405EXr instances.
The cpu_name for these entries now includes the
Rev, in similar fashion to the 440GX.

Signed-off-by: Lee Nipper <lee.nipper@gmail.com>
---
Patch applies against v2.6.35-rc4.
Tested with 405EX Rev C and Rev D.
Followed u-boot arch/powerpc/include/asm/processor.h for 405EX[r] PVR values.

 arch/powerpc/kernel/cputable.c |  118 +++++++++++++++++++++++++++++++++++++---
 1 files changed, 111 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 87aa0f3..65e2b4e 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -1364,10 +1364,10 @@ static struct cpu_spec __initdata cpu_specs[] = {
 		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
-	{	/* 405EX */
-		.pvr_mask		= 0xffff0004,
-		.pvr_value		= 0x12910004,
-		.cpu_name		= "405EX",
+	{	/* 405EX Rev. A/B with Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x12910007,
+		.cpu_name		= "405EX Rev. A/B",
 		.cpu_features		= CPU_FTRS_40X,
 		.cpu_user_features	= PPC_FEATURE_32 |
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
@@ -1377,10 +1377,114 @@ static struct cpu_spec __initdata cpu_specs[] = {
 		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
-	{	/* 405EXr */
-		.pvr_mask		= 0xffff0004,
+	{	/* 405EX Rev. C without Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x1291000d,
+		.cpu_name		= "405EX Rev. C",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EX Rev. C with Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x1291000f,
+		.cpu_name		= "405EX Rev. C",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EX Rev. D without Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x12910003,
+		.cpu_name		= "405EX Rev. D",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EX Rev. D with Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x12910005,
+		.cpu_name		= "405EX Rev. D",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EXr Rev. A/B without Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x12910001,
+		.cpu_name		= "405EXr Rev. A/B",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EXr Rev. C without Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x12910009,
+		.cpu_name		= "405EXr Rev. C",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EXr Rev. C with Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x1291000b,
+		.cpu_name		= "405EXr Rev. C",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EXr Rev. D without Security */
+		.pvr_mask		= 0xffff000f,
 		.pvr_value		= 0x12910000,
-		.cpu_name		= "405EXr",
+		.cpu_name		= "405EXr Rev. D",
+		.cpu_features		= CPU_FTRS_40X,
+		.cpu_user_features	= PPC_FEATURE_32 |
+			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+		.mmu_features		= MMU_FTR_TYPE_40x,
+		.icache_bsize		= 32,
+		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
+		.platform		= "ppc405",
+	},
+	{	/* 405EXr Rev. D with Security */
+		.pvr_mask		= 0xffff000f,
+		.pvr_value		= 0x12910002,
+		.cpu_name		= "405EXr Rev. D",
 		.cpu_features		= CPU_FTRS_40X,
 		.cpu_user_features	= PPC_FEATURE_32 |
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
-- 
1.6.0.4

^ permalink raw reply related

* Re: [PATCH 27/27] KVM: PPC: Add Documentation about PV interface
From: Alexander Graf @ 2010-07-09  9:15 UTC (permalink / raw)
  To: MJ embd; +Cc: linuxppc-dev, KVM list, kvm-ppc
In-Reply-To: <AANLkTil6RekYBNnpM2MjSH7rcrHQZdHRwK5cBrvKQKhM@mail.gmail.com>


On 09.07.2010, at 11:11, MJ embd wrote:

> On Thu, Jul 1, 2010 at 4:13 PM, Alexander Graf <agraf@suse.de> wrote:
>> We just introduced a new PV interface that screams for documentation. =
So here
>> it is - a shiny new and awesome text file describing the internal =
works of
>> the PPC KVM paravirtual interface.
>>=20
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>=20
>> +
>> +
>> +Some instructions require more logic to determine what's going on =
than a load
>> +or store instruction can deliver. To enable patching of those, we =
keep some
>> +RAM around where we can live translate instructions to. What happens =
is the
>> +following:
>> +
>> +       1) copy emulation code to memory
>> +       2) patch that code to fit the emulated instruction
>> +       3) patch that code to return to the original pc + 4
>> +       4) patch the original instruction to branch to the new code
>> +
>> +That way we can inject an arbitrary amount of code as replacement =
for a single
>> +instruction. This allows us to check for pending interrupts when =
setting EE=3D1
>> +for example.
>> +
>=20
> Which patch does this mapping ? Can you please point to that.

The branch patching is in patch 22/27. For the respective users, see =
patch 23-26/27.


Alex

^ permalink raw reply

* Re: [PATCH 27/27] KVM: PPC: Add Documentation about PV interface
From: MJ embd @ 2010-07-09  9:11 UTC (permalink / raw)
  To: Alexander Graf; +Cc: linuxppc-dev, KVM list, kvm-ppc
In-Reply-To: <1277980982-12433-28-git-send-email-agraf@suse.de>

On Thu, Jul 1, 2010 at 4:13 PM, Alexander Graf <agraf@suse.de> wrote:
> We just introduced a new PV interface that screams for documentation. So =
here
> it is - a shiny new and awesome text file describing the internal works o=
f
> the PPC KVM paravirtual interface.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
>
> ---
>
> v1 -> v2:
>
> =A0- clarify guest implementation
> =A0- clarify that privileged instructions still work
> =A0- explain safe MSR bits
> =A0- Fix dsisr patch description
> =A0- change hypervisor calls to use new register values
> ---
> =A0Documentation/kvm/ppc-pv.txt | =A0185 ++++++++++++++++++++++++++++++++=
++++++++++
> =A01 files changed, 185 insertions(+), 0 deletions(-)
> =A0create mode 100644 Documentation/kvm/ppc-pv.txt
>
> diff --git a/Documentation/kvm/ppc-pv.txt b/Documentation/kvm/ppc-pv.txt
> new file mode 100644
> index 0000000..82de6c6
> --- /dev/null
> +++ b/Documentation/kvm/ppc-pv.txt
> @@ -0,0 +1,185 @@
> +The PPC KVM paravirtual interface
> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D
> +
> +The basic execution principle by which KVM on PowerPC works is to run al=
l kernel
> +space code in PR=3D1 which is user space. This way we trap all privilege=
d
> +instructions and can emulate them accordingly.
> +
> +Unfortunately that is also the downfall. There are quite some privileged
> +instructions that needlessly return us to the hypervisor even though the=
y
> +could be handled differently.
> +
> +This is what the PPC PV interface helps with. It takes privileged instru=
ctions
> +and transforms them into unprivileged ones with some help from the hyper=
visor.
> +This cuts down virtualization costs by about 50% on some of my benchmark=
s.
> +
> +The code for that interface can be found in arch/powerpc/kernel/kvm*
> +
> +Querying for existence
> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> +
> +To find out if we're running on KVM or not, we overlay the PVR register.=
 Usually
> +the PVR register contains an id that identifies your CPU type. If, howev=
er, you
> +pass KVM_PVR_PARA in the register that you want the PVR result in, the r=
egister
> +still contains KVM_PVR_PARA after the mfpvr call.
> +
> + =A0 =A0 =A0 LOAD_REG_IMM(r5, KVM_PVR_PARA)
> + =A0 =A0 =A0 mfpvr =A0 r5
> + =A0 =A0 =A0 [r5 still contains KVM_PVR_PARA]
> +
> +Once determined to run under a PV capable KVM, you can now use hypercall=
s as
> +described below.
> +
> +PPC hypercalls
> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> +
> +The only viable ways to reliably get from guest context to host context =
are:
> +
> + =A0 =A0 =A0 1) Call an invalid instruction
> + =A0 =A0 =A0 2) Call the "sc" instruction with a parameter to "sc"
> + =A0 =A0 =A0 3) Call the "sc" instruction with parameters in GPRs
> +
> +Method 1 is always a bad idea. Invalid instructions can be replaced late=
r on
> +by valid instructions, rendering the interface broken.
> +
> +Method 2 also has downfalls. If the parameter to "sc" is !=3D 0 the spec=
 is
> +rather unclear if the sc is targeted directly for the hypervisor or the
> +supervisor. It would also require that we read the syscall issuing instr=
uction
> +every time a syscall is issued, slowing down guest syscalls.
> +
> +Method 3 is what KVM uses. We pass magic constants (KVM_SC_MAGIC_R0 and
> +KVM_SC_MAGIC_R3) in r0 and r3 respectively. If a syscall instruction wit=
h these
> +magic values arrives from the guest's kernel mode, we take the syscall a=
s a
> +hypercall.
> +
> +The parameters are as follows:
> +
> + =A0 =A0 =A0 r0 =A0 =A0 =A0 =A0 =A0 =A0 =A0KVM_SC_MAGIC_R0
> + =A0 =A0 =A0 r3 =A0 =A0 =A0 =A0 =A0 =A0 =A0KVM_SC_MAGIC_R3 =A0 =A0 =A0 =
=A0 Return code
> + =A0 =A0 =A0 r4 =A0 =A0 =A0 =A0 =A0 =A0 =A0Hypercall number
> + =A0 =A0 =A0 r5 =A0 =A0 =A0 =A0 =A0 =A0 =A0First parameter
> + =A0 =A0 =A0 r6 =A0 =A0 =A0 =A0 =A0 =A0 =A0Second parameter
> + =A0 =A0 =A0 r7 =A0 =A0 =A0 =A0 =A0 =A0 =A0Third parameter
> + =A0 =A0 =A0 r8 =A0 =A0 =A0 =A0 =A0 =A0 =A0Fourth parameter
> +
> +Hypercall definitions are shared in generic code, so the same hypercall =
numbers
> +apply for x86 and powerpc alike.
> +
> +The magic page
> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> +
> +To enable communication between the hypervisor and guest there is a new =
shared
> +page that contains parts of supervisor visible register state. The guest=
 can
> +map this shared page using the KVM hypercall KVM_HC_PPC_MAP_MAGIC_PAGE.
> +
> +With this hypercall issued the guest always gets the magic page mapped a=
t the
> +desired location in effective and physical address space. For now, we al=
ways
> +map the page to -4096. This way we can access it using absolute load and=
 store
> +functions. The following instruction reads the first field of the magic =
page:
> +
> + =A0 =A0 =A0 ld =A0 =A0 =A0rX, -4096(0)
> +
> +The interface is designed to be extensible should there be need later to=
 add
> +additional registers to the magic page. If you add fields to the magic p=
age,
> +also define a new hypercall feature to indicate that the host can give y=
ou more
> +registers. Only if the host supports the additional features, make use o=
f them.
> +
> +The magic page has the following layout as described in
> +arch/powerpc/include/asm/kvm_para.h:
> +
> +struct kvm_vcpu_arch_shared {
> + =A0 =A0 =A0 __u64 scratch1;
> + =A0 =A0 =A0 __u64 scratch2;
> + =A0 =A0 =A0 __u64 scratch3;
> + =A0 =A0 =A0 __u64 critical; =A0 =A0 =A0 =A0 /* Guest may not get interr=
upts if =3D=3D r1 */
> + =A0 =A0 =A0 __u64 sprg0;
> + =A0 =A0 =A0 __u64 sprg1;
> + =A0 =A0 =A0 __u64 sprg2;
> + =A0 =A0 =A0 __u64 sprg3;
> + =A0 =A0 =A0 __u64 srr0;
> + =A0 =A0 =A0 __u64 srr1;
> + =A0 =A0 =A0 __u64 dar;
> + =A0 =A0 =A0 __u64 msr;
> + =A0 =A0 =A0 __u32 dsisr;
> + =A0 =A0 =A0 __u32 int_pending; =A0 =A0 =A0/* Tells the guest if we have=
 an interrupt */
> +};
> +
> +Additions to the page must only occur at the end. Struct fields are alwa=
ys 32
> +bit aligned.
> +
> +MSR bits
> +=3D=3D=3D=3D=3D=3D=3D=3D
> +
> +The MSR contains bits that require hypervisor intervention and bits that=
 do
> +not require direct hypervisor intervention because they only get interpr=
eted
> +when entering the guest or don't have any impact on the hypervisor's beh=
avior.
> +
> +The following bits are safe to be set inside the guest:
> +
> + =A0MSR_EE
> + =A0MSR_RI
> + =A0MSR_CR
> + =A0MSR_ME
> +
> +If any other bit changes in the MSR, please still use mtmsr(d).
> +
> +Patched instructions
> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> +
> +The "ld" and "std" instructions are transormed to "lwz" and "stw" instru=
ctions
> +respectively on 32 bit systems with an added offset of 4 to accomodate f=
or big
> +endianness.
> +
> +The following is a list of mapping the Linux kernel performs when runnin=
g as
> +guest. Implementing any of those mappings is optional, as the instructio=
n traps
> +also act on the shared page. So calling privileged instructions still wo=
rks as
> +before.
> +
> +From =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 To
> +=3D=3D=3D=3D =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D=3D
> +
> +mfmsr =A0rX =A0 =A0 =A0 =A0 =A0 =A0 =A0ld =A0 =A0 =A0rX, magic_page->msr
> +mfsprg rX, 0 =A0 =A0 =A0 =A0 =A0 ld =A0 =A0 =A0rX, magic_page->sprg0
> +mfsprg rX, 1 =A0 =A0 =A0 =A0 =A0 ld =A0 =A0 =A0rX, magic_page->sprg1
> +mfsprg rX, 2 =A0 =A0 =A0 =A0 =A0 ld =A0 =A0 =A0rX, magic_page->sprg2
> +mfsprg rX, 3 =A0 =A0 =A0 =A0 =A0 ld =A0 =A0 =A0rX, magic_page->sprg3
> +mfsrr0 rX =A0 =A0 =A0 =A0 =A0 =A0 =A0ld =A0 =A0 =A0rX, magic_page->srr0
> +mfsrr1 rX =A0 =A0 =A0 =A0 =A0 =A0 =A0ld =A0 =A0 =A0rX, magic_page->srr1
> +mfdar =A0rX =A0 =A0 =A0 =A0 =A0 =A0 =A0ld =A0 =A0 =A0rX, magic_page->dar
> +mfdsisr =A0 =A0 =A0 =A0rX =A0 =A0 =A0 =A0 =A0 =A0 =A0lwz =A0 =A0 rX, mag=
ic_page->dsisr
> +
> +mtmsr =A0rX =A0 =A0 =A0 =A0 =A0 =A0 =A0std =A0 =A0 rX, magic_page->msr
> +mtsprg 0, rX =A0 =A0 =A0 =A0 =A0 std =A0 =A0 rX, magic_page->sprg0
> +mtsprg 1, rX =A0 =A0 =A0 =A0 =A0 std =A0 =A0 rX, magic_page->sprg1
> +mtsprg 2, rX =A0 =A0 =A0 =A0 =A0 std =A0 =A0 rX, magic_page->sprg2
> +mtsprg 3, rX =A0 =A0 =A0 =A0 =A0 std =A0 =A0 rX, magic_page->sprg3
> +mtsrr0 rX =A0 =A0 =A0 =A0 =A0 =A0 =A0std =A0 =A0 rX, magic_page->srr0
> +mtsrr1 rX =A0 =A0 =A0 =A0 =A0 =A0 =A0std =A0 =A0 rX, magic_page->srr1
> +mtdar =A0rX =A0 =A0 =A0 =A0 =A0 =A0 =A0std =A0 =A0 rX, magic_page->dar
> +mtdsisr =A0 =A0 =A0 =A0rX =A0 =A0 =A0 =A0 =A0 =A0 =A0stw =A0 =A0 rX, mag=
ic_page->dsisr
> +
> +tlbsync =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0nop
> +
> +mtmsrd rX, 0 =A0 =A0 =A0 =A0 =A0 b =A0 =A0 =A0 <special mtmsr section>
> +mtmsr =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0b =A0 =A0 =A0 <special mtmsr se=
ction>
> +
> +mtmsrd rX, 1 =A0 =A0 =A0 =A0 =A0 b =A0 =A0 =A0 <special mtmsrd section>
> +
> +[BookE only]
> +wrteei [0|1] =A0 =A0 =A0 =A0 =A0 b =A0 =A0 =A0 <special wrteei section>
> +
> +
> +Some instructions require more logic to determine what's going on than a=
 load
> +or store instruction can deliver. To enable patching of those, we keep s=
ome
> +RAM around where we can live translate instructions to. What happens is =
the
> +following:
> +
> + =A0 =A0 =A0 1) copy emulation code to memory
> + =A0 =A0 =A0 2) patch that code to fit the emulated instruction
> + =A0 =A0 =A0 3) patch that code to return to the original pc + 4
> + =A0 =A0 =A0 4) patch the original instruction to branch to the new code
> +
> +That way we can inject an arbitrary amount of code as replacement for a =
single
> +instruction. This allows us to check for pending interrupts when setting=
 EE=3D1
> +for example.
> +

Which patch does this mapping ? Can you please point to that.


> --
> 1.6.0.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>



--=20
-mj

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox