mirror of https://gitee.com/openkylin/linux.git
rtc: ds1307: add clock provider support for DS3231
DS3231 has programmable square-wave output signal. This enables to use this feature as a clock provider of common clock framework. Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Reviewed-by: Michael Turquette <mturquette@baylibre.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
This commit is contained in:
parent
f3937549a9
commit
6c6ff145b3
|
@ -0,0 +1,37 @@
|
||||||
|
* Maxim DS3231 Real Time Clock
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
see: Documentation/devicetree/bindings/i2c/trivial-devices.txt
|
||||||
|
|
||||||
|
Optional property:
|
||||||
|
- #clock-cells: Should be 1.
|
||||||
|
- clock-output-names:
|
||||||
|
overwrite the default clock names "ds3231_clk_sqw" and "ds3231_clk_32khz".
|
||||||
|
|
||||||
|
Each clock is assigned an identifier and client nodes can use this identifier
|
||||||
|
to specify the clock which they consume. Following indices are allowed:
|
||||||
|
- 0: square-wave output on the SQW pin
|
||||||
|
- 1: square-wave output on the 32kHz pin
|
||||||
|
|
||||||
|
- interrupts: rtc alarm/event interrupt. When this property is selected,
|
||||||
|
clock on the SQW pin cannot be used.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
ds3231: ds3231@51 {
|
||||||
|
compatible = "maxim,ds3231";
|
||||||
|
reg = <0x68>;
|
||||||
|
#clock-cells = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
device1 {
|
||||||
|
...
|
||||||
|
clocks = <&ds3231 0>;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
device2 {
|
||||||
|
...
|
||||||
|
clocks = <&ds3231 1>;
|
||||||
|
...
|
||||||
|
};
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't determine type by probing, but if we expect pre-Linux code
|
* We can't determine type by probing, but if we expect pre-Linux code
|
||||||
|
@ -91,6 +92,7 @@ enum ds_type {
|
||||||
# define DS1340_BIT_OSF 0x80
|
# define DS1340_BIT_OSF 0x80
|
||||||
#define DS1337_REG_STATUS 0x0f
|
#define DS1337_REG_STATUS 0x0f
|
||||||
# define DS1337_BIT_OSF 0x80
|
# define DS1337_BIT_OSF 0x80
|
||||||
|
# define DS3231_BIT_EN32KHZ 0x08
|
||||||
# define DS1337_BIT_A2I 0x02
|
# define DS1337_BIT_A2I 0x02
|
||||||
# define DS1337_BIT_A1I 0x01
|
# define DS1337_BIT_A1I 0x01
|
||||||
#define DS1339_REG_ALARM1_SECS 0x07
|
#define DS1339_REG_ALARM1_SECS 0x07
|
||||||
|
@ -120,6 +122,9 @@ struct ds1307 {
|
||||||
u8 length, u8 *values);
|
u8 length, u8 *values);
|
||||||
s32 (*write_block_data)(const struct i2c_client *client, u8 command,
|
s32 (*write_block_data)(const struct i2c_client *client, u8 command,
|
||||||
u8 length, const u8 *values);
|
u8 length, const u8 *values);
|
||||||
|
#ifdef CONFIG_COMMON_CLK
|
||||||
|
struct clk_hw clks[2];
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chip_desc {
|
struct chip_desc {
|
||||||
|
@ -926,7 +931,295 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif /* CONFIG_RTC_DRV_DS1307_HWMON */
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Square-wave output support for DS3231
|
||||||
|
* Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_COMMON_CLK
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DS3231_CLK_SQW = 0,
|
||||||
|
DS3231_CLK_32KHZ,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define clk_sqw_to_ds1307(clk) \
|
||||||
|
container_of(clk, struct ds1307, clks[DS3231_CLK_SQW])
|
||||||
|
#define clk_32khz_to_ds1307(clk) \
|
||||||
|
container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ])
|
||||||
|
|
||||||
|
static int ds3231_clk_sqw_rates[] = {
|
||||||
|
1,
|
||||||
|
1024,
|
||||||
|
4096,
|
||||||
|
8192,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = ds1307->client;
|
||||||
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
||||||
|
int control;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(lock);
|
||||||
|
|
||||||
|
control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
|
||||||
|
if (control < 0) {
|
||||||
|
ret = control;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
control &= ~mask;
|
||||||
|
control |= value;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control);
|
||||||
|
out:
|
||||||
|
mutex_unlock(lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||||
|
int control;
|
||||||
|
int rate_sel = 0;
|
||||||
|
|
||||||
|
control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL);
|
||||||
|
if (control < 0)
|
||||||
|
return control;
|
||||||
|
if (control & DS1337_BIT_RS1)
|
||||||
|
rate_sel += 1;
|
||||||
|
if (control & DS1337_BIT_RS2)
|
||||||
|
rate_sel += 2;
|
||||||
|
|
||||||
|
return ds3231_clk_sqw_rates[rate_sel];
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
unsigned long *prate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) {
|
||||||
|
if (ds3231_clk_sqw_rates[i] <= rate)
|
||||||
|
return ds3231_clk_sqw_rates[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||||
|
int control = 0;
|
||||||
|
int rate_sel;
|
||||||
|
|
||||||
|
for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds3231_clk_sqw_rates);
|
||||||
|
rate_sel++) {
|
||||||
|
if (ds3231_clk_sqw_rates[rate_sel] == rate)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rate_sel == ARRAY_SIZE(ds3231_clk_sqw_rates))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (rate_sel & 1)
|
||||||
|
control |= DS1337_BIT_RS1;
|
||||||
|
if (rate_sel & 2)
|
||||||
|
control |= DS1337_BIT_RS2;
|
||||||
|
|
||||||
|
return ds1337_write_control(ds1307, DS1337_BIT_RS1 | DS1337_BIT_RS2,
|
||||||
|
control);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3231_clk_sqw_prepare(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||||
|
|
||||||
|
return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ds3231_clk_sqw_unprepare(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||||
|
|
||||||
|
ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||||
|
int control;
|
||||||
|
|
||||||
|
control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL);
|
||||||
|
if (control < 0)
|
||||||
|
return control;
|
||||||
|
|
||||||
|
return !(control & DS1337_BIT_INTCN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops ds3231_clk_sqw_ops = {
|
||||||
|
.prepare = ds3231_clk_sqw_prepare,
|
||||||
|
.unprepare = ds3231_clk_sqw_unprepare,
|
||||||
|
.is_prepared = ds3231_clk_sqw_is_prepared,
|
||||||
|
.recalc_rate = ds3231_clk_sqw_recalc_rate,
|
||||||
|
.round_rate = ds3231_clk_sqw_round_rate,
|
||||||
|
.set_rate = ds3231_clk_sqw_set_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
return 32768;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = ds1307->client;
|
||||||
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
||||||
|
int status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(lock);
|
||||||
|
|
||||||
|
status = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
|
||||||
|
if (status < 0) {
|
||||||
|
ret = status;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
status |= DS3231_BIT_EN32KHZ;
|
||||||
|
else
|
||||||
|
status &= ~DS3231_BIT_EN32KHZ;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, status);
|
||||||
|
out:
|
||||||
|
mutex_unlock(lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3231_clk_32khz_prepare(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
||||||
|
|
||||||
|
return ds3231_clk_32khz_control(ds1307, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ds3231_clk_32khz_unprepare(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
||||||
|
|
||||||
|
ds3231_clk_32khz_control(ds1307, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_STATUS);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
return !!(status & DS3231_BIT_EN32KHZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops ds3231_clk_32khz_ops = {
|
||||||
|
.prepare = ds3231_clk_32khz_prepare,
|
||||||
|
.unprepare = ds3231_clk_32khz_unprepare,
|
||||||
|
.is_prepared = ds3231_clk_32khz_is_prepared,
|
||||||
|
.recalc_rate = ds3231_clk_32khz_recalc_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk_init_data ds3231_clks_init[] = {
|
||||||
|
[DS3231_CLK_SQW] = {
|
||||||
|
.name = "ds3231_clk_sqw",
|
||||||
|
.ops = &ds3231_clk_sqw_ops,
|
||||||
|
.flags = CLK_IS_ROOT,
|
||||||
|
},
|
||||||
|
[DS3231_CLK_32KHZ] = {
|
||||||
|
.name = "ds3231_clk_32khz",
|
||||||
|
.ops = &ds3231_clk_32khz_ops,
|
||||||
|
.flags = CLK_IS_ROOT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ds3231_clks_register(struct ds1307 *ds1307)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = ds1307->client;
|
||||||
|
struct device_node *node = client->dev.of_node;
|
||||||
|
struct clk_onecell_data *onecell;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
onecell = devm_kzalloc(&client->dev, sizeof(*onecell), GFP_KERNEL);
|
||||||
|
if (!onecell)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
onecell->clk_num = ARRAY_SIZE(ds3231_clks_init);
|
||||||
|
onecell->clks = devm_kcalloc(&client->dev, onecell->clk_num,
|
||||||
|
sizeof(onecell->clks[0]), GFP_KERNEL);
|
||||||
|
if (!onecell->clks)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ds3231_clks_init); i++) {
|
||||||
|
struct clk_init_data init = ds3231_clks_init[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt signal due to alarm conditions and square-wave
|
||||||
|
* output share same pin, so don't initialize both.
|
||||||
|
*/
|
||||||
|
if (i == DS3231_CLK_SQW && test_bit(HAS_ALARM, &ds1307->flags))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* optional override of the clockname */
|
||||||
|
of_property_read_string_index(node, "clock-output-names", i,
|
||||||
|
&init.name);
|
||||||
|
ds1307->clks[i].init = &init;
|
||||||
|
|
||||||
|
onecell->clks[i] = devm_clk_register(&client->dev,
|
||||||
|
&ds1307->clks[i]);
|
||||||
|
if (IS_ERR(onecell->clks[i]))
|
||||||
|
return PTR_ERR(onecell->clks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
of_clk_add_provider(node, of_clk_src_onecell_get, onecell);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ds1307_clks_register(struct ds1307 *ds1307)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (ds1307->type != ds_3231)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = ds3231_clks_register(ds1307);
|
||||||
|
if (ret) {
|
||||||
|
dev_warn(&ds1307->client->dev,
|
||||||
|
"unable to register clock device %d\n", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void ds1307_clks_register(struct ds1307 *ds1307)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_COMMON_CLK */
|
||||||
|
|
||||||
static int ds1307_probe(struct i2c_client *client,
|
static int ds1307_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
|
@ -1294,6 +1587,7 @@ static int ds1307_probe(struct i2c_client *client,
|
||||||
}
|
}
|
||||||
|
|
||||||
ds1307_hwmon_register(ds1307);
|
ds1307_hwmon_register(ds1307);
|
||||||
|
ds1307_clks_register(ds1307);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue