brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · b991180 Raw
295 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>4 */5 6#include <linux/clk-provider.h>7#include <linux/clkdev.h>8#include <linux/clk/at91_pmc.h>9#include <linux/of.h>10#include <linux/mfd/syscon.h>11#include <linux/regmap.h>12#include <soc/at91/atmel-sfr.h>13 14#include "pmc.h"15 16/*17 * The purpose of this clock is to generate a 480 MHz signal. A different18 * rate can't be configured.19 */20#define UTMI_RATE	48000000021 22struct clk_utmi {23	struct clk_hw hw;24	struct regmap *regmap_pmc;25	struct regmap *regmap_sfr;26	struct at91_clk_pms pms;27};28 29#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)30 31static inline bool clk_utmi_ready(struct regmap *regmap)32{33	unsigned int status;34 35	regmap_read(regmap, AT91_PMC_SR, &status);36 37	return status & AT91_PMC_LOCKU;38}39 40static int clk_utmi_prepare(struct clk_hw *hw)41{42	struct clk_hw *hw_parent;43	struct clk_utmi *utmi = to_clk_utmi(hw);44	unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |45			    AT91_PMC_BIASEN;46	unsigned int utmi_ref_clk_freq;47	unsigned long parent_rate;48 49	/*50	 * If mainck rate is different from 12 MHz, we have to configure the51	 * FREQ field of the SFR_UTMICKTRIM register to generate properly52	 * the utmi clock.53	 */54	hw_parent = clk_hw_get_parent(hw);55	parent_rate = clk_hw_get_rate(hw_parent);56 57	switch (parent_rate) {58	case 12000000:59		utmi_ref_clk_freq = 0;60		break;61	case 16000000:62		utmi_ref_clk_freq = 1;63		break;64	case 24000000:65		utmi_ref_clk_freq = 2;66		break;67	/*68	 * Not supported on SAMA5D2 but it's not an issue since MAINCK69	 * maximum value is 24 MHz.70	 */71	case 48000000:72		utmi_ref_clk_freq = 3;73		break;74	default:75		pr_err("UTMICK: unsupported mainck rate\n");76		return -EINVAL;77	}78 79	if (utmi->regmap_sfr) {80		regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,81				   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);82	} else if (utmi_ref_clk_freq) {83		pr_err("UTMICK: sfr node required\n");84		return -EINVAL;85	}86 87	regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);88 89	while (!clk_utmi_ready(utmi->regmap_pmc))90		cpu_relax();91 92	return 0;93}94 95static int clk_utmi_is_prepared(struct clk_hw *hw)96{97	struct clk_utmi *utmi = to_clk_utmi(hw);98 99	return clk_utmi_ready(utmi->regmap_pmc);100}101 102static void clk_utmi_unprepare(struct clk_hw *hw)103{104	struct clk_utmi *utmi = to_clk_utmi(hw);105 106	regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,107			   AT91_PMC_UPLLEN, 0);108}109 110static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,111					  unsigned long parent_rate)112{113	/* UTMI clk rate is fixed. */114	return UTMI_RATE;115}116 117static int clk_utmi_save_context(struct clk_hw *hw)118{119	struct clk_utmi *utmi = to_clk_utmi(hw);120 121	utmi->pms.status = clk_utmi_is_prepared(hw);122 123	return 0;124}125 126static void clk_utmi_restore_context(struct clk_hw *hw)127{128	struct clk_utmi *utmi = to_clk_utmi(hw);129 130	if (utmi->pms.status)131		clk_utmi_prepare(hw);132}133 134static const struct clk_ops utmi_ops = {135	.prepare = clk_utmi_prepare,136	.unprepare = clk_utmi_unprepare,137	.is_prepared = clk_utmi_is_prepared,138	.recalc_rate = clk_utmi_recalc_rate,139	.save_context = clk_utmi_save_context,140	.restore_context = clk_utmi_restore_context,141};142 143static struct clk_hw * __init144at91_clk_register_utmi_internal(struct regmap *regmap_pmc,145				struct regmap *regmap_sfr,146				const char *name, const char *parent_name,147				struct clk_hw *parent_hw,148				const struct clk_ops *ops, unsigned long flags)149{150	struct clk_utmi *utmi;151	struct clk_hw *hw;152	struct clk_init_data init = {};153	int ret;154 155	if (!(parent_name || parent_hw))156		return ERR_PTR(-EINVAL);157 158	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);159	if (!utmi)160		return ERR_PTR(-ENOMEM);161 162	init.name = name;163	init.ops = ops;164	if (parent_hw)165		init.parent_hws = (const struct clk_hw **)&parent_hw;166	else167		init.parent_names = &parent_name;168	init.num_parents = 1;169	init.flags = flags;170 171	utmi->hw.init = &init;172	utmi->regmap_pmc = regmap_pmc;173	utmi->regmap_sfr = regmap_sfr;174 175	hw = &utmi->hw;176	ret = clk_hw_register(NULL, &utmi->hw);177	if (ret) {178		kfree(utmi);179		hw = ERR_PTR(ret);180	}181 182	return hw;183}184 185struct clk_hw * __init186at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,187		       const char *name, const char *parent_name,188		       struct clk_hw *parent_hw)189{190	return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,191			parent_name, parent_hw, &utmi_ops, CLK_SET_RATE_GATE);192}193 194static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)195{196	struct clk_utmi *utmi = to_clk_utmi(hw);197	struct clk_hw *hw_parent;198	unsigned long parent_rate;199	unsigned int val;200 201	hw_parent = clk_hw_get_parent(hw);202	parent_rate = clk_hw_get_rate(hw_parent);203 204	switch (parent_rate) {205	case 16000000:206		val = 0;207		break;208	case 20000000:209		val = 2;210		break;211	case 24000000:212		val = 3;213		break;214	case 32000000:215		val = 5;216		break;217	default:218		pr_err("UTMICK: unsupported main_xtal rate\n");219		return -EINVAL;220	}221 222	regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);223 224	return 0;225 226}227 228static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)229{230	struct clk_utmi *utmi = to_clk_utmi(hw);231	struct clk_hw *hw_parent;232	unsigned long parent_rate;233	unsigned int val;234 235	hw_parent = clk_hw_get_parent(hw);236	parent_rate = clk_hw_get_rate(hw_parent);237 238	regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);239	switch (val & 0x7) {240	case 0:241		if (parent_rate == 16000000)242			return 1;243		break;244	case 2:245		if (parent_rate == 20000000)246			return 1;247		break;248	case 3:249		if (parent_rate == 24000000)250			return 1;251		break;252	case 5:253		if (parent_rate == 32000000)254			return 1;255		break;256	default:257		break;258	}259 260	return 0;261}262 263static int clk_utmi_sama7g5_save_context(struct clk_hw *hw)264{265	struct clk_utmi *utmi = to_clk_utmi(hw);266 267	utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw);268 269	return 0;270}271 272static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw)273{274	struct clk_utmi *utmi = to_clk_utmi(hw);275 276	if (utmi->pms.status)277		clk_utmi_sama7g5_prepare(hw);278}279 280static const struct clk_ops sama7g5_utmi_ops = {281	.prepare = clk_utmi_sama7g5_prepare,282	.is_prepared = clk_utmi_sama7g5_is_prepared,283	.recalc_rate = clk_utmi_recalc_rate,284	.save_context = clk_utmi_sama7g5_save_context,285	.restore_context = clk_utmi_sama7g5_restore_context,286};287 288struct clk_hw * __init289at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,290			       const char *parent_name, struct clk_hw *parent_hw)291{292	return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,293			parent_name, parent_hw, &sama7g5_utmi_ops, 0);294}295