in s5p-sss.c [2156:2317]
static int s5p_aes_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int i, j, err;
const struct samsung_aes_variant *variant;
struct s5p_aes_dev *pdata;
struct resource *res;
unsigned int hash_i;
if (s5p_dev)
return -EEXIST;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
variant = find_s5p_sss_version(pdev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
/*
* Note: HASH and PRNG uses the same registers in secss, avoid
* overwrite each other. This will drop HASH when CONFIG_EXYNOS_RNG
* is enabled in config. We need larger size for HASH registers in
* secss, current describe only AES/DES
*/
if (IS_ENABLED(CONFIG_CRYPTO_DEV_EXYNOS_HASH)) {
if (variant == &exynos_aes_data) {
res->end += 0x300;
pdata->use_hash = true;
}
}
pdata->res = res;
pdata->ioaddr = devm_ioremap_resource(dev, res);
if (IS_ERR(pdata->ioaddr)) {
if (!pdata->use_hash)
return PTR_ERR(pdata->ioaddr);
/* try AES without HASH */
res->end -= 0x300;
pdata->use_hash = false;
pdata->ioaddr = devm_ioremap_resource(dev, res);
if (IS_ERR(pdata->ioaddr))
return PTR_ERR(pdata->ioaddr);
}
pdata->clk = devm_clk_get(dev, variant->clk_names[0]);
if (IS_ERR(pdata->clk))
return dev_err_probe(dev, PTR_ERR(pdata->clk),
"failed to find secss clock %s\n",
variant->clk_names[0]);
err = clk_prepare_enable(pdata->clk);
if (err < 0) {
dev_err(dev, "Enabling clock %s failed, err %d\n",
variant->clk_names[0], err);
return err;
}
if (variant->clk_names[1]) {
pdata->pclk = devm_clk_get(dev, variant->clk_names[1]);
if (IS_ERR(pdata->pclk)) {
err = dev_err_probe(dev, PTR_ERR(pdata->pclk),
"failed to find clock %s\n",
variant->clk_names[1]);
goto err_clk;
}
err = clk_prepare_enable(pdata->pclk);
if (err < 0) {
dev_err(dev, "Enabling clock %s failed, err %d\n",
variant->clk_names[0], err);
goto err_clk;
}
} else {
pdata->pclk = NULL;
}
spin_lock_init(&pdata->lock);
spin_lock_init(&pdata->hash_lock);
pdata->aes_ioaddr = pdata->ioaddr + variant->aes_offset;
pdata->io_hash_base = pdata->ioaddr + variant->hash_offset;
pdata->irq_fc = platform_get_irq(pdev, 0);
if (pdata->irq_fc < 0) {
err = pdata->irq_fc;
dev_warn(dev, "feed control interrupt is not available.\n");
goto err_irq;
}
err = devm_request_threaded_irq(dev, pdata->irq_fc, NULL,
s5p_aes_interrupt, IRQF_ONESHOT,
pdev->name, pdev);
if (err < 0) {
dev_warn(dev, "feed control interrupt is not available.\n");
goto err_irq;
}
pdata->busy = false;
pdata->dev = dev;
platform_set_drvdata(pdev, pdata);
s5p_dev = pdata;
tasklet_init(&pdata->tasklet, s5p_tasklet_cb, (unsigned long)pdata);
crypto_init_queue(&pdata->queue, CRYPTO_QUEUE_LEN);
for (i = 0; i < ARRAY_SIZE(algs); i++) {
err = crypto_register_skcipher(&algs[i]);
if (err)
goto err_algs;
}
if (pdata->use_hash) {
tasklet_init(&pdata->hash_tasklet, s5p_hash_tasklet_cb,
(unsigned long)pdata);
crypto_init_queue(&pdata->hash_queue, SSS_HASH_QUEUE_LENGTH);
for (hash_i = 0; hash_i < ARRAY_SIZE(algs_sha1_md5_sha256);
hash_i++) {
struct ahash_alg *alg;
alg = &algs_sha1_md5_sha256[hash_i];
err = crypto_register_ahash(alg);
if (err) {
dev_err(dev, "can't register '%s': %d\n",
alg->halg.base.cra_driver_name, err);
goto err_hash;
}
}
}
dev_info(dev, "s5p-sss driver registered\n");
return 0;
err_hash:
for (j = hash_i - 1; j >= 0; j--)
crypto_unregister_ahash(&algs_sha1_md5_sha256[j]);
tasklet_kill(&pdata->hash_tasklet);
res->end -= 0x300;
err_algs:
if (i < ARRAY_SIZE(algs))
dev_err(dev, "can't register '%s': %d\n", algs[i].base.cra_name,
err);
for (j = 0; j < i; j++)
crypto_unregister_skcipher(&algs[j]);
tasklet_kill(&pdata->tasklet);
err_irq:
clk_disable_unprepare(pdata->pclk);
err_clk:
clk_disable_unprepare(pdata->clk);
s5p_dev = NULL;
return err;
}