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/hwmon.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
|
||||
|
@ -91,6 +92,7 @@ enum ds_type {
|
|||
# define DS1340_BIT_OSF 0x80
|
||||
#define DS1337_REG_STATUS 0x0f
|
||||
# define DS1337_BIT_OSF 0x80
|
||||
# define DS3231_BIT_EN32KHZ 0x08
|
||||
# define DS1337_BIT_A2I 0x02
|
||||
# define DS1337_BIT_A1I 0x01
|
||||
#define DS1339_REG_ALARM1_SECS 0x07
|
||||
|
@ -120,6 +122,9 @@ struct ds1307 {
|
|||
u8 length, u8 *values);
|
||||
s32 (*write_block_data)(const struct i2c_client *client, u8 command,
|
||||
u8 length, const u8 *values);
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw clks[2];
|
||||
#endif
|
||||
};
|
||||
|
||||
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,
|
||||
const struct i2c_device_id *id)
|
||||
|
@ -1294,6 +1587,7 @@ read_rtc:
|
|||
}
|
||||
|
||||
ds1307_hwmon_register(ds1307);
|
||||
ds1307_clks_register(ds1307);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue