944 lines · 24.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	Copyright (C) 2014 Linaro Ltd. *	Author:	Ashwin Chaugule <ashwin.chaugule@linaro.org> * *  PCC (Platform Communication Channel) is defined in the ACPI 5.0+ *  specification. It is a mailbox like mechanism to allow clients *  such as CPPC (Collaborative Processor Performance Control), RAS *  (Reliability, Availability and Serviceability) and MPST (Memory *  Node Power State Table) to talk to the platform (e.g. BMC) through *  shared memory regions as defined in the PCC table entries. The PCC *  specification supports a Doorbell mechanism for the PCC clients *  to notify the platform about new data. This Doorbell information *  is also specified in each PCC table entry. * *  Typical high level flow of operation is: * *  PCC Reads: *  * Client tries to acquire a channel lock. *  * After it is acquired it writes READ cmd in communication region cmd *		address. *  * Client issues mbox_send_message() which rings the PCC doorbell *		for its PCC channel. *  * If command completes, then client has control over channel and *		it can proceed with its reads. *  * Client releases lock. * *  PCC Writes: *  * Client tries to acquire channel lock. *  * Client writes to its communication region after it acquires a *		channel lock. *  * Client writes WRITE cmd in communication region cmd address. *  * Client issues mbox_send_message() which rings the PCC doorbell *		for its PCC channel. *  * If command completes, then writes have succeeded and it can release *		the channel lock. * *  There is a Nominal latency defined for each channel which indicates *  how long to wait until a command completes. If command is not complete *  the client needs to retry or assume failure. * *	For more details about PCC, please see the ACPI specification from *  http://www.uefi.org/ACPIv5.1 Section 14. * *  This file implements PCC as a Mailbox controller and allows for PCC *  clients to be implemented as its Mailbox Client Channels. */ #include <linux/acpi.h>#include <linux/delay.h>#include <linux/io.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/log2.h>#include <linux/platform_device.h>#include <linux/mailbox_controller.h>#include <linux/mailbox_client.h>#include <linux/io-64-nonatomic-lo-hi.h>#include <acpi/pcc.h> #define MBOX_IRQ_NAME		"pcc-mbox" /** * struct pcc_chan_reg - PCC register bundle * * @vaddr: cached virtual address for this register * @gas: pointer to the generic address structure for this register * @preserve_mask: bitmask to preserve when writing to this register * @set_mask: bitmask to set when writing to this register * @status_mask: bitmask to determine and/or update the status for this register */struct pcc_chan_reg {	void __iomem *vaddr;	struct acpi_generic_address *gas;	u64 preserve_mask;	u64 set_mask;	u64 status_mask;}; /** * struct pcc_chan_info - PCC channel specific information * * @chan: PCC channel information with Shared Memory Region info * @db: PCC register bundle for the doorbell register * @plat_irq_ack: PCC register bundle for the platform interrupt acknowledge *	register * @cmd_complete: PCC register bundle for the command complete check register * @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: lockless flag used by type 3 initiator subspaces to filter *		platform interrupts. Only one transfer can occur at a time, but *		the interrupt handler may sample the flag on another CPU, so all *		accesses must use READ_ONCE() or WRITE_ONCE(). Other subspace *		types do not test it. */struct pcc_chan_info {	struct pcc_mbox_chan chan;	struct pcc_chan_reg db;	struct pcc_chan_reg plat_irq_ack;	struct pcc_chan_reg cmd_complete;	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; /* * PCC can be used with perf critical drivers such as CPPC * So it makes sense to locally cache the virtual address and * use it to read/write to PCC registers such as doorbell register * * The below read_register and write_registers are used to read and * write from perf critical registers such as PCC doorbell register */static void read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width){	switch (bit_width) {	case 8:		*val = readb(vaddr);		break;	case 16:		*val = readw(vaddr);		break;	case 32:		*val = readl(vaddr);		break;	case 64:		*val = readq(vaddr);		break;	}} static void write_register(void __iomem *vaddr, u64 val, unsigned int bit_width){	switch (bit_width) {	case 8:		writeb(val, vaddr);		break;	case 16:		writew(val, vaddr);		break;	case 32:		writel(val, vaddr);		break;	case 64:		writeq(val, vaddr);		break;	}} static int pcc_chan_reg_read(struct pcc_chan_reg *reg, u64 *val){	int ret = 0; 	if (!reg->gas) {		*val = 0;		return 0;	} 	if (reg->vaddr)		read_register(reg->vaddr, val, reg->gas->bit_width);	else		ret = acpi_read(val, reg->gas); 	return ret;} static int pcc_chan_reg_write(struct pcc_chan_reg *reg, u64 val){	int ret = 0; 	if (!reg->gas)		return 0; 	if (reg->vaddr)		write_register(reg->vaddr, val, reg->gas->bit_width);	else		ret = acpi_write(val, reg->gas); 	return ret;} static int pcc_chan_reg_read_modify_write(struct pcc_chan_reg *reg){	int ret = 0;	u64 val; 	ret = pcc_chan_reg_read(reg, &val);	if (ret)		return ret; 	val &= reg->preserve_mask;	val |= reg->set_mask; 	return pcc_chan_reg_write(reg, val);} /** * pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number * @interrupt: GSI number. * @flags: interrupt flags * * Returns: a valid linux IRQ number on success *		0 or -EINVAL on failure */static int pcc_map_interrupt(u32 interrupt, u32 flags){	int trigger, polarity; 	if (!interrupt)		return 0; 	trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE			: ACPI_LEVEL_SENSITIVE; 	polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW			: ACPI_ACTIVE_HIGH; 	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; 	if (!pchan->cmd_complete.gas)		return true; 	ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);	if (ret)		return false; 	/*	 * 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;} static int pcc_mbox_error_check_and_clear(struct pcc_chan_info *pchan){	u64 val;	int ret; 	ret = pcc_chan_reg_read(&pchan->error, &val);	if (ret)		return ret; 	if (val & pchan->error.status_mask) {		val &= pchan->error.preserve_mask;		pcc_chan_reg_write(&pchan->error, val);		return -EIO;	} 	return 0;} static void pcc_chan_acknowledge(struct pcc_chan_info *pchan){	struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_hdr; 	if (pchan->type != ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)		return; 	pcc_chan_reg_read_modify_write(&pchan->cmd_update); 	pcc_hdr = pchan->chan.shmem; 	/*	 * The PCC slave subspace channel needs to set the command	 * complete bit after processing message. If the PCC_ACK_FLAG	 * is set, it should also ring the doorbell.	 */	if (ioread32(&pcc_hdr->flags) & PCC_CMD_COMPLETION_NOTIFY)		pcc_chan_reg_read_modify_write(&pchan->db);} /** * 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){	struct pcc_chan_info *pchan;	struct mbox_chan *chan = p; 	pchan = chan->con_priv; 	if (pcc_chan_reg_read_modify_write(&pchan->plat_irq_ack))		return IRQ_NONE; 	/*	 * Initiator subspaces use this flag to filter shared interrupts. Use	 * READ_ONCE() to sample the lockless flag written by pcc_send_data()	 * on another CPU.	 */	if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE &&	    !READ_ONCE(pchan->chan_in_use))		return IRQ_NONE; 	if (!pcc_mbox_cmd_complete_check(pchan))		return IRQ_NONE; 	if (pcc_mbox_error_check_and_clear(pchan))		return IRQ_NONE; 	/*	 * Clear this flag after updating the interrupt ack register and before	 * notifying the client and mailbox core. mbox_chan_txdone() may submit	 * the next queued transfer and set the flag again. Use WRITE_ONCE() for	 * the lockless update observed by the send and interrupt paths.	 */	WRITE_ONCE(pchan->chan_in_use, false);	mbox_chan_received_data(chan, NULL);	mbox_chan_txdone(chan, 0); 	pcc_chan_acknowledge(pchan); 	return IRQ_HANDLED;} static int pcc_mbox_validate_signature(struct pcc_mbox_chan *pcc_mchan,				       int subspace_id){	u32 expected_signature = PCC_SIGNATURE | subspace_id;	u32 signature; 	if (pcc_mchan->shmem_size < sizeof(signature)) {		pr_err("PCC subspace %d shared memory is too small\n",		       subspace_id);		return -EINVAL;	} 	signature = ioread32(pcc_mchan->shmem);	if (signature != expected_signature)		pr_warn("PCC subspace %d invalid signature %#x expected %#x\n",			subspace_id, signature, expected_signature); 	return 0;} /** * pcc_mbox_request_channel - PCC clients call this function to *		request a pointer to their PCC subspace, from which they *		can get the details of communicating with the remote. * @cl: Pointer to Mailbox client, so we know where to bind the *		Channel. * @subspace_id: The PCC Subspace index as parsed in the PCC client *		ACPI package. This is used to lookup the array of PCC *		subspaces as parsed by the PCC Mailbox controller. * * Return: Pointer to the PCC Mailbox Channel if successful or ERR_PTR. */struct pcc_mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id){	struct pcc_mbox_chan *pcc_mchan;	struct pcc_chan_info *pchan;	struct mbox_chan *chan;	int rc; 	if (subspace_id < 0 || subspace_id >= pcc_chan_count)		return ERR_PTR(-ENOENT); 	pchan = chan_info + subspace_id;	chan = pchan->chan.mchan;	if (IS_ERR(chan) || chan->cl) {		pr_err("Channel not found for idx: %d\n", subspace_id);		return ERR_PTR(-EBUSY);	} 	pcc_mchan = &pchan->chan;	pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,					   pcc_mchan->shmem_size);	if (!pcc_mchan->shmem)		return ERR_PTR(-ENXIO); 	rc = pcc_mbox_validate_signature(pcc_mchan, subspace_id);	if (rc)		goto err_unmap_shmem; 	rc = mbox_bind_client(chan, cl);	if (rc)		goto err_unmap_shmem; 	return pcc_mchan; err_unmap_shmem:	iounmap(pcc_mchan->shmem);	pcc_mchan->shmem = NULL;	return ERR_PTR(rc);}EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); /** * pcc_mbox_free_channel - Clients call this to free their Channel. * * @pchan: Pointer to the PCC mailbox channel as returned by *	   pcc_mbox_request_channel() */void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan){	struct mbox_chan *chan = pchan->mchan;	struct pcc_chan_info *pchan_info;	struct pcc_mbox_chan *pcc_mbox_chan; 	if (!chan || !chan->cl)		return;	pchan_info = chan->con_priv;	pcc_mbox_chan = &pchan_info->chan;	if (pcc_mbox_chan->shmem) {		iounmap(pcc_mbox_chan->shmem);		pcc_mbox_chan->shmem = NULL;	} 	mbox_free_channel(chan);}EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); /** * pcc_send_data - Called from Mailbox Controller code. Used *		here only to ring the channel doorbell. The PCC client *		specific read/write is done in the client driver in *		order to maintain atomicity over PCC channel once *		OS has control over it. See above for flow of operations. * @chan: Pointer to Mailbox channel over which to send data. * @data: Client specific data written over channel. Used here *		only for debug after PCC transaction completes. * * Return: Err if something failed else 0 for success. */static int pcc_send_data(struct mbox_chan *chan, void *data){	int ret;	struct pcc_chan_info *pchan = chan->con_priv; 	ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update);	if (ret)		return ret; 	/*	 * Set chan_in_use before ringing the doorbell so a fast completion	 * interrupt is not mistaken for a shared interrupt from another	 * subspace. Use WRITE_ONCE() for the lockless flag update. The	 * ordered I/O accessor used to ring the doorbell orders this store	 * before the platform is notified.	 */	if (pchan->plat_irq > 0)		WRITE_ONCE(pchan->chan_in_use, true);	ret = pcc_chan_reg_read_modify_write(&pchan->db);	if (ret && pchan->plat_irq > 0)		WRITE_ONCE(pchan->chan_in_use, false); 	return ret;} static bool pcc_last_tx_done(struct mbox_chan *chan){	struct pcc_chan_info *pchan = chan->con_priv; 	if (!(chan->txdone_method & MBOX_TXDONE_BY_POLL))		return false; 	if (!pcc_mbox_cmd_complete_check(pchan))		return false; 	mbox_chan_received_data(chan, NULL); 	return true;}

Lines 1–500 of 944.