brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · 29662df Raw
238 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Realtek RTD129x RTC4 *5 * Copyright (c) 2017 Andreas Färber6 */7 8#include <linux/clk.h>9#include <linux/io.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_address.h>13#include <linux/platform_device.h>14#include <linux/rtc.h>15#include <linux/spinlock.h>16 17#define RTD_RTCSEC		0x0018#define RTD_RTCMIN		0x0419#define RTD_RTCHR		0x0820#define RTD_RTCDATE1		0x0c21#define RTD_RTCDATE2		0x1022#define RTD_RTCACR		0x2823#define RTD_RTCEN		0x2c24#define RTD_RTCCR		0x3025 26#define RTD_RTCSEC_RTCSEC_MASK		0x7f27 28#define RTD_RTCMIN_RTCMIN_MASK		0x3f29 30#define RTD_RTCHR_RTCHR_MASK		0x1f31 32#define RTD_RTCDATE1_RTCDATE1_MASK	0xff33 34#define RTD_RTCDATE2_RTCDATE2_MASK	0x7f35 36#define RTD_RTCACR_RTCPWR		BIT(7)37 38#define RTD_RTCEN_RTCEN_MASK		0xff39 40#define RTD_RTCCR_RTCRST		BIT(6)41 42struct rtd119x_rtc {43	void __iomem *base;44	struct clk *clk;45	struct rtc_device *rtcdev;46	unsigned int base_year;47};48 49static inline int rtd119x_rtc_days_in_year(int year)50{51	return 365 + (is_leap_year(year) ? 1 : 0);52}53 54static void rtd119x_rtc_reset(struct device *dev)55{56	struct rtd119x_rtc *data = dev_get_drvdata(dev);57	u32 val;58 59	val = readl_relaxed(data->base + RTD_RTCCR);60	val |= RTD_RTCCR_RTCRST;61	writel_relaxed(val, data->base + RTD_RTCCR);62 63	val &= ~RTD_RTCCR_RTCRST;64	writel(val, data->base + RTD_RTCCR);65}66 67static void rtd119x_rtc_set_enabled(struct device *dev, bool enable)68{69	struct rtd119x_rtc *data = dev_get_drvdata(dev);70	u32 val;71 72	val = readl_relaxed(data->base + RTD_RTCEN);73	if (enable) {74		if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a)75			return;76		writel_relaxed(0x5a, data->base + RTD_RTCEN);77	} else {78		writel_relaxed(0, data->base + RTD_RTCEN);79	}80}81 82static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm)83{84	struct rtd119x_rtc *data = dev_get_drvdata(dev);85	s32 day;86	u32 sec;87	unsigned int year;88	int tries = 0;89 90	while (true) {91		tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1;92		tm->tm_min  = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK;93		tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK;94		day  =  readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK;95		day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8;96		sec  = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1;97		tries++;98 99		if (sec == tm->tm_sec)100			break;101 102		if (tries >= 3)103			return -EINVAL;104	}105	if (tries > 1)106		dev_dbg(dev, "%s: needed %i tries\n", __func__, tries);107 108	year = data->base_year;109	while (day >= rtd119x_rtc_days_in_year(year)) {110		day -= rtd119x_rtc_days_in_year(year);111		year++;112	}113	tm->tm_year = year - 1900;114	tm->tm_yday = day;115 116	tm->tm_mon = 0;117	while (day >= rtc_month_days(tm->tm_mon, year)) {118		day -= rtc_month_days(tm->tm_mon, year);119		tm->tm_mon++;120	}121	tm->tm_mday = day + 1;122 123	return 0;124}125 126static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm)127{128	struct rtd119x_rtc *data = dev_get_drvdata(dev);129	unsigned int day;130	int i;131 132	if (1900 + tm->tm_year < data->base_year)133		return -EINVAL;134 135	day = 0;136	for (i = data->base_year; i < 1900 + tm->tm_year; i++)137		day += rtd119x_rtc_days_in_year(i);138 139	day += tm->tm_yday;140	if (day > 0x7fff)141		return -EINVAL;142 143	rtd119x_rtc_set_enabled(dev, false);144 145	writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC);146	writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN);147	writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR);148	writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1);149	writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2);150 151	rtd119x_rtc_set_enabled(dev, true);152 153	return 0;154}155 156static const struct rtc_class_ops rtd119x_rtc_ops = {157	.read_time	= rtd119x_rtc_read_time,158	.set_time	= rtd119x_rtc_set_time,159};160 161static const struct of_device_id rtd119x_rtc_dt_ids[] = {162	 { .compatible = "realtek,rtd1295-rtc" },163	 { }164};165 166static int rtd119x_rtc_probe(struct platform_device *pdev)167{168	struct rtd119x_rtc *data;169	u32 val;170	int ret;171 172	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);173	if (!data)174		return -ENOMEM;175 176	platform_set_drvdata(pdev, data);177	data->base_year = 2014;178 179	data->base = devm_platform_ioremap_resource(pdev, 0);180	if (IS_ERR(data->base))181		return PTR_ERR(data->base);182 183	data->clk = of_clk_get(pdev->dev.of_node, 0);184	if (IS_ERR(data->clk))185		return PTR_ERR(data->clk);186 187	ret = clk_prepare_enable(data->clk);188	if (ret) {189		clk_put(data->clk);190		return ret;191	}192 193	val = readl_relaxed(data->base + RTD_RTCACR);194	if (!(val & RTD_RTCACR_RTCPWR)) {195		writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR);196 197		rtd119x_rtc_reset(&pdev->dev);198 199		writel_relaxed(0, data->base + RTD_RTCMIN);200		writel_relaxed(0, data->base + RTD_RTCHR);201		writel_relaxed(0, data->base + RTD_RTCDATE1);202		writel_relaxed(0, data->base + RTD_RTCDATE2);203	}204 205	rtd119x_rtc_set_enabled(&pdev->dev, true);206 207	data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc",208						&rtd119x_rtc_ops, THIS_MODULE);209	if (IS_ERR(data->rtcdev)) {210		dev_err(&pdev->dev, "failed to register rtc device");211		clk_disable_unprepare(data->clk);212		clk_put(data->clk);213		return PTR_ERR(data->rtcdev);214	}215 216	return 0;217}218 219static void rtd119x_rtc_remove(struct platform_device *pdev)220{221	struct rtd119x_rtc *data = platform_get_drvdata(pdev);222 223	rtd119x_rtc_set_enabled(&pdev->dev, false);224 225	clk_disable_unprepare(data->clk);226	clk_put(data->clk);227}228 229static struct platform_driver rtd119x_rtc_driver = {230	.probe = rtd119x_rtc_probe,231	.remove_new = rtd119x_rtc_remove,232	.driver = {233		.name = "rtd1295-rtc",234		.of_match_table	= rtd119x_rtc_dt_ids,235	},236};237builtin_platform_driver(rtd119x_rtc_driver);238