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, ®); 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, ®); 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, ®); 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