brintos

brintos / linux-shallow public Read only

0
0
Text · 2.9 KiB · 90eed39 Raw
145 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 13#include "pmc.h"14 15#define SYSTEM_MAX_ID		3116 17#define SYSTEM_MAX_NAME_SZ	3218 19#define to_clk_system(hw) container_of(hw, struct clk_system, hw)20struct clk_system {21	struct clk_hw hw;22	struct regmap *regmap;23	struct at91_clk_pms pms;24	u8 id;25};26 27static inline int is_pck(int id)28{29	return (id >= 8) && (id <= 15);30}31 32static inline bool clk_system_ready(struct regmap *regmap, int id)33{34	unsigned int status;35 36	regmap_read(regmap, AT91_PMC_SR, &status);37 38	return !!(status & (1 << id));39}40 41static int clk_system_prepare(struct clk_hw *hw)42{43	struct clk_system *sys = to_clk_system(hw);44 45	regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);46 47	if (!is_pck(sys->id))48		return 0;49 50	while (!clk_system_ready(sys->regmap, sys->id))51		cpu_relax();52 53	return 0;54}55 56static void clk_system_unprepare(struct clk_hw *hw)57{58	struct clk_system *sys = to_clk_system(hw);59 60	regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);61}62 63static int clk_system_is_prepared(struct clk_hw *hw)64{65	struct clk_system *sys = to_clk_system(hw);66	unsigned int status;67 68	regmap_read(sys->regmap, AT91_PMC_SCSR, &status);69 70	if (!(status & (1 << sys->id)))71		return 0;72 73	if (!is_pck(sys->id))74		return 1;75 76	regmap_read(sys->regmap, AT91_PMC_SR, &status);77 78	return !!(status & (1 << sys->id));79}80 81static int clk_system_save_context(struct clk_hw *hw)82{83	struct clk_system *sys = to_clk_system(hw);84 85	sys->pms.status = clk_system_is_prepared(hw);86 87	return 0;88}89 90static void clk_system_restore_context(struct clk_hw *hw)91{92	struct clk_system *sys = to_clk_system(hw);93 94	if (sys->pms.status)95		clk_system_prepare(&sys->hw);96}97 98static const struct clk_ops system_ops = {99	.prepare = clk_system_prepare,100	.unprepare = clk_system_unprepare,101	.is_prepared = clk_system_is_prepared,102	.save_context = clk_system_save_context,103	.restore_context = clk_system_restore_context,104};105 106struct clk_hw * __init107at91_clk_register_system(struct regmap *regmap, const char *name,108			 const char *parent_name, struct clk_hw *parent_hw, u8 id,109			 unsigned long flags)110{111	struct clk_system *sys;112	struct clk_hw *hw;113	struct clk_init_data init = {};114	int ret;115 116	if (!(parent_name || parent_hw) || id > SYSTEM_MAX_ID)117		return ERR_PTR(-EINVAL);118 119	sys = kzalloc(sizeof(*sys), GFP_KERNEL);120	if (!sys)121		return ERR_PTR(-ENOMEM);122 123	init.name = name;124	init.ops = &system_ops;125	if (parent_hw)126		init.parent_hws = (const struct clk_hw **)&parent_hw;127	else128		init.parent_names = &parent_name;129	init.num_parents = 1;130	init.flags = CLK_SET_RATE_PARENT | flags;131 132	sys->id = id;133	sys->hw.init = &init;134	sys->regmap = regmap;135 136	hw = &sys->hw;137	ret = clk_hw_register(NULL, &sys->hw);138	if (ret) {139		kfree(sys);140		hw = ERR_PTR(ret);141	}142 143	return hw;144}145