[RFC 9/9] crypto: Add Samsung crypto engine driver

Kyungmin Park kmpark at infradead.org
Tue Aug 3 00:44:35 EDT 2010


Hi,

Any update on this?

Thank you,
Kyungmin Park

On Sat, Jun 12, 2010 at 4:49 AM, Maurus Cuelenaere
<mcuelenaere at gmail.com> wrote:
> This patch adds support for the Samsung crypto engine driver available in the
> S3C64XX and S5PC100 SoCs. Currently this supports AES and (T)DES with ECB and
> CBC block ciphers (also CTR for AES). Support for (HMAC)-SHA1 acceleration is
> also available in this engine, but isn't used in the driver yet.
>
> Support for DMA has been added in the code but is currently disabled due to
> issues with data transfers.
>
> Signed-off-by: Maurus Cuelenaere <mcuelenaere at gmail.com>
> ---
>  drivers/crypto/Kconfig   |   11 +
>  drivers/crypto/Makefile  |    1 +
>  drivers/crypto/s3c-sss.c | 1320 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1332 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/crypto/s3c-sss.c
>
> diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
> index b08403d..597a151 100644
> --- a/drivers/crypto/Kconfig
> +++ b/drivers/crypto/Kconfig
> @@ -222,4 +222,15 @@ config CRYPTO_DEV_PPC4XX
>        help
>          This option allows you to have support for AMCC crypto acceleration.
>
> +config CRYPTO_DEV_SSS
> +       tristate "Samsung Security Sub-Systems"
> +       depends on SAMSUNG_DEV_SSS
> +       select CRYPTO_ALGAPI
> +       select CRYPTO_BLKCIPHER
> +       select CRYPTO_DES
> +       select CRYPTO_HASH
> +       help
> +           This driver utilizes the cryptographic engine in Samsung S3C64XX
> +           and S5PC100 SoCs.
> +
>  endif # CRYPTO_HW
> diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
> index 6ffcb3f..ef14b4d 100644
> --- a/drivers/crypto/Makefile
> +++ b/drivers/crypto/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
>  obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
>  obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
>  obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
> +obj-$(CONFIG_CRYPTO_DEV_SSS) += s3c-sss.o
> diff --git a/drivers/crypto/s3c-sss.c b/drivers/crypto/s3c-sss.c
> new file mode 100644
> index 0000000..9fd5288
> --- /dev/null
> +++ b/drivers/crypto/s3c-sss.c
> @@ -0,0 +1,1320 @@
> +/*
> + * linux/drivers/crypto/s3c-sss.c
> + *
> + * Copyright (C) 2010 Maurus Cuelenaere
> + *
> + * Support for S3C64XX Security Sub-Systems
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +/*#define DEBUG*/
> +
> +#include <linux/clk.h>
> +#include <linux/crypto.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/timer.h>
> +
> +#include <crypto/internal/hash.h>
> +#include <crypto/algapi.h>
> +#include <crypto/aes.h>
> +#include <crypto/ctr.h>
> +#include <crypto/des.h>
> +#include <crypto/sha.h>
> +#include <crypto/scatterwalk.h>
> +
> +#include <mach/dma.h>
> +
> +#include <plat/regs-sss.h>
> +
> +#define SSS_CRA_PRIORITY       300
> +#define SSS_MAX_KEY_SIZE       AES_MAX_KEY_SIZE
> +#define SSS_FIFO_SIZE          0x40U
> +#define SSS_TIMEOUT            (3*HZ)
> +
> +/**
> + * struct s3c_sss - driver state.
> + * @dev: pointer to the device struct
> + * @clock: clock associated with peripheral
> + * @irq: irq associated with peripheral
> + * @regs: pointer to mapped registers
> + * @regs_phys: pointer to physical address of registers
> + * @regs_res: pointer to struct resource representing registers
> + * @cur_req: pointer to pending request (NULL indicates no current request)
> + * @dma_client: struct used for passing to DMA core
> + * @lock: lock used for synchronizing queue accesses
> + * @tasklet: tasklet doing the main work
> + * @timer: timer used for timing out faulty requests
> + * @queue: queue containing requests
> + */
> +struct s3c_sss {
> +       struct device                   *dev;
> +
> +       struct clk                      *clock;
> +       int                             irq;
> +       void __iomem                    *regs;
> +       void __iomem                    *regs_phys;
> +       struct resource                 *regs_res;
> +
> +       struct ablkcipher_request       *cur_req;
> +       struct s3c2410_dma_client       dma_client;
> +       spinlock_t                      lock;
> +       struct tasklet_struct           tasklet;
> +       struct timer_list               timer;
> +       struct crypto_queue             queue;
> +};
> +
> +/**
> + * struct sss_context - cipher/hash key state
> + * @key: storage for the key
> + * @key_len: length of the key
> + * @dev: pointer to struct containing the driver state
> + */
> +struct sss_context {
> +       u8                              key[SSS_MAX_KEY_SIZE];
> +       unsigned int                    key_len;
> +
> +       struct s3c_sss                  *dev;
> +};
> +
> +/**
> + * struct sss_fifo_channel - FIFO handling state
> + *
> + * @cur_sg: scatterlist used in current transfer
> + * @offset: offset within current scatterlist
> + * @dir: FIFO direction
> + * @sg: pointer to scatter-gather lists
> + * @sg_count: amount of scatter-gather lists
> + * @req_size: size of current request
> + * @bytes_done: amount of data transferred
> + * @dev: pointer to struct containing the driver state
> + */
> +struct sss_fifo_channel {
> +       /* DMA */
> +       struct scatterlist              *cur_sg;
> +       int                             offset;
> +       int                             sg_count;
> +
> +       /* generic */
> +       enum {
> +               FIFO_RX,
> +               FIFO_TX
> +       }                               dir;
> +       struct scatterlist              *sg;
> +       size_t                          req_size;
> +       size_t                          bytes_done;
> +       struct s3c_sss                  *dev;
> +};
> +
> +/**
> + * struct sss_req_context - driver-specific data associated with a request
> + * @algorithm: algorithm used in request
> + * @blk_cipher: block cipher used in request
> + * @direction: whether to encrypt or decrypt
> + * @rx: RX FIFO channel, see struct sss_fifo_channel
> + * @tx: TX FIFO channel, see struct sss_fifo_channel
> + * @setup_done: whether hardware has been set up
> + * @err: indicates any occured error during request
> + */
> +struct sss_req_context {
> +       enum sss_algorithm {
> +               ALGO_AES,
> +               ALGO_DES,
> +               ALGO_TDES,
> +               ALGO_SHA1,
> +               ALGO_HMAC_SHA1,
> +       }                               algorithm;
> +       enum sss_block_cipher {
> +               CIPH_ECB,
> +               CIPH_CBC,
> +               CIPH_CTR,
> +       }                               blk_cipher;
> +       enum sss_direction {
> +               ENCRYPT,
> +               DECRYPT,
> +       }                               direction;
> +
> +       struct sss_fifo_channel         rx;
> +       struct sss_fifo_channel         tx;
> +       bool                            setup_done;
> +       int                             err;
> +};
> +
> +/**
> + * struct sss_crypto_wrapper - simple wrapper for easy access to the driver data
> + * @alg: wrapped crypto algorithm
> + * @dev: pointer to the driver state
> + */
> +struct sss_crypto_wrapper {
> +       struct crypto_alg                alg;
> +       struct s3c_sss                  *dev;
> +};
> +
> +/*** Helper functions ***/
> +
> +#define fifo_to_req_ctx(fifo, dir) container_of((fifo), \
> +                                               struct sss_req_context, dir)
> +
> +static inline struct sss_req_context *sss_to_req_ctx(struct s3c_sss *sss)
> +{
> +       struct ablkcipher_request *req = sss->cur_req;
> +       return req ? ablkcipher_request_ctx(req) : NULL;
> +}
> +
> +static inline unsigned int fifo_to_dma_channel(struct sss_fifo_channel *chan)
> +{
> +       return chan->dir == FIFO_TX ? DMACH_SECURITY_TX : DMACH_SECURITY_RX;
> +}
> +
> +static inline bool sss_dma_enabled(void)
> +{
> +       /* DMA is disabled till someone figures out why it's not transmitting
> +          all data to the crypto engine. */
> +       return false;
> +}
> +
> +static int count_sgs(struct scatterlist *sg)
> +{
> +       int i;
> +
> +       for (i = 0; sg; i++)
> +               sg = sg_next(sg);
> +
> +       return i;
> +}
> +
> +static inline void orrl(u32 val, void __iomem *reg)
> +{
> +       writel(readl(reg) | val, reg);
> +}
> +
> +#ifdef DEBUG
> +static void sss_dump_regs(struct s3c_sss *sss)
> +{
> +       dev_dbg(sss->dev, "DnI_CFG: %x\n", readl(sss->regs + DnI_CFG));
> +
> +       dev_dbg(sss->dev, "FRx_Ctrl: %x\n", readl(sss->regs + FRx_Ctrl));
> +       dev_dbg(sss->dev, "FRx_MLen: %x\n", readl(sss->regs + FRx_MLen));
> +       dev_dbg(sss->dev, "FRx_BlkSz: %x\n", readl(sss->regs + FRx_BlkSz));
> +       dev_dbg(sss->dev, "FRx_Addr: %x\n", readl(sss->regs + FRx_Addr));
> +       dev_dbg(sss->dev, "FRx_MLenCnt: %x\n", readl(sss->regs + FRx_MLenCnt));
> +
> +       dev_dbg(sss->dev, "FTx_Ctrl: %x\n", readl(sss->regs + FTx_Ctrl));
> +       dev_dbg(sss->dev, "FTx_MLen: %x\n", readl(sss->regs + FTx_MLen));
> +       dev_dbg(sss->dev, "FTx_BlkSz: %x\n", readl(sss->regs + FTx_BlkSz));
> +       dev_dbg(sss->dev, "FTx_Addr: %x\n", readl(sss->regs + FTx_Addr));
> +       dev_dbg(sss->dev, "FTx_MLenCnt: %x\n", readl(sss->regs + FTx_MLenCnt));
> +
> +       dev_dbg(sss->dev, "AES_CTRL: %x\n", readl(sss->regs + AES_CTRL));
> +       dev_dbg(sss->dev, "TDES_CTRL: %x\n", readl(sss->regs + TDES_CTRL));
> +       dev_dbg(sss->dev, "HASH_CTRL: %x\n", readl(sss->regs + HASH_CTRL));
> +       dev_dbg(sss->dev, "HASH_STATUS: %x\n", readl(sss->regs + HASH_STATUS));
> +}
> +#else
> +#define sss_dump_regs(...)
> +#endif
> +
> +#ifdef DEBUG
> +static void sss_dump_channel(struct sss_fifo_channel *chan)
> +{
> +       struct s3c_sss *sss = chan->dev;
> +       bool tx = (chan->dir == FIFO_TX);
> +       u32 val;
> +
> +       val = readl(sss->regs + (tx ? FTx_Ctrl : FRx_Ctrl));
> +
> +       dev_dbg(sss->dev, "FIFO_%cX: %c%c%c%c%c\n", tx ? 'T' : 'R',
> +               val & FXx_Ctrl_Full ? 'F' : ' ',
> +               val & FXx_Ctrl_Empty ? 'E' : ' ',
> +               val & FXx_Ctrl_Done ? 'D' : ' ',
> +               val & FXx_Ctrl_Running ? 'R' : ' ',
> +               val & FXx_Ctrl_Start ? 'S' : ' ');
> +
> +       if (sss_dma_enabled()) {
> +               dev_dbg(sss->dev, "      cur_sg: %p\n", chan->cur_sg);
> +               dev_dbg(sss->dev, "      offset: %d\n", chan->offset);
> +               dev_dbg(sss->dev, "    sg_count: %d\n", chan->sg_count);
> +       }
> +
> +       dev_dbg(sss->dev, "    req_size: %d\n", chan->req_size);
> +       dev_dbg(sss->dev, "  bytes_done: %d\n", chan->bytes_done);
> +       dev_dbg(sss->dev, "     mlencnt: %d\n",
> +               readl(sss->regs + (tx ? FTx_MLenCnt : FRx_MLenCnt)) * 4);
> +       dev_dbg(sss->dev, "    wd2write: %d\n", (val >> tx ? 8 : 16) & 0xFF);
> +       dev_dbg(sss->dev, "     wd2read: %d\n", (val >> tx ? 16 : 8) & 0xFF);
> +}
> +#else
> +#define sss_dump_channel(...)
> +#endif
> +
> +static void sss_reset_fifo(struct s3c_sss *sss, int reg)
> +{
> +       int timeout = 1000;
> +       u32 val;
> +
> +       writel(FXx_Ctrl_Reset, sss->regs + reg);
> +
> +       while (timeout-- > 0) {
> +               val = readl(sss->regs + reg);
> +               if (!(val & FXx_Ctrl_Reset))
> +                       break;
> +       }
> +
> +       if (timeout <= 0)
> +               dev_warn(sss->dev, "Failed to reset FIFO_%cX!\n",
> +                        reg == FRx_Ctrl ? 'R' : 'T');
> +}
> +
> +static void sss_reset_hw(struct s3c_sss *sss)
> +{
> +       u32 val = 0;
> +
> +       if (sss_dma_enabled())
> +               val |= (DnI_CFG_RxDmaEnb | DnI_CFG_TxDmaEnb |
> +                       DnI_CFG_RxTrgLevel(16) | DnI_CFG_TxTrgLevel(16));
> +
> +       writel(val, sss->regs + DnI_CFG);
> +
> +       /* Reset FIFOs */
> +       sss_reset_fifo(sss, FRx_Ctrl);
> +       sss_reset_fifo(sss, FTx_Ctrl);
> +
> +       /* Ensure all subsystems are disabled */
> +       writel(0, sss->regs + AES_CTRL);
> +       writel(0, sss->regs + TDES_CTRL);
> +       writel(0, sss->regs + HASH_CTRL);
> +}
> +
> +#ifdef DEBUG
> +static void check_priv_mismatch(struct s3c_sss *sss, const char* name, int reg)
> +{
> +       u32 val = readl(sss->regs + reg);
> +
> +       if (val & RdPrivMismatch)
> +               dev_warn(sss->dev, "%s read privilege mismatch! (0x%x)\n", name,
> +                        val);
> +
> +       if (val & WrPrivMismatch)
> +               dev_warn(sss->dev, "%s write privilege mismatch! (0x%x)\n",
> +                        name, val);
> +}
> +#else
> +#define check_priv_mismatch(...)
> +#endif
> +
> +static irqreturn_t sss_irq(int irq, void *priv)
> +{
> +       struct s3c_sss *sss = priv;
> +       u32 cfg = readl(sss->regs + DnI_CFG);
> +
> +       check_priv_mismatch(sss, "CONFIG", DnI_CFG);
> +
> +       if (cfg & DnI_CFG_FRx_Intr_Status) {
> +               dev_dbg(sss->dev, "%s: FIFO_RX IRQ\n", __func__);
> +               check_priv_mismatch(sss, "FIFO RX", FRx_Ctrl);
> +
> +               if (sss->cur_req && !sss_dma_enabled()) {
> +                       struct sss_fifo_channel *ch = &sss_to_req_ctx(sss)->rx;
> +                       if (ch->req_size) {
> +                               dev_dbg(sss->dev, "Increasing consumption with "
> +                                                 "%d bytes\n", ch->req_size);
> +                               ch->bytes_done += ch->req_size;
> +                               ch->req_size = 0;
> +                               tasklet_schedule(&sss->tasklet);
> +                       }
> +               }
> +       }
> +
> +       if (cfg & DnI_CFG_FTx_Intr_Status) {
> +               dev_dbg(sss->dev, "%s: FIFO_TX IRQ\n", __func__);
> +               check_priv_mismatch(sss, "FIFO TX", FTx_Ctrl);
> +
> +               if (sss->cur_req && !sss_dma_enabled())
> +                       tasklet_schedule(&sss->tasklet);
> +       }
> +
> +       if (cfg & DnI_CFG_SHA_Intr_Status)
> +               dev_dbg(sss->dev, "%s: HASH IRQ\n", __func__);
> +
> +       if (cfg & DnI_CFG_DES_Intr_Status) {
> +               dev_dbg(sss->dev, "%s: TDES IRQ\n", __func__);
> +               check_priv_mismatch(sss, "TDES", TDES_CTRL);
> +       }
> +
> +       if (cfg & DnI_CFG_AES_Intr_Status) {
> +               dev_dbg(sss->dev, "%s: AES IRQ\n", __func__);
> +               check_priv_mismatch(sss, "AES", AES_CTRL);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void sss_dma_cb(struct s3c2410_dma_chan *chan, void *pw, int size,
> +                      enum s3c2410_dma_buffresult res)
> +{
> +       struct sss_fifo_channel *fifo_chan = pw;
> +       struct s3c_sss *sss = fifo_chan->dev;
> +       struct sss_req_context *req_ctx;
> +
> +       dev_dbg(sss->dev, "%s: FIFO_%cX\n", __func__,
> +               fifo_chan->dir == FIFO_RX ? 'R' : 'T');
> +
> +       if (fifo_chan->dir == FIFO_RX)
> +               req_ctx = fifo_to_req_ctx(fifo_chan, rx);
> +       else
> +               req_ctx = fifo_to_req_ctx(fifo_chan, tx);
> +
> +       switch (res) {
> +       case S3C2410_RES_OK:
> +               fifo_chan->bytes_done += fifo_chan->req_size;
> +               fifo_chan->offset += fifo_chan->req_size;
> +               fifo_chan->req_size = 0;
> +               break;
> +       case S3C2410_RES_ERR:
> +       case S3C2410_RES_ABORT:
> +       default:
> +               dev_err(sss->dev, "Error occured during DMA transfer!\n");
> +               if (!req_ctx->err)
> +                       req_ctx->err = -EIO;
> +       }
> +
> +       tasklet_schedule(&sss->tasklet);
> +}
> +
> +static int sss_setup_dma(struct s3c_sss *sss, unsigned int channel)
> +{
> +       enum s3c2410_dmasrc source;
> +       unsigned long reg;
> +       int ret;
> +
> +       ret = s3c2410_dma_request(channel, &sss->dma_client, sss);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (channel == DMACH_SECURITY_RX) {
> +               reg = (unsigned long)(sss->regs_phys + SDMA_FRx_Buf);
> +               source = S3C2410_DMASRC_MEM;
> +       } else { /* DMACH_SECURITY_TX */
> +               reg = (unsigned long)(sss->regs_phys + SDMA_FTx_Buf);
> +               source = S3C2410_DMASRC_HW;
> +       }
> +
> +       s3c2410_dma_config(channel, 4);
> +       s3c2410_dma_devconfig(channel, source, reg);
> +       s3c2410_dma_set_buffdone_fn(channel, sss_dma_cb);
> +
> +       return 0;
> +}
> +
> +static void sss_setup_hw(struct s3c_sss *sss)
> +{
> +       struct ablkcipher_request *req = sss->cur_req;
> +       struct sss_context *ctx = crypto_tfm_ctx(req->base.tfm);
> +       struct sss_req_context *req_ctx = ablkcipher_request_ctx(req);
> +       u32 val, cfg, fifo_rx, fifo_tx;
> +
> +       dev_dbg(sss->dev, "%s: setting up hw\n", __func__);
> +
> +       sss_reset_hw(sss);
> +
> +       cfg = readl(sss->regs + DnI_CFG);
> +       cfg |= (DnI_CFG_FTx_Intr_En | DnI_CFG_FRx_Intr_En);
> +
> +       fifo_rx = (FXx_Ctrl_Host_Rd_En | FXx_Ctrl_Host_Wr_En |
> +                  FRx_Ctrl_Sync_Tx);
> +       fifo_tx = (FXx_Ctrl_Host_Rd_En | FXx_Ctrl_Host_Wr_En);
> +
> +       switch (req_ctx->algorithm) {
> +       case ALGO_AES:
> +               cfg |= DnI_CFG_AES_Intr_En;
> +               fifo_rx |= FXx_Ctrl_Module_AES;
> +               fifo_tx |= FXx_Ctrl_Module_AES;
> +
> +               switch (req_ctx->blk_cipher) {
> +               case CIPH_ECB:
> +                       val = AES_CTRL_OpMode_ECB;
> +                       break;
> +               case CIPH_CBC:
> +                       val = AES_CTRL_OpMode_CBC;
> +                       memcpy(sss->regs + AES_IV, req->info, 16);
> +                       break;
> +               case CIPH_CTR:
> +                       val = AES_CTRL_OpMode_CTR;
> +                       memcpy(sss->regs + AES_CTR, req->info, 16); /* ??? */
> +                       break;
> +               }
> +
> +               if (req_ctx->direction == DECRYPT &&
> +                   req_ctx->blk_cipher != CIPH_CTR)
> +                       val |= AES_CTRL_OpDirection_Dec;
> +
> +               switch (ctx->key_len) {
> +               case AES_KEYSIZE_128:
> +                       val |= AES_CTRL_KeyMode_128bits;
> +                       break;
> +               case AES_KEYSIZE_192:
> +                       val |= AES_CTRL_KeyMode_192bits;
> +                       break;
> +               case AES_KEYSIZE_256:
> +                       val |= AES_CTRL_KeyMode_256bits;
> +                       break;
> +               }
> +               memcpy(sss->regs + AES_KEY, ctx->key, ctx->key_len);
> +
> +               writel(val, sss->regs + AES_CTRL);
> +
> +               writel(AES_BLOCK_SIZE / 4, sss->regs + FRx_BlkSz);
> +               writel(AES_BLOCK_SIZE / 4, sss->regs + FTx_BlkSz);
> +               writel(sss->regs_phys + AES_DIN, sss->regs + FRx_Addr);
> +               writel(sss->regs_phys + AES_DOUT, sss->regs + FTx_Addr);
> +
> +               break;
> +       case ALGO_DES:
> +       case ALGO_TDES:
> +               cfg |= DnI_CFG_DES_Intr_En;
> +               fifo_rx |= FXx_Ctrl_Module_DES;
> +               fifo_tx |= FXx_Ctrl_Module_DES;
> +
> +               switch (req_ctx->blk_cipher) {
> +               case CIPH_ECB:
> +                       val = TDES_CTRL_Mode_ECB;
> +                       break;
> +               case CIPH_CBC:
> +                       val = TDES_CTRL_Mode_CBC;
> +                       memcpy(sss->regs + TDES_IV, req->info, 16);
> +                       break;
> +               case CIPH_CTR:
> +                       /* NOP */
> +                       break;
> +               }
> +
> +               if (req_ctx->direction == DECRYPT)
> +                       val |= TDES_CTRL_OpDirection_Dec;
> +
> +               if (req_ctx->algorithm == ALGO_TDES)
> +                       val |= TDES_CTRL_Mode_Tdes;
> +
> +               val |= TDES_CTRL_IntMode;
> +
> +               memcpy(sss->regs + TDES_KEY, ctx->key, ctx->key_len);
> +
> +               writel(val, sss->regs + TDES_CTRL);
> +
> +               writel(DES_BLOCK_SIZE / 4, sss->regs + FRx_BlkSz);
> +               writel(DES_BLOCK_SIZE / 4, sss->regs + FTx_BlkSz);
> +               writel(sss->regs_phys + TDES_INPUT, sss->regs + FRx_Addr);
> +               writel(sss->regs_phys + TDES_OUTPUT, sss->regs + FTx_Addr);
> +
> +               break;
> +       case ALGO_SHA1:
> +       case ALGO_HMAC_SHA1:
> +               cfg |= DnI_CFG_SHA_Intr_En;
> +               fifo_rx |= FXx_Ctrl_Module_SHA;
> +               fifo_tx |= FXx_Ctrl_Module_SHA;
> +
> +               /*TODO*/
> +
> +               break;
> +       }
> +
> +       writel(cfg, sss->regs + DnI_CFG);
> +       writel(fifo_rx, sss->regs + FRx_Ctrl);
> +       writel(fifo_tx, sss->regs + FTx_Ctrl);
> +}
> +
> +static void sss_setup_hw_mlen(struct sss_fifo_channel *chan, size_t len)
> +{
> +       struct s3c_sss *sss = chan->dev;
> +
> +       if (chan->dir == FIFO_RX) {
> +               writel(len / 4, sss->regs + FRx_MLen);
> +               orrl(FXx_Ctrl_Start, sss->regs + FRx_Ctrl);
> +       } else {
> +               writel(len / 4, sss->regs + FTx_MLen);
> +               orrl(FXx_Ctrl_Start, sss->regs + FTx_Ctrl);
> +       }
> +}
> +
> +static int sss_setup_dma_channel(struct sss_fifo_channel *chan)
> +{
> +       struct s3c_sss *sss = chan->dev;
> +       unsigned int channel = fifo_to_dma_channel(chan);
> +       int ret;
> +
> +       if (chan->offset >= sg_dma_len(chan->cur_sg)) {
> +               chan->cur_sg = sg_next(chan->cur_sg);
> +               chan->offset = 0;
> +       }
> +
> +       chan->req_size = min(sg_dma_len(chan->cur_sg) - chan->offset,
> +                            SSS_FIFO_SIZE);
> +
> +       sss_setup_hw_mlen(chan, chan->req_size);
> +
> +       dev_dbg(sss->dev, "Enqueue'ing for FIFO_%cX: %x (%d)\n",
> +               channel == DMACH_SECURITY_TX ? 'T' : 'R',
> +               sg_dma_address(chan->cur_sg) + chan->offset, chan->req_size);
> +
> +       ret = s3c2410_dma_enqueue(channel, chan,
> +                                 sg_dma_address(chan->cur_sg) + chan->offset,
> +                                 chan->req_size);
> +       if (ret)
> +               return ret;
> +
> +       return s3c2410_dma_ctrl(fifo_to_dma_channel(chan), S3C2410_DMAOP_START);
> +}
> +
> +static void sss_setup_fifo(struct sss_fifo_channel *chan)
> +{
> +       struct s3c_sss *sss = chan->dev;
> +       struct ablkcipher_request *req = sss->cur_req;
> +       enum dma_data_direction dir;
> +       unsigned int sg_flags;
> +
> +       if (chan->dir == FIFO_RX) {
> +               sg_flags = SG_MITER_FROM_SG;
> +               chan->sg = req->src;
> +               dir = DMA_TO_DEVICE;
> +       } else {
> +               sg_flags = SG_MITER_TO_SG;
> +               chan->sg = req->dst;
> +               dir = DMA_FROM_DEVICE;
> +       }
> +
> +       if (sss_dma_enabled()) {
> +               int sg_count = count_sgs(chan->sg);
> +               chan->sg_count = dma_map_sg(sss->dev, chan->sg, sg_count, dir);
> +               chan->cur_sg = chan->sg;
> +
> +               s3c2410_dma_ctrl(fifo_to_dma_channel(chan),
> +                                S3C2410_DMAOP_FLUSH);
> +       }
> +}
> +
> +static int sss_handle_fifo(struct sss_fifo_channel *chan)
> +{
> +       struct s3c_sss *sss = chan->dev;
> +       struct ablkcipher_request *req = sss->cur_req;
> +       void __iomem *fifo;
> +
> +       if (chan->req_size)
> +               /* FIFO is still transferring data */
> +               return -EINPROGRESS;
> +
> +       if (sss_dma_enabled())
> +               return sss_setup_dma_channel(chan);
> +
> +       /* PIO */
> +       if (chan->dir == FIFO_RX)
> +               fifo = sss->regs + FRx_Buf;
> +       else
> +               fifo = sss->regs + FTx_Buf;
> +
> +       chan->req_size = min(req->nbytes - chan->bytes_done, SSS_FIFO_SIZE);
> +
> +       sss_setup_hw_mlen(chan, chan->req_size);
> +
> +       dev_dbg(sss->dev, "Transferring %d bytes to FIFO_%s\n", chan->req_size,
> +               chan->dir == FIFO_TX ? "TX" : "RX");
> +
> +       scatterwalk_map_and_copy(fifo, chan->sg, chan->bytes_done,
> +                                chan->req_size, (chan->dir == FIFO_TX));
> +
> +       if (chan->dir == FIFO_TX) {
> +               chan->bytes_done += chan->req_size;
> +               chan->req_size = 0;
> +       }
> +
> +       return 0;
> +}
> +
> +static void sss_cleanup_fifo(struct sss_fifo_channel *chan)
> +{
> +       struct s3c_sss *sss = chan->dev;
> +       enum dma_data_direction dir;
> +
> +       if (sss_dma_enabled()) {
> +               if (chan->dir == FIFO_RX)
> +                       dir = DMA_TO_DEVICE;
> +               else
> +                       dir = DMA_FROM_DEVICE;
> +
> +               dma_unmap_sg(sss->dev, chan->sg, chan->sg_count, dir);
> +
> +               s3c2410_dma_ctrl(fifo_to_dma_channel(chan), S3C2410_DMAOP_STOP);
> +       }
> +}
> +
> +static void sss_timer_callback(unsigned long priv)
> +{
> +       struct s3c_sss *sss = (struct s3c_sss *)priv;
> +       struct sss_req_context *req_ctx = sss_to_req_ctx(sss);
> +
> +       dev_err(sss->dev, "Request timed out!\n");
> +       req_ctx->err = -ETIMEDOUT;
> +
> +       tasklet_schedule(&sss->tasklet);
> +}
> +
> +static void sss_tasklet_callback(unsigned long priv)
> +{
> +       struct s3c_sss *sss = (struct s3c_sss *)priv;
> +       struct sss_req_context *req_ctx;
> +       struct ablkcipher_request *req;
> +       unsigned long flags;
> +
> +       if (!sss->cur_req) {
> +               spin_lock_irqsave(&sss->lock, flags);
> +               sss->cur_req = ablkcipher_dequeue_request(&sss->queue);
> +               spin_unlock_irqrestore(&sss->lock, flags);
> +
> +               if (!sss->cur_req) {
> +                       dev_warn(sss->dev, "Tasklet was called without any "
> +                                          "pending request!\n");
> +                       return;
> +               }
> +       }
> +
> +       /*TODO: backlog*/
> +
> +       req = sss->cur_req;
> +       req_ctx = ablkcipher_request_ctx(req);
> +
> +       dev_dbg(sss->dev, "Current request: %p (%d)\n", req, req->nbytes);
> +
> +       if (!req_ctx->setup_done) {
> +               clk_enable(sss->clock);
> +
> +               sss_setup_hw(sss);
> +               sss_setup_fifo(&req_ctx->rx);
> +               sss_setup_fifo(&req_ctx->tx);
> +
> +               req_ctx->setup_done = true;
> +       }
> +
> +       /* Ensure timeout handler is killed */
> +       if (timer_pending(&sss->timer))
> +               del_timer(&sss->timer);
> +
> +       if (!req_ctx->err && (req_ctx->rx.bytes_done < req->nbytes ||
> +                             req_ctx->tx.bytes_done < req->nbytes)) {
> +               int ret;
> +
> +               if (req_ctx->tx.bytes_done < req_ctx->rx.bytes_done)
> +                       /* Keep TX in sync with RX */
> +                       ret = sss_handle_fifo(&req_ctx->tx);
> +               else
> +                       /* Transmit some more data to RX */
> +                       ret = sss_handle_fifo(&req_ctx->rx);
> +
> +               sss_dump_channel(&req_ctx->tx);
> +               sss_dump_channel(&req_ctx->rx);
> +
> +               if (ret && ret != -EINPROGRESS) {
> +                       req_ctx->err = ret;
> +                       goto cleanup;
> +               }
> +
> +               mod_timer(&sss->timer, jiffies + SSS_TIMEOUT);
> +               return;
> +       }
> +
> +cleanup:
> +       sss_cleanup_fifo(&req_ctx->rx);
> +       sss_cleanup_fifo(&req_ctx->tx);
> +
> +       clk_disable(sss->clock);
> +
> +       /* Inform client of completion */
> +       req->base.complete(&req->base, req_ctx->err);
> +
> +       spin_lock_irqsave(&sss->lock, flags);
> +       sss->cur_req = NULL;
> +       /* Check whether there's still work to do */
> +       if (sss->queue.qlen || crypto_get_backlog(&sss->queue))
> +               tasklet_schedule(&sss->tasklet);
> +       spin_unlock_irqrestore(&sss->lock, flags);
> +}
> +
> +/*** SW handling ***/
> +
> +static int sss_crypto_generic(struct ablkcipher_request *req,
> +                             enum sss_algorithm alg,
> +                             enum sss_block_cipher blk_ciph,
> +                             enum sss_direction dir)
> +{
> +       struct sss_context *ctx = crypto_tfm_ctx(req->base.tfm);
> +       struct sss_req_context *req_ctx = ablkcipher_request_ctx(req);
> +       struct s3c_sss *sss = ctx->dev;
> +       unsigned long flags;
> +       int ret;
> +
> +       /* Fill the request */
> +       *req_ctx = (struct sss_req_context){
> +               .algorithm      = alg,
> +               .blk_cipher     = blk_ciph,
> +               .direction      = dir,
> +               .rx             = {
> +                               .dev = sss,
> +                               .dir = FIFO_RX,
> +               },
> +               .tx             = {
> +                               .dev = sss,
> +                               .dir = FIFO_TX,
> +               },
> +       };
> +
> +       /* Enqueue the request */
> +       spin_lock_irqsave(&sss->lock, flags);
> +       ret = ablkcipher_enqueue_request(&sss->queue, req);
> +       if (ret == -EINPROGRESS && !sss->cur_req)
> +               tasklet_schedule(&sss->tasklet);
> +       spin_unlock_irqrestore(&sss->lock, flags);
> +
> +       if (ret != -EINPROGRESS)
> +               dev_err(sss->dev, "Couldn't enqueue request!\n");
> +
> +       return ret;
> +}
> +
> +static int sss_aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
> +                         unsigned int len)
> +{
> +       struct crypto_tfm *tfm  = crypto_ablkcipher_tfm(cipher);
> +       struct sss_context *ctx = crypto_tfm_ctx(tfm);
> +
> +       switch (len) {
> +       case AES_KEYSIZE_128:
> +       case AES_KEYSIZE_192:
> +       case AES_KEYSIZE_256:
> +               break;
> +       default:
> +               crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
> +               return -EINVAL;
> +       }
> +
> +       memcpy(ctx->key, key, len);
> +       ctx->key_len = len;
> +
> +       return 0;
> +}
> +
> +static int sss_des_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
> +                         unsigned int len)
> +{
> +       struct crypto_tfm *tfm  = crypto_ablkcipher_tfm(cipher);
> +       struct sss_context *ctx = crypto_tfm_ctx(tfm);
> +
> +       if (len > SSS_MAX_KEY_SIZE) {
> +               crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
> +               return -EINVAL;
> +       }
> +
> +       /* RFC2451: weak key checks SHOULD be performed */
> +       if (len == DES_KEY_SIZE) {
> +               u32 tmp[DES_EXPKEY_WORDS];
> +               int ret = des_ekey(tmp, key);
> +
> +               if (unlikely(ret == 0) &&
> +                   (crypto_tfm_get_flags(tfm) & CRYPTO_TFM_REQ_WEAK_KEY)) {
> +                       crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_WEAK_KEY);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       memcpy(ctx->key, key, len);
> +       ctx->key_len = len;
> +
> +       return 0;
> +}
> +
> +static int sss_cra_init(struct crypto_tfm *tfm)
> +{
> +       struct sss_crypto_wrapper *wrapper = container_of(tfm->__crt_alg,
> +                                               struct sss_crypto_wrapper, alg);
> +       struct s3c_sss *sss = wrapper->dev;
> +       struct sss_context *ctx = crypto_tfm_ctx(tfm);
> +
> +       ctx->dev = sss;
> +       tfm->crt_ablkcipher.reqsize = sizeof(struct sss_req_context);
> +
> +       return 0;
> +}
> +
> +static int sss_aes_cbc_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_AES, CIPH_CBC, ENCRYPT);
> +}
> +
> +static int sss_aes_cbc_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_AES, CIPH_CBC, DECRYPT);
> +}
> +
> +static int sss_aes_ecb_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_AES, CIPH_ECB, ENCRYPT);
> +}
> +
> +static int sss_aes_ecb_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_AES, CIPH_ECB, DECRYPT);
> +}
> +
> +static int sss_aes_ctr_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_AES, CIPH_CTR, ENCRYPT);
> +}
> +
> +static int sss_aes_ctr_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_AES, CIPH_CTR, DECRYPT);
> +}
> +
> +static int sss_des_ecb_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_DES, CIPH_ECB, ENCRYPT);
> +}
> +
> +static int sss_des_ecb_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_DES, CIPH_ECB, DECRYPT);
> +}
> +
> +static int sss_des_cbc_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_DES, CIPH_CBC, ENCRYPT);
> +}
> +
> +static int sss_des_cbc_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_DES, CIPH_CBC, DECRYPT);
> +}
> +
> +static int sss_tdes_ecb_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_TDES, CIPH_ECB, ENCRYPT);
> +}
> +
> +static int sss_tdes_ecb_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_TDES, CIPH_ECB, DECRYPT);
> +}
> +
> +static int sss_tdes_cbc_encrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_TDES, CIPH_CBC, ENCRYPT);
> +}
> +
> +static int sss_tdes_cbc_decrypt(struct ablkcipher_request *req)
> +{
> +       return sss_crypto_generic(req, ALGO_TDES, CIPH_CBC, DECRYPT);
> +}
> +
> +static struct sss_algo_template {
> +       char                            *alg_name;
> +       char                            *blk_ciph_name;
> +       int                              blk_size;
> +       struct ablkcipher_alg            ablkcipher;
> +
> +       struct sss_crypto_wrapper       *alg;
> +} sss_crypto_algos[] = {
> +       /* AES ECB/CBC/CTR */
> +       {
> +               .alg_name = "aes",
> +               .blk_ciph_name = "ecb",
> +               .blk_size = AES_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .min_keysize    =       AES_MIN_KEY_SIZE,
> +                       .max_keysize    =       AES_MAX_KEY_SIZE,
> +                       .setkey         =       sss_aes_setkey,
> +                       .encrypt        =       sss_aes_ecb_encrypt,
> +                       .decrypt        =       sss_aes_ecb_decrypt,
> +               },
> +       },
> +       {
> +               .alg_name = "aes",
> +               .blk_ciph_name = "cbc",
> +               .blk_size = AES_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .ivsize         =       AES_BLOCK_SIZE,
> +                       .min_keysize    =       AES_MIN_KEY_SIZE,
> +                       .max_keysize    =       AES_MAX_KEY_SIZE,
> +                       .setkey         =       sss_aes_setkey,
> +                       .encrypt        =       sss_aes_cbc_encrypt,
> +                       .decrypt        =       sss_aes_cbc_decrypt,
> +               },
> +       },
> +       {
> +               .alg_name = "aes",
> +               .blk_ciph_name = "ctr",
> +               .blk_size = AES_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .ivsize         =       CTR_RFC3686_IV_SIZE,
> +                       .min_keysize    =       AES_MIN_KEY_SIZE,
> +                       .max_keysize    =       AES_MAX_KEY_SIZE,
> +                       .setkey         =       sss_aes_setkey,
> +                       .encrypt        =       sss_aes_ctr_encrypt,
> +                       .decrypt        =       sss_aes_ctr_decrypt,
> +               },
> +       },
> +       /* DES CBC/ECB */
> +       {
> +               .alg_name = "des",
> +               .blk_ciph_name = "cbc",
> +               .blk_size = DES_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .ivsize         =       DES_BLOCK_SIZE,
> +                       .min_keysize    =       DES_KEY_SIZE,
> +                       .max_keysize    =       DES_KEY_SIZE,
> +                       .setkey         =       sss_des_setkey,
> +                       .encrypt        =       sss_des_cbc_encrypt,
> +                       .decrypt        =       sss_des_cbc_decrypt,
> +               },
> +       },
> +       {
> +               .alg_name = "des",
> +               .blk_ciph_name = "ecb",
> +               .blk_size = DES_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .min_keysize    =       DES_KEY_SIZE,
> +                       .max_keysize    =       DES_KEY_SIZE,
> +                       .setkey         =       sss_des_setkey,
> +                       .encrypt        =       sss_des_ecb_encrypt,
> +                       .decrypt        =       sss_des_ecb_decrypt,
> +               },
> +       },
> +       /* TDES CBC/ECB */
> +       {
> +               .alg_name = "des3_ede",
> +               .blk_ciph_name = "cbc",
> +               .blk_size = DES3_EDE_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .ivsize         =       DES3_EDE_BLOCK_SIZE,
> +                       .min_keysize    =       DES3_EDE_KEY_SIZE,
> +                       .max_keysize    =       DES3_EDE_KEY_SIZE,
> +                       .setkey         =       sss_des_setkey,
> +                       .encrypt        =       sss_tdes_cbc_encrypt,
> +                       .decrypt        =       sss_tdes_cbc_decrypt,
> +               },
> +       },
> +       {
> +               .alg_name = "des3_ede",
> +               .blk_ciph_name = "ecb",
> +               .blk_size = DES3_EDE_BLOCK_SIZE,
> +               .ablkcipher = {
> +                       .min_keysize    =       DES3_EDE_KEY_SIZE,
> +                       .max_keysize    =       DES3_EDE_KEY_SIZE,
> +                       .setkey         =       sss_des_setkey,
> +                       .encrypt        =       sss_tdes_ecb_encrypt,
> +                       .decrypt        =       sss_tdes_ecb_decrypt,
> +               },
> +       },
> +};
> +
> +static int sss_init_template(struct platform_device *pdev,
> +                            struct sss_algo_template *templ)
> +{
> +       struct s3c_sss *sss = platform_get_drvdata(pdev);
> +       struct sss_crypto_wrapper *alg;
> +
> +       alg = kzalloc(sizeof(struct sss_crypto_wrapper), GFP_KERNEL);
> +       if (!alg)
> +               return -ENOMEM;
> +
> +       alg->dev = sss;
> +       alg->alg = (struct crypto_alg){
> +                       .cra_ablkcipher = templ->ablkcipher,
> +                       .cra_blocksize  = templ->blk_size,
> +                       .cra_ctxsize    = sizeof(struct sss_context),
> +                       .cra_flags      = CRYPTO_ALG_TYPE_ABLKCIPHER |
> +                                         CRYPTO_ALG_ASYNC,
> +                       .cra_init       = sss_cra_init,
> +                       .cra_module     = THIS_MODULE,
> +                       .cra_priority   = SSS_CRA_PRIORITY,
> +                       .cra_type       = &crypto_ablkcipher_type,
> +       };
> +
> +       snprintf(alg->alg.cra_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
> +                templ->blk_ciph_name, templ->alg_name);
> +       snprintf(alg->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s-%s-%s",
> +                pdev->name, templ->alg_name, templ->blk_ciph_name);
> +
> +       /* Save pointer for removal */
> +       templ->alg = alg;
> +
> +       dev_info(sss->dev, "crypto acceleration for %s", alg->alg.cra_name);
> +
> +       return 0;
> +}
> +
> +static struct ahash_alg sss_hash_algos[] = {
> +#if 0
> +       {
> +               .init           =       sss_sha_init,
> +               .update         =       sss_sha_update,
> +               .final          =       sss_sha1_final,
> +               .digest         =       sss_sha1_digest,
> +               .halg           =       {
> +                       .digestsize             =       SHA1_DIGEST_SIZE,
> +                       .statesize              =       sizeof(struct sha1_state),
> +                       .base                   =       {
> +                               .cra_name               =       "sha1",
> +                               .cra_driver_name        =       "s3c-sss-sha1",
> +                               .cra_priority           =       SSS_CRA_PRIORITY,
> +                               .cra_flags              =       CRYPTO_ALG_TYPE_AHASH |
> +                                                               CRYPTO_ALG_ASYNC,
> +                               .cra_blocksize          =       SHA1_BLOCK_SIZE,
> +                               .cra_ctxsize            =       sizeof(struct sss_context),
> +                               .cra_type               =       &crypto_ahash_type,
> +                               .cra_module             =       THIS_MODULE,
> +                               .cra_init               =       sss_cra_init,
> +                       }
> +               },
> +       },
> +       {
> +               .init           =       sss_sha_init,
> +               .update         =       sss_sha_update,
> +               .final          =       sss_sha1_final,
> +               .digest         =       sss_sha1_digest,
> +               .setkey         =       sss_sha1_setkey,
> +               .halg           =       {
> +                       .digestsize             =       SHA1_DIGEST_SIZE,
> +                       .statesize              =       sizeof(struct sha1_state),
> +                       .base                   =       {
> +                               .cra_name               =       "hmac(sha1)",
> +                               .cra_driver_name        =       "s3c-sss-hmac-sha1",
> +                               .cra_priority           =       SSS_CRA_PRIORITY,
> +                               .cra_flags              =       CRYPTO_ALG_TYPE_AHASH |
> +                                                               CRYPTO_ALG_ASYNC,
> +                               .cra_blocksize          =       SHA1_BLOCK_SIZE,
> +                               .cra_ctxsize            =       sizeof(struct sss_context),
> +                               .cra_type               =       &crypto_ahash_type,
> +                               .cra_module             =       THIS_MODULE,
> +                               .cra_init               =       sss_cra_init,
> +                       }
> +               },
> +       },
> +#endif
> +};
> +
> +static void sss_unregister_algos(void)
> +{
> +       struct sss_crypto_wrapper *alg;
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(sss_crypto_algos); i++) {
> +               alg = sss_crypto_algos[i].alg;
> +
> +               if (alg) {
> +                       crypto_unregister_alg(&alg->alg);
> +                       kfree(alg);
> +
> +                       sss_crypto_algos[i].alg = NULL;
> +               }
> +       }
> +
> +       /* Unregistering algorithms that weren't registered in the first place
> +          doesn't do any harm, so just do it for all. */
> +       for (i = 0; i < ARRAY_SIZE(sss_hash_algos); i++)
> +               crypto_unregister_ahash(&sss_hash_algos[i]);
> +}
> +
> +static int sss_register_algos(struct platform_device *pdev)
> +{
> +       int i, ret;
> +
> +       for (i = 0; i < ARRAY_SIZE(sss_crypto_algos); i++) {
> +               ret = sss_init_template(pdev, &sss_crypto_algos[i]);
> +               if (ret)
> +                       goto exit;
> +
> +               ret = crypto_register_alg(&sss_crypto_algos[i].alg->alg);
> +               if (ret)
> +                       goto exit;
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(sss_hash_algos); i++) {
> +               ret = crypto_register_ahash(&sss_hash_algos[i]);
> +               if (ret)
> +                       goto exit;
> +       }
> +
> +       return 0;
> +
> +exit:
> +       sss_unregister_algos();
> +       return ret;
> +}
> +
> +static int __devinit sss_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       struct s3c_sss *sss;
> +       int ret;
> +
> +       sss = kzalloc(sizeof(struct s3c_sss), GFP_KERNEL);
> +       if (!sss) {
> +               dev_err(dev, "cannot allocate memory\n");
> +               return -ENOMEM;
> +       }
> +
> +       spin_lock_init(&sss->lock);
> +       crypto_init_queue(&sss->queue, 50);
> +       tasklet_init(&sss->tasklet, sss_tasklet_callback, (unsigned long) sss);
> +       setup_timer(&sss->timer, sss_timer_callback, (unsigned long) sss);
> +
> +       sss->dev = dev;
> +       sss->dma_client.name = (char *) pdev->name;
> +       platform_set_drvdata(pdev, sss);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res) {
> +               dev_err(dev, "cannot find register resource\n");
> +               ret = -EINVAL;
> +               goto exit_dev;
> +       }
> +
> +       sss->regs_res = request_mem_region(res->start, resource_size(res),
> +                                          dev_name(dev));
> +       if (!sss->regs_res) {
> +               dev_err(dev, "cannot request register resource\n");
> +               ret = -ENOENT;
> +               goto exit_dev;
> +       }
> +
> +       sss->regs_phys = (void __iomem *) res->start;
> +
> +       sss->regs = ioremap(res->start, resource_size(res));
> +       if (!sss->regs) {
> +               dev_err(dev, "cannot map registers\n");
> +               ret = -ENXIO;
> +               goto exit_resource;
> +       }
> +
> +       ret = platform_get_irq(pdev, 0);
> +       if (ret < 0 || ret == NO_IRQ) {
> +               dev_err(dev, "cannot find IRQ\n");
> +               goto exit_regs_remap;
> +       }
> +
> +       sss->irq = ret;
> +
> +       ret = request_irq(sss->irq, sss_irq, 0, dev_name(dev), sss);
> +       if (ret < 0) {
> +               dev_err(dev, "cannot claim IRQ\n");
> +               goto exit_regs_remap;
> +       }
> +
> +       sss->clock = clk_get(dev, "secur");
> +       if (!sss->clock) {
> +               dev_err(dev, "cannot find clock\n");
> +               ret = -ENXIO;
> +               goto exit_irq;
> +       }
> +
> +       WARN_ON(clk_set_rate(sss->clock, 66*1000000)); /*REMOVEME*/
> +
> +       if (sss_dma_enabled()) {
> +               ret = sss_setup_dma(sss, DMACH_SECURITY_RX);
> +               if (ret < 0) {
> +                       dev_err(dev, "cannot setup SECURITY_RX DMA channel\n");
> +                       goto exit_clock;
> +               }
> +
> +               ret = sss_setup_dma(sss, DMACH_SECURITY_TX);
> +               if (ret < 0) {
> +                       dev_err(dev, "cannot setup SECURITY_TX DMA channel\n");
> +                       goto exit_dma_rx;
> +               }
> +       }
> +
> +       ret = sss_register_algos(pdev);
> +       if (ret) {
> +               dev_err(dev, "cannot register algos\n");
> +               goto exit_dma_tx;
> +       }
> +
> +       return 0;
> +
> +exit_dma_tx:
> +       if (sss_dma_enabled())
> +               s3c2410_dma_free(DMACH_SECURITY_TX, &sss->dma_client);
> +exit_dma_rx:
> +       if (sss_dma_enabled())
> +               s3c2410_dma_free(DMACH_SECURITY_RX, &sss->dma_client);
> +exit_clock:
> +       clk_put(sss->clock);
> +exit_irq:
> +       free_irq(sss->irq, sss);
> +exit_regs_remap:
> +       iounmap(sss->regs);
> +exit_resource:
> +       release_resource(sss->regs_res);
> +       kfree(sss->regs_res);
> +exit_dev:
> +       tasklet_kill(&sss->tasklet);
> +       kfree(sss);
> +
> +       return ret;
> +}
> +
> +static int __devexit sss_remove(struct platform_device *pdev)
> +{
> +       struct s3c_sss *sss = platform_get_drvdata(pdev);
> +
> +       if (timer_pending(&sss->timer))
> +               del_timer(&sss->timer);
> +
> +       if (sss_dma_enabled()) {
> +               s3c2410_dma_free(DMACH_SECURITY_TX, &sss->dma_client);
> +               s3c2410_dma_free(DMACH_SECURITY_RX, &sss->dma_client);
> +       }
> +
> +       sss_unregister_algos();
> +       clk_put(sss->clock);
> +       free_irq(sss->irq, sss);
> +       iounmap(sss->regs);
> +       release_resource(sss->regs_res);
> +       kfree(sss->regs_res);
> +       tasklet_kill(&sss->tasklet);
> +       kfree(sss);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver sss_crypto = {
> +       .driver         = {
> +               .name   = "s3c-sss",
> +               .owner  = THIS_MODULE,
> +       },
> +       .probe          = sss_probe,
> +       .remove         = __devexit_p(sss_remove),
> +};
> +
> +static int __init sss_crypto_init(void)
> +{
> +       return platform_driver_register(&sss_crypto);
> +}
> +module_init(sss_crypto_init);
> +
> +static void __exit sss_crypto_exit(void)
> +{
> +       platform_driver_unregister(&sss_crypto);
> +}
> +module_exit(sss_crypto_exit);
> +
> +MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere at gmail.com>");
> +MODULE_DESCRIPTION("Support for Samsung's Security Sub-Systems");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:sss_crypto");
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



More information about the linux-arm-kernel mailing list