Unverified Commit da5320e4 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!8008 【OLK-5.10】backport PCC patches to support shared interrupt for multiple...

!8008 【OLK-5.10】backport PCC patches to support shared interrupt for multiple subspaces and platform notification handling

Merge Pull Request from: @huangdengdui 
 
It will never support multiple channel communication on kunpeng platform used shared interrupt if has not this patch.
1bd4ab6e4d11 mailbox: pcc: Support shared interrupt for multiple subspaces
08436163fa34 mailbox: pcc: Add support for platform notification handling
d9179f06ec08 mailbox: pcc: Use mbox_bind_client
c3900f10 mailbox: omap: Use mbox_bind_client
991e5781 mailbox: Allow direct registration to a channel
ac47fcdd mailbox: pcc: Fix kernel doc warnings

https://gitee.com/openeuler/kernel/issues/I9S5DK 
 
Link:https://gitee.com/openeuler/kernel/pulls/8008

 

Reviewed-by: default avatarXiongfeng Wang <wangxiongfeng2@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parents ecd3e628 44c680f8
Loading
Loading
Loading
Loading
+68 −28
Original line number Diff line number Diff line
@@ -317,6 +317,71 @@ int mbox_flush(struct mbox_chan *chan, unsigned long timeout)
}
EXPORT_SYMBOL_GPL(mbox_flush);

static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
{
	struct device *dev = cl->dev;
	unsigned long flags;
	int ret;

	if (chan->cl || !try_module_get(chan->mbox->dev->driver->owner)) {
		dev_dbg(dev, "%s: mailbox not free\n", __func__);
		return -EBUSY;
	}

	spin_lock_irqsave(&chan->lock, flags);
	chan->msg_free = 0;
	chan->msg_count = 0;
	chan->active_req = NULL;
	chan->cl = cl;
	init_completion(&chan->tx_complete);

	if (chan->txdone_method	== TXDONE_BY_POLL && cl->knows_txdone)
		chan->txdone_method = TXDONE_BY_ACK;

	spin_unlock_irqrestore(&chan->lock, flags);

	if (chan->mbox->ops->startup) {
		ret = chan->mbox->ops->startup(chan);

		if (ret) {
			dev_err(dev, "Unable to startup the chan (%d)\n", ret);
			mbox_free_channel(chan);
			return ret;
		}
	}

	return 0;
}

/**
 * mbox_bind_client - Request a mailbox channel.
 * @chan: The mailbox channel to bind the client to.
 * @cl: Identity of the client requesting the channel.
 *
 * The Client specifies its requirements and capabilities while asking for
 * a mailbox channel. It can't be called from atomic context.
 * The channel is exclusively allocated and can't be used by another
 * client before the owner calls mbox_free_channel.
 * After assignment, any packet received on this channel will be
 * handed over to the client via the 'rx_callback'.
 * The framework holds reference to the client, so the mbox_client
 * structure shouldn't be modified until the mbox_free_channel returns.
 *
 * Return: 0 if the channel was assigned to the client successfully.
 *         <0 for request failure.
 */
int mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
{
	int ret;

	mutex_lock(&con_mutex);
	ret = __mbox_bind_client(chan, cl);
	mutex_unlock(&con_mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(mbox_bind_client);

/**
 * mbox_request_channel - Request a mailbox channel.
 * @cl: Identity of the client requesting the channel.
@@ -340,7 +405,6 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
	struct mbox_controller *mbox;
	struct of_phandle_args spec;
	struct mbox_chan *chan;
	unsigned long flags;
	int ret;

	if (!dev || !dev->of_node) {
@@ -372,33 +436,9 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
		return chan;
	}

	if (chan->cl || !try_module_get(mbox->dev->driver->owner)) {
		dev_dbg(dev, "%s: mailbox not free\n", __func__);
		mutex_unlock(&con_mutex);
		return ERR_PTR(-EBUSY);
	}

	spin_lock_irqsave(&chan->lock, flags);
	chan->msg_free = 0;
	chan->msg_count = 0;
	chan->active_req = NULL;
	chan->cl = cl;
	init_completion(&chan->tx_complete);

	if (chan->txdone_method	== TXDONE_BY_POLL && cl->knows_txdone)
		chan->txdone_method = TXDONE_BY_ACK;

	spin_unlock_irqrestore(&chan->lock, flags);

	if (chan->mbox->ops->startup) {
		ret = chan->mbox->ops->startup(chan);

		if (ret) {
			dev_err(dev, "Unable to startup the chan (%d)\n", ret);
			mbox_free_channel(chan);
	ret = __mbox_bind_client(chan, cl);
	if (ret)
		chan = ERR_PTR(ret);
		}
	}

	mutex_unlock(&con_mutex);
	return chan;
+4 −18
Original line number Diff line number Diff line
@@ -417,8 +417,6 @@ struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl,
	struct device *dev = cl->dev;
	struct omap_mbox *mbox = NULL;
	struct omap_mbox_device *mdev;
	struct mbox_chan *chan;
	unsigned long flags;
	int ret;

	if (!dev)
@@ -441,23 +439,11 @@ struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl,
	if (!mbox || !mbox->chan)
		return ERR_PTR(-ENOENT);

	chan = mbox->chan;
	spin_lock_irqsave(&chan->lock, flags);
	chan->msg_free = 0;
	chan->msg_count = 0;
	chan->active_req = NULL;
	chan->cl = cl;
	init_completion(&chan->tx_complete);
	spin_unlock_irqrestore(&chan->lock, flags);

	ret = chan->mbox->ops->startup(chan);
	if (ret) {
		pr_err("Unable to startup the chan (%d)\n", ret);
		mbox_free_channel(chan);
		chan = ERR_PTR(ret);
	}
	ret = mbox_bind_client(mbox->chan, cl);
	if (ret)
		return ERR_PTR(ret);

	return chan;
	return mbox->chan;
}
EXPORT_SYMBOL(omap_mbox_request_channel);

+128 −49
Original line number Diff line number Diff line
@@ -91,6 +91,14 @@ struct pcc_chan_reg {
 * @cmd_update: PCC register bundle for the command complete update register
 * @error: PCC register bundle for the error status register
 * @plat_irq: platform interrupt
 * @type: PCC subspace type
 * @plat_irq_flags: platform interrupt flags
 * @chan_in_use: this flag is used just to check if the interrupt needs
 *		handling when it is shared. Since only one transfer can occur
 *		at a time and mailbox takes care of locking, this flag can be
 *		accessed without a lock. Note: the type only support the
 *		communication from OSPM to Platform, like type3, use it, and
 *		other types completely ignore it.
 */
struct pcc_chan_info {
	struct pcc_mbox_chan chan;
@@ -100,12 +108,17 @@ struct pcc_chan_info {
	struct pcc_chan_reg cmd_update;
	struct pcc_chan_reg error;
	int plat_irq;
	u8 type;
	unsigned int plat_irq_flags;
	bool chan_in_use;
};

#define to_pcc_chan_info(c) container_of(c, struct pcc_chan_info, chan)
static struct pcc_chan_info *chan_info;
static int pcc_chan_count;

static int pcc_send_data(struct mbox_chan *chan, void *data);

/*
 * PCC can be used with perf critical drivers such as CPPC
 * So it makes sense to locally cache the virtual address and
@@ -221,8 +234,47 @@ static int pcc_map_interrupt(u32 interrupt, u32 flags)
	return acpi_register_gsi(NULL, interrupt, trigger, polarity);
}

static bool pcc_chan_plat_irq_can_be_shared(struct pcc_chan_info *pchan)
{
	return (pchan->plat_irq_flags & ACPI_PCCT_INTERRUPT_MODE) ==
		ACPI_LEVEL_SENSITIVE;
}

static bool pcc_mbox_cmd_complete_check(struct pcc_chan_info *pchan)
{
	u64 val;
	int ret;

	ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);
	if (ret)
		return false;

	if (!pchan->cmd_complete.gas)
		return true;

	/*
	 * Judge if the channel respond the interrupt based on the value of
	 * command complete.
	 */
	val &= pchan->cmd_complete.status_mask;

	/*
	 * If this is PCC slave subspace channel, and the command complete
	 * bit 0 indicates that Platform is sending a notification and OSPM
	 * needs to respond this interrupt to process this command.
	 */
	if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
		return !val;

	return !!val;
}

/**
 * pcc_mbox_irq - PCC mailbox interrupt handler
 * @irq:	interrupt number
 * @p: data/cookie passed from the caller to identify the channel
 *
 * Returns: IRQ_HANDLED if interrupt is handled or IRQ_NONE if not
 */
static irqreturn_t pcc_mbox_irq(int irq, void *p)
{
@@ -232,16 +284,12 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
	int ret;

	pchan = chan->con_priv;

	ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);
	if (ret)
	if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE &&
	    !pchan->chan_in_use)
		return IRQ_NONE;

	if (val) { /* Ensure GAS exists and value is non-zero */
		val &= pchan->cmd_complete.status_mask;
		if (!val)
	if (!pcc_mbox_cmd_complete_check(pchan))
		return IRQ_NONE;
	}

	ret = pcc_chan_reg_read(&pchan->error, &val);
	if (ret)
@@ -258,6 +306,16 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)

	mbox_chan_received_data(chan, NULL);

	/*
	 * The PCC slave subspace channel needs to set the command complete bit
	 * and ring doorbell after processing message.
	 *
	 * The PCC master subspace channel clears chan_in_use to free channel.
	 */
	if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
		pcc_send_data(chan, NULL);
	pchan->chan_in_use = false;

	return IRQ_HANDLED;
}

@@ -278,8 +336,7 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
{
	struct pcc_chan_info *pchan;
	struct mbox_chan *chan;
	struct device *dev;
	unsigned long flags;
	int rc;

	if (subspace_id < 0 || subspace_id >= pcc_chan_count)
		return ERR_PTR(-ENOENT);
@@ -290,32 +347,10 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
		pr_err("Channel not found for idx: %d\n", subspace_id);
		return ERR_PTR(-EBUSY);
	}
	dev = chan->mbox->dev;

	spin_lock_irqsave(&chan->lock, flags);
	chan->msg_free = 0;
	chan->msg_count = 0;
	chan->active_req = NULL;
	chan->cl = cl;
	init_completion(&chan->tx_complete);

	if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
		chan->txdone_method = TXDONE_BY_ACK;

	spin_unlock_irqrestore(&chan->lock, flags);

	if (pchan->plat_irq > 0) {
		int rc;

		rc = devm_request_irq(dev, pchan->plat_irq, pcc_mbox_irq, 0,
				      MBOX_IRQ_NAME, chan);
		if (unlikely(rc)) {
			dev_err(dev, "failed to register PCC interrupt %d\n",
				pchan->plat_irq);
			pcc_mbox_free_channel(&pchan->chan);
	rc = mbox_bind_client(chan, cl);
	if (rc)
		return ERR_PTR(rc);
		}
	}

	return &pchan->chan;
}
@@ -329,23 +364,12 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
 */
void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
{
	struct pcc_chan_info *pchan_info = to_pcc_chan_info(pchan);
	struct mbox_chan *chan = pchan->mchan;
	unsigned long flags;

	if (!chan || !chan->cl)
		return;

	if (pchan_info->plat_irq > 0)
		devm_free_irq(chan->mbox->dev, pchan_info->plat_irq, chan);

	spin_lock_irqsave(&chan->lock, flags);
	chan->cl = NULL;
	chan->active_req = NULL;
	if (chan->txdone_method == TXDONE_BY_ACK)
		chan->txdone_method = TXDONE_BY_POLL;

	spin_unlock_irqrestore(&chan->lock, flags);
	mbox_free_channel(chan);
}
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);

@@ -370,15 +394,62 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
	if (ret)
		return ret;

	return pcc_chan_reg_read_modify_write(&pchan->db);
	ret = pcc_chan_reg_read_modify_write(&pchan->db);
	if (!ret && pchan->plat_irq > 0)
		pchan->chan_in_use = true;

	return ret;
}

/**
 * pcc_startup - Called from Mailbox Controller code. Used here
 *		to request the interrupt.
 * @chan: Pointer to Mailbox channel to startup.
 *
 * Return: Err if something failed else 0 for success.
 */
static int pcc_startup(struct mbox_chan *chan)
{
	struct pcc_chan_info *pchan = chan->con_priv;
	unsigned long irqflags;
	int rc;

	if (pchan->plat_irq > 0) {
		irqflags = pcc_chan_plat_irq_can_be_shared(pchan) ?
						IRQF_SHARED | IRQF_ONESHOT : 0;
		rc = devm_request_irq(chan->mbox->dev, pchan->plat_irq, pcc_mbox_irq,
				      irqflags, MBOX_IRQ_NAME, chan);
		if (unlikely(rc)) {
			dev_err(chan->mbox->dev, "failed to register PCC interrupt %d\n",
				pchan->plat_irq);
			return rc;
		}
	}

	return 0;
}

/**
 * pcc_shutdown - Called from Mailbox Controller code. Used here
 *		to free the interrupt.
 * @chan: Pointer to Mailbox channel to shutdown.
 */
static void pcc_shutdown(struct mbox_chan *chan)
{
	struct pcc_chan_info *pchan = chan->con_priv;

	if (pchan->plat_irq > 0)
		devm_free_irq(chan->mbox->dev, pchan->plat_irq, chan);
}

static const struct mbox_chan_ops pcc_chan_ops = {
	.send_data = pcc_send_data,
	.startup = pcc_startup,
	.shutdown = pcc_shutdown,
};

/**
 * parse_pcc_subspaces -- Count PCC subspaces defined
 * parse_pcc_subspace - Count PCC subspaces defined
 * @header: Pointer to the ACPI subtable header under the PCCT.
 * @end: End of subtable entry.
 *
@@ -453,6 +524,7 @@ static int pcc_parse_subspace_irq(struct pcc_chan_info *pchan,
		       pcct_ss->platform_interrupt);
		return -EINVAL;
	}
	pchan->plat_irq_flags = pcct_ss->flags;

	if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
		struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss;
@@ -474,6 +546,12 @@ static int pcc_parse_subspace_irq(struct pcc_chan_info *pchan,
					"PLAT IRQ ACK");
	}

	if (pcc_chan_plat_irq_can_be_shared(pchan) &&
	    !pchan->plat_irq_ack.gas) {
		pr_err("PCC subspace has level IRQ with no ACK register\n");
		return -EINVAL;
	}

	return ret;
}

@@ -688,6 +766,7 @@ static int pcc_mbox_probe(struct platform_device *pdev)

		pcc_parse_subspace_shmem(pchan, pcct_entry);

		pchan->type = pcct_entry->type;
		pcct_entry = (struct acpi_subtable_header *)
			((unsigned long) pcct_entry + pcct_entry->length);
	}
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ struct mbox_client {
	void (*tx_done)(struct mbox_client *cl, void *mssg, int r);
};

int mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl);
struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
					      const char *name);
struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);