diff options
Diffstat (limited to 'drivers/clk/at91/clk-generated.c')
-rw-r--r-- | drivers/clk/at91/clk-generated.c | 162 |
1 files changed, 96 insertions, 66 deletions
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index 396c35f3a7..e59cff2bdf 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -1,85 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2015 Atmel Corporation, * Nicolas Ferre <nicolas.ferre@atmel.com> * * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/bitfield.h> +#include <linux/printk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" #define GENERATED_MAX_DIV 255 struct clk_generated { - struct clk hw; + struct clk_hw hw; struct regmap *regmap; struct clk_range range; + spinlock_t *lock; + u32 *mux_table; u32 id; u32 gckdiv; + const struct clk_pcr_layout *layout; + struct at91_clk_pms pms; u8 parent_id; + int chg_pid; }; #define to_clk_generated(hw) \ container_of(hw, struct clk_generated, hw) -static int clk_generated_enable(struct clk *hw) +static int clk_generated_set(struct clk_generated *gck, int status) +{ + unsigned long flags; + unsigned int enable = status ? AT91_PMC_PCR_GCKEN : 0; + + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + regmap_update_bits(gck->regmap, gck->layout->offset, + AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask | + gck->layout->cmd | enable, + field_prep(gck->layout->gckcss_mask, gck->parent_id) | + gck->layout->cmd | + FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) | + enable); + spin_unlock_irqrestore(gck->lock, flags); + + return 0; +} + +static int clk_generated_enable(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n", __func__, gck->gckdiv, gck->parent_id); - regmap_write(gck->regmap, AT91_PMC_PCR, - (gck->id & AT91_PMC_PCR_PID_MASK)); - regmap_update_bits(gck->regmap, AT91_PMC_PCR, - AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK | - AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, - AT91_PMC_PCR_GCKCSS(gck->parent_id) | - AT91_PMC_PCR_CMD | - AT91_PMC_PCR_GCKDIV(gck->gckdiv) | - AT91_PMC_PCR_GCKEN); + clk_generated_set(gck, 1); + return 0; } -static void clk_generated_disable(struct clk *hw) +static void clk_generated_disable(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); - - regmap_write(gck->regmap, AT91_PMC_PCR, - (gck->id & AT91_PMC_PCR_PID_MASK)); - regmap_update_bits(gck->regmap, AT91_PMC_PCR, - AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, - AT91_PMC_PCR_CMD); + unsigned long flags; + + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + regmap_update_bits(gck->regmap, gck->layout->offset, + gck->layout->cmd | AT91_PMC_PCR_GCKEN, + gck->layout->cmd); + spin_unlock_irqrestore(gck->lock, flags); } -static int clk_generated_is_enabled(struct clk *hw) +static int clk_generated_is_enabled(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); + unsigned long flags; unsigned int status; - regmap_write(gck->regmap, AT91_PMC_PCR, - (gck->id & AT91_PMC_PCR_PID_MASK)); - regmap_read(gck->regmap, AT91_PMC_PCR, &status); + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + regmap_read(gck->regmap, gck->layout->offset, &status); + spin_unlock_irqrestore(gck->lock, flags); - return status & AT91_PMC_PCR_GCKEN ? 1 : 0; + return !!(status & AT91_PMC_PCR_GCKEN); } static unsigned long -clk_generated_recalc_rate(struct clk *hw, +clk_generated_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_generated *gck = to_clk_generated(hw); @@ -88,18 +107,22 @@ clk_generated_recalc_rate(struct clk *hw, } /* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */ -static int clk_generated_set_parent(struct clk *hw, u8 index) +static int clk_generated_set_parent(struct clk_hw *hw, u8 index) { struct clk_generated *gck = to_clk_generated(hw); - if (index >= clk_get_num_parents(hw)) + if (index >= clk_hw_get_num_parents(hw)) return -EINVAL; - gck->parent_id = index; + if (gck->mux_table) + gck->parent_id = clk_mux_index_to_val(gck->mux_table, 0, index); + else + gck->parent_id = index; + return 0; } -static int clk_generated_get_parent(struct clk *hw) +static int clk_generated_get_parent(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); @@ -107,7 +130,7 @@ static int clk_generated_get_parent(struct clk *hw) } /* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */ -static int clk_generated_set_rate(struct clk *hw, +static int clk_generated_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -150,52 +173,59 @@ static const struct clk_ops generated_ops = { static void clk_generated_startup(struct clk_generated *gck) { u32 tmp; + unsigned long flags; - regmap_write(gck->regmap, AT91_PMC_PCR, - (gck->id & AT91_PMC_PCR_PID_MASK)); - regmap_read(gck->regmap, AT91_PMC_PCR, &tmp); + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, gck->layout->offset, + (gck->id & gck->layout->pid_mask)); + regmap_read(gck->regmap, gck->layout->offset, &tmp); + spin_unlock_irqrestore(gck->lock, flags); - gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK) - >> AT91_PMC_PCR_GCKCSS_OFFSET; - gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) - >> AT91_PMC_PCR_GCKDIV_OFFSET; + gck->parent_id = field_get(gck->layout->gckcss_mask, tmp); + gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp); } -struct clk * __init -at91_clk_register_generated(struct regmap *regmap, +struct clk_hw * __init +at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, + const struct clk_pcr_layout *layout, const char *name, const char **parent_names, - u8 num_parents, u8 id, bool pll_audio, - const struct clk_range *range) + u32 *mux_table, u8 num_parents, u8 id, + const struct clk_range *range, + int chg_pid) { - size_t parents_array_size; struct clk_generated *gck; - struct clk *hw; + struct clk_init_data init; + struct clk_hw *hw; int ret; gck = kzalloc(sizeof(*gck), GFP_KERNEL); if (!gck) return ERR_PTR(-ENOMEM); - gck->id = id; - gck->hw.name = name; - gck->hw.ops = &generated_ops; - - parents_array_size = num_parents * sizeof(gck->hw.parent_names[0]); - gck->hw.parent_names = xmemdup(parent_names, parents_array_size); - gck->hw.num_parents = num_parents; + init.name = name; + init.ops = &generated_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + if (chg_pid >= 0) + init.flags |= CLK_SET_RATE_PARENT; - /* gck->hw.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */ + gck->id = id; + gck->hw.init = &init; gck->regmap = regmap; + gck->lock = lock; gck->range = *range; - /* gck->audio_pll_allowed = pll_audio; */ + gck->chg_pid = chg_pid; + gck->layout = layout; + gck->mux_table = mux_table; + clk_generated_startup(gck); hw = &gck->hw; - ret = clk_register(&gck->hw); + ret = clk_hw_register(NULL, &gck->hw); if (ret) { kfree(gck); hw = ERR_PTR(ret); - } else - clk_generated_startup(gck); + } return hw; } |