lkml.org 
[lkml]   [2023]   [Jan]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    Date
    SubjectRe: [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions
    Den tor. 15. dec. 2022 kl. 16.48 skrev Hugo Villeneuve <hugo@hugovil.com>:
    >
    > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
    >
    > This will simplify the implementation of new variants into this driver.
    >
    > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
    > ---
    > drivers/rtc/rtc-pcf2127.c | 303 +++++++++++++++++++++++++++-----------
    > 1 file changed, 215 insertions(+), 88 deletions(-)
    >
    > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
    > index 38816ad065eb..3265878edc48 100644
    > --- a/drivers/rtc/rtc-pcf2127.c
    > +++ b/drivers/rtc/rtc-pcf2127.c
    > @@ -75,16 +75,19 @@
    > #define PCF2127_BIT_WD_CTL_CD0 BIT(6)
    > #define PCF2127_BIT_WD_CTL_CD1 BIT(7)
    > #define PCF2127_REG_WD_VAL 0x11
    > -/* Tamper timestamp registers */
    > -#define PCF2127_REG_TS_CTRL 0x12
    > +/* Tamper timestamp1 registers */
    > +#define PCF2127_REG_TS1_BASE 0x12
    > +/* Tamper timestamp registers common offsets (starting from base register) */
    > +#define PCF2127_OFFSET_TS_CTL 0
    > +#define PCF2127_OFFSET_TS_SC 1
    > +#define PCF2127_OFFSET_TS_MN 2
    > +#define PCF2127_OFFSET_TS_HR 3
    > +#define PCF2127_OFFSET_TS_DM 4
    > +#define PCF2127_OFFSET_TS_MO 5
    > +#define PCF2127_OFFSET_TS_YR 6
    > +/* Tamper timestamp registers common bits */
    > #define PCF2127_BIT_TS_CTRL_TSOFF BIT(6)
    > #define PCF2127_BIT_TS_CTRL_TSM BIT(7)
    > -#define PCF2127_REG_TS_SC 0x13
    > -#define PCF2127_REG_TS_MN 0x14
    > -#define PCF2127_REG_TS_HR 0x15
    > -#define PCF2127_REG_TS_DM 0x16
    > -#define PCF2127_REG_TS_MO 0x17
    > -#define PCF2127_REG_TS_YR 0x18
    > /*
    > * RAM registers
    > * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
    > @@ -108,6 +111,20 @@
    > PCF2127_BIT_CTRL2_WDTF | \
    > PCF2127_BIT_CTRL2_TSF2)
    >
    > +struct pcf21xx_ts_config {
    > + u8 regs_base; /* Base register to read timestamp values. */

    reg_base as there is only one.

    > + /* TS input pin driven to GND detection (supported by all variants): */
    > + u8 low_reg; /* Interrupt control register. */
    > + u8 low_bit; /* Interrupt flag in low_reg control register. */
    > + /* TS input pin driven to intermediate level between GND and supply
    > + * detection (optional feature depending on variant):
    > + */
    > + u8 inter_reg; /* Interrupt control register. */
    > + u8 inter_bit; /* Interrupt flag in inter_reg control register. */

    I had to read this a couple of times to understand it :)

    Maybe rename variables to low_ctl_reg, low_ctl_bit,
    inter_ctl_reg and inter_ctl_bit.

    > + u8 ie_reg; /* Interrupt enable control register. */
    > + u8 ie_bit; /* Interrupt enable bit. */
    > +};
    > +
    > struct pcf21xx_config {
    > int max_register;
    > unsigned int has_nvmem:1;
    > @@ -117,6 +134,9 @@ struct pcf21xx_config {
    > u8 reg_wd_ctl; /* Watchdog control register. */
    > u8 reg_wd_val; /* Watchdog value register. */
    > u8 reg_clkout; /* Clkout register. */
    > + unsigned int ts_count;
    > + struct pcf21xx_ts_config ts[4];

    You should create a driver constant with the maximum number
    of supported timestamps, something like:

    #define PCF2127_MAX_TS_SUPPORTED 1

    and raise it to 4 when pcf2131 support is added.

    > + struct attribute_group attribute_group;
    > };
    >
    > struct pcf2127 {
    > @@ -124,9 +144,9 @@ struct pcf2127 {
    > struct watchdog_device wdd;
    > struct regmap *regmap;
    > const struct pcf21xx_config *cfg;
    > - time64_t ts;
    > - bool ts_valid;
    > bool irq_enabled;
    > + time64_t ts[4]; /* Timestamp values. */
    > + bool ts_valid[4]; /* Timestamp valid indication. */
    > };
    >
    > /*
    > @@ -469,38 +489,39 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
    > }
    >
    > /*
    > - * This function reads ctrl2 register, caller is responsible for calling
    > - * pcf2127_wdt_active_ping()
    > + * This function reads one timestamp function data, caller is responsible for
    > + * calling pcf2127_wdt_active_ping()
    > */
    > -static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
    > +static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts,
    > + int ts_id)
    > {
    > struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
    > struct rtc_time tm;
    > int ret;
    > - unsigned char data[25];
    > + unsigned char data[7]; /* To store result of reading 7 consecutive
    > + * timestamp registers.
    > + */

    Remove comment about data variable.

    >
    > - ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
    > - sizeof(data));
    > + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->ts[ts_id].regs_base,
    > + data, sizeof(data));
    > if (ret) {
    > - dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
    > + dev_err(dev, "%s: bulk read error ret=%d\n", __func__, ret);
    > return ret;
    > }
    >
    > dev_dbg(dev,
    > - "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
    > - __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
    > - data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
    > - data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
    > - data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
    > - data[PCF2127_REG_TS_YR]);
    > -
    > - tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
    > - tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
    > - tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
    > - tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
    > + "%s: raw data is ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
    > + __func__, data[PCF2127_OFFSET_TS_SC], data[PCF2127_OFFSET_TS_MN],
    > + data[PCF2127_OFFSET_TS_HR], data[PCF2127_OFFSET_TS_DM],
    > + data[PCF2127_OFFSET_TS_MO], data[PCF2127_OFFSET_TS_YR]);
    > +
    > + tm.tm_sec = bcd2bin(data[PCF2127_OFFSET_TS_SC] & 0x7F);
    > + tm.tm_min = bcd2bin(data[PCF2127_OFFSET_TS_MN] & 0x7F);
    > + tm.tm_hour = bcd2bin(data[PCF2127_OFFSET_TS_HR] & 0x3F);
    > + tm.tm_mday = bcd2bin(data[PCF2127_OFFSET_TS_DM] & 0x3F);
    > /* TS_MO register (month) value range: 1-12 */
    > - tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
    > - tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
    > + tm.tm_mon = bcd2bin(data[PCF2127_OFFSET_TS_MO] & 0x1F) - 1;
    > + tm.tm_year = bcd2bin(data[PCF2127_OFFSET_TS_YR]);
    > if (tm.tm_year < 70)
    > tm.tm_year += 100; /* assume we are in 1970...2069 */
    >
    > @@ -514,18 +535,21 @@ static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
    > return 0;
    > };
    >
    > -static void pcf2127_rtc_ts_snapshot(struct device *dev)
    > +static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
    > {
    > struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
    > int ret;
    >
    > + if (ts_id >= pcf2127->cfg->ts_count)
    > + return;
    > +
    > /* Let userspace read the first timestamp */
    > - if (pcf2127->ts_valid)
    > + if (pcf2127->ts_valid[ts_id])
    > return;
    >
    > - ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts);
    > + ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts[ts_id], ts_id);
    > if (!ret)
    > - pcf2127->ts_valid = true;
    > + pcf2127->ts_valid[ts_id] = true;
    > }
    >
    > static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
    > @@ -546,7 +570,7 @@ static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
    > return IRQ_NONE;
    >
    > if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
    > - pcf2127_rtc_ts_snapshot(dev);
    > + pcf2127_rtc_ts_snapshot(dev, 0);
    >
    > if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
    > regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
    > @@ -575,28 +599,40 @@ static const struct rtc_class_ops pcf2127_rtc_ops = {
    >
    > /* sysfs interface */
    >
    > -static ssize_t timestamp0_store(struct device *dev,
    > - struct device_attribute *attr,
    > - const char *buf, size_t count)
    > +static ssize_t timestamp_store(struct device *dev,
    > + struct device_attribute *attr,
    > + const char *buf, size_t count, int ts_id)
    > {
    > struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
    > int ret;
    >
    > + if (ts_id >= pcf2127->cfg->ts_count)
    > + return 0;
    > +
    > if (pcf2127->irq_enabled) {
    > - pcf2127->ts_valid = false;
    > + pcf2127->ts_valid[ts_id] = false;
    > } else {
    > - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
    > - PCF2127_BIT_CTRL1_TSF1, 0);
    > + /* Always clear LOW interrupt bit. */
    > + ret = regmap_update_bits(pcf2127->regmap,
    > + pcf2127->cfg->ts[ts_id].low_reg,
    > + pcf2127->cfg->ts[ts_id].low_bit,
    > + 0);
    > +
    > if (ret) {
    > - dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
    > + dev_err(dev, "%s: update TS low ret=%d\n", __func__, ret);
    > return ret;
    > }
    >
    > - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
    > - PCF2127_BIT_CTRL2_TSF2, 0);
    > - if (ret) {
    > - dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
    > - return ret;
    > + if (pcf2127->cfg->ts[ts_id].inter_bit) {
    > + /* Clear INTERMEDIATE interrupt bit if supported. */
    > + ret = regmap_update_bits(pcf2127->regmap,
    > + pcf2127->cfg->ts[ts_id].inter_reg,
    > + pcf2127->cfg->ts[ts_id].inter_bit,
    > + 0);
    > + if (ret) {
    > + dev_err(dev, "%s: update TS intermediate ret=%d\n", __func__, ret);
    > + return ret;
    > + }
    > }
    >
    > ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
    > @@ -605,34 +641,63 @@ static ssize_t timestamp0_store(struct device *dev,
    > }
    >
    > return count;
    > +}
    > +
    > +static ssize_t timestamp0_store(struct device *dev,
    > + struct device_attribute *attr,
    > + const char *buf, size_t count)
    > +{
    > + return timestamp_store(dev, attr, buf, count, 0);
    > };
    >
    > -static ssize_t timestamp0_show(struct device *dev,
    > - struct device_attribute *attr, char *buf)
    > +static ssize_t timestamp_show(struct device *dev,
    > + struct device_attribute *attr, char *buf,
    > + int ts_id)
    > {
    > struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
    > - unsigned int ctrl1, ctrl2;
    > int ret;
    > time64_t ts;
    >
    > + if (ts_id >= pcf2127->cfg->ts_count)
    > + return 0;
    > +
    > if (pcf2127->irq_enabled) {
    > - if (!pcf2127->ts_valid)
    > + if (!pcf2127->ts_valid[ts_id])
    > return 0;
    > - ts = pcf2127->ts;
    > + ts = pcf2127->ts[ts_id];
    > } else {
    > - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
    > - if (ret)
    > - return 0;
    > + u8 valid_low = 0;
    > + u8 valid_inter = 0;
    > + unsigned int ctrl;
    >
    > - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
    > + /* Check if TS input pin is driven to GND, supported by all
    > + * variants.
    > + */
    > + ret = regmap_read(pcf2127->regmap,
    > + pcf2127->cfg->ts[ts_id].low_reg,
    > + &ctrl);
    > if (ret)
    > return 0;
    >
    > - if (!(ctrl1 & PCF2127_BIT_CTRL1_TSF1) &&
    > - !(ctrl2 & PCF2127_BIT_CTRL2_TSF2))
    > + valid_low = ctrl & pcf2127->cfg->ts[ts_id].low_bit;
    > +
    > + if (pcf2127->cfg->ts[ts_id].inter_bit) {
    > + /* Check if TS input pin is driven to intermediate level
    > + * between GND and supply, if supported by variant.
    > + */
    > + ret = regmap_read(pcf2127->regmap,
    > + pcf2127->cfg->ts[ts_id].inter_reg,
    > + &ctrl);
    > + if (ret)
    > + return 0;
    > +
    > + valid_inter = ctrl & pcf2127->cfg->ts[ts_id].inter_bit;
    > + }
    > +
    > + if (!valid_low && !valid_inter)
    > return 0;
    >
    > - ret = pcf2127_rtc_ts_read(dev->parent, &ts);
    > + ret = pcf2127_rtc_ts_read(dev->parent, &ts, ts_id);
    > if (ret)
    > return 0;
    >
    > @@ -641,6 +706,12 @@ static ssize_t timestamp0_show(struct device *dev,
    > return ret;
    > }
    > return sprintf(buf, "%llu\n", (unsigned long long)ts);
    > +}
    > +
    > +static ssize_t timestamp0_show(struct device *dev,
    > + struct device_attribute *attr, char *buf)
    > +{
    > + return timestamp_show(dev, attr, buf, 0);
    > };
    >
    > static DEVICE_ATTR_RW(timestamp0);
    > @@ -650,10 +721,6 @@ static struct attribute *pcf2127_attrs[] = {
    > NULL
    > };
    >
    > -static const struct attribute_group pcf2127_attr_group = {
    > - .attrs = pcf2127_attrs,
    > -};
    > -
    > enum pcf21xx_type {
    > PCF2127,
    > PCF2129,
    > @@ -670,6 +737,19 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
    > .reg_wd_ctl = PCF2127_REG_WD_CTL,
    > .reg_wd_val = PCF2127_REG_WD_VAL,
    > .reg_clkout = PCF2127_REG_CLKOUT,
    > + .ts_count = 1,
    > + .ts[0] = {
    > + .regs_base = PCF2127_REG_TS1_BASE,
    > + .low_reg = PCF2127_REG_CTRL1,
    > + .low_bit = PCF2127_BIT_CTRL1_TSF1,
    > + .inter_reg = PCF2127_REG_CTRL2,
    > + .inter_bit = PCF2127_BIT_CTRL2_TSF2,
    > + .ie_reg = PCF2127_REG_CTRL2,
    > + .ie_bit = PCF2127_BIT_CTRL2_TSIE,
    > + },
    > + .attribute_group = {
    > + .attrs = pcf2127_attrs,
    > + },
    > },
    > [PCF2129] = {
    > .max_register = 0x19,
    > @@ -680,15 +760,81 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
    > .reg_wd_ctl = PCF2127_REG_WD_CTL,
    > .reg_wd_val = PCF2127_REG_WD_VAL,
    > .reg_clkout = PCF2127_REG_CLKOUT,
    > + .ts_count = 1,
    > + .ts[0] = {
    > + .regs_base = PCF2127_REG_TS1_BASE,
    > + .low_reg = PCF2127_REG_CTRL1,
    > + .low_bit = PCF2127_BIT_CTRL1_TSF1,
    > + .inter_reg = PCF2127_REG_CTRL2,
    > + .inter_bit = PCF2127_BIT_CTRL2_TSF2,
    > + .ie_reg = PCF2127_REG_CTRL2,
    > + .ie_bit = PCF2127_BIT_CTRL2_TSIE,
    > + },
    > + .attribute_group = {
    > + .attrs = pcf2127_attrs,
    > + },
    > },
    > };
    >
    > +/*
    > + * Enable timestamp function and corresponding interrupt(s).
    > + */
    > +static int pcf2127_enable_ts(struct device *dev, int ts_id)
    > +{
    > + struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
    > + int ret;
    > +
    > + if (ts_id >= pcf2127->cfg->ts_count) {
    > + dev_err(dev, "%s: invalid tamper detection ID (%d)\n",
    > + __func__, ts_id);
    > + return -EINVAL;
    > + }
    > +
    > + /* Enable timestamp function. */
    > + ret = regmap_update_bits(pcf2127->regmap,
    > + pcf2127->cfg->ts[ts_id].regs_base,
    > + PCF2127_BIT_TS_CTRL_TSOFF |
    > + PCF2127_BIT_TS_CTRL_TSM,
    > + PCF2127_BIT_TS_CTRL_TSM);
    > + if (ret) {
    > + dev_err(dev, "%s: tamper detection config (ts%d_ctrl) failed\n",
    > + __func__, ts_id);
    > + return ret;
    > + }
    > +
    > + /* TS input pin driven to GND detection is supported by all variants.
    > + * Make sure that low_bit is defined.
    > + */
    > + if (pcf2127->cfg->ts[ts_id].low_bit == 0) {
    > + dev_err(dev, "%s: tamper detection LOW configuration invalid\n",
    > + __func__);
    > + return ret;
    > + }
    > +
    > + /*
    > + * Enable interrupt generation when TSF timestamp flag is set.
    > + * Interrupt signals are open-drain outputs and can be left floating if
    > + * unused.
    > + */
    > + ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->ts[ts_id].ie_reg,
    > + pcf2127->cfg->ts[ts_id].ie_bit,
    > + pcf2127->cfg->ts[ts_id].ie_bit);
    > + if (ret) {
    > + dev_err(dev, "%s: tamper detection TSIE%d config failed\n",
    > + __func__, ts_id);
    > + return ret;
    > + }
    > +
    > + return ret;
    > +}
    > +
    > static int pcf2127_probe(struct device *dev, struct regmap *regmap,
    > int alarm_irq, const char *name, const struct pcf21xx_config *config)
    > {
    > struct pcf2127 *pcf2127;
    > int ret = 0;
    > unsigned int val;
    > + int i;
    >
    > dev_dbg(dev, "%s\n", __func__);
    >
    > @@ -813,34 +959,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
    > }
    >
    > /*
    > - * Enable timestamp function and store timestamp of first trigger
    > - * event until TSF1 and TSF2 interrupt flags are cleared.
    > + * Enable timestamp functions 1 to 4.
    > */
    > - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL,
    > - PCF2127_BIT_TS_CTRL_TSOFF |
    > - PCF2127_BIT_TS_CTRL_TSM,
    > - PCF2127_BIT_TS_CTRL_TSM);
    > - if (ret) {
    > - dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n",
    > - __func__);
    > - return ret;
    > - }
    > -
    > - /*
    > - * Enable interrupt generation when TSF1 or TSF2 timestamp flags
    > - * are set. Interrupt signal is an open-drain output and can be
    > - * left floating if unused.
    > - */
    > - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
    > - PCF2127_BIT_CTRL2_TSIE,
    > - PCF2127_BIT_CTRL2_TSIE);
    > - if (ret) {
    > - dev_err(dev, "%s: tamper detection config (ctrl2) failed\n",
    > - __func__);
    > - return ret;
    > + for (i = 0; i < pcf2127->cfg->ts_count; i++) {

    Just define the loop counter variable inline.

    for (int i = 0; i < pcf2127->cfg->ts_count; i++) {

    > + ret = pcf2127_enable_ts(dev, i);
    > + if (ret)
    > + return ret;
    > }
    >
    > - ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group);
    > + ret = rtc_add_group(pcf2127->rtc, &pcf2127->cfg->attribute_group);
    > if (ret) {
    > dev_err(dev, "%s: tamper sysfs registering failed\n",
    > __func__);
    > --
    > 2.30.2
    >

    \
     
     \ /
      Last update: 2023-03-26 23:31    [W:6.351 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site