/*
* Copyright (C) 2013 Broadcom Corporation
* Copyright 2013 Linaro Limited
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "clk-kona.h"
#include <linux/delay.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 */
/*
* Implement DIV_ROUND_CLOSEST() for 64-bit dividend and both values
* unsigned. Note that unlike do_div(), the remainder is discarded
* and the return value is the quotient (not the remainder).
*/
u64 do_div_round_closest(u64 dividend, unsigned long divisor)
{
u64 result;
result = dividend + ((u64)divisor >> 1);
(void)do_div(result, divisor);
return result;
}
/* 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 do_div_round_closest(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