]> git.karo-electronics.de Git - linux-beck.git/blobdiff - drivers/crypto/s5p-sss.c
Merge tag 'locks-v3.16-2' of git://git.samba.org/jlayton/linux
[linux-beck.git] / drivers / crypto / s5p-sss.c
index be45762f390a274f2b916effd52b46fe12fd1619..4197ad9a711bd6c04e8a993f3fafe1a6e553dea2 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/scatterlist.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <linux/crypto.h>
 #include <linux/interrupt.h>
 
@@ -29,9 +30,6 @@
 #include <crypto/aes.h>
 #include <crypto/ctr.h>
 
-#include <plat/cpu.h>
-#include <mach/dma.h>
-
 #define _SBF(s, v)                      ((v) << (s))
 #define _BIT(b)                         _SBF(b, 1)
 
 #define SSS_REG_FCPKDMAO                0x005C
 
 /* AES registers */
-#define SSS_REG_AES_CONTROL             0x4000
+#define SSS_REG_AES_CONTROL            0x00
 #define SSS_AES_BYTESWAP_DI             _BIT(11)
 #define SSS_AES_BYTESWAP_DO             _BIT(10)
 #define SSS_AES_BYTESWAP_IV             _BIT(9)
 #define SSS_AES_CHAIN_MODE_CTR          _SBF(1, 0x02)
 #define SSS_AES_MODE_DECRYPT            _BIT(0)
 
-#define SSS_REG_AES_STATUS              0x4004
+#define SSS_REG_AES_STATUS             0x04
 #define SSS_AES_BUSY                    _BIT(2)
 #define SSS_AES_INPUT_READY             _BIT(1)
 #define SSS_AES_OUTPUT_READY            _BIT(0)
 
-#define SSS_REG_AES_IN_DATA(s)          (0x4010 + (s << 2))
-#define SSS_REG_AES_OUT_DATA(s)         (0x4020 + (s << 2))
-#define SSS_REG_AES_IV_DATA(s)          (0x4030 + (s << 2))
-#define SSS_REG_AES_CNT_DATA(s)         (0x4040 + (s << 2))
-#define SSS_REG_AES_KEY_DATA(s)         (0x4080 + (s << 2))
+#define SSS_REG_AES_IN_DATA(s)         (0x10 + (s << 2))
+#define SSS_REG_AES_OUT_DATA(s)                (0x20 + (s << 2))
+#define SSS_REG_AES_IV_DATA(s)         (0x30 + (s << 2))
+#define SSS_REG_AES_CNT_DATA(s)                (0x40 + (s << 2))
+#define SSS_REG_AES_KEY_DATA(s)                (0x80 + (s << 2))
 
 #define SSS_REG(dev, reg)               ((dev)->ioaddr + (SSS_REG_##reg))
 #define SSS_READ(dev, reg)              __raw_readl(SSS_REG(dev, reg))
 #define SSS_WRITE(dev, reg, val)        __raw_writel((val), SSS_REG(dev, reg))
 
+#define SSS_AES_REG(dev, reg)           ((dev)->aes_ioaddr + SSS_REG_##reg)
+#define SSS_AES_WRITE(dev, reg, val)    __raw_writel((val), \
+                                               SSS_AES_REG(dev, reg))
+
 /* HW engine modes */
 #define FLAGS_AES_DECRYPT               _BIT(0)
 #define FLAGS_AES_MODE_MASK             _SBF(1, 0x03)
 #define AES_KEY_LEN         16
 #define CRYPTO_QUEUE_LEN    1
 
+/**
+ * struct samsung_aes_variant - platform specific SSS driver data
+ * @has_hash_irq: true if SSS module uses hash interrupt, false otherwise
+ * @aes_offset: AES register offset from SSS module's base.
+ *
+ * Specifies platform specific configuration of SSS module.
+ * Note: A structure for driver specific platform data is used for future
+ * expansion of its usage.
+ */
+struct samsung_aes_variant {
+       bool                        has_hash_irq;
+       unsigned int                aes_offset;
+};
+
 struct s5p_aes_reqctx {
        unsigned long mode;
 };
@@ -161,6 +177,7 @@ struct s5p_aes_dev {
        struct device              *dev;
        struct clk                 *clk;
        void __iomem               *ioaddr;
+       void __iomem               *aes_ioaddr;
        int                         irq_hash;
        int                         irq_fc;
 
@@ -173,10 +190,48 @@ struct s5p_aes_dev {
        struct crypto_queue         queue;
        bool                        busy;
        spinlock_t                  lock;
+
+       struct samsung_aes_variant *variant;
 };
 
 static struct s5p_aes_dev *s5p_dev;
 
+static const struct samsung_aes_variant s5p_aes_data = {
+       .has_hash_irq   = true,
+       .aes_offset     = 0x4000,
+};
+
+static const struct samsung_aes_variant exynos_aes_data = {
+       .has_hash_irq   = false,
+       .aes_offset     = 0x200,
+};
+
+static const struct of_device_id s5p_sss_dt_match[] = {
+       {
+               .compatible = "samsung,s5pv210-secss",
+               .data = &s5p_aes_data,
+       },
+       {
+               .compatible = "samsung,exynos4210-secss",
+               .data = &exynos_aes_data,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, s5p_sss_dt_match);
+
+static inline struct samsung_aes_variant *find_s5p_sss_version
+                                  (struct platform_device *pdev)
+{
+       if (IS_ENABLED(CONFIG_OF) && (pdev->dev.of_node)) {
+               const struct of_device_id *match;
+               match = of_match_node(s5p_sss_dt_match,
+                                       pdev->dev.of_node);
+               return (struct samsung_aes_variant *)match->data;
+       }
+       return (struct samsung_aes_variant *)
+                       platform_get_device_id(pdev)->driver_data;
+}
+
 static void s5p_set_dma_indata(struct s5p_aes_dev *dev, struct scatterlist *sg)
 {
        SSS_WRITE(dev, FCBRDMAS, sg_dma_address(sg));
@@ -272,8 +327,12 @@ static void s5p_aes_tx(struct s5p_aes_dev *dev)
                }
 
                s5p_set_dma_outdata(dev, dev->sg_dst);
-       } else
+       } else {
                s5p_aes_complete(dev, err);
+
+               dev->busy = true;
+               tasklet_schedule(&dev->tasklet);
+       }
 }
 
 static void s5p_aes_rx(struct s5p_aes_dev *dev)
@@ -322,14 +381,15 @@ static void s5p_set_aes(struct s5p_aes_dev *dev,
 {
        void __iomem *keystart;
 
-       memcpy(dev->ioaddr + SSS_REG_AES_IV_DATA(0), iv, 0x10);
+       if (iv)
+               memcpy(dev->aes_ioaddr + SSS_REG_AES_IV_DATA(0), iv, 0x10);
 
        if (keylen == AES_KEYSIZE_256)
-               keystart = dev->ioaddr + SSS_REG_AES_KEY_DATA(0);
+               keystart = dev->aes_ioaddr + SSS_REG_AES_KEY_DATA(0);
        else if (keylen == AES_KEYSIZE_192)
-               keystart = dev->ioaddr + SSS_REG_AES_KEY_DATA(2);
+               keystart = dev->aes_ioaddr + SSS_REG_AES_KEY_DATA(2);
        else
-               keystart = dev->ioaddr + SSS_REG_AES_KEY_DATA(4);
+               keystart = dev->aes_ioaddr + SSS_REG_AES_KEY_DATA(4);
 
        memcpy(keystart, key, keylen);
 }
@@ -379,7 +439,7 @@ static void s5p_aes_crypt_start(struct s5p_aes_dev *dev, unsigned long mode)
        if (err)
                goto outdata_error;
 
-       SSS_WRITE(dev, AES_CONTROL, aes_control);
+       SSS_AES_WRITE(dev, AES_CONTROL, aes_control);
        s5p_set_aes(dev, dev->ctx->aes_key, req->info, dev->ctx->keylen);
 
        s5p_set_dma_indata(dev,  req->src);
@@ -410,10 +470,13 @@ static void s5p_tasklet_cb(unsigned long data)
        spin_lock_irqsave(&dev->lock, flags);
        backlog   = crypto_get_backlog(&dev->queue);
        async_req = crypto_dequeue_request(&dev->queue);
-       spin_unlock_irqrestore(&dev->lock, flags);
 
-       if (!async_req)
+       if (!async_req) {
+               dev->busy = false;
+               spin_unlock_irqrestore(&dev->lock, flags);
                return;
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
 
        if (backlog)
                backlog->complete(backlog, -EINPROGRESS);
@@ -432,14 +495,13 @@ static int s5p_aes_handle_req(struct s5p_aes_dev *dev,
        int err;
 
        spin_lock_irqsave(&dev->lock, flags);
+       err = ablkcipher_enqueue_request(&dev->queue, req);
        if (dev->busy) {
-               err = -EAGAIN;
                spin_unlock_irqrestore(&dev->lock, flags);
                goto exit;
        }
        dev->busy = true;
 
-       err = ablkcipher_enqueue_request(&dev->queue, req);
        spin_unlock_irqrestore(&dev->lock, flags);
 
        tasklet_schedule(&dev->tasklet);
@@ -564,6 +626,7 @@ static int s5p_aes_probe(struct platform_device *pdev)
        struct s5p_aes_dev *pdata;
        struct device      *dev = &pdev->dev;
        struct resource    *res;
+       struct samsung_aes_variant *variant;
 
        if (s5p_dev)
                return -EEXIST;
@@ -577,30 +640,25 @@ static int s5p_aes_probe(struct platform_device *pdev)
        if (IS_ERR(pdata->ioaddr))
                return PTR_ERR(pdata->ioaddr);
 
+       variant = find_s5p_sss_version(pdev);
+
        pdata->clk = devm_clk_get(dev, "secss");
        if (IS_ERR(pdata->clk)) {
                dev_err(dev, "failed to find secss clock source\n");
                return -ENOENT;
        }
 
-       clk_enable(pdata->clk);
+       err = clk_prepare_enable(pdata->clk);
+       if (err < 0) {
+               dev_err(dev, "Enabling SSS clk failed, err %d\n", err);
+               return err;
+       }
 
        spin_lock_init(&pdata->lock);
 
-       pdata->irq_hash = platform_get_irq_byname(pdev, "hash");
-       if (pdata->irq_hash < 0) {
-               err = pdata->irq_hash;
-               dev_warn(dev, "hash interrupt is not available.\n");
-               goto err_irq;
-       }
-       err = devm_request_irq(dev, pdata->irq_hash, s5p_aes_interrupt,
-                              IRQF_SHARED, pdev->name, pdev);
-       if (err < 0) {
-               dev_warn(dev, "hash interrupt is not available.\n");
-               goto err_irq;
-       }
+       pdata->aes_ioaddr = pdata->ioaddr + variant->aes_offset;
 
-       pdata->irq_fc = platform_get_irq_byname(pdev, "feed control");
+       pdata->irq_fc = platform_get_irq(pdev, 0);
        if (pdata->irq_fc < 0) {
                err = pdata->irq_fc;
                dev_warn(dev, "feed control interrupt is not available.\n");
@@ -613,6 +671,23 @@ static int s5p_aes_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
+       if (variant->has_hash_irq) {
+               pdata->irq_hash = platform_get_irq(pdev, 1);
+               if (pdata->irq_hash < 0) {
+                       err = pdata->irq_hash;
+                       dev_warn(dev, "hash interrupt is not available.\n");
+                       goto err_irq;
+               }
+               err = devm_request_irq(dev, pdata->irq_hash, s5p_aes_interrupt,
+                                      IRQF_SHARED, pdev->name, pdev);
+               if (err < 0) {
+                       dev_warn(dev, "hash interrupt is not available.\n");
+                       goto err_irq;
+               }
+       }
+
+       pdata->busy = false;
+       pdata->variant = variant;
        pdata->dev = dev;
        platform_set_drvdata(pdev, pdata);
        s5p_dev = pdata;
@@ -639,7 +714,7 @@ static int s5p_aes_probe(struct platform_device *pdev)
        tasklet_kill(&pdata->tasklet);
 
  err_irq:
-       clk_disable(pdata->clk);
+       clk_disable_unprepare(pdata->clk);
 
        s5p_dev = NULL;
 
@@ -659,7 +734,7 @@ static int s5p_aes_remove(struct platform_device *pdev)
 
        tasklet_kill(&pdata->tasklet);
 
-       clk_disable(pdata->clk);
+       clk_disable_unprepare(pdata->clk);
 
        s5p_dev = NULL;
 
@@ -672,6 +747,7 @@ static struct platform_driver s5p_aes_crypto = {
        .driver = {
                .owner  = THIS_MODULE,
                .name   = "s5p-secss",
+               .of_match_table = s5p_sss_dt_match,
        },
 };