]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/mtd/devices/elm.c
Merge remote-tracking branch 'nfsd/nfsd-next'
[karo-tx-linux.git] / drivers / mtd / devices / elm.c
index 4fab47a4a139bc3d86a085af0b37498ce85e0e77..d1dd6a33a0500831f78012f9be91afa0161e4574 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/sched.h>
 #include <linux/pm_runtime.h>
 #include <linux/platform_data/elm.h>
 
+#define ELM_SYSCONFIG                  0x010
 #define ELM_IRQSTATUS                  0x018
 #define ELM_IRQENABLE                  0x01c
 #define ELM_LOCATION_CONFIG            0x020
 #define ELM_PAGE_CTRL                  0x080
 #define ELM_SYNDROME_FRAGMENT_0                0x400
+#define ELM_SYNDROME_FRAGMENT_1                0x404
+#define ELM_SYNDROME_FRAGMENT_2                0x408
+#define ELM_SYNDROME_FRAGMENT_3                0x40c
+#define ELM_SYNDROME_FRAGMENT_4                0x410
+#define ELM_SYNDROME_FRAGMENT_5                0x414
 #define ELM_SYNDROME_FRAGMENT_6                0x418
 #define ELM_LOCATION_STATUS            0x800
 #define ELM_ERROR_LOCATION_0           0x880
 #define SYNDROME_FRAGMENT_REG_SIZE     0x40
 #define ERROR_LOCATION_SIZE            0x100
 
+struct elm_registers {
+       u32 elm_irqenable;
+       u32 elm_sysconfig;
+       u32 elm_location_config;
+       u32 elm_page_ctrl;
+       u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX];
+       u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX];
+       u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX];
+       u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX];
+       u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX];
+       u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX];
+       u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX];
+};
+
 struct elm_info {
        struct device *dev;
        void __iomem *elm_base;
        struct completion elm_completion;
        struct list_head list;
        enum bch_ecc bch_type;
+       struct elm_registers elm_regs;
 };
 
 static LIST_HEAD(elm_devices);
@@ -346,11 +368,6 @@ static int elm_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "no memory resource defined\n");
-               return -ENODEV;
-       }
-
        info->elm_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(info->elm_base))
                return PTR_ERR(info->elm_base);
@@ -384,6 +401,100 @@ static int elm_remove(struct platform_device *pdev)
        return 0;
 }
 
+/**
+ * elm_context_save
+ * saves ELM configurations to preserve them across Hardware powered-down
+ */
+static int elm_context_save(struct elm_info *info)
+{
+       struct elm_registers *regs = &info->elm_regs;
+       enum bch_ecc bch_type = info->bch_type;
+       u32 offset = 0, i;
+
+       regs->elm_irqenable       = elm_read_reg(info, ELM_IRQENABLE);
+       regs->elm_sysconfig       = elm_read_reg(info, ELM_SYSCONFIG);
+       regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG);
+       regs->elm_page_ctrl       = elm_read_reg(info, ELM_PAGE_CTRL);
+       for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+               offset = i * SYNDROME_FRAGMENT_REG_SIZE;
+               switch (bch_type) {
+               case BCH8_ECC:
+                       regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
+                                       ELM_SYNDROME_FRAGMENT_3 + offset);
+                       regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,
+                                       ELM_SYNDROME_FRAGMENT_2 + offset);
+               case BCH4_ECC:
+                       regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,
+                                       ELM_SYNDROME_FRAGMENT_1 + offset);
+                       regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,
+                                       ELM_SYNDROME_FRAGMENT_0 + offset);
+               default:
+                       return -EINVAL;
+               }
+               /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
+                * to be saved for all BCH schemes*/
+               regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
+                                       ELM_SYNDROME_FRAGMENT_6 + offset);
+       }
+       return 0;
+}
+
+/**
+ * elm_context_restore
+ * writes configurations saved duing power-down back into ELM registers
+ */
+static int elm_context_restore(struct elm_info *info)
+{
+       struct elm_registers *regs = &info->elm_regs;
+       enum bch_ecc bch_type = info->bch_type;
+       u32 offset = 0, i;
+
+       elm_write_reg(info, ELM_IRQENABLE,       regs->elm_irqenable);
+       elm_write_reg(info, ELM_SYSCONFIG,       regs->elm_sysconfig);
+       elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config);
+       elm_write_reg(info, ELM_PAGE_CTRL,       regs->elm_page_ctrl);
+       for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+               offset = i * SYNDROME_FRAGMENT_REG_SIZE;
+               switch (bch_type) {
+               case BCH8_ECC:
+                       elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
+                                       regs->elm_syndrome_fragment_3[i]);
+                       elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,
+                                       regs->elm_syndrome_fragment_2[i]);
+               case BCH4_ECC:
+                       elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,
+                                       regs->elm_syndrome_fragment_1[i]);
+                       elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,
+                                       regs->elm_syndrome_fragment_0[i]);
+               default:
+                       return -EINVAL;
+               }
+               /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
+               elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
+                                       regs->elm_syndrome_fragment_6[i] &
+                                                        ELM_SYNDROME_VALID);
+       }
+       return 0;
+}
+
+static int elm_suspend(struct device *dev)
+{
+       struct elm_info *info = dev_get_drvdata(dev);
+       elm_context_save(info);
+       pm_runtime_put_sync(dev);
+       return 0;
+}
+
+static int elm_resume(struct device *dev)
+{
+       struct elm_info *info = dev_get_drvdata(dev);
+       pm_runtime_get_sync(dev);
+       elm_context_restore(info);
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
+
 #ifdef CONFIG_OF
 static const struct of_device_id elm_of_match[] = {
        { .compatible = "ti,am3352-elm" },
@@ -397,6 +508,7 @@ static struct platform_driver elm_driver = {
                .name   = "elm",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(elm_of_match),
+               .pm     = &elm_pm_ops,
        },
        .probe  = elm_probe,
        .remove = elm_remove,