openwrt/target/linux/generic-2.6/files/crypto/ocf/talitos/talitos.c

1360 lines
40 KiB
C

/*
* crypto/ocf/talitos/talitos.c
*
* An OCF-Linux module that uses Freescale's SEC to do the crypto.
* Based on crypto/ocf/hifn and crypto/ocf/safe OCF drivers
*
* Copyright (c) 2006 Freescale Semiconductor, Inc.
*
* This code written by Kim A. B. Phillips <kim.phillips@freescale.com>
* some code copied from files with the following:
* Copyright (C) 2004-2007 David McCullough <david_mccullough@securecomputing.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ---------------------------------------------------------------------------
*
* NOTES:
*
* The Freescale SEC (also known as 'talitos') resides on the
* internal bus, and runs asynchronous to the processor core. It has
* a wide gamut of cryptographic acceleration features, including single-
* pass IPsec (also known as algorithm chaining). To properly utilize
* all of the SEC's performance enhancing features, further reworking
* of higher level code (framework, applications) will be necessary.
*
* The following table shows which SEC version is present in which devices:
*
* Devices SEC version
*
* 8272, 8248 SEC 1.0
* 885, 875 SEC 1.2
* 8555E, 8541E SEC 2.0
* 8349E SEC 2.01
* 8548E SEC 2.1
*
* The following table shows the features offered by each SEC version:
*
* Max. chan-
* version Bus I/F Clock nels DEU AESU AFEU MDEU PKEU RNG KEU
*
* SEC 1.0 internal 64b 100MHz 4 1 1 1 1 1 1 0
* SEC 1.2 internal 32b 66MHz 1 1 1 0 1 0 0 0
* SEC 2.0 internal 64b 166MHz 4 1 1 1 1 1 1 0
* SEC 2.01 internal 64b 166MHz 4 1 1 1 1 1 1 0
* SEC 2.1 internal 64b 333MHz 4 1 1 1 1 1 1 1
*
* Each execution unit in the SEC has two modes of execution; channel and
* slave/debug. This driver employs the channel infrastructure in the
* device for convenience. Only the RNG is directly accessed due to the
* convenience of its random fifo pool. The relationship between the
* channels and execution units is depicted in the following diagram:
*
* ------- ------------
* ---| ch0 |---| |
* ------- | |
* | |------+-------+-------+-------+------------
* ------- | | | | | | |
* ---| ch1 |---| | | | | | |
* ------- | | ------ ------ ------ ------ ------
* |controller| |DEU | |AESU| |MDEU| |PKEU| ... |RNG |
* ------- | | ------ ------ ------ ------ ------
* ---| ch2 |---| | | | | | |
* ------- | | | | | | |
* | |------+-------+-------+-------+------------
* ------- | |
* ---| ch3 |---| |
* ------- ------------
*
* Channel ch0 may drive an aes operation to the aes unit (AESU),
* and, at the same time, ch1 may drive a message digest operation
* to the mdeu. Each channel has an input descriptor FIFO, and the
* FIFO can contain, e.g. on the 8541E, up to 24 entries, before a
* a buffer overrun error is triggered. The controller is responsible
* for fetching the data from descriptor pointers, and passing the
* data to the appropriate EUs. The controller also writes the
* cryptographic operation's result to memory. The SEC notifies
* completion by triggering an interrupt and/or setting the 1st byte
* of the hdr field to 0xff.
*
* TODO:
* o support more algorithms
* o support more versions of the SEC
* o add support for linux 2.4
* o scatter-gather (sg) support
* o add support for public key ops (PKEU)
* o add statistics
*/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/skbuff.h>
#include <asm/scatterlist.h>
#include <linux/dma-mapping.h> /* dma_map_single() */
#include <linux/moduleparam.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
#include <linux/platform_device.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
#include <linux/of_platform.h>
#endif
#include <cryptodev.h>
#include <uio.h>
#define DRV_NAME "talitos"
#include "talitos_dev.h"
#include "talitos_soft.h"
#define read_random(p,l) get_random_bytes(p,l)
const char talitos_driver_name[] = "Talitos OCF";
const char talitos_driver_version[] = "0.2";
static int talitos_newsession(device_t dev, u_int32_t *sidp,
struct cryptoini *cri);
static int talitos_freesession(device_t dev, u_int64_t tid);
static int talitos_process(device_t dev, struct cryptop *crp, int hint);
static void dump_talitos_status(struct talitos_softc *sc);
static int talitos_submit(struct talitos_softc *sc, struct talitos_desc *td,
int chsel);
static void talitos_doneprocessing(struct talitos_softc *sc);
static void talitos_init_device(struct talitos_softc *sc);
static void talitos_reset_device_master(struct talitos_softc *sc);
static void talitos_reset_device(struct talitos_softc *sc);
static void talitos_errorprocessing(struct talitos_softc *sc);
#ifdef CONFIG_PPC_MERGE
static int talitos_probe(struct of_device *ofdev, const struct of_device_id *match);
static int talitos_remove(struct of_device *ofdev);
#else
static int talitos_probe(struct platform_device *pdev);
static int talitos_remove(struct platform_device *pdev);
#endif
#ifdef CONFIG_OCF_RANDOMHARVEST
static int talitos_read_random(void *arg, u_int32_t *buf, int maxwords);
static void talitos_rng_init(struct talitos_softc *sc);
#endif
static device_method_t talitos_methods = {
/* crypto device methods */
DEVMETHOD(cryptodev_newsession, talitos_newsession),
DEVMETHOD(cryptodev_freesession,talitos_freesession),
DEVMETHOD(cryptodev_process, talitos_process),
};
#define debug talitos_debug
int talitos_debug = 0;
module_param(talitos_debug, int, 0644);
MODULE_PARM_DESC(talitos_debug, "Enable debug");
static inline void talitos_write(volatile unsigned *addr, u32 val)
{
out_be32(addr, val);
}
static inline u32 talitos_read(volatile unsigned *addr)
{
u32 val;
val = in_be32(addr);
return val;
}
static void dump_talitos_status(struct talitos_softc *sc)
{
unsigned int v, v_hi, i, *ptr;
v = talitos_read(sc->sc_base_addr + TALITOS_MCR);
v_hi = talitos_read(sc->sc_base_addr + TALITOS_MCR_HI);
printk(KERN_INFO "%s: MCR 0x%08x_%08x\n",
device_get_nameunit(sc->sc_cdev), v, v_hi);
v = talitos_read(sc->sc_base_addr + TALITOS_IMR);
v_hi = talitos_read(sc->sc_base_addr + TALITOS_IMR_HI);
printk(KERN_INFO "%s: IMR 0x%08x_%08x\n",
device_get_nameunit(sc->sc_cdev), v, v_hi);
v = talitos_read(sc->sc_base_addr + TALITOS_ISR);
v_hi = talitos_read(sc->sc_base_addr + TALITOS_ISR_HI);
printk(KERN_INFO "%s: ISR 0x%08x_%08x\n",
device_get_nameunit(sc->sc_cdev), v, v_hi);
for (i = 0; i < sc->sc_num_channels; i++) {
v = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
TALITOS_CH_CDPR);
v_hi = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
TALITOS_CH_CDPR_HI);
printk(KERN_INFO "%s: CDPR ch%d 0x%08x_%08x\n",
device_get_nameunit(sc->sc_cdev), i, v, v_hi);
}
for (i = 0; i < sc->sc_num_channels; i++) {
v = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
TALITOS_CH_CCPSR);
v_hi = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
TALITOS_CH_CCPSR_HI);
printk(KERN_INFO "%s: CCPSR ch%d 0x%08x_%08x\n",
device_get_nameunit(sc->sc_cdev), i, v, v_hi);
}
ptr = sc->sc_base_addr + TALITOS_CH_DESCBUF;
for (i = 0; i < 16; i++) {
v = talitos_read(ptr++); v_hi = talitos_read(ptr++);
printk(KERN_INFO "%s: DESCBUF ch0 0x%08x_%08x (tdp%02d)\n",
device_get_nameunit(sc->sc_cdev), v, v_hi, i);
}
return;
}
#ifdef CONFIG_OCF_RANDOMHARVEST
/*
* pull random numbers off the RNG FIFO, not exceeding amount available
*/
static int
talitos_read_random(void *arg, u_int32_t *buf, int maxwords)
{
struct talitos_softc *sc = (struct talitos_softc *) arg;
int rc;
u_int32_t v;
DPRINTF("%s()\n", __FUNCTION__);
/* check for things like FIFO underflow */
v = talitos_read(sc->sc_base_addr + TALITOS_RNGISR_HI);
if (unlikely(v)) {
printk(KERN_ERR "%s: RNGISR_HI error %08x\n",
device_get_nameunit(sc->sc_cdev), v);
return 0;
}
/*
* OFL is number of available 64-bit words,
* shift and convert to a 32-bit word count
*/
v = talitos_read(sc->sc_base_addr + TALITOS_RNGSR_HI);
v = (v & TALITOS_RNGSR_HI_OFL) >> (16 - 1);
if (maxwords > v)
maxwords = v;
for (rc = 0; rc < maxwords; rc++) {
buf[rc] = talitos_read(sc->sc_base_addr +
TALITOS_RNG_FIFO + rc*sizeof(u_int32_t));
}
if (maxwords & 1) {
/*
* RNG will complain with an AE in the RNGISR
* if we don't complete the pairs of 32-bit reads
* to its 64-bit register based FIFO
*/
v = talitos_read(sc->sc_base_addr +
TALITOS_RNG_FIFO + rc*sizeof(u_int32_t));
}
return rc;
}
static void
talitos_rng_init(struct talitos_softc *sc)
{
u_int32_t v;
DPRINTF("%s()\n", __FUNCTION__);
/* reset RNG EU */
v = talitos_read(sc->sc_base_addr + TALITOS_RNGRCR_HI);
v |= TALITOS_RNGRCR_HI_SR;
talitos_write(sc->sc_base_addr + TALITOS_RNGRCR_HI, v);
while ((talitos_read(sc->sc_base_addr + TALITOS_RNGSR_HI)
& TALITOS_RNGSR_HI_RD) == 0)
cpu_relax();
/*
* we tell the RNG to start filling the RNG FIFO
* by writing the RNGDSR
*/
v = talitos_read(sc->sc_base_addr + TALITOS_RNGDSR_HI);
talitos_write(sc->sc_base_addr + TALITOS_RNGDSR_HI, v);
/*
* 64 bits of data will be pushed onto the FIFO every
* 256 SEC cycles until the FIFO is full. The RNG then
* attempts to keep the FIFO full.
*/
v = talitos_read(sc->sc_base_addr + TALITOS_RNGISR_HI);
if (v) {
printk(KERN_ERR "%s: RNGISR_HI error %08x\n",
device_get_nameunit(sc->sc_cdev), v);
return;
}
/*
* n.b. we need to add a FIPS test here - if the RNG is going
* to fail, it's going to fail at reset time
*/
return;
}
#endif /* CONFIG_OCF_RANDOMHARVEST */
/*
* Generate a new software session.
*/
static int
talitos_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri)
{
struct cryptoini *c, *encini = NULL, *macini = NULL;
struct talitos_softc *sc = device_get_softc(dev);
struct talitos_session *ses = NULL;
int sesn;
DPRINTF("%s()\n", __FUNCTION__);
if (sidp == NULL || cri == NULL || sc == NULL) {
DPRINTF("%s,%d - EINVAL\n", __FILE__, __LINE__);
return EINVAL;
}
for (c = cri; c != NULL; c = c->cri_next) {
if (c->cri_alg == CRYPTO_MD5 ||
c->cri_alg == CRYPTO_MD5_HMAC ||
c->cri_alg == CRYPTO_SHA1 ||
c->cri_alg == CRYPTO_SHA1_HMAC ||
c->cri_alg == CRYPTO_NULL_HMAC) {
if (macini)
return EINVAL;
macini = c;
} else if (c->cri_alg == CRYPTO_DES_CBC ||
c->cri_alg == CRYPTO_3DES_CBC ||
c->cri_alg == CRYPTO_AES_CBC ||
c->cri_alg == CRYPTO_NULL_CBC) {
if (encini)
return EINVAL;
encini = c;
} else {
DPRINTF("UNKNOWN c->cri_alg %d\n", encini->cri_alg);
return EINVAL;
}
}
if (encini == NULL && macini == NULL)
return EINVAL;
if (encini) {
/* validate key length */
switch (encini->cri_alg) {
case CRYPTO_DES_CBC:
if (encini->cri_klen != 64)
return EINVAL;
break;
case CRYPTO_3DES_CBC:
if (encini->cri_klen != 192) {
return EINVAL;
}
break;
case CRYPTO_AES_CBC:
if (encini->cri_klen != 128 &&
encini->cri_klen != 192 &&
encini->cri_klen != 256)
return EINVAL;
break;
default:
DPRINTF("UNKNOWN encini->cri_alg %d\n",
encini->cri_alg);
return EINVAL;
}
}
if (sc->sc_sessions == NULL) {
ses = sc->sc_sessions = (struct talitos_session *)
kmalloc(sizeof(struct talitos_session), SLAB_ATOMIC);
if (ses == NULL)
return ENOMEM;
memset(ses, 0, sizeof(struct talitos_session));
sesn = 0;
sc->sc_nsessions = 1;
} else {
for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
if (sc->sc_sessions[sesn].ses_used == 0) {
ses = &sc->sc_sessions[sesn];
break;
}
}
if (ses == NULL) {
/* allocating session */
sesn = sc->sc_nsessions;
ses = (struct talitos_session *) kmalloc(
(sesn + 1) * sizeof(struct talitos_session),
SLAB_ATOMIC);
if (ses == NULL)
return ENOMEM;
memset(ses, 0,
(sesn + 1) * sizeof(struct talitos_session));
memcpy(ses, sc->sc_sessions,
sesn * sizeof(struct talitos_session));
memset(sc->sc_sessions, 0,
sesn * sizeof(struct talitos_session));
kfree(sc->sc_sessions);
sc->sc_sessions = ses;
ses = &sc->sc_sessions[sesn];
sc->sc_nsessions++;
}
}
ses->ses_used = 1;
if (encini) {
/* get an IV */
/* XXX may read fewer than requested */
read_random(ses->ses_iv, sizeof(ses->ses_iv));
ses->ses_klen = (encini->cri_klen + 7) / 8;
memcpy(ses->ses_key, encini->cri_key, ses->ses_klen);
if (macini) {
/* doing hash on top of cipher */
ses->ses_hmac_len = (macini->cri_klen + 7) / 8;
memcpy(ses->ses_hmac, macini->cri_key,
ses->ses_hmac_len);
}
} else if (macini) {
/* doing hash */
ses->ses_klen = (macini->cri_klen + 7) / 8;
memcpy(ses->ses_key, macini->cri_key, ses->ses_klen);
}
/* back compat way of determining MSC result len */
if (macini) {
ses->ses_mlen = macini->cri_mlen;
if (ses->ses_mlen == 0) {
if (macini->cri_alg == CRYPTO_MD5_HMAC)
ses->ses_mlen = MD5_HASH_LEN;
else
ses->ses_mlen = SHA1_HASH_LEN;
}
}
/* really should make up a template td here,
* and only fill things like i/o and direction in process() */
/* assign session ID */
*sidp = TALITOS_SID(sc->sc_num, sesn);
return 0;
}
/*
* Deallocate a session.
*/
static int
talitos_freesession(device_t dev, u_int64_t tid)
{
struct talitos_softc *sc = device_get_softc(dev);
int session, ret;
u_int32_t sid = ((u_int32_t) tid) & 0xffffffff;
if (sc == NULL)
return EINVAL;
session = TALITOS_SESSION(sid);
if (session < sc->sc_nsessions) {
memset(&sc->sc_sessions[session], 0,
sizeof(sc->sc_sessions[session]));
ret = 0;
} else
ret = EINVAL;
return ret;
}
/*
* launch device processing - it will come back with done notification
* in the form of an interrupt and/or HDR_DONE_BITS in header
*/
static int
talitos_submit(
struct talitos_softc *sc,
struct talitos_desc *td,
int chsel)
{
u_int32_t v;
v = dma_map_single(NULL, td, sizeof(*td), DMA_TO_DEVICE);
talitos_write(sc->sc_base_addr +
chsel*TALITOS_CH_OFFSET + TALITOS_CH_FF, 0);
talitos_write(sc->sc_base_addr +
chsel*TALITOS_CH_OFFSET + TALITOS_CH_FF_HI, v);
return 0;
}
static int
talitos_process(device_t dev, struct cryptop *crp, int hint)
{
int i, err = 0, ivsize;
struct talitos_softc *sc = device_get_softc(dev);
struct cryptodesc *crd1, *crd2, *maccrd, *enccrd;
caddr_t iv;
struct talitos_session *ses;
struct talitos_desc *td;
unsigned long flags;
/* descriptor mappings */
int hmac_key, hmac_data, cipher_iv, cipher_key,
in_fifo, out_fifo, cipher_iv_out;
static int chsel = -1;
DPRINTF("%s()\n", __FUNCTION__);
if (crp == NULL || crp->crp_callback == NULL || sc == NULL) {
return EINVAL;
}
crp->crp_etype = 0;
if (TALITOS_SESSION(crp->crp_sid) >= sc->sc_nsessions) {
return EINVAL;
}
ses = &sc->sc_sessions[TALITOS_SESSION(crp->crp_sid)];
/* enter the channel scheduler */
spin_lock_irqsave(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
/* reuse channel that already had/has requests for the required EU */
for (i = 0; i < sc->sc_num_channels; i++) {
if (sc->sc_chnlastalg[i] == crp->crp_desc->crd_alg)
break;
}
if (i == sc->sc_num_channels) {
/*
* haven't seen this algo the last sc_num_channels or more
* use round robin in this case
* nb: sc->sc_num_channels must be power of 2
*/
chsel = (chsel + 1) & (sc->sc_num_channels - 1);
} else {
/*
* matches channel with same target execution unit;
* use same channel in this case
*/
chsel = i;
}
sc->sc_chnlastalg[chsel] = crp->crp_desc->crd_alg;
/* release the channel scheduler lock */
spin_unlock_irqrestore(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
/* acquire the selected channel fifo lock */
spin_lock_irqsave(&sc->sc_chnfifolock[chsel], flags);
/* find and reserve next available descriptor-cryptop pair */
for (i = 0; i < sc->sc_chfifo_len; i++) {
if (sc->sc_chnfifo[chsel][i].cf_desc.hdr == 0) {
/*
* ensure correct descriptor formation by
* avoiding inadvertently setting "optional" entries
* e.g. not using "optional" dptr2 for MD/HMAC descs
*/
memset(&sc->sc_chnfifo[chsel][i].cf_desc,
0, sizeof(*td));
/* reserve it with done notification request bit */
sc->sc_chnfifo[chsel][i].cf_desc.hdr |=
TALITOS_DONE_NOTIFY;
break;
}
}
spin_unlock_irqrestore(&sc->sc_chnfifolock[chsel], flags);
if (i == sc->sc_chfifo_len) {
/* fifo full */
err = ERESTART;
goto errout;
}
td = &sc->sc_chnfifo[chsel][i].cf_desc;
sc->sc_chnfifo[chsel][i].cf_crp = crp;
crd1 = crp->crp_desc;
if (crd1 == NULL) {
err = EINVAL;
goto errout;
}
crd2 = crd1->crd_next;
/* prevent compiler warning */
hmac_key = 0;
hmac_data = 0;
if (crd2 == NULL) {
td->hdr |= TD_TYPE_COMMON_NONSNOOP_NO_AFEU;
/* assign descriptor dword ptr mappings for this desc. type */
cipher_iv = 1;
cipher_key = 2;
in_fifo = 3;
cipher_iv_out = 5;
if (crd1->crd_alg == CRYPTO_MD5_HMAC ||
crd1->crd_alg == CRYPTO_SHA1_HMAC ||
crd1->crd_alg == CRYPTO_SHA1 ||
crd1->crd_alg == CRYPTO_MD5) {
out_fifo = 5;
maccrd = crd1;
enccrd = NULL;
} else if (crd1->crd_alg == CRYPTO_DES_CBC ||
crd1->crd_alg == CRYPTO_3DES_CBC ||
crd1->crd_alg == CRYPTO_AES_CBC ||
crd1->crd_alg == CRYPTO_ARC4) {
out_fifo = 4;
maccrd = NULL;
enccrd = crd1;
} else {
DPRINTF("UNKNOWN crd1->crd_alg %d\n", crd1->crd_alg);
err = EINVAL;
goto errout;
}
} else {
if (sc->sc_desc_types & TALITOS_HAS_DT_IPSEC_ESP) {
td->hdr |= TD_TYPE_IPSEC_ESP;
} else {
DPRINTF("unimplemented: multiple descriptor ipsec\n");
err = EINVAL;
goto errout;
}
/* assign descriptor dword ptr mappings for this desc. type */
hmac_key = 0;
hmac_data = 1;
cipher_iv = 2;
cipher_key = 3;
in_fifo = 4;
out_fifo = 5;
cipher_iv_out = 6;
if ((crd1->crd_alg == CRYPTO_MD5_HMAC ||
crd1->crd_alg == CRYPTO_SHA1_HMAC ||
crd1->crd_alg == CRYPTO_MD5 ||
crd1->crd_alg == CRYPTO_SHA1) &&
(crd2->crd_alg == CRYPTO_DES_CBC ||
crd2->crd_alg == CRYPTO_3DES_CBC ||
crd2->crd_alg == CRYPTO_AES_CBC ||
crd2->crd_alg == CRYPTO_ARC4) &&
((crd2->crd_flags & CRD_F_ENCRYPT) == 0)) {
maccrd = crd1;
enccrd = crd2;
} else if ((crd1->crd_alg == CRYPTO_DES_CBC ||
crd1->crd_alg == CRYPTO_ARC4 ||
crd1->crd_alg == CRYPTO_3DES_CBC ||
crd1->crd_alg == CRYPTO_AES_CBC) &&
(crd2->crd_alg == CRYPTO_MD5_HMAC ||
crd2->crd_alg == CRYPTO_SHA1_HMAC ||
crd2->crd_alg == CRYPTO_MD5 ||
crd2->crd_alg == CRYPTO_SHA1) &&
(crd1->crd_flags & CRD_F_ENCRYPT)) {
enccrd = crd1;
maccrd = crd2;
} else {
/* We cannot order the SEC as requested */
printk("%s: cannot do the order\n",
device_get_nameunit(sc->sc_cdev));
err = EINVAL;
goto errout;
}
}
/* assign in_fifo and out_fifo based on input/output struct type */
if (crp->crp_flags & CRYPTO_F_SKBUF) {
/* using SKB buffers */
struct sk_buff *skb = (struct sk_buff *)crp->crp_buf;
if (skb_shinfo(skb)->nr_frags) {
printk("%s: skb frags unimplemented\n",
device_get_nameunit(sc->sc_cdev));
err = EINVAL;
goto errout;
}
td->ptr[in_fifo].ptr = dma_map_single(NULL, skb->data,
skb->len, DMA_TO_DEVICE);
td->ptr[in_fifo].len = skb->len;
td->ptr[out_fifo].ptr = dma_map_single(NULL, skb->data,
skb->len, DMA_TO_DEVICE);
td->ptr[out_fifo].len = skb->len;
td->ptr[hmac_data].ptr = dma_map_single(NULL, skb->data,
skb->len, DMA_TO_DEVICE);
} else if (crp->crp_flags & CRYPTO_F_IOV) {
/* using IOV buffers */
struct uio *uiop = (struct uio *)crp->crp_buf;
if (uiop->uio_iovcnt > 1) {
printk("%s: iov frags unimplemented\n",
device_get_nameunit(sc->sc_cdev));
err = EINVAL;
goto errout;
}
td->ptr[in_fifo].ptr = dma_map_single(NULL,
uiop->uio_iov->iov_base, crp->crp_ilen, DMA_TO_DEVICE);
td->ptr[in_fifo].len = crp->crp_ilen;
/* crp_olen is never set; always use crp_ilen */
td->ptr[out_fifo].ptr = dma_map_single(NULL,
uiop->uio_iov->iov_base,
crp->crp_ilen, DMA_TO_DEVICE);
td->ptr[out_fifo].len = crp->crp_ilen;
} else {
/* using contig buffers */
td->ptr[in_fifo].ptr = dma_map_single(NULL,
crp->crp_buf, crp->crp_ilen, DMA_TO_DEVICE);
td->ptr[in_fifo].len = crp->crp_ilen;
td->ptr[out_fifo].ptr = dma_map_single(NULL,
crp->crp_buf, crp->crp_ilen, DMA_TO_DEVICE);
td->ptr[out_fifo].len = crp->crp_ilen;
}
if (enccrd) {
switch (enccrd->crd_alg) {
case CRYPTO_3DES_CBC:
td->hdr |= TALITOS_MODE0_DEU_3DES;
/* FALLTHROUGH */
case CRYPTO_DES_CBC:
td->hdr |= TALITOS_SEL0_DEU
| TALITOS_MODE0_DEU_CBC;
if (enccrd->crd_flags & CRD_F_ENCRYPT)
td->hdr |= TALITOS_MODE0_DEU_ENC;
ivsize = 2*sizeof(u_int32_t);
DPRINTF("%cDES ses %d ch %d len %d\n",
(td->hdr & TALITOS_MODE0_DEU_3DES)?'3':'1',
(u32)TALITOS_SESSION(crp->crp_sid),
chsel, td->ptr[in_fifo].len);
break;
case CRYPTO_AES_CBC:
td->hdr |= TALITOS_SEL0_AESU
| TALITOS_MODE0_AESU_CBC;
if (enccrd->crd_flags & CRD_F_ENCRYPT)
td->hdr |= TALITOS_MODE0_AESU_ENC;
ivsize = 4*sizeof(u_int32_t);
DPRINTF("AES ses %d ch %d len %d\n",
(u32)TALITOS_SESSION(crp->crp_sid),
chsel, td->ptr[in_fifo].len);
break;
default:
printk("%s: unimplemented enccrd->crd_alg %d\n",
device_get_nameunit(sc->sc_cdev), enccrd->crd_alg);
err = EINVAL;
goto errout;
}
/*
* Setup encrypt/decrypt state. When using basic ops
* we can't use an inline IV because hash/crypt offset
* must be from the end of the IV to the start of the
* crypt data and this leaves out the preceding header
* from the hash calculation. Instead we place the IV
* in the state record and set the hash/crypt offset to
* copy both the header+IV.
*/
if (enccrd->crd_flags & CRD_F_ENCRYPT) {
td->hdr |= TALITOS_DIR_OUTBOUND;
if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
iv = enccrd->crd_iv;
else
iv = (caddr_t) ses->ses_iv;
if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) {
crypto_copyback(crp->crp_flags, crp->crp_buf,
enccrd->crd_inject, ivsize, iv);
}
} else {
td->hdr |= TALITOS_DIR_INBOUND;
if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) {
iv = enccrd->crd_iv;
bcopy(enccrd->crd_iv, iv, ivsize);
} else {
iv = (caddr_t) ses->ses_iv;
crypto_copydata(crp->crp_flags, crp->crp_buf,
enccrd->crd_inject, ivsize, iv);
}
}
td->ptr[cipher_iv].ptr = dma_map_single(NULL, iv, ivsize,
DMA_TO_DEVICE);
td->ptr[cipher_iv].len = ivsize;
/*
* we don't need the cipher iv out length/pointer
* field to do ESP IPsec. Therefore we set the len field as 0,
* which tells the SEC not to do anything with this len/ptr
* field. Previously, when length/pointer as pointing to iv,
* it gave us corruption of packets.
*/
td->ptr[cipher_iv_out].len = 0;
}
if (enccrd && maccrd) {
/* this is ipsec only for now */
td->hdr |= TALITOS_SEL1_MDEU
| TALITOS_MODE1_MDEU_INIT
| TALITOS_MODE1_MDEU_PAD;
switch (maccrd->crd_alg) {
case CRYPTO_MD5:
td->hdr |= TALITOS_MODE1_MDEU_MD5;
break;
case CRYPTO_MD5_HMAC:
td->hdr |= TALITOS_MODE1_MDEU_MD5_HMAC;
break;
case CRYPTO_SHA1:
td->hdr |= TALITOS_MODE1_MDEU_SHA1;
break;
case CRYPTO_SHA1_HMAC:
td->hdr |= TALITOS_MODE1_MDEU_SHA1_HMAC;
break;
default:
/* We cannot order the SEC as requested */
printk("%s: cannot do the order\n",
device_get_nameunit(sc->sc_cdev));
err = EINVAL;
goto errout;
}
if ((maccrd->crd_alg == CRYPTO_MD5_HMAC) ||
(maccrd->crd_alg == CRYPTO_SHA1_HMAC)) {
/*
* The offset from hash data to the start of
* crypt data is the difference in the skips.
*/
/* ipsec only for now */
td->ptr[hmac_key].ptr = dma_map_single(NULL,
ses->ses_hmac, ses->ses_hmac_len, DMA_TO_DEVICE);
td->ptr[hmac_key].len = ses->ses_hmac_len;
td->ptr[in_fifo].ptr += enccrd->crd_skip;
td->ptr[in_fifo].len = enccrd->crd_len;
td->ptr[out_fifo].ptr += enccrd->crd_skip;
td->ptr[out_fifo].len = enccrd->crd_len;
/* bytes of HMAC to postpend to ciphertext */
td->ptr[out_fifo].extent = ses->ses_mlen;
td->ptr[hmac_data].ptr += maccrd->crd_skip;
td->ptr[hmac_data].len = enccrd->crd_skip - maccrd->crd_skip;
}
if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
printk("%s: CRD_F_KEY_EXPLICIT unimplemented\n",
device_get_nameunit(sc->sc_cdev));
}
}
if (!enccrd && maccrd) {
/* single MD5 or SHA */
td->hdr |= TALITOS_SEL0_MDEU
| TALITOS_MODE0_MDEU_INIT
| TALITOS_MODE0_MDEU_PAD;
switch (maccrd->crd_alg) {
case CRYPTO_MD5:
td->hdr |= TALITOS_MODE0_MDEU_MD5;
DPRINTF("MD5 ses %d ch %d len %d\n",
(u32)TALITOS_SESSION(crp->crp_sid),
chsel, td->ptr[in_fifo].len);
break;
case CRYPTO_MD5_HMAC:
td->hdr |= TALITOS_MODE0_MDEU_MD5_HMAC;
break;
case CRYPTO_SHA1:
td->hdr |= TALITOS_MODE0_MDEU_SHA1;
DPRINTF("SHA1 ses %d ch %d len %d\n",
(u32)TALITOS_SESSION(crp->crp_sid),
chsel, td->ptr[in_fifo].len);
break;
case CRYPTO_SHA1_HMAC:
td->hdr |= TALITOS_MODE0_MDEU_SHA1_HMAC;
break;
default:
/* We cannot order the SEC as requested */
DPRINTF("cannot do the order\n");
err = EINVAL;
goto errout;
}
if (crp->crp_flags & CRYPTO_F_IOV)
td->ptr[out_fifo].ptr += maccrd->crd_inject;
if ((maccrd->crd_alg == CRYPTO_MD5_HMAC) ||
(maccrd->crd_alg == CRYPTO_SHA1_HMAC)) {
td->ptr[hmac_key].ptr = dma_map_single(NULL,
ses->ses_hmac, ses->ses_hmac_len,
DMA_TO_DEVICE);
td->ptr[hmac_key].len = ses->ses_hmac_len;
}
}
else {
/* using process key (session data has duplicate) */
td->ptr[cipher_key].ptr = dma_map_single(NULL,
enccrd->crd_key, (enccrd->crd_klen + 7) / 8,
DMA_TO_DEVICE);
td->ptr[cipher_key].len = (enccrd->crd_klen + 7) / 8;
}
/* descriptor complete - GO! */
return talitos_submit(sc, td, chsel);
errout:
if (err != ERESTART) {
crp->crp_etype = err;
crypto_done(crp);
}
return err;
}
/* go through all channels descriptors, notifying OCF what has
* _and_hasn't_ successfully completed and reset the device
* (otherwise it's up to decoding desc hdrs!)
*/
static void talitos_errorprocessing(struct talitos_softc *sc)
{
unsigned long flags;
int i, j;
/* disable further scheduling until under control */
spin_lock_irqsave(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
if (debug) dump_talitos_status(sc);
/* go through descriptors, try and salvage those successfully done,
* and EIO those that weren't
*/
for (i = 0; i < sc->sc_num_channels; i++) {
spin_lock_irqsave(&sc->sc_chnfifolock[i], flags);
for (j = 0; j < sc->sc_chfifo_len; j++) {
if (sc->sc_chnfifo[i][j].cf_desc.hdr) {
if ((sc->sc_chnfifo[i][j].cf_desc.hdr
& TALITOS_HDR_DONE_BITS)
!= TALITOS_HDR_DONE_BITS) {
/* this one didn't finish */
/* signify in crp->etype */
sc->sc_chnfifo[i][j].cf_crp->crp_etype
= EIO;
}
} else
continue; /* free entry */
/* either way, notify ocf */
crypto_done(sc->sc_chnfifo[i][j].cf_crp);
/* and tag it available again
*
* memset to ensure correct descriptor formation by
* avoiding inadvertently setting "optional" entries
* e.g. not using "optional" dptr2 MD/HMAC processing
*/
memset(&sc->sc_chnfifo[i][j].cf_desc,
0, sizeof(struct talitos_desc));
}
spin_unlock_irqrestore(&sc->sc_chnfifolock[i], flags);
}
/* reset and initialize the SEC h/w device */
talitos_reset_device(sc);
talitos_init_device(sc);
#ifdef CONFIG_OCF_RANDOMHARVEST
if (sc->sc_exec_units & TALITOS_HAS_EU_RNG)
talitos_rng_init(sc);
#endif
/* Okay. Stand by. */
spin_unlock_irqrestore(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
return;
}
/* go through all channels descriptors, notifying OCF what's been done */
static void talitos_doneprocessing(struct talitos_softc *sc)
{
unsigned long flags;
int i, j;
/* go through descriptors looking for done bits */
for (i = 0; i < sc->sc_num_channels; i++) {
spin_lock_irqsave(&sc->sc_chnfifolock[i], flags);
for (j = 0; j < sc->sc_chfifo_len; j++) {
/* descriptor has done bits set? */
if ((sc->sc_chnfifo[i][j].cf_desc.hdr
& TALITOS_HDR_DONE_BITS)
== TALITOS_HDR_DONE_BITS) {
/* notify ocf */
crypto_done(sc->sc_chnfifo[i][j].cf_crp);
/* and tag it available again
*
* memset to ensure correct descriptor formation by
* avoiding inadvertently setting "optional" entries
* e.g. not using "optional" dptr2 MD/HMAC processing
*/
memset(&sc->sc_chnfifo[i][j].cf_desc,
0, sizeof(struct talitos_desc));
}
}
spin_unlock_irqrestore(&sc->sc_chnfifolock[i], flags);
}
return;
}
static irqreturn_t
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
talitos_intr(int irq, void *arg)
#else
talitos_intr(int irq, void *arg, struct pt_regs *regs)
#endif
{
struct talitos_softc *sc = arg;
u_int32_t v, v_hi;
/* ack */
v = talitos_read(sc->sc_base_addr + TALITOS_ISR);
v_hi = talitos_read(sc->sc_base_addr + TALITOS_ISR_HI);
talitos_write(sc->sc_base_addr + TALITOS_ICR, v);
talitos_write(sc->sc_base_addr + TALITOS_ICR_HI, v_hi);
if (unlikely(v & TALITOS_ISR_ERROR)) {
/* Okay, Houston, we've had a problem here. */
printk(KERN_DEBUG "%s: got error interrupt - ISR 0x%08x_%08x\n",
device_get_nameunit(sc->sc_cdev), v, v_hi);
talitos_errorprocessing(sc);
} else
if (likely(v & TALITOS_ISR_DONE)) {
talitos_doneprocessing(sc);
}
return IRQ_HANDLED;
}
/*
* Initialize registers we need to touch only once.
*/
static void
talitos_init_device(struct talitos_softc *sc)
{
u_int32_t v;
int i;
DPRINTF("%s()\n", __FUNCTION__);
/* init all channels */
for (i = 0; i < sc->sc_num_channels; i++) {
v = talitos_read(sc->sc_base_addr +
i*TALITOS_CH_OFFSET + TALITOS_CH_CCCR_HI);
v |= TALITOS_CH_CCCR_HI_CDWE
| TALITOS_CH_CCCR_HI_CDIE; /* invoke interrupt if done */
talitos_write(sc->sc_base_addr +
i*TALITOS_CH_OFFSET + TALITOS_CH_CCCR_HI, v);
}
/* enable all interrupts */
v = talitos_read(sc->sc_base_addr + TALITOS_IMR);
v |= TALITOS_IMR_ALL;
talitos_write(sc->sc_base_addr + TALITOS_IMR, v);
v = talitos_read(sc->sc_base_addr + TALITOS_IMR_HI);
v |= TALITOS_IMR_HI_ERRONLY;
talitos_write(sc->sc_base_addr + TALITOS_IMR_HI, v);
return;
}
/*
* set the master reset bit on the device.
*/
static void
talitos_reset_device_master(struct talitos_softc *sc)
{
u_int32_t v;
/* Reset the device by writing 1 to MCR:SWR and waiting 'til cleared */
v = talitos_read(sc->sc_base_addr + TALITOS_MCR);
talitos_write(sc->sc_base_addr + TALITOS_MCR, v | TALITOS_MCR_SWR);
while (talitos_read(sc->sc_base_addr + TALITOS_MCR) & TALITOS_MCR_SWR)
cpu_relax();
return;
}
/*
* Resets the device. Values in the registers are left as is
* from the reset (i.e. initial values are assigned elsewhere).
*/
static void
talitos_reset_device(struct talitos_softc *sc)
{
u_int32_t v;
int i;
DPRINTF("%s()\n", __FUNCTION__);
/*
* Master reset
* errata documentation: warning: certain SEC interrupts
* are not fully cleared by writing the MCR:SWR bit,
* set bit twice to completely reset
*/
talitos_reset_device_master(sc); /* once */
talitos_reset_device_master(sc); /* and once again */
/* reset all channels */
for (i = 0; i < sc->sc_num_channels; i++) {
v = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
TALITOS_CH_CCCR);
talitos_write(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
TALITOS_CH_CCCR, v | TALITOS_CH_CCCR_RESET);
}
}
/* Set up the crypto device structure, private data,
* and anything else we need before we start */
#ifdef CONFIG_PPC_MERGE
static int talitos_probe(struct of_device *ofdev, const struct of_device_id *match)
#else
static int talitos_probe(struct platform_device *pdev)
#endif
{
struct talitos_softc *sc = NULL;
struct resource *r;
#ifdef CONFIG_PPC_MERGE
struct device *device = &ofdev->dev;
struct device_node *np = ofdev->node;
const unsigned int *prop;
int err;
struct resource res;
#endif
static int num_chips = 0;
int rc;
int i;
DPRINTF("%s()\n", __FUNCTION__);
sc = (struct talitos_softc *) kmalloc(sizeof(*sc), GFP_KERNEL);
if (!sc)
return -ENOMEM;
memset(sc, 0, sizeof(*sc));
softc_device_init(sc, DRV_NAME, num_chips, talitos_methods);
sc->sc_irq = -1;
sc->sc_cid = -1;
#ifndef CONFIG_PPC_MERGE
sc->sc_dev = pdev;
#endif
sc->sc_num = num_chips++;
#ifdef CONFIG_PPC_MERGE
dev_set_drvdata(device, sc);
#else
platform_set_drvdata(sc->sc_dev, sc);
#endif
/* get the irq line */
#ifdef CONFIG_PPC_MERGE
err = of_address_to_resource(np, 0, &res);
if (err)
return -EINVAL;
r = &res;
sc->sc_irq = irq_of_parse_and_map(np, 0);
#else
/* get a pointer to the register memory */
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sc->sc_irq = platform_get_irq(pdev, 0);
#endif
rc = request_irq(sc->sc_irq, talitos_intr, 0,
device_get_nameunit(sc->sc_cdev), sc);
if (rc) {
printk(KERN_ERR "%s: failed to hook irq %d\n",
device_get_nameunit(sc->sc_cdev), sc->sc_irq);
sc->sc_irq = -1;
goto out;
}
sc->sc_base_addr = (ocf_iomem_t) ioremap(r->start, (r->end - r->start));
if (!sc->sc_base_addr) {
printk(KERN_ERR "%s: failed to ioremap\n",
device_get_nameunit(sc->sc_cdev));
goto out;
}
/* figure out our SEC's properties and capabilities */
sc->sc_chiprev = (u64)talitos_read(sc->sc_base_addr + TALITOS_ID) << 32
| talitos_read(sc->sc_base_addr + TALITOS_ID_HI);
DPRINTF("sec id 0x%llx\n", sc->sc_chiprev);
#ifdef CONFIG_PPC_MERGE
/* get SEC properties from device tree, defaulting to SEC 2.0 */
prop = of_get_property(np, "num-channels", NULL);
sc->sc_num_channels = prop ? *prop : TALITOS_NCHANNELS_SEC_2_0;
prop = of_get_property(np, "channel-fifo-len", NULL);
sc->sc_chfifo_len = prop ? *prop : TALITOS_CHFIFOLEN_SEC_2_0;
prop = of_get_property(np, "exec-units-mask", NULL);
sc->sc_exec_units = prop ? *prop : TALITOS_HAS_EUS_SEC_2_0;
prop = of_get_property(np, "descriptor-types-mask", NULL);
sc->sc_desc_types = prop ? *prop : TALITOS_HAS_DESCTYPES_SEC_2_0;
#else
/* bulk should go away with openfirmware flat device tree support */
if (sc->sc_chiprev & TALITOS_ID_SEC_2_0) {
sc->sc_num_channels = TALITOS_NCHANNELS_SEC_2_0;
sc->sc_chfifo_len = TALITOS_CHFIFOLEN_SEC_2_0;
sc->sc_exec_units = TALITOS_HAS_EUS_SEC_2_0;
sc->sc_desc_types = TALITOS_HAS_DESCTYPES_SEC_2_0;
} else {
printk(KERN_ERR "%s: failed to id device\n",
device_get_nameunit(sc->sc_cdev));
goto out;
}
#endif
/* + 1 is for the meta-channel lock used by the channel scheduler */
sc->sc_chnfifolock = (spinlock_t *) kmalloc(
(sc->sc_num_channels + 1) * sizeof(spinlock_t), GFP_KERNEL);
if (!sc->sc_chnfifolock)
goto out;
for (i = 0; i < sc->sc_num_channels + 1; i++) {
spin_lock_init(&sc->sc_chnfifolock[i]);
}
sc->sc_chnlastalg = (int *) kmalloc(
sc->sc_num_channels * sizeof(int), GFP_KERNEL);
if (!sc->sc_chnlastalg)
goto out;
memset(sc->sc_chnlastalg, 0, sc->sc_num_channels * sizeof(int));
sc->sc_chnfifo = (struct desc_cryptop_pair **) kmalloc(
sc->sc_num_channels * sizeof(struct desc_cryptop_pair *),
GFP_KERNEL);
if (!sc->sc_chnfifo)
goto out;
for (i = 0; i < sc->sc_num_channels; i++) {
sc->sc_chnfifo[i] = (struct desc_cryptop_pair *) kmalloc(
sc->sc_chfifo_len * sizeof(struct desc_cryptop_pair),
GFP_KERNEL);
if (!sc->sc_chnfifo[i])
goto out;
memset(sc->sc_chnfifo[i], 0,
sc->sc_chfifo_len * sizeof(struct desc_cryptop_pair));
}
/* reset and initialize the SEC h/w device */
talitos_reset_device(sc);
talitos_init_device(sc);
sc->sc_cid = crypto_get_driverid(softc_get_device(sc),CRYPTOCAP_F_HARDWARE);
if (sc->sc_cid < 0) {
printk(KERN_ERR "%s: could not get crypto driver id\n",
device_get_nameunit(sc->sc_cdev));
goto out;
}
/* register algorithms with the framework */
printk("%s:", device_get_nameunit(sc->sc_cdev));
if (sc->sc_exec_units & TALITOS_HAS_EU_RNG) {
printk(" rng");
#ifdef CONFIG_OCF_RANDOMHARVEST
talitos_rng_init(sc);
crypto_rregister(sc->sc_cid, talitos_read_random, sc);
#endif
}
if (sc->sc_exec_units & TALITOS_HAS_EU_DEU) {
printk(" des/3des");
crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
}
if (sc->sc_exec_units & TALITOS_HAS_EU_AESU) {
printk(" aes");
crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
}
if (sc->sc_exec_units & TALITOS_HAS_EU_MDEU) {
printk(" md5");
crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0);
/* HMAC support only with IPsec for now */
crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
printk(" sha1");
crypto_register(sc->sc_cid, CRYPTO_SHA1, 0, 0);
/* HMAC support only with IPsec for now */
crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
}
printk("\n");
return 0;
out:
#ifndef CONFIG_PPC_MERGE
talitos_remove(pdev);
#endif
return -ENOMEM;
}
#ifdef CONFIG_PPC_MERGE
static int talitos_remove(struct of_device *ofdev)
#else
static int talitos_remove(struct platform_device *pdev)
#endif
{
#ifdef CONFIG_PPC_MERGE
struct talitos_softc *sc = dev_get_drvdata(&ofdev->dev);
#else
struct talitos_softc *sc = platform_get_drvdata(pdev);
#endif
int i;
DPRINTF("%s()\n", __FUNCTION__);
if (sc->sc_cid >= 0)
crypto_unregister_all(sc->sc_cid);
if (sc->sc_chnfifo) {
for (i = 0; i < sc->sc_num_channels; i++)
if (sc->sc_chnfifo[i])
kfree(sc->sc_chnfifo[i]);
kfree(sc->sc_chnfifo);
}
if (sc->sc_chnlastalg)
kfree(sc->sc_chnlastalg);
if (sc->sc_chnfifolock)
kfree(sc->sc_chnfifolock);
if (sc->sc_irq != -1)
free_irq(sc->sc_irq, sc);
if (sc->sc_base_addr)
iounmap((void *) sc->sc_base_addr);
kfree(sc);
return 0;
}
#ifdef CONFIG_PPC_MERGE
static struct of_device_id talitos_match[] = {
{
.type = "crypto",
.compatible = "talitos",
},
{},
};
MODULE_DEVICE_TABLE(of, talitos_match);
static struct of_platform_driver talitos_driver = {
.name = DRV_NAME,
.match_table = talitos_match,
.probe = talitos_probe,
.remove = talitos_remove,
};
static int __init talitos_init(void)
{
return of_register_platform_driver(&talitos_driver);
}
static void __exit talitos_exit(void)
{
of_unregister_platform_driver(&talitos_driver);
}
#else
/* Structure for a platform device driver */
static struct platform_driver talitos_driver = {
.probe = talitos_probe,
.remove = talitos_remove,
.driver = {
.name = "fsl-sec2",
}
};
static int __init talitos_init(void)
{
return platform_driver_register(&talitos_driver);
}
static void __exit talitos_exit(void)
{
platform_driver_unregister(&talitos_driver);
}
#endif
module_init(talitos_init);
module_exit(talitos_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("kim.phillips@freescale.com");
MODULE_DESCRIPTION("OCF driver for Freescale SEC (talitos)");