From mboxrd@z Thu Jan 1 00:00:00 1970 From: kmpark@infradead.org (Kyungmin Park) Date: Tue, 3 Aug 2010 13:44:35 +0900 Subject: [RFC 9/9] crypto: Add Samsung crypto engine driver In-Reply-To: <4C129341.2060407@gmail.com> References: <4C129341.2060407@gmail.com> Message-ID: To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Hi, Any update on this? Thank you, Kyungmin Park On Sat, Jun 12, 2010 at 4:49 AM, Maurus Cuelenaere 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 > --- > ?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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#include > + > +#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 "); > +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 >