Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8407ef46 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull more RTC updates from Alexandre Belloni:
 "A second pull request for v4.6 with a few fixesi before -rc1.  The new
  features for abx80x actually make the RTC behave correctly.

  Drivers:
   - abx80x: handle both XT and RC oscillators, XT failure bit and
     autocalibration
   - m41t80: avoid out of range year values
   - rv8803: workaround an i2c HW issue"

* tag 'rtc-4.6-2' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux:
  rtc: abx80x: handle the oscillator failure bit
  rtc: abx80x: handle autocalibration
  rtc: rv8803: workaround i2c HW issue
  rtc: mcp795: add devicetree support
  rtc: asm9260: remove incorrect __init/__exit annotations
  rtc: m41t80: avoid out of range year values
  rtc: s3c: Don't print an error on probe deferral
  rtc: rv3029: stop mentioning rv3029c2
parents b4ae78ed ee087744
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
* Maxim MCP795		SPI Serial Real-Time Clock

Required properties:
- compatible: Should contain "maxim,mcp795".
- reg: SPI address for chip

Example:
	mcp795: rtc@0 {
		compatible = "maxim,mcp795";
		reg = <0>;
	};
+1 −1
Original line number Diff line number Diff line
@@ -589,7 +589,7 @@ config RTC_DRV_RV3029_HWMON
	default y
	help
	  Say Y here if you want to expose temperature sensor data on
	  rtc-rv3029c2.
	  rtc-rv3029.

config RTC_DRV_RV8803
	tristate "Micro Crystal RV8803"
+250 −2
Original line number Diff line number Diff line
@@ -49,7 +49,20 @@

#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_OF		BIT(1)
#define ABX8XX_OSS_OMODE	BIT(4)

#define ABX8XX_REG_CFG_KEY	0x1f
#define ABX8XX_CFG_KEY_OSC	0xa1
#define ABX8XX_CFG_KEY_MISC	0x9d

#define ABX8XX_REG_ID0		0x28
@@ -81,6 +94,20 @@ static struct abx80x_cap abx80x_caps[] = {
	[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,
					 u8 trickle_cfg)
{
@@ -112,7 +139,23 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	struct i2c_client *client = to_i2c_client(dev);
	unsigned char buf[8];
	int err;
	int err, flags, rc_mode = 0;

	/* Read the Oscillator Failure only in XT mode */
	rc_mode = abx80x_is_rc_mode(client);
	if (rc_mode < 0)
		return rc_mode;

	if (!rc_mode) {
		flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
		if (flags < 0)
			return flags;

		if (flags & ABX8XX_OSS_OF) {
			dev_err(dev, "Oscillator failure, data is invalid.\n");
			return -EINVAL;
		}
	}

	err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH,
					    sizeof(buf), buf);
@@ -140,7 +183,7 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	struct i2c_client *client = to_i2c_client(dev);
	unsigned char buf[8];
	int err;
	int err, flags;

	if (tm->tm_year < 100)
		return -EINVAL;
@@ -161,6 +204,18 @@ static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
		return -EIO;
	}

	/* Clear the OF bit of Oscillator Status Register */
	flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS);
	if (flags < 0)
		return flags;

	err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSS,
					flags & ~ABX8XX_OSS_OF);
	if (err < 0) {
		dev_err(&client->dev, "Unable to write oscillator status register\n");
		return err;
	}

	return 0;
}

@@ -248,6 +303,174 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t)
	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)
{
	struct i2c_client *client = to_i2c_client(dev);
@@ -303,6 +526,13 @@ static int abx80x_dt_trickle_cfg(struct device_node *np)
	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,
			const struct i2c_device_id *id)
{
@@ -405,6 +635,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;
}

+2 −2
Original line number Diff line number Diff line
@@ -255,7 +255,7 @@ static const struct rtc_class_ops asm9260_rtc_ops = {
	.alarm_irq_enable	= asm9260_alarm_irq_enable,
};

static int __init asm9260_rtc_probe(struct platform_device *pdev)
static int asm9260_rtc_probe(struct platform_device *pdev)
{
	struct asm9260_rtc_priv *priv;
	struct device *dev = &pdev->dev;
@@ -323,7 +323,7 @@ static int __init asm9260_rtc_probe(struct platform_device *pdev)
	return ret;
}

static int __exit asm9260_rtc_remove(struct platform_device *pdev)
static int asm9260_rtc_remove(struct platform_device *pdev)
{
	struct asm9260_rtc_priv *priv = platform_get_drvdata(pdev);

+6 −0
Original line number Diff line number Diff line
@@ -176,7 +176,13 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
		bin2bcd(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f);
	buf[M41T80_REG_MON] =
		bin2bcd(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f);

	/* assume 20YY not 19YY */
	if (tm->tm_year < 100 || tm->tm_year > 199) {
		dev_err(&client->dev, "Year must be between 2000 and 2099. It's %d.\n",
			tm->tm_year + 1900);
		return -EINVAL;
	}
	buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year % 100);

	if (i2c_transfer(client->adapter, msgs, 1) != 1) {
Loading