| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Holtek HT1380/HT1381 Serial Timekeeper Chip
- *
- * Communication with the chip is vendor-specific.
- * It is done via 3 GPIO pins: reset, clock, and data.
- * Describe in .dts this way:
- *
- * rtc {
- * compatible = "holtek,ht1380";
- * rst-gpios = <&gpio 19 GPIO_ACTIVE_LOW>;
- * clk-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
- * dat-gpios = <&gpio 21 GPIO_ACTIVE_HIGH>;
- * };
- *
- */
- #include <common.h>
- #include <dm.h>
- #include <rtc.h>
- #include <bcd.h>
- #include <asm/gpio.h>
- #include <linux/delay.h>
- struct ht1380_priv {
- struct gpio_desc rst_desc;
- struct gpio_desc clk_desc;
- struct gpio_desc dat_desc;
- };
- enum registers {
- SEC,
- MIN,
- HOUR,
- MDAY,
- MONTH,
- WDAY,
- YEAR,
- WP,
- N_REGS
- };
- enum hour_mode {
- AMPM_MODE = 0x80, /* RTC is in AM/PM mode */
- PM_NOW = 0x20, /* set if PM, clear if AM */
- };
- static const int BURST = 0xbe;
- static const int READ = 1;
- static void ht1380_half_period_delay(void)
- {
- /*
- * Delay for half a period. 1 us complies with the 500 KHz maximum
- * input serial clock limit given by the datasheet.
- */
- udelay(1);
- }
- static int ht1380_send_byte(struct ht1380_priv *priv, int byte)
- {
- int ret;
- for (int bit = 0; bit < 8; bit++) {
- ret = dm_gpio_set_value(&priv->dat_desc, byte >> bit & 1);
- if (ret)
- break;
- ht1380_half_period_delay();
- ret = dm_gpio_set_value(&priv->clk_desc, 1);
- if (ret)
- break;
- ht1380_half_period_delay();
- ret = dm_gpio_set_value(&priv->clk_desc, 0);
- if (ret)
- break;
- }
- return ret;
- }
- /*
- * Leave reset state. The transfer operation can then be started.
- */
- static int ht1380_reset_off(struct ht1380_priv *priv)
- {
- const unsigned int T_CC = 4; /* us, Reset to Clock Setup */
- int ret;
- /*
- * Leave RESET state.
- * Make sure we make the minimal delay required by the datasheet.
- */
- ret = dm_gpio_set_value(&priv->rst_desc, 0);
- udelay(T_CC);
- return ret;
- }
- /*
- * Enter reset state. Completes the transfer operation.
- */
- static int ht1380_reset_on(struct ht1380_priv *priv)
- {
- const unsigned int T_CWH = 4; /* us, Reset Inactive Time */
- int ret;
- /*
- * Enter RESET state.
- * Make sure we make the minimal delay required by the datasheet.
- */
- ret = dm_gpio_set_value(&priv->rst_desc, 1);
- udelay(T_CWH);
- return ret;
- }
- static int ht1380_rtc_get(struct udevice *dev, struct rtc_time *tm)
- {
- struct ht1380_priv *priv = dev_get_priv(dev);
- int ret, i, bit, reg[N_REGS];
- ret = dm_gpio_set_value(&priv->clk_desc, 0);
- if (ret)
- return ret;
- ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT);
- if (ret)
- return ret;
- ret = ht1380_reset_off(priv);
- if (ret)
- goto exit;
- ret = ht1380_send_byte(priv, BURST + READ);
- if (ret)
- goto exit;
- ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_IN);
- if (ret)
- goto exit;
- for (i = 0; i < N_REGS; i++) {
- reg[i] = 0;
- for (bit = 0; bit < 8; bit++) {
- ht1380_half_period_delay();
- ret = dm_gpio_set_value(&priv->clk_desc, 1);
- if (ret)
- goto exit;
- ht1380_half_period_delay();
- reg[i] |= dm_gpio_get_value(&priv->dat_desc) << bit;
- ret = dm_gpio_set_value(&priv->clk_desc, 0);
- if (ret)
- goto exit;
- }
- }
- ret = -EINVAL;
- /* Correctness check: some bits are always zero */
- if (reg[MIN] & 0x80 || reg[HOUR] & 0x40 || reg[MDAY] & 0xc0 ||
- reg[MONTH] & 0xe0 || reg[WDAY] & 0xf8 || reg[WP] & 0x7f)
- goto exit;
- /* Correctness check: some registers are always non-zero */
- if (!reg[MDAY] || !reg[MONTH] || !reg[WDAY])
- goto exit;
- tm->tm_sec = bcd2bin(reg[SEC]);
- tm->tm_min = bcd2bin(reg[MIN]);
- if (reg[HOUR] & AMPM_MODE) {
- /* AM-PM Mode, range is 01-12 */
- tm->tm_hour = bcd2bin(reg[HOUR] & 0x1f) % 12;
- if (reg[HOUR] & PM_NOW) {
- /* it is PM (otherwise AM) */
- tm->tm_hour += 12;
- }
- } else {
- /* 24-hour Mode, range is 0-23 */
- tm->tm_hour = bcd2bin(reg[HOUR]);
- }
- tm->tm_mday = bcd2bin(reg[MDAY]);
- tm->tm_mon = bcd2bin(reg[MONTH]);
- tm->tm_year = 2000 + bcd2bin(reg[YEAR]);
- tm->tm_wday = bcd2bin(reg[WDAY]) - 1;
- tm->tm_yday = 0;
- tm->tm_isdst = 0;
- ret = 0;
- exit:
- ht1380_reset_on(priv);
- return ret;
- }
- static int ht1380_write_protection_off(struct ht1380_priv *priv)
- {
- int ret;
- const int PROTECT = 0x8e;
- ret = ht1380_reset_off(priv);
- if (ret)
- return ret;
- ret = ht1380_send_byte(priv, PROTECT);
- if (ret)
- return ret;
- ret = ht1380_send_byte(priv, 0); /* WP bit is 0 */
- if (ret)
- return ret;
- return ht1380_reset_on(priv);
- }
- static int ht1380_rtc_set(struct udevice *dev, const struct rtc_time *tm)
- {
- struct ht1380_priv *priv = dev_get_priv(dev);
- int ret, i, reg[N_REGS];
- ret = dm_gpio_set_value(&priv->clk_desc, 0);
- if (ret)
- return ret;
- ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT);
- if (ret)
- goto exit;
- ret = ht1380_write_protection_off(priv);
- if (ret)
- goto exit;
- reg[SEC] = bin2bcd(tm->tm_sec);
- reg[MIN] = bin2bcd(tm->tm_min);
- reg[HOUR] = bin2bcd(tm->tm_hour);
- reg[MDAY] = bin2bcd(tm->tm_mday);
- reg[MONTH] = bin2bcd(tm->tm_mon);
- reg[WDAY] = bin2bcd(tm->tm_wday) + 1;
- reg[YEAR] = bin2bcd(tm->tm_year - 2000);
- reg[WP] = 0x80; /* WP bit is 1 */
- ret = ht1380_reset_off(priv);
- if (ret)
- goto exit;
- ret = ht1380_send_byte(priv, BURST);
- for (i = 0; i < N_REGS && ret; i++)
- ret = ht1380_send_byte(priv, reg[i]);
- exit:
- ht1380_reset_on(priv);
- return ret;
- }
- static int ht1380_probe(struct udevice *dev)
- {
- int ret;
- struct ht1380_priv *priv;
- priv = dev_get_priv(dev);
- if (!priv)
- return -EINVAL;
- ret = gpio_request_by_name(dev, "rst-gpios", 0,
- &priv->rst_desc, GPIOD_IS_OUT);
- if (ret)
- goto fail_rst;
- ret = gpio_request_by_name(dev, "clk-gpios", 0,
- &priv->clk_desc, GPIOD_IS_OUT);
- if (ret)
- goto fail_clk;
- ret = gpio_request_by_name(dev, "dat-gpios", 0,
- &priv->dat_desc, 0);
- if (ret)
- goto fail_dat;
- ret = ht1380_reset_on(priv);
- if (ret)
- goto fail;
- return 0;
- fail:
- dm_gpio_free(dev, &priv->dat_desc);
- fail_dat:
- dm_gpio_free(dev, &priv->clk_desc);
- fail_clk:
- dm_gpio_free(dev, &priv->rst_desc);
- fail_rst:
- return ret;
- }
- static int ht1380_remove(struct udevice *dev)
- {
- struct ht1380_priv *priv = dev_get_priv(dev);
- dm_gpio_free(dev, &priv->rst_desc);
- dm_gpio_free(dev, &priv->clk_desc);
- dm_gpio_free(dev, &priv->dat_desc);
- return 0;
- }
- static const struct rtc_ops ht1380_rtc_ops = {
- .get = ht1380_rtc_get,
- .set = ht1380_rtc_set,
- };
- static const struct udevice_id ht1380_rtc_ids[] = {
- { .compatible = "holtek,ht1380" },
- { }
- };
- U_BOOT_DRIVER(rtc_ht1380) = {
- .name = "rtc-ht1380",
- .id = UCLASS_RTC,
- .probe = ht1380_probe,
- .remove = ht1380_remove,
- .of_match = ht1380_rtc_ids,
- .ops = &ht1380_rtc_ops,
- .priv_auto = sizeof(struct ht1380_priv),
- };
|