162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mmp gate clock operation source file 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Marvell 662306a36Sopenharmony_ci * Chao Xie <chao.xie@marvell.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "clk.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Some clocks will have mutiple bits to enable the clocks, and 1962306a36Sopenharmony_ci * the bits to disable the clock is not same as enabling bits. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define to_clk_mmp_gate(hw) container_of(hw, struct mmp_clk_gate, hw) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int mmp_clk_gate_enable(struct clk_hw *hw) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); 2762306a36Sopenharmony_ci unsigned long flags = 0; 2862306a36Sopenharmony_ci unsigned long rate; 2962306a36Sopenharmony_ci u32 tmp; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (gate->lock) 3262306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci tmp = readl(gate->reg); 3562306a36Sopenharmony_ci tmp &= ~gate->mask; 3662306a36Sopenharmony_ci tmp |= gate->val_enable; 3762306a36Sopenharmony_ci writel(tmp, gate->reg); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (gate->lock) 4062306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (gate->flags & MMP_CLK_GATE_NEED_DELAY) { 4362306a36Sopenharmony_ci rate = clk_hw_get_rate(hw); 4462306a36Sopenharmony_ci /* Need delay 2 cycles. */ 4562306a36Sopenharmony_ci udelay(2000000/rate); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void mmp_clk_gate_disable(struct clk_hw *hw) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); 5462306a36Sopenharmony_ci unsigned long flags = 0; 5562306a36Sopenharmony_ci u32 tmp; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (gate->lock) 5862306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci tmp = readl(gate->reg); 6162306a36Sopenharmony_ci tmp &= ~gate->mask; 6262306a36Sopenharmony_ci tmp |= gate->val_disable; 6362306a36Sopenharmony_ci writel(tmp, gate->reg); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (gate->lock) 6662306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int mmp_clk_gate_is_enabled(struct clk_hw *hw) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); 7262306a36Sopenharmony_ci unsigned long flags = 0; 7362306a36Sopenharmony_ci u32 tmp; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (gate->lock) 7662306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci tmp = readl(gate->reg); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (gate->lock) 8162306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return (tmp & gate->mask) == gate->val_enable; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciconst struct clk_ops mmp_clk_gate_ops = { 8762306a36Sopenharmony_ci .enable = mmp_clk_gate_enable, 8862306a36Sopenharmony_ci .disable = mmp_clk_gate_disable, 8962306a36Sopenharmony_ci .is_enabled = mmp_clk_gate_is_enabled, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct clk *mmp_clk_register_gate(struct device *dev, const char *name, 9362306a36Sopenharmony_ci const char *parent_name, unsigned long flags, 9462306a36Sopenharmony_ci void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable, 9562306a36Sopenharmony_ci unsigned int gate_flags, spinlock_t *lock) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct mmp_clk_gate *gate; 9862306a36Sopenharmony_ci struct clk *clk; 9962306a36Sopenharmony_ci struct clk_init_data init; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* allocate the gate */ 10262306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 10362306a36Sopenharmony_ci if (!gate) 10462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci init.name = name; 10762306a36Sopenharmony_ci init.ops = &mmp_clk_gate_ops; 10862306a36Sopenharmony_ci init.flags = flags; 10962306a36Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 11062306a36Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* struct clk_gate assignments */ 11362306a36Sopenharmony_ci gate->reg = reg; 11462306a36Sopenharmony_ci gate->mask = mask; 11562306a36Sopenharmony_ci gate->val_enable = val_enable; 11662306a36Sopenharmony_ci gate->val_disable = val_disable; 11762306a36Sopenharmony_ci gate->flags = gate_flags; 11862306a36Sopenharmony_ci gate->lock = lock; 11962306a36Sopenharmony_ci gate->hw.init = &init; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci clk = clk_register(dev, &gate->hw); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (IS_ERR(clk)) 12462306a36Sopenharmony_ci kfree(gate); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return clk; 12762306a36Sopenharmony_ci} 128