18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * PCC (Platform Communication Channel) is defined in the ACPI 5.0+ 78c2ecf20Sopenharmony_ci * specification. It is a mailbox like mechanism to allow clients 88c2ecf20Sopenharmony_ci * such as CPPC (Collaborative Processor Performance Control), RAS 98c2ecf20Sopenharmony_ci * (Reliability, Availability and Serviceability) and MPST (Memory 108c2ecf20Sopenharmony_ci * Node Power State Table) to talk to the platform (e.g. BMC) through 118c2ecf20Sopenharmony_ci * shared memory regions as defined in the PCC table entries. The PCC 128c2ecf20Sopenharmony_ci * specification supports a Doorbell mechanism for the PCC clients 138c2ecf20Sopenharmony_ci * to notify the platform about new data. This Doorbell information 148c2ecf20Sopenharmony_ci * is also specified in each PCC table entry. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Typical high level flow of operation is: 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * PCC Reads: 198c2ecf20Sopenharmony_ci * * Client tries to acquire a channel lock. 208c2ecf20Sopenharmony_ci * * After it is acquired it writes READ cmd in communication region cmd 218c2ecf20Sopenharmony_ci * address. 228c2ecf20Sopenharmony_ci * * Client issues mbox_send_message() which rings the PCC doorbell 238c2ecf20Sopenharmony_ci * for its PCC channel. 248c2ecf20Sopenharmony_ci * * If command completes, then client has control over channel and 258c2ecf20Sopenharmony_ci * it can proceed with its reads. 268c2ecf20Sopenharmony_ci * * Client releases lock. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * PCC Writes: 298c2ecf20Sopenharmony_ci * * Client tries to acquire channel lock. 308c2ecf20Sopenharmony_ci * * Client writes to its communication region after it acquires a 318c2ecf20Sopenharmony_ci * channel lock. 328c2ecf20Sopenharmony_ci * * Client writes WRITE cmd in communication region cmd address. 338c2ecf20Sopenharmony_ci * * Client issues mbox_send_message() which rings the PCC doorbell 348c2ecf20Sopenharmony_ci * for its PCC channel. 358c2ecf20Sopenharmony_ci * * If command completes, then writes have succeded and it can release 368c2ecf20Sopenharmony_ci * the channel lock. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * There is a Nominal latency defined for each channel which indicates 398c2ecf20Sopenharmony_ci * how long to wait until a command completes. If command is not complete 408c2ecf20Sopenharmony_ci * the client needs to retry or assume failure. 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * For more details about PCC, please see the ACPI specification from 438c2ecf20Sopenharmony_ci * http://www.uefi.org/ACPIv5.1 Section 14. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * This file implements PCC as a Mailbox controller and allows for PCC 468c2ecf20Sopenharmony_ci * clients to be implemented as its Mailbox Client Channels. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include <linux/acpi.h> 508c2ecf20Sopenharmony_ci#include <linux/delay.h> 518c2ecf20Sopenharmony_ci#include <linux/io.h> 528c2ecf20Sopenharmony_ci#include <linux/init.h> 538c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 548c2ecf20Sopenharmony_ci#include <linux/list.h> 558c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 568c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 578c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h> 588c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 598c2ecf20Sopenharmony_ci#include <acpi/pcc.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include "mailbox.h" 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define MBOX_IRQ_NAME "pcc-mbox" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct mbox_chan *pcc_mbox_channels; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Array of cached virtual address for doorbell registers */ 688c2ecf20Sopenharmony_cistatic void __iomem **pcc_doorbell_vaddr; 698c2ecf20Sopenharmony_ci/* Array of cached virtual address for doorbell ack registers */ 708c2ecf20Sopenharmony_cistatic void __iomem **pcc_doorbell_ack_vaddr; 718c2ecf20Sopenharmony_ci/* Array of doorbell interrupts */ 728c2ecf20Sopenharmony_cistatic int *pcc_doorbell_irq; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic struct mbox_controller pcc_mbox_ctrl = {}; 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * get_pcc_channel - Given a PCC subspace idx, get 778c2ecf20Sopenharmony_ci * the respective mbox_channel. 788c2ecf20Sopenharmony_ci * @id: PCC subspace index. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Return: ERR_PTR(errno) if error, else pointer 818c2ecf20Sopenharmony_ci * to mbox channel. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic struct mbox_chan *get_pcc_channel(int id) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci if (id < 0 || id >= pcc_mbox_ctrl.num_chans) 868c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return &pcc_mbox_channels[id]; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * PCC can be used with perf critical drivers such as CPPC 938c2ecf20Sopenharmony_ci * So it makes sense to locally cache the virtual address and 948c2ecf20Sopenharmony_ci * use it to read/write to PCC registers such as doorbell register 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * The below read_register and write_registers are used to read and 978c2ecf20Sopenharmony_ci * write from perf critical registers such as PCC doorbell register 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int ret_val = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci switch (bit_width) { 1048c2ecf20Sopenharmony_ci case 8: 1058c2ecf20Sopenharmony_ci *val = readb(vaddr); 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case 16: 1088c2ecf20Sopenharmony_ci *val = readw(vaddr); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci case 32: 1118c2ecf20Sopenharmony_ci *val = readl(vaddr); 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case 64: 1148c2ecf20Sopenharmony_ci *val = readq(vaddr); 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci default: 1178c2ecf20Sopenharmony_ci pr_debug("Error: Cannot read register of %u bit width", 1188c2ecf20Sopenharmony_ci bit_width); 1198c2ecf20Sopenharmony_ci ret_val = -EFAULT; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci return ret_val; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int ret_val = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci switch (bit_width) { 1308c2ecf20Sopenharmony_ci case 8: 1318c2ecf20Sopenharmony_ci writeb(val, vaddr); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case 16: 1348c2ecf20Sopenharmony_ci writew(val, vaddr); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case 32: 1378c2ecf20Sopenharmony_ci writel(val, vaddr); 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case 64: 1408c2ecf20Sopenharmony_ci writeq(val, vaddr); 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci default: 1438c2ecf20Sopenharmony_ci pr_debug("Error: Cannot write register of %u bit width", 1448c2ecf20Sopenharmony_ci bit_width); 1458c2ecf20Sopenharmony_ci ret_val = -EFAULT; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci return ret_val; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/** 1528c2ecf20Sopenharmony_ci * pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number 1538c2ecf20Sopenharmony_ci * @interrupt: GSI number. 1548c2ecf20Sopenharmony_ci * @flags: interrupt flags 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Returns: a valid linux IRQ number on success 1578c2ecf20Sopenharmony_ci * 0 or -EINVAL on failure 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic int pcc_map_interrupt(u32 interrupt, u32 flags) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int trigger, polarity; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!interrupt) 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE 1678c2ecf20Sopenharmony_ci : ACPI_LEVEL_SENSITIVE; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW 1708c2ecf20Sopenharmony_ci : ACPI_ACTIVE_HIGH; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return acpi_register_gsi(NULL, interrupt, trigger, polarity); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * pcc_mbox_irq - PCC mailbox interrupt handler 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic irqreturn_t pcc_mbox_irq(int irq, void *p) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct acpi_generic_address *doorbell_ack; 1818c2ecf20Sopenharmony_ci struct acpi_pcct_hw_reduced *pcct_ss; 1828c2ecf20Sopenharmony_ci struct mbox_chan *chan = p; 1838c2ecf20Sopenharmony_ci u64 doorbell_ack_preserve; 1848c2ecf20Sopenharmony_ci u64 doorbell_ack_write; 1858c2ecf20Sopenharmony_ci u64 doorbell_ack_val; 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci pcct_ss = chan->con_priv; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci mbox_chan_received_data(chan, NULL); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { 1938c2ecf20Sopenharmony_ci struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv; 1948c2ecf20Sopenharmony_ci u32 id = chan - pcc_mbox_channels; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci doorbell_ack = &pcct2_ss->platform_ack_register; 1978c2ecf20Sopenharmony_ci doorbell_ack_preserve = pcct2_ss->ack_preserve_mask; 1988c2ecf20Sopenharmony_ci doorbell_ack_write = pcct2_ss->ack_write_mask; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = read_register(pcc_doorbell_ack_vaddr[id], 2018c2ecf20Sopenharmony_ci &doorbell_ack_val, 2028c2ecf20Sopenharmony_ci doorbell_ack->bit_width); 2038c2ecf20Sopenharmony_ci if (ret) 2048c2ecf20Sopenharmony_ci return IRQ_NONE; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = write_register(pcc_doorbell_ack_vaddr[id], 2078c2ecf20Sopenharmony_ci (doorbell_ack_val & doorbell_ack_preserve) 2088c2ecf20Sopenharmony_ci | doorbell_ack_write, 2098c2ecf20Sopenharmony_ci doorbell_ack->bit_width); 2108c2ecf20Sopenharmony_ci if (ret) 2118c2ecf20Sopenharmony_ci return IRQ_NONE; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/** 2188c2ecf20Sopenharmony_ci * pcc_mbox_request_channel - PCC clients call this function to 2198c2ecf20Sopenharmony_ci * request a pointer to their PCC subspace, from which they 2208c2ecf20Sopenharmony_ci * can get the details of communicating with the remote. 2218c2ecf20Sopenharmony_ci * @cl: Pointer to Mailbox client, so we know where to bind the 2228c2ecf20Sopenharmony_ci * Channel. 2238c2ecf20Sopenharmony_ci * @subspace_id: The PCC Subspace index as parsed in the PCC client 2248c2ecf20Sopenharmony_ci * ACPI package. This is used to lookup the array of PCC 2258c2ecf20Sopenharmony_ci * subspaces as parsed by the PCC Mailbox controller. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Return: Pointer to the Mailbox Channel if successful or 2288c2ecf20Sopenharmony_ci * ERR_PTR. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_cistruct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, 2318c2ecf20Sopenharmony_ci int subspace_id) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct device *dev = pcc_mbox_ctrl.dev; 2348c2ecf20Sopenharmony_ci struct mbox_chan *chan; 2358c2ecf20Sopenharmony_ci unsigned long flags; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * Each PCC Subspace is a Mailbox Channel. 2398c2ecf20Sopenharmony_ci * The PCC Clients get their PCC Subspace ID 2408c2ecf20Sopenharmony_ci * from their own tables and pass it here. 2418c2ecf20Sopenharmony_ci * This returns a pointer to the PCC subspace 2428c2ecf20Sopenharmony_ci * for the Client to operate on. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci chan = get_pcc_channel(subspace_id); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (IS_ERR(chan) || chan->cl) { 2478c2ecf20Sopenharmony_ci dev_err(dev, "Channel not found for idx: %d\n", subspace_id); 2488c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 2528c2ecf20Sopenharmony_ci chan->msg_free = 0; 2538c2ecf20Sopenharmony_ci chan->msg_count = 0; 2548c2ecf20Sopenharmony_ci chan->active_req = NULL; 2558c2ecf20Sopenharmony_ci chan->cl = cl; 2568c2ecf20Sopenharmony_ci init_completion(&chan->tx_complete); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) 2598c2ecf20Sopenharmony_ci chan->txdone_method = TXDONE_BY_ACK; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (pcc_doorbell_irq[subspace_id] > 0) { 2648c2ecf20Sopenharmony_ci int rc; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id], 2678c2ecf20Sopenharmony_ci pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan); 2688c2ecf20Sopenharmony_ci if (unlikely(rc)) { 2698c2ecf20Sopenharmony_ci dev_err(dev, "failed to register PCC interrupt %d\n", 2708c2ecf20Sopenharmony_ci pcc_doorbell_irq[subspace_id]); 2718c2ecf20Sopenharmony_ci pcc_mbox_free_channel(chan); 2728c2ecf20Sopenharmony_ci chan = ERR_PTR(rc); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return chan; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcc_mbox_request_channel); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/** 2818c2ecf20Sopenharmony_ci * pcc_mbox_free_channel - Clients call this to free their Channel. 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * @chan: Pointer to the mailbox channel as returned by 2848c2ecf20Sopenharmony_ci * pcc_mbox_request_channel() 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_civoid pcc_mbox_free_channel(struct mbox_chan *chan) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci u32 id = chan - pcc_mbox_channels; 2898c2ecf20Sopenharmony_ci unsigned long flags; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!chan || !chan->cl) 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (id >= pcc_mbox_ctrl.num_chans) { 2958c2ecf20Sopenharmony_ci pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n"); 2968c2ecf20Sopenharmony_ci return; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (pcc_doorbell_irq[id] > 0) 3008c2ecf20Sopenharmony_ci devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 3038c2ecf20Sopenharmony_ci chan->cl = NULL; 3048c2ecf20Sopenharmony_ci chan->active_req = NULL; 3058c2ecf20Sopenharmony_ci if (chan->txdone_method == TXDONE_BY_ACK) 3068c2ecf20Sopenharmony_ci chan->txdone_method = TXDONE_BY_POLL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pcc_mbox_free_channel); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/** 3138c2ecf20Sopenharmony_ci * pcc_send_data - Called from Mailbox Controller code. Used 3148c2ecf20Sopenharmony_ci * here only to ring the channel doorbell. The PCC client 3158c2ecf20Sopenharmony_ci * specific read/write is done in the client driver in 3168c2ecf20Sopenharmony_ci * order to maintain atomicity over PCC channel once 3178c2ecf20Sopenharmony_ci * OS has control over it. See above for flow of operations. 3188c2ecf20Sopenharmony_ci * @chan: Pointer to Mailbox channel over which to send data. 3198c2ecf20Sopenharmony_ci * @data: Client specific data written over channel. Used here 3208c2ecf20Sopenharmony_ci * only for debug after PCC transaction completes. 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * Return: Err if something failed else 0 for success. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_cistatic int pcc_send_data(struct mbox_chan *chan, void *data) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv; 3278c2ecf20Sopenharmony_ci struct acpi_generic_address *doorbell; 3288c2ecf20Sopenharmony_ci u64 doorbell_preserve; 3298c2ecf20Sopenharmony_ci u64 doorbell_val; 3308c2ecf20Sopenharmony_ci u64 doorbell_write; 3318c2ecf20Sopenharmony_ci u32 id = chan - pcc_mbox_channels; 3328c2ecf20Sopenharmony_ci int ret = 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (id >= pcc_mbox_ctrl.num_chans) { 3358c2ecf20Sopenharmony_ci pr_debug("pcc_send_data: Invalid mbox_chan passed\n"); 3368c2ecf20Sopenharmony_ci return -ENOENT; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci doorbell = &pcct_ss->doorbell_register; 3408c2ecf20Sopenharmony_ci doorbell_preserve = pcct_ss->preserve_mask; 3418c2ecf20Sopenharmony_ci doorbell_write = pcct_ss->write_mask; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Sync notification from OS to Platform. */ 3448c2ecf20Sopenharmony_ci if (pcc_doorbell_vaddr[id]) { 3458c2ecf20Sopenharmony_ci ret = read_register(pcc_doorbell_vaddr[id], &doorbell_val, 3468c2ecf20Sopenharmony_ci doorbell->bit_width); 3478c2ecf20Sopenharmony_ci if (ret) 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci ret = write_register(pcc_doorbell_vaddr[id], 3508c2ecf20Sopenharmony_ci (doorbell_val & doorbell_preserve) | doorbell_write, 3518c2ecf20Sopenharmony_ci doorbell->bit_width); 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci ret = acpi_read(&doorbell_val, doorbell); 3548c2ecf20Sopenharmony_ci if (ret) 3558c2ecf20Sopenharmony_ci return ret; 3568c2ecf20Sopenharmony_ci ret = acpi_write((doorbell_val & doorbell_preserve) | doorbell_write, 3578c2ecf20Sopenharmony_ci doorbell); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops pcc_chan_ops = { 3638c2ecf20Sopenharmony_ci .send_data = pcc_send_data, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/** 3678c2ecf20Sopenharmony_ci * parse_pcc_subspaces -- Count PCC subspaces defined 3688c2ecf20Sopenharmony_ci * @header: Pointer to the ACPI subtable header under the PCCT. 3698c2ecf20Sopenharmony_ci * @end: End of subtable entry. 3708c2ecf20Sopenharmony_ci * 3718c2ecf20Sopenharmony_ci * Return: If we find a PCC subspace entry of a valid type, return 0. 3728c2ecf20Sopenharmony_ci * Otherwise, return -EINVAL. 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * This gets called for each entry in the PCC table. 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_cistatic int parse_pcc_subspace(union acpi_subtable_headers *header, 3778c2ecf20Sopenharmony_ci const unsigned long end) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct acpi_pcct_subspace *ss = (struct acpi_pcct_subspace *) header; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (ss->header.type < ACPI_PCCT_TYPE_RESERVED) 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/** 3888c2ecf20Sopenharmony_ci * pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register 3898c2ecf20Sopenharmony_ci * There should be one entry per PCC client. 3908c2ecf20Sopenharmony_ci * @id: PCC subspace index. 3918c2ecf20Sopenharmony_ci * @pcct_ss: Pointer to the ACPI subtable header under the PCCT. 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * Return: 0 for Success, else errno. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * This gets called for each entry in the PCC table. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_cistatic int pcc_parse_subspace_irq(int id, 3988c2ecf20Sopenharmony_ci struct acpi_pcct_hw_reduced *pcct_ss) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->platform_interrupt, 4018c2ecf20Sopenharmony_ci (u32)pcct_ss->flags); 4028c2ecf20Sopenharmony_ci if (pcc_doorbell_irq[id] <= 0) { 4038c2ecf20Sopenharmony_ci pr_err("PCC GSI %d not registered\n", 4048c2ecf20Sopenharmony_ci pcct_ss->platform_interrupt); 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (pcct_ss->header.type 4098c2ecf20Sopenharmony_ci == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { 4108c2ecf20Sopenharmony_ci struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap( 4138c2ecf20Sopenharmony_ci pcct2_ss->platform_ack_register.address, 4148c2ecf20Sopenharmony_ci pcct2_ss->platform_ack_register.bit_width / 8); 4158c2ecf20Sopenharmony_ci if (!pcc_doorbell_ack_vaddr[id]) { 4168c2ecf20Sopenharmony_ci pr_err("Failed to ioremap PCC ACK register\n"); 4178c2ecf20Sopenharmony_ci return -ENOMEM; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/** 4258c2ecf20Sopenharmony_ci * acpi_pcc_probe - Parse the ACPI tree for the PCCT. 4268c2ecf20Sopenharmony_ci * 4278c2ecf20Sopenharmony_ci * Return: 0 for Success, else errno. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_cistatic int __init acpi_pcc_probe(void) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct acpi_table_header *pcct_tbl; 4328c2ecf20Sopenharmony_ci struct acpi_subtable_header *pcct_entry; 4338c2ecf20Sopenharmony_ci struct acpi_table_pcct *acpi_pcct_tbl; 4348c2ecf20Sopenharmony_ci struct acpi_subtable_proc proc[ACPI_PCCT_TYPE_RESERVED]; 4358c2ecf20Sopenharmony_ci int count, i, rc; 4368c2ecf20Sopenharmony_ci acpi_status status = AE_OK; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Search for PCCT */ 4398c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PCCT, 0, &pcct_tbl); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || !pcct_tbl) 4428c2ecf20Sopenharmony_ci return -ENODEV; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Set up the subtable handlers */ 4458c2ecf20Sopenharmony_ci for (i = ACPI_PCCT_TYPE_GENERIC_SUBSPACE; 4468c2ecf20Sopenharmony_ci i < ACPI_PCCT_TYPE_RESERVED; i++) { 4478c2ecf20Sopenharmony_ci proc[i].id = i; 4488c2ecf20Sopenharmony_ci proc[i].count = 0; 4498c2ecf20Sopenharmony_ci proc[i].handler = parse_pcc_subspace; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci count = acpi_table_parse_entries_array(ACPI_SIG_PCCT, 4538c2ecf20Sopenharmony_ci sizeof(struct acpi_table_pcct), proc, 4548c2ecf20Sopenharmony_ci ACPI_PCCT_TYPE_RESERVED, MAX_PCC_SUBSPACES); 4558c2ecf20Sopenharmony_ci if (count <= 0 || count > MAX_PCC_SUBSPACES) { 4568c2ecf20Sopenharmony_ci if (count < 0) 4578c2ecf20Sopenharmony_ci pr_warn("Error parsing PCC subspaces from PCCT\n"); 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci pr_warn("Invalid PCCT: %d PCC subspaces\n", count); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci rc = -EINVAL; 4628c2ecf20Sopenharmony_ci goto err_put_pcct; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci pcc_mbox_channels = kcalloc(count, sizeof(struct mbox_chan), 4668c2ecf20Sopenharmony_ci GFP_KERNEL); 4678c2ecf20Sopenharmony_ci if (!pcc_mbox_channels) { 4688c2ecf20Sopenharmony_ci pr_err("Could not allocate space for PCC mbox channels\n"); 4698c2ecf20Sopenharmony_ci rc = -ENOMEM; 4708c2ecf20Sopenharmony_ci goto err_put_pcct; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); 4748c2ecf20Sopenharmony_ci if (!pcc_doorbell_vaddr) { 4758c2ecf20Sopenharmony_ci rc = -ENOMEM; 4768c2ecf20Sopenharmony_ci goto err_free_mbox; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci pcc_doorbell_ack_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); 4808c2ecf20Sopenharmony_ci if (!pcc_doorbell_ack_vaddr) { 4818c2ecf20Sopenharmony_ci rc = -ENOMEM; 4828c2ecf20Sopenharmony_ci goto err_free_db_vaddr; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci pcc_doorbell_irq = kcalloc(count, sizeof(int), GFP_KERNEL); 4868c2ecf20Sopenharmony_ci if (!pcc_doorbell_irq) { 4878c2ecf20Sopenharmony_ci rc = -ENOMEM; 4888c2ecf20Sopenharmony_ci goto err_free_db_ack_vaddr; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Point to the first PCC subspace entry */ 4928c2ecf20Sopenharmony_ci pcct_entry = (struct acpi_subtable_header *) ( 4938c2ecf20Sopenharmony_ci (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl; 4968c2ecf20Sopenharmony_ci if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) 4978c2ecf20Sopenharmony_ci pcc_mbox_ctrl.txdone_irq = true; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 5008c2ecf20Sopenharmony_ci struct acpi_generic_address *db_reg; 5018c2ecf20Sopenharmony_ci struct acpi_pcct_subspace *pcct_ss; 5028c2ecf20Sopenharmony_ci pcc_mbox_channels[i].con_priv = pcct_entry; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE || 5058c2ecf20Sopenharmony_ci pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { 5068c2ecf20Sopenharmony_ci struct acpi_pcct_hw_reduced *pcct_hrss; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci pcct_hrss = (struct acpi_pcct_hw_reduced *) pcct_entry; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (pcc_mbox_ctrl.txdone_irq) { 5118c2ecf20Sopenharmony_ci rc = pcc_parse_subspace_irq(i, pcct_hrss); 5128c2ecf20Sopenharmony_ci if (rc < 0) 5138c2ecf20Sopenharmony_ci goto err; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci pcct_ss = (struct acpi_pcct_subspace *) pcct_entry; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* If doorbell is in system memory cache the virt address */ 5198c2ecf20Sopenharmony_ci db_reg = &pcct_ss->doorbell_register; 5208c2ecf20Sopenharmony_ci if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) 5218c2ecf20Sopenharmony_ci pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address, 5228c2ecf20Sopenharmony_ci db_reg->bit_width/8); 5238c2ecf20Sopenharmony_ci pcct_entry = (struct acpi_subtable_header *) 5248c2ecf20Sopenharmony_ci ((unsigned long) pcct_entry + pcct_entry->length); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci pcc_mbox_ctrl.num_chans = count; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cierr: 5348c2ecf20Sopenharmony_ci kfree(pcc_doorbell_irq); 5358c2ecf20Sopenharmony_cierr_free_db_ack_vaddr: 5368c2ecf20Sopenharmony_ci kfree(pcc_doorbell_ack_vaddr); 5378c2ecf20Sopenharmony_cierr_free_db_vaddr: 5388c2ecf20Sopenharmony_ci kfree(pcc_doorbell_vaddr); 5398c2ecf20Sopenharmony_cierr_free_mbox: 5408c2ecf20Sopenharmony_ci kfree(pcc_mbox_channels); 5418c2ecf20Sopenharmony_cierr_put_pcct: 5428c2ecf20Sopenharmony_ci acpi_put_table(pcct_tbl); 5438c2ecf20Sopenharmony_ci return rc; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/** 5478c2ecf20Sopenharmony_ci * pcc_mbox_probe - Called when we find a match for the 5488c2ecf20Sopenharmony_ci * PCCT platform device. This is purely used to represent 5498c2ecf20Sopenharmony_ci * the PCCT as a virtual device for registering with the 5508c2ecf20Sopenharmony_ci * generic Mailbox framework. 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * @pdev: Pointer to platform device returned when a match 5538c2ecf20Sopenharmony_ci * is found. 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * Return: 0 for Success, else errno. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic int pcc_mbox_probe(struct platform_device *pdev) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci int ret = 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci pcc_mbox_ctrl.chans = pcc_mbox_channels; 5628c2ecf20Sopenharmony_ci pcc_mbox_ctrl.ops = &pcc_chan_ops; 5638c2ecf20Sopenharmony_ci pcc_mbox_ctrl.dev = &pdev->dev; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci pr_info("Registering PCC driver as Mailbox controller\n"); 5668c2ecf20Sopenharmony_ci ret = mbox_controller_register(&pcc_mbox_ctrl); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (ret) { 5698c2ecf20Sopenharmony_ci pr_err("Err registering PCC as Mailbox controller: %d\n", ret); 5708c2ecf20Sopenharmony_ci ret = -ENODEV; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return ret; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic struct platform_driver pcc_mbox_driver = { 5778c2ecf20Sopenharmony_ci .probe = pcc_mbox_probe, 5788c2ecf20Sopenharmony_ci .driver = { 5798c2ecf20Sopenharmony_ci .name = "PCCT", 5808c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5818c2ecf20Sopenharmony_ci }, 5828c2ecf20Sopenharmony_ci}; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int __init pcc_init(void) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci int ret; 5878c2ecf20Sopenharmony_ci struct platform_device *pcc_pdev; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (acpi_disabled) 5908c2ecf20Sopenharmony_ci return -ENODEV; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Check if PCC support is available. */ 5938c2ecf20Sopenharmony_ci ret = acpi_pcc_probe(); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (ret) { 5968c2ecf20Sopenharmony_ci pr_debug("ACPI PCC probe failed.\n"); 5978c2ecf20Sopenharmony_ci return -ENODEV; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci pcc_pdev = platform_create_bundle(&pcc_mbox_driver, 6018c2ecf20Sopenharmony_ci pcc_mbox_probe, NULL, 0, NULL, 0); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (IS_ERR(pcc_pdev)) { 6048c2ecf20Sopenharmony_ci pr_debug("Err creating PCC platform bundle\n"); 6058c2ecf20Sopenharmony_ci return PTR_ERR(pcc_pdev); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return 0; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/* 6128c2ecf20Sopenharmony_ci * Make PCC init postcore so that users of this mailbox 6138c2ecf20Sopenharmony_ci * such as the ACPI Processor driver have it available 6148c2ecf20Sopenharmony_ci * at their init. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_cipostcore_initcall(pcc_init); 617