/* * Smartreflex error config register is special as it contains * certain status bits which if written a 1 into means a clear * of those bits. So in order to make sure no accidental write of * 1 happens to those status bits, do a clear of them in the read * value. This mean this API doesn't rewrite values in these bits * if they are currently set, but does allow the caller to write * those bits.
*/ if (sr->ip_type == SR_TYPE_V1 && offset == ERRCONFIG_V1)
mask |= ERRCONFIG_STATUS_V1_MASK; elseif (sr->ip_type == SR_TYPE_V2 && offset == ERRCONFIG_V2)
mask |= ERRCONFIG_VPBOUNDINTST_V2;
switch (sr_info->ip_type) { case SR_TYPE_V1: /* Read the status bits */
status = sr_read_reg(sr_info, ERRCONFIG_V1);
/* Clear them by writing back */
sr_write_reg(sr_info, ERRCONFIG_V1, status); break; case SR_TYPE_V2: /* Read the status bits */
status = sr_read_reg(sr_info, IRQSTATUS);
/* Clear them by writing back */
sr_write_reg(sr_info, IRQSTATUS, status); break; default:
dev_err(&sr_info->pdev->dev, "UNKNOWN IP type %d\n",
sr_info->ip_type); return IRQ_NONE;
}
if (sr_class->notify)
sr_class->notify(sr_info, status);
/* Try interconnect target module fck first if it already exists */ if (IS_ERR(sr->fck)) return;
fclk_speed = clk_get_rate(sr->fck);
switch (fclk_speed) { case 12000000:
sr->clk_length = SRCLKLENGTH_12MHZ_SYSCLK; break; case 13000000:
sr->clk_length = SRCLKLENGTH_13MHZ_SYSCLK; break; case 19200000:
sr->clk_length = SRCLKLENGTH_19MHZ_SYSCLK; break; case 26000000:
sr->clk_length = SRCLKLENGTH_26MHZ_SYSCLK; break; case 38400000:
sr->clk_length = SRCLKLENGTH_38MHZ_SYSCLK; break; default:
dev_err(&sr->pdev->dev, "%s: Invalid fclk rate: %d\n",
__func__, fclk_speed); break;
}
}
staticvoid sr_start_vddautocomp(struct omap_sr *sr)
{ if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) {
dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
__func__); return;
}
if (!sr_class->enable(sr))
sr->autocomp_active = true;
}
staticvoid sr_stop_vddautocomp(struct omap_sr *sr)
{ if (!sr_class || !(sr_class->disable)) {
dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
__func__); return;
}
if (sr->autocomp_active) {
sr_class->disable(sr, 1);
sr->autocomp_active = false;
}
}
/* * This function handles the initializations which have to be done * only when both sr device and class driver regiter has * completed. This will be attempted to be called from both sr class * driver register and sr device intializtion API's. Only one call * will ultimately succeed. * * Currently this function registers interrupt handler for a particular SR * if smartreflex class driver is already registered and has * requested for interrupts and the SR interrupt line in present.
*/ staticint sr_late_init(struct omap_sr *sr_info)
{ int ret = 0;
if (sr_class->notify && sr_class->notify_flags && sr_info->irq) {
ret = devm_request_irq(&sr_info->pdev->dev, sr_info->irq,
sr_interrupt, IRQF_NO_AUTOEN,
sr_info->name, sr_info); if (ret) goto error;
}
return ret;
error:
list_del(&sr_info->node);
dev_err(&sr_info->pdev->dev, "%s: ERROR in registering interrupt handler. Smartreflex will not function as desired\n",
__func__);
return ret;
}
staticvoid sr_v1_disable(struct omap_sr *sr)
{ int timeout = 0; int errconf_val = ERRCONFIG_MCUACCUMINTST | ERRCONFIG_MCUVALIDINTST |
ERRCONFIG_MCUBOUNDINTST;
/* SRCONFIG - disable SR */
sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0);
/* Disable all other SR interrupts and clear the status as needed */ if (sr_read_reg(sr, ERRCONFIG_V1) & ERRCONFIG_VPBOUNDINTST_V1)
errconf_val |= ERRCONFIG_VPBOUNDINTST_V1;
sr_modify_reg(sr, ERRCONFIG_V1,
(ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN |
ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_VPBOUNDINTEN_V1),
errconf_val);
/* * Wait for SR to be disabled. * wait until ERRCONFIG.MCUDISACKINTST = 1. Typical latency is 1us.
*/
sr_test_cond_timeout((sr_read_reg(sr, ERRCONFIG_V1) &
ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT,
timeout);
if (timeout >= SR_DISABLE_TIMEOUT)
dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n",
__func__);
/* SRCONFIG - disable SR */
sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0);
/* * Disable all other SR interrupts and clear the status * write to status register ONLY on need basis - only if status * is set.
*/ if (sr_read_reg(sr, ERRCONFIG_V2) & ERRCONFIG_VPBOUNDINTST_V2)
sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2,
ERRCONFIG_VPBOUNDINTST_V2); else
sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2,
0x0);
sr_write_reg(sr, IRQENABLE_CLR, (IRQENABLE_MCUACCUMINT |
IRQENABLE_MCUVALIDINT |
IRQENABLE_MCUBOUNDSINT));
sr_write_reg(sr, IRQSTATUS, (IRQSTATUS_MCUACCUMINT |
IRQSTATUS_MCVALIDINT |
IRQSTATUS_MCBOUNDSINT));
/* * Wait for SR to be disabled. * wait until IRQSTATUS.MCUDISACKINTST = 1. Typical latency is 1us.
*/
sr_test_cond_timeout((sr_read_reg(sr, IRQSTATUS) &
IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT,
timeout);
if (timeout >= SR_DISABLE_TIMEOUT)
dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n",
__func__);
if (!sr->nvalue_table) {
dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n",
__func__); return NULL;
}
for (i = 0; i < sr->nvalue_count; i++) { if (sr->nvalue_table[i].efuse_offs == efuse_offs) return &sr->nvalue_table[i];
}
return NULL;
}
/* Public Functions */
/** * sr_configure_errgen() - Configures the SmartReflex to perform AVS using the * error generator module. * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * configure the error generator module inside the smartreflex module. * SR settings if using the ERROR module inside Smartreflex. * SR CLASS 3 by default uses only the ERROR module where as * SR CLASS 2 can choose between ERROR module and MINMAXAVG * module. Returns 0 on success and error value in case of failure.
*/ int sr_configure_errgen(struct omap_sr *sr)
{
u32 sr_config, sr_errconfig, errconfig_offs;
u32 vpboundint_en, vpboundint_st;
u32 senp_en = 0, senn_en = 0;
u8 senp_shift, senn_shift;
if (!sr) {
pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); return -EINVAL;
}
/* Enabling the interrupts if the ERROR module is used */
sr_modify_reg(sr, errconfig_offs, (vpboundint_en | vpboundint_st),
vpboundint_en);
return 0;
}
/** * sr_disable_errgen() - Disables SmartReflex AVS module's errgen component * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * disable the error generator module inside the smartreflex module. * * Returns 0 on success and error value in case of failure.
*/ int sr_disable_errgen(struct omap_sr *sr)
{
u32 errconfig_offs;
u32 vpboundint_en, vpboundint_st;
if (!sr) {
pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); return -EINVAL;
}
switch (sr->ip_type) { case SR_TYPE_V1:
errconfig_offs = ERRCONFIG_V1;
vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1;
vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; break; case SR_TYPE_V2:
errconfig_offs = ERRCONFIG_V2;
vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2;
vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; break; default:
dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n",
__func__); return -EINVAL;
}
/* Disable the Sensor and errorgen */
sr_modify_reg(sr, SRCONFIG, SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN, 0);
/* * Disable the interrupts of ERROR module * NOTE: modify is a read, modify,write - an implicit OCP barrier * which is required is present here - sequencing is critical * at this point (after errgen is disabled, vpboundint disable)
*/
sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0);
return 0;
}
/** * sr_configure_minmax() - Configures the SmartReflex to perform AVS using the * minmaxavg module. * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * configure the minmaxavg module inside the smartreflex module. * SR settings if using the ERROR module inside Smartreflex. * SR CLASS 3 by default uses only the ERROR module where as * SR CLASS 2 can choose between ERROR module and MINMAXAVG * module. Returns 0 on success and error value in case of failure.
*/ int sr_configure_minmax(struct omap_sr *sr)
{
u32 sr_config, sr_avgwt;
u32 senp_en = 0, senn_en = 0;
u8 senp_shift, senn_shift;
if (!sr) {
pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); return -EINVAL;
}
/* * Enabling the interrupts if MINMAXAVG module is used. * TODO: check if all the interrupts are mandatory
*/ switch (sr->ip_type) { case SR_TYPE_V1:
sr_modify_reg(sr, ERRCONFIG_V1,
(ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN |
ERRCONFIG_MCUBOUNDINTEN),
(ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUACCUMINTST |
ERRCONFIG_MCUVALIDINTEN | ERRCONFIG_MCUVALIDINTST |
ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_MCUBOUNDINTST)); break; case SR_TYPE_V2:
sr_write_reg(sr, IRQSTATUS,
IRQSTATUS_MCUACCUMINT | IRQSTATUS_MCVALIDINT |
IRQSTATUS_MCBOUNDSINT | IRQSTATUS_MCUDISABLEACKINT);
sr_write_reg(sr, IRQENABLE_SET,
IRQENABLE_MCUACCUMINT | IRQENABLE_MCUVALIDINT |
IRQENABLE_MCUBOUNDSINT | IRQENABLE_MCUDISABLEACKINT); break; default:
dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n",
__func__); return -EINVAL;
}
return 0;
}
/** * sr_enable() - Enables the smartreflex module. * @sr: pointer to which the SR module to be configured belongs to. * @volt: The voltage at which the Voltage domain associated with * the smartreflex module is operating at. * This is required only to program the correct Ntarget value. * * This API is to be called from the smartreflex class driver to * enable a smartreflex module. Returns 0 on success. Returns error * value if the voltage passed is wrong or if ntarget value is wrong.
*/ int sr_enable(struct omap_sr *sr, unsignedlong volt)
{ struct omap_volt_data *volt_data; struct omap_sr_nvalue_table *nvalue_row; int ret;
if (!sr) {
pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); return -EINVAL;
}
if (IS_ERR(volt_data)) {
dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table for nominal voltage %ld\n",
__func__, volt); return PTR_ERR(volt_data);
}
/* SRCONFIG - enable SR */
sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE);
out_enabled:
sr->enabled = 1;
return 0;
}
/** * sr_disable() - Disables the smartreflex module. * @sr: pointer to which the SR module to be configured belongs to. * * This API is to be called from the smartreflex class driver to * disable a smartreflex module.
*/ void sr_disable(struct omap_sr *sr)
{ if (!sr) {
pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); return;
}
/* Check if SR clocks are already disabled. If yes do nothing */ if (!sr->enabled) return;
/* * Disable SR if only it is indeed enabled. Else just * disable the clocks.
*/ if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) { switch (sr->ip_type) { case SR_TYPE_V1:
sr_v1_disable(sr); break; case SR_TYPE_V2:
sr_v2_disable(sr); break; default:
dev_err(&sr->pdev->dev, "UNKNOWN IP type %d\n",
sr->ip_type);
}
}
clk_disable(sr->fck);
sr->enabled = 0;
}
/** * sr_register_class() - API to register a smartreflex class parameters. * @class_data: The structure containing various sr class specific data. * * This API is to be called by the smartreflex class driver to register itself * with the smartreflex driver during init. Returns 0 on success else the * error value.
*/ int sr_register_class(struct omap_sr_class_data *class_data)
{ struct omap_sr *sr_info;
if (!class_data) {
pr_warn("%s:, Smartreflex class data passed is NULL\n",
__func__); return -EINVAL;
}
if (sr_class) {
pr_warn("%s: Smartreflex class driver already registered\n",
__func__); return -EBUSY;
}
sr_class = class_data;
/* * Call into late init to do initializations that require * both sr driver and sr class driver to be initiallized.
*/
list_for_each_entry(sr_info, &sr_list, node)
sr_late_init(sr_info);
return 0;
}
/** * omap_sr_enable() - API to enable SR clocks and to call into the * registered smartreflex class enable API. * @voltdm: VDD pointer to which the SR module to be configured belongs to. * * This API is to be called from the kernel in order to enable * a particular smartreflex module. This API will do the initial * configurations to turn on the smartreflex module and in turn call * into the registered smartreflex class enable API.
*/ void omap_sr_enable(struct voltagedomain *voltdm)
{ struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warn("%s: omap_sr struct for voltdm not found\n", __func__); return;
}
if (!sr->autocomp_active) return;
if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) {
dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
__func__); return;
}
sr_class->enable(sr);
}
/** * omap_sr_disable() - API to disable SR without resetting the voltage * processor voltage * @voltdm: VDD pointer to which the SR module to be configured belongs to. * * This API is to be called from the kernel in order to disable * a particular smartreflex module. This API will in turn call * into the registered smartreflex class disable API. This API will tell * the smartreflex class disable not to reset the VP voltage after * disabling smartreflex.
*/ void omap_sr_disable(struct voltagedomain *voltdm)
{ struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warn("%s: omap_sr struct for voltdm not found\n", __func__); return;
}
if (!sr->autocomp_active) return;
if (!sr_class || !(sr_class->disable)) {
dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
__func__); return;
}
sr_class->disable(sr, 0);
}
/** * omap_sr_disable_reset_volt() - API to disable SR and reset the * voltage processor voltage * @voltdm: VDD pointer to which the SR module to be configured belongs to. * * This API is to be called from the kernel in order to disable * a particular smartreflex module. This API will in turn call * into the registered smartreflex class disable API. This API will tell * the smartreflex class disable to reset the VP voltage after * disabling smartreflex.
*/ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
{ struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
pr_warn("%s: omap_sr struct for voltdm not found\n", __func__); return;
}
if (!sr->autocomp_active) return;
if (!sr_class || !(sr_class->disable)) {
dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
__func__); return;
}
/* control enable/disable only if there is a delta in value */ if (sr_info->autocomp_active != val) { if (!val)
sr_stop_vddautocomp(sr_info); else
sr_start_vddautocomp(sr_info);
}
staticint omap_sr_probe(struct platform_device *pdev)
{ struct omap_sr *sr_info; struct omap_sr_data *pdata = pdev->dev.platform_data; struct dentry *nvalue_dir; int i, ret = 0;
sr_info = devm_kzalloc(&pdev->dev, sizeof(struct omap_sr), GFP_KERNEL); if (!sr_info) return -ENOMEM;
sr_info->name = devm_kzalloc(&pdev->dev,
SMARTREFLEX_NAME_LEN, GFP_KERNEL); if (!sr_info->name) return -ENOMEM;
platform_set_drvdata(pdev, sr_info);
if (!pdata) {
dev_err(&pdev->dev, "%s: platform data missing\n", __func__); return -EINVAL;
}
sr_info->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sr_info->base)) return PTR_ERR(sr_info->base);
ret = platform_get_irq_optional(pdev, 0); if (ret < 0 && ret != -ENXIO) return dev_err_probe(&pdev->dev, ret, "failed to get IRQ resource\n"); if (ret > 0)
sr_info->irq = ret;
sr_info->fck = devm_clk_get(pdev->dev.parent, "fck"); if (IS_ERR(sr_info->fck)) return PTR_ERR(sr_info->fck);
clk_prepare(sr_info->fck);
/* * Call into late init to do initializations that require * both sr driver and sr class driver to be initiallized.
*/ if (sr_class) {
ret = sr_late_init(sr_info); if (ret) {
pr_warn("%s: Error in SR late init\n", __func__); goto err_list_del;
}
}
if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) {
dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n",
__func__, sr_info->name);
ret = -ENODATA; goto err_debugfs;
}
for (i = 0; i < sr_info->nvalue_count; i++) { char name[NVALUE_NAME_LEN + 1];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.