CI20: jz4780-rng
Mathieu Malaterre <[email protected]>
| Newsgroups | gmane.linux.debian.ports.mips |
|---|---|
| Message-ID | <CA+7wUsw=aC-5tdTfAcAPGjPu=dshBV-KMFrDkx484g_F7zCGQA@mail.gmail.com> |
Hi there, If anyone owns a CI20 (or X1000 SoC) I'd be interested in feedback from the attached patch. Thanks much !
jz4780_rng_cgu_v3.patch
(application/octet-stream, 17.2 KB)
diff --git a/MAINTAINERS b/MAINTAINERS index f8d77c8..83be066 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6568,6 +6568,11 @@ L: [email protected] S: Maintained F: drivers/mtd/nand/jz4780_* +INGENIC JZ4780 PRNG DRIVER +M: PrasannaKumar Muralidharan <[email protected]> +S: Maintained +F: drivers/crypto/jz4780-rng.c + INTEGRITY MEASUREMENT ARCHITECTURE (IMA) M: Mimi Zohar <[email protected]> M: Dmitry Kasatkin <[email protected]> diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index b868b42..f58371c 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -34,14 +34,22 @@ clock-frequency = <32768>; }; - cgu: jz4780-cgu@10000000 { - compatible = "ingenic,jz4780-cgu"; + cgu_registers { + compatible = "simple-mfd", "syscon"; reg = <0x10000000 0x100>; - clocks = <&ext>, <&rtc>; - clock-names = "ext", "rtc"; + cgu: jz4780-cgu@10000000 { + compatible = "ingenic,jz4780-cgu"; - #clock-cells = <1>; + clocks = <&ext>, <&rtc>; + clock-names = "ext", "rtc"; + + #clock-cells = <1>; + }; + + rng: rng@d8 { + compatible = "ingenic,jz480-rng"; + }; }; uart0: serial@10030000 { diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index e8248f9..37bcdcb 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -29,6 +29,18 @@ #define MHZ (1000 * 1000) +unsigned int cgu_readl(struct ingenic_cgu *cgu, unsigned int reg) +{ + unsigned int val = 0; + regmap_read(cgu->regmap, reg, &val); + return val; +} + +int cgu_writel(struct ingenic_cgu *cgu, unsigned int val, unsigned int reg) +{ + return regmap_write(cgu->regmap, reg, val); +} + /** * ingenic_cgu_gate_get() - get the value of clock gate register bit * @cgu: reference to the CGU whose registers should be read @@ -43,7 +55,7 @@ static inline bool ingenic_cgu_gate_get(struct ingenic_cgu *cgu, const struct ingenic_cgu_gate_info *info) { - return readl(cgu->base + info->reg) & BIT(info->bit); + return cgu_readl(cgu, info->reg) & BIT(info->bit); } /** @@ -60,14 +72,14 @@ static inline void ingenic_cgu_gate_set(struct ingenic_cgu *cgu, const struct ingenic_cgu_gate_info *info, bool val) { - u32 clkgr = readl(cgu->base + info->reg); + u32 clkgr = cgu_readl(cgu, info->reg); if (val) clkgr |= BIT(info->bit); else clkgr &= ~BIT(info->bit); - writel(clkgr, cgu->base + info->reg); + cgu_writel(cgu, clkgr, info->reg); } /* @@ -91,7 +103,7 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) pll_info = &clk_info->pll; spin_lock_irqsave(&cgu->lock, flags); - ctl = readl(cgu->base + pll_info->reg); + ctl = cgu_readl(cgu, pll_info->reg); spin_unlock_irqrestore(&cgu->lock, flags); m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); @@ -190,7 +202,7 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, clk_info->name, req_rate, rate); spin_lock_irqsave(&cgu->lock, flags); - ctl = readl(cgu->base + pll_info->reg); + ctl = cgu_readl(cgu, pll_info->reg); ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift); ctl |= (m - pll_info->m_offset) << pll_info->m_shift; @@ -204,11 +216,11 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, ctl &= ~BIT(pll_info->bypass_bit); ctl |= BIT(pll_info->enable_bit); - writel(ctl, cgu->base + pll_info->reg); + cgu_writel(cgu, ctl, pll_info->reg); /* wait for the PLL to stabilise */ for (i = 0; i < timeout; i++) { - ctl = readl(cgu->base + pll_info->reg); + ctl = cgu_readl(cgu, pll_info->reg); if (ctl & BIT(pll_info->stable_bit)) break; mdelay(1); @@ -243,7 +255,7 @@ static u8 ingenic_clk_get_parent(struct clk_hw *hw) clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_MUX) { - reg = readl(cgu->base + clk_info->mux.reg); + reg = cgu_readl(cgu, clk_info->mux.reg); hw_idx = (reg >> clk_info->mux.shift) & GENMASK(clk_info->mux.bits - 1, 0); @@ -297,10 +309,10 @@ static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) spin_lock_irqsave(&cgu->lock, flags); /* write the register */ - reg = readl(cgu->base + clk_info->mux.reg); + reg = cgu_readl(cgu, clk_info->mux.reg); reg &= ~mask; reg |= hw_idx << clk_info->mux.shift; - writel(reg, cgu->base + clk_info->mux.reg); + cgu_writel(cgu, reg, clk_info->mux.reg); spin_unlock_irqrestore(&cgu->lock, flags); return 0; @@ -321,7 +333,7 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_DIV) { - div_reg = readl(cgu->base + clk_info->div.reg); + div_reg = cgu_readl(cgu, clk_info->div.reg); div = (div_reg >> clk_info->div.shift) & GENMASK(clk_info->div.bits - 1, 0); div += 1; @@ -399,7 +411,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, return -EINVAL; spin_lock_irqsave(&cgu->lock, flags); - reg = readl(cgu->base + clk_info->div.reg); + reg = cgu_readl(cgu, clk_info->div.reg); /* update the divide */ mask = GENMASK(clk_info->div.bits - 1, 0); @@ -415,12 +427,12 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, reg |= BIT(clk_info->div.ce_bit); /* update the hardware */ - writel(reg, cgu->base + clk_info->div.reg); + cgu_writel(cgu, reg, clk_info->div.reg); /* wait for the change to take effect */ if (clk_info->div.busy_bit != -1) { for (i = 0; i < timeout; i++) { - reg = readl(cgu->base + clk_info->div.reg); + reg = cgu_readl(cgu, clk_info->div.reg); if (!(reg & BIT(clk_info->div.busy_bit))) break; mdelay(1); @@ -661,9 +673,8 @@ ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, if (!cgu) goto err_out; - cgu->base = of_iomap(np, 0); - if (!cgu->base) { - pr_err("%s: failed to map CGU registers\n", __func__); + cgu->regmap = syscon_node_to_regmap(np->parent); + if (cgu->regmap == NULL) { goto err_out_free; } diff --git a/drivers/clk/ingenic/cgu.h b/drivers/clk/ingenic/cgu.h index 09700b2..86fd5b0 100644 --- a/drivers/clk/ingenic/cgu.h +++ b/drivers/clk/ingenic/cgu.h @@ -19,7 +19,9 @@ #define __DRIVERS_CLK_INGENIC_CGU_H__ #include <linux/bitops.h> +#include <linux/mfd/syscon.h> #include <linux/of.h> +#include <linux/regmap.h> #include <linux/spinlock.h> /** @@ -171,14 +173,14 @@ struct ingenic_cgu_clk_info { /** * struct ingenic_cgu - data about the CGU * @np: the device tree node that caused the CGU to be probed - * @base: the ioremap'ed base address of the CGU registers + * @regmap: the regmap object used to access the CGU registers * @clock_info: an array containing information about implemented clocks * @clocks: used to provide clocks to DT, allows lookup of struct clk* * @lock: lock to be held whilst manipulating CGU registers */ struct ingenic_cgu { struct device_node *np; - void __iomem *base; + struct regmap *regmap; const struct ingenic_cgu_clk_info *clock_info; struct clk_onecell_data clocks; @@ -186,6 +188,9 @@ struct ingenic_cgu { spinlock_t lock; }; +unsigned int cgu_readl(struct ingenic_cgu *cgu, unsigned int reg); +int cgu_writel(struct ingenic_cgu *cgu, unsigned int val, unsigned int reg); + /** * struct ingenic_clk - private data for a clock * @hw: see Documentation/clk.txt diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 510fe7e..3003afb 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -232,7 +232,7 @@ CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) { - uint32_t lcr = readl(cgu->base + CGU_REG_LCR); + uint32_t lcr = cgu_readl(cgu, CGU_REG_LCR); switch (mode) { case JZ4740_WAIT_MODE_IDLE: @@ -244,24 +244,24 @@ void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) break; } - writel(lcr, cgu->base + CGU_REG_LCR); + cgu_writel(cgu, lcr, CGU_REG_LCR); } void jz4740_clock_udc_disable_auto_suspend(void) { - uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); + uint32_t clkgr = cgu_readl(cgu, CGU_REG_CLKGR); clkgr &= ~CLKGR_UDC; - writel(clkgr, cgu->base + CGU_REG_CLKGR); + cgu_writel(cgu, clkgr, CGU_REG_CLKGR); } EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); void jz4740_clock_udc_enable_auto_suspend(void) { - uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); + uint32_t clkgr = cgu_readl(cgu, CGU_REG_CLKGR); clkgr |= CLKGR_UDC; - writel(clkgr, cgu->base + CGU_REG_CLKGR); + cgu_writel(cgu, clkgr, CGU_REG_CLKGR); } EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); @@ -273,31 +273,31 @@ void jz4740_clock_suspend(void) { uint32_t clkgr, cppcr; - clkgr = readl(cgu->base + CGU_REG_CLKGR); + clkgr = cgu_readl(cgu, CGU_REG_CLKGR); clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0; - writel(clkgr, cgu->base + CGU_REG_CLKGR); + cgu_writel(cgu, clkgr, CGU_REG_CLKGR); - cppcr = readl(cgu->base + CGU_REG_CPPCR); + cppcr = cgu_readl(cgu, CGU_REG_CPPCR); cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); - writel(cppcr, cgu->base + CGU_REG_CPPCR); + cgu_writel(cgu, cppcr, CGU_REG_CPPCR); } void jz4740_clock_resume(void) { uint32_t clkgr, cppcr, stable; - cppcr = readl(cgu->base + CGU_REG_CPPCR); + cppcr = cgu_readl(cgu, CGU_REG_CPPCR); cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); - writel(cppcr, cgu->base + CGU_REG_CPPCR); + cgu_writel(cgu, cppcr, CGU_REG_CPPCR); stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); do { - cppcr = readl(cgu->base + CGU_REG_CPPCR); + cppcr = cgu_readl(cgu, CGU_REG_CPPCR); } while (!(cppcr & stable)); - clkgr = readl(cgu->base + CGU_REG_CLKGR); + clkgr = cgu_readl(cgu, CGU_REG_CLKGR); clkgr &= ~JZ_CLOCK_GATE_TCU; clkgr &= ~JZ_CLOCK_GATE_DMAC; clkgr &= ~JZ_CLOCK_GATE_UART0; - writel(clkgr, cgu->base + CGU_REG_CLKGR); + cgu_writel(cgu, clkgr, CGU_REG_CLKGR); } diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c index b35d6d9..8f83433 100644 --- a/drivers/clk/ingenic/jz4780-cgu.c +++ b/drivers/clk/ingenic/jz4780-cgu.c @@ -113,11 +113,11 @@ static int jz4780_otg_phy_set_parent(struct clk_hw *hw, u8 idx) spin_lock_irqsave(&cgu->lock, flags); - usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); + usbpcr1 = cgu_readl(cgu, CGU_REG_USBPCR1); usbpcr1 &= ~USBPCR1_REFCLKSEL_MASK; /* we only use CLKCORE */ usbpcr1 |= USBPCR1_REFCLKSEL_CORE; - writel(usbpcr1, cgu->base + CGU_REG_USBPCR1); + cgu_writel(cgu, usbpcr1, CGU_REG_USBPCR1); spin_unlock_irqrestore(&cgu->lock, flags); return 0; @@ -129,7 +129,7 @@ static unsigned long jz4780_otg_phy_recalc_rate(struct clk_hw *hw, u32 usbpcr1; unsigned refclk_div; - usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); + usbpcr1 = cgu_readl(cgu, CGU_REG_USBPCR1); refclk_div = usbpcr1 & USBPCR1_REFCLKDIV_MASK; switch (refclk_div) { @@ -194,10 +194,10 @@ static int jz4780_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate, spin_lock_irqsave(&cgu->lock, flags); - usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1); + usbpcr1 = cgu_readl(cgu, CGU_REG_USBPCR1); usbpcr1 &= ~USBPCR1_REFCLKDIV_MASK; usbpcr1 |= div_bits; - writel(usbpcr1, cgu->base + CGU_REG_USBPCR1); + cgu_writel(cgu, usbpcr1, CGU_REG_USBPCR1); spin_unlock_irqrestore(&cgu->lock, flags); return 0; diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 2888b91..15caf38 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -587,6 +587,22 @@ config CRYPTO_DEV_IMGTEC_HASH hardware hash accelerator. Supporting MD5/SHA1/SHA224/SHA256 hashing algorithms. +config CRYPTO_DEV_JZ4780_RNG + tristate "JZ4780 HW pseudo random number generator support" + depends on MACH_JZ4780 || COMPILE_TEST + depends on HAS_IOMEM + select CRYPTO_RNG + ---help--- + This driver provides kernel-side support through the + cryptographic API for the pseudo random number generator + hardware found in ingenic JZ4780 and X1000 SoC. MIPS + Creator CI20 uses JZ4780 SoC. + + To compile this driver as a module, choose M here: the + module will be called jz4780-rng. + + If unsure, say Y. + config CRYPTO_DEV_SUN4I_SS tristate "Support for Allwinner Security System cryptographic accelerator" depends on ARCH_SUNXI && !64BIT diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 463f335..37ebb1a 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o +obj-$(CONFIG_CRYPTO_DEV_JZ4780_RNG) += jz4780-rng.o obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/ obj-$(CONFIG_CRYPTO_DEV_MEDIATEK) += mediatek/ diff --git a/drivers/crypto/jz4780-rng.c b/drivers/crypto/jz4780-rng.c new file mode 100644 index 0000000..2377e62 --- /dev/null +++ b/drivers/crypto/jz4780-rng.c @@ -0,0 +1,172 @@ +/* + * jz4780-rng.c - Random Number Generator driver for the jz4780 + * + * Copyright (c) 2017 PrasannaKumar Muralidharan <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/crypto.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <crypto/internal/rng.h> + +#define REG_RNG_CTRL 0xD8 +#define REG_RNG_DATA 0xDC + +/* Context for crypto */ +struct jz4780_rng_ctx { + struct jz4780_rng *rng; +}; + +/* Device associated memory */ +struct jz4780_rng { + struct device *dev; + struct regmap *regmap; +}; + +static struct jz4780_rng *jz4780_rng; + +static int jz4780_rng_readl(struct jz4780_rng *rng, u32 offset) +{ + u32 val = 0; + int ret; + ret = regmap_read(rng->regmap, offset, &val); + if (!ret) + return val; + + return ret; +} + +static int jz4780_rng_writel(struct jz4780_rng *rng, u32 val, u32 offset) +{ + return regmap_write(rng->regmap, offset, val); +} + +static int jz4780_rng_generate(struct crypto_rng *tfm, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int dlen) +{ + struct jz4780_rng_ctx *ctx = crypto_rng_ctx(tfm); + struct jz4780_rng *rng = ctx->rng; + u32 data; + + /* + * JZ4780 Programmers manual says the RNG should not run continuously + * for more than 1s. So enable RNG, read data and disable it. + * NOTE: No issue was observed with MIPS creator CI20 board even when + * RNG ran continuously for longer periods. This is just a precaution. + * + * A delay is required so that the current RNG data is not bit shifted + * version of previous RNG data which could happen if random data is + * read continuously from this device. + */ + jz4780_rng_writel(rng, 1, REG_RNG_CTRL); + do { + data = jz4780_rng_readl(rng, REG_RNG_DATA); + memcpy((void *)dst, (void *)&data, 4); + dlen -= 4; + dst += 4; + udelay(20); + } while (dlen >= 4); + + if (dlen > 0) { + data = jz4780_rng_readl(rng, REG_RNG_DATA); + memcpy((void *)dst, (void *)&data, dlen); + } + jz4780_rng_writel(rng, 0, REG_RNG_CTRL); + + return 0; +} + +static int jz4780_rng_kcapi_init(struct crypto_tfm *tfm) +{ + struct jz4780_rng_ctx *ctx = crypto_tfm_ctx(tfm); + + ctx->rng = jz4780_rng; + + return 0; +} + +static struct rng_alg jz4780_rng_alg = { + .generate = jz4780_rng_generate, + .base = { + .cra_name = "stdrng", + .cra_driver_name = "jz4780_rng", + .cra_priority = 100, + .cra_ctxsize = sizeof(jz4780_rng_ctx), + .cra_module = THIS_MODULE, + .cra_init = jz4780_rng_kcapi_init, + } +}; + +static int jz4780_rng_probe(struct platform_device *pdev) +{ + struct jz4780_rng *rng; + struct resource *res; + int ret; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rng->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(rng->regmap)) + return PTR_ERR(rng->regmap); + + jz4780_rng = rng; + + ret = crypto_register_rng(&jz4780_rng_alg); + if (ret) { + dev_err(&pdev->dev, + "Couldn't register rng crypto alg: %d\n", ret); + jz4780_rng = NULL; + } + + return ret; +} + +static int jz4780_rng_remove(struct platform_device *pdev) +{ + crypto_unregister_rng(&jz4780_rng_alg); + + jz4780_rng = NULL; + + return 0; +} + +static const struct of_device_id jz4780_rng_dt_match[] = { + { + .compatible = "ingenic,jz4780-rng", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, jz4780_rng_dt_match); + +static struct platform_driver jz4780_rng_driver = { + .driver = { + .name = "jz4780-rng", + .of_match_table = jz4780_rng_dt_match, + }, + .probe = jz4780_rng_probe, + .remove = jz4780_rng_remove, +}; + +module_platform_driver(jz4780_rng_driver); + +MODULE_DESCRIPTION("Ingenic JZ4780 H/W Psuedo Random Number Generator driver"); +MODULE_AUTHOR("PrasannaKumar Muralidharan <[email protected]>"); +MODULE_LICENSE("GPL");