Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/rtc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 10 kB image not shown  

Quelle  rtc-rx8010.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Driver for the Epson RTC module RX-8010 SJ
 *
 * Copyright(C) Timesys Corporation 2015
 * Copyright(C) General Electric Company 2015
 */


#include <linux/bcd.h>
#include <linux/bitops.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/rtc.h>

#define RX8010_SEC  0x10
#define RX8010_MIN  0x11
#define RX8010_HOUR  0x12
#define RX8010_WDAY  0x13
#define RX8010_MDAY  0x14
#define RX8010_MONTH  0x15
#define RX8010_YEAR  0x16
#define RX8010_RESV17  0x17
#define RX8010_ALMIN  0x18
#define RX8010_ALHOUR  0x19
#define RX8010_ALWDAY  0x1A
#define RX8010_TCOUNT0  0x1B
#define RX8010_TCOUNT1  0x1C
#define RX8010_EXT  0x1D
#define RX8010_FLAG  0x1E
#define RX8010_CTRL  0x1F
/* 0x20 to 0x2F are user registers */
#define RX8010_RESV30  0x30
#define RX8010_RESV31  0x31
#define RX8010_IRQ  0x32

#define RX8010_EXT_WADA  BIT(3)

#define RX8010_FLAG_VLF  BIT(1)
#define RX8010_FLAG_AF  BIT(3)
#define RX8010_FLAG_TF  BIT(4)
#define RX8010_FLAG_UF  BIT(5)

#define RX8010_CTRL_AIE  BIT(3)
#define RX8010_CTRL_UIE  BIT(5)
#define RX8010_CTRL_STOP BIT(6)
#define RX8010_CTRL_TEST BIT(7)

#define RX8010_ALARM_AE  BIT(7)

static const struct i2c_device_id rx8010_id[] = {
 { "rx8010" },
 { }
};
MODULE_DEVICE_TABLE(i2c, rx8010_id);

static const __maybe_unused struct of_device_id rx8010_of_match[] = {
 { .compatible = "epson,rx8010" },
 { }
};
MODULE_DEVICE_TABLE(of, rx8010_of_match);

struct rx8010_data {
 struct regmap *regs;
 struct rtc_device *rtc;
 u8 ctrlreg;
};

static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
{
 struct i2c_client *client = dev_id;
 struct rx8010_data *rx8010 = i2c_get_clientdata(client);
 int flagreg, err;

 rtc_lock(rx8010->rtc);

 err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
 if (err) {
  rtc_unlock(rx8010->rtc);
  return IRQ_NONE;
 }

 if (flagreg & RX8010_FLAG_VLF)
  dev_warn(&client->dev, "Frequency stop detected\n");

 if (flagreg & RX8010_FLAG_TF) {
  flagreg &= ~RX8010_FLAG_TF;
  rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF);
 }

 if (flagreg & RX8010_FLAG_AF) {
  flagreg &= ~RX8010_FLAG_AF;
  rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF);
 }

 if (flagreg & RX8010_FLAG_UF) {
  flagreg &= ~RX8010_FLAG_UF;
  rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF);
 }

 err = regmap_write(rx8010->regs, RX8010_FLAG, flagreg);
 rtc_unlock(rx8010->rtc);
 return err ? IRQ_NONE : IRQ_HANDLED;
}

static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 u8 date[RX8010_YEAR - RX8010_SEC + 1];
 int flagreg, err;

 err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
 if (err)
  return err;

 if (flagreg & RX8010_FLAG_VLF) {
  dev_warn(dev, "Frequency stop detected\n");
  return -EINVAL;
 }

 err = regmap_bulk_read(rx8010->regs, RX8010_SEC, date, sizeof(date));
 if (err)
  return err;

 dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f);
 dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f);
 dt->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f);
 dt->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f);
 dt->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1;
 dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100;
 dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f);

 return 0;
}

static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 u8 date[RX8010_YEAR - RX8010_SEC + 1];
 int err;

 /* set STOP bit before changing clock/calendar */
 err = regmap_set_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP);
 if (err)
  return err;

 date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec);
 date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min);
 date[RX8010_HOUR - RX8010_SEC] = bin2bcd(dt->tm_hour);
 date[RX8010_MDAY - RX8010_SEC] = bin2bcd(dt->tm_mday);
 date[RX8010_MONTH - RX8010_SEC] = bin2bcd(dt->tm_mon + 1);
 date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100);
 date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday);

 err = regmap_bulk_write(rx8010->regs, RX8010_SEC, date, sizeof(date));
 if (err)
  return err;

 /* clear STOP bit after changing clock/calendar */
 err = regmap_clear_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP);
 if (err)
  return err;

 err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_VLF);
 if (err)
  return err;

 return 0;
}

static int rx8010_init(struct device *dev)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 u8 ctrl[2];
 int need_clear = 0, err;

 /* Initialize reserved registers as specified in datasheet */
 err = regmap_write(rx8010->regs, RX8010_RESV17, 0xD8);
 if (err)
  return err;

 err = regmap_write(rx8010->regs, RX8010_RESV30, 0x00);
 if (err)
  return err;

 err = regmap_write(rx8010->regs, RX8010_RESV31, 0x08);
 if (err)
  return err;

 err = regmap_write(rx8010->regs, RX8010_IRQ, 0x00);
 if (err)
  return err;

 err = regmap_bulk_read(rx8010->regs, RX8010_FLAG, ctrl, 2);
 if (err)
  return err;

 if (ctrl[0] & RX8010_FLAG_VLF)
  dev_warn(dev, "Frequency stop was detected\n");

 if (ctrl[0] & RX8010_FLAG_AF) {
  dev_warn(dev, "Alarm was detected\n");
  need_clear = 1;
 }

 if (ctrl[0] & RX8010_FLAG_TF)
  need_clear = 1;

 if (ctrl[0] & RX8010_FLAG_UF)
  need_clear = 1;

 if (need_clear) {
  ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF);
  err = regmap_write(rx8010->regs, RX8010_FLAG, ctrl[0]);
  if (err)
   return err;
 }

 rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST);

 return 0;
}

static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 u8 alarmvals[3];
 int flagreg, err;

 err = regmap_bulk_read(rx8010->regs, RX8010_ALMIN, alarmvals, 3);
 if (err)
  return err;

 err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
 if (err)
  return err;

 t->time.tm_sec = 0;
 t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
 t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);

 if (!(alarmvals[2] & RX8010_ALARM_AE))
  t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f);

 t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
 t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;

 return 0;
}

static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 u8 alarmvals[3];
 int err;

 if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) {
  rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
  err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
  if (err)
   return err;
 }

 err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF);
 if (err)
  return err;

 alarmvals[0] = bin2bcd(t->time.tm_min);
 alarmvals[1] = bin2bcd(t->time.tm_hour);
 alarmvals[2] = bin2bcd(t->time.tm_mday);

 err = regmap_bulk_write(rx8010->regs, RX8010_ALMIN, alarmvals, 2);
 if (err)
  return err;

 err = regmap_clear_bits(rx8010->regs, RX8010_EXT, RX8010_EXT_WADA);
 if (err)
  return err;

 if (alarmvals[2] == 0)
  alarmvals[2] |= RX8010_ALARM_AE;

 err = regmap_write(rx8010->regs, RX8010_ALWDAY, alarmvals[2]);
 if (err)
  return err;

 if (t->enabled) {
  if (rx8010->rtc->uie_rtctimer.enabled)
   rx8010->ctrlreg |= RX8010_CTRL_UIE;
  if (rx8010->rtc->aie_timer.enabled)
   rx8010->ctrlreg |=
    (RX8010_CTRL_AIE | RX8010_CTRL_UIE);

  err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
  if (err)
   return err;
 }

 return 0;
}

static int rx8010_alarm_irq_enable(struct device *dev,
       unsigned int enabled)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 int err;
 u8 ctrl;

 ctrl = rx8010->ctrlreg;

 if (enabled) {
  if (rx8010->rtc->uie_rtctimer.enabled)
   ctrl |= RX8010_CTRL_UIE;
  if (rx8010->rtc->aie_timer.enabled)
   ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE);
 } else {
  if (!rx8010->rtc->uie_rtctimer.enabled)
   ctrl &= ~RX8010_CTRL_UIE;
  if (!rx8010->rtc->aie_timer.enabled)
   ctrl &= ~RX8010_CTRL_AIE;
 }

 err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF);
 if (err)
  return err;

 if (ctrl != rx8010->ctrlreg) {
  rx8010->ctrlreg = ctrl;
  err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
  if (err)
   return err;
 }

 return 0;
}

static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
 struct rx8010_data *rx8010 = dev_get_drvdata(dev);
 int tmp, flagreg, err;

 switch (cmd) {
 case RTC_VL_READ:
  err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
  if (err)
   return err;

  tmp = flagreg & RX8010_FLAG_VLF ? RTC_VL_DATA_INVALID : 0;
  return put_user(tmp, (unsigned int __user *)arg);

 default:
  return -ENOIOCTLCMD;
 }
}

static const struct rtc_class_ops rx8010_rtc_ops = {
 .read_time = rx8010_get_time,
 .set_time = rx8010_set_time,
 .ioctl = rx8010_ioctl,
 .read_alarm = rx8010_read_alarm,
 .set_alarm = rx8010_set_alarm,
 .alarm_irq_enable = rx8010_alarm_irq_enable,
};

static const struct regmap_config rx8010_regmap_config = {
 .name = "rx8010-rtc",
 .reg_bits = 8,
 .val_bits = 8,
};

static int rx8010_probe(struct i2c_client *client)
{
 struct device *dev = &client->dev;
 struct rx8010_data *rx8010;
 int err = 0;

 rx8010 = devm_kzalloc(dev, sizeof(*rx8010), GFP_KERNEL);
 if (!rx8010)
  return -ENOMEM;

 i2c_set_clientdata(client, rx8010);

 rx8010->regs = devm_regmap_init_i2c(client, &rx8010_regmap_config);
 if (IS_ERR(rx8010->regs))
  return PTR_ERR(rx8010->regs);

 err = rx8010_init(dev);
 if (err)
  return err;

 rx8010->rtc = devm_rtc_allocate_device(dev);
 if (IS_ERR(rx8010->rtc))
  return PTR_ERR(rx8010->rtc);

 if (client->irq > 0) {
  unsigned long irqflags = IRQF_TRIGGER_LOW;

  if (dev_fwnode(&client->dev))
   irqflags = 0;

  err = devm_request_threaded_irq(dev, client->irq, NULL,
      rx8010_irq_1_handler,
      irqflags | IRQF_ONESHOT,
      "rx8010", client);
  if (err) {
   dev_err(dev, "unable to request IRQ\n");
   return err;
  }
 } else {
  clear_bit(RTC_FEATURE_ALARM, rx8010->rtc->features);
 }

 rx8010->rtc->ops = &rx8010_rtc_ops;
 rx8010->rtc->max_user_freq = 1;
 rx8010->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
 rx8010->rtc->range_max = RTC_TIMESTAMP_END_2099;

 return devm_rtc_register_device(rx8010->rtc);
}

static struct i2c_driver rx8010_driver = {
 .driver = {
  .name = "rtc-rx8010",
  .of_match_table = of_match_ptr(rx8010_of_match),
 },
 .probe  = rx8010_probe,
 .id_table = rx8010_id,
};

module_i2c_driver(rx8010_driver);

MODULE_AUTHOR("Akshay Bhat ");
MODULE_DESCRIPTION("Epson RX8010SJ RTC driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.