Re: [PATCH v2 6/6] firmware: tegra: bpmp: Add support for DRAM MRQ GSCs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, May 10, 2023 at 04:33:35PM +0200, Thierry Reding wrote:
> On Wed, May 10, 2023 at 02:31:36PM +0300, Peter De Schrijver wrote:
> > Implement support for DRAM MRQ GSCs.
> > 
> > Signed-off-by: Peter De Schrijver <pdeschrijver@xxxxxxxxxx>
> > ---
> >  drivers/firmware/tegra/bpmp-tegra186.c | 214 +++++++++++++++++--------
> >  drivers/firmware/tegra/bpmp.c          |   4 +-
> >  2 files changed, 153 insertions(+), 65 deletions(-)
> > 
> > diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c
> > index 2e26199041cd..43e2563575fc 100644
> > --- a/drivers/firmware/tegra/bpmp-tegra186.c
> > +++ b/drivers/firmware/tegra/bpmp-tegra186.c
> > @@ -4,8 +4,11 @@
> >   */
> >  
> >  #include <linux/genalloc.h>
> > +#include <linux/io.h>
> >  #include <linux/mailbox_client.h>
> > +#include <linux/of_address.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/range.h>
> 
> Why do we need range.h?
> 

We probably don't need this indeed.

> >  
> >  #include <soc/tegra/bpmp.h>
> >  #include <soc/tegra/bpmp-abi.h>
> > @@ -13,12 +16,13 @@
> >  
> >  #include "bpmp-private.h"
> >  
> > +enum tegra_bpmp_mem_type { TEGRA_INVALID, TEGRA_SRAM, TEGRA_RMEM };
> > +
> 
> This is a strange construct. We can already use the pool pointer to
> determine which type of memory is being used. Your usage of this leads
> to very unintuitive code when you're error checking, etc. and prevents
> you from propagating proper error codes.
> 

No? How would indicate SRAM and DRAM are mutually exclusive?

> >  struct tegra186_bpmp {
> >  	struct tegra_bpmp *parent;
> >  
> >  	struct {
> > -		struct gen_pool *pool;
> > -		void __iomem *virt;
> > +		void *virt;
> 
> I think what we really need is a union that contains both an __iomem
> annotated pointer and a regular one.

And then add a struct gen_pool * in the union and use the enum as a tag.

> 
> >  		dma_addr_t phys;
> >  	} tx, rx;
> >  
> > @@ -26,6 +30,12 @@ struct tegra186_bpmp {
> >  		struct mbox_client client;
> >  		struct mbox_chan *channel;
> >  	} mbox;
> > +
> > +	struct {
> > +		struct gen_pool *tx, *rx;
> > +	} sram;
> 
> Please keep this in the tx/rx structure. This would perhaps be useful if
> there was an equivalent "dram" structure, but as it is there's no
> advantage in keeping this separate from the other memory-related fields.
> 
> > +
> > +	enum tegra_bpmp_mem_type type;
> >  };
> >  
> >  static inline struct tegra_bpmp *
> > @@ -118,8 +128,8 @@ static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel,
> >  	queue_size = tegra_ivc_total_queue_size(message_size);
> >  	offset = queue_size * index;
> >  
> > -	iosys_map_set_vaddr_iomem(&rx, priv->rx.virt + offset);
> > -	iosys_map_set_vaddr_iomem(&tx, priv->tx.virt + offset);
> > +	iosys_map_set_vaddr_iomem(&rx, (void __iomem *)priv->rx.virt + offset);
> > +	iosys_map_set_vaddr_iomem(&tx, (void __iomem *)priv->tx.virt + offset);
> 
> This completely defies the purpose of using the iosys_map helpers. What
> you really want to do is check if we're using SRAM and use the _iomem
> variant, otherwise, use the plain one, something like:
> 
> 	if (priv->rx.pool)
> 		iosys_map_set_vaddr_iomem(&rx, priv->rx.sram + offset);

Even the current code does not have an rx.sram field, so I don't quite
understand what you mean here.

> 	else
> 		iosys_map_set_vaddr(&rx, priv->rx.dram + offset);
> 
> And repeat that for TX. I suppose you could also do the iosys_map
> assignment for both in the same blocks above since we don't support
> mixing SRAM and DRAM modes.
> 

Currently the code does:

iosys_map_set_vaddr_iomem(&rx, priv->rx.virt + offset);

and priv->rx.virt is initialized by:

priv->rx.virt = (void __iomem *)gen_pool_dma_alloc();

so we cast this today as well?

> >  
> >  	err = tegra_ivc_init(channel->ivc, NULL, &rx, priv->rx.phys + offset, &tx,
> >  			     priv->tx.phys + offset, 1, message_size, tegra186_bpmp_ivc_notify,
> > @@ -158,64 +168,171 @@ static void mbox_handle_rx(struct mbox_client *client, void *data)
> >  	tegra_bpmp_handle_rx(bpmp);
> >  }
> >  
> > -static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)
> > +static void tegra186_bpmp_channel_deinit(struct tegra_bpmp *bpmp)
> > +{
> > +	int i;
> 
> Can be unsigned int. The preferred ordering for variable declarations is
> the inverse christmas tree (i.e. sort by length in decreasing order). It
> often matches the result of sorting by importance (i.e. the "priv"
> pointer is more important than the loop variable).
> 
> > +	struct tegra186_bpmp *priv = bpmp->priv;
> > +
> > +	for (i = 0; i < bpmp->threaded.count; i++) {
> > +		if (!bpmp->threaded_channels[i].bpmp)
> > +			continue;
> > +
> > +		tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
> > +	}
> > +
> > +	tegra186_bpmp_channel_cleanup(bpmp->rx_channel);
> > +	tegra186_bpmp_channel_cleanup(bpmp->tx_channel);
> > +
> > +	if (priv->type == TEGRA_SRAM) {
> > +		gen_pool_free(priv->sram.tx, (unsigned long)priv->tx.virt, 4096);
> > +		gen_pool_free(priv->sram.rx, (unsigned long)priv->rx.virt, 4096);
> > +	} else if (priv->type == TEGRA_RMEM) {
> > +		memunmap(priv->tx.virt);
> > +	}
> 
> This introduces a bit of an asymmetry because tegra_bpmp_channel_setup()
> doesn't actually set up the pool or reserved-memory. Since the memory is
> only used for the channels, we can probably move the initialization into
> tegra186_bpmp_channel_setup() below.

This is the teardown, not the initialization?

> 
> > +}
> > +
> > +static int tegra186_bpmp_channel_setup(struct tegra_bpmp *bpmp)
> 
> This name could be confusing because we already use the
> tegra186_bpmp_channel_ prefix for functions that operate on individual
> channels, whereas this function operates on the BPMP object.
> 
> Perhaps something like tegra186_bpmp_setup_channels() would better
> reflect what this does.
> 
> The same goes for tegra186_bpmp_channel_deinit() above. Maybe something
> like tegra186_bpmp_cleanup_channels() to make it more obvious that it's
> the counterpart of tegra186_bpmp_setup_channels().
> 
> >  {
> > -	struct tegra186_bpmp *priv;
> >  	unsigned int i;
> >  	int err;
> >  
> > -	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
> > -	if (!priv)
> > -		return -ENOMEM;
> > +	err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp,
> > +					 bpmp->soc->channels.cpu_tx.offset);
> > +	if (err < 0)
> > +		return err;
> >  
> > -	bpmp->priv = priv;
> > -	priv->parent = bpmp;
> > +	err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp,
> > +					 bpmp->soc->channels.cpu_rx.offset);
> > +	if (err < 0) {
> > +		tegra186_bpmp_channel_cleanup(bpmp->tx_channel);
> > +		return err;
> > +	}
> > +
> > +	for (i = 0; i < bpmp->threaded.count; i++) {
> > +		unsigned int index = bpmp->soc->channels.thread.offset + i;
> >  
> > -	priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0);
> > -	if (!priv->tx.pool) {
> > +		err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i],
> > +						 bpmp, index);
> > +		if (err < 0)
> > +			break;
> > +	}
> > +
> > +	if (err < 0)
> > +		tegra186_bpmp_channel_deinit(bpmp);
> 
> See how the name is confusing here? This is very close to the call to
> tegra186_bpmp_channel_init() above and the common prefix makes it seem
> like this would undo the effects of the above and then immediately
> raises the question why it's only undoing all of the above channel
> initializations. You then have to actually look at the implementation to
> find out that that's exactly what it does.
> 
> > +
> > +	return err;
> > +}
> > +
> > +static void tegra186_bpmp_reset_channels(struct tegra_bpmp *bpmp)
> > +{
> > +	unsigned int i;
> > +
> > +	tegra186_bpmp_channel_reset(bpmp->tx_channel);
> > +	tegra186_bpmp_channel_reset(bpmp->rx_channel);
> > +
> > +	for (i = 0; i < bpmp->threaded.count; i++)
> > +		tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]);
> > +}
> 
> I think this now matches the tegra186_bpmp_resume() implementation, so
> it could be reused in that.
> 

Ok.

> > +
> > +static int tegra186_bpmp_sram_init(struct tegra_bpmp *bpmp)
> > +{
> > +	int err;
> > +	struct tegra186_bpmp *priv = bpmp->priv;
> > +
> > +	priv->sram.tx = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0);
> > +	if (!priv->sram.tx) {
> >  		dev_err(bpmp->dev, "TX shmem pool not found\n");
> >  		return -EPROBE_DEFER;
> >  	}
> >  
> > -	priv->tx.virt = (void __iomem *)gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys);
> > +	priv->tx.virt = gen_pool_dma_alloc(priv->sram.tx, 4096, &priv->tx.phys);
> >  	if (!priv->tx.virt) {
> >  		dev_err(bpmp->dev, "failed to allocate from TX pool\n");
> >  		return -ENOMEM;
> >  	}
> >  
> > -	priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1);
> > -	if (!priv->rx.pool) {
> > +	priv->sram.rx = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1);
> > +	if (!priv->sram.rx) {
> >  		dev_err(bpmp->dev, "RX shmem pool not found\n");
> >  		err = -EPROBE_DEFER;
> >  		goto free_tx;
> >  	}
> >  
> > -	priv->rx.virt = (void __iomem *)gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys);
> > +	priv->rx.virt = gen_pool_dma_alloc(priv->sram.rx, 4096, &priv->rx.phys);
> >  	if (!priv->rx.virt) {
> >  		dev_err(bpmp->dev, "failed to allocate from RX pool\n");
> >  		err = -ENOMEM;
> >  		goto free_tx;
> >  	}
> >  
> > -	err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp,
> > -					 bpmp->soc->channels.cpu_tx.offset);
> > -	if (err < 0)
> > -		goto free_rx;
> > +	priv->type = TEGRA_SRAM;
> >  
> > -	err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp,
> > -					 bpmp->soc->channels.cpu_rx.offset);
> > -	if (err < 0)
> > -		goto cleanup_tx_channel;
> > +	return 0;
> >  
> > -	for (i = 0; i < bpmp->threaded.count; i++) {
> > -		unsigned int index = bpmp->soc->channels.thread.offset + i;
> > +free_tx:
> > +	gen_pool_free(priv->sram.tx, (unsigned long)priv->tx.virt, 4096);
> >  
> > -		err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i],
> > -						 bpmp, index);
> > +	return err;
> > +}
> > +
> > +static enum tegra_bpmp_mem_type tegra186_bpmp_dram_init(struct tegra_bpmp *bpmp)
> > +{
> > +	int err;
> > +	struct resource res;
> > +	struct device_node *np;
> > +	struct tegra186_bpmp *priv = bpmp->priv;
> > +
> > +	np = of_parse_phandle(bpmp->dev->of_node, "memory-region", 0);
> > +	if (!np)
> > +		return TEGRA_INVALID;
> > +
> > +	err = of_address_to_resource(np, 0, &res);
> > +	if (err) {
> > +		dev_warn(bpmp->dev,  "Parsing memory region returned: %d\n", err);
> > +		return TEGRA_INVALID;
> > +	}
> > +
> > +	if ((res.end - res.start + 1) < 0x2000) {
> 
> resource_size(), and maybe use SZ_8K instead of the literal here.
> 
> > +		dev_warn(bpmp->dev,  "DRAM region less than 0x2000 bytes\n");
> 
> Also, better to use a more human-readable string here. While at it,
> perhaps we can make this a bit more assertive, maybe something like:
> 
> 	"DRAM region must be larger than 8 KiB"
> 
> ?
> 
> > +		return TEGRA_INVALID;
> > +	}
> 
> This doesn't allow the caller to differentiate between potentially fatal
> errors and non-fatal ones. For instance, we don't want the absence of a
> "memory-region" property to be fatal (because we want to fall back to
> use SRAM in that case, or at least attempt to), but if "memory-region"
> exists, any of the subsequent errors probably should be fatal. It's
> easier to deal with that situation if you return regular error codes
> here. The !np check above could return -ENODEV, for example, as a way of
> letting the caller know that we don't have DRAM support in DT. For the
> of_address_to_resource() failure we can instead propagate the error code
> and so on.
> 
> Also, I think it'd be better to use a named constant like SZ_8K instead
> of the literal 0x2000 above. 
> 

Will look at this.

> > +
> > +	priv->tx.phys = res.start;
> > +	priv->rx.phys = res.start + 0x1000;
> 
> SZ_4K
> 
> > +
> > +	priv->tx.virt = memremap(priv->tx.phys, res.end - res.start + 1, MEMREMAP_WC);
> 
> Another case where we can use resource_size(). Might be a good idea to
> introduce a local "size" variable.
> 
> > +	if (priv->tx.virt == NULL) {
> > +		dev_warn(bpmp->dev,  "DRAM region mapping failed\n");
> > +		return TEGRA_INVALID;
> > +	}
> > +	priv->rx.virt = priv->tx.virt + 0x1000;
> 
> SZ_4K
> 
> We should probably do the same thing for the SRAM paths, but that should
> be a separate patch and can be done at another time.
> 

Yeah, this is basically the same as 4096 in the sram init but I think
hex is more readable.

> > +
> > +	return TEGRA_RMEM;
> > +}
> > +
> > +static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)
> > +{
> > +	struct tegra186_bpmp *priv;
> > +	int err;
> > +
> > +	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	bpmp->priv = priv;
> > +	priv->parent = bpmp;
> > +
> > +	priv->type = tegra186_bpmp_dram_init(bpmp);
> > +	if (priv->type == TEGRA_INVALID) {
> > +		err = tegra186_bpmp_sram_init(bpmp);
> >  		if (err < 0)
> > -			goto cleanup_channels;
> > +			return err;
> >  	}
> 
> As I mentioned previously, I think we can move the block above into
> tegra186_bpmp_setup_channels() to make it symmetric with the teardown of
> this in tegra186_bpmp_cleanup_channels().
> 
> Thierry
> 

Peter.



[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux