Skip to content

rtc-ds1307.c: add DT property 'wakeup-source' #1260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions arch/arm/boot/dts/overlays/wittypi-overlay.dts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Device Tree overlay for Witty Pi extension board by UUGear
*
*/

/dts-v1/;
/plugin/;

/ {

compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709";

fragment@0 {
target = <&leds>;
__overlay__ {
compatible = "gpio-leds";
wittypi_led: wittypi_led {
label = "wittypi_led";
linux,default-trigger = "default-on";
gpios = <&gpio 17 0>;
};
};
};

fragment@1 {
target = <&i2c1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;

rtc: ds1337@68 {
compatible = "dallas,ds1337";
reg = <0x68>;
wakeup-source;
};
};
};

__overrides__ {
led_gpio = <&wittypi_led>,"gpios:4";
led_trigger = <&wittypi_led>,"linux,default-trigger";
};

};
141 changes: 65 additions & 76 deletions drivers/rtc/rtc-ds1307.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
* published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <linux/bcd.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/rtc/ds1307.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/rtc/ds1307.h>

/*
* We can't determine type by probing, but if we expect pre-Linux code
Expand Down Expand Up @@ -114,7 +114,6 @@ struct ds1307 {
#define HAS_ALARM 1 /* bit 1 == irq claimed */
struct i2c_client *client;
struct rtc_device *rtc;
struct work_struct work;
s32 (*read_block_data)(const struct i2c_client *client, u8 command,
u8 length, u8 *values);
s32 (*write_block_data)(const struct i2c_client *client, u8 command,
Expand Down Expand Up @@ -311,27 +310,17 @@ static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client,
/*----------------------------------------------------------------------*/

/*
* The IRQ logic includes a "real" handler running in IRQ context just
* long enough to schedule this workqueue entry. We need a task context
* to talk to the RTC, since I2C I/O calls require that; and disable the
* IRQ until we clear its status on the chip, so that this handler can
* work with any type of triggering (not just falling edge).
*
* The ds1337 and ds1339 both have two alarms, but we only use the first
* one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
* signal; ds1339 chips have only one alarm signal.
*/
static void ds1307_work(struct work_struct *work)
static irqreturn_t ds1307_irq(int irq, void *dev_id)
{
struct ds1307 *ds1307;
struct i2c_client *client;
struct mutex *lock;
struct i2c_client *client = dev_id;
struct ds1307 *ds1307 = i2c_get_clientdata(client);
struct mutex *lock = &ds1307->rtc->ops_lock;
int stat, control;

ds1307 = container_of(work, struct ds1307, work);
client = ds1307->client;
lock = &ds1307->rtc->ops_lock;

mutex_lock(lock);
stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
if (stat < 0)
Expand All @@ -352,18 +341,8 @@ static void ds1307_work(struct work_struct *work)
}

out:
if (test_bit(HAS_ALARM, &ds1307->flags))
enable_irq(client->irq);
mutex_unlock(lock);
}

static irqreturn_t ds1307_irq(int irq, void *dev_id)
{
struct i2c_client *client = dev_id;
struct ds1307 *ds1307 = i2c_get_clientdata(client);

disable_irq_nosync(irq);
schedule_work(&ds1307->work);
return IRQ_HANDLED;
}

Expand Down Expand Up @@ -634,13 +613,14 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
MCP794XX_BIT_ALMX_C1 | \
MCP794XX_BIT_ALMX_C2)

static void mcp794xx_work(struct work_struct *work)
static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
{
struct ds1307 *ds1307 = container_of(work, struct ds1307, work);
struct i2c_client *client = ds1307->client;
struct i2c_client *client = dev_id;
struct ds1307 *ds1307 = i2c_get_clientdata(client);
struct mutex *lock = &ds1307->rtc->ops_lock;
int reg, ret;

mutex_lock(&ds1307->rtc->ops_lock);
mutex_lock(lock);

/* Check and clear alarm 0 interrupt flag. */
reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_ALARM0_CTRL);
Expand All @@ -665,9 +645,9 @@ static void mcp794xx_work(struct work_struct *work)
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);

out:
if (test_bit(HAS_ALARM, &ds1307->flags))
enable_irq(client->irq);
mutex_unlock(&ds1307->rtc->ops_lock);
mutex_unlock(lock);

return IRQ_HANDLED;
}

static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
Expand Down Expand Up @@ -734,25 +714,25 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
regs[3] = bin2bcd(t->time.tm_sec);
regs[4] = bin2bcd(t->time.tm_min);
regs[5] = bin2bcd(t->time.tm_hour);
regs[6] = bin2bcd(t->time.tm_wday) + 1;
regs[6] = bin2bcd(t->time.tm_wday + 1);
regs[7] = bin2bcd(t->time.tm_mday);
regs[8] = bin2bcd(t->time.tm_mon) + 1;
regs[8] = bin2bcd(t->time.tm_mon + 1);

/* Clear the alarm 0 interrupt flag. */
regs[6] &= ~MCP794XX_BIT_ALMX_IF;
/* Set alarm match: second, minute, hour, day, date, month. */
regs[6] |= MCP794XX_MSK_ALMX_MATCH;

if (t->enabled)
regs[0] |= MCP794XX_BIT_ALM0_EN;
else
regs[0] &= ~MCP794XX_BIT_ALM0_EN;
/* Disable interrupt. We will not enable until completely programmed */
regs[0] &= ~MCP794XX_BIT_ALM0_EN;

ret = ds1307->write_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
if (ret < 0)
return ret;

return 0;
if (!t->enabled)
return 0;
regs[0] |= MCP794XX_BIT_ALM0_EN;
return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, regs[0]);
}

static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
Expand Down Expand Up @@ -798,13 +778,6 @@ ds1307_nvram_read(struct file *filp, struct kobject *kobj,
client = kobj_to_i2c_client(kobj);
ds1307 = i2c_get_clientdata(client);

if (unlikely(off >= ds1307->nvram->size))
return 0;
if ((off + count) > ds1307->nvram->size)
count = ds1307->nvram->size - off;
if (unlikely(!count))
return count;

result = ds1307->read_block_data(client, ds1307->nvram_offset + off,
count, buf);
if (result < 0)
Expand All @@ -824,13 +797,6 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj,
client = kobj_to_i2c_client(kobj);
ds1307 = i2c_get_clientdata(client);

if (unlikely(off >= ds1307->nvram->size))
return -EFBIG;
if ((off + count) > ds1307->nvram->size)
count = ds1307->nvram->size - off;
if (unlikely(!count))
return count;

result = ds1307->write_block_data(client, ds1307->nvram_offset + off,
count, buf);
if (result < 0) {
Expand Down Expand Up @@ -894,8 +860,11 @@ static int ds1307_probe(struct i2c_client *client,
struct chip_desc *chip = &chips[id->driver_data];
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
bool want_irq = false;
bool ds1307_can_wakeup_device = false;
unsigned char *buf;
struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev);
irq_handler_t irq_handler = ds1307_irq;

static const int bbsqi_bitpos[] = {
[ds_1337] = 0,
[ds_1339] = DS1339_BIT_BBSQI,
Expand Down Expand Up @@ -939,6 +908,20 @@ static int ds1307_probe(struct i2c_client *client,
ds1307->write_block_data = ds1307_write_block_data;
}

#ifdef CONFIG_OF
/*
* For devices with no IRQ directly connected to the SoC, the RTC chip
* can be forced as a wakeup source by stating that explicitly in
* the device's .dts file using the "wakeup-source" boolean property.
* If the "wakeup-source" property is set, don't request an IRQ.
* This will guarantee the 'wakealarm' sysfs entry is available on the device,
* if supported by the RTC.
*/
if (of_property_read_bool(client->dev.of_node, "wakeup-source")) {
ds1307_can_wakeup_device = true;
}
#endif

switch (ds1307->type) {
case ds_1337:
case ds_1339:
Expand All @@ -957,13 +940,12 @@ static int ds1307_probe(struct i2c_client *client,
ds1307->regs[0] &= ~DS1337_BIT_nEOSC;

/*
* Using IRQ? Disable the square wave and both alarms.
* Using IRQ or defined as wakeup-source?
* Disable the square wave and both alarms.
* For some variants, be sure alarms can trigger when we're
* running on Vbackup (BBSQI/BBSQW)
*/
if (ds1307->client->irq > 0 && chip->alarm) {
INIT_WORK(&ds1307->work, ds1307_work);

if (chip->alarm && (ds1307->client->irq > 0 || ds1307_can_wakeup_device)) {
ds1307->regs[0] |= DS1337_BIT_INTCN
| bbsqi_bitpos[ds1307->type];
ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
Expand Down Expand Up @@ -1053,7 +1035,7 @@ static int ds1307_probe(struct i2c_client *client,
case mcp794xx:
rtc_ops = &mcp794xx_rtc_ops;
if (ds1307->client->irq > 0 && chip->alarm) {
INIT_WORK(&ds1307->work, mcp794xx_work);
irq_handler = mcp794xx_irq;
want_irq = true;
}
break;
Expand Down Expand Up @@ -1168,24 +1150,36 @@ static int ds1307_probe(struct i2c_client *client,
bin2bcd(tmp));
}

device_set_wakeup_capable(&client->dev, want_irq);
if (want_irq) {
device_set_wakeup_capable(&client->dev, true);
set_bit(HAS_ALARM, &ds1307->flags);
}
ds1307->rtc = devm_rtc_device_register(&client->dev, client->name,
rtc_ops, THIS_MODULE);
if (IS_ERR(ds1307->rtc)) {
return PTR_ERR(ds1307->rtc);
}

if (ds1307_can_wakeup_device) {
/* Disable request for an IRQ */
want_irq = false;
dev_info(&client->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n");
/* We cannot support UIE mode if we do not have an IRQ line */
ds1307->rtc->uie_unsupported = 1;
}

if (want_irq) {
err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,
ds1307->rtc->name, client);
err = devm_request_threaded_irq(&client->dev,
client->irq, NULL, irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
ds1307->rtc->name, client);
if (err) {
client->irq = 0;
device_set_wakeup_capable(&client->dev, false);
clear_bit(HAS_ALARM, &ds1307->flags);
dev_err(&client->dev, "unable to request IRQ!\n");
} else {

set_bit(HAS_ALARM, &ds1307->flags);
} else
dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
}
}

if (chip->nvram_size) {
Expand Down Expand Up @@ -1231,11 +1225,6 @@ static int ds1307_remove(struct i2c_client *client)
{
struct ds1307 *ds1307 = i2c_get_clientdata(client);

if (test_and_clear_bit(HAS_ALARM, &ds1307->flags)) {
free_irq(client->irq, client);
cancel_work_sync(&ds1307->work);
}

if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags))
sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram);

Expand Down