Lines Matching +full:shared +full:- +full:interrupt

1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
7 #include <linux/interrupt.h>
19 * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
22 * The implementation is based on two sections of shared memory; the first
27 * read-write, while the rest should be considered read-only.
34 * The subscription matrix is laid out in entry-major order:
40 * A third, optional, shared memory region might contain information regarding
46 * Shared memory identifiers, used to acquire handles to respective memory
63 * struct qcom_smsm - smsm driver context
72 * @lock: spinlock for read-modify-write of the outgoing state
98 * struct smsm_entry - per remote processor entry context
99 * @smsm: back-reference to driver context
123 * struct smsm_host - representation of a remote host
124 * @ipc_regmap: regmap for outgoing interrupt
125 * @ipc_offset: offset in @ipc_regmap for outgoing interrupt
126 * @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt
138 * smsm_update_bits() - change bit in outgoing entry and inform subscribers
156 spin_lock_irqsave(&smsm->lock, flags); in smsm_update_bits()
159 val = orig = readl(smsm->local_state); in smsm_update_bits()
166 spin_unlock_irqrestore(&smsm->lock, flags); in smsm_update_bits()
171 writel(val, smsm->local_state); in smsm_update_bits()
172 spin_unlock_irqrestore(&smsm->lock, flags); in smsm_update_bits()
178 for (host = 0; host < smsm->num_hosts; host++) { in smsm_update_bits()
179 hostp = &smsm->hosts[host]; in smsm_update_bits()
181 val = readl(smsm->subscription + host); in smsm_update_bits()
185 if (hostp->mbox_chan) { in smsm_update_bits()
186 mbox_send_message(hostp->mbox_chan, NULL); in smsm_update_bits()
187 mbox_client_txdone(hostp->mbox_chan, 0); in smsm_update_bits()
188 } else if (hostp->ipc_regmap) { in smsm_update_bits()
189 regmap_write(hostp->ipc_regmap, in smsm_update_bits()
190 hostp->ipc_offset, in smsm_update_bits()
191 BIT(hostp->ipc_bit)); in smsm_update_bits()
204 * smsm_intr() - cascading IRQ handler for SMSM
208 * This function cascades an incoming interrupt from a remote system, based on
219 val = readl(entry->remote_state); in smsm_intr()
220 changed = val ^ xchg(&entry->last_value, val); in smsm_intr()
222 for_each_set_bit(i, entry->irq_enabled, 32) { in smsm_intr()
227 if (test_bit(i, entry->irq_rising)) { in smsm_intr()
228 irq_pin = irq_find_mapping(entry->domain, i); in smsm_intr()
232 if (test_bit(i, entry->irq_falling)) { in smsm_intr()
233 irq_pin = irq_find_mapping(entry->domain, i); in smsm_intr()
243 * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
246 * This un-subscribes the local CPU from interrupts upon changes to the defines
253 struct qcom_smsm *smsm = entry->smsm; in smsm_mask_irq()
256 if (entry->subscription) { in smsm_mask_irq()
257 val = readl(entry->subscription + smsm->local_host); in smsm_mask_irq()
259 writel(val, entry->subscription + smsm->local_host); in smsm_mask_irq()
262 clear_bit(irq, entry->irq_enabled); in smsm_mask_irq()
266 * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
276 struct qcom_smsm *smsm = entry->smsm; in smsm_unmask_irq()
279 /* Make sure our last cached state is up-to-date */ in smsm_unmask_irq()
280 if (readl(entry->remote_state) & BIT(irq)) in smsm_unmask_irq()
281 set_bit(irq, &entry->last_value); in smsm_unmask_irq()
283 clear_bit(irq, &entry->last_value); in smsm_unmask_irq()
285 set_bit(irq, entry->irq_enabled); in smsm_unmask_irq()
287 if (entry->subscription) { in smsm_unmask_irq()
288 val = readl(entry->subscription + smsm->local_host); in smsm_unmask_irq()
290 writel(val, entry->subscription + smsm->local_host); in smsm_unmask_irq()
295 * smsm_set_irq_type() - updates the requested IRQ type for the cascading
296 * @irqd: consumer interrupt handle
305 return -EINVAL; in smsm_set_irq_type()
308 set_bit(irq, entry->irq_rising); in smsm_set_irq_type()
310 clear_bit(irq, entry->irq_rising); in smsm_set_irq_type()
313 set_bit(irq, entry->irq_falling); in smsm_set_irq_type()
315 clear_bit(irq, entry->irq_falling); in smsm_set_irq_type()
328 return -EINVAL; in smsm_get_irqchip_state()
330 val = readl(entry->remote_state); in smsm_get_irqchip_state()
345 * smsm_irq_map() - sets up a mapping for a cascaded IRQ
354 struct smsm_entry *entry = d->host_data; in smsm_irq_map()
369 * smsm_parse_mbox() - requests an mbox channel
374 * sending the outgoing interrupts to a remove hosts - identified by @host_id.
378 struct smsm_host *host = &smsm->hosts[host_id]; in smsm_parse_mbox()
381 host->mbox_chan = mbox_request_channel(&smsm->mbox_client, host_id); in smsm_parse_mbox()
382 if (IS_ERR(host->mbox_chan)) { in smsm_parse_mbox()
383 ret = PTR_ERR(host->mbox_chan); in smsm_parse_mbox()
384 host->mbox_chan = NULL; in smsm_parse_mbox()
391 * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
396 * outgoing interrupts to a remote host - identified by @host_id.
401 struct device_node *node = smsm->dev->of_node; in smsm_parse_ipc()
402 struct smsm_host *host = &smsm->hosts[host_id]; in smsm_parse_ipc()
406 snprintf(key, sizeof(key), "qcom,ipc-%d", host_id); in smsm_parse_ipc()
411 host->ipc_regmap = syscon_node_to_regmap(syscon); in smsm_parse_ipc()
413 if (IS_ERR(host->ipc_regmap)) in smsm_parse_ipc()
414 return PTR_ERR(host->ipc_regmap); in smsm_parse_ipc()
416 ret = of_property_read_u32_index(node, key, 1, &host->ipc_offset); in smsm_parse_ipc()
418 dev_err(smsm->dev, "no offset in %s\n", key); in smsm_parse_ipc()
419 return -EINVAL; in smsm_parse_ipc()
422 ret = of_property_read_u32_index(node, key, 2, &host->ipc_bit); in smsm_parse_ipc()
424 dev_err(smsm->dev, "no bit in %s\n", key); in smsm_parse_ipc()
425 return -EINVAL; in smsm_parse_ipc()
432 * smsm_inbound_entry() - parse DT and set up an entry representing a remote system
446 dev_err(smsm->dev, "failed to parse smsm interrupt\n"); in smsm_inbound_entry()
447 return -EINVAL; in smsm_inbound_entry()
450 ret = devm_request_threaded_irq(smsm->dev, irq, in smsm_inbound_entry()
455 dev_err(smsm->dev, "failed to request interrupt\n"); in smsm_inbound_entry()
459 entry->domain = irq_domain_add_linear(node, 32, &smsm_irq_ops, entry); in smsm_inbound_entry()
460 if (!entry->domain) { in smsm_inbound_entry()
461 dev_err(smsm->dev, "failed to add irq_domain\n"); in smsm_inbound_entry()
462 return -ENOMEM; in smsm_inbound_entry()
469 * smsm_get_size_info() - parse the optional memory segment for sizes
472 * Attempt to acquire the number of hosts and entries from the optional shared
490 if (IS_ERR(info) && PTR_ERR(info) != -ENOENT) in smsm_get_size_info()
491 return dev_err_probe(smsm->dev, PTR_ERR(info), in smsm_get_size_info()
494 dev_warn(smsm->dev, "no smsm size info, using defaults\n"); in smsm_get_size_info()
495 smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES; in smsm_get_size_info()
496 smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS; in smsm_get_size_info()
500 smsm->num_entries = info->num_entries; in smsm_get_size_info()
501 smsm->num_hosts = info->num_hosts; in smsm_get_size_info()
503 dev_dbg(smsm->dev, in smsm_get_size_info()
505 smsm->num_entries, smsm->num_hosts); in smsm_get_size_info()
522 smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL); in qcom_smsm_probe()
524 return -ENOMEM; in qcom_smsm_probe()
525 smsm->dev = &pdev->dev; in qcom_smsm_probe()
526 spin_lock_init(&smsm->lock); in qcom_smsm_probe()
532 smsm->entries = devm_kcalloc(&pdev->dev, in qcom_smsm_probe()
533 smsm->num_entries, in qcom_smsm_probe()
536 if (!smsm->entries) in qcom_smsm_probe()
537 return -ENOMEM; in qcom_smsm_probe()
539 smsm->hosts = devm_kcalloc(&pdev->dev, in qcom_smsm_probe()
540 smsm->num_hosts, in qcom_smsm_probe()
543 if (!smsm->hosts) in qcom_smsm_probe()
544 return -ENOMEM; in qcom_smsm_probe()
546 for_each_child_of_node(pdev->dev.of_node, local_node) { in qcom_smsm_probe()
547 if (of_property_present(local_node, "#qcom,smem-state-cells")) in qcom_smsm_probe()
551 dev_err(&pdev->dev, "no state entry\n"); in qcom_smsm_probe()
552 return -EINVAL; in qcom_smsm_probe()
555 of_property_read_u32(pdev->dev.of_node, in qcom_smsm_probe()
556 "qcom,local-host", in qcom_smsm_probe()
557 &smsm->local_host); in qcom_smsm_probe()
559 smsm->mbox_client.dev = &pdev->dev; in qcom_smsm_probe()
560 smsm->mbox_client.knows_txdone = true; in qcom_smsm_probe()
563 for (id = 0; id < smsm->num_hosts; id++) { in qcom_smsm_probe()
576 smsm->num_entries * sizeof(u32)); in qcom_smsm_probe()
577 if (ret < 0 && ret != -EEXIST) { in qcom_smsm_probe()
578 dev_err(&pdev->dev, "unable to allocate shared state entry\n"); in qcom_smsm_probe()
584 dev_err(&pdev->dev, "Unable to acquire shared state entry\n"); in qcom_smsm_probe()
589 /* Acquire the list of interrupt mask vectors */ in qcom_smsm_probe()
590 size = smsm->num_entries * smsm->num_hosts * sizeof(u32); in qcom_smsm_probe()
592 if (ret < 0 && ret != -EEXIST) { in qcom_smsm_probe()
593 dev_err(&pdev->dev, "unable to allocate smsm interrupt mask\n"); in qcom_smsm_probe()
599 dev_err(&pdev->dev, "unable to acquire shared memory interrupt mask\n"); in qcom_smsm_probe()
605 smsm->local_state = states + smsm->local_host; in qcom_smsm_probe()
606 smsm->subscription = intr_mask + smsm->local_host * smsm->num_hosts; in qcom_smsm_probe()
609 smsm->state = qcom_smem_state_register(local_node, &smsm_state_ops, smsm); in qcom_smsm_probe()
610 if (IS_ERR(smsm->state)) { in qcom_smsm_probe()
611 dev_err(smsm->dev, "failed to register qcom_smem_state\n"); in qcom_smsm_probe()
612 ret = PTR_ERR(smsm->state); in qcom_smsm_probe()
617 for_each_available_child_of_node(pdev->dev.of_node, node) { in qcom_smsm_probe()
618 if (!of_property_read_bool(node, "interrupt-controller")) in qcom_smsm_probe()
622 if (ret || id >= smsm->num_entries) { in qcom_smsm_probe()
623 dev_err(&pdev->dev, "invalid reg of entry\n"); in qcom_smsm_probe()
625 ret = -EINVAL; in qcom_smsm_probe()
628 entry = &smsm->entries[id]; in qcom_smsm_probe()
630 entry->smsm = smsm; in qcom_smsm_probe()
631 entry->remote_state = states + id; in qcom_smsm_probe()
634 entry->subscription = intr_mask + id * smsm->num_hosts; in qcom_smsm_probe()
635 writel(0, entry->subscription + smsm->local_host); in qcom_smsm_probe()
649 for (id = 0; id < smsm->num_entries; id++) in qcom_smsm_probe()
650 if (smsm->entries[id].domain) in qcom_smsm_probe()
651 irq_domain_remove(smsm->entries[id].domain); in qcom_smsm_probe()
653 qcom_smem_state_unregister(smsm->state); in qcom_smsm_probe()
655 for (id = 0; id < smsm->num_hosts; id++) in qcom_smsm_probe()
656 mbox_free_channel(smsm->hosts[id].mbox_chan); in qcom_smsm_probe()
667 for (id = 0; id < smsm->num_entries; id++) in qcom_smsm_remove()
668 if (smsm->entries[id].domain) in qcom_smsm_remove()
669 irq_domain_remove(smsm->entries[id].domain); in qcom_smsm_remove()
671 for (id = 0; id < smsm->num_hosts; id++) in qcom_smsm_remove()
672 mbox_free_channel(smsm->hosts[id].mbox_chan); in qcom_smsm_remove()
674 qcom_smem_state_unregister(smsm->state); in qcom_smsm_remove()
687 .name = "qcom-smsm",
693 MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");