zynqmp: pm: Reimplement clock set divider EEMI API
Clock set divider EEMI API is reimplemented to use system-level clock set divider EEMI API rather than direct MMIO read/write accesses to clock control registers. Signed-off-by: Mirela Simonovic <mirela.simonovic@aggios.com> Acked-by: Will Wong <WILLW@xilinx.com> Signed-off-by: Jolly Shah <jollys@xilinx.com>
This commit is contained in:
parent
bd30503a9d
commit
48dc44e383
|
@ -2558,74 +2558,6 @@ enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id,
|
||||||
return PM_RET_ERROR_ARGS;
|
return PM_RET_ERROR_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* pll_get_lockbit() - Returns lockbit index for pll id
|
|
||||||
* @pll_id: Id of the pll
|
|
||||||
*
|
|
||||||
* This function return the PLL_LOCKED bit index in
|
|
||||||
* pll status register accosiated with given pll id.
|
|
||||||
*
|
|
||||||
* Return: Returns bit index
|
|
||||||
*/
|
|
||||||
static int pll_get_lockbit(unsigned int pll_id)
|
|
||||||
{
|
|
||||||
switch (pll_id) {
|
|
||||||
case CLK_APLL_INT:
|
|
||||||
case CLK_IOPLL_INT:
|
|
||||||
return 0;
|
|
||||||
case CLK_DPLL_INT:
|
|
||||||
case CLK_RPLL_INT:
|
|
||||||
return 1;
|
|
||||||
case CLK_VPLL_INT:
|
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pm_api_pll_bypass_and_reset() - Bypass and reset PLL
|
|
||||||
* @clock_id: Id of the PLL
|
|
||||||
*
|
|
||||||
* This function is to bypass and reset PLL.
|
|
||||||
*/
|
|
||||||
static inline enum pm_ret_status
|
|
||||||
pm_api_pll_bypass_and_reset(unsigned int clock_id, unsigned int flag)
|
|
||||||
{
|
|
||||||
enum pm_ret_status ret = PM_RET_SUCCESS;
|
|
||||||
unsigned int reg, val;
|
|
||||||
int lockbit;
|
|
||||||
|
|
||||||
reg = clocks[clock_id].control_reg;
|
|
||||||
|
|
||||||
if (flag & CLK_PLL_RESET_ASSERT) {
|
|
||||||
ret = pm_mmio_write(reg, PLLCTRL_BP_MASK, PLLCTRL_BP_MASK);
|
|
||||||
if (ret != PM_RET_SUCCESS)
|
|
||||||
return ret;
|
|
||||||
ret = pm_mmio_write(reg, PLLCTRL_RESET_MASK,
|
|
||||||
PLLCTRL_RESET_MASK);
|
|
||||||
if (ret != PM_RET_SUCCESS)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (flag & CLK_PLL_RESET_RELEASE) {
|
|
||||||
ret = pm_mmio_write(reg, PLLCTRL_RESET_MASK,
|
|
||||||
~PLLCTRL_RESET_MASK);
|
|
||||||
if (ret != PM_RET_SUCCESS)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
lockbit = pll_get_lockbit(clock_id);
|
|
||||||
do {
|
|
||||||
ret = pm_mmio_read(clocks[clock_id].status_reg, &val);
|
|
||||||
if (ret != PM_RET_SUCCESS)
|
|
||||||
return ret;
|
|
||||||
} while ((lockbit >= 0) && !(val & (1 << lockbit)));
|
|
||||||
|
|
||||||
ret = pm_mmio_write(reg, PLLCTRL_BP_MASK,
|
|
||||||
~(unsigned int)PLLCTRL_BP_MASK);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL)
|
* pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL)
|
||||||
* @pll: PLL to be locked
|
* @pll: PLL to be locked
|
||||||
|
@ -2696,95 +2628,6 @@ enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll,
|
||||||
return PM_RET_SUCCESS;
|
return PM_RET_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum pm_ret_status pm_api_clk_set_divider(unsigned int clock_id,
|
|
||||||
uint32_t divider)
|
|
||||||
{
|
|
||||||
enum pm_ret_status ret = PM_RET_SUCCESS;
|
|
||||||
struct pm_clock_node *nodes;
|
|
||||||
uint8_t num_nodes;
|
|
||||||
uint16_t div1, div2;
|
|
||||||
unsigned int reg, mask = 0, val = 0, i;
|
|
||||||
uint8_t div1_width = NA_WIDTH, div1_offset = NA_SHIFT;
|
|
||||||
uint8_t div2_width = NA_WIDTH, div2_offset = NA_SHIFT;
|
|
||||||
|
|
||||||
div1 = (uint16_t)(divider & 0xFFFFU);
|
|
||||||
div2 = (uint16_t)((divider >> 16) & 0xFFFFU);
|
|
||||||
|
|
||||||
reg = clocks[clock_id].control_reg;
|
|
||||||
|
|
||||||
nodes = *clocks[clock_id].nodes;
|
|
||||||
num_nodes = clocks[clock_id].num_nodes;
|
|
||||||
for (i = 0; i < num_nodes; i++) {
|
|
||||||
if (nodes->type == TYPE_DIV1) {
|
|
||||||
div1_offset = nodes->offset;
|
|
||||||
div1_width = nodes->width;
|
|
||||||
}
|
|
||||||
if (nodes->type == TYPE_DIV2) {
|
|
||||||
div2_offset = nodes->offset;
|
|
||||||
div2_width = nodes->width;
|
|
||||||
}
|
|
||||||
nodes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (div1 != (uint16_t)-1) {
|
|
||||||
if (div1_width == NA_WIDTH)
|
|
||||||
return PM_RET_ERROR_NOTSUPPORTED;
|
|
||||||
val |= div1 << div1_offset;
|
|
||||||
mask |= BIT_MASK(div1_offset, div1_width);
|
|
||||||
}
|
|
||||||
if (div2 != (uint16_t)-1) {
|
|
||||||
if (div2_width == NA_WIDTH)
|
|
||||||
return PM_RET_ERROR_NOTSUPPORTED;
|
|
||||||
val |= div2 << div2_offset;
|
|
||||||
mask |= BIT_MASK(div2_offset, div2_width);
|
|
||||||
}
|
|
||||||
ret = pm_mmio_write(reg, mask, val);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum pm_ret_status pm_api_pll_set_divider(unsigned int clock_id,
|
|
||||||
unsigned int divider)
|
|
||||||
{
|
|
||||||
unsigned int reg = clocks[clock_id].control_reg;
|
|
||||||
enum pm_ret_status ret;
|
|
||||||
|
|
||||||
pm_api_pll_bypass_and_reset(clock_id, CLK_PLL_RESET_ASSERT);
|
|
||||||
ret = pm_mmio_write(reg, PLL_FBDIV_MASK, divider << PLL_FBDIV_SHIFT);
|
|
||||||
pm_api_pll_bypass_and_reset(clock_id, CLK_PLL_RESET_RELEASE);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pm_api_clock_setdivider - Set the clock divider for given id
|
|
||||||
* @clock_id Id of the clock
|
|
||||||
* @divider Divider value
|
|
||||||
*
|
|
||||||
* This function is used by master to set divider for any clock
|
|
||||||
* to achieve desired rate.
|
|
||||||
*
|
|
||||||
* Return: Returns status, either success or error+reason.
|
|
||||||
*/
|
|
||||||
enum pm_ret_status pm_api_clock_setdivider(unsigned int clock_id,
|
|
||||||
unsigned int divider)
|
|
||||||
{
|
|
||||||
enum pm_ret_status ret;
|
|
||||||
|
|
||||||
if (!pm_clock_valid(clock_id))
|
|
||||||
return PM_RET_ERROR_ARGS;
|
|
||||||
|
|
||||||
if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT)
|
|
||||||
return PM_RET_ERROR_NOTSUPPORTED;
|
|
||||||
|
|
||||||
if (ISPLL(clock_id))
|
|
||||||
ret = pm_api_pll_set_divider(clock_id, divider);
|
|
||||||
else
|
|
||||||
ret = pm_api_clk_set_divider(clock_id, divider);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum pm_ret_status pm_api_clk_get_divider(unsigned int clock_id,
|
static enum pm_ret_status pm_api_clk_get_divider(unsigned int clock_id,
|
||||||
uint32_t *divider)
|
uint32_t *divider)
|
||||||
{
|
{
|
||||||
|
|
|
@ -299,9 +299,6 @@ enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll);
|
||||||
enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll);
|
enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll);
|
||||||
enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll,
|
enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll,
|
||||||
unsigned int *state);
|
unsigned int *state);
|
||||||
|
|
||||||
enum pm_ret_status pm_api_clock_setdivider(unsigned int clock_id,
|
|
||||||
unsigned int divider);
|
|
||||||
enum pm_ret_status pm_api_clock_getdivider(unsigned int clock_id,
|
enum pm_ret_status pm_api_clock_getdivider(unsigned int clock_id,
|
||||||
unsigned int *divider);
|
unsigned int *divider);
|
||||||
enum pm_ret_status pm_api_clock_setrate(unsigned int clock_id,
|
enum pm_ret_status pm_api_clock_setrate(unsigned int clock_id,
|
||||||
|
|
|
@ -964,7 +964,37 @@ enum pm_ret_status pm_clock_getstate(unsigned int clock_id,
|
||||||
enum pm_ret_status pm_clock_setdivider(unsigned int clock_id,
|
enum pm_ret_status pm_clock_setdivider(unsigned int clock_id,
|
||||||
unsigned int divider)
|
unsigned int divider)
|
||||||
{
|
{
|
||||||
return pm_api_clock_setdivider(clock_id, divider);
|
enum pm_ret_status status;
|
||||||
|
enum pm_node_id nid;
|
||||||
|
enum pm_clock_div_id div_id;
|
||||||
|
uint32_t payload[PAYLOAD_ARG_CNT];
|
||||||
|
const uint32_t div0 = 0xFFFF0000;
|
||||||
|
const uint32_t div1 = 0x0000FFFF;
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
|
/* Get PLL node ID using PLL clock ID */
|
||||||
|
status = pm_clock_get_pll_node_id(clock_id, &nid);
|
||||||
|
if (status == PM_RET_SUCCESS)
|
||||||
|
return pm_pll_set_parameter(nid, PM_PLL_PARAM_FBDIV, divider);
|
||||||
|
|
||||||
|
/* Check if clock ID is a valid on-chip clock */
|
||||||
|
status = pm_clock_id_is_valid(clock_id);
|
||||||
|
if (status != PM_RET_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (div0 == (divider & div0)) {
|
||||||
|
div_id = PM_CLOCK_DIV0_ID;
|
||||||
|
val = divider & ~div0;
|
||||||
|
} else if (div1 == (divider & div1)) {
|
||||||
|
div_id = PM_CLOCK_DIV1_ID;
|
||||||
|
val = (divider & ~div1) >> 16;
|
||||||
|
} else {
|
||||||
|
return PM_RET_ERROR_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send request to the PMU */
|
||||||
|
PM_PACK_PAYLOAD4(payload, PM_CLOCK_SETDIVIDER, clock_id, div_id, val);
|
||||||
|
return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -308,4 +308,13 @@ enum pm_pll_mode {
|
||||||
PM_PLL_MODE_MAX,
|
PM_PLL_MODE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @PM_CLOCK_DIV0_ID: Clock divider 0
|
||||||
|
* @PM_CLOCK_DIV1_ID: Clock divider 1
|
||||||
|
*/
|
||||||
|
enum pm_clock_div_id {
|
||||||
|
PM_CLOCK_DIV0_ID,
|
||||||
|
PM_CLOCK_DIV1_ID,
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* PM_DEFS_H */
|
#endif /* PM_DEFS_H */
|
||||||
|
|
Loading…
Reference in New Issue