diff --git a/product/n1sdp/module/n1sdp_pll/include/internal/n1sdp_pll.h b/product/n1sdp/module/n1sdp_pll/include/internal/n1sdp_pll.h index 3af7d546fbb5ffc48310546c91a39b4dad3edcec..9beb68d6b17d24542d7b27713e73502b2f79ebb6 100644 --- a/product/n1sdp/module/n1sdp_pll/include/internal/n1sdp_pll.h +++ b/product/n1sdp/module/n1sdp_pll/include/internal/n1sdp_pll.h @@ -39,4 +39,19 @@ /*! Step size for the PLL. */ #define MOD_N1SDP_PLL_STEP_SIZE UINT64_C(1000) +/*! The minimum feedback divider value */ +#define MOD_N1SDP_PLL_FBDIV_MIN 16 +/*! The maximum feedback divider value */ +#define MOD_N1SDP_PLL_FBDIV_MAX 1600 + +/*! The minimum reference clock divider value */ +#define MOD_N1SDP_PLL_REFDIV_MIN 1 +/*! The maximum reference clock divider value */ +#define MOD_N1SDP_PLL_REFDIV_MAX 63 + +/*! The minimum post divider value */ +#define MOD_N1SDP_PLL_POSTDIV_MIN 1 +/*! The maximum post divider value */ +#define MOD_N1SDP_PLL_POSTDIV_MAX 7 + #endif /* N1SDP_PLL_H */ diff --git a/product/n1sdp/module/n1sdp_pll/include/mod_n1sdp_pll.h b/product/n1sdp/module/n1sdp_pll/include/mod_n1sdp_pll.h index 41e7c69166f111d2dbe2d85db829336d6838a6e1..139e0f22bdc75df8d3a00c6dd10b43fe6332e1e2 100644 --- a/product/n1sdp/module/n1sdp_pll/include/mod_n1sdp_pll.h +++ b/product/n1sdp/module/n1sdp_pll/include/mod_n1sdp_pll.h @@ -65,6 +65,34 @@ struct mod_n1sdp_pll_dev_config { const bool defer_initialization; }; +/*! + * \brief PLL parameter values for non-absolute frequencies. + */ +struct n1sdp_pll_custom_freq_param_entry { + /*! Required output frequency value in MHz */ + uint16_t freq_value_mhz; + + /*! Feedback divider value for this frequency */ + uint16_t fbdiv; + + /*! Reference clock divider value for this frequency */ + uint8_t refdiv; + + /*! Post divider 1 value for this frequency */ + uint8_t postdiv; +}; + +/*! + * \brief N1SDP PLL module configuration. + */ +struct n1sdp_pll_module_config { + /*! Pointer to custom frequency table */ + struct n1sdp_pll_custom_freq_param_entry *custom_freq_table; + + /*! Size of custom frequency table */ + size_t custom_freq_table_size; +}; + /*! * @} */ diff --git a/product/n1sdp/module/n1sdp_pll/src/mod_n1sdp_pll.c b/product/n1sdp/module/n1sdp_pll/src/mod_n1sdp_pll.c index 415ab58babb14d3c53bdb2e6085b6cf71d5385bf..fa3599498d659d5c75601cd5ecc048e7a49640b6 100644 --- a/product/n1sdp/module/n1sdp_pll/src/mod_n1sdp_pll.c +++ b/product/n1sdp/module/n1sdp_pll/src/mod_n1sdp_pll.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,9 @@ struct n1sdp_pll_dev_ctx { /* Module context */ struct n1sdp_pll_ctx { + /* Pointer to module configuration data */ + const struct n1sdp_pll_module_config *mod_config; + /* List of device contexts of all PLLs */ struct n1sdp_pll_dev_ctx *dev_ctx_table; @@ -52,10 +56,16 @@ static int pll_set_rate(struct n1sdp_pll_dev_ctx *ctx, uint64_t rate, { uint64_t rounded_rate; uint16_t fbdiv; + uint8_t refdiv; + uint8_t postdiv; uint32_t wait_cycles; + uint16_t rate_val_mhz; const struct mod_n1sdp_pll_dev_config *config = NULL; + struct n1sdp_pll_custom_freq_param_entry *freq_entry = NULL; + size_t i; - assert(ctx != NULL); + fwk_assert(ctx != NULL); + fwk_assert(rate <= (UINT16_MAX * FWK_MHZ)); config = ctx->config; @@ -65,12 +75,37 @@ static int pll_set_rate(struct n1sdp_pll_dev_ctx *ctx, uint64_t rate, if ((rate < MOD_N1SDP_PLL_RATE_MIN) || (rate > MOD_N1SDP_PLL_RATE_MAX)) return FWK_E_RANGE; + /* Assume initial refdiv and postdiv to be 1 */ + refdiv = MOD_N1SDP_PLL_REFDIV_MIN; + postdiv = MOD_N1SDP_PLL_POSTDIV_MIN; fbdiv = rate / config->ref_rate; rounded_rate = fbdiv * config->ref_rate; + /* + * If required output value is not exact multiplication of reference + * clock value then look for the frequency in custom frequencies table. + */ + if (rounded_rate != rate) { + rate_val_mhz = (uint16_t)(rate / FWK_MHZ); + for (i = 0; i < module_ctx.mod_config->custom_freq_table_size; i++) { + freq_entry = &module_ctx.mod_config->custom_freq_table[i]; + if (freq_entry->freq_value_mhz == rate_val_mhz) { + fbdiv = freq_entry->fbdiv; + refdiv = freq_entry->refdiv; + postdiv = freq_entry->postdiv; + goto result; + } + } + /* Custom frequency table does not have matching frequency */ + return FWK_E_RANGE; + } + +result: /* Configure PLL settings */ *config->control_reg0 = (fbdiv << PLL_FBDIV_BIT_POS) | - (1 << PLL_REFDIV_POS); + (refdiv << PLL_REFDIV_POS); + *config->control_reg1 = (postdiv << PLL_POSTDIV1_POS) | + (1 << PLL_POSTDIV2_POS); /* Enable PLL settings */ *config->control_reg0 |= (1 << PLL_PLLEN_POS); @@ -83,17 +118,8 @@ static int pll_set_rate(struct n1sdp_pll_dev_ctx *ctx, uint64_t rate, return FWK_E_TIMEOUT; } - /* - * The multiply factor for a PLL is calculated by dividing the required - * output clock frequency by the reference clock frequency. This integer - * division may leave out remainder value, if any, and hence the actual - * output frequency may not be exactly same as the required output - * frequency. For example, if the required output frequency is 120MHz and - * ref_rate is 50MHz, then the division generates a multiply factor of 2 - * (120/50 = 2, integer division). Now the actual PLL output will be - * (50MHz * 2 = 100MHz). - */ - ctx->current_rate = rounded_rate; + /* Store the current configured PLL rate */ + ctx->current_rate = rate; return FWK_SUCCESS; } @@ -276,9 +302,12 @@ static const struct mod_clock_drv_api n1sdp_pll_api = { */ static int n1sdp_pll_init(fwk_id_t module_id, unsigned int element_count, - const void *unused) + const void *config) { - if (element_count == 0) + size_t i; + struct n1sdp_pll_custom_freq_param_entry *freq_entry; + + if ((element_count == 0) || (config == NULL)) return FWK_E_PARAM; module_ctx.dev_count = element_count; @@ -288,6 +317,19 @@ static int n1sdp_pll_init(fwk_id_t module_id, unsigned int element_count, if (module_ctx.dev_ctx_table == NULL) return FWK_E_NOMEM; + module_ctx.mod_config = config; + /* Validate custom frequency table entries */ + for (i = 0; i < module_ctx.mod_config->custom_freq_table_size; i++) { + freq_entry = &module_ctx.mod_config->custom_freq_table[i]; + if ((freq_entry->fbdiv < MOD_N1SDP_PLL_FBDIV_MIN) || + (freq_entry->fbdiv > MOD_N1SDP_PLL_FBDIV_MAX) || + (freq_entry->refdiv < MOD_N1SDP_PLL_REFDIV_MIN) || + (freq_entry->refdiv > MOD_N1SDP_PLL_REFDIV_MAX) || + (freq_entry->postdiv < MOD_N1SDP_PLL_POSTDIV_MIN) || + (freq_entry->postdiv > MOD_N1SDP_PLL_POSTDIV_MAX)) + return FWK_E_RANGE; + } + return FWK_SUCCESS; } diff --git a/product/n1sdp/scp_ramfw/config_n1sdp_pll.c b/product/n1sdp/scp_ramfw/config_n1sdp_pll.c index 6a87cd1d1c03f3bba9458dbad1fadf9afaed060f..d4b720b3e5b040ab38d83ef330445e4efa77bcd0 100644 --- a/product/n1sdp/scp_ramfw/config_n1sdp_pll.c +++ b/product/n1sdp/scp_ramfw/config_n1sdp_pll.c @@ -14,6 +14,15 @@ #include #include +static struct n1sdp_pll_custom_freq_param_entry freq_table[] = { + { + .freq_value_mhz = 1333, + .fbdiv = 160, + .refdiv = 3, + .postdiv = 2, + }, +}; + static const struct fwk_element n1sdp_pll_element_table[] = { [CLOCK_PLL_IDX_CPU0] = { .name = "CPU_PLL_0", @@ -80,4 +89,8 @@ static const struct fwk_element *n1sdp_pll_get_element_table const struct fwk_module_config config_n1sdp_pll = { .get_element_table = n1sdp_pll_get_element_table, + .data = &((struct n1sdp_pll_module_config) { + .custom_freq_table = freq_table, + .custom_freq_table_size = FWK_ARRAY_SIZE(freq_table), + }), };