| /******************************************************************************* |
| Copyright (C) Marvell International Ltd. and its affiliates |
| |
| This software file (the "File") is owned and distributed by Marvell |
| International Ltd. and/or its affiliates ("Marvell") under the following |
| alternative licensing terms. Once you have made an election to distribute the |
| File under one of the following license alternatives, please (i) delete this |
| introductory statement regarding license alternatives, (ii) delete the two |
| license alternatives that you have not elected to use and (iii) preserve the |
| Marvell copyright notice above. |
| |
| |
| ******************************************************************************** |
| Marvell GPL License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File in accordance with the terms and conditions of the General |
| Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| available along with the File in the license.txt file or by writing to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| |
| THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| DISCLAIMED. The GPL License provides additional details about this warranty |
| disclaimer. |
| *******************************************************************************/ |
| |
| #include <linux/version.h> |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) && !defined(AUTOCONF_INCLUDED) |
| #include <linux/config.h> |
| #endif |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/list.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| #include <linux/wait.h> |
| #include <linux/crypto.h> |
| #include <linux/mm.h> |
| #include <linux/skbuff.h> |
| #include <linux/random.h> |
| #include <linux/platform_device.h> |
| #include <asm/scatterlist.h> |
| #include <linux/spinlock.h> |
| #include "ctrlEnv/sys/mvSysCesa.h" |
| #include "cesa/mvCesa.h" /* moved here before cryptodev.h due to include dependencies */ |
| #include <cryptodev.h> |
| #include <uio.h> |
| #include <plat/mv_cesa.h> |
| #include <linux/mbus.h> |
| #include "mvDebug.h" |
| |
| #include "cesa/mvMD5.h" |
| #include "cesa/mvSHA1.h" |
| |
| #include "cesa/mvCesaRegs.h" |
| #include "cesa/AES/mvAes.h" |
| #include "cesa/mvLru.h" |
| |
| #undef RT_DEBUG |
| #ifdef RT_DEBUG |
| static int debug = 1; |
| module_param(debug, int, 1); |
| MODULE_PARM_DESC(debug, "Enable debug"); |
| #undef dprintk |
| #define dprintk(a...) if (debug) { printk(a); } else |
| #else |
| static int debug = 0; |
| #undef dprintk |
| #define dprintk(a...) |
| #endif |
| |
| |
| /* TDMA Regs */ |
| #define WINDOW_BASE(i) 0xA00 + (i << 3) |
| #define WINDOW_CTRL(i) 0xA04 + (i << 3) |
| |
| /* interrupt handling */ |
| #undef CESA_OCF_POLLING |
| #undef CESA_OCF_TASKLET |
| |
| #if defined(CESA_OCF_POLLING) && defined(CESA_OCF_TASKLET) |
| #error "don't use both tasklet and polling mode" |
| #endif |
| |
| extern int cesaReqResources; |
| /* support for spliting action into 2 actions */ |
| #define CESA_OCF_SPLIT |
| |
| /* general defines */ |
| #define CESA_OCF_MAX_SES 128 |
| #define CESA_Q_SIZE 64 |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) |
| #define FRAG_PAGE(f) (f).p |
| #else |
| #define FRAG_PAGE(f) (f) |
| #endif |
| |
| /* data structures */ |
| struct cesa_ocf_data { |
| int cipher_alg; |
| int auth_alg; |
| int encrypt_tn_auth; |
| #define auth_tn_decrypt encrypt_tn_auth |
| int ivlen; |
| int digestlen; |
| short sid_encrypt; |
| short sid_decrypt; |
| /* fragment workaround sessions */ |
| short frag_wa_encrypt; |
| short frag_wa_decrypt; |
| short frag_wa_auth; |
| }; |
| |
| /* CESA device data */ |
| struct cesa_dev { |
| void __iomem *sram; |
| void __iomem *reg; |
| struct mv_cesa_platform_data *plat_data; |
| int irq; |
| }; |
| |
| #define DIGEST_BUF_SIZE 32 |
| struct cesa_ocf_process { |
| MV_CESA_COMMAND cesa_cmd; |
| MV_CESA_MBUF cesa_mbuf; |
| MV_BUF_INFO cesa_bufs[MV_CESA_MAX_MBUF_FRAGS]; |
| char digest[DIGEST_BUF_SIZE]; |
| int digest_len; |
| struct cryptop *crp; |
| int need_cb; |
| }; |
| |
| /* global variables */ |
| static int32_t cesa_ocf_id = -1; |
| static struct cesa_ocf_data *cesa_ocf_sessions[CESA_OCF_MAX_SES]; |
| static spinlock_t cesa_lock; |
| static struct cesa_dev cesa_device; |
| |
| /* static APIs */ |
| static int cesa_ocf_process (device_t, struct cryptop *, int); |
| static int cesa_ocf_newsession (device_t, u_int32_t *, struct cryptoini *); |
| static int cesa_ocf_freesession (device_t, u_int64_t); |
| static void cesa_callback (unsigned long); |
| static irqreturn_t cesa_interrupt_handler (int, void *); |
| #ifdef CESA_OCF_POLLING |
| static void cesa_interrupt_polling(void); |
| #endif |
| #ifdef CESA_OCF_TASKLET |
| static struct tasklet_struct cesa_ocf_tasklet; |
| #endif |
| |
| static struct timeval tt_start; |
| static struct timeval tt_end; |
| |
| /* |
| * dummy device structure |
| */ |
| |
| static struct { |
| softc_device_decl sc_dev; |
| } mv_cesa_dev; |
| |
| static device_method_t mv_cesa_methods = { |
| /* crypto device methods */ |
| DEVMETHOD(cryptodev_newsession, cesa_ocf_newsession), |
| DEVMETHOD(cryptodev_freesession,cesa_ocf_freesession), |
| DEVMETHOD(cryptodev_process, cesa_ocf_process), |
| DEVMETHOD(cryptodev_kprocess, NULL), |
| }; |
| |
| |
| |
| /* Add debug Trace */ |
| #undef CESA_OCF_TRACE_DEBUG |
| #ifdef CESA_OCF_TRACE_DEBUG |
| |
| #define MV_CESA_USE_TIMER_ID 0 |
| |
| typedef struct |
| { |
| int type; /* 0 - isrEmpty, 1 - cesaReadyGet, 2 - cesaAction */ |
| MV_U32 timeStamp; |
| MV_U32 cause; |
| MV_U32 realCause; |
| MV_U32 dmaCause; |
| int resources; |
| MV_CESA_REQ* pReqReady; |
| MV_CESA_REQ* pReqEmpty; |
| MV_CESA_REQ* pReqProcess; |
| } MV_CESA_TEST_TRACE; |
| |
| #define MV_CESA_TEST_TRACE_SIZE 50 |
| |
| static int cesaTestTraceIdx = 0; |
| static MV_CESA_TEST_TRACE cesaTestTrace[MV_CESA_TEST_TRACE_SIZE]; |
| |
| static void cesaTestTraceAdd(int type) |
| { |
| cesaTestTrace[cesaTestTraceIdx].type = type; |
| cesaTestTrace[cesaTestTraceIdx].realCause = MV_REG_READ(MV_CESA_ISR_CAUSE_REG); |
| //cesaTestTrace[cesaTestTraceIdx].idmaCause = MV_REG_READ(IDMA_CAUSE_REG); |
| cesaTestTrace[cesaTestTraceIdx].resources = cesaReqResources; |
| cesaTestTrace[cesaTestTraceIdx].pReqReady = pCesaReqReady; |
| cesaTestTrace[cesaTestTraceIdx].pReqEmpty = pCesaReqEmpty; |
| cesaTestTrace[cesaTestTraceIdx].pReqProcess = pCesaReqProcess; |
| cesaTestTrace[cesaTestTraceIdx].timeStamp = mvCntmrRead(MV_CESA_USE_TIMER_ID); |
| cesaTestTraceIdx++; |
| if(cesaTestTraceIdx == MV_CESA_TEST_TRACE_SIZE) |
| cesaTestTraceIdx = 0; |
| } |
| |
| #else /* CESA_OCF_TRACE_DEBUG */ |
| |
| #define cesaTestTraceAdd(x) |
| |
| #endif /* CESA_OCF_TRACE_DEBUG */ |
| |
| unsigned int |
| get_usec(unsigned int start) |
| { |
| if(start) { |
| do_gettimeofday (&tt_start); |
| return 0; |
| } |
| else { |
| do_gettimeofday (&tt_end); |
| tt_end.tv_sec -= tt_start.tv_sec; |
| tt_end.tv_usec -= tt_start.tv_usec; |
| if (tt_end.tv_usec < 0) { |
| tt_end.tv_usec += 1000 * 1000; |
| tt_end.tv_sec -= 1; |
| } |
| } |
| printk("time taken is %d\n", (unsigned int)(tt_end.tv_usec + tt_end.tv_sec * 1000000)); |
| return (tt_end.tv_usec + tt_end.tv_sec * 1000000); |
| } |
| |
| #ifdef RT_DEBUG |
| /* |
| * check that the crp action match the current session |
| */ |
| static int |
| ocf_check_action(struct cryptop *crp, struct cesa_ocf_data *cesa_ocf_cur_ses) { |
| int count = 0; |
| int encrypt = 0, decrypt = 0, auth = 0; |
| struct cryptodesc *crd; |
| |
| /* Go through crypto descriptors, processing as we go */ |
| for (crd = crp->crp_desc; crd; crd = crd->crd_next, count++) { |
| if(count > 2) { |
| printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| |
| /* Encryption /Decryption */ |
| if(crd->crd_alg == cesa_ocf_cur_ses->cipher_alg) { |
| /* check that the action is compatible with session */ |
| if(encrypt || decrypt) { |
| printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| |
| if(crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */ |
| if( (count == 2) && (cesa_ocf_cur_ses->encrypt_tn_auth) ) { |
| printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| encrypt++; |
| } |
| else { /* decrypt */ |
| if( (count == 2) && !(cesa_ocf_cur_ses->auth_tn_decrypt) ) { |
| printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| decrypt++; |
| } |
| |
| } |
| /* Authentication */ |
| else if(crd->crd_alg == cesa_ocf_cur_ses->auth_alg) { |
| /* check that the action is compatible with session */ |
| if(auth) { |
| printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| if( (count == 2) && (decrypt) && (cesa_ocf_cur_ses->auth_tn_decrypt)) { |
| printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| if( (count == 2) && (encrypt) && !(cesa_ocf_cur_ses->encrypt_tn_auth)) { |
| printk("%s,%d: sequence isn't supported by this session.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| auth++; |
| } |
| else { |
| printk("%s,%d: Alg isn't supported by this session.\n", __FILE__, __LINE__); |
| return 1; |
| } |
| } |
| return 0; |
| |
| } |
| #endif |
| |
| /* |
| * Process a request. |
| */ |
| static int |
| cesa_ocf_process(device_t dev, struct cryptop *crp, int hint) |
| { |
| struct cesa_ocf_process *cesa_ocf_cmd = NULL; |
| struct cesa_ocf_process *cesa_ocf_cmd_wa = NULL; |
| MV_CESA_COMMAND *cesa_cmd; |
| struct cryptodesc *crd; |
| struct cesa_ocf_data *cesa_ocf_cur_ses; |
| int sid = 0, temp_len = 0, i; |
| int encrypt = 0, decrypt = 0, auth = 0; |
| int status; |
| struct sk_buff *skb = NULL; |
| struct uio *uiop = NULL; |
| unsigned char *ivp; |
| MV_BUF_INFO *p_buf_info; |
| MV_CESA_MBUF *p_mbuf_info; |
| unsigned long flags; |
| |
| dprintk("%s()\n", __FUNCTION__); |
| |
| if( cesaReqResources <= 1 ) { |
| dprintk("%s,%d: ERESTART\n", __FILE__, __LINE__); |
| return ERESTART; |
| } |
| |
| #ifdef RT_DEBUG |
| /* Sanity check */ |
| if (crp == NULL) { |
| printk("%s,%d: EINVAL\n", __FILE__, __LINE__); |
| return EINVAL; |
| } |
| |
| if (crp->crp_desc == NULL || crp->crp_buf == NULL ) { |
| printk("%s,%d: EINVAL\n", __FILE__, __LINE__); |
| crp->crp_etype = EINVAL; |
| return EINVAL; |
| } |
| |
| sid = crp->crp_sid & 0xffffffff; |
| if ((sid >= CESA_OCF_MAX_SES) || (cesa_ocf_sessions[sid] == NULL)) { |
| crp->crp_etype = ENOENT; |
| printk("%s,%d: ENOENT session %d \n", __FILE__, __LINE__, sid); |
| return EINVAL; |
| } |
| #endif |
| |
| sid = crp->crp_sid & 0xffffffff; |
| crp->crp_etype = 0; |
| cesa_ocf_cur_ses = cesa_ocf_sessions[sid]; |
| |
| #ifdef RT_DEBUG |
| if(ocf_check_action(crp, cesa_ocf_cur_ses)){ |
| goto p_error; |
| } |
| #endif |
| |
| /* malloc a new cesa process */ |
| cesa_ocf_cmd = kmalloc(sizeof(struct cesa_ocf_process), GFP_ATOMIC); |
| |
| if (cesa_ocf_cmd == NULL) { |
| printk("%s,%d: ENOBUFS \n", __FILE__, __LINE__); |
| goto p_error; |
| } |
| memset(cesa_ocf_cmd, 0, sizeof(struct cesa_ocf_process)); |
| |
| /* init cesa_process */ |
| cesa_ocf_cmd->crp = crp; |
| /* always call callback */ |
| cesa_ocf_cmd->need_cb = 1; |
| |
| /* init cesa_cmd for usage of the HALs */ |
| cesa_cmd = &cesa_ocf_cmd->cesa_cmd; |
| cesa_cmd->pReqPrv = (void *)cesa_ocf_cmd; |
| cesa_cmd->sessionId = cesa_ocf_cur_ses->sid_encrypt; /* defualt use encrypt */ |
| |
| /* prepare src buffer */ |
| /* we send the entire buffer to the HAL, even if only part of it should be encrypt/auth. */ |
| /* if not using seesions for both encrypt and auth, then it will be wiser to to copy only */ |
| /* from skip to crd_len. */ |
| p_buf_info = cesa_ocf_cmd->cesa_bufs; |
| p_mbuf_info = &cesa_ocf_cmd->cesa_mbuf; |
| |
| p_buf_info += 2; /* save 2 first buffers for IV and digest - |
| we won't append them to the end since, they |
| might be places in an unaligned addresses. */ |
| |
| p_mbuf_info->pFrags = p_buf_info; |
| temp_len = 0; |
| |
| /* handle SKB */ |
| if (crp->crp_flags & CRYPTO_F_SKBUF) { |
| |
| dprintk("%s,%d: handle SKB.\n", __FILE__, __LINE__); |
| skb = (struct sk_buff *) crp->crp_buf; |
| |
| if (skb_shinfo(skb)->nr_frags >= (MV_CESA_MAX_MBUF_FRAGS - 1)) { |
| printk("%s,%d: %d nr_frags > MV_CESA_MAX_MBUF_FRAGS", __FILE__, __LINE__, skb_shinfo(skb)->nr_frags); |
| goto p_error; |
| } |
| |
| p_mbuf_info->mbufSize = skb->len; |
| temp_len = skb->len; |
| /* first skb fragment */ |
| p_buf_info->bufSize = skb_headlen(skb); |
| p_buf_info->bufVirtPtr = skb->data; |
| p_buf_info++; |
| |
| /* now handle all other skb fragments */ |
| for ( i = 0; i < skb_shinfo(skb)->nr_frags; i++ ) { |
| skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; |
| p_buf_info->bufSize = frag->size; |
| p_buf_info->bufVirtPtr = page_address(FRAG_PAGE(frag->page)) + frag->page_offset; |
| p_buf_info++; |
| } |
| p_mbuf_info->numFrags = skb_shinfo(skb)->nr_frags + 1; |
| } |
| /* handle UIO */ |
| else if(crp->crp_flags & CRYPTO_F_IOV) { |
| |
| dprintk("%s,%d: handle UIO.\n", __FILE__, __LINE__); |
| uiop = (struct uio *) crp->crp_buf; |
| |
| if (uiop->uio_iovcnt > (MV_CESA_MAX_MBUF_FRAGS - 1)) { |
| printk("%s,%d: %d uio_iovcnt > MV_CESA_MAX_MBUF_FRAGS \n", __FILE__, __LINE__, uiop->uio_iovcnt); |
| goto p_error; |
| } |
| |
| p_mbuf_info->mbufSize = crp->crp_ilen; |
| p_mbuf_info->numFrags = uiop->uio_iovcnt; |
| for(i = 0; i < uiop->uio_iovcnt; i++) { |
| p_buf_info->bufVirtPtr = uiop->uio_iov[i].iov_base; |
| p_buf_info->bufSize = uiop->uio_iov[i].iov_len; |
| temp_len += p_buf_info->bufSize; |
| dprintk("%s,%d: buf %x-> addr %x, size %x \n" |
| , __FILE__, __LINE__, i, (unsigned int)p_buf_info->bufVirtPtr, p_buf_info->bufSize); |
| p_buf_info++; |
| } |
| |
| } |
| /* handle CONTIG */ |
| else { |
| dprintk("%s,%d: handle CONTIG.\n", __FILE__, __LINE__); |
| p_mbuf_info->numFrags = 1; |
| p_mbuf_info->mbufSize = crp->crp_ilen; |
| p_buf_info->bufVirtPtr = crp->crp_buf; |
| p_buf_info->bufSize = crp->crp_ilen; |
| temp_len = crp->crp_ilen; |
| p_buf_info++; |
| } |
| |
| /* Support up to 64K why? cause! */ |
| if(crp->crp_ilen > 64*1024) { |
| printk("%s,%d: buf too big %x \n", __FILE__, __LINE__, crp->crp_ilen); |
| goto p_error; |
| } |
| |
| if( temp_len != crp->crp_ilen ) { |
| printk("%s,%d: warning size don't match.(%x %x) \n", __FILE__, __LINE__, temp_len, crp->crp_ilen); |
| } |
| |
| cesa_cmd->pSrc = p_mbuf_info; |
| cesa_cmd->pDst = p_mbuf_info; |
| |
| /* restore p_buf_info to point to first available buf */ |
| p_buf_info = cesa_ocf_cmd->cesa_bufs; |
| p_buf_info += 1; |
| |
| |
| /* Go through crypto descriptors, processing as we go */ |
| for (crd = crp->crp_desc; crd; crd = crd->crd_next) { |
| |
| /* Encryption /Decryption */ |
| if(crd->crd_alg == cesa_ocf_cur_ses->cipher_alg) { |
| |
| dprintk("%s,%d: cipher", __FILE__, __LINE__); |
| |
| cesa_cmd->cryptoOffset = crd->crd_skip; |
| cesa_cmd->cryptoLength = crd->crd_len; |
| |
| if(crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */ |
| dprintk(" encrypt \n"); |
| encrypt++; |
| |
| /* handle IV */ |
| if (crd->crd_flags & CRD_F_IV_EXPLICIT) { /* IV from USER */ |
| dprintk("%s,%d: IV from USER (offset %x) \n", __FILE__, __LINE__, crd->crd_inject); |
| cesa_cmd->ivFromUser = 1; |
| ivp = crd->crd_iv; |
| |
| /* |
| * do we have to copy the IV back to the buffer ? |
| */ |
| if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) { |
| dprintk("%s,%d: copy the IV back to the buffer\n", __FILE__, __LINE__); |
| cesa_cmd->ivOffset = crd->crd_inject; |
| crypto_copyback(crp->crp_flags, crp->crp_buf, crd->crd_inject, cesa_ocf_cur_ses->ivlen, ivp); |
| } |
| else { |
| dprintk("%s,%d: don't copy the IV back to the buffer \n", __FILE__, __LINE__); |
| p_mbuf_info->numFrags++; |
| p_mbuf_info->mbufSize += cesa_ocf_cur_ses->ivlen; |
| p_mbuf_info->pFrags = p_buf_info; |
| |
| p_buf_info->bufVirtPtr = ivp; |
| p_buf_info->bufSize = cesa_ocf_cur_ses->ivlen; |
| p_buf_info--; |
| |
| /* offsets */ |
| cesa_cmd->ivOffset = 0; |
| cesa_cmd->cryptoOffset += cesa_ocf_cur_ses->ivlen; |
| if(auth) { |
| cesa_cmd->macOffset += cesa_ocf_cur_ses->ivlen; |
| cesa_cmd->digestOffset += cesa_ocf_cur_ses->ivlen; |
| } |
| } |
| } |
| else { /* random IV */ |
| dprintk("%s,%d: random IV \n", __FILE__, __LINE__); |
| cesa_cmd->ivFromUser = 0; |
| |
| /* |
| * do we have to copy the IV back to the buffer ? |
| */ |
| /* in this mode the HAL will always copy the IV */ |
| /* given by the session to the ivOffset */ |
| if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) { |
| cesa_cmd->ivOffset = crd->crd_inject; |
| } |
| else { |
| /* if IV isn't copy, then how will the user know which IV did we use??? */ |
| printk("%s,%d: EINVAL\n", __FILE__, __LINE__); |
| goto p_error; |
| } |
| } |
| } |
| else { /* decrypt */ |
| dprintk(" decrypt \n"); |
| decrypt++; |
| cesa_cmd->sessionId = cesa_ocf_cur_ses->sid_decrypt; |
| |
| /* handle IV */ |
| if (crd->crd_flags & CRD_F_IV_EXPLICIT) { |
| dprintk("%s,%d: IV from USER \n", __FILE__, __LINE__); |
| /* append the IV buf to the mbuf */ |
| cesa_cmd->ivFromUser = 1; |
| p_mbuf_info->numFrags++; |
| p_mbuf_info->mbufSize += cesa_ocf_cur_ses->ivlen; |
| p_mbuf_info->pFrags = p_buf_info; |
| |
| p_buf_info->bufVirtPtr = crd->crd_iv; |
| p_buf_info->bufSize = cesa_ocf_cur_ses->ivlen; |
| p_buf_info--; |
| |
| /* offsets */ |
| cesa_cmd->ivOffset = 0; |
| cesa_cmd->cryptoOffset += cesa_ocf_cur_ses->ivlen; |
| if(auth) { |
| cesa_cmd->macOffset += cesa_ocf_cur_ses->ivlen; |
| cesa_cmd->digestOffset += cesa_ocf_cur_ses->ivlen; |
| } |
| } |
| else { |
| dprintk("%s,%d: IV inside the buffer \n", __FILE__, __LINE__); |
| cesa_cmd->ivFromUser = 0; |
| cesa_cmd->ivOffset = crd->crd_inject; |
| } |
| } |
| |
| } |
| /* Authentication */ |
| else if(crd->crd_alg == cesa_ocf_cur_ses->auth_alg) { |
| dprintk("%s,%d: Authentication \n", __FILE__, __LINE__); |
| auth++; |
| cesa_cmd->macOffset = crd->crd_skip; |
| cesa_cmd->macLength = crd->crd_len; |
| |
| /* digest + mac */ |
| cesa_cmd->digestOffset = crd->crd_inject; |
| } |
| else { |
| printk("%s,%d: Alg isn't supported by this session.\n", __FILE__, __LINE__); |
| goto p_error; |
| } |
| } |
| |
| dprintk("\n"); |
| dprintk("%s,%d: Sending Action: \n", __FILE__, __LINE__); |
| dprintk("%s,%d: IV from user: %d. IV offset %x \n", __FILE__, __LINE__, cesa_cmd->ivFromUser, cesa_cmd->ivOffset); |
| dprintk("%s,%d: crypt offset %x len %x \n", __FILE__, __LINE__, cesa_cmd->cryptoOffset, cesa_cmd->cryptoLength); |
| dprintk("%s,%d: Auth offset %x len %x \n", __FILE__, __LINE__, cesa_cmd->macOffset, cesa_cmd->macLength); |
| dprintk("%s,%d: set digest in offset %x . \n", __FILE__, __LINE__, cesa_cmd->digestOffset); |
| if(debug) { |
| mvCesaDebugMbuf("SRC BUFFER", cesa_cmd->pSrc, 0, cesa_cmd->pSrc->mbufSize); |
| } |
| |
| |
| /* send action to HAL */ |
| spin_lock_irqsave(&cesa_lock, flags); |
| status = mvCesaAction(cesa_cmd); |
| spin_unlock_irqrestore(&cesa_lock, flags); |
| |
| /* action not allowed */ |
| if(status == MV_NOT_ALLOWED) { |
| #ifdef CESA_OCF_SPLIT |
| /* if both encrypt and auth try to split */ |
| if(auth && (encrypt || decrypt)) { |
| MV_CESA_COMMAND *cesa_cmd_wa; |
| |
| /* malloc a new cesa process and init it */ |
| cesa_ocf_cmd_wa = kmalloc(sizeof(struct cesa_ocf_process), GFP_ATOMIC); |
| |
| if (cesa_ocf_cmd_wa == NULL) { |
| printk("%s,%d: ENOBUFS \n", __FILE__, __LINE__); |
| goto p_error; |
| } |
| memcpy(cesa_ocf_cmd_wa, cesa_ocf_cmd, sizeof(struct cesa_ocf_process)); |
| cesa_cmd_wa = &cesa_ocf_cmd_wa->cesa_cmd; |
| cesa_cmd_wa->pReqPrv = (void *)cesa_ocf_cmd_wa; |
| cesa_ocf_cmd_wa->need_cb = 0; |
| |
| /* break requests to two operation, first operation completion won't call callback */ |
| if((decrypt) && (cesa_ocf_cur_ses->auth_tn_decrypt)) { |
| cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_auth; |
| cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_decrypt; |
| } |
| else if((decrypt) && !(cesa_ocf_cur_ses->auth_tn_decrypt)) { |
| cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_decrypt; |
| cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_auth; |
| } |
| else if((encrypt) && (cesa_ocf_cur_ses->encrypt_tn_auth)) { |
| cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_encrypt; |
| cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_auth; |
| } |
| else if((encrypt) && !(cesa_ocf_cur_ses->encrypt_tn_auth)){ |
| cesa_cmd_wa->sessionId = cesa_ocf_cur_ses->frag_wa_auth; |
| cesa_cmd->sessionId = cesa_ocf_cur_ses->frag_wa_encrypt; |
| } |
| else { |
| printk("%s,%d: Unsupporterd fragment wa mode \n", __FILE__, __LINE__); |
| goto p_error; |
| } |
| |
| /* send the 2 actions to the HAL */ |
| spin_lock_irqsave(&cesa_lock, flags); |
| status = mvCesaAction(cesa_cmd_wa); |
| spin_unlock_irqrestore(&cesa_lock, flags); |
| |
| if((status != MV_NO_MORE) && (status != MV_OK)) { |
| printk("%s,%d: cesa action failed, status = 0x%x\n", __FILE__, __LINE__, status); |
| goto p_error; |
| } |
| spin_lock_irqsave(&cesa_lock, flags); |
| status = mvCesaAction(cesa_cmd); |
| spin_unlock_irqrestore(&cesa_lock, flags); |
| |
| } |
| /* action not allowed and can't split */ |
| else |
| #endif |
| { |
| goto p_error; |
| } |
| } |
| |
| /* Hal Q is full, send again. This should never happen */ |
| if(status == MV_NO_RESOURCE) { |
| printk("%s,%d: cesa no more resources \n", __FILE__, __LINE__); |
| if(cesa_ocf_cmd) |
| kfree(cesa_ocf_cmd); |
| if(cesa_ocf_cmd_wa) |
| kfree(cesa_ocf_cmd_wa); |
| return ERESTART; |
| } |
| else if((status != MV_NO_MORE) && (status != MV_OK)) { |
| printk("%s,%d: cesa action failed, status = 0x%x\n", __FILE__, __LINE__, status); |
| goto p_error; |
| } |
| |
| |
| #ifdef CESA_OCF_POLLING |
| cesa_interrupt_polling(); |
| #endif |
| cesaTestTraceAdd(5); |
| |
| return 0; |
| p_error: |
| crp->crp_etype = EINVAL; |
| if(cesa_ocf_cmd) |
| kfree(cesa_ocf_cmd); |
| if(cesa_ocf_cmd_wa) |
| kfree(cesa_ocf_cmd_wa); |
| return EINVAL; |
| } |
| |
| /* |
| * cesa callback. |
| */ |
| static void |
| cesa_callback(unsigned long dummy) |
| { |
| struct cesa_ocf_process *cesa_ocf_cmd = NULL; |
| struct cryptop *crp = NULL; |
| MV_CESA_RESULT result[MV_CESA_MAX_CHAN]; |
| int res_idx = 0,i; |
| MV_STATUS status; |
| |
| dprintk("%s()\n", __FUNCTION__); |
| |
| #ifdef CESA_OCF_TASKLET |
| disable_irq(cesa_device.irq); |
| #endif |
| while(MV_TRUE) { |
| |
| /* Get Ready requests */ |
| spin_lock(&cesa_lock); |
| status = mvCesaReadyGet(&result[res_idx]); |
| spin_unlock(&cesa_lock); |
| |
| cesaTestTraceAdd(2); |
| |
| if(status != MV_OK) { |
| #ifdef CESA_OCF_POLLING |
| if(status == MV_BUSY) { /* Fragment */ |
| cesa_interrupt_polling(); |
| return; |
| } |
| #endif |
| break; |
| } |
| res_idx++; |
| break; |
| } |
| |
| for(i = 0; i < res_idx; i++) { |
| |
| if(!result[i].pReqPrv) { |
| printk("%s,%d: warning private is NULL\n", __FILE__, __LINE__); |
| break; |
| } |
| |
| cesa_ocf_cmd = result[i].pReqPrv; |
| crp = cesa_ocf_cmd->crp; |
| |
| // ignore HMAC error. |
| //if(result->retCode) |
| // crp->crp_etype = EIO; |
| |
| #if defined(CESA_OCF_POLLING) |
| if(!cesa_ocf_cmd->need_cb){ |
| cesa_interrupt_polling(); |
| } |
| #endif |
| if(cesa_ocf_cmd->need_cb) { |
| if(debug) { |
| mvCesaDebugMbuf("DST BUFFER", cesa_ocf_cmd->cesa_cmd.pDst, 0, cesa_ocf_cmd->cesa_cmd.pDst->mbufSize); |
| } |
| crypto_done(crp); |
| } |
| kfree(cesa_ocf_cmd); |
| } |
| #ifdef CESA_OCF_TASKLET |
| enable_irq(cesa_device.irq); |
| #endif |
| |
| cesaTestTraceAdd(3); |
| |
| return; |
| } |
| |
| #ifdef CESA_OCF_POLLING |
| static void |
| cesa_interrupt_polling(void) |
| { |
| u32 cause; |
| |
| dprintk("%s()\n", __FUNCTION__); |
| |
| /* Read cause register */ |
| do { |
| cause = MV_REG_READ(MV_CESA_ISR_CAUSE_REG); |
| cause &= MV_CESA_CAUSE_ACC_DMA_ALL_MASK; |
| |
| } while (cause == 0); |
| |
| /* clear interrupts */ |
| MV_REG_WRITE(MV_CESA_ISR_CAUSE_REG, 0); |
| |
| cesa_callback(0); |
| |
| return; |
| } |
| |
| #endif |
| |
| /* |
| * cesa Interrupt polling routine. |
| */ |
| static irqreturn_t |
| cesa_interrupt_handler(int irq, void *arg) |
| { |
| u32 cause; |
| |
| dprintk("%s()\n", __FUNCTION__); |
| |
| cesaTestTraceAdd(0); |
| |
| /* Read cause register */ |
| cause = MV_REG_READ(MV_CESA_ISR_CAUSE_REG); |
| |
| if( (cause & MV_CESA_CAUSE_ACC_DMA_ALL_MASK) == 0) |
| { |
| /* Empty interrupt */ |
| dprintk("%s,%d: cesaTestReadyIsr: cause=0x%x\n", __FILE__, __LINE__, cause); |
| return IRQ_HANDLED; |
| } |
| |
| /* clear interrupts */ |
| MV_REG_WRITE(MV_CESA_ISR_CAUSE_REG, 0); |
| |
| cesaTestTraceAdd(1); |
| #ifdef CESA_OCF_TASKLET |
| tasklet_hi_schedule(&cesa_ocf_tasklet); |
| #else |
| cesa_callback(0); |
| #endif |
| return IRQ_HANDLED; |
| } |
| |
| /* |
| * Open a session. |
| */ |
| static int |
| /*cesa_ocf_newsession(void *arg, u_int32_t *sid, struct cryptoini *cri)*/ |
| cesa_ocf_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri) |
| { |
| u32 status = 0, i; |
| u32 count = 0, auth = 0, encrypt =0; |
| struct cesa_ocf_data *cesa_ocf_cur_ses; |
| MV_CESA_OPEN_SESSION cesa_session; |
| MV_CESA_OPEN_SESSION *cesa_ses = &cesa_session; |
| |
| |
| dprintk("%s()\n", __FUNCTION__); |
| if (sid == NULL || cri == NULL) { |
| printk("%s,%d: EINVAL\n", __FILE__, __LINE__); |
| return EINVAL; |
| } |
| |
| /* leave first empty like in other implementations */ |
| for (i = 1; i < CESA_OCF_MAX_SES; i++) { |
| if (cesa_ocf_sessions[i] == NULL) |
| break; |
| } |
| |
| if(i >= CESA_OCF_MAX_SES) { |
| printk("%s,%d: no more sessions \n", __FILE__, __LINE__); |
| return EINVAL; |
| } |
| |
| cesa_ocf_sessions[i] = (struct cesa_ocf_data *) kmalloc(sizeof(struct cesa_ocf_data), GFP_ATOMIC); |
| if (cesa_ocf_sessions[i] == NULL) { |
| cesa_ocf_freesession(NULL, i); |
| printk("%s,%d: ENOBUFS \n", __FILE__, __LINE__); |
| return ENOBUFS; |
| } |
| dprintk("%s,%d: new session %d \n", __FILE__, __LINE__, i); |
| |
| *sid = i; |
| cesa_ocf_cur_ses = cesa_ocf_sessions[i]; |
| memset(cesa_ocf_cur_ses, 0, sizeof(struct cesa_ocf_data)); |
| cesa_ocf_cur_ses->sid_encrypt = -1; |
| cesa_ocf_cur_ses->sid_decrypt = -1; |
| cesa_ocf_cur_ses->frag_wa_encrypt = -1; |
| cesa_ocf_cur_ses->frag_wa_decrypt = -1; |
| cesa_ocf_cur_ses->frag_wa_auth = -1; |
| |
| /* init the session */ |
| memset(cesa_ses, 0, sizeof(MV_CESA_OPEN_SESSION)); |
| count = 1; |
| while (cri) { |
| if(count > 2) { |
| printk("%s,%d: don't support more then 2 operations\n", __FILE__, __LINE__); |
| goto error; |
| } |
| switch (cri->cri_alg) { |
| case CRYPTO_AES_CBC: |
| dprintk("%s,%d: (%d) AES CBC \n", __FILE__, __LINE__, count); |
| cesa_ocf_cur_ses->cipher_alg = cri->cri_alg; |
| cesa_ocf_cur_ses->ivlen = MV_CESA_AES_BLOCK_SIZE; |
| cesa_ses->cryptoAlgorithm = MV_CESA_CRYPTO_AES; |
| cesa_ses->cryptoMode = MV_CESA_CRYPTO_CBC; |
| if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { |
| printk("%s,%d: CRYPTO key too long.\n", __FILE__, __LINE__); |
| goto error; |
| } |
| memcpy(cesa_ses->cryptoKey, cri->cri_key, cri->cri_klen/8); |
| dprintk("%s,%d: key length %d \n", __FILE__, __LINE__, cri->cri_klen/8); |
| cesa_ses->cryptoKeyLength = cri->cri_klen/8; |
| encrypt += count; |
| break; |
| case CRYPTO_3DES_CBC: |
| dprintk("%s,%d: (%d) 3DES CBC \n", __FILE__, __LINE__, count); |
| cesa_ocf_cur_ses->cipher_alg = cri->cri_alg; |
| cesa_ocf_cur_ses->ivlen = MV_CESA_3DES_BLOCK_SIZE; |
| cesa_ses->cryptoAlgorithm = MV_CESA_CRYPTO_3DES; |
| cesa_ses->cryptoMode = MV_CESA_CRYPTO_CBC; |
| if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { |
| printk("%s,%d: CRYPTO key too long.\n", __FILE__, __LINE__); |
| goto error; |
| } |
| memcpy(cesa_ses->cryptoKey, cri->cri_key, cri->cri_klen/8); |
| cesa_ses->cryptoKeyLength = cri->cri_klen/8; |
| encrypt += count; |
| break; |
| case CRYPTO_DES_CBC: |
| dprintk("%s,%d: (%d) DES CBC \n", __FILE__, __LINE__, count); |
| cesa_ocf_cur_ses->cipher_alg = cri->cri_alg; |
| cesa_ocf_cur_ses->ivlen = MV_CESA_DES_BLOCK_SIZE; |
| cesa_ses->cryptoAlgorithm = MV_CESA_CRYPTO_DES; |
| cesa_ses->cryptoMode = MV_CESA_CRYPTO_CBC; |
| if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { |
| printk("%s,%d: CRYPTO key too long.\n", __FILE__, __LINE__); |
| goto error; |
| } |
| memcpy(cesa_ses->cryptoKey, cri->cri_key, cri->cri_klen/8); |
| cesa_ses->cryptoKeyLength = cri->cri_klen/8; |
| encrypt += count; |
| break; |
| case CRYPTO_MD5: |
| case CRYPTO_MD5_HMAC: |
| dprintk("%s,%d: (%d) %sMD5 CBC \n", __FILE__, __LINE__, count, (cri->cri_alg != CRYPTO_MD5)? "H-":" "); |
| cesa_ocf_cur_ses->auth_alg = cri->cri_alg; |
| cesa_ocf_cur_ses->digestlen = (cri->cri_alg == CRYPTO_MD5)? MV_CESA_MD5_DIGEST_SIZE : 12; |
| cesa_ses->macMode = (cri->cri_alg == CRYPTO_MD5)? MV_CESA_MAC_MD5 : MV_CESA_MAC_HMAC_MD5; |
| if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { |
| printk("%s,%d: MAC key too long. \n", __FILE__, __LINE__); |
| goto error; |
| } |
| cesa_ses->macKeyLength = cri->cri_klen/8; |
| memcpy(cesa_ses->macKey, cri->cri_key, cri->cri_klen/8); |
| cesa_ses->digestSize = cesa_ocf_cur_ses->digestlen; |
| auth += count; |
| break; |
| case CRYPTO_SHA1: |
| case CRYPTO_SHA1_HMAC: |
| dprintk("%s,%d: (%d) %sSHA1 CBC \n", __FILE__, __LINE__, count, (cri->cri_alg != CRYPTO_SHA1)? "H-":" "); |
| cesa_ocf_cur_ses->auth_alg = cri->cri_alg; |
| cesa_ocf_cur_ses->digestlen = (cri->cri_alg == CRYPTO_SHA1)? MV_CESA_SHA1_DIGEST_SIZE : 12; |
| cesa_ses->macMode = (cri->cri_alg == CRYPTO_SHA1)? MV_CESA_MAC_SHA1 : MV_CESA_MAC_HMAC_SHA1; |
| if(cri->cri_klen/8 > MV_CESA_MAX_CRYPTO_KEY_LENGTH) { |
| printk("%s,%d: MAC key too long. \n", __FILE__, __LINE__); |
| goto error; |
| } |
| cesa_ses->macKeyLength = cri->cri_klen/8; |
| memcpy(cesa_ses->macKey, cri->cri_key, cri->cri_klen/8); |
| cesa_ses->digestSize = cesa_ocf_cur_ses->digestlen; |
| auth += count; |
| break; |
| default: |
| printk("%s,%d: unknown algo 0x%x\n", __FILE__, __LINE__, cri->cri_alg); |
| goto error; |
| } |
| cri = cri->cri_next; |
| count++; |
| } |
| |
| if((encrypt > 2) || (auth > 2)) { |
| printk("%s,%d: session mode is not supported.\n", __FILE__, __LINE__); |
| goto error; |
| } |
| /* create new sessions in HAL */ |
| if(encrypt) { |
| cesa_ses->operation = MV_CESA_CRYPTO_ONLY; |
| /* encrypt session */ |
| if(auth == 1) { |
| cesa_ses->operation = MV_CESA_MAC_THEN_CRYPTO; |
| } |
| else if(auth == 2) { |
| cesa_ses->operation = MV_CESA_CRYPTO_THEN_MAC; |
| cesa_ocf_cur_ses->encrypt_tn_auth = 1; |
| } |
| else { |
| cesa_ses->operation = MV_CESA_CRYPTO_ONLY; |
| } |
| cesa_ses->direction = MV_CESA_DIR_ENCODE; |
| status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->sid_encrypt); |
| if(status != MV_OK) { |
| printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); |
| goto error; |
| } |
| /* decrypt session */ |
| if( cesa_ses->operation == MV_CESA_MAC_THEN_CRYPTO ) { |
| cesa_ses->operation = MV_CESA_CRYPTO_THEN_MAC; |
| } |
| else if( cesa_ses->operation == MV_CESA_CRYPTO_THEN_MAC ) { |
| cesa_ses->operation = MV_CESA_MAC_THEN_CRYPTO; |
| } |
| cesa_ses->direction = MV_CESA_DIR_DECODE; |
| status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->sid_decrypt); |
| if(status != MV_OK) { |
| printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); |
| goto error; |
| } |
| |
| /* preapre one action sessions for case we will need to split an action */ |
| #ifdef CESA_OCF_SPLIT |
| if(( cesa_ses->operation == MV_CESA_MAC_THEN_CRYPTO ) || |
| ( cesa_ses->operation == MV_CESA_CRYPTO_THEN_MAC )) { |
| /* open one session for encode and one for decode */ |
| cesa_ses->operation = MV_CESA_CRYPTO_ONLY; |
| cesa_ses->direction = MV_CESA_DIR_ENCODE; |
| status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->frag_wa_encrypt); |
| if(status != MV_OK) { |
| printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); |
| goto error; |
| } |
| |
| cesa_ses->direction = MV_CESA_DIR_DECODE; |
| status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->frag_wa_decrypt); |
| if(status != MV_OK) { |
| printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); |
| goto error; |
| } |
| /* open one session for auth */ |
| cesa_ses->operation = MV_CESA_MAC_ONLY; |
| cesa_ses->direction = MV_CESA_DIR_ENCODE; |
| status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->frag_wa_auth); |
| if(status != MV_OK) { |
| printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); |
| goto error; |
| } |
| } |
| #endif |
| } |
| else { /* only auth */ |
| cesa_ses->operation = MV_CESA_MAC_ONLY; |
| cesa_ses->direction = MV_CESA_DIR_ENCODE; |
| status = mvCesaSessionOpen(cesa_ses, &cesa_ocf_cur_ses->sid_encrypt); |
| if(status != MV_OK) { |
| printk("%s,%d: Can't open new session - status = 0x%x\n", __FILE__, __LINE__, status); |
| goto error; |
| } |
| } |
| |
| return 0; |
| error: |
| cesa_ocf_freesession(NULL, *sid); |
| return EINVAL; |
| |
| } |
| |
| |
| /* |
| * Free a session. |
| */ |
| static int |
| cesa_ocf_freesession(device_t dev, u_int64_t tid) |
| { |
| struct cesa_ocf_data *cesa_ocf_cur_ses; |
| u_int32_t sid = CRYPTO_SESID2LID(tid); |
| //unsigned long flags; |
| |
| dprintk("%s() %d \n", __FUNCTION__, sid); |
| if ( (sid >= CESA_OCF_MAX_SES) || (cesa_ocf_sessions[sid] == NULL) ) { |
| printk("%s,%d: EINVAL can't free session %d \n", __FILE__, __LINE__, sid); |
| return(EINVAL); |
| } |
| |
| /* Silently accept and return */ |
| if (sid == 0) |
| return(0); |
| |
| /* release session from HAL */ |
| cesa_ocf_cur_ses = cesa_ocf_sessions[sid]; |
| if (cesa_ocf_cur_ses->sid_encrypt != -1) { |
| mvCesaSessionClose(cesa_ocf_cur_ses->sid_encrypt); |
| } |
| if (cesa_ocf_cur_ses->sid_decrypt != -1) { |
| mvCesaSessionClose(cesa_ocf_cur_ses->sid_decrypt); |
| } |
| if (cesa_ocf_cur_ses->frag_wa_encrypt != -1) { |
| mvCesaSessionClose(cesa_ocf_cur_ses->frag_wa_encrypt); |
| } |
| if (cesa_ocf_cur_ses->frag_wa_decrypt != -1) { |
| mvCesaSessionClose(cesa_ocf_cur_ses->frag_wa_decrypt); |
| } |
| if (cesa_ocf_cur_ses->frag_wa_auth != -1) { |
| mvCesaSessionClose(cesa_ocf_cur_ses->frag_wa_auth); |
| } |
| |
| kfree(cesa_ocf_cur_ses); |
| cesa_ocf_sessions[sid] = NULL; |
| |
| return 0; |
| } |
| |
| |
| /* TDMA Window setup */ |
| |
| static void __init |
| setup_tdma_mbus_windows(struct cesa_dev *dev) |
| { |
| int i; |
| |
| for (i = 0; i < 4; i++) { |
| writel(0, dev->reg + WINDOW_BASE(i)); |
| writel(0, dev->reg + WINDOW_CTRL(i)); |
| } |
| |
| for (i = 0; i < dev->plat_data->dram->num_cs; i++) { |
| struct mbus_dram_window *cs = dev->plat_data->dram->cs + i; |
| writel( |
| ((cs->size - 1) & 0xffff0000) | |
| (cs->mbus_attr << 8) | |
| (dev->plat_data->dram->mbus_dram_target_id << 4) | 1, |
| dev->reg + WINDOW_CTRL(i) |
| ); |
| writel(cs->base, dev->reg + WINDOW_BASE(i)); |
| } |
| } |
| |
| /* |
| * our driver startup and shutdown routines |
| */ |
| static int |
| mv_cesa_ocf_init(struct platform_device *pdev) |
| { |
| #if defined(CONFIG_MV78200) || defined(CONFIG_MV632X) |
| if (MV_FALSE == mvSocUnitIsMappedToThisCpu(CESA)) |
| { |
| dprintk("CESA is not mapped to this CPU\n"); |
| return -ENODEV; |
| } |
| #endif |
| |
| dprintk("%s\n", __FUNCTION__); |
| memset(&mv_cesa_dev, 0, sizeof(mv_cesa_dev)); |
| softc_device_init(&mv_cesa_dev, "MV CESA", 0, mv_cesa_methods); |
| cesa_ocf_id = crypto_get_driverid(softc_get_device(&mv_cesa_dev),CRYPTOCAP_F_HARDWARE); |
| |
| if (cesa_ocf_id < 0) |
| panic("MV CESA crypto device cannot initialize!"); |
| |
| dprintk("%s,%d: cesa ocf device id is %d \n", __FILE__, __LINE__, cesa_ocf_id); |
| |
| /* CESA unit is auto power on off */ |
| #if 0 |
| if (MV_FALSE == mvCtrlPwrClckGet(CESA_UNIT_ID,0)) |
| { |
| printk("\nWarning CESA %d is Powered Off\n",0); |
| return EINVAL; |
| } |
| #endif |
| |
| memset(&cesa_device, 0, sizeof(struct cesa_dev)); |
| /* Get the IRQ, and crypto memory regions */ |
| { |
| struct resource *res; |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); |
| |
| if (!res) |
| return -ENXIO; |
| |
| cesa_device.sram = ioremap(res->start, res->end - res->start + 1); |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); |
| |
| if (!res) { |
| iounmap(cesa_device.sram); |
| return -ENXIO; |
| } |
| cesa_device.reg = ioremap(res->start, res->end - res->start + 1); |
| cesa_device.irq = platform_get_irq(pdev, 0); |
| cesa_device.plat_data = pdev->dev.platform_data; |
| setup_tdma_mbus_windows(&cesa_device); |
| |
| } |
| |
| |
| if( MV_OK != mvCesaInit(CESA_OCF_MAX_SES*5, CESA_Q_SIZE, cesa_device.reg, |
| NULL) ) { |
| printk("%s,%d: mvCesaInit Failed. \n", __FILE__, __LINE__); |
| return EINVAL; |
| } |
| |
| /* clear and unmask Int */ |
| MV_REG_WRITE( MV_CESA_ISR_CAUSE_REG, 0); |
| #ifndef CESA_OCF_POLLING |
| MV_REG_WRITE( MV_CESA_ISR_MASK_REG, MV_CESA_CAUSE_ACC_DMA_MASK); |
| #endif |
| #ifdef CESA_OCF_TASKLET |
| tasklet_init(&cesa_ocf_tasklet, cesa_callback, (unsigned int) 0); |
| #endif |
| /* register interrupt */ |
| if( request_irq( cesa_device.irq, cesa_interrupt_handler, |
| (IRQF_DISABLED) , "cesa", &cesa_ocf_id) < 0) { |
| printk("%s,%d: cannot assign irq %x\n", __FILE__, __LINE__, cesa_device.reg); |
| return EINVAL; |
| } |
| |
| |
| memset(cesa_ocf_sessions, 0, sizeof(struct cesa_ocf_data *) * CESA_OCF_MAX_SES); |
| |
| #define REGISTER(alg) \ |
| crypto_register(cesa_ocf_id, alg, 0,0) |
| REGISTER(CRYPTO_AES_CBC); |
| REGISTER(CRYPTO_DES_CBC); |
| REGISTER(CRYPTO_3DES_CBC); |
| REGISTER(CRYPTO_MD5); |
| REGISTER(CRYPTO_MD5_HMAC); |
| REGISTER(CRYPTO_SHA1); |
| REGISTER(CRYPTO_SHA1_HMAC); |
| #undef REGISTER |
| |
| return 0; |
| } |
| |
| static void |
| mv_cesa_ocf_exit(struct platform_device *pdev) |
| { |
| dprintk("%s()\n", __FUNCTION__); |
| |
| crypto_unregister_all(cesa_ocf_id); |
| cesa_ocf_id = -1; |
| iounmap(cesa_device.reg); |
| iounmap(cesa_device.sram); |
| free_irq(cesa_device.irq, NULL); |
| |
| /* mask and clear Int */ |
| MV_REG_WRITE( MV_CESA_ISR_MASK_REG, 0); |
| MV_REG_WRITE( MV_CESA_ISR_CAUSE_REG, 0); |
| |
| |
| if( MV_OK != mvCesaFinish() ) { |
| printk("%s,%d: mvCesaFinish Failed. \n", __FILE__, __LINE__); |
| return; |
| } |
| } |
| |
| |
| void cesa_ocf_debug(void) |
| { |
| |
| #ifdef CESA_OCF_TRACE_DEBUG |
| { |
| int i, j; |
| j = cesaTestTraceIdx; |
| mvOsPrintf("No Type rCause iCause Proc Isr Res Time pReady pProc pEmpty\n"); |
| for(i=0; i<MV_CESA_TEST_TRACE_SIZE; i++) |
| { |
| mvOsPrintf("%02d. %d 0x%04x 0x%04x 0x%02x 0x%02x %02d 0x%06x %p %p %p\n", |
| j, cesaTestTrace[j].type, cesaTestTrace[j].realCause, |
| cesaTestTrace[j].idmaCause, |
| cesaTestTrace[j].resources, cesaTestTrace[j].timeStamp, |
| cesaTestTrace[j].pReqReady, cesaTestTrace[j].pReqProcess, cesaTestTrace[j].pReqEmpty); |
| j++; |
| if(j == MV_CESA_TEST_TRACE_SIZE) |
| j = 0; |
| } |
| } |
| #endif |
| |
| } |
| |
| static struct platform_driver marvell_cesa = { |
| .probe = mv_cesa_ocf_init, |
| .remove = mv_cesa_ocf_exit, |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = "mv_crypto", |
| }, |
| }; |
| |
| MODULE_ALIAS("platform:mv_crypto"); |
| |
| static int __init mv_cesa_init(void) |
| { |
| return platform_driver_register(&marvell_cesa); |
| } |
| |
| module_init(mv_cesa_init); |
| |
| static void __exit mv_cesa_exit(void) |
| { |
| platform_driver_unregister(&marvell_cesa); |
| } |
| |
| module_exit(mv_cesa_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Ronen Shitrit"); |
| MODULE_DESCRIPTION("OCF module for Orion CESA crypto"); |