// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015, Sony Mobile Communications Inc. * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. */ #include <linux/interrupt.h> #include <linux/mailbox_client.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/spinlock.h> #include <linux/regmap.h> #include <linux/soc/qcom/smem.h> #include <linux/soc/qcom/smem_state.h> /* * This driver implements the Qualcomm Shared Memory State Machine, a mechanism * for communicating single bit state information to remote processors. * * The implementation is based on two sections of shared memory; the first * holding the state bits and the second holding a matrix of subscription bits. * * The state bits are structured in entries of 32 bits, each belonging to one * system in the SoC. The entry belonging to the local system is considered * read-write, while the rest should be considered read-only. * * The subscription matrix consists of N bitmaps per entry, denoting interest * in updates of the entry for each of the N hosts. Upon updating a state bit * each host's subscription bitmap should be queried and the remote system * should be interrupted if they request so. * * The subscription matrix is laid out in entry-major order: * entry0: [host0 ... hostN] * . * . * entryM: [host0 ... hostN] * * A third, optional, shared memory region might contain information regarding * the number of entries in the state bitmap as well as number of columns in * the subscription matrix. */ /* * Shared memory identifiers, used to acquire handles to respective memory * region. */ #define SMEM_SMSM_SHARED_STATE … #define SMEM_SMSM_CPU_INTR_MASK … #define SMEM_SMSM_SIZE_INFO … /* * Default sizes, in case SMEM_SMSM_SIZE_INFO is not found. */ #define SMSM_DEFAULT_NUM_ENTRIES … #define SMSM_DEFAULT_NUM_HOSTS … struct smsm_entry; struct smsm_host; /** * struct qcom_smsm - smsm driver context * @dev: smsm device pointer * @local_host: column in the subscription matrix representing this system * @num_hosts: number of columns in the subscription matrix * @num_entries: number of entries in the state map and rows in the subscription * matrix * @local_state: pointer to the local processor's state bits * @subscription: pointer to local processor's row in subscription matrix * @state: smem state handle * @lock: spinlock for read-modify-write of the outgoing state * @entries: context for each of the entries * @hosts: context for each of the hosts * @mbox_client: mailbox client handle */ struct qcom_smsm { … }; /** * struct smsm_entry - per remote processor entry context * @smsm: back-reference to driver context * @domain: IRQ domain for this entry, if representing a remote system * @irq_enabled: bitmap of which state bits IRQs are enabled * @irq_rising: bitmap tracking if rising bits should be propagated * @irq_falling: bitmap tracking if falling bits should be propagated * @last_value: snapshot of state bits last time the interrupts where propagated * @remote_state: pointer to this entry's state bits * @subscription: pointer to a row in the subscription matrix representing this * entry */ struct smsm_entry { … }; /** * struct smsm_host - representation of a remote host * @ipc_regmap: regmap for outgoing interrupt * @ipc_offset: offset in @ipc_regmap for outgoing interrupt * @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt * @mbox_chan: apcs ipc mailbox channel handle */ struct smsm_host { … }; /** * smsm_update_bits() - change bit in outgoing entry and inform subscribers * @data: smsm context pointer * @mask: value mask * @value: new value * * Used to set and clear the bits in the outgoing/local entry and inform * subscribers about the change. */ static int smsm_update_bits(void *data, u32 mask, u32 value) { … } static const struct qcom_smem_state_ops smsm_state_ops = …; /** * smsm_intr() - cascading IRQ handler for SMSM * @irq: unused * @data: entry related to this IRQ * * This function cascades an incoming interrupt from a remote system, based on * the state bits and configuration. */ static irqreturn_t smsm_intr(int irq, void *data) { … } /** * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit * @irqd: IRQ handle to be masked * * This un-subscribes the local CPU from interrupts upon changes to the defines * status bit. The bit is also cleared from cascading. */ static void smsm_mask_irq(struct irq_data *irqd) { … } /** * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit * @irqd: IRQ handle to be unmasked * * This subscribes the local CPU to interrupts upon changes to the defined * status bit. The bit is also marked for cascading. */ static void smsm_unmask_irq(struct irq_data *irqd) { … } /** * smsm_set_irq_type() - updates the requested IRQ type for the cascading * @irqd: consumer interrupt handle * @type: requested flags */ static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type) { … } static int smsm_get_irqchip_state(struct irq_data *irqd, enum irqchip_irq_state which, bool *state) { … } static struct irq_chip smsm_irq_chip = …; /** * smsm_irq_map() - sets up a mapping for a cascaded IRQ * @d: IRQ domain representing an entry * @irq: IRQ to set up * @hw: unused */ static int smsm_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { … } static const struct irq_domain_ops smsm_irq_ops = …; /** * smsm_parse_mbox() - requests an mbox channel * @smsm: smsm driver context * @host_id: index of the remote host to be resolved * * Requests the desired channel using the mbox interface which is needed for * sending the outgoing interrupts to a remove hosts - identified by @host_id. */ static int smsm_parse_mbox(struct qcom_smsm *smsm, unsigned int host_id) { … } /** * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property * @smsm: smsm driver context * @host_id: index of the remote host to be resolved * * Parses device tree to acquire the information needed for sending the * outgoing interrupts to a remote host - identified by @host_id. */ static int smsm_parse_ipc(struct qcom_smsm *smsm, unsigned host_id) { … } /** * smsm_inbound_entry() - parse DT and set up an entry representing a remote system * @smsm: smsm driver context * @entry: entry context to be set up * @node: dt node containing the entry's properties */ static int smsm_inbound_entry(struct qcom_smsm *smsm, struct smsm_entry *entry, struct device_node *node) { … } /** * smsm_get_size_info() - parse the optional memory segment for sizes * @smsm: smsm driver context * * Attempt to acquire the number of hosts and entries from the optional shared * memory location. Not being able to find this segment should indicate that * we're on a older system where these values was hard coded to * SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS. * * Returns 0 on success, negative errno on failure. */ static int smsm_get_size_info(struct qcom_smsm *smsm) { … } static int qcom_smsm_probe(struct platform_device *pdev) { … } static void qcom_smsm_remove(struct platform_device *pdev) { … } static const struct of_device_id qcom_smsm_of_match[] = …; MODULE_DEVICE_TABLE(of, qcom_smsm_of_match); static struct platform_driver qcom_smsm_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;