summaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-ds1347.c
blob: c82b4c0503264031938095353d2129acff16f846 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* rtc-ds1347.c
 *
 * Driver for Dallas Semiconductor DS1347 Low Current, SPI Compatible
 * Real Time Clock
 *
 * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/bcd.h>

/* Registers in ds1347 rtc */

#define DS1347_SECONDS_REG	0x01
#define DS1347_MINUTES_REG	0x03
#define DS1347_HOURS_REG	0x05
#define DS1347_DATE_REG		0x07
#define DS1347_MONTH_REG	0x09
#define DS1347_DAY_REG		0x0B
#define DS1347_YEAR_REG		0x0D
#define DS1347_CONTROL_REG	0x0F
#define DS1347_STATUS_REG	0x17
#define DS1347_CLOCK_BURST	0x3F

static int ds1347_read_reg(struct device *dev, unsigned char address,
				unsigned char *data)
{
	struct spi_device *spi = to_spi_device(dev);

	*data = address | 0x80;

	return spi_write_then_read(spi, data, 1, data, 1);
}

static int ds1347_write_reg(struct device *dev, unsigned char address,
				unsigned char data)
{
	struct spi_device *spi = to_spi_device(dev);
	unsigned char buf[2];

	buf[0] = address & 0x7F;
	buf[1] = data;

	return spi_write_then_read(spi, buf, 2, NULL, 0);
}

static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
{
	struct spi_device *spi = to_spi_device(dev);
	int err;
	unsigned char buf[8];

	buf[0] = DS1347_CLOCK_BURST | 0x80;

	err = spi_write_then_read(spi, buf, 1, buf, 8);
	if (err)
		return err;

	dt->tm_sec = bcd2bin(buf[0]);
	dt->tm_min = bcd2bin(buf[1]);
	dt->tm_hour = bcd2bin(buf[2] & 0x3F);
	dt->tm_mday = bcd2bin(buf[3]);
	dt->tm_mon = bcd2bin(buf[4]) - 1;
	dt->tm_wday = bcd2bin(buf[5]) - 1;
	dt->tm_year = bcd2bin(buf[6]) + 100;

	return rtc_valid_tm(dt);
}

static int ds1347_set_time(struct device *dev, struct rtc_time *dt)
{
	struct spi_device *spi = to_spi_device(dev);
	unsigned char buf[9];

	buf[0] = DS1347_CLOCK_BURST & 0x7F;
	buf[1] = bin2bcd(dt->tm_sec);
	buf[2] = bin2bcd(dt->tm_min);
	buf[3] = (bin2bcd(dt->tm_hour) & 0x3F);
	buf[4] = bin2bcd(dt->tm_mday);
	buf[5] = bin2bcd(dt->tm_mon + 1);
	buf[6] = bin2bcd(dt->tm_wday + 1);

	/* year in linux is from 1900 i.e in range of 100
	in rtc it is from 00 to 99 */
	dt->tm_year = dt->tm_year % 100;

	buf[7] = bin2bcd(dt->tm_year);
	buf[8] = bin2bcd(0x00);

	/* write the rtc settings */
	return spi_write_then_read(spi, buf, 9, NULL, 0);
}

static const struct rtc_class_ops ds1347_rtc_ops = {
	.read_time = ds1347_read_time,
	.set_time = ds1347_set_time,
};

static int ds1347_probe(struct spi_device *spi)
{
	struct rtc_device *rtc;
	unsigned char data;
	int res;

	/* spi setup with ds1347 in mode 3 and bits per word as 8 */
	spi->mode = SPI_MODE_3;
	spi->bits_per_word = 8;
	spi_setup(spi);

	/* RTC Settings */
	res = ds1347_read_reg(&spi->dev, DS1347_SECONDS_REG, &data);
	if (res)
		return res;

	/* Disable the write protect of rtc */
	ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
	data = data & ~(1<<7);
	ds1347_write_reg(&spi->dev, DS1347_CONTROL_REG, data);

	/* Enable the oscillator , disable the oscillator stop flag,
	 and glitch filter to reduce current consumption */
	ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
	data = data & 0x1B;
	ds1347_write_reg(&spi->dev, DS1347_STATUS_REG, data);

	/* display the settings */
	ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
	dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data);

	ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
	dev_info(&spi->dev, "DS1347 RTC Status Reg = 0x%02x\n", data);

	rtc = devm_rtc_device_register(&spi->dev, "ds1347",
				&ds1347_rtc_ops, THIS_MODULE);

	if (IS_ERR(rtc))
		return PTR_ERR(rtc);

	spi_set_drvdata(spi, rtc);

	return 0;
}

static struct spi_driver ds1347_driver = {
	.driver = {
		.name = "ds1347",
		.owner = THIS_MODULE,
	},
	.probe = ds1347_probe,
};

module_spi_driver(ds1347_driver);

MODULE_DESCRIPTION("DS1347 SPI RTC DRIVER");
MODULE_AUTHOR("Raghavendra C Ganiga <ravi23ganiga@gmail.com>");
MODULE_LICENSE("GPL v2");
) return tag_names _safe_re = re.compile(r'^((\.\.)?/+)+') def make_path_safe(path): """Make path safe by making it relative and local """ return _safe_re.sub('', path) or '.' def hardlinkable(mode): """return True if we support hardlinked items of this type""" return stat.S_ISREG(mode) or stat.S_ISBLK(mode) or stat.S_ISCHR(mode) or stat.S_ISFIFO(mode) def scandir_keyfunc(dirent): try: return (0, dirent.inode()) except OSError as e: # maybe a permission denied error while doing a stat() on the dirent logger.debug('scandir_inorder: Unable to stat %s: %s', dirent.path, e) # order this dirent after all the others lexically by file name # we may not break the whole scandir just because of an exception in one dirent # ignore the exception for now, since another stat will be done later anyways # (or the entry will be skipped by an exclude pattern) return (1, dirent.name) def scandir_inorder(*, path, fd=None): arg = fd if fd is not None else path return sorted(os.scandir(arg), key=scandir_keyfunc) def secure_erase(path, *, avoid_collateral_damage): """Attempt to securely erase a file by writing random data over it before deleting it. If avoid_collateral_damage is True, we only secure erase if the total link count is 1, otherwise we just do a normal "delete" (unlink) without first overwriting it with random. This avoids other hardlinks pointing to same inode as <path> getting damaged, but might be less secure. A typical scenario where this is useful are quick "hardlink copies" of bigger directories. If avoid_collateral_damage is False, we always secure erase. If there are hardlinks pointing to the same inode as <path>, they will contain random garbage afterwards. """ with open(path, 'r+b') as fd: st = os.stat(fd.fileno()) if not (st.st_nlink > 1 and avoid_collateral_damage): fd.write(os.urandom(st.st_size)) fd.flush() os.fsync(fd.fileno()) os.unlink(path) def safe_unlink(path): """ Safely unlink (delete) *path*. If we run out of space while deleting the file, we try truncating it first. BUT we truncate only if path is the only hardlink referring to this content. Use this when deleting potentially large files when recovering from a VFS error such as ENOSPC. It can help a full file system recover. Refer to the "File system interaction" section in repository.py for further explanations. """ try: os.unlink(path) except OSError as unlink_err: if unlink_err.errno != errno.ENOSPC: # not free space related, give up here. raise # we ran out of space while trying to delete the file. st = os.stat(path) if st.st_nlink > 1: # rather give up here than cause collateral damage to the other hardlink. raise # no other hardlink! try to recover free space by truncating this file. try: # Do not create *path* if it does not exist, open for truncation in r+b mode (=O_RDWR|O_BINARY). with open(path, 'r+b') as fd: fd.truncate() except OSError: # truncate didn't work, so we still have the original unlink issue - give up: raise unlink_err else: # successfully truncated the file, try again deleting it: os.unlink(path) def dash_open(path, mode): assert '+' not in mode # the streams are either r or w, but never both if path == '-': stream = sys.stdin if 'r' in mode else sys.stdout return stream.buffer if 'b' in mode else stream else: return open(path, mode) def O_(*flags): result = 0 for flag in flags: result |= getattr(os, 'O_' + flag, 0) return result flags_base = O_('BINARY', 'NOCTTY', 'RDONLY') flags_special = flags_base | O_('NOFOLLOW') # BLOCK == wait when reading devices or fifos flags_special_follow = flags_base # BLOCK == wait when reading symlinked devices or fifos flags_normal = flags_base | O_('NONBLOCK', 'NOFOLLOW') flags_noatime = flags_normal | O_('NOATIME') flags_dir = O_('DIRECTORY', 'RDONLY', 'NOFOLLOW') def os_open(*, flags, path=None, parent_fd=None, name=None, noatime=False): """ Use os.open to open a fs item. If parent_fd and name are given, they are preferred and openat will be used, path is not used in this case. :param path: full (but not necessarily absolute) path :param parent_fd: open directory file descriptor :param name: name relative to parent_fd :param flags: open flags for os.open() (int) :param noatime: True if access time shall be preserved :return: file descriptor """ if name and parent_fd is not None: # name is neither None nor empty, parent_fd given. fname = name # use name relative to parent_fd else: fname, parent_fd = path, None # just use the path if is_win32 and os.path.isdir(fname): # Directories can not be opened on Windows. return None _flags_normal = flags if noatime: _flags_noatime = _flags_normal | O_('NOATIME') try: # if we have O_NOATIME, this likely will succeed if we are root or owner of file: fd = os.open(fname, _flags_noatime, dir_fd=parent_fd) except PermissionError: if _flags_noatime == _flags_normal: # we do not have O_NOATIME, no need to try again: raise # Was this EPERM due to the O_NOATIME flag? Try again without it: fd = os.open(fname, _flags_normal, dir_fd=parent_fd) except OSError as exc: # O_NOATIME causes EROFS when accessing a volume shadow copy in WSL1 from . import workarounds if 'retry_erofs' in workarounds and exc.errno == errno.EROFS and _flags_noatime != _flags_normal: fd = os.open(fname, _flags_normal, dir_fd=parent_fd) else: raise else: fd = os.open(fname, _flags_normal, dir_fd=parent_fd) return fd def os_stat(*, path=None, parent_fd=None, name=None, follow_symlinks=False): """ Use os.stat to open a fs item. If parent_fd and name are given, they are preferred and statat will be used, path is not used in this case. :param path: full (but not necessarily absolute) path :param parent_fd: open directory file descriptor :param name: name relative to parent_fd :return: stat info """ if name