linux/drivers/crypto/atmel-sha204a.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Microchip / Atmel SHA204A (I2C) driver.
   4 *
   5 * Copyright (c) 2019 Linaro, Ltd. <ard.biesheuvel@linaro.org>
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/device.h>
  10#include <linux/err.h>
  11#include <linux/errno.h>
  12#include <linux/i2c.h>
  13#include <linux/init.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/scatterlist.h>
  17#include <linux/slab.h>
  18#include <linux/workqueue.h>
  19#include "atmel-i2c.h"
  20
  21static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
  22                                   void *areq, int status)
  23{
  24        struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
  25        struct hwrng *rng = areq;
  26
  27        if (status)
  28                dev_warn_ratelimited(&i2c_priv->client->dev,
  29                                     "i2c transaction failed (%d)\n",
  30                                     status);
  31
  32        rng->priv = (unsigned long)work_data;
  33        atomic_dec(&i2c_priv->tfm_count);
  34}
  35
  36static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
  37                                              size_t max)
  38{
  39        struct atmel_i2c_client_priv *i2c_priv;
  40        struct atmel_i2c_work_data *work_data;
  41
  42        i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
  43
  44        /* keep maximum 1 asynchronous read in flight at any time */
  45        if (!atomic_add_unless(&i2c_priv->tfm_count, 1, 1))
  46                return 0;
  47
  48        if (rng->priv) {
  49                work_data = (struct atmel_i2c_work_data *)rng->priv;
  50                max = min(sizeof(work_data->cmd.data), max);
  51                memcpy(data, &work_data->cmd.data, max);
  52                rng->priv = 0;
  53        } else {
  54                work_data = kmalloc(sizeof(*work_data), GFP_ATOMIC);
  55                if (!work_data)
  56                        return -ENOMEM;
  57
  58                work_data->ctx = i2c_priv;
  59                work_data->client = i2c_priv->client;
  60
  61                max = 0;
  62        }
  63
  64        atmel_i2c_init_random_cmd(&work_data->cmd);
  65        atmel_i2c_enqueue(work_data, atmel_sha204a_rng_done, rng);
  66
  67        return max;
  68}
  69
  70static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max,
  71                                  bool wait)
  72{
  73        struct atmel_i2c_client_priv *i2c_priv;
  74        struct atmel_i2c_cmd cmd;
  75        int ret;
  76
  77        if (!wait)
  78                return atmel_sha204a_rng_read_nonblocking(rng, data, max);
  79
  80        i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
  81
  82        atmel_i2c_init_random_cmd(&cmd);
  83
  84        ret = atmel_i2c_send_receive(i2c_priv->client, &cmd);
  85        if (ret)
  86                return ret;
  87
  88        max = min(sizeof(cmd.data), max);
  89        memcpy(data, cmd.data, max);
  90
  91        return max;
  92}
  93
  94static int atmel_sha204a_probe(struct i2c_client *client,
  95                               const struct i2c_device_id *id)
  96{
  97        struct atmel_i2c_client_priv *i2c_priv;
  98        int ret;
  99
 100        ret = atmel_i2c_probe(client, id);
 101        if (ret)
 102                return ret;
 103
 104        i2c_priv = i2c_get_clientdata(client);
 105
 106        memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
 107
 108        i2c_priv->hwrng.name = dev_name(&client->dev);
 109        i2c_priv->hwrng.read = atmel_sha204a_rng_read;
 110        i2c_priv->hwrng.quality = 1024;
 111
 112        ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
 113        if (ret)
 114                dev_warn(&client->dev, "failed to register RNG (%d)\n", ret);
 115
 116        return ret;
 117}
 118
 119static int atmel_sha204a_remove(struct i2c_client *client)
 120{
 121        struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
 122
 123        if (atomic_read(&i2c_priv->tfm_count)) {
 124                dev_err(&client->dev, "Device is busy\n");
 125                return -EBUSY;
 126        }
 127
 128        if (i2c_priv->hwrng.priv)
 129                kfree((void *)i2c_priv->hwrng.priv);
 130
 131        return 0;
 132}
 133
 134static const struct of_device_id atmel_sha204a_dt_ids[] = {
 135        { .compatible = "atmel,atsha204a", },
 136        { /* sentinel */ }
 137};
 138MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
 139
 140static const struct i2c_device_id atmel_sha204a_id[] = {
 141        { "atsha204a", 0 },
 142        { /* sentinel */ }
 143};
 144MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id);
 145
 146static struct i2c_driver atmel_sha204a_driver = {
 147        .probe                  = atmel_sha204a_probe,
 148        .remove                 = atmel_sha204a_remove,
 149        .id_table               = atmel_sha204a_id,
 150
 151        .driver.name            = "atmel-sha204a",
 152        .driver.of_match_table  = of_match_ptr(atmel_sha204a_dt_ids),
 153};
 154
 155static int __init atmel_sha204a_init(void)
 156{
 157        return i2c_add_driver(&atmel_sha204a_driver);
 158}
 159
 160static void __exit atmel_sha204a_exit(void)
 161{
 162        flush_scheduled_work();
 163        i2c_del_driver(&atmel_sha204a_driver);
 164}
 165
 166module_init(atmel_sha204a_init);
 167module_exit(atmel_sha204a_exit);
 168
 169MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
 170MODULE_LICENSE("GPL v2");
 171