diff options
Diffstat (limited to 'arch/arm/mach-omap2/smartreflex.c')
-rw-r--r-- | arch/arm/mach-omap2/smartreflex.c | 461 |
1 files changed, 383 insertions, 78 deletions
diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index f5a6bc1..c2d85c1 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -28,6 +28,7 @@ #include <plat/common.h> #include "pm.h" +#include "dvfs.h" #include "smartreflex.h" #define SMARTREFLEX_NAME_LEN 16 @@ -49,11 +50,14 @@ struct omap_sr { u32 senp_mod; u32 senn_mod; unsigned int irq; + bool irq_enabled; void __iomem *base; struct platform_device *pdev; struct list_head node; struct omap_sr_nvalue_table *nvalue_table; struct voltagedomain *voltdm; + /* Managed by class driver as needed */ + void *voltdm_cdata; struct dentry *dbg_dir; }; @@ -62,6 +66,7 @@ static LIST_HEAD(sr_list); static struct omap_sr_class_data *sr_class; static struct omap_sr_pmic_data *sr_pmic_data; +static struct dentry *sr_dbg_dir; static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value) { @@ -72,10 +77,6 @@ static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask, u32 value) { u32 reg_val; - u32 errconfig_offs = 0, errconfig_mask = 0; - - reg_val = __raw_readl(sr->base + offset); - reg_val &= ~mask; /* * Smartreflex error config register is special as it contains @@ -86,16 +87,15 @@ static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask, * if they are currently set, but does allow the caller to write * those bits. */ - if (sr->ip_type == SR_TYPE_V1) { - errconfig_offs = ERRCONFIG_V1; - errconfig_mask = ERRCONFIG_STATUS_V1_MASK; - } else if (sr->ip_type == SR_TYPE_V2) { - errconfig_offs = ERRCONFIG_V2; - errconfig_mask = ERRCONFIG_VPBOUNDINTST_V2; - } + if (sr->ip_type == SR_TYPE_V1 && offset == ERRCONFIG_V1) + mask |= ERRCONFIG_STATUS_V1_MASK; + else if (sr->ip_type == SR_TYPE_V2 && offset == ERRCONFIG_V2) + mask |= ERRCONFIG_VPBOUNDINTST_V2; + + reg_val = __raw_readl(sr->base + offset); + reg_val &= ~mask; - if (offset == errconfig_offs) - reg_val &= ~errconfig_mask; + value &= mask; reg_val |= value; @@ -124,27 +124,119 @@ static struct omap_sr *_sr_lookup(struct voltagedomain *voltdm) return ERR_PTR(-ENODATA); } +static inline u32 notifier_to_irqen_v1(u8 notify_flags) +{ + u32 val; + + val = (notify_flags & SR_NOTIFY_MCUACCUM) ? + ERRCONFIG_MCUACCUMINTEN : 0; + val |= (notify_flags & SR_NOTIFY_MCUVALID) ? + ERRCONFIG_MCUVALIDINTEN : 0; + val |= (notify_flags & SR_NOTIFY_MCUBOUND) ? + ERRCONFIG_MCUBOUNDINTEN : 0; + val |= (notify_flags & SR_NOTIFY_MCUDISACK) ? + ERRCONFIG_MCUDISACKINTEN : 0; + + return val; +} + +static inline u32 notifier_to_irqen_v2(u8 notify_flags) +{ + u32 val; + + val = (notify_flags & SR_NOTIFY_MCUACCUM) ? + IRQENABLE_MCUACCUMINT : 0; + val |= (notify_flags & SR_NOTIFY_MCUVALID) ? + IRQENABLE_MCUVALIDINT : 0; + val |= (notify_flags & SR_NOTIFY_MCUBOUND) ? + IRQENABLE_MCUBOUNDSINT : 0; + val |= (notify_flags & SR_NOTIFY_MCUDISACK) ? + IRQENABLE_MCUDISABLEACKINT : 0; + + return val; +} + +static inline u8 irqstat_to_notifier_v1(u32 status) +{ + u8 val; + + val = (status & ERRCONFIG_MCUACCUMINTST) ? + SR_NOTIFY_MCUACCUM : 0; + val |= (status & ERRCONFIG_MCUVALIDINTEN) ? + SR_NOTIFY_MCUVALID : 0; + val |= (status & ERRCONFIG_MCUBOUNDINTEN) ? + SR_NOTIFY_MCUBOUND : 0; + val |= (status & ERRCONFIG_MCUDISACKINTEN) ? + SR_NOTIFY_MCUDISACK : 0; + + return val; +} + +static inline u8 irqstat_to_notifier_v2(u32 status) +{ + u8 val; + + val = (status & IRQENABLE_MCUACCUMINT) ? + SR_NOTIFY_MCUACCUM : 0; + val |= (status & IRQENABLE_MCUVALIDINT) ? + SR_NOTIFY_MCUVALID : 0; + val |= (status & IRQENABLE_MCUBOUNDSINT) ? + SR_NOTIFY_MCUBOUND : 0; + val |= (status & IRQENABLE_MCUDISABLEACKINT) ? + SR_NOTIFY_MCUDISACK : 0; + + return val; +} + + static irqreturn_t sr_interrupt(int irq, void *data) { struct omap_sr *sr_info = (struct omap_sr *)data; u32 status = 0; + u32 value = 0; if (sr_info->ip_type == SR_TYPE_V1) { + /* Status bits are one bit before enable bits in v1 */ + value = notifier_to_irqen_v1(sr_class->notify_flags) >> 1; + /* Read the status bits */ status = sr_read_reg(sr_info, ERRCONFIG_V1); + status &= value; /* Clear them by writing back */ - sr_write_reg(sr_info, ERRCONFIG_V1, status); + sr_modify_reg(sr_info, ERRCONFIG_V1, value, status); + + value = irqstat_to_notifier_v1(status); } else if (sr_info->ip_type == SR_TYPE_V2) { + value = notifier_to_irqen_v2(sr_class->notify_flags); /* Read the status bits */ status = sr_read_reg(sr_info, IRQSTATUS); + status &= value; /* Clear them by writing back */ sr_write_reg(sr_info, IRQSTATUS, status); + value = irqstat_to_notifier_v2(status); } - if (sr_class->class_type == SR_CLASS2 && sr_class->notify) - sr_class->notify(sr_info->voltdm, status); + /* Attempt some resemblance of recovery! */ + if (!value) { + dev_err(&sr_info->pdev->dev, "%s: Spurious interrupt!" + "status = 0x%08x. Disabling to prevent spamming!!\n", + __func__, status); + disable_irq_nosync(sr_info->irq); + sr_info->irq_enabled = false; + } else { + /* If the caller reports inability to handle, disable as well */ + if (sr_class->notify && sr_class->notify(sr_info->voltdm, + sr_info->voltdm_cdata, value)) { + dev_err(&sr_info->pdev->dev, "%s: Callback cant handle!" + "status=0x%08x. Disabling to prevent spam!!\n", + __func__, status); + disable_irq_nosync(sr_info->irq); + sr_info->irq_enabled = false; + } + + } return IRQ_HANDLED; } @@ -214,6 +306,7 @@ static void sr_set_regfields(struct omap_sr *sr) static void sr_start_vddautocomp(struct omap_sr *sr) { + int r; if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n", @@ -221,8 +314,23 @@ static void sr_start_vddautocomp(struct omap_sr *sr) return; } - if (!sr_class->enable(sr->voltdm)) + /* pause dvfs from interfereing with our operations */ + mutex_lock(&omap_dvfs_lock); + + if (sr_class->init && + sr_class->init(sr->voltdm, &sr->voltdm_cdata, + sr_class->class_priv_data)) { + dev_err(&sr->pdev->dev, + "%s: SRClass initialization failed\n", __func__); + mutex_unlock(&omap_dvfs_lock); + return; + } + + r = sr_class->enable(sr->voltdm, sr->voltdm_cdata, + omap_voltage_get_curr_vdata(sr->voltdm)); + if (!r) sr->autocomp_active = true; + mutex_unlock(&omap_dvfs_lock); } static void sr_stop_vddautocomp(struct omap_sr *sr) @@ -235,8 +343,19 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) } if (sr->autocomp_active) { - sr_class->disable(sr->voltdm, 1); + /* Pause dvfs from interfereing with our operations */ + mutex_lock(&omap_dvfs_lock); + sr_class->disable(sr->voltdm, sr->voltdm_cdata, + omap_voltage_get_curr_vdata(sr->voltdm), 1); + if (sr_class->deinit && + sr_class->deinit(sr->voltdm, &sr->voltdm_cdata, + sr_class->class_priv_data)) { + dev_err(&sr->pdev->dev, + "%s: SR[%d]Class deinitialization failed\n", + __func__, sr->srid); + } sr->autocomp_active = false; + mutex_unlock(&omap_dvfs_lock); } } @@ -258,9 +377,7 @@ static int sr_late_init(struct omap_sr *sr_info) struct resource *mem; int ret = 0; - if (sr_class->class_type == SR_CLASS2 && - sr_class->notify_flags && sr_info->irq) { - + if (sr_class->notify && sr_class->notify_flags && sr_info->irq) { name = kasprintf(GFP_KERNEL, "sr_%s", sr_info->voltdm->name); if (name == NULL) { ret = -ENOMEM; @@ -270,6 +387,7 @@ static int sr_late_init(struct omap_sr *sr_info) 0, name, (void *)sr_info); if (ret) goto error; + disable_irq(sr_info->irq); } if (pdata && pdata->enable_on_init) @@ -278,21 +396,23 @@ static int sr_late_init(struct omap_sr *sr_info) return ret; error: - iounmap(sr_info->base); - mem = platform_get_resource(sr_info->pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); - 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__); - kfree(name); - kfree(sr_info); - return ret; + iounmap(sr_info->base); + mem = platform_get_resource(sr_info->pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + 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__); + kfree(name); + kfree(sr_info); + return ret; } static void sr_v1_disable(struct omap_sr *sr) { int timeout = 0; + int errconf_val = ERRCONFIG_MCUACCUMINTST | ERRCONFIG_MCUVALIDINTST | + ERRCONFIG_MCUBOUNDINTST; /* Enable MCUDisableAcknowledge interrupt */ sr_modify_reg(sr, ERRCONFIG_V1, @@ -301,13 +421,13 @@ static void sr_v1_disable(struct omap_sr *sr) /* SRCONFIG - disable SR */ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0); - /* Disable all other SR interrupts and clear the status */ + /* 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), - (ERRCONFIG_MCUACCUMINTST | ERRCONFIG_MCUVALIDINTST | - ERRCONFIG_MCUBOUNDINTST | - ERRCONFIG_VPBOUNDINTST_V1)); + errconf_val); /* * Wait for SR to be disabled. @@ -336,15 +456,23 @@ static void sr_v2_disable(struct omap_sr *sr) /* SRCONFIG - disable SR */ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0); - /* Disable all other SR interrupts and clear the status */ - sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, + /* + * 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); - sr_write_reg(sr, IRQENABLE_CLR, (IRQENABLE_MCUACCUMINT | - IRQENABLE_MCUVALIDINT | - IRQENABLE_MCUBOUNDSINT)); + else + sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, + 0x0); sr_write_reg(sr, IRQSTATUS, (IRQSTATUS_MCUACCUMINT | IRQSTATUS_MCVALIDINT | IRQSTATUS_MCBOUNDSINT)); + sr_write_reg(sr, IRQENABLE_CLR, (IRQENABLE_MCUACCUMINT | + IRQENABLE_MCUVALIDINT | + IRQENABLE_MCUBOUNDSINT)); /* * Wait for SR to be disabled. @@ -359,8 +487,8 @@ static void sr_v2_disable(struct omap_sr *sr) __func__); /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ - sr_write_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUDISABLEACKINT); sr_write_reg(sr, IRQSTATUS, IRQSTATUS_MCUDISABLEACKINT); + sr_write_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUDISABLEACKINT); } static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs) @@ -384,6 +512,28 @@ static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs) /* Public Functions */ /** + * is_sr_enabled() - is Smart reflex enabled for this domain? + * @voltdm: voltage domain to check + * + * Returns 0 if SR is enabled for this domain, else returns err + */ +bool is_sr_enabled(struct voltagedomain *voltdm) +{ + struct omap_sr *sr; + if (IS_ERR_OR_NULL(voltdm)) { + pr_warning("%s: invalid param voltdm\n", __func__); + return false; + } + sr = _sr_lookup(voltdm); + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for sr_%s not found\n", + __func__, voltdm->name); + return false; + } + return sr->autocomp_active; +} + +/** * sr_configure_errgen() - Configures the smrtreflex to perform AVS using the * error generator module. * @voltdm: VDD pointer to which the SR module to be configured belongs to. @@ -446,8 +596,52 @@ int sr_configure_errgen(struct voltagedomain *voltdm) sr_errconfig); /* Enabling the interrupts if the ERROR module is used */ - sr_modify_reg(sr, errconfig_offs, - vpboundint_en, (vpboundint_en | vpboundint_st)); + sr_modify_reg(sr, errconfig_offs, (vpboundint_en | vpboundint_st), + vpboundint_en); + + return 0; +} + +/** + * sr_disable_errgen() - Disables SmartReflex AVS module's errgen component + * @voltdm: voltagedomain pointer to which the SR module to be configured belongs to. + * + * 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 voltagedomain *voltdm) +{ + u32 errconfig_offs, vpboundint_en; + u32 vpboundint_st; + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for sr_%s not found\n", + __func__, voltdm->name); + return -EINVAL; + } + + if (sr->ip_type == SR_TYPE_V1) { + errconfig_offs = ERRCONFIG_V1; + vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1; + vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; + } else if (sr->ip_type == SR_TYPE_V2) { + errconfig_offs = ERRCONFIG_V2; + vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2; + vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; + } else { + dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" + "module without specifying the ip\n", __func__); + return -EINVAL; + } + + /* Disable the interrupts of ERROR module */ + sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0); + + /* Disable the Sensor and errorgen */ + sr_modify_reg(sr, SRCONFIG, SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN, 0); return 0; } @@ -532,7 +726,7 @@ int sr_configure_minmax(struct voltagedomain *voltdm) /** * sr_enable() - Enables the smartreflex module. * @voltdm: VDD pointer to which the SR module to be configured belongs to. - * @volt: The voltage at which the Voltage domain associated with + * @volt_data: 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. * @@ -540,10 +734,9 @@ int sr_configure_minmax(struct voltagedomain *voltdm) * 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 voltagedomain *voltdm, unsigned long volt) +int sr_enable(struct voltagedomain *voltdm, struct omap_volt_data *volt_data) { u32 nvalue_reciprocal; - struct omap_volt_data *volt_data; struct omap_sr *sr = _sr_lookup(voltdm); int ret; @@ -553,19 +746,16 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) return -EINVAL; } - volt_data = omap_voltage_get_voltdata(sr->voltdm, volt); - - if (IS_ERR(volt_data)) { - dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table" - "for nominal voltage %ld\n", __func__, volt); - return -ENODATA; + if (IS_ERR_OR_NULL(volt_data)) { + dev_warn(&sr->pdev->dev, "%s: bad voltage data\n", __func__); + return -EINVAL; } nvalue_reciprocal = sr_retrieve_nvalue(sr, volt_data->sr_efuse_offs); if (!nvalue_reciprocal) { dev_warn(&sr->pdev->dev, "%s: NVALUE = 0 at voltage %ld\n", - __func__, volt); + __func__, omap_get_operation_voltage(volt_data)); return -ENODATA; } @@ -579,7 +769,7 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) return 0; /* Configure SR */ - ret = sr_class->configure(voltdm); + ret = sr_class->configure(voltdm, sr->voltdm_cdata); if (ret) return ret; @@ -622,7 +812,79 @@ void sr_disable(struct voltagedomain *voltdm) sr_v2_disable(sr); } - pm_runtime_put_sync(&sr->pdev->dev); + pm_runtime_put_sync_suspend(&sr->pdev->dev); +} + +/** + * sr_notifier_control() - control the notifier mechanism + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @enable: true to enable notifiers and false to disable the same + * + * SR modules allow an MCU interrupt mechanism that vary based on the IP + * revision, we allow the system to generate interrupt if the class driver + * has capability to handle the same. it is upto the class driver to ensure + * the proper sequencing and handling for a clean implementation. returns + * 0 if all goes fine, else returns failure results + */ +int sr_notifier_control(struct voltagedomain *voltdm, bool enable) +{ + struct omap_sr *sr = _sr_lookup(voltdm); + u32 value = 0; + + if (!sr) { + pr_warning("%s: sr corresponding to domain not found\n", + __func__); + return -EINVAL; + } + if (!sr->autocomp_active) + return -EINVAL; + + /* If I could never register an ISR, why bother?? */ + if (!(sr_class && sr_class->notify && sr_class->notify_flags && + sr->irq)) { + dev_warn(&sr->pdev->dev, + "%s: unable to setup IRQ without handling mechanism\n", + __func__); + return -EINVAL; + } + + + switch (sr->ip_type) { + case SR_TYPE_V1: + value = notifier_to_irqen_v1(sr_class->notify_flags); + break; + case SR_TYPE_V2: + value = notifier_to_irqen_v2(sr_class->notify_flags); + break; + default: + dev_warn(&sr->pdev->dev, "%s: unknown type of sr??\n", + __func__); + return -EINVAL; + } + + if (!enable) + sr_write_reg(sr, IRQSTATUS, value); + + switch (sr->ip_type) { + case SR_TYPE_V1: + sr_modify_reg(sr, ERRCONFIG_V1, value, + (enable) ? value : 0); + break; + case SR_TYPE_V2: + sr_write_reg(sr, (enable) ? IRQENABLE_SET : IRQENABLE_CLR, + value); + break; + } + + if (enable != sr->irq_enabled) { + if (enable) + enable_irq(sr->irq); + else + disable_irq(sr->irq); + sr->irq_enabled = enable; + } + + return 0; } /** @@ -665,13 +927,15 @@ int sr_register_class(struct omap_sr_class_data *class_data) * 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. + * @volt_data: Voltage data to go 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) +void omap_sr_enable(struct voltagedomain *voltdm, + struct omap_volt_data *volt_data) { struct omap_sr *sr = _sr_lookup(voltdm); @@ -690,7 +954,8 @@ void omap_sr_enable(struct voltagedomain *voltdm) return; } - sr_class->enable(voltdm); + sr_class->enable(voltdm, sr->voltdm_cdata, + omap_voltage_get_curr_vdata(voltdm)); } /** @@ -723,7 +988,8 @@ void omap_sr_disable(struct voltagedomain *voltdm) return; } - sr_class->disable(voltdm, 0); + sr_class->disable(voltdm, sr->voltdm_cdata, + omap_voltage_get_curr_vdata(voltdm), 0); } /** @@ -736,27 +1002,30 @@ void omap_sr_disable(struct voltagedomain *voltdm) * into the registered smartreflex class disable API. This API will tell * the smartreflex class disable to reset the VP voltage after * disabling smartreflex. + * + * Returns result of transition request. */ -void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) +int omap_sr_disable_reset_volt(struct voltagedomain *voltdm) { struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { pr_warning("%s: omap_sr struct for sr_%s not found\n", __func__, voltdm->name); - return; + return -ENODEV; } if (!sr->autocomp_active) - return; + return 0; if (!sr_class || !(sr_class->disable)) { dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" "registered\n", __func__); - return; + return -ENODEV; } - sr_class->disable(voltdm, 1); + return sr_class->disable(voltdm, sr->voltdm_cdata, + omap_voltage_get_curr_vdata(voltdm), 1); } /** @@ -808,10 +1077,13 @@ static int omap_sr_autocomp_store(void *data, u64 val) return -EINVAL; } - if (!val) - sr_stop_vddautocomp(sr_info); - else - sr_start_vddautocomp(sr_info); + /* 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); + } return 0; } @@ -824,9 +1096,10 @@ static int __init omap_sr_probe(struct platform_device *pdev) struct omap_sr *sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); struct omap_sr_data *pdata = pdev->dev.platform_data; struct resource *mem, *irq; - struct dentry *vdd_dbg_dir, *nvalue_dir; + struct dentry *nvalue_dir; struct omap_volt_data *volt_data; int i, ret = 0; + char *name; if (!sr_info) { dev_err(&pdev->dev, "%s: unable to allocate sr_info\n", @@ -858,6 +1131,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); pm_runtime_enable(&pdev->dev); + pm_runtime_irq_safe(&pdev->dev); sr_info->pdev = pdev; sr_info->srid = pdev->id; @@ -891,23 +1165,30 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = sr_late_init(sr_info); if (ret) { pr_warning("%s: Error in SR late init\n", __func__); - return ret; + goto err_iounmap; } } dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__); + if (!sr_dbg_dir) { + sr_dbg_dir = debugfs_create_dir("smartreflex", NULL); + if (!sr_dbg_dir) { + ret = PTR_ERR(sr_dbg_dir); + pr_err("%s:sr debugfs dir creation failed(%d)\n", + __func__, ret); + goto err_iounmap; + } + } - /* - * If the voltage domain debugfs directory is not created, do - * not try to create rest of the debugfs entries. - */ - vdd_dbg_dir = omap_voltage_get_dbgdir(sr_info->voltdm); - if (!vdd_dbg_dir) { - ret = -EINVAL; + name = kasprintf(GFP_KERNEL, "sr_%s", sr_info->voltdm->name); + if (!name) { + dev_err(&pdev->dev, "%s: Unable to alloc debugfs name\n", + __func__); + ret = -ENOMEM; goto err_iounmap; } - - sr_info->dbg_dir = debugfs_create_dir("smartreflex", vdd_dbg_dir); + sr_info->dbg_dir = debugfs_create_dir(name, sr_dbg_dir); + kfree(name); if (IS_ERR(sr_info->dbg_dir)) { dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n", __func__); @@ -998,8 +1279,32 @@ static int __devexit omap_sr_remove(struct platform_device *pdev) return 0; } +static void __devexit omap_sr_shutdown(struct platform_device *pdev) +{ + struct omap_sr_data *pdata = pdev->dev.platform_data; + struct omap_sr *sr_info; + + if (!pdata) { + dev_err(&pdev->dev, "%s: platform data missing\n", __func__); + return; + } + + sr_info = _sr_lookup(pdata->voltdm); + if (IS_ERR(sr_info)) { + dev_warn(&pdev->dev, "%s: omap_sr struct not found\n", + __func__); + return; + } + + if (sr_info->autocomp_active) + sr_stop_vddautocomp(sr_info); + + return; +} + static struct platform_driver smartreflex_driver = { .remove = omap_sr_remove, + .shutdown = omap_sr_shutdown, .driver = { .name = "smartreflex", }, |