Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Microchip / Atmel SHA204A (I2C) driver.
0004  *
0005  * Copyright (c) 2019 Linaro, Ltd. <ard.biesheuvel@linaro.org>
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/device.h>
0010 #include <linux/err.h>
0011 #include <linux/errno.h>
0012 #include <linux/i2c.h>
0013 #include <linux/init.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/scatterlist.h>
0017 #include <linux/slab.h>
0018 #include <linux/workqueue.h>
0019 #include "atmel-i2c.h"
0020 
0021 static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
0022                    void *areq, int status)
0023 {
0024     struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
0025     struct hwrng *rng = areq;
0026 
0027     if (status)
0028         dev_warn_ratelimited(&i2c_priv->client->dev,
0029                      "i2c transaction failed (%d)\n",
0030                      status);
0031 
0032     rng->priv = (unsigned long)work_data;
0033     atomic_dec(&i2c_priv->tfm_count);
0034 }
0035 
0036 static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
0037                           size_t max)
0038 {
0039     struct atmel_i2c_client_priv *i2c_priv;
0040     struct atmel_i2c_work_data *work_data;
0041 
0042     i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
0043 
0044     /* keep maximum 1 asynchronous read in flight at any time */
0045     if (!atomic_add_unless(&i2c_priv->tfm_count, 1, 1))
0046         return 0;
0047 
0048     if (rng->priv) {
0049         work_data = (struct atmel_i2c_work_data *)rng->priv;
0050         max = min(sizeof(work_data->cmd.data), max);
0051         memcpy(data, &work_data->cmd.data, max);
0052         rng->priv = 0;
0053     } else {
0054         work_data = kmalloc(sizeof(*work_data), GFP_ATOMIC);
0055         if (!work_data)
0056             return -ENOMEM;
0057 
0058         work_data->ctx = i2c_priv;
0059         work_data->client = i2c_priv->client;
0060 
0061         max = 0;
0062     }
0063 
0064     atmel_i2c_init_random_cmd(&work_data->cmd);
0065     atmel_i2c_enqueue(work_data, atmel_sha204a_rng_done, rng);
0066 
0067     return max;
0068 }
0069 
0070 static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max,
0071                   bool wait)
0072 {
0073     struct atmel_i2c_client_priv *i2c_priv;
0074     struct atmel_i2c_cmd cmd;
0075     int ret;
0076 
0077     if (!wait)
0078         return atmel_sha204a_rng_read_nonblocking(rng, data, max);
0079 
0080     i2c_priv = container_of(rng, struct atmel_i2c_client_priv, hwrng);
0081 
0082     atmel_i2c_init_random_cmd(&cmd);
0083 
0084     ret = atmel_i2c_send_receive(i2c_priv->client, &cmd);
0085     if (ret)
0086         return ret;
0087 
0088     max = min(sizeof(cmd.data), max);
0089     memcpy(data, cmd.data, max);
0090 
0091     return max;
0092 }
0093 
0094 static int atmel_sha204a_probe(struct i2c_client *client,
0095                    const struct i2c_device_id *id)
0096 {
0097     struct atmel_i2c_client_priv *i2c_priv;
0098     int ret;
0099 
0100     ret = atmel_i2c_probe(client, id);
0101     if (ret)
0102         return ret;
0103 
0104     i2c_priv = i2c_get_clientdata(client);
0105 
0106     memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
0107 
0108     i2c_priv->hwrng.name = dev_name(&client->dev);
0109     i2c_priv->hwrng.read = atmel_sha204a_rng_read;
0110     i2c_priv->hwrng.quality = 1024;
0111 
0112     ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
0113     if (ret)
0114         dev_warn(&client->dev, "failed to register RNG (%d)\n", ret);
0115 
0116     return ret;
0117 }
0118 
0119 static int atmel_sha204a_remove(struct i2c_client *client)
0120 {
0121     struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
0122 
0123     if (atomic_read(&i2c_priv->tfm_count)) {
0124         dev_emerg(&client->dev, "Device is busy, will remove it anyhow\n");
0125         return 0;
0126     }
0127 
0128     kfree((void *)i2c_priv->hwrng.priv);
0129 
0130     return 0;
0131 }
0132 
0133 static const struct of_device_id atmel_sha204a_dt_ids[] = {
0134     { .compatible = "atmel,atsha204", },
0135     { .compatible = "atmel,atsha204a", },
0136     { /* sentinel */ }
0137 };
0138 MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
0139 
0140 static const struct i2c_device_id atmel_sha204a_id[] = {
0141     { "atsha204", 0 },
0142     { "atsha204a", 0 },
0143     { /* sentinel */ }
0144 };
0145 MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id);
0146 
0147 static struct i2c_driver atmel_sha204a_driver = {
0148     .probe          = atmel_sha204a_probe,
0149     .remove         = atmel_sha204a_remove,
0150     .id_table       = atmel_sha204a_id,
0151 
0152     .driver.name        = "atmel-sha204a",
0153     .driver.of_match_table  = of_match_ptr(atmel_sha204a_dt_ids),
0154 };
0155 
0156 static int __init atmel_sha204a_init(void)
0157 {
0158     return i2c_add_driver(&atmel_sha204a_driver);
0159 }
0160 
0161 static void __exit atmel_sha204a_exit(void)
0162 {
0163     atmel_i2c_flush_queue();
0164     i2c_del_driver(&atmel_sha204a_driver);
0165 }
0166 
0167 module_init(atmel_sha204a_init);
0168 module_exit(atmel_sha204a_exit);
0169 
0170 MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
0171 MODULE_LICENSE("GPL v2");