149 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * mmp APB clock operation source file4 *5 * Copyright (C) 2012 Marvell6 * Chao Xie <xiechao.mail@gmail.com>7 */8 9#include <linux/kernel.h>10#include <linux/io.h>11#include <linux/err.h>12#include <linux/delay.h>13#include <linux/slab.h>14 15#include "clk.h"16 17/* Common APB clock register bit definitions */18#define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */19#define APBC_FNCLK (1 << 1) /* Functional Clock Enable */20#define APBC_RST (1 << 2) /* Reset Generation */21#define APBC_POWER (1 << 7) /* Reset Generation */22 23#define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw)24struct clk_apbc {25 struct clk_hw hw;26 void __iomem *base;27 unsigned int delay;28 unsigned int flags;29 spinlock_t *lock;30};31 32static int clk_apbc_prepare(struct clk_hw *hw)33{34 struct clk_apbc *apbc = to_clk_apbc(hw);35 unsigned int data;36 unsigned long flags = 0;37 38 /*39 * It may share same register as MUX clock,40 * and it will impact FNCLK enable. Spinlock is needed41 */42 if (apbc->lock)43 spin_lock_irqsave(apbc->lock, flags);44 45 data = readl_relaxed(apbc->base);46 if (apbc->flags & APBC_POWER_CTRL)47 data |= APBC_POWER;48 data |= APBC_FNCLK;49 writel_relaxed(data, apbc->base);50 51 if (apbc->lock)52 spin_unlock_irqrestore(apbc->lock, flags);53 54 udelay(apbc->delay);55 56 if (apbc->lock)57 spin_lock_irqsave(apbc->lock, flags);58 59 data = readl_relaxed(apbc->base);60 data |= APBC_APBCLK;61 writel_relaxed(data, apbc->base);62 63 if (apbc->lock)64 spin_unlock_irqrestore(apbc->lock, flags);65 66 udelay(apbc->delay);67 68 if (!(apbc->flags & APBC_NO_BUS_CTRL)) {69 if (apbc->lock)70 spin_lock_irqsave(apbc->lock, flags);71 72 data = readl_relaxed(apbc->base);73 data &= ~APBC_RST;74 writel_relaxed(data, apbc->base);75 76 if (apbc->lock)77 spin_unlock_irqrestore(apbc->lock, flags);78 }79 80 return 0;81}82 83static void clk_apbc_unprepare(struct clk_hw *hw)84{85 struct clk_apbc *apbc = to_clk_apbc(hw);86 unsigned long data;87 unsigned long flags = 0;88 89 if (apbc->lock)90 spin_lock_irqsave(apbc->lock, flags);91 92 data = readl_relaxed(apbc->base);93 if (apbc->flags & APBC_POWER_CTRL)94 data &= ~APBC_POWER;95 data &= ~APBC_FNCLK;96 writel_relaxed(data, apbc->base);97 98 if (apbc->lock)99 spin_unlock_irqrestore(apbc->lock, flags);100 101 udelay(10);102 103 if (apbc->lock)104 spin_lock_irqsave(apbc->lock, flags);105 106 data = readl_relaxed(apbc->base);107 data &= ~APBC_APBCLK;108 writel_relaxed(data, apbc->base);109 110 if (apbc->lock)111 spin_unlock_irqrestore(apbc->lock, flags);112}113 114static const struct clk_ops clk_apbc_ops = {115 .prepare = clk_apbc_prepare,116 .unprepare = clk_apbc_unprepare,117};118 119struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name,120 void __iomem *base, unsigned int delay,121 unsigned int apbc_flags, spinlock_t *lock)122{123 struct clk_apbc *apbc;124 struct clk *clk;125 struct clk_init_data init;126 127 apbc = kzalloc(sizeof(*apbc), GFP_KERNEL);128 if (!apbc)129 return NULL;130 131 init.name = name;132 init.ops = &clk_apbc_ops;133 init.flags = CLK_SET_RATE_PARENT;134 init.parent_names = (parent_name ? &parent_name : NULL);135 init.num_parents = (parent_name ? 1 : 0);136 137 apbc->base = base;138 apbc->delay = delay;139 apbc->flags = apbc_flags;140 apbc->lock = lock;141 apbc->hw.init = &init;142 143 clk = clk_register(NULL, &apbc->hw);144 if (IS_ERR(clk))145 kfree(apbc);146 147 return clk;148}149