brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · 2091fc9 Raw
177 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.4 */5 6#include <linux/clk-provider.h>7#include <linux/slab.h>8#include <linux/io.h>9#include <linux/delay.h>10#include <linux/err.h>11 12#include <soc/tegra/fuse.h>13 14#include "clk.h"15 16static DEFINE_SPINLOCK(periph_ref_lock);17 18/* Macros to assist peripheral gate clock */19#define read_enb(gate) \20	readl_relaxed(gate->clk_base + (gate->regs->enb_reg))21#define write_enb_set(val, gate) \22	writel_relaxed(val, gate->clk_base + (gate->regs->enb_set_reg))23#define write_enb_clr(val, gate) \24	writel_relaxed(val, gate->clk_base + (gate->regs->enb_clr_reg))25 26#define read_rst(gate) \27	readl_relaxed(gate->clk_base + (gate->regs->rst_reg))28#define write_rst_clr(val, gate) \29	writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))30 31#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32))32 33#define LVL2_CLK_GATE_OVRE 0x55434 35/* Peripheral gate clock ops */36static int clk_periph_is_enabled(struct clk_hw *hw)37{38	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);39	int state = 1;40 41	if (!(read_enb(gate) & periph_clk_to_bit(gate)))42		state = 0;43 44	if (!(gate->flags & TEGRA_PERIPH_NO_RESET))45		if (read_rst(gate) & periph_clk_to_bit(gate))46			state = 0;47 48	return state;49}50 51static void clk_periph_enable_locked(struct clk_hw *hw)52{53	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);54 55	write_enb_set(periph_clk_to_bit(gate), gate);56	udelay(2);57 58	if (gate->flags & TEGRA_PERIPH_WAR_1005168) {59		writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);60		writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);61		udelay(1);62		writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);63	}64}65 66static void clk_periph_disable_locked(struct clk_hw *hw)67{68	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);69 70	/*71	 * If peripheral is in the APB bus then read the APB bus to72	 * flush the write operation in apb bus. This will avoid the73	 * peripheral access after disabling clock74	 */75	if (gate->flags & TEGRA_PERIPH_ON_APB)76		tegra_read_chipid();77 78	write_enb_clr(periph_clk_to_bit(gate), gate);79}80 81static int clk_periph_enable(struct clk_hw *hw)82{83	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);84	unsigned long flags = 0;85 86	spin_lock_irqsave(&periph_ref_lock, flags);87 88	if (!gate->enable_refcnt[gate->clk_num]++)89		clk_periph_enable_locked(hw);90 91	spin_unlock_irqrestore(&periph_ref_lock, flags);92 93	return 0;94}95 96static void clk_periph_disable(struct clk_hw *hw)97{98	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);99	unsigned long flags = 0;100 101	spin_lock_irqsave(&periph_ref_lock, flags);102 103	WARN_ON(!gate->enable_refcnt[gate->clk_num]);104 105	if (--gate->enable_refcnt[gate->clk_num] == 0)106		clk_periph_disable_locked(hw);107 108	spin_unlock_irqrestore(&periph_ref_lock, flags);109}110 111static void clk_periph_disable_unused(struct clk_hw *hw)112{113	struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);114	unsigned long flags = 0;115 116	spin_lock_irqsave(&periph_ref_lock, flags);117 118	/*119	 * Some clocks are duplicated and some of them are marked as critical,120	 * like fuse and fuse_burn for example, thus the enable_refcnt will121	 * be non-zero here if the "unused" duplicate is disabled by CCF.122	 */123	if (!gate->enable_refcnt[gate->clk_num])124		clk_periph_disable_locked(hw);125 126	spin_unlock_irqrestore(&periph_ref_lock, flags);127}128 129const struct clk_ops tegra_clk_periph_gate_ops = {130	.is_enabled = clk_periph_is_enabled,131	.enable = clk_periph_enable,132	.disable = clk_periph_disable,133	.disable_unused = clk_periph_disable_unused,134};135 136struct clk *tegra_clk_register_periph_gate(const char *name,137		const char *parent_name, u8 gate_flags, void __iomem *clk_base,138		unsigned long flags, int clk_num, int *enable_refcnt)139{140	struct tegra_clk_periph_gate *gate;141	struct clk *clk;142	struct clk_init_data init;143	const struct tegra_clk_periph_regs *pregs;144 145	pregs = get_reg_bank(clk_num);146	if (!pregs)147		return ERR_PTR(-EINVAL);148 149	gate = kzalloc(sizeof(*gate), GFP_KERNEL);150	if (!gate) {151		pr_err("%s: could not allocate periph gate clk\n", __func__);152		return ERR_PTR(-ENOMEM);153	}154 155	init.name = name;156	init.flags = flags;157	init.parent_names = parent_name ? &parent_name : NULL;158	init.num_parents = parent_name ? 1 : 0;159	init.ops = &tegra_clk_periph_gate_ops;160 161	gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC;162	gate->clk_base = clk_base;163	gate->clk_num = clk_num;164	gate->flags = gate_flags;165	gate->enable_refcnt = enable_refcnt;166	gate->regs = pregs;167 168	/* Data in .init is copied by clk_register(), so stack variable OK */169	gate->hw.init = &init;170 171	clk = clk_register(NULL, &gate->hw);172	if (IS_ERR(clk))173		kfree(gate);174 175	return clk;176}177