18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * RTC Driver for X-Powers AC100
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Chen-Yu Tsai
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Chen-Yu Tsai <wens@csie.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/bcd.h>
118c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/mfd/ac100.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/regmap.h>
218c2ecf20Sopenharmony_ci#include <linux/rtc.h>
228c2ecf20Sopenharmony_ci#include <linux/types.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Control register */
258c2ecf20Sopenharmony_ci#define AC100_RTC_CTRL_24HOUR	BIT(0)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* Clock output register bits */
288c2ecf20Sopenharmony_ci#define AC100_CLKOUT_PRE_DIV_SHIFT	5
298c2ecf20Sopenharmony_ci#define AC100_CLKOUT_PRE_DIV_WIDTH	3
308c2ecf20Sopenharmony_ci#define AC100_CLKOUT_MUX_SHIFT		4
318c2ecf20Sopenharmony_ci#define AC100_CLKOUT_MUX_WIDTH		1
328c2ecf20Sopenharmony_ci#define AC100_CLKOUT_DIV_SHIFT		1
338c2ecf20Sopenharmony_ci#define AC100_CLKOUT_DIV_WIDTH		3
348c2ecf20Sopenharmony_ci#define AC100_CLKOUT_EN			BIT(0)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* RTC */
378c2ecf20Sopenharmony_ci#define AC100_RTC_SEC_MASK	GENMASK(6, 0)
388c2ecf20Sopenharmony_ci#define AC100_RTC_MIN_MASK	GENMASK(6, 0)
398c2ecf20Sopenharmony_ci#define AC100_RTC_HOU_MASK	GENMASK(5, 0)
408c2ecf20Sopenharmony_ci#define AC100_RTC_WEE_MASK	GENMASK(2, 0)
418c2ecf20Sopenharmony_ci#define AC100_RTC_DAY_MASK	GENMASK(5, 0)
428c2ecf20Sopenharmony_ci#define AC100_RTC_MON_MASK	GENMASK(4, 0)
438c2ecf20Sopenharmony_ci#define AC100_RTC_YEA_MASK	GENMASK(7, 0)
448c2ecf20Sopenharmony_ci#define AC100_RTC_YEA_LEAP	BIT(15)
458c2ecf20Sopenharmony_ci#define AC100_RTC_UPD_TRIGGER	BIT(15)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Alarm (wall clock) */
488c2ecf20Sopenharmony_ci#define AC100_ALM_INT_ENABLE	BIT(0)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define AC100_ALM_SEC_MASK	GENMASK(6, 0)
518c2ecf20Sopenharmony_ci#define AC100_ALM_MIN_MASK	GENMASK(6, 0)
528c2ecf20Sopenharmony_ci#define AC100_ALM_HOU_MASK	GENMASK(5, 0)
538c2ecf20Sopenharmony_ci#define AC100_ALM_WEE_MASK	GENMASK(2, 0)
548c2ecf20Sopenharmony_ci#define AC100_ALM_DAY_MASK	GENMASK(5, 0)
558c2ecf20Sopenharmony_ci#define AC100_ALM_MON_MASK	GENMASK(4, 0)
568c2ecf20Sopenharmony_ci#define AC100_ALM_YEA_MASK	GENMASK(7, 0)
578c2ecf20Sopenharmony_ci#define AC100_ALM_ENABLE_FLAG	BIT(15)
588c2ecf20Sopenharmony_ci#define AC100_ALM_UPD_TRIGGER	BIT(15)
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * The year parameter passed to the driver is usually an offset relative to
628c2ecf20Sopenharmony_ci * the year 1900. This macro is used to convert this offset to another one
638c2ecf20Sopenharmony_ci * relative to the minimum year allowed by the hardware.
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci * The year range is 1970 - 2069. This range is selected to match Allwinner's
668c2ecf20Sopenharmony_ci * driver.
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_ci#define AC100_YEAR_MIN				1970
698c2ecf20Sopenharmony_ci#define AC100_YEAR_MAX				2069
708c2ecf20Sopenharmony_ci#define AC100_YEAR_OFF				(AC100_YEAR_MIN - 1900)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct ac100_clkout {
738c2ecf20Sopenharmony_ci	struct clk_hw hw;
748c2ecf20Sopenharmony_ci	struct regmap *regmap;
758c2ecf20Sopenharmony_ci	u8 offset;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define to_ac100_clkout(_hw) container_of(_hw, struct ac100_clkout, hw)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define AC100_RTC_32K_NAME	"ac100-rtc-32k"
818c2ecf20Sopenharmony_ci#define AC100_RTC_32K_RATE	32768
828c2ecf20Sopenharmony_ci#define AC100_CLKOUT_NUM	3
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic const char * const ac100_clkout_names[AC100_CLKOUT_NUM] = {
858c2ecf20Sopenharmony_ci	"ac100-cko1-rtc",
868c2ecf20Sopenharmony_ci	"ac100-cko2-rtc",
878c2ecf20Sopenharmony_ci	"ac100-cko3-rtc",
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct ac100_rtc_dev {
918c2ecf20Sopenharmony_ci	struct rtc_device *rtc;
928c2ecf20Sopenharmony_ci	struct device *dev;
938c2ecf20Sopenharmony_ci	struct regmap *regmap;
948c2ecf20Sopenharmony_ci	int irq;
958c2ecf20Sopenharmony_ci	unsigned long alarm;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	struct clk_hw *rtc_32k_clk;
988c2ecf20Sopenharmony_ci	struct ac100_clkout clks[AC100_CLKOUT_NUM];
998c2ecf20Sopenharmony_ci	struct clk_hw_onecell_data *clk_data;
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/**
1038c2ecf20Sopenharmony_ci * Clock controls for 3 clock output pins
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic const struct clk_div_table ac100_clkout_prediv[] = {
1078c2ecf20Sopenharmony_ci	{ .val = 0, .div = 1 },
1088c2ecf20Sopenharmony_ci	{ .val = 1, .div = 2 },
1098c2ecf20Sopenharmony_ci	{ .val = 2, .div = 4 },
1108c2ecf20Sopenharmony_ci	{ .val = 3, .div = 8 },
1118c2ecf20Sopenharmony_ci	{ .val = 4, .div = 16 },
1128c2ecf20Sopenharmony_ci	{ .val = 5, .div = 32 },
1138c2ecf20Sopenharmony_ci	{ .val = 6, .div = 64 },
1148c2ecf20Sopenharmony_ci	{ .val = 7, .div = 122 },
1158c2ecf20Sopenharmony_ci	{ },
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Abuse the fact that one parent is 32768 Hz, and the other is 4 MHz */
1198c2ecf20Sopenharmony_cistatic unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw,
1208c2ecf20Sopenharmony_ci					      unsigned long prate)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
1238c2ecf20Sopenharmony_ci	unsigned int reg, div;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	regmap_read(clk->regmap, clk->offset, &reg);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Handle pre-divider first */
1288c2ecf20Sopenharmony_ci	if (prate != AC100_RTC_32K_RATE) {
1298c2ecf20Sopenharmony_ci		div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) &
1308c2ecf20Sopenharmony_ci			((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1);
1318c2ecf20Sopenharmony_ci		prate = divider_recalc_rate(hw, prate, div,
1328c2ecf20Sopenharmony_ci					    ac100_clkout_prediv, 0,
1338c2ecf20Sopenharmony_ci					    AC100_CLKOUT_PRE_DIV_WIDTH);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	div = (reg >> AC100_CLKOUT_DIV_SHIFT) &
1378c2ecf20Sopenharmony_ci		(BIT(AC100_CLKOUT_DIV_WIDTH) - 1);
1388c2ecf20Sopenharmony_ci	return divider_recalc_rate(hw, prate, div, NULL,
1398c2ecf20Sopenharmony_ci				   CLK_DIVIDER_POWER_OF_TWO,
1408c2ecf20Sopenharmony_ci				   AC100_CLKOUT_DIV_WIDTH);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic long ac100_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
1448c2ecf20Sopenharmony_ci				    unsigned long prate)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	unsigned long best_rate = 0, tmp_rate, tmp_prate;
1478c2ecf20Sopenharmony_ci	int i;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (prate == AC100_RTC_32K_RATE)
1508c2ecf20Sopenharmony_ci		return divider_round_rate(hw, rate, &prate, NULL,
1518c2ecf20Sopenharmony_ci					  AC100_CLKOUT_DIV_WIDTH,
1528c2ecf20Sopenharmony_ci					  CLK_DIVIDER_POWER_OF_TWO);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	for (i = 0; ac100_clkout_prediv[i].div; i++) {
1558c2ecf20Sopenharmony_ci		tmp_prate = DIV_ROUND_UP(prate, ac100_clkout_prediv[i].val);
1568c2ecf20Sopenharmony_ci		tmp_rate = divider_round_rate(hw, rate, &tmp_prate, NULL,
1578c2ecf20Sopenharmony_ci					      AC100_CLKOUT_DIV_WIDTH,
1588c2ecf20Sopenharmony_ci					      CLK_DIVIDER_POWER_OF_TWO);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		if (tmp_rate > rate)
1618c2ecf20Sopenharmony_ci			continue;
1628c2ecf20Sopenharmony_ci		if (rate - tmp_rate < best_rate - tmp_rate)
1638c2ecf20Sopenharmony_ci			best_rate = tmp_rate;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return best_rate;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int ac100_clkout_determine_rate(struct clk_hw *hw,
1708c2ecf20Sopenharmony_ci				       struct clk_rate_request *req)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct clk_hw *best_parent;
1738c2ecf20Sopenharmony_ci	unsigned long best = 0;
1748c2ecf20Sopenharmony_ci	int i, num_parents = clk_hw_get_num_parents(hw);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	for (i = 0; i < num_parents; i++) {
1778c2ecf20Sopenharmony_ci		struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
1788c2ecf20Sopenharmony_ci		unsigned long tmp, prate;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		/*
1818c2ecf20Sopenharmony_ci		 * The clock has two parents, one is a fixed clock which is
1828c2ecf20Sopenharmony_ci		 * internally registered by the ac100 driver. The other parent
1838c2ecf20Sopenharmony_ci		 * is a clock from the codec side of the chip, which we
1848c2ecf20Sopenharmony_ci		 * properly declare and reference in the devicetree and is
1858c2ecf20Sopenharmony_ci		 * not implemented in any driver right now.
1868c2ecf20Sopenharmony_ci		 * If the clock core looks for the parent of that second
1878c2ecf20Sopenharmony_ci		 * missing clock, it can't find one that is registered and
1888c2ecf20Sopenharmony_ci		 * returns NULL.
1898c2ecf20Sopenharmony_ci		 * So we end up in a situation where clk_hw_get_num_parents
1908c2ecf20Sopenharmony_ci		 * returns the amount of clocks we can be parented to, but
1918c2ecf20Sopenharmony_ci		 * clk_hw_get_parent_by_index will not return the orphan
1928c2ecf20Sopenharmony_ci		 * clocks.
1938c2ecf20Sopenharmony_ci		 * Thus we need to check if the parent exists before
1948c2ecf20Sopenharmony_ci		 * we get the parent rate, so we could use the RTC
1958c2ecf20Sopenharmony_ci		 * without waiting for the codec to be supported.
1968c2ecf20Sopenharmony_ci		 */
1978c2ecf20Sopenharmony_ci		if (!parent)
1988c2ecf20Sopenharmony_ci			continue;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		prate = clk_hw_get_rate(parent);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		tmp = ac100_clkout_round_rate(hw, req->rate, prate);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		if (tmp > req->rate)
2058c2ecf20Sopenharmony_ci			continue;
2068c2ecf20Sopenharmony_ci		if (req->rate - tmp < req->rate - best) {
2078c2ecf20Sopenharmony_ci			best = tmp;
2088c2ecf20Sopenharmony_ci			best_parent = parent;
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (!best)
2138c2ecf20Sopenharmony_ci		return -EINVAL;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	req->best_parent_hw = best_parent;
2168c2ecf20Sopenharmony_ci	req->best_parent_rate = best;
2178c2ecf20Sopenharmony_ci	req->rate = best;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int ac100_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
2238c2ecf20Sopenharmony_ci				 unsigned long prate)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
2268c2ecf20Sopenharmony_ci	int div = 0, pre_div = 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	do {
2298c2ecf20Sopenharmony_ci		div = divider_get_val(rate * ac100_clkout_prediv[pre_div].div,
2308c2ecf20Sopenharmony_ci				      prate, NULL, AC100_CLKOUT_DIV_WIDTH,
2318c2ecf20Sopenharmony_ci				      CLK_DIVIDER_POWER_OF_TWO);
2328c2ecf20Sopenharmony_ci		if (div >= 0)
2338c2ecf20Sopenharmony_ci			break;
2348c2ecf20Sopenharmony_ci	} while (prate != AC100_RTC_32K_RATE &&
2358c2ecf20Sopenharmony_ci		 ac100_clkout_prediv[++pre_div].div);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (div < 0)
2388c2ecf20Sopenharmony_ci		return div;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	pre_div = ac100_clkout_prediv[pre_div].val;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	regmap_update_bits(clk->regmap, clk->offset,
2438c2ecf20Sopenharmony_ci			   ((1 << AC100_CLKOUT_DIV_WIDTH) - 1) << AC100_CLKOUT_DIV_SHIFT |
2448c2ecf20Sopenharmony_ci			   ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1) << AC100_CLKOUT_PRE_DIV_SHIFT,
2458c2ecf20Sopenharmony_ci			   (div - 1) << AC100_CLKOUT_DIV_SHIFT |
2468c2ecf20Sopenharmony_ci			   (pre_div - 1) << AC100_CLKOUT_PRE_DIV_SHIFT);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int ac100_clkout_prepare(struct clk_hw *hw)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN,
2568c2ecf20Sopenharmony_ci				  AC100_CLKOUT_EN);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void ac100_clkout_unprepare(struct clk_hw *hw)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN, 0);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int ac100_clkout_is_prepared(struct clk_hw *hw)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
2698c2ecf20Sopenharmony_ci	unsigned int reg;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	regmap_read(clk->regmap, clk->offset, &reg);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return reg & AC100_CLKOUT_EN;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic u8 ac100_clkout_get_parent(struct clk_hw *hw)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
2798c2ecf20Sopenharmony_ci	unsigned int reg;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	regmap_read(clk->regmap, clk->offset, &reg);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return (reg >> AC100_CLKOUT_MUX_SHIFT) & 0x1;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int ac100_clkout_set_parent(struct clk_hw *hw, u8 index)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct ac100_clkout *clk = to_ac100_clkout(hw);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return regmap_update_bits(clk->regmap, clk->offset,
2918c2ecf20Sopenharmony_ci				  BIT(AC100_CLKOUT_MUX_SHIFT),
2928c2ecf20Sopenharmony_ci				  index ? BIT(AC100_CLKOUT_MUX_SHIFT) : 0);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic const struct clk_ops ac100_clkout_ops = {
2968c2ecf20Sopenharmony_ci	.prepare	= ac100_clkout_prepare,
2978c2ecf20Sopenharmony_ci	.unprepare	= ac100_clkout_unprepare,
2988c2ecf20Sopenharmony_ci	.is_prepared	= ac100_clkout_is_prepared,
2998c2ecf20Sopenharmony_ci	.recalc_rate	= ac100_clkout_recalc_rate,
3008c2ecf20Sopenharmony_ci	.determine_rate	= ac100_clkout_determine_rate,
3018c2ecf20Sopenharmony_ci	.get_parent	= ac100_clkout_get_parent,
3028c2ecf20Sopenharmony_ci	.set_parent	= ac100_clkout_set_parent,
3038c2ecf20Sopenharmony_ci	.set_rate	= ac100_clkout_set_rate,
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct device_node *np = chip->dev->of_node;
3098c2ecf20Sopenharmony_ci	const char *parents[2] = {AC100_RTC_32K_NAME};
3108c2ecf20Sopenharmony_ci	int i, ret;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	chip->clk_data = devm_kzalloc(chip->dev,
3138c2ecf20Sopenharmony_ci				      struct_size(chip->clk_data, hws,
3148c2ecf20Sopenharmony_ci						  AC100_CLKOUT_NUM),
3158c2ecf20Sopenharmony_ci				      GFP_KERNEL);
3168c2ecf20Sopenharmony_ci	if (!chip->clk_data)
3178c2ecf20Sopenharmony_ci		return -ENOMEM;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev,
3208c2ecf20Sopenharmony_ci						       AC100_RTC_32K_NAME,
3218c2ecf20Sopenharmony_ci						       NULL, 0,
3228c2ecf20Sopenharmony_ci						       AC100_RTC_32K_RATE);
3238c2ecf20Sopenharmony_ci	if (IS_ERR(chip->rtc_32k_clk)) {
3248c2ecf20Sopenharmony_ci		ret = PTR_ERR(chip->rtc_32k_clk);
3258c2ecf20Sopenharmony_ci		dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n",
3268c2ecf20Sopenharmony_ci			ret);
3278c2ecf20Sopenharmony_ci		return ret;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	parents[1] = of_clk_get_parent_name(np, 0);
3318c2ecf20Sopenharmony_ci	if (!parents[1]) {
3328c2ecf20Sopenharmony_ci		dev_err(chip->dev, "Failed to get ADDA 4M clock\n");
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	for (i = 0; i < AC100_CLKOUT_NUM; i++) {
3378c2ecf20Sopenharmony_ci		struct ac100_clkout *clk = &chip->clks[i];
3388c2ecf20Sopenharmony_ci		struct clk_init_data init = {
3398c2ecf20Sopenharmony_ci			.name = ac100_clkout_names[i],
3408c2ecf20Sopenharmony_ci			.ops = &ac100_clkout_ops,
3418c2ecf20Sopenharmony_ci			.parent_names = parents,
3428c2ecf20Sopenharmony_ci			.num_parents = ARRAY_SIZE(parents),
3438c2ecf20Sopenharmony_ci			.flags = 0,
3448c2ecf20Sopenharmony_ci		};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		of_property_read_string_index(np, "clock-output-names",
3478c2ecf20Sopenharmony_ci					      i, &init.name);
3488c2ecf20Sopenharmony_ci		clk->regmap = chip->regmap;
3498c2ecf20Sopenharmony_ci		clk->offset = AC100_CLKOUT_CTRL1 + i;
3508c2ecf20Sopenharmony_ci		clk->hw.init = &init;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		ret = devm_clk_hw_register(chip->dev, &clk->hw);
3538c2ecf20Sopenharmony_ci		if (ret) {
3548c2ecf20Sopenharmony_ci			dev_err(chip->dev, "Failed to register clk '%s': %d\n",
3558c2ecf20Sopenharmony_ci				init.name, ret);
3568c2ecf20Sopenharmony_ci			goto err_unregister_rtc_32k;
3578c2ecf20Sopenharmony_ci		}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		chip->clk_data->hws[i] = &clk->hw;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	chip->clk_data->num = i;
3638c2ecf20Sopenharmony_ci	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data);
3648c2ecf20Sopenharmony_ci	if (ret)
3658c2ecf20Sopenharmony_ci		goto err_unregister_rtc_32k;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cierr_unregister_rtc_32k:
3708c2ecf20Sopenharmony_ci	clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return ret;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	of_clk_del_provider(chip->dev->of_node);
3788c2ecf20Sopenharmony_ci	clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/**
3828c2ecf20Sopenharmony_ci * RTC related bits
3838c2ecf20Sopenharmony_ci */
3848c2ecf20Sopenharmony_cistatic int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
3878c2ecf20Sopenharmony_ci	struct regmap *regmap = chip->regmap;
3888c2ecf20Sopenharmony_ci	u16 reg[7];
3898c2ecf20Sopenharmony_ci	int ret;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ret = regmap_bulk_read(regmap, AC100_RTC_SEC, reg, 7);
3928c2ecf20Sopenharmony_ci	if (ret)
3938c2ecf20Sopenharmony_ci		return ret;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	rtc_tm->tm_sec  = bcd2bin(reg[0] & AC100_RTC_SEC_MASK);
3968c2ecf20Sopenharmony_ci	rtc_tm->tm_min  = bcd2bin(reg[1] & AC100_RTC_MIN_MASK);
3978c2ecf20Sopenharmony_ci	rtc_tm->tm_hour = bcd2bin(reg[2] & AC100_RTC_HOU_MASK);
3988c2ecf20Sopenharmony_ci	rtc_tm->tm_wday = bcd2bin(reg[3] & AC100_RTC_WEE_MASK);
3998c2ecf20Sopenharmony_ci	rtc_tm->tm_mday = bcd2bin(reg[4] & AC100_RTC_DAY_MASK);
4008c2ecf20Sopenharmony_ci	rtc_tm->tm_mon  = bcd2bin(reg[5] & AC100_RTC_MON_MASK) - 1;
4018c2ecf20Sopenharmony_ci	rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK) +
4028c2ecf20Sopenharmony_ci			  AC100_YEAR_OFF;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return 0;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
4108c2ecf20Sopenharmony_ci	struct regmap *regmap = chip->regmap;
4118c2ecf20Sopenharmony_ci	int year;
4128c2ecf20Sopenharmony_ci	u16 reg[8];
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* our RTC has a limited year range... */
4158c2ecf20Sopenharmony_ci	year = rtc_tm->tm_year - AC100_YEAR_OFF;
4168c2ecf20Sopenharmony_ci	if (year < 0 || year > (AC100_YEAR_MAX - 1900)) {
4178c2ecf20Sopenharmony_ci		dev_err(dev, "rtc only supports year in range %d - %d\n",
4188c2ecf20Sopenharmony_ci			AC100_YEAR_MIN, AC100_YEAR_MAX);
4198c2ecf20Sopenharmony_ci		return -EINVAL;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* convert to BCD */
4238c2ecf20Sopenharmony_ci	reg[0] = bin2bcd(rtc_tm->tm_sec)     & AC100_RTC_SEC_MASK;
4248c2ecf20Sopenharmony_ci	reg[1] = bin2bcd(rtc_tm->tm_min)     & AC100_RTC_MIN_MASK;
4258c2ecf20Sopenharmony_ci	reg[2] = bin2bcd(rtc_tm->tm_hour)    & AC100_RTC_HOU_MASK;
4268c2ecf20Sopenharmony_ci	reg[3] = bin2bcd(rtc_tm->tm_wday)    & AC100_RTC_WEE_MASK;
4278c2ecf20Sopenharmony_ci	reg[4] = bin2bcd(rtc_tm->tm_mday)    & AC100_RTC_DAY_MASK;
4288c2ecf20Sopenharmony_ci	reg[5] = bin2bcd(rtc_tm->tm_mon + 1) & AC100_RTC_MON_MASK;
4298c2ecf20Sopenharmony_ci	reg[6] = bin2bcd(year)		     & AC100_RTC_YEA_MASK;
4308c2ecf20Sopenharmony_ci	/* trigger write */
4318c2ecf20Sopenharmony_ci	reg[7] = AC100_RTC_UPD_TRIGGER;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* Is it a leap year? */
4348c2ecf20Sopenharmony_ci	if (is_leap_year(year + AC100_YEAR_OFF + 1900))
4358c2ecf20Sopenharmony_ci		reg[6] |= AC100_RTC_YEA_LEAP;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return regmap_bulk_write(regmap, AC100_RTC_SEC, reg, 8);
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic int ac100_rtc_alarm_irq_enable(struct device *dev, unsigned int en)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
4438c2ecf20Sopenharmony_ci	struct regmap *regmap = chip->regmap;
4448c2ecf20Sopenharmony_ci	unsigned int val;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	val = en ? AC100_ALM_INT_ENABLE : 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return regmap_write(regmap, AC100_ALM_INT_ENA, val);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int ac100_rtc_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
4548c2ecf20Sopenharmony_ci	struct regmap *regmap = chip->regmap;
4558c2ecf20Sopenharmony_ci	struct rtc_time *alrm_tm = &alrm->time;
4568c2ecf20Sopenharmony_ci	u16 reg[7];
4578c2ecf20Sopenharmony_ci	unsigned int val;
4588c2ecf20Sopenharmony_ci	int ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, AC100_ALM_INT_ENA, &val);
4618c2ecf20Sopenharmony_ci	if (ret)
4628c2ecf20Sopenharmony_ci		return ret;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	alrm->enabled = !!(val & AC100_ALM_INT_ENABLE);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	ret = regmap_bulk_read(regmap, AC100_ALM_SEC, reg, 7);
4678c2ecf20Sopenharmony_ci	if (ret)
4688c2ecf20Sopenharmony_ci		return ret;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	alrm_tm->tm_sec  = bcd2bin(reg[0] & AC100_ALM_SEC_MASK);
4718c2ecf20Sopenharmony_ci	alrm_tm->tm_min  = bcd2bin(reg[1] & AC100_ALM_MIN_MASK);
4728c2ecf20Sopenharmony_ci	alrm_tm->tm_hour = bcd2bin(reg[2] & AC100_ALM_HOU_MASK);
4738c2ecf20Sopenharmony_ci	alrm_tm->tm_wday = bcd2bin(reg[3] & AC100_ALM_WEE_MASK);
4748c2ecf20Sopenharmony_ci	alrm_tm->tm_mday = bcd2bin(reg[4] & AC100_ALM_DAY_MASK);
4758c2ecf20Sopenharmony_ci	alrm_tm->tm_mon  = bcd2bin(reg[5] & AC100_ALM_MON_MASK) - 1;
4768c2ecf20Sopenharmony_ci	alrm_tm->tm_year = bcd2bin(reg[6] & AC100_ALM_YEA_MASK) +
4778c2ecf20Sopenharmony_ci			   AC100_YEAR_OFF;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	return 0;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic int ac100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = dev_get_drvdata(dev);
4858c2ecf20Sopenharmony_ci	struct regmap *regmap = chip->regmap;
4868c2ecf20Sopenharmony_ci	struct rtc_time *alrm_tm = &alrm->time;
4878c2ecf20Sopenharmony_ci	u16 reg[8];
4888c2ecf20Sopenharmony_ci	int year;
4898c2ecf20Sopenharmony_ci	int ret;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* our alarm has a limited year range... */
4928c2ecf20Sopenharmony_ci	year = alrm_tm->tm_year - AC100_YEAR_OFF;
4938c2ecf20Sopenharmony_ci	if (year < 0 || year > (AC100_YEAR_MAX - 1900)) {
4948c2ecf20Sopenharmony_ci		dev_err(dev, "alarm only supports year in range %d - %d\n",
4958c2ecf20Sopenharmony_ci			AC100_YEAR_MIN, AC100_YEAR_MAX);
4968c2ecf20Sopenharmony_ci		return -EINVAL;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* convert to BCD */
5008c2ecf20Sopenharmony_ci	reg[0] = (bin2bcd(alrm_tm->tm_sec)  & AC100_ALM_SEC_MASK) |
5018c2ecf20Sopenharmony_ci			AC100_ALM_ENABLE_FLAG;
5028c2ecf20Sopenharmony_ci	reg[1] = (bin2bcd(alrm_tm->tm_min)  & AC100_ALM_MIN_MASK) |
5038c2ecf20Sopenharmony_ci			AC100_ALM_ENABLE_FLAG;
5048c2ecf20Sopenharmony_ci	reg[2] = (bin2bcd(alrm_tm->tm_hour) & AC100_ALM_HOU_MASK) |
5058c2ecf20Sopenharmony_ci			AC100_ALM_ENABLE_FLAG;
5068c2ecf20Sopenharmony_ci	/* Do not enable weekday alarm */
5078c2ecf20Sopenharmony_ci	reg[3] = bin2bcd(alrm_tm->tm_wday) & AC100_ALM_WEE_MASK;
5088c2ecf20Sopenharmony_ci	reg[4] = (bin2bcd(alrm_tm->tm_mday) & AC100_ALM_DAY_MASK) |
5098c2ecf20Sopenharmony_ci			AC100_ALM_ENABLE_FLAG;
5108c2ecf20Sopenharmony_ci	reg[5] = (bin2bcd(alrm_tm->tm_mon + 1)  & AC100_ALM_MON_MASK) |
5118c2ecf20Sopenharmony_ci			AC100_ALM_ENABLE_FLAG;
5128c2ecf20Sopenharmony_ci	reg[6] = (bin2bcd(year) & AC100_ALM_YEA_MASK) |
5138c2ecf20Sopenharmony_ci			AC100_ALM_ENABLE_FLAG;
5148c2ecf20Sopenharmony_ci	/* trigger write */
5158c2ecf20Sopenharmony_ci	reg[7] = AC100_ALM_UPD_TRIGGER;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	ret = regmap_bulk_write(regmap, AC100_ALM_SEC, reg, 8);
5188c2ecf20Sopenharmony_ci	if (ret)
5198c2ecf20Sopenharmony_ci		return ret;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return ac100_rtc_alarm_irq_enable(dev, alrm->enabled);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic irqreturn_t ac100_rtc_irq(int irq, void *data)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = data;
5278c2ecf20Sopenharmony_ci	struct regmap *regmap = chip->regmap;
5288c2ecf20Sopenharmony_ci	unsigned int val = 0;
5298c2ecf20Sopenharmony_ci	int ret;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	mutex_lock(&chip->rtc->ops_lock);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* read status */
5348c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, AC100_ALM_INT_STA, &val);
5358c2ecf20Sopenharmony_ci	if (ret)
5368c2ecf20Sopenharmony_ci		goto out;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (val & AC100_ALM_INT_ENABLE) {
5398c2ecf20Sopenharmony_ci		/* signal rtc framework */
5408c2ecf20Sopenharmony_ci		rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		/* clear status */
5438c2ecf20Sopenharmony_ci		ret = regmap_write(regmap, AC100_ALM_INT_STA, val);
5448c2ecf20Sopenharmony_ci		if (ret)
5458c2ecf20Sopenharmony_ci			goto out;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		/* disable interrupt */
5488c2ecf20Sopenharmony_ci		ret = ac100_rtc_alarm_irq_enable(chip->dev, 0);
5498c2ecf20Sopenharmony_ci		if (ret)
5508c2ecf20Sopenharmony_ci			goto out;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ciout:
5548c2ecf20Sopenharmony_ci	mutex_unlock(&chip->rtc->ops_lock);
5558c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic const struct rtc_class_ops ac100_rtc_ops = {
5598c2ecf20Sopenharmony_ci	.read_time	  = ac100_rtc_get_time,
5608c2ecf20Sopenharmony_ci	.set_time	  = ac100_rtc_set_time,
5618c2ecf20Sopenharmony_ci	.read_alarm	  = ac100_rtc_get_alarm,
5628c2ecf20Sopenharmony_ci	.set_alarm	  = ac100_rtc_set_alarm,
5638c2ecf20Sopenharmony_ci	.alarm_irq_enable = ac100_rtc_alarm_irq_enable,
5648c2ecf20Sopenharmony_ci};
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int ac100_rtc_probe(struct platform_device *pdev)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent);
5698c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip;
5708c2ecf20Sopenharmony_ci	int ret;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
5738c2ecf20Sopenharmony_ci	if (!chip)
5748c2ecf20Sopenharmony_ci		return -ENOMEM;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, chip);
5778c2ecf20Sopenharmony_ci	chip->dev = &pdev->dev;
5788c2ecf20Sopenharmony_ci	chip->regmap = ac100->regmap;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	chip->irq = platform_get_irq(pdev, 0);
5818c2ecf20Sopenharmony_ci	if (chip->irq < 0)
5828c2ecf20Sopenharmony_ci		return chip->irq;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	chip->rtc = devm_rtc_allocate_device(&pdev->dev);
5858c2ecf20Sopenharmony_ci	if (IS_ERR(chip->rtc))
5868c2ecf20Sopenharmony_ci		return PTR_ERR(chip->rtc);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	chip->rtc->ops = &ac100_rtc_ops;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL,
5918c2ecf20Sopenharmony_ci					ac100_rtc_irq,
5928c2ecf20Sopenharmony_ci					IRQF_SHARED | IRQF_ONESHOT,
5938c2ecf20Sopenharmony_ci					dev_name(&pdev->dev), chip);
5948c2ecf20Sopenharmony_ci	if (ret) {
5958c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not request IRQ\n");
5968c2ecf20Sopenharmony_ci		return ret;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	/* always use 24 hour mode */
6008c2ecf20Sopenharmony_ci	regmap_write_bits(chip->regmap, AC100_RTC_CTRL, AC100_RTC_CTRL_24HOUR,
6018c2ecf20Sopenharmony_ci			  AC100_RTC_CTRL_24HOUR);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	/* disable counter alarm interrupt */
6048c2ecf20Sopenharmony_ci	regmap_write(chip->regmap, AC100_ALM_INT_ENA, 0);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	/* clear counter alarm pending interrupts */
6078c2ecf20Sopenharmony_ci	regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	ret = ac100_rtc_register_clks(chip);
6108c2ecf20Sopenharmony_ci	if (ret)
6118c2ecf20Sopenharmony_ci		return ret;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return rtc_register_device(chip->rtc);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int ac100_rtc_remove(struct platform_device *pdev)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct ac100_rtc_dev *chip = platform_get_drvdata(pdev);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	ac100_rtc_unregister_clks(chip);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return 0;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic const struct of_device_id ac100_rtc_match[] = {
6268c2ecf20Sopenharmony_ci	{ .compatible = "x-powers,ac100-rtc" },
6278c2ecf20Sopenharmony_ci	{ },
6288c2ecf20Sopenharmony_ci};
6298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ac100_rtc_match);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic struct platform_driver ac100_rtc_driver = {
6328c2ecf20Sopenharmony_ci	.probe		= ac100_rtc_probe,
6338c2ecf20Sopenharmony_ci	.remove		= ac100_rtc_remove,
6348c2ecf20Sopenharmony_ci	.driver		= {
6358c2ecf20Sopenharmony_ci		.name		= "ac100-rtc",
6368c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(ac100_rtc_match),
6378c2ecf20Sopenharmony_ci	},
6388c2ecf20Sopenharmony_ci};
6398c2ecf20Sopenharmony_cimodule_platform_driver(ac100_rtc_driver);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("X-Powers AC100 RTC driver");
6428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
6438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
644