sdk-hwV1.3/lichee/rtos-hal/hal/source/ccmu/sunxi/clk_periph.c

561 lines
15 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
*
* Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
*the the People's Republic of China and other countries.
* All Allwinner Technology Co.,Ltd. trademarks are used with permission.
*
* DISCLAIMER
* THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
* IF YOU NEED TO INTEGRATE THIRD PARTYS TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
* IN ALLWINNERSSDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
* ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
* ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
* COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
* YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTYS TECHNOLOGY.
*
*
* THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
* PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
* THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
* OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sunxi_hal_common.h"
#include "clk_periph.h"
#define NEW_RATE_CALCULATE 1
hal_clk_status_t sunxi_clk_periph_get_parent(clk_periph_pt clk, u8 *parent_index)
{
u32 reg = 0;
struct sunxi_clk_periph *periph = clk->config;
CCMU_TRACE();
if (!periph->mux.reg)
{
*parent_index = 0;
return HAL_CLK_STATUS_OK;
}
//if (periph->lock)
// spin_lock_irqsave(periph->lock, flags);
reg = readl(periph->mux.reg);
*parent_index = GET_BITS(periph->mux.shift, periph->mux.width, reg);
//if (periph->lock)
// spin_unlock_irqrestore(periph->lock, flags);
CCMU_DBG("parent index %d \n", (*parent_index));
return HAL_CLK_STATUS_OK;
}
hal_clk_status_t sunxi_clk_periph_set_parent(clk_periph_pt clk, u8 index)
{
unsigned long reg, flags = 0;
struct sunxi_clk_periph *periph = clk->config;
//if (periph->flags & CLK_READONLY)
// return 0;
if (!periph->mux.reg)
{
return HAL_CLK_STATUS_OK;
}
//if (periph->lock)
// spin_lock_irqsave(periph->lock, flags);
reg = readl(periph->mux.reg);
reg = SET_BITS(periph->mux.shift, periph->mux.width, reg, index);
writel(reg, periph->mux.reg);
//if (periph->lock)
// spin_unlock_irqrestore(periph->lock, flags);
return HAL_CLK_STATUS_OK;
}
hal_clk_status_t __sunxi_clk_periph_enable_shared(struct sunxi_clk_periph *periph)
{
unsigned long reg;
struct sunxi_clk_periph_gate *gate = &periph->gate;
if (!periph->com_gate->val)
{
/* de-assert module */
if (gate->reset && IS_SHARE_RST_GATE(periph))
{
reg = readl(gate->reset);
reg = SET_BITS(gate->rst_shift, 1, reg, 1);
writel(reg, gate->reset);
}
/* enable bus gating */
if (gate->bus && IS_SHARE_BUS_GATE(periph))
{
reg = readl(gate->bus);
reg = SET_BITS(gate->bus_shift, 1, reg, 1);
writel(reg, gate->bus);
}
/* enable module gating */
if (gate->enable && IS_SHARE_MOD_GATE(periph))
{
reg = readl(gate->enable);
reg = SET_BITS(gate->enb_shift, 1, reg, 1);
writel(reg, gate->enable);
}
/* enable dram gating */
if (gate->dram && IS_SHARE_MBUS_GATE(periph))
{
reg = readl(gate->dram);
reg = SET_BITS(gate->ddr_shift, 1, reg, 1);
writel(reg, gate->dram);
}
}
periph->com_gate->val |= 1 << periph->com_gate_off;
return HAL_CLK_STATUS_OK;
}
hal_clk_status_t __sunxi_clk_periph_enable(struct sunxi_clk_periph *periph)
{
u32 reg;
struct sunxi_clk_periph_gate *gate = &periph->gate;
/* de-assert module */
if (gate->reset && !IS_SHARE_RST_GATE(periph))
{
reg = readl(gate->reset);
reg = SET_BITS(gate->rst_shift, 1, reg, 1);
writel(reg, gate->reset);
}
/* enable bus gating */
if (gate->bus && !IS_SHARE_BUS_GATE(periph))
{
reg = readl(gate->bus);
reg = SET_BITS(gate->bus_shift, 1, reg, 1);
writel(reg, gate->bus);
}
/* enable module gating */
if (gate->enable && !IS_SHARE_MOD_GATE(periph))
{
reg = readl(gate->enable);
//if (periph->flags & CLK_REVERT_ENABLE)
// reg = SET_BITS(gate->enb_shift, 1, reg, 0);
//else
reg = SET_BITS(gate->enb_shift, 1, reg, 1);
writel(reg, gate->enable);
}
/* enable dram gating */
if (gate->dram && !IS_SHARE_MBUS_GATE(periph))
{
reg = readl(gate->dram);
reg = SET_BITS(gate->ddr_shift, 1, reg, 1);
writel(reg, gate->dram);
}
return HAL_CLK_STATUS_OK;
}
hal_clk_status_t sunxi_clk_periph_enable(clk_periph_pt clk)
{
//unsigned long flags = 0;
hal_clk_status_t ret = 0;
struct sunxi_clk_periph *periph = NULL;
//if (periph->flags & CLK_READONLY)
// return 0;
periph = clk->config;
//if (periph->lock)
// spin_lock_irqsave(periph->lock, flags);
/* if common gate exist, enable it first */
if (periph->com_gate)
{
ret = __sunxi_clk_periph_enable_shared(periph);
}
if (!ret)
{
ret = __sunxi_clk_periph_enable(periph);
}
//if (periph->lock)
// spin_unlock_irqrestore(periph->lock, flags);
return ret == HAL_CLK_STATUS_OK ? \
HAL_CLK_STATUS_ENABLED : HAL_CLK_STATUS_ERROR_CLK_ENABLED_FAILED;
}
hal_clk_status_t sunxi_clk_periph_is_enabled(clk_periph_pt clk)
{
u32 state = 0, reg = 0;
//unsigned long flags = 0;
struct sunxi_clk_periph *periph = NULL;
struct sunxi_clk_periph_gate *gate = NULL;
periph = clk->config;
gate = &periph->gate;
//if (periph->lock)
// spin_lock_irqsave(periph->lock, flags);
/* enable bus gating */
if (gate->bus)
{
reg = readl(gate->bus);
state &= GET_BITS(gate->bus_shift, 1, reg);
}
/* enable module gating */
if (gate->enable)
{
reg = readl(gate->enable);
state &= GET_BITS(gate->enb_shift, 1, reg);
}
/* de-assert module */
if (gate->reset)
{
reg = readl(gate->reset);
state &= GET_BITS(gate->rst_shift, 1, reg);
}
/* enable dram gating */
if (gate->dram)
{
reg = readl(gate->dram);
state &= GET_BITS(gate->ddr_shift, 1, reg);
}
//if (periph->lock)
// spin_unlock_irqrestore(periph->lock, flags);
return state ? HAL_CLK_STATUS_ENABLED : HAL_CLK_STATUS_DISABLED;
}
static void __sunxi_clk_periph_disable_shared(struct sunxi_clk_periph *periph)
{
unsigned long reg;
struct sunxi_clk_periph_gate *gate = &periph->gate;
if (!periph->com_gate->val)
{
return;
}
periph->com_gate->val &= ~(1 << periph->com_gate_off);
if (!periph->com_gate->val)
{
/* disable dram gating */
if (gate->dram && IS_SHARE_MBUS_GATE(periph))
{
reg = readl(gate->dram);
reg = SET_BITS(gate->ddr_shift, 1, reg, 0);
writel(reg, gate->dram);
}
/* disable module gating */
if (gate->enable && IS_SHARE_MOD_GATE(periph))
{
reg = readl(gate->enable);
reg = SET_BITS(gate->enb_shift, 1, reg, 0);
writel(reg, gate->enable);
}
/* disable bus gating */
if (gate->bus && IS_SHARE_BUS_GATE(periph))
{
reg = readl(gate->bus);
reg = SET_BITS(gate->bus_shift, 1, reg, 0);
writel(reg, gate->bus);
}
/* assert module */
if (gate->reset && IS_SHARE_RST_GATE(periph))
{
reg = readl(gate->reset);
reg = SET_BITS(gate->rst_shift, 1, reg, 0);
writel(reg, gate->reset);
}
}
}
static void __sunxi_clk_periph_disable(struct sunxi_clk_periph *periph)
{
u32 reg;
struct sunxi_clk_periph_gate *gate = &periph->gate;
/* disable dram gating */
if (gate->dram && !IS_SHARE_MBUS_GATE(periph))
{
reg = readl(gate->dram);
reg = SET_BITS(gate->ddr_shift, 1, reg, 0);
writel(reg, gate->dram);
}
/* disable module gating */
if (gate->enable && !IS_SHARE_MOD_GATE(periph))
{
reg = readl(gate->enable);
//if (periph->flags & CLK_REVERT_ENABLE)
// reg = SET_BITS(gate->enb_shift, 1, reg, 1);
//else
reg = SET_BITS(gate->enb_shift, 1, reg, 0);
writel(reg, gate->enable);
}
/* disable bus gating */
if (gate->bus && !IS_SHARE_BUS_GATE(periph))
{
reg = readl(gate->bus);
reg = SET_BITS(gate->bus_shift, 1, reg, 0);
writel(reg, gate->bus);
}
/* assert module */
if (gate->reset && !IS_SHARE_RST_GATE(periph))
{
reg = readl(gate->reset);
reg = SET_BITS(gate->rst_shift, 1, reg, 0);
writel(reg, gate->reset);
}
}
hal_clk_status_t sunxi_clk_periph_disable(clk_periph_pt clk)
{
//unsigned long flags = 0;
struct sunxi_clk_periph *periph = NULL;
//if (periph->flags & CLK_READONLY)
// return 0;
periph = clk->config;
//if (periph->lock)
// spin_lock_irqsave(periph->lock, flags);
__sunxi_clk_periph_disable(periph);
/* if common gate exist, disable it */
if (periph->com_gate)
{
__sunxi_clk_periph_disable_shared(periph);
}
//if (periph->lock)
// spin_unlock_irqrestore(periph->lock, flags);
return HAL_CLK_STATUS_DISABLED;
}
hal_clk_status_t sunxi_clk_periph_recalc_rate(clk_periph_pt clk, u32 *rate)
{
u32 reg = 0, parent_rate = 0;
u64 div, div_m = 0, div_n = 0, clk_rate = 0;
hal_clk_status_t ret = HAL_CLK_STATUS_OK;
struct sunxi_clk_periph *periph = NULL;
struct sunxi_clk_periph_div *divider = NULL;
periph = clk->config;
divider = &periph->divider;
parent_rate = clk->clk_core.parent_rate;
clk_rate = (u64)parent_rate;
if (!divider->reg)
{
*rate = parent_rate;
return ret;
}
//if (periph->lock)
// spin_lock_irqsave(periph->lock, flags);
reg = readl(divider->reg);
if (divider->mwidth)
{
div_m = GET_BITS(divider->mshift, divider->mwidth, reg);
}
if (divider->nwidth)
{
div_n = GET_BITS(divider->nshift, divider->nwidth, reg);
}
div = (div_m + 1) * (1 << div_n);
do_div(clk_rate, div);
//if (periph->lock)
// spin_unlock_irqrestore(periph->lock, flags);
*rate = (u32)clk_rate;
return ret;
}
u32 sunxi_clk_periph_round_rate(clk_periph_pt clk, u32 rate, u32 prate)
{
struct sunxi_clk_periph *periph = NULL;
struct sunxi_clk_periph_div *divider = NULL;
u32 i = 0, round_rate = 0;
u64 div, div_m = 0, div_n = 0;
u64 factor_m = 0, factor_n = 0, found = 0;
CCMU_TRACE();
periph = clk->config;
divider = &periph->divider;
round_rate = (prate + rate / 2 - 1);
if (!rate)
{
return 0;
}
do_div(round_rate, rate);
div = round_rate;
if (!div)
{
return prate;
}
round_rate = prate;
div_m = 1 << divider->mwidth;
if (divider->nwidth)
{
div_n = 1 << divider->nwidth;
div_n = 1 << (div_n - 1);
}
else
{
div_n = 1;
}
/* NEW_RATE_CALCULATE */
while (i < (1 << divider->nwidth))
{
if (div <= div_m)
{
factor_m = div - 1;
factor_n = i;
do_div(round_rate, (factor_m + 1) * (1 << factor_n));
found = 1;
break;
}
div = div >> 1;
i++;
if (!div)
{
factor_m = 0;
factor_n = i;
do_div(round_rate, (factor_m + 1) * (1 << factor_n));
found = 1;
break;
}
}
if (!found)
{
factor_m = (div > div_m ? div_m : div) - 1;
factor_n = (1 << divider->nwidth) - 1;
do_div(round_rate, (factor_m + 1) * (1 << factor_n));
}
CCMU_DBG("parent rate %dHZ, target rate %dHZ, round rate %dHZ\n", prate, rate, round_rate);
CCMU_TRACE();
return round_rate;
}
hal_clk_status_t sunxi_clk_periph_set_rate(clk_periph_pt clk, u32 rate)
{
u32 i = 0, factor_m = 0, factor_n = 0, found = 0, parent_rate = 0;
u32 reg = 0;
struct sunxi_clk_periph *periph = NULL;
struct sunxi_clk_periph_div *divider = NULL;
u32 div, div_m = 0, div_n = 0;
CCMU_TRACE();
periph = clk->config;
divider = &periph->divider;
parent_rate = clk->clk_core.parent_rate;
CCMU_DBG("parent rate %dHZ, set rate %dHZ\n", parent_rate, rate);
u64 tmp_rate = parent_rate;
do_div(tmp_rate, rate);
div = tmp_rate;
if (!div)
{
div_m = div_n = 0;
}
else
{
div_m = 1 << divider->mwidth;
div_n = (1 << divider->nwidth) - 1;
if (div > (div_m << div_n))
{
//WARN(1, "clk %s rate is too large : %lu\n", hw->init->name, rate);
div = div_m << div_n;
}
found = 0;
while (i < (1 << divider->nwidth))
{
if (div <= div_m)
{
factor_m = div - 1;
factor_n = i;
found = 1;
break;
}
div = div >> 1;
i++;
if (!div)
{
factor_m = 0;
factor_n = i;
found = 1;
break;
}
}
if (!found)
{
factor_m = (div > div_m ? div_m : div) - 1;
factor_n = (1 << divider->nwidth) - 1;
}
div_m = factor_m;
div_n = factor_n;
}
CCMU_DBG("divider->reg %d divider->mwidth %d divider->nshift %d \n", divider->reg, divider->mwidth, divider->nshift);
if (!divider->reg)
{
return HAL_CLK_STATUS_OK;
}
reg = readl(divider->reg);
if (divider->mwidth)
{
reg = SET_BITS(divider->mshift, divider->mwidth, reg, div_m);
}
if (divider->nwidth)
{
reg = SET_BITS(divider->nshift, divider->nwidth, reg, div_n);
}
writel(reg, divider->reg);
CCMU_TRACE();
return HAL_CLK_STATUS_OK;
}