diff mbox series

[v1] drivers: rng: add check status bit feature

Message ID 20230621102401.76646-1-avromanov@sberdevices.ru
State New
Delegated to: Neil Armstrong
Headers show
Series [v1] drivers: rng: add check status bit feature | expand

Commit Message

Alexey Romanov June 21, 2023, 10:24 a.m. UTC
For some Amlogic SOC's, the mechanism for obtain a random number
has been changed. For example, S4 now uses a status bit wait algo.

Signed-off-by: Alexey Romanov <avromanov@sberdevices.ru>
---
 drivers/rng/meson-rng.c | 73 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 67 insertions(+), 6 deletions(-)

Comments

Neil Armstrong June 22, 2023, 4:18 p.m. UTC | #1
Hi,

On 21/06/2023 12:24, Alexey Romanov wrote:
> For some Amlogic SOC's, the mechanism for obtain a random number
> has been changed. For example, S4 now uses a status bit wait algo.

Thanks for the change, but could you add this first in Linux with the
associated bindings update and DT changes then port it to U-boot ?

Thanks,
Neil

> 
> Signed-off-by: Alexey Romanov <avromanov@sberdevices.ru>
> ---
>   drivers/rng/meson-rng.c | 73 +++++++++++++++++++++++++++++++++++++----
>   1 file changed, 67 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/rng/meson-rng.c b/drivers/rng/meson-rng.c
> index e0a1e8c7e04..3bf2eb9cf87 100644
> --- a/drivers/rng/meson-rng.c
> +++ b/drivers/rng/meson-rng.c
> @@ -11,36 +11,82 @@
>   #include <rng.h>
>   #include <asm/io.h>
>   
> +struct meson_rng_data {
> +	bool check_status_bit;
> +};
> +
>   struct meson_rng_plat {
>   	fdt_addr_t base;
>   	struct clk clk;
> +	struct meson_rng_data *data;
>   };
>   
> +#define RETRY_CNT 100
> +#define RNG_OUT_OFFSET 0x08
> +
> +#define SEED_READY_STS_BIT 0
> +#define RUN_BIT 31
> +
> +static int meson_rng_wait_status(struct meson_rng_plat *pdata, int bit)
> +{
> +	u32 status;
> +	u32 cnt = 0;
> +
> +	pr_debug("Poll status of bit: %d\n", bit);
> +
> +	do {
> +		status = readl(pdata->base) & BIT(bit);
> +	} while (status && (cnt++ < RETRY_CNT));
> +
> +	if (cnt == RETRY_CNT) {
> +		pr_err("Can't get random number, try again");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
>   /**
>    * meson_rng_read() - fill buffer with random bytes
>    *
>    * @buffer:	buffer to receive data
>    * @size:	size of buffer
>    *
> - * Return:	0
> + * Return:	0 on success or -errno in failure
>    */
>   static int meson_rng_read(struct udevice *dev, void *data, size_t len)
>   {
>   	struct meson_rng_plat *pdata = dev_get_plat(dev);
> +	struct meson_rng_data *rng_data = pdata->data;
>   	char *buffer = (char *)data;
> +	int err;
>   
>   	while (len) {
> -		u32 rand = readl(pdata->base);
> +		u32 rand;
>   		size_t step;
>   
> -		if (len >= 4)
> -			step = 4;
> -		else
> -			step = len;
> +		if (rng_data->check_status_bit) {
> +			writel(readl(pdata->base) | BIT(SEED_READY_STS_BIT), pdata->base);
> +
> +			err = meson_rng_wait_status(pdata, SEED_READY_STS_BIT);
> +			if (err)
> +				return err;
> +
> +			err = meson_rng_wait_status(pdata, RUN_BIT);
> +			if (err)
> +				return err;
> +
> +			rand = readl(pdata->base + RNG_OUT_OFFSET);
> +		} else {
> +			rand = readl(pdata->base);
> +		}
> +
> +		step = min_t(u32, len, 4);
>   		memcpy(buffer, &rand, step);
>   		buffer += step;
>   		len -= step;
>   	}
> +
>   	return 0;
>   }
>   
> @@ -90,6 +136,8 @@ static int meson_rng_of_to_plat(struct udevice *dev)
>   	if (!pdata->base)
>   		return -ENODEV;
>   
> +	pdata->data = (struct meson_rng_data *)dev_get_driver_data(dev);
> +
>   	/* Get optional "core" clock */
>   	err = clk_get_by_name_optional(dev, "core", &pdata->clk);
>   	if (err)
> @@ -102,9 +150,22 @@ static const struct dm_rng_ops meson_rng_ops = {
>   	.read = meson_rng_read,
>   };
>   
> +static const struct meson_rng_data meson_rng_data = {
> +	.check_status_bit = false,
> +};
> +
> +static const struct meson_rng_data meson_rng_data_s4 = {
> +	.check_status_bit = true,
> +};
> +
>   static const struct udevice_id meson_rng_match[] = {
>   	{
>   		.compatible = "amlogic,meson-rng",
> +		.data = (ulong)&meson_rng_data,
> +	},
> +	{
> +		.compatible = "amlogic,meson-rng-s4",
> +		.data = (ulong)&meson_rng_data_s4,
>   	},
>   	{},
>   };
Alexey Romanov June 23, 2023, 10:53 a.m. UTC | #2
Hello!

On Thu, Jun 22, 2023 at 06:18:28PM +0200, neil.armstrong@linaro.org wrote:
> Hi,
> 
> On 21/06/2023 12:24, Alexey Romanov wrote:
> > For some Amlogic SOC's, the mechanism for obtain a random number
> > has been changed. For example, S4 now uses a status bit wait algo.
> 
> Thanks for the change, but could you add this first in Linux with the
> associated bindings update and DT changes then port it to U-boot ?
> 
> Thanks,
> Neil
> 
> > 
> > Signed-off-by: Alexey Romanov <avromanov@sberdevices.ru>
> > ---
> >   drivers/rng/meson-rng.c | 73 +++++++++++++++++++++++++++++++++++++----
> >   1 file changed, 67 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/rng/meson-rng.c b/drivers/rng/meson-rng.c
> > index e0a1e8c7e04..3bf2eb9cf87 100644
> > --- a/drivers/rng/meson-rng.c
> > +++ b/drivers/rng/meson-rng.c
> > @@ -11,36 +11,82 @@
> >   #include <rng.h>
> >   #include <asm/io.h>
> > +struct meson_rng_data {
> > +	bool check_status_bit;
> > +};
> > +
> >   struct meson_rng_plat {
> >   	fdt_addr_t base;
> >   	struct clk clk;
> > +	struct meson_rng_data *data;
> >   };
> > +#define RETRY_CNT 100
> > +#define RNG_OUT_OFFSET 0x08
> > +
> > +#define SEED_READY_STS_BIT 0
> > +#define RUN_BIT 31
> > +
> > +static int meson_rng_wait_status(struct meson_rng_plat *pdata, int bit)
> > +{
> > +	u32 status;
> > +	u32 cnt = 0;
> > +
> > +	pr_debug("Poll status of bit: %d\n", bit);
> > +
> > +	do {
> > +		status = readl(pdata->base) & BIT(bit);
> > +	} while (status && (cnt++ < RETRY_CNT));
> > +
> > +	if (cnt == RETRY_CNT) {
> > +		pr_err("Can't get random number, try again");
> > +		return -EBUSY;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >   /**
> >    * meson_rng_read() - fill buffer with random bytes
> >    *
> >    * @buffer:	buffer to receive data
> >    * @size:	size of buffer
> >    *
> > - * Return:	0
> > + * Return:	0 on success or -errno in failure
> >    */
> >   static int meson_rng_read(struct udevice *dev, void *data, size_t len)
> >   {
> >   	struct meson_rng_plat *pdata = dev_get_plat(dev);
> > +	struct meson_rng_data *rng_data = pdata->data;
> >   	char *buffer = (char *)data;
> > +	int err;
> >   	while (len) {
> > -		u32 rand = readl(pdata->base);
> > +		u32 rand;
> >   		size_t step;
> > -		if (len >= 4)
> > -			step = 4;
> > -		else
> > -			step = len;
> > +		if (rng_data->check_status_bit) {
> > +			writel(readl(pdata->base) | BIT(SEED_READY_STS_BIT), pdata->base);
> > +
> > +			err = meson_rng_wait_status(pdata, SEED_READY_STS_BIT);
> > +			if (err)
> > +				return err;
> > +
> > +			err = meson_rng_wait_status(pdata, RUN_BIT);
> > +			if (err)
> > +				return err;
> > +
> > +			rand = readl(pdata->base + RNG_OUT_OFFSET);
> > +		} else {
> > +			rand = readl(pdata->base);
> > +		}
> > +
> > +		step = min_t(u32, len, 4);
> >   		memcpy(buffer, &rand, step);
> >   		buffer += step;
> >   		len -= step;
> >   	}
> > +
> >   	return 0;
> >   }
> > @@ -90,6 +136,8 @@ static int meson_rng_of_to_plat(struct udevice *dev)
> >   	if (!pdata->base)
> >   		return -ENODEV;
> > +	pdata->data = (struct meson_rng_data *)dev_get_driver_data(dev);
> > +
> >   	/* Get optional "core" clock */
> >   	err = clk_get_by_name_optional(dev, "core", &pdata->clk);
> >   	if (err)
> > @@ -102,9 +150,22 @@ static const struct dm_rng_ops meson_rng_ops = {
> >   	.read = meson_rng_read,
> >   };
> > +static const struct meson_rng_data meson_rng_data = {
> > +	.check_status_bit = false,
> > +};
> > +
> > +static const struct meson_rng_data meson_rng_data_s4 = {
> > +	.check_status_bit = true,
> > +};
> > +
> >   static const struct udevice_id meson_rng_match[] = {
> >   	{
> >   		.compatible = "amlogic,meson-rng",
> > +		.data = (ulong)&meson_rng_data,
> > +	},
> > +	{
> > +		.compatible = "amlogic,meson-rng-s4",
> > +		.data = (ulong)&meson_rng_data_s4,
> >   	},
> >   	{},
> >   };
> 

Sure, I will prepare patches and send them in LKML next week.
diff mbox series

Patch

diff --git a/drivers/rng/meson-rng.c b/drivers/rng/meson-rng.c
index e0a1e8c7e04..3bf2eb9cf87 100644
--- a/drivers/rng/meson-rng.c
+++ b/drivers/rng/meson-rng.c
@@ -11,36 +11,82 @@ 
 #include <rng.h>
 #include <asm/io.h>
 
+struct meson_rng_data {
+	bool check_status_bit;
+};
+
 struct meson_rng_plat {
 	fdt_addr_t base;
 	struct clk clk;
+	struct meson_rng_data *data;
 };
 
+#define RETRY_CNT 100
+#define RNG_OUT_OFFSET 0x08
+
+#define SEED_READY_STS_BIT 0
+#define RUN_BIT 31
+
+static int meson_rng_wait_status(struct meson_rng_plat *pdata, int bit)
+{
+	u32 status;
+	u32 cnt = 0;
+
+	pr_debug("Poll status of bit: %d\n", bit);
+
+	do {
+		status = readl(pdata->base) & BIT(bit);
+	} while (status && (cnt++ < RETRY_CNT));
+
+	if (cnt == RETRY_CNT) {
+		pr_err("Can't get random number, try again");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
 /**
  * meson_rng_read() - fill buffer with random bytes
  *
  * @buffer:	buffer to receive data
  * @size:	size of buffer
  *
- * Return:	0
+ * Return:	0 on success or -errno in failure
  */
 static int meson_rng_read(struct udevice *dev, void *data, size_t len)
 {
 	struct meson_rng_plat *pdata = dev_get_plat(dev);
+	struct meson_rng_data *rng_data = pdata->data;
 	char *buffer = (char *)data;
+	int err;
 
 	while (len) {
-		u32 rand = readl(pdata->base);
+		u32 rand;
 		size_t step;
 
-		if (len >= 4)
-			step = 4;
-		else
-			step = len;
+		if (rng_data->check_status_bit) {
+			writel(readl(pdata->base) | BIT(SEED_READY_STS_BIT), pdata->base);
+
+			err = meson_rng_wait_status(pdata, SEED_READY_STS_BIT);
+			if (err)
+				return err;
+
+			err = meson_rng_wait_status(pdata, RUN_BIT);
+			if (err)
+				return err;
+
+			rand = readl(pdata->base + RNG_OUT_OFFSET);
+		} else {
+			rand = readl(pdata->base);
+		}
+
+		step = min_t(u32, len, 4);
 		memcpy(buffer, &rand, step);
 		buffer += step;
 		len -= step;
 	}
+
 	return 0;
 }
 
@@ -90,6 +136,8 @@  static int meson_rng_of_to_plat(struct udevice *dev)
 	if (!pdata->base)
 		return -ENODEV;
 
+	pdata->data = (struct meson_rng_data *)dev_get_driver_data(dev);
+
 	/* Get optional "core" clock */
 	err = clk_get_by_name_optional(dev, "core", &pdata->clk);
 	if (err)
@@ -102,9 +150,22 @@  static const struct dm_rng_ops meson_rng_ops = {
 	.read = meson_rng_read,
 };
 
+static const struct meson_rng_data meson_rng_data = {
+	.check_status_bit = false,
+};
+
+static const struct meson_rng_data meson_rng_data_s4 = {
+	.check_status_bit = true,
+};
+
 static const struct udevice_id meson_rng_match[] = {
 	{
 		.compatible = "amlogic,meson-rng",
+		.data = (ulong)&meson_rng_data,
+	},
+	{
+		.compatible = "amlogic,meson-rng-s4",
+		.data = (ulong)&meson_rng_data_s4,
 	},
 	{},
 };