// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Broadcom Corporation
* Copyright 2013 Linaro Limited
*/
#include "clk-kona.h"
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/clk-provider.h>
/*
* "Policies" affect the frequencies of bus clocks provided by a
* CCU. (I believe these polices are named "Deep Sleep", "Economy",
* "Normal", and "Turbo".) A lower policy number has lower power
* consumption, and policy 2 is the default.
*/
#define CCU_POLICY_COUNT 4
#define CCU_ACCESS_PASSWORD 0xA5A500
#define CLK_GATE_DELAY_LOOP 2000
/* Bitfield operations */
/* Produces a mask of set bits covering a range of a 32-bit value */
static inline u32 bitfield_mask(u32 shift, u32 width)
{
return ((1 << width) - 1) << shift;
}
/* Extract the value of a bitfield found within a given register value */
static inline u32 bitfield_extract(u32 reg_val, u32 shift, u32 width)
{
return (reg_val & bitfield_mask(shift, width)) >> shift;
}
/* Replace the value of a bitfield found within a given register value */
static inline u32 bitfield_replace(u32 reg_val, u32 shift, u32 width, u32 val)
{
u32 mask = bitfield_mask(shift, width);
return (reg_val & ~mask) | (val << shift);
}
/* Divider and scaling helpers */
/* Convert a divider into the scaled divisor value it represents. */
static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div)
{
return (u64)reg_div + ((u64)1 << div->u.s.frac_width);
}
/*
* Build a scaled divider value as close as possible to the
* given whole part (div_value) and fractional part (expressed
* in billionths).
*/
u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths)
{
u64 combined;
BUG_ON(!div_value);
BUG_ON(billionths >= BILLION);
combined = (u64)div_value * BILLION + billionths;
combined <<= div->u.s.frac_width;
return DIV_ROUND_CLOSEST_ULL(combined, BILLION);
}
/* The scaled minimum divisor representable by a divider */
static inline u64
scaled_div_min(struct bcm_clk_div *div)
{
if (divider_is_fixed(div))
return (u64)div->u.fixed;
return scaled_div_value(div, 0);
}
/* The scaled maximum divisor representable by a divider */
u64 scaled_div_max(struct bcm_clk_div *div)
{
u32 reg_div;
if (divider_is_fixed(div))
return (u64)div->u.fixed;
reg_div = ((u32)1 << div->u.s.width) - 1;
return scaled_div_value(div, reg_div);
}
/*
* Convert a scaled divisor into its divider representation as
* stored in a divider register field.
*/
static inline u32
divider(struct bcm_clk_div *div, u64 scaled_div)
{
BUG_ON(scaled_div < scaled_div_min(div));
BUG_ON(scaled_div > scaled_div_max(div));
return (u32)(scaled_div - ((u64)1 << div->u.s.frac_width));
}
/* Return a rate scaled for use when dividing by a scaled divisor. */
static inline u64
scale_rate(struct bcm_clk_div *div, u32 rate)
{
if (divider_is_fixed(div))
return (u64)rate;
return (u64)rate << div->u.s.frac_width;
}
/* CCU access */
/* Read a 32-bit register value from a CCU's address space. */
static inline u32 __ccu_read(struct ccu_data *ccu