brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · dd4a62e Raw
293 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ST M48T86 / Dallas DS12887 RTC driver4 * Copyright (c) 2006 Tower Technologies5 *6 * Author: Alessandro Zummo <a.zummo@towertech.it>7 *8 * This drivers only supports the clock running in BCD and 24H mode.9 * If it will be ever adapted to binary and 12H mode, care must be taken10 * to not introduce bugs.11 */12 13#include <linux/module.h>14#include <linux/mod_devicetable.h>15#include <linux/rtc.h>16#include <linux/platform_device.h>17#include <linux/bcd.h>18#include <linux/io.h>19 20#define M48T86_SEC		0x0021#define M48T86_SECALRM		0x0122#define M48T86_MIN		0x0223#define M48T86_MINALRM		0x0324#define M48T86_HOUR		0x0425#define M48T86_HOURALRM		0x0526#define M48T86_DOW		0x06 /* 1 = sunday */27#define M48T86_DOM		0x0728#define M48T86_MONTH		0x08 /* 1 - 12 */29#define M48T86_YEAR		0x09 /* 0 - 99 */30#define M48T86_A		0x0a31#define M48T86_B		0x0b32#define M48T86_B_SET		BIT(7)33#define M48T86_B_DM		BIT(2)34#define M48T86_B_H24		BIT(1)35#define M48T86_C		0x0c36#define M48T86_D		0x0d37#define M48T86_D_VRT		BIT(7)38#define M48T86_NVRAM(x)		(0x0e + (x))39#define M48T86_NVRAM_LEN	11440 41struct m48t86_rtc_info {42	void __iomem *index_reg;43	void __iomem *data_reg;44	struct rtc_device *rtc;45};46 47static unsigned char m48t86_readb(struct device *dev, unsigned long addr)48{49	struct m48t86_rtc_info *info = dev_get_drvdata(dev);50	unsigned char value;51 52	writeb(addr, info->index_reg);53	value = readb(info->data_reg);54 55	return value;56}57 58static void m48t86_writeb(struct device *dev,59			  unsigned char value, unsigned long addr)60{61	struct m48t86_rtc_info *info = dev_get_drvdata(dev);62 63	writeb(addr, info->index_reg);64	writeb(value, info->data_reg);65}66 67static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)68{69	unsigned char reg;70 71	reg = m48t86_readb(dev, M48T86_B);72 73	if (reg & M48T86_B_DM) {74		/* data (binary) mode */75		tm->tm_sec	= m48t86_readb(dev, M48T86_SEC);76		tm->tm_min	= m48t86_readb(dev, M48T86_MIN);77		tm->tm_hour	= m48t86_readb(dev, M48T86_HOUR) & 0x3f;78		tm->tm_mday	= m48t86_readb(dev, M48T86_DOM);79		/* tm_mon is 0-11 */80		tm->tm_mon	= m48t86_readb(dev, M48T86_MONTH) - 1;81		tm->tm_year	= m48t86_readb(dev, M48T86_YEAR) + 100;82		tm->tm_wday	= m48t86_readb(dev, M48T86_DOW);83	} else {84		/* bcd mode */85		tm->tm_sec	= bcd2bin(m48t86_readb(dev, M48T86_SEC));86		tm->tm_min	= bcd2bin(m48t86_readb(dev, M48T86_MIN));87		tm->tm_hour	= bcd2bin(m48t86_readb(dev, M48T86_HOUR) &88					  0x3f);89		tm->tm_mday	= bcd2bin(m48t86_readb(dev, M48T86_DOM));90		/* tm_mon is 0-11 */91		tm->tm_mon	= bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1;92		tm->tm_year	= bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100;93		tm->tm_wday	= bcd2bin(m48t86_readb(dev, M48T86_DOW));94	}95 96	/* correct the hour if the clock is in 12h mode */97	if (!(reg & M48T86_B_H24))98		if (m48t86_readb(dev, M48T86_HOUR) & 0x80)99			tm->tm_hour += 12;100 101	return 0;102}103 104static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)105{106	unsigned char reg;107 108	reg = m48t86_readb(dev, M48T86_B);109 110	/* update flag and 24h mode */111	reg |= M48T86_B_SET | M48T86_B_H24;112	m48t86_writeb(dev, reg, M48T86_B);113 114	if (reg & M48T86_B_DM) {115		/* data (binary) mode */116		m48t86_writeb(dev, tm->tm_sec, M48T86_SEC);117		m48t86_writeb(dev, tm->tm_min, M48T86_MIN);118		m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR);119		m48t86_writeb(dev, tm->tm_mday, M48T86_DOM);120		m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH);121		m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR);122		m48t86_writeb(dev, tm->tm_wday, M48T86_DOW);123	} else {124		/* bcd mode */125		m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC);126		m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN);127		m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR);128		m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM);129		m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH);130		m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR);131		m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW);132	}133 134	/* update ended */135	reg &= ~M48T86_B_SET;136	m48t86_writeb(dev, reg, M48T86_B);137 138	return 0;139}140 141static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)142{143	unsigned char reg;144 145	reg = m48t86_readb(dev, M48T86_B);146 147	seq_printf(seq, "mode\t\t: %s\n",148		   (reg & M48T86_B_DM) ? "binary" : "bcd");149 150	reg = m48t86_readb(dev, M48T86_D);151 152	seq_printf(seq, "battery\t\t: %s\n",153		   (reg & M48T86_D_VRT) ? "ok" : "exhausted");154 155	return 0;156}157 158static const struct rtc_class_ops m48t86_rtc_ops = {159	.read_time	= m48t86_rtc_read_time,160	.set_time	= m48t86_rtc_set_time,161	.proc		= m48t86_rtc_proc,162};163 164static int m48t86_nvram_read(void *priv, unsigned int off, void *buf,165			     size_t count)166{167	struct device *dev = priv;168	unsigned int i;169 170	for (i = 0; i < count; i++)171		((u8 *)buf)[i] = m48t86_readb(dev, M48T86_NVRAM(off + i));172 173	return 0;174}175 176static int m48t86_nvram_write(void *priv, unsigned int off, void *buf,177			      size_t count)178{179	struct device *dev = priv;180	unsigned int i;181 182	for (i = 0; i < count; i++)183		m48t86_writeb(dev, ((u8 *)buf)[i], M48T86_NVRAM(off + i));184 185	return 0;186}187 188/*189 * The RTC is an optional feature at purchase time on some Technologic Systems190 * boards. Verify that it actually exists by checking if the last two bytes191 * of the NVRAM can be changed.192 *193 * This is based on the method used in their rtc7800.c example.194 */195static bool m48t86_verify_chip(struct platform_device *pdev)196{197	unsigned int offset0 = M48T86_NVRAM(M48T86_NVRAM_LEN - 2);198	unsigned int offset1 = M48T86_NVRAM(M48T86_NVRAM_LEN - 1);199	unsigned char tmp0, tmp1;200 201	tmp0 = m48t86_readb(&pdev->dev, offset0);202	tmp1 = m48t86_readb(&pdev->dev, offset1);203 204	m48t86_writeb(&pdev->dev, 0x00, offset0);205	m48t86_writeb(&pdev->dev, 0x55, offset1);206	if (m48t86_readb(&pdev->dev, offset1) == 0x55) {207		m48t86_writeb(&pdev->dev, 0xaa, offset1);208		if (m48t86_readb(&pdev->dev, offset1) == 0xaa &&209		    m48t86_readb(&pdev->dev, offset0) == 0x00) {210			m48t86_writeb(&pdev->dev, tmp0, offset0);211			m48t86_writeb(&pdev->dev, tmp1, offset1);212 213			return true;214		}215	}216	return false;217}218 219static int m48t86_rtc_probe(struct platform_device *pdev)220{221	struct m48t86_rtc_info *info;222	unsigned char reg;223	int err;224	struct nvmem_config m48t86_nvmem_cfg = {225		.name = "m48t86_nvram",226		.word_size = 1,227		.stride = 1,228		.size = M48T86_NVRAM_LEN,229		.reg_read = m48t86_nvram_read,230		.reg_write = m48t86_nvram_write,231		.priv = &pdev->dev,232	};233 234	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);235	if (!info)236		return -ENOMEM;237 238	info->index_reg = devm_platform_ioremap_resource(pdev, 0);239	if (IS_ERR(info->index_reg))240		return PTR_ERR(info->index_reg);241 242	info->data_reg = devm_platform_ioremap_resource(pdev, 1);243	if (IS_ERR(info->data_reg))244		return PTR_ERR(info->data_reg);245 246	dev_set_drvdata(&pdev->dev, info);247 248	if (!m48t86_verify_chip(pdev)) {249		dev_info(&pdev->dev, "RTC not present\n");250		return -ENODEV;251	}252 253	info->rtc = devm_rtc_allocate_device(&pdev->dev);254	if (IS_ERR(info->rtc))255		return PTR_ERR(info->rtc);256 257	info->rtc->ops = &m48t86_rtc_ops;258 259	err = devm_rtc_register_device(info->rtc);260	if (err)261		return err;262 263	devm_rtc_nvmem_register(info->rtc, &m48t86_nvmem_cfg);264 265	/* read battery status */266	reg = m48t86_readb(&pdev->dev, M48T86_D);267	dev_info(&pdev->dev, "battery %s\n",268		 (reg & M48T86_D_VRT) ? "ok" : "exhausted");269 270	return 0;271}272 273static const struct of_device_id m48t86_rtc_of_ids[] = {274	{ .compatible = "st,m48t86" },275	{ /* sentinel */ }276};277MODULE_DEVICE_TABLE(of, m48t86_rtc_of_ids);278 279static struct platform_driver m48t86_rtc_platform_driver = {280	.driver		= {281		.name	= "rtc-m48t86",282		.of_match_table = m48t86_rtc_of_ids,283	},284	.probe		= m48t86_rtc_probe,285};286 287module_platform_driver(m48t86_rtc_platform_driver);288 289MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");290MODULE_DESCRIPTION("M48T86 RTC driver");291MODULE_LICENSE("GPL");292MODULE_ALIAS("platform:rtc-m48t86");293