Linux-RTC Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH RFC] rtc: abx80x: Add attributes to control oscillator switching modes
@ 2020-06-16 11:41 Kevin P. Fleming
  0 siblings, 0 replies; only message in thread
From: Kevin P. Fleming @ 2020-06-16 11:41 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, linux-rtc; +Cc: Kevin P. Fleming

The devices supported by this driver have two oscillator switching
modes available which can switch from the crystal oscillator to
the RC oscillator when triggered by an event.

The 'AOS' mode switches to the RC oscillator when the primary power
supply is lost, and the device is operating on backup power. A boolean
device attribute named 'auto_osc_switch' controls this mode.

The 'FOS' mode switches to the RC oscillator when a failure of the
crystal oscillator has been detected by the device. A boolean device
attribute named 'fail_osc_switch' controls this mode.

Signed-off-by: Kevin P. Fleming <kevin+linux@km6g.us>
---
 drivers/rtc/rtc-abx80x.c | 163 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 151 insertions(+), 12 deletions(-)

diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 1b428fe2029ef..3a92015395417 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -55,10 +55,9 @@
 
 #define ABX8XX_REG_OSC		0x1c
 #define ABX8XX_OSC_FOS		BIT(3)
-#define ABX8XX_OSC_BOS		BIT(4)
+#define ABX8XX_OSC_AOS		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
@@ -136,15 +135,34 @@ 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");
+	if (flags < 0)
 		return flags;
-	}
 
 	return (flags & ABX8XX_OSS_OMODE) ? 1 : 0;
 }
 
+static int abx80x_is_auto_osc_switch_mode(struct i2c_client *client)
+{
+	int flags = 0;
+
+	flags =  i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
+	if (flags < 0)
+		return flags;
+
+	return (flags & ABX8XX_OSC_AOS) ? 1 : 0;
+}
+
+static int abx80x_is_fail_osc_switch_mode(struct i2c_client *client)
+{
+	int flags = 0;
+
+	flags =  i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
+	if (flags < 0)
+		return flags;
+
+	return (flags & ABX8XX_OSC_FOS) ? 1 : 0;
+}
+
 static int abx80x_set_autocal_filter(struct i2c_client *client, u8 filter_cfg)
 {
 	int err;
@@ -196,8 +214,11 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
 
 	/* Read the Oscillator Failure only in XT mode */
 	rc_mode = abx80x_is_rc_mode(client);
-	if (rc_mode < 0)
+	if (rc_mode < 0) {
+		dev_err(&client->dev,
+			"Failed to read oscillator mode\n");
 		return rc_mode;
+	}
 
 	if (!rc_mode) {
 		flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
@@ -497,7 +518,7 @@ static ssize_t oscillator_show(struct device *dev,
 	rc_mode = abx80x_is_rc_mode(client);
 
 	if (rc_mode < 0) {
-		dev_err(dev, "Failed to read RTC oscillator selection\n");
+		dev_err(dev, "Failed to read oscillator mode\n");
 		sprintf(buf, "\n");
 		return rc_mode;
 	}
@@ -510,14 +531,132 @@ static ssize_t oscillator_show(struct device *dev,
 
 static DEVICE_ATTR_RW(oscillator);
 
-static struct attribute *rtc_calib_attrs[] = {
+static ssize_t auto_osc_switch_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev->parent);
+	int retval, flags;
+	bool auto_osc_switch = false;
+
+	retval = kstrtobool(buf,  &auto_osc_switch);
+	if (retval < 0) {
+		dev_err(dev, "Failed to parse auto_osc_switch attribute\n");
+		return -EINVAL;
+	}
+
+	flags =  i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
+	if (flags < 0)
+		return flags;
+
+	if (auto_osc_switch)
+		flags |= (ABX8XX_OSC_AOS);
+	else
+		flags &= ~(ABX8XX_OSC_AOS);
+
+	/* Unlock write access on Oscillator Control register */
+	if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_OSC) < 0)
+		return -EIO;
+
+	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 auto_osc_switch_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	int aos_mode = 0;
+	struct i2c_client *client = to_i2c_client(dev->parent);
+
+	aos_mode = abx80x_is_auto_osc_switch_mode(client);
+
+	if (aos_mode < 0) {
+		dev_err(dev, "Failed to read RTC oscillator control register\n");
+		sprintf(buf, "\n");
+		return aos_mode;
+	}
+
+	if (aos_mode)
+		return sprintf(buf, "on\n");
+	else
+		return sprintf(buf, "off\n");
+}
+
+static DEVICE_ATTR_RW(auto_osc_switch);
+
+static ssize_t fail_osc_switch_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev->parent);
+	int retval, flags;
+	bool fail_osc_switch = false;
+
+	retval = kstrtobool(buf,  &fail_osc_switch);
+	if (retval < 0) {
+		dev_err(dev, "Failed to parse fail_osc_switch attribute\n");
+		return -EINVAL;
+	}
+
+	flags =  i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC);
+	if (flags < 0)
+		return flags;
+
+	if (fail_osc_switch)
+		flags |= (ABX8XX_OSC_FOS);
+	else
+		flags &= ~(ABX8XX_OSC_FOS);
+
+	/* Unlock write access on Oscillator Control register */
+	if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_OSC) < 0)
+		return -EIO;
+
+	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 fail_osc_switch_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	int aos_mode = 0;
+	struct i2c_client *client = to_i2c_client(dev->parent);
+
+	aos_mode = abx80x_is_fail_osc_switch_mode(client);
+
+	if (aos_mode < 0) {
+		dev_err(dev, "Failed to read RTC oscillator control register\n");
+		sprintf(buf, "\n");
+		return aos_mode;
+	}
+
+	if (aos_mode)
+		return sprintf(buf, "on\n");
+	else
+		return sprintf(buf, "off\n");
+}
+
+static DEVICE_ATTR_RW(fail_osc_switch);
+
+static struct attribute *rtc_osc_ctrl_attrs[] = {
 	&dev_attr_autocalibration.attr,
 	&dev_attr_oscillator.attr,
+	&dev_attr_auto_osc_switch.attr,
+	&dev_attr_fail_osc_switch.attr,
 	NULL,
 };
 
-static const struct attribute_group rtc_calib_attr_group = {
-	.attrs		= rtc_calib_attrs,
+static const struct attribute_group rtc_osc_ctrl_attr_group = {
+	.attrs		= rtc_osc_ctrl_attrs,
 };
 
 static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled)
@@ -871,7 +1010,7 @@ static int abx80x_probe(struct i2c_client *client,
 		}
 	}
 
-	err = rtc_add_group(priv->rtc, &rtc_calib_attr_group);
+	err = rtc_add_group(priv->rtc, &rtc_osc_ctrl_attr_group);
 	if (err) {
 		dev_err(&client->dev, "Failed to create sysfs group: %d\n",
 			err);
-- 
2.26.2


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-16 11:41 [PATCH RFC] rtc: abx80x: Add attributes to control oscillator switching modes Kevin P. Fleming

Linux-RTC Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-rtc/0 linux-rtc/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-rtc linux-rtc/ https://lore.kernel.org/linux-rtc \
		linux-rtc@vger.kernel.org
	public-inbox-index linux-rtc

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-rtc


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git