Commit f5b08ac7 authored by Oleg Dzhimiev's avatar Oleg Dzhimiev

updated driver to current version, added our fall-through

parent 27a7df33
...@@ -16,10 +16,12 @@ ...@@ -16,10 +16,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/clk-provider.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -52,6 +54,8 @@ ...@@ -52,6 +54,8 @@
#define M41T80_ALARM_REG_SIZE \ #define M41T80_ALARM_REG_SIZE \
(M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON) (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
#define M41T80_SQW_MAX_FREQ 32768
#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */ #define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */
#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */ #define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */
#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */ #define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */
...@@ -86,9 +90,71 @@ static const struct i2c_device_id m41t80_id[] = { ...@@ -86,9 +90,71 @@ static const struct i2c_device_id m41t80_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, m41t80_id); MODULE_DEVICE_TABLE(i2c, m41t80_id);
static const struct of_device_id m41t80_of_match[] = {
{
.compatible = "st,m41t62",
.data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT)
},
{
.compatible = "st,m41t65",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_WD)
},
{
.compatible = "st,m41t80",
.data = (void *)(M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t81",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t81s",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t82",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t83",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t84",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t85",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ)
},
{
.compatible = "st,m41t87",
.data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ)
},
{
.compatible = "microcrystal,rv4162",
.data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT)
},
/* DT compatibility only, do not use compatibles below: */
{
.compatible = "st,rv4162",
.data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT)
},
{
.compatible = "rv4162",
.data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT)
},
{ }
};
MODULE_DEVICE_TABLE(of, m41t80_of_match);
struct m41t80_data { struct m41t80_data {
u8 features; unsigned long features;
struct i2c_client *client;
struct rtc_device *rtc; struct rtc_device *rtc;
#ifdef CONFIG_COMMON_CLK
struct clk_hw sqw;
#endif
}; };
static irqreturn_t m41t80_handle_irq(int irq, void *dev_id) static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
...@@ -142,7 +208,7 @@ static int m41t80_get_datetime(struct i2c_client *client, ...@@ -142,7 +208,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
return flags; return flags;
if (flags & M41T80_FLAGS_OF) { if (flags & M41T80_FLAGS_OF) {
dev_err(&client->dev, "Oscillator failure, data might be invalid. Elphel: proceeding anyway.\n"); dev_err(&client->dev, "Oscillator failure, data is invalid. Elphel: proceeding anyway.\n");
//return -EINVAL; //return -EINVAL;
} }
...@@ -168,6 +234,7 @@ static int m41t80_get_datetime(struct i2c_client *client, ...@@ -168,6 +234,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* Sets the given date and time to the real time clock. */ /* Sets the given date and time to the real time clock. */
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{ {
struct m41t80_data *clientdata = i2c_get_clientdata(client);
unsigned char buf[8]; unsigned char buf[8];
int err, flags; int err, flags;
...@@ -183,6 +250,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) ...@@ -183,6 +250,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100); buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
buf[M41T80_REG_WDAY] = tm->tm_wday; buf[M41T80_REG_WDAY] = tm->tm_wday;
/* If the square wave output is controlled in the weekday register */
if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
int val;
val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY);
if (val < 0)
return val;
buf[M41T80_REG_WDAY] |= (val & 0xf0);
}
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC, err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
sizeof(buf), buf); sizeof(buf), buf);
if (err < 0) { if (err < 0) {
...@@ -273,6 +351,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ...@@ -273,6 +351,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
return err; return err;
} }
/* Keep SQWE bit value */
alarmvals[0] |= (ret & M41T80_ALMON_SQWE);
ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -359,116 +440,166 @@ static int m41t80_resume(struct device *dev) ...@@ -359,116 +440,166 @@ static int m41t80_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume); static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume);
static ssize_t flags_show(struct device *dev, #ifdef CONFIG_COMMON_CLK
struct device_attribute *attr, char *buf) #define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
{
struct i2c_client *client = to_i2c_client(dev);
int val;
val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (val < 0)
return val;
return sprintf(buf, "%#x\n", val);
}
static DEVICE_ATTR_RO(flags);
static ssize_t sqwfreq_show(struct device *dev, static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
struct device_attribute *attr, char *buf) unsigned long parent_rate)
{ {
struct i2c_client *client = to_i2c_client(dev); struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct m41t80_data *clientdata = i2c_get_clientdata(client); struct i2c_client *client = m41t80->client;
int val, reg_sqw; int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
M41T80_REG_WDAY : M41T80_REG_SQW;
int ret = i2c_smbus_read_byte_data(client, reg_sqw);
unsigned long val = M41T80_SQW_MAX_FREQ;
if (!(clientdata->features & M41T80_FEATURE_SQ)) if (ret < 0)
return -EINVAL; return 0;
ret >>= 4;
if (ret == 0)
val = 0;
else if (ret > 1)
val = val / (1 << ret);
reg_sqw = M41T80_REG_SQW;
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
reg_sqw = M41T80_REG_WDAY;
val = i2c_smbus_read_byte_data(client, reg_sqw);
if (val < 0)
return val; return val;
val = (val >> 4) & 0xf;
switch (val) {
case 0:
break;
case 1:
val = 32768;
break;
default:
val = 32768 >> val;
}
return sprintf(buf, "%d\n", val);
} }
static ssize_t sqwfreq_store(struct device *dev, static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
struct device_attribute *attr, unsigned long *prate)
const char *buf, size_t count)
{ {
struct i2c_client *client = to_i2c_client(dev); int i, freq = M41T80_SQW_MAX_FREQ;
struct m41t80_data *clientdata = i2c_get_clientdata(client);
int almon, sqw, reg_sqw, rc;
unsigned long val;
rc = kstrtoul(buf, 0, &val); if (freq <= rate)
if (rc < 0) return freq;
return rc;
if (!(clientdata->features & M41T80_FEATURE_SQ)) for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
return -EINVAL; freq /= 1 << i;
if (freq <= rate)
return freq;
}
if (val) { return 0;
if (!is_power_of_2(val)) }
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
M41T80_REG_WDAY : M41T80_REG_SQW;
int reg, ret, val = 0;
if (rate) {
if (!is_power_of_2(rate))
return -EINVAL; return -EINVAL;
val = ilog2(val); val = ilog2(rate);
if (val == 15) if (val == ilog2(M41T80_SQW_MAX_FREQ))
val = 1; val = 1;
else if (val < 14) else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
val = 15 - val; val = ilog2(M41T80_SQW_MAX_FREQ) - val;
else else
return -EINVAL; return -EINVAL;
} }
/* disable SQW, set SQW frequency & re-enable */
almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (almon < 0)
return almon;
reg_sqw = M41T80_REG_SQW;
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
reg_sqw = M41T80_REG_WDAY;
sqw = i2c_smbus_read_byte_data(client, reg_sqw);
if (sqw < 0)
return sqw;
sqw = (sqw & 0x0f) | (val << 4);
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
almon & ~M41T80_ALMON_SQWE);
if (rc < 0)
return rc;
if (val) { reg = i2c_smbus_read_byte_data(client, reg_sqw);
rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw); if (reg < 0)
if (rc < 0) return reg;
return rc;
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, reg = (reg & 0x0f) | (val << 4);
almon | M41T80_ALMON_SQWE);
if (rc < 0) ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
return rc; if (ret < 0)
} return ret;
return count;
return -EINVAL;
} }
static DEVICE_ATTR_RW(sqwfreq);
static struct attribute *attrs[] = { static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
&dev_attr_flags.attr, {
&dev_attr_sqwfreq.attr, struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
NULL, struct i2c_client *client = m41t80->client;
}; int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (ret < 0)
return ret;
if (enable)
ret |= M41T80_ALMON_SQWE;
else
ret &= ~M41T80_ALMON_SQWE;
static struct attribute_group attr_group = { return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
.attrs = attrs, }
static int m41t80_sqw_prepare(struct clk_hw *hw)
{
return m41t80_sqw_control(hw, 1);
}
static void m41t80_sqw_unprepare(struct clk_hw *hw)
{
m41t80_sqw_control(hw, 0);
}
static int m41t80_sqw_is_prepared(struct clk_hw *hw)
{
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (ret < 0)
return ret;
return !!(ret & M41T80_ALMON_SQWE);
}
static const struct clk_ops m41t80_sqw_ops = {
.prepare = m41t80_sqw_prepare,
.unprepare = m41t80_sqw_unprepare,
.is_prepared = m41t80_sqw_is_prepared,
.recalc_rate = m41t80_sqw_recalc_rate,
.round_rate = m41t80_sqw_round_rate,
.set_rate = m41t80_sqw_set_rate,
}; };
static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
{
struct i2c_client *client = m41t80->client;
struct device_node *node = client->dev.of_node;
struct clk *clk;
struct clk_init_data init;
int ret;
/* First disable the clock */
ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (ret < 0)
return ERR_PTR(ret);
ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
ret & ~(M41T80_ALMON_SQWE));
if (ret < 0)
return ERR_PTR(ret);
init.name = "m41t80-sqw";
init.ops = &m41t80_sqw_ops;
init.flags = 0;
init.parent_names = NULL;
init.num_parents = 0;
m41t80->sqw.init = &init;
/* optional override of the clockname */
of_property_read_string(node, "clock-output-names", &init.name);
/* register the clock */
clk = clk_register(&client->dev, &m41t80->sqw);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
return clk;
}
#endif
#ifdef CONFIG_RTC_DRV_M41T80_WDT #ifdef CONFIG_RTC_DRV_M41T80_WDT
/* /*
***************************************************************************** *****************************************************************************
...@@ -759,13 +890,6 @@ static struct notifier_block wdt_notifier = { ...@@ -759,13 +890,6 @@ static struct notifier_block wdt_notifier = {
***************************************************************************** *****************************************************************************
*/ */
static void m41t80_remove_sysfs_group(void *_dev)
{
struct device *dev = _dev;
sysfs_remove_group(&dev->kobj, &attr_group);
}
static int m41t80_probe(struct i2c_client *client, static int m41t80_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -774,6 +898,7 @@ static int m41t80_probe(struct i2c_client *client, ...@@ -774,6 +898,7 @@ static int m41t80_probe(struct i2c_client *client,
struct rtc_device *rtc = NULL; struct rtc_device *rtc = NULL;
struct rtc_time tm; struct rtc_time tm;
struct m41t80_data *m41t80_data = NULL; struct m41t80_data *m41t80_data = NULL;
bool wakeup_source = false;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK |
I2C_FUNC_SMBUS_BYTE_DATA)) { I2C_FUNC_SMBUS_BYTE_DATA)) {
...@@ -786,9 +911,18 @@ static int m41t80_probe(struct i2c_client *client, ...@@ -786,9 +911,18 @@ static int m41t80_probe(struct i2c_client *client,
if (!m41t80_data) if (!m41t80_data)
return -ENOMEM; return -ENOMEM;
m41t80_data->client = client;
if (client->dev.of_node)
m41t80_data->features = (unsigned long)
of_device_get_match_data(&client->dev);
else
m41t80_data->features = id->driver_data; m41t80_data->features = id->driver_data;
i2c_set_clientdata(client, m41t80_data); i2c_set_clientdata(client, m41t80_data);
#ifdef CONFIG_OF
wakeup_source = of_property_read_bool(client->dev.of_node,
"wakeup-source");
#endif
if (client->irq > 0) { if (client->irq > 0) {
rc = devm_request_threaded_irq(&client->dev, client->irq, rc = devm_request_threaded_irq(&client->dev, client->irq,
NULL, m41t80_handle_irq, NULL, m41t80_handle_irq,
...@@ -797,14 +931,16 @@ static int m41t80_probe(struct i2c_client *client, ...@@ -797,14 +931,16 @@ static int m41t80_probe(struct i2c_client *client,
if (rc) { if (rc) {
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
client->irq = 0; client->irq = 0;
} else { wakeup_source = false;
}
}
if (client->irq > 0 || wakeup_source) {
m41t80_rtc_ops.read_alarm = m41t80_read_alarm; m41t80_rtc_ops.read_alarm = m41t80_read_alarm;
m41t80_rtc_ops.set_alarm = m41t80_set_alarm; m41t80_rtc_ops.set_alarm = m41t80_set_alarm;
m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable; m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable;
/* Enable the wakealarm */ /* Enable the wakealarm */
device_init_wakeup(&client->dev, true); device_init_wakeup(&client->dev, true);
} }
}
rtc = devm_rtc_device_register(&client->dev, client->name, rtc = devm_rtc_device_register(&client->dev, client->name,
&m41t80_rtc_ops, THIS_MODULE); &m41t80_rtc_ops, THIS_MODULE);
...@@ -812,6 +948,10 @@ static int m41t80_probe(struct i2c_client *client, ...@@ -812,6 +948,10 @@ static int m41t80_probe(struct i2c_client *client,
return PTR_ERR(rtc); return PTR_ERR(rtc);
m41t80_data->rtc = rtc; m41t80_data->rtc = rtc;
if (client->irq <= 0) {
/* We cannot support UIE mode if we do not have an IRQ line */
rtc->uie_unsupported = 1;
}
/* Make sure HT (Halt Update) bit is cleared */ /* Make sure HT (Halt Update) bit is cleared */
rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
...@@ -846,21 +986,6 @@ static int m41t80_probe(struct i2c_client *client, ...@@ -846,21 +986,6 @@ static int m41t80_probe(struct i2c_client *client,
return rc; return rc;
} }
/* Export sysfs entries */
rc = sysfs_create_group(&(&client->dev)->kobj, &attr_group);
if (rc) {
dev_err(&client->dev, "Failed to create sysfs group: %d\n", rc);
return rc;
}
rc = devm_add_action_or_reset(&client->dev, m41t80_remove_sysfs_group,
&client->dev);
if (rc) {
dev_err(&client->dev,
"Failed to add sysfs cleanup action: %d\n", rc);
return rc;
}
#ifdef CONFIG_RTC_DRV_M41T80_WDT #ifdef CONFIG_RTC_DRV_M41T80_WDT
if (m41t80_data->features & M41T80_FEATURE_HT) { if (m41t80_data->features & M41T80_FEATURE_HT) {
save_client = client; save_client = client;
...@@ -873,6 +998,10 @@ static int m41t80_probe(struct i2c_client *client, ...@@ -873,6 +998,10 @@ static int m41t80_probe(struct i2c_client *client,
return rc; return rc;
} }
} }
#endif
#ifdef CONFIG_COMMON_CLK
if (m41t80_data->features & M41T80_FEATURE_SQ)
m41t80_sqw_register_clk(m41t80_data);
#endif #endif
return 0; return 0;
} }
...@@ -894,6 +1023,7 @@ static int m41t80_remove(struct i2c_client *client) ...@@ -894,6 +1023,7 @@ static int m41t80_remove(struct i2c_client *client)
static struct i2c_driver m41t80_driver = { static struct i2c_driver m41t80_driver = {
.driver = { .driver = {
.name = "rtc-m41t80", .name = "rtc-m41t80",
.of_match_table = of_match_ptr(m41t80_of_match),
.pm = &m41t80_pm, .pm = &m41t80_pm,
}, },
.probe = m41t80_probe, .probe = m41t80_probe,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment