summaryrefslogtreecommitdiffstats
path: root/drivers/crypto/s5p-sss.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/crypto/s5p-sss.c')
-rw-r--r--drivers/crypto/s5p-sss.c148
1 files changed, 112 insertions, 36 deletions
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index be45762f390..4197ad9a711 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -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)
@@ -105,7 +103,7 @@
#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)
@@ -121,21 +119,25 @@
#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)
@@ -145,6 +147,20 @@
#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,
},
};