diff options
Diffstat (limited to 'drivers/crypto/caam/ctrl.c')
-rw-r--r-- | drivers/crypto/caam/ctrl.c | 179 |
1 files changed, 173 insertions, 6 deletions
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 77557ebcd33..414ba20c05a 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -2,13 +2,16 @@ * CAAM control-plane driver backend * Controller-level driver, kernel property detection, initialization * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008-2012 Freescale Semiconductor, Inc. */ #include "compat.h" #include "regs.h" #include "intern.h" #include "jr.h" +#include "desc_constr.h" +#include "error.h" +#include "ctrl.h" static int caam_remove(struct platform_device *pdev) { @@ -43,10 +46,154 @@ static int caam_remove(struct platform_device *pdev) return ret; } +/* + * Descriptor to instantiate RNG State Handle 0 in normal mode and + * load the JDKEK, TDKEK and TDSK registers + */ +static void build_instantiation_desc(u32 *desc) +{ + u32 *jump_cmd; + + init_job_desc(desc, 0); + + /* INIT RNG in non-test mode */ + append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | + OP_ALG_AS_INIT); + + /* wait for done */ + jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1); + set_jump_tgt_here(desc, jump_cmd); + + /* + * load 1 to clear written reg: + * resets the done interrrupt and returns the RNG to idle. + */ + append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW); + + /* generate secure keys (non-test) */ + append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | + OP_ALG_RNG4_SK); +} + +struct instantiate_result { + struct completion completion; + int err; +}; + +static void rng4_init_done(struct device *dev, u32 *desc, u32 err, + void *context) +{ + struct instantiate_result *instantiation = context; + + if (err) { + char tmp[CAAM_ERROR_STR_MAX]; + + dev_err(dev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); + } + + instantiation->err = err; + complete(&instantiation->completion); +} + +static int instantiate_rng(struct device *jrdev) +{ + struct instantiate_result instantiation; + + dma_addr_t desc_dma; + u32 *desc; + int ret; + + desc = kmalloc(CAAM_CMD_SZ * 6, GFP_KERNEL | GFP_DMA); + if (!desc) { + dev_err(jrdev, "cannot allocate RNG init descriptor memory\n"); + return -ENOMEM; + } + + build_instantiation_desc(desc); + desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); + init_completion(&instantiation.completion); + ret = caam_jr_enqueue(jrdev, desc, rng4_init_done, &instantiation); + if (!ret) { + wait_for_completion_interruptible(&instantiation.completion); + ret = instantiation.err; + if (ret) + dev_err(jrdev, "unable to instantiate RNG\n"); + } + + dma_unmap_single(jrdev, desc_dma, desc_bytes(desc), DMA_TO_DEVICE); + + kfree(desc); + + return ret; +} + +/* + * By default, the TRNG runs for 200 clocks per sample; + * 800 clocks per sample generates better entropy. + */ +static void kick_trng(struct platform_device *pdev) +{ + struct device *ctrldev = &pdev->dev; + struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev); + struct caam_full __iomem *topregs; + struct rng4tst __iomem *r4tst; + u32 val; + + topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; + r4tst = &topregs->ctrl.r4tst[0]; + + /* put RNG4 into program mode */ + setbits32(&r4tst->rtmctl, RTMCTL_PRGM); + /* 800 clocks per sample */ + val = rd_reg32(&r4tst->rtsdctl); + val = (val & ~RTSDCTL_ENT_DLY_MASK) | (800 << RTSDCTL_ENT_DLY_SHIFT); + wr_reg32(&r4tst->rtsdctl, val); + /* min. freq. count */ + wr_reg32(&r4tst->rtfrqmin, 400); + /* max. freq. count */ + wr_reg32(&r4tst->rtfrqmax, 6400); + /* put RNG4 into run mode */ + clrbits32(&r4tst->rtmctl, RTMCTL_PRGM); +} + +/** + * caam_get_era() - Return the ERA of the SEC on SoC, based + * on the SEC_VID register. + * Returns the ERA number (1..4) or -ENOTSUPP if the ERA is unknown. + * @caam_id - the value of the SEC_VID register + **/ +int caam_get_era(u64 caam_id) +{ + struct sec_vid *sec_vid = (struct sec_vid *)&caam_id; + static const struct { + u16 ip_id; + u8 maj_rev; + u8 era; + } caam_eras[] = { + {0x0A10, 1, 1}, + {0x0A10, 2, 2}, + {0x0A12, 1, 3}, + {0x0A14, 1, 3}, + {0x0A14, 2, 4}, + {0x0A16, 1, 4}, + {0x0A11, 1, 4} + }; + int i; + + for (i = 0; i < ARRAY_SIZE(caam_eras); i++) + if (caam_eras[i].ip_id == sec_vid->ip_id && + caam_eras[i].maj_rev == sec_vid->maj_rev) + return caam_eras[i].era; + + return -ENOTSUPP; +} +EXPORT_SYMBOL(caam_get_era); + /* Probe routine for CAAM top (controller) level */ static int caam_probe(struct platform_device *pdev) { - int ring, rspec; + int ret, ring, rspec; + u64 caam_id; struct device *dev; struct device_node *nprop, *np; struct caam_ctrl __iomem *ctrl; @@ -82,13 +229,18 @@ static int caam_probe(struct platform_device *pdev) /* * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, - * 36-bit pointers in master configuration register + * long pointers in master configuration register */ setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); if (sizeof(dma_addr_t) == sizeof(u64)) - dma_set_mask(dev, DMA_BIT_MASK(36)); + if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) + dma_set_mask(dev, DMA_BIT_MASK(40)); + else + dma_set_mask(dev, DMA_BIT_MASK(36)); + else + dma_set_mask(dev, DMA_BIT_MASK(32)); /* * Detect and enable JobRs @@ -141,14 +293,29 @@ static int caam_probe(struct platform_device *pdev) return -ENOMEM; } + /* + * RNG4 based SECs (v5+) need special initialization prior + * to executing any descriptors + */ + if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) { + kick_trng(pdev); + ret = instantiate_rng(ctrlpriv->jrdev[0]); + if (ret) { + caam_remove(pdev); + return ret; + } + } + /* NOTE: RTIC detection ought to go here, around Si time */ /* Initialize queue allocator lock */ spin_lock_init(&ctrlpriv->jr_alloc_lock); + caam_id = rd_reg64(&topregs->ctrl.perfmon.caam_id); + /* Report "alive" for developer to see */ - dev_info(dev, "device ID = 0x%016llx\n", - rd_reg64(&topregs->ctrl.perfmon.caam_id)); + dev_info(dev, "device ID = 0x%016llx (Era %d)\n", caam_id, + caam_get_era(caam_id)); dev_info(dev, "job rings = %d, qi = %d\n", ctrlpriv->total_jobrs, ctrlpriv->qi_present); |