Commit 59a8383a authored by Mylène Josserand's avatar Mylène Josserand Committed by Alexandre Belloni

rtc: abx80x: handle autocalibration

The autocalibration is separated in two bits to set in Oscillator
Control register (0x1c) :
 - OSEL bit to select the oscillator type (XT or RC).
 - ACAL bit to select the autocalibration type.

These functionnalities are exported in sysfs entries : "oscillator"
and "autocalibration". Respectively, the values are "xtal" for XT
oscillator and "rc" for RC oscillator and 0 to disable the
autocalibration cycle, 512 for a 512 seconds autocalibration cycle
and 1024 for a cycle of 1024 seconds.

Examples :
Set to XT Oscillator
echo xtal > /sys/class/rtc/rtc0/device/oscillator
Activate an autocalibration every 512 seconds
echo 512 > /sys/class/rtc/rtc0/device/autocalibration
Signed-off-by: default avatarMylène Josserand <mylene.josserand@free-electrons.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
parent 85062c9b
...@@ -49,7 +49,19 @@ ...@@ -49,7 +49,19 @@
#define ABX8XX_REG_CD_TIMER_CTL 0x18 #define ABX8XX_REG_CD_TIMER_CTL 0x18
#define ABX8XX_REG_OSC 0x1c
#define ABX8XX_OSC_FOS BIT(3)
#define ABX8XX_OSC_BOS BIT(4)
#define ABX8XX_OSC_ACAL_512 BIT(5)
#define ABX8XX_OSC_ACAL_1024 BIT(6)
#define ABX8XX_OSC_OSEL BIT(7)
#define ABX8XX_REG_OSS 0x1d
#define ABX8XX_OSS_OMODE BIT(4)
#define ABX8XX_REG_CFG_KEY 0x1f #define ABX8XX_REG_CFG_KEY 0x1f
#define ABX8XX_CFG_KEY_OSC 0xa1
#define ABX8XX_CFG_KEY_MISC 0x9d #define ABX8XX_CFG_KEY_MISC 0x9d
#define ABX8XX_REG_ID0 0x28 #define ABX8XX_REG_ID0 0x28
...@@ -81,6 +93,20 @@ static struct abx80x_cap abx80x_caps[] = { ...@@ -81,6 +93,20 @@ static struct abx80x_cap abx80x_caps[] = {
[ABX80X] = {.pn = 0} [ABX80X] = {.pn = 0}
}; };
static int abx80x_is_rc_mode(struct i2c_client *client)
{
int flags = 0;
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
if (flags < 0) {
dev_err(&client->dev,
"Failed to read autocalibration attribute\n");
return flags;
}
return (flags & ABX8XX_OSS_OMODE) ? 1 : 0;
}
static int abx80x_enable_trickle_charger(struct i2c_client *client, static int abx80x_enable_trickle_charger(struct i2c_client *client,
u8 trickle_cfg) u8 trickle_cfg)
{ {
...@@ -248,6 +274,174 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t) ...@@ -248,6 +274,174 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
return 0; return 0;
} }
static int abx80x_rtc_set_autocalibration(struct device *dev,
int autocalibration)
{
struct i2c_client *client = to_i2c_client(dev);
int retval, flags = 0;
if ((autocalibration != 0) && (autocalibration != 1024) &&
(autocalibration != 512)) {
dev_err(dev, "autocalibration value outside permitted range\n");
return -EINVAL;
}
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
if (flags < 0)
return flags;
if (autocalibration == 0) {
flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024);
} else if (autocalibration == 1024) {
/* 1024 autocalibration is 0x10 */
flags |= ABX8XX_OSC_ACAL_1024;
flags &= ~(ABX8XX_OSC_ACAL_512);
} else {
/* 512 autocalibration is 0x11 */
flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512);
}
/* Unlock write access to Oscillator Control Register */
retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY,
ABX8XX_CFG_KEY_OSC);
if (retval < 0) {
dev_err(dev, "Failed to write CONFIG_KEY register\n");
return retval;
}
retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags);
return retval;
}
static int abx80x_rtc_get_autocalibration(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
int flags = 0, autocalibration;
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
if (flags < 0)
return flags;
if (flags & ABX8XX_OSC_ACAL_512)
autocalibration = 512;
else if (flags & ABX8XX_OSC_ACAL_1024)
autocalibration = 1024;
else
autocalibration = 0;
return autocalibration;
}
static ssize_t autocalibration_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int retval;
unsigned long autocalibration = 0;
retval = kstrtoul(buf, 10, &autocalibration);
if (retval < 0) {
dev_err(dev, "Failed to store RTC autocalibration attribute\n");
return -EINVAL;
}
retval = abx80x_rtc_set_autocalibration(dev, autocalibration);
return retval ? retval : count;
}
static ssize_t autocalibration_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int autocalibration = 0;
autocalibration = abx80x_rtc_get_autocalibration(dev);
if (autocalibration < 0) {
dev_err(dev, "Failed to read RTC autocalibration\n");
sprintf(buf, "0\n");
return autocalibration;
}
return sprintf(buf, "%d\n", autocalibration);
}
static DEVICE_ATTR_RW(autocalibration);
static ssize_t oscillator_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int retval, flags, rc_mode = 0;
if (strncmp(buf, "rc", 2) == 0) {
rc_mode = 1;
} else if (strncmp(buf, "xtal", 4) == 0) {
rc_mode = 0;
} else {
dev_err(dev, "Oscillator selection value outside permitted ones\n");
return -EINVAL;
}
flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
if (flags < 0)
return flags;
if (rc_mode == 0)
flags &= ~(ABX8XX_OSC_OSEL);
else
flags |= (ABX8XX_OSC_OSEL);
/* Unlock write access on Oscillator Control register */
retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY,
ABX8XX_CFG_KEY_OSC);
if (retval < 0) {
dev_err(dev, "Failed to write CONFIG_KEY register\n");
return retval;
}
retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags);
if (retval < 0) {
dev_err(dev, "Failed to write Oscillator Control register\n");
return retval;
}
return retval ? retval : count;
}
static ssize_t oscillator_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc_mode = 0;
struct i2c_client *client = to_i2c_client(dev);
rc_mode = abx80x_is_rc_mode(client);
if (rc_mode < 0) {
dev_err(dev, "Failed to read RTC oscillator selection\n");
sprintf(buf, "\n");
return rc_mode;
}
if (rc_mode)
return sprintf(buf, "rc\n");
else
return sprintf(buf, "xtal\n");
}
static DEVICE_ATTR_RW(oscillator);
static struct attribute *rtc_calib_attrs[] = {
&dev_attr_autocalibration.attr,
&dev_attr_oscillator.attr,
NULL,
};
static const struct attribute_group rtc_calib_attr_group = {
.attrs = rtc_calib_attrs,
};
static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled) static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
...@@ -303,6 +497,13 @@ static int abx80x_dt_trickle_cfg(struct device_node *np) ...@@ -303,6 +497,13 @@ static int abx80x_dt_trickle_cfg(struct device_node *np)
return (trickle_cfg | i); return (trickle_cfg | i);
} }
static void rtc_calib_remove_sysfs_group(void *_dev)
{
struct device *dev = _dev;
sysfs_remove_group(&dev->kobj, &rtc_calib_attr_group);
}
static int abx80x_probe(struct i2c_client *client, static int abx80x_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -405,6 +606,24 @@ static int abx80x_probe(struct i2c_client *client, ...@@ -405,6 +606,24 @@ static int abx80x_probe(struct i2c_client *client,
} }
} }
/* Export sysfs entries */
err = sysfs_create_group(&(&client->dev)->kobj, &rtc_calib_attr_group);
if (err) {
dev_err(&client->dev, "Failed to create sysfs group: %d\n",
err);
return err;
}
err = devm_add_action(&client->dev, rtc_calib_remove_sysfs_group,
&client->dev);
if (err) {
rtc_calib_remove_sysfs_group(&client->dev);
dev_err(&client->dev,
"Failed to add sysfs cleanup action: %d\n",
err);
return err;
}
return 0; return 0;
} }
......
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