| /* |
| * 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)"); |