1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015, Sony Mobile Communications Inc.
4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
7 #include <linux/interrupt.h>
8 #include <linux/mfd/syscon.h>
9 #include <linux/module.h>
10 #include <linux/of_irq.h>
11 #include <linux/platform_device.h>
12 #include <linux/spinlock.h>
13 #include <linux/regmap.h>
14 #include <linux/soc/qcom/smem.h>
15 #include <linux/soc/qcom/smem_state.h>
18 * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
19 * for communicating single bit state information to remote processors.
21 * The implementation is based on two sections of shared memory; the first
22 * holding the state bits and the second holding a matrix of subscription bits.
24 * The state bits are structured in entries of 32 bits, each belonging to one
25 * system in the SoC. The entry belonging to the local system is considered
26 * read-write, while the rest should be considered read-only.
28 * The subscription matrix consists of N bitmaps per entry, denoting interest
29 * in updates of the entry for each of the N hosts. Upon updating a state bit
30 * each host's subscription bitmap should be queried and the remote system
31 * should be interrupted if they request so.
33 * The subscription matrix is laid out in entry-major order:
34 * entry0: [host0 ... hostN]
37 * entryM: [host0 ... hostN]
39 * A third, optional, shared memory region might contain information regarding
40 * the number of entries in the state bitmap as well as number of columns in
41 * the subscription matrix.
45 * Shared memory identifiers, used to acquire handles to respective memory
48 #define SMEM_SMSM_SHARED_STATE 85
49 #define SMEM_SMSM_CPU_INTR_MASK 333
50 #define SMEM_SMSM_SIZE_INFO 419
53 * Default sizes, in case SMEM_SMSM_SIZE_INFO is not found.
55 #define SMSM_DEFAULT_NUM_ENTRIES 8
56 #define SMSM_DEFAULT_NUM_HOSTS 3
62 * struct qcom_smsm - smsm driver context
63 * @dev: smsm device pointer
64 * @local_host: column in the subscription matrix representing this system
65 * @num_hosts: number of columns in the subscription matrix
66 * @num_entries: number of entries in the state map and rows in the subscription
68 * @local_state: pointer to the local processor's state bits
69 * @subscription: pointer to local processor's row in subscription matrix
70 * @state: smem state handle
71 * @lock: spinlock for read-modify-write of the outgoing state
72 * @entries: context for each of the entries
73 * @hosts: context for each of the hosts
85 struct qcom_smem_state
*state
;
89 struct smsm_entry
*entries
;
90 struct smsm_host
*hosts
;
94 * struct smsm_entry - per remote processor entry context
95 * @smsm: back-reference to driver context
96 * @domain: IRQ domain for this entry, if representing a remote system
97 * @irq_enabled: bitmap of which state bits IRQs are enabled
98 * @irq_rising: bitmap tracking if rising bits should be propagated
99 * @irq_falling: bitmap tracking if falling bits should be propagated
100 * @last_value: snapshot of state bits last time the interrupts where propagated
101 * @remote_state: pointer to this entry's state bits
102 * @subscription: pointer to a row in the subscription matrix representing this
106 struct qcom_smsm
*smsm
;
108 struct irq_domain
*domain
;
109 DECLARE_BITMAP(irq_enabled
, 32);
110 DECLARE_BITMAP(irq_rising
, 32);
111 DECLARE_BITMAP(irq_falling
, 32);
119 * struct smsm_host - representation of a remote host
120 * @ipc_regmap: regmap for outgoing interrupt
121 * @ipc_offset: offset in @ipc_regmap for outgoing interrupt
122 * @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt
125 struct regmap
*ipc_regmap
;
131 * smsm_update_bits() - change bit in outgoing entry and inform subscribers
132 * @data: smsm context pointer
133 * @offset: bit in the entry
136 * Used to set and clear the bits in the outgoing/local entry and inform
137 * subscribers about the change.
139 static int smsm_update_bits(void *data
, u32 mask
, u32 value
)
141 struct qcom_smsm
*smsm
= data
;
142 struct smsm_host
*hostp
;
149 spin_lock_irqsave(&smsm
->lock
, flags
);
151 /* Update the entry */
152 val
= orig
= readl(smsm
->local_state
);
156 /* Don't signal if we didn't change the value */
157 changes
= val
^ orig
;
159 spin_unlock_irqrestore(&smsm
->lock
, flags
);
163 /* Write out the new value */
164 writel(val
, smsm
->local_state
);
165 spin_unlock_irqrestore(&smsm
->lock
, flags
);
167 /* Make sure the value update is ordered before any kicks */
170 /* Iterate over all hosts to check whom wants a kick */
171 for (host
= 0; host
< smsm
->num_hosts
; host
++) {
172 hostp
= &smsm
->hosts
[host
];
174 val
= readl(smsm
->subscription
+ host
);
175 if (val
& changes
&& hostp
->ipc_regmap
) {
176 regmap_write(hostp
->ipc_regmap
,
178 BIT(hostp
->ipc_bit
));
186 static const struct qcom_smem_state_ops smsm_state_ops
= {
187 .update_bits
= smsm_update_bits
,
191 * smsm_intr() - cascading IRQ handler for SMSM
193 * @data: entry related to this IRQ
195 * This function cascades an incoming interrupt from a remote system, based on
196 * the state bits and configuration.
198 static irqreturn_t
smsm_intr(int irq
, void *data
)
200 struct smsm_entry
*entry
= data
;
206 val
= readl(entry
->remote_state
);
207 changed
= val
^ entry
->last_value
;
208 entry
->last_value
= val
;
210 for_each_set_bit(i
, entry
->irq_enabled
, 32) {
211 if (!(changed
& BIT(i
)))
215 if (test_bit(i
, entry
->irq_rising
)) {
216 irq_pin
= irq_find_mapping(entry
->domain
, i
);
217 handle_nested_irq(irq_pin
);
220 if (test_bit(i
, entry
->irq_falling
)) {
221 irq_pin
= irq_find_mapping(entry
->domain
, i
);
222 handle_nested_irq(irq_pin
);
231 * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
232 * @irqd: IRQ handle to be masked
234 * This un-subscribes the local CPU from interrupts upon changes to the defines
235 * status bit. The bit is also cleared from cascading.
237 static void smsm_mask_irq(struct irq_data
*irqd
)
239 struct smsm_entry
*entry
= irq_data_get_irq_chip_data(irqd
);
240 irq_hw_number_t irq
= irqd_to_hwirq(irqd
);
241 struct qcom_smsm
*smsm
= entry
->smsm
;
244 if (entry
->subscription
) {
245 val
= readl(entry
->subscription
+ smsm
->local_host
);
247 writel(val
, entry
->subscription
+ smsm
->local_host
);
250 clear_bit(irq
, entry
->irq_enabled
);
254 * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
255 * @irqd: IRQ handle to be unmasked
258 * This subscribes the local CPU to interrupts upon changes to the defined
259 * status bit. The bit is also marked for cascading.
262 static void smsm_unmask_irq(struct irq_data
*irqd
)
264 struct smsm_entry
*entry
= irq_data_get_irq_chip_data(irqd
);
265 irq_hw_number_t irq
= irqd_to_hwirq(irqd
);
266 struct qcom_smsm
*smsm
= entry
->smsm
;
269 set_bit(irq
, entry
->irq_enabled
);
271 if (entry
->subscription
) {
272 val
= readl(entry
->subscription
+ smsm
->local_host
);
274 writel(val
, entry
->subscription
+ smsm
->local_host
);
279 * smsm_set_irq_type() - updates the requested IRQ type for the cascading
280 * @irqd: consumer interrupt handle
281 * @type: requested flags
283 static int smsm_set_irq_type(struct irq_data
*irqd
, unsigned int type
)
285 struct smsm_entry
*entry
= irq_data_get_irq_chip_data(irqd
);
286 irq_hw_number_t irq
= irqd_to_hwirq(irqd
);
288 if (!(type
& IRQ_TYPE_EDGE_BOTH
))
291 if (type
& IRQ_TYPE_EDGE_RISING
)
292 set_bit(irq
, entry
->irq_rising
);
294 clear_bit(irq
, entry
->irq_rising
);
296 if (type
& IRQ_TYPE_EDGE_FALLING
)
297 set_bit(irq
, entry
->irq_falling
);
299 clear_bit(irq
, entry
->irq_falling
);
304 static struct irq_chip smsm_irq_chip
= {
306 .irq_mask
= smsm_mask_irq
,
307 .irq_unmask
= smsm_unmask_irq
,
308 .irq_set_type
= smsm_set_irq_type
,
312 * smsm_irq_map() - sets up a mapping for a cascaded IRQ
313 * @d: IRQ domain representing an entry
314 * @irq: IRQ to set up
317 static int smsm_irq_map(struct irq_domain
*d
,
321 struct smsm_entry
*entry
= d
->host_data
;
323 irq_set_chip_and_handler(irq
, &smsm_irq_chip
, handle_level_irq
);
324 irq_set_chip_data(irq
, entry
);
325 irq_set_nested_thread(irq
, 1);
330 static const struct irq_domain_ops smsm_irq_ops
= {
332 .xlate
= irq_domain_xlate_twocell
,
336 * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
337 * @smsm: smsm driver context
338 * @host_id: index of the remote host to be resolved
340 * Parses device tree to acquire the information needed for sending the
341 * outgoing interrupts to a remote host - identified by @host_id.
343 static int smsm_parse_ipc(struct qcom_smsm
*smsm
, unsigned host_id
)
345 struct device_node
*syscon
;
346 struct device_node
*node
= smsm
->dev
->of_node
;
347 struct smsm_host
*host
= &smsm
->hosts
[host_id
];
351 snprintf(key
, sizeof(key
), "qcom,ipc-%d", host_id
);
352 syscon
= of_parse_phandle(node
, key
, 0);
356 host
->ipc_regmap
= syscon_node_to_regmap(syscon
);
357 if (IS_ERR(host
->ipc_regmap
))
358 return PTR_ERR(host
->ipc_regmap
);
360 ret
= of_property_read_u32_index(node
, key
, 1, &host
->ipc_offset
);
362 dev_err(smsm
->dev
, "no offset in %s\n", key
);
366 ret
= of_property_read_u32_index(node
, key
, 2, &host
->ipc_bit
);
368 dev_err(smsm
->dev
, "no bit in %s\n", key
);
376 * smsm_inbound_entry() - parse DT and set up an entry representing a remote system
377 * @smsm: smsm driver context
378 * @entry: entry context to be set up
379 * @node: dt node containing the entry's properties
381 static int smsm_inbound_entry(struct qcom_smsm
*smsm
,
382 struct smsm_entry
*entry
,
383 struct device_node
*node
)
388 irq
= irq_of_parse_and_map(node
, 0);
390 dev_err(smsm
->dev
, "failed to parse smsm interrupt\n");
394 ret
= devm_request_threaded_irq(smsm
->dev
, irq
,
397 "smsm", (void *)entry
);
399 dev_err(smsm
->dev
, "failed to request interrupt\n");
403 entry
->domain
= irq_domain_add_linear(node
, 32, &smsm_irq_ops
, entry
);
404 if (!entry
->domain
) {
405 dev_err(smsm
->dev
, "failed to add irq_domain\n");
413 * smsm_get_size_info() - parse the optional memory segment for sizes
414 * @smsm: smsm driver context
416 * Attempt to acquire the number of hosts and entries from the optional shared
417 * memory location. Not being able to find this segment should indicate that
418 * we're on a older system where these values was hard coded to
419 * SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS.
421 * Returns 0 on success, negative errno on failure.
423 static int smsm_get_size_info(struct qcom_smsm
*smsm
)
433 info
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_SIZE_INFO
, &size
);
434 if (IS_ERR(info
) && PTR_ERR(info
) != -ENOENT
) {
435 if (PTR_ERR(info
) != -EPROBE_DEFER
)
436 dev_err(smsm
->dev
, "unable to retrieve smsm size info\n");
437 return PTR_ERR(info
);
438 } else if (IS_ERR(info
) || size
!= sizeof(*info
)) {
439 dev_warn(smsm
->dev
, "no smsm size info, using defaults\n");
440 smsm
->num_entries
= SMSM_DEFAULT_NUM_ENTRIES
;
441 smsm
->num_hosts
= SMSM_DEFAULT_NUM_HOSTS
;
445 smsm
->num_entries
= info
->num_entries
;
446 smsm
->num_hosts
= info
->num_hosts
;
449 "found custom size of smsm: %d entries %d hosts\n",
450 smsm
->num_entries
, smsm
->num_hosts
);
455 static int qcom_smsm_probe(struct platform_device
*pdev
)
457 struct device_node
*local_node
;
458 struct device_node
*node
;
459 struct smsm_entry
*entry
;
460 struct qcom_smsm
*smsm
;
467 smsm
= devm_kzalloc(&pdev
->dev
, sizeof(*smsm
), GFP_KERNEL
);
470 smsm
->dev
= &pdev
->dev
;
471 spin_lock_init(&smsm
->lock
);
473 ret
= smsm_get_size_info(smsm
);
477 smsm
->entries
= devm_kcalloc(&pdev
->dev
,
479 sizeof(struct smsm_entry
),
484 smsm
->hosts
= devm_kcalloc(&pdev
->dev
,
486 sizeof(struct smsm_host
),
491 for_each_child_of_node(pdev
->dev
.of_node
, local_node
) {
492 if (of_find_property(local_node
, "#qcom,smem-state-cells", NULL
))
496 dev_err(&pdev
->dev
, "no state entry\n");
500 of_property_read_u32(pdev
->dev
.of_node
,
504 /* Parse the host properties */
505 for (id
= 0; id
< smsm
->num_hosts
; id
++) {
506 ret
= smsm_parse_ipc(smsm
, id
);
511 /* Acquire the main SMSM state vector */
512 ret
= qcom_smem_alloc(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_SHARED_STATE
,
513 smsm
->num_entries
* sizeof(u32
));
514 if (ret
< 0 && ret
!= -EEXIST
) {
515 dev_err(&pdev
->dev
, "unable to allocate shared state entry\n");
519 states
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_SHARED_STATE
, NULL
);
520 if (IS_ERR(states
)) {
521 dev_err(&pdev
->dev
, "Unable to acquire shared state entry\n");
522 return PTR_ERR(states
);
525 /* Acquire the list of interrupt mask vectors */
526 size
= smsm
->num_entries
* smsm
->num_hosts
* sizeof(u32
);
527 ret
= qcom_smem_alloc(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_CPU_INTR_MASK
, size
);
528 if (ret
< 0 && ret
!= -EEXIST
) {
529 dev_err(&pdev
->dev
, "unable to allocate smsm interrupt mask\n");
533 intr_mask
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_CPU_INTR_MASK
, NULL
);
534 if (IS_ERR(intr_mask
)) {
535 dev_err(&pdev
->dev
, "unable to acquire shared memory interrupt mask\n");
536 return PTR_ERR(intr_mask
);
539 /* Setup the reference to the local state bits */
540 smsm
->local_state
= states
+ smsm
->local_host
;
541 smsm
->subscription
= intr_mask
+ smsm
->local_host
* smsm
->num_hosts
;
543 /* Register the outgoing state */
544 smsm
->state
= qcom_smem_state_register(local_node
, &smsm_state_ops
, smsm
);
545 if (IS_ERR(smsm
->state
)) {
546 dev_err(smsm
->dev
, "failed to register qcom_smem_state\n");
547 return PTR_ERR(smsm
->state
);
550 /* Register handlers for remote processor entries of interest. */
551 for_each_available_child_of_node(pdev
->dev
.of_node
, node
) {
552 if (!of_property_read_bool(node
, "interrupt-controller"))
555 ret
= of_property_read_u32(node
, "reg", &id
);
556 if (ret
|| id
>= smsm
->num_entries
) {
557 dev_err(&pdev
->dev
, "invalid reg of entry\n");
560 goto unwind_interfaces
;
562 entry
= &smsm
->entries
[id
];
565 entry
->remote_state
= states
+ id
;
567 /* Setup subscription pointers and unsubscribe to any kicks */
568 entry
->subscription
= intr_mask
+ id
* smsm
->num_hosts
;
569 writel(0, entry
->subscription
+ smsm
->local_host
);
571 ret
= smsm_inbound_entry(smsm
, entry
, node
);
573 goto unwind_interfaces
;
576 platform_set_drvdata(pdev
, smsm
);
581 for (id
= 0; id
< smsm
->num_entries
; id
++)
582 if (smsm
->entries
[id
].domain
)
583 irq_domain_remove(smsm
->entries
[id
].domain
);
585 qcom_smem_state_unregister(smsm
->state
);
590 static int qcom_smsm_remove(struct platform_device
*pdev
)
592 struct qcom_smsm
*smsm
= platform_get_drvdata(pdev
);
595 for (id
= 0; id
< smsm
->num_entries
; id
++)
596 if (smsm
->entries
[id
].domain
)
597 irq_domain_remove(smsm
->entries
[id
].domain
);
599 qcom_smem_state_unregister(smsm
->state
);
604 static const struct of_device_id qcom_smsm_of_match
[] = {
605 { .compatible
= "qcom,smsm" },
608 MODULE_DEVICE_TABLE(of
, qcom_smsm_of_match
);
610 static struct platform_driver qcom_smsm_driver
= {
611 .probe
= qcom_smsm_probe
,
612 .remove
= qcom_smsm_remove
,
615 .of_match_table
= qcom_smsm_of_match
,
618 module_platform_driver(qcom_smsm_driver
);
620 MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");
621 MODULE_LICENSE("GPL v2");