ARM: OMAP3+: SmartReflex: micro-optimization for sanity check
[pandora-kernel.git] / arch / arm / mach-omap2 / smartreflex.c
index cf246b3..9460d3a 100644 (file)
 #define SR_DISABLE_TIMEOUT     200
 
 struct omap_sr {
+       struct list_head                node;
+       struct platform_device          *pdev;
+       struct omap_sr_nvalue_table     *nvalue_table;
+       struct voltagedomain            *voltdm;
+       struct dentry                   *dbg_dir;
+       unsigned int                    irq;
        int                             srid;
        int                             ip_type;
        int                             nvalue_count;
@@ -49,13 +55,7 @@ struct omap_sr {
        u32                             senp_avgweight;
        u32                             senp_mod;
        u32                             senn_mod;
-       unsigned int                    irq;
        void __iomem                    *base;
-       struct platform_device          *pdev;
-       struct list_head                node;
-       struct omap_sr_nvalue_table     *nvalue_table;
-       struct voltagedomain            *voltdm;
-       struct dentry                   *dbg_dir;
 };
 
 /* sr_list contains all the instances of smartreflex module */
@@ -74,10 +74,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
@@ -88,16 +84,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;
 
@@ -128,21 +123,28 @@ static struct omap_sr *_sr_lookup(struct voltagedomain *voltdm)
 
 static irqreturn_t sr_interrupt(int irq, void *data)
 {
-       struct omap_sr *sr_info = (struct omap_sr *)data;
+       struct omap_sr *sr_info = data;
        u32 status = 0;
 
-       if (sr_info->ip_type == SR_TYPE_V1) {
+       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);
-       } else if (sr_info->ip_type == SR_TYPE_V2) {
+               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)
@@ -166,6 +168,7 @@ static void sr_set_clk_length(struct omap_sr *sr)
                        __func__);
                return;
        }
+
        sys_clk_speed = clk_get_rate(sys_ck);
        clk_put(sys_ck);
 
@@ -267,7 +270,7 @@ static int sr_late_init(struct omap_sr *sr_info)
                        goto error;
                }
                ret = request_irq(sr_info->irq, sr_interrupt,
-                               0, name, (void *)sr_info);
+                               0, name, sr_info);
                if (ret)
                        goto error;
                disable_irq(sr_info->irq);
@@ -288,12 +291,15 @@ error:
                "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,
@@ -302,13 +308,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.
@@ -337,9 +343,17 @@ 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);
+       else
+               sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2,
+                               0x0);
        sr_write_reg(sr, IRQENABLE_CLR, (IRQENABLE_MCUACCUMINT |
                        IRQENABLE_MCUVALIDINT |
                        IRQENABLE_MCUBOUNDSINT));
@@ -398,8 +412,9 @@ static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs)
  */
 int sr_configure_errgen(struct voltagedomain *voltdm)
 {
-       u32 sr_config, sr_errconfig, errconfig_offs, vpboundint_en;
-       u32 vpboundint_st, senp_en = 0, senn_en = 0;
+       u32 sr_config, sr_errconfig, errconfig_offs;
+       u32 vpboundint_en, vpboundint_st;
+       u32 senp_en = 0, senn_en = 0;
        u8 senp_shift, senn_shift;
        struct omap_sr *sr = _sr_lookup(voltdm);
 
@@ -418,20 +433,23 @@ int sr_configure_errgen(struct voltagedomain *voltdm)
        sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) |
                SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN;
 
-       if (sr->ip_type == SR_TYPE_V1) {
+       switch (sr->ip_type) {
+       case SR_TYPE_V1:
                sr_config |= SRCONFIG_DELAYCTRL;
                senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT;
                senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT;
                errconfig_offs = ERRCONFIG_V1;
                vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1;
                vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1;
-       } else if (sr->ip_type == SR_TYPE_V2) {
+               break;
+       case SR_TYPE_V2:
                senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT;
                senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT;
                errconfig_offs = ERRCONFIG_V2;
                vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2;
                vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2;
-       } else {
+               break;
+       default:
                dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex"
                        "module without specifying the ip\n", __func__);
                return -EINVAL;
@@ -447,8 +465,55 @@ 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:    VDD 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;
+       u32 vpboundint_en, 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;
+       }
+
+       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 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;
 }
@@ -488,14 +553,17 @@ int sr_configure_minmax(struct voltagedomain *voltdm)
                SRCONFIG_SENENABLE |
                (sr->accum_data << SRCONFIG_ACCUMDATA_SHIFT);
 
-       if (sr->ip_type == SR_TYPE_V1) {
+       switch (sr->ip_type) {
+       case SR_TYPE_V1:
                sr_config |= SRCONFIG_DELAYCTRL;
                senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT;
                senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT;
-       } else if (sr->ip_type == SR_TYPE_V2) {
+               break;
+       case SR_TYPE_V2:
                senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT;
                senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT;
-       } else {
+               break;
+       default:
                dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex"
                        "module without specifying the ip\n", __func__);
                return -EINVAL;
@@ -511,20 +579,27 @@ int sr_configure_minmax(struct voltagedomain *voltdm)
         * Enabling the interrupts if MINMAXAVG module is used.
         * TODO: check if all the interrupts are mandatory
         */
-       if (sr->ip_type == SR_TYPE_V1) {
+       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));
-       } else if (sr->ip_type == SR_TYPE_V2) {
+               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;
@@ -543,9 +618,9 @@ int sr_configure_minmax(struct voltagedomain *voltdm)
  */
 int sr_enable(struct voltagedomain *voltdm, unsigned long volt)
 {
-       u32 nvalue_reciprocal;
        struct omap_volt_data *volt_data;
        struct omap_sr *sr = _sr_lookup(voltdm);
+       u32 nvalue_reciprocal;
        int ret;
 
        if (IS_ERR(sr)) {
@@ -617,10 +692,17 @@ void sr_disable(struct voltagedomain *voltdm)
         * disable the clocks.
         */
        if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) {
-               if (sr->ip_type == SR_TYPE_V1)
+               switch (sr->ip_type) {
+               case SR_TYPE_V1:
                        sr_v1_disable(sr);
-               else if (sr->ip_type == SR_TYPE_V2)
+                       break;
+               case SR_TYPE_V2:
                        sr_v2_disable(sr);
+                       break;
+               default:
+                       dev_err(&sr->pdev->dev, "UNKNOWN IP type %d\n",
+                               sr->ip_type);
+               }
        }
 
        pm_runtime_put_sync_suspend(&sr->pdev->dev);
@@ -779,10 +861,10 @@ void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data)
        sr_pmic_data = pmic_data;
 }
 
-/* PM Debug Fs enteries to enable disable smartreflex. */
+/* PM Debug FS entries to enable and disable smartreflex. */
 static int omap_sr_autocomp_show(void *data, u64 *val)
 {
-       struct omap_sr *sr_info = (struct omap_sr *) data;
+       struct omap_sr *sr_info = data;
 
        if (!sr_info) {
                pr_warning("%s: omap_sr struct not found\n", __func__);
@@ -796,7 +878,7 @@ static int omap_sr_autocomp_show(void *data, u64 *val)
 
 static int omap_sr_autocomp_store(void *data, u64 val)
 {
-       struct omap_sr *sr_info = (struct omap_sr *) data;
+       struct omap_sr *sr_info = data;
 
        if (!sr_info) {
                pr_warning("%s: omap_sr struct not found\n", __func__);
@@ -804,7 +886,7 @@ static int omap_sr_autocomp_store(void *data, u64 val)
        }
 
        /* Sanity check */
-       if (val && (val != 1)) {
+       if (val > 1) {
                pr_warning("%s: Invalid argument %lld\n", __func__, val);
                return -EINVAL;
        }
@@ -821,11 +903,11 @@ static int omap_sr_autocomp_store(void *data, u64 val)
 }
 
 DEFINE_SIMPLE_ATTRIBUTE(pm_sr_fops, omap_sr_autocomp_show,
-               omap_sr_autocomp_store, "%llu\n");
+                       omap_sr_autocomp_store, "%llu\n");
 
 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 *sr_info;
        struct omap_sr_data *pdata = pdev->dev.platform_data;
        struct resource *mem, *irq;
        struct dentry *nvalue_dir;
@@ -833,12 +915,15 @@ static int __init omap_sr_probe(struct platform_device *pdev)
        int i, ret = 0;
        char *name;
 
+       sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL);
        if (!sr_info) {
                dev_err(&pdev->dev, "%s: unable to allocate sr_info\n",
                        __func__);
                return -ENOMEM;
        }
 
+       platform_set_drvdata(pdev, sr_info);
+
        if (!pdata) {
                dev_err(&pdev->dev, "%s: platform data missing\n", __func__);
                ret = -EINVAL;
@@ -897,7 +982,7 @@ 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;
                }
        }
 
@@ -1011,8 +1096,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,
+       .remove         = __devexit_p(omap_sr_remove),
+       .shutdown       = __devexit_p(omap_sr_shutdown),
        .driver         = {
                .name   = "smartreflex",
        },
@@ -1042,12 +1151,12 @@ static int __init sr_init(void)
 
        return 0;
 }
+late_initcall(sr_init);
 
 static void __exit sr_exit(void)
 {
        platform_driver_unregister(&smartreflex_driver);
 }
-late_initcall(sr_init);
 module_exit(sr_exit);
 
 MODULE_DESCRIPTION("OMAP Smartreflex Driver");