cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rtc-rs5c348.c (6212B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * A SPI driver for the Ricoh RS5C348 RTC
      4 *
      5 * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
      6 *
      7 * The board specific init code should provide characteristics of this
      8 * device:
      9 *     Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS
     10 */
     11
     12#include <linux/bcd.h>
     13#include <linux/delay.h>
     14#include <linux/device.h>
     15#include <linux/errno.h>
     16#include <linux/init.h>
     17#include <linux/kernel.h>
     18#include <linux/string.h>
     19#include <linux/slab.h>
     20#include <linux/rtc.h>
     21#include <linux/workqueue.h>
     22#include <linux/spi/spi.h>
     23#include <linux/module.h>
     24
     25#define RS5C348_REG_SECS	0
     26#define RS5C348_REG_MINS	1
     27#define RS5C348_REG_HOURS	2
     28#define RS5C348_REG_WDAY	3
     29#define RS5C348_REG_DAY	4
     30#define RS5C348_REG_MONTH	5
     31#define RS5C348_REG_YEAR	6
     32#define RS5C348_REG_CTL1	14
     33#define RS5C348_REG_CTL2	15
     34
     35#define RS5C348_SECS_MASK	0x7f
     36#define RS5C348_MINS_MASK	0x7f
     37#define RS5C348_HOURS_MASK	0x3f
     38#define RS5C348_WDAY_MASK	0x03
     39#define RS5C348_DAY_MASK	0x3f
     40#define RS5C348_MONTH_MASK	0x1f
     41
     42#define RS5C348_BIT_PM	0x20	/* REG_HOURS */
     43#define RS5C348_BIT_Y2K	0x80	/* REG_MONTH */
     44#define RS5C348_BIT_24H	0x20	/* REG_CTL1 */
     45#define RS5C348_BIT_XSTP	0x10	/* REG_CTL2 */
     46#define RS5C348_BIT_VDET	0x40	/* REG_CTL2 */
     47
     48#define RS5C348_CMD_W(addr)	(((addr) << 4) | 0x08)	/* single write */
     49#define RS5C348_CMD_R(addr)	(((addr) << 4) | 0x0c)	/* single read */
     50#define RS5C348_CMD_MW(addr)	(((addr) << 4) | 0x00)	/* burst write */
     51#define RS5C348_CMD_MR(addr)	(((addr) << 4) | 0x04)	/* burst read */
     52
     53struct rs5c348_plat_data {
     54	struct rtc_device *rtc;
     55	int rtc_24h;
     56};
     57
     58static int
     59rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm)
     60{
     61	struct spi_device *spi = to_spi_device(dev);
     62	struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev);
     63	u8 txbuf[5+7], *txp;
     64	int ret;
     65
     66	ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
     67	if (ret < 0)
     68		return ret;
     69	if (ret & RS5C348_BIT_XSTP) {
     70		txbuf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
     71		txbuf[1] = 0;
     72		ret = spi_write_then_read(spi, txbuf, 2, NULL, 0);
     73		if (ret < 0)
     74			return ret;
     75	}
     76
     77	/* Transfer 5 bytes before writing SEC.  This gives 31us for carry. */
     78	txp = txbuf;
     79	txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
     80	txbuf[1] = 0;	/* dummy */
     81	txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
     82	txbuf[3] = 0;	/* dummy */
     83	txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */
     84	txp = &txbuf[5];
     85	txp[RS5C348_REG_SECS] = bin2bcd(tm->tm_sec);
     86	txp[RS5C348_REG_MINS] = bin2bcd(tm->tm_min);
     87	if (pdata->rtc_24h) {
     88		txp[RS5C348_REG_HOURS] = bin2bcd(tm->tm_hour);
     89	} else {
     90		/* hour 0 is AM12, noon is PM12 */
     91		txp[RS5C348_REG_HOURS] = bin2bcd((tm->tm_hour + 11) % 12 + 1) |
     92			(tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0);
     93	}
     94	txp[RS5C348_REG_WDAY] = bin2bcd(tm->tm_wday);
     95	txp[RS5C348_REG_DAY] = bin2bcd(tm->tm_mday);
     96	txp[RS5C348_REG_MONTH] = bin2bcd(tm->tm_mon + 1) |
     97		(tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0);
     98	txp[RS5C348_REG_YEAR] = bin2bcd(tm->tm_year % 100);
     99	/* write in one transfer to avoid data inconsistency */
    100	ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0);
    101	udelay(62);	/* Tcsr 62us */
    102	return ret;
    103}
    104
    105static int
    106rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
    107{
    108	struct spi_device *spi = to_spi_device(dev);
    109	struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev);
    110	u8 txbuf[5], rxbuf[7];
    111	int ret;
    112
    113	ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
    114	if (ret < 0)
    115		return ret;
    116	if (ret & RS5C348_BIT_VDET)
    117		dev_warn(&spi->dev, "voltage-low detected.\n");
    118	if (ret & RS5C348_BIT_XSTP) {
    119		dev_warn(&spi->dev, "oscillator-stop detected.\n");
    120		return -EINVAL;
    121	}
    122
    123	/* Transfer 5 byte befores reading SEC.  This gives 31us for carry. */
    124	txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
    125	txbuf[1] = 0;	/* dummy */
    126	txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
    127	txbuf[3] = 0;	/* dummy */
    128	txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */
    129
    130	/* read in one transfer to avoid data inconsistency */
    131	ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
    132				  rxbuf, sizeof(rxbuf));
    133	udelay(62);	/* Tcsr 62us */
    134	if (ret < 0)
    135		return ret;
    136
    137	tm->tm_sec = bcd2bin(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK);
    138	tm->tm_min = bcd2bin(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
    139	tm->tm_hour = bcd2bin(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
    140	if (!pdata->rtc_24h) {
    141		if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM) {
    142			tm->tm_hour -= 20;
    143			tm->tm_hour %= 12;
    144			tm->tm_hour += 12;
    145		} else
    146			tm->tm_hour %= 12;
    147	}
    148	tm->tm_wday = bcd2bin(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
    149	tm->tm_mday = bcd2bin(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
    150	tm->tm_mon =
    151		bcd2bin(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1;
    152	/* year is 1900 + tm->tm_year */
    153	tm->tm_year = bcd2bin(rxbuf[RS5C348_REG_YEAR]) +
    154		((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0);
    155
    156	return 0;
    157}
    158
    159static const struct rtc_class_ops rs5c348_rtc_ops = {
    160	.read_time	= rs5c348_rtc_read_time,
    161	.set_time	= rs5c348_rtc_set_time,
    162};
    163
    164static int rs5c348_probe(struct spi_device *spi)
    165{
    166	int ret;
    167	struct rtc_device *rtc;
    168	struct rs5c348_plat_data *pdata;
    169
    170	pdata = devm_kzalloc(&spi->dev, sizeof(struct rs5c348_plat_data),
    171				GFP_KERNEL);
    172	if (!pdata)
    173		return -ENOMEM;
    174	spi->dev.platform_data = pdata;
    175
    176	/* Check D7 of SECOND register */
    177	ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS));
    178	if (ret < 0 || (ret & 0x80)) {
    179		dev_err(&spi->dev, "not found.\n");
    180		return ret;
    181	}
    182
    183	dev_info(&spi->dev, "spiclk %u KHz.\n",
    184		 (spi->max_speed_hz + 500) / 1000);
    185
    186	ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1));
    187	if (ret < 0)
    188		return ret;
    189	if (ret & RS5C348_BIT_24H)
    190		pdata->rtc_24h = 1;
    191
    192	rtc = devm_rtc_allocate_device(&spi->dev);
    193	if (IS_ERR(rtc))
    194		return PTR_ERR(rtc);
    195
    196	pdata->rtc = rtc;
    197
    198	rtc->ops = &rs5c348_rtc_ops;
    199
    200	return devm_rtc_register_device(rtc);
    201}
    202
    203static struct spi_driver rs5c348_driver = {
    204	.driver = {
    205		.name	= "rtc-rs5c348",
    206	},
    207	.probe	= rs5c348_probe,
    208};
    209
    210module_spi_driver(rs5c348_driver);
    211
    212MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
    213MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver");
    214MODULE_LICENSE("GPL");
    215MODULE_ALIAS("spi:rtc-rs5c348");