diff options
Diffstat (limited to 'drivers/rtc/rtc-ac100.c')
| -rw-r--r-- | drivers/rtc/rtc-ac100.c | 83 |
1 files changed, 46 insertions, 37 deletions
diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index 9e336184491c..33626311fa78 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * RTC Driver for X-Powers AC100 * * Copyright (c) 2016 Chen-Yu Tsai * * Chen-Yu Tsai <wens@csie.org> - * - * 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include <linux/bcd.h> @@ -107,7 +99,7 @@ struct ac100_rtc_dev { struct clk_hw_onecell_data *clk_data; }; -/** +/* * Clock controls for 3 clock output pins */ @@ -137,13 +129,15 @@ static unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw, div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) & ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1); prate = divider_recalc_rate(hw, prate, div, - ac100_clkout_prediv, 0); + ac100_clkout_prediv, 0, + AC100_CLKOUT_PRE_DIV_WIDTH); } div = (reg >> AC100_CLKOUT_DIV_SHIFT) & (BIT(AC100_CLKOUT_DIV_WIDTH) - 1); return divider_recalc_rate(hw, prate, div, NULL, - CLK_DIVIDER_POWER_OF_TWO); + CLK_DIVIDER_POWER_OF_TWO, + AC100_CLKOUT_DIV_WIDTH); } static long ac100_clkout_round_rate(struct clk_hw *hw, unsigned long rate, @@ -181,7 +175,29 @@ static int ac100_clkout_determine_rate(struct clk_hw *hw, for (i = 0; i < num_parents; i++) { struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); - unsigned long tmp, prate = clk_hw_get_rate(parent); + unsigned long tmp, prate; + + /* + * The clock has two parents, one is a fixed clock which is + * internally registered by the ac100 driver. The other parent + * is a clock from the codec side of the chip, which we + * properly declare and reference in the devicetree and is + * not implemented in any driver right now. + * If the clock core looks for the parent of that second + * missing clock, it can't find one that is registered and + * returns NULL. + * So we end up in a situation where clk_hw_get_num_parents + * returns the amount of clocks we can be parented to, but + * clk_hw_get_parent_by_index will not return the orphan + * clocks. + * Thus we need to check if the parent exists before + * we get the parent rate, so we could use the RTC + * without waiting for the codec to be supported. + */ + if (!parent) + continue; + + prate = clk_hw_get_rate(parent); tmp = ac100_clkout_round_rate(hw, req->rate, prate); @@ -293,10 +309,10 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) const char *parents[2] = {AC100_RTC_32K_NAME}; int i, ret; - chip->clk_data = devm_kzalloc(chip->dev, sizeof(*chip->clk_data) + - sizeof(*chip->clk_data->hws) * - AC100_CLKOUT_NUM, - GFP_KERNEL); + chip->clk_data = devm_kzalloc(chip->dev, + struct_size(chip->clk_data, hws, + AC100_CLKOUT_NUM), + GFP_KERNEL); if (!chip->clk_data) return -ENOMEM; @@ -362,7 +378,7 @@ static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); } -/** +/* * RTC related bits */ static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm) @@ -385,7 +401,7 @@ static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK) + AC100_YEAR_OFF; - return rtc_valid_tm(rtc_tm); + return 0; } static int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) @@ -512,7 +528,7 @@ static irqreturn_t ac100_rtc_irq(int irq, void *data) unsigned int val = 0; int ret; - mutex_lock(&chip->rtc->ops_lock); + rtc_lock(chip->rtc); /* read status */ ret = regmap_read(regmap, AC100_ALM_INT_STA, &val); @@ -535,7 +551,7 @@ static irqreturn_t ac100_rtc_irq(int irq, void *data) } out: - mutex_unlock(&chip->rtc->ops_lock); + rtc_unlock(chip->rtc); return IRQ_HANDLED; } @@ -562,10 +578,14 @@ static int ac100_rtc_probe(struct platform_device *pdev) chip->regmap = ac100->regmap; chip->irq = platform_get_irq(pdev, 0); - if (chip->irq < 0) { - dev_err(&pdev->dev, "No IRQ resource\n"); + if (chip->irq < 0) return chip->irq; - } + + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + chip->rtc->ops = &ac100_rtc_ops; ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL, ac100_rtc_irq, @@ -586,29 +606,18 @@ static int ac100_rtc_probe(struct platform_device *pdev) /* clear counter alarm pending interrupts */ regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE); - chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-ac100", - &ac100_rtc_ops, THIS_MODULE); - if (IS_ERR(chip->rtc)) { - dev_err(&pdev->dev, "unable to register device\n"); - return PTR_ERR(chip->rtc); - } - ret = ac100_rtc_register_clks(chip); if (ret) return ret; - dev_info(&pdev->dev, "RTC enabled\n"); - - return 0; + return devm_rtc_register_device(chip->rtc); } -static int ac100_rtc_remove(struct platform_device *pdev) +static void ac100_rtc_remove(struct platform_device *pdev) { struct ac100_rtc_dev *chip = platform_get_drvdata(pdev); ac100_rtc_unregister_clks(chip); - - return 0; } static const struct of_device_id ac100_rtc_match[] = { |
