2 * Copyright (c) 2015, Sony Mobile Communications Inc.
3 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <linux/interrupt.h>
16 #include <linux/mfd/syscon.h>
17 #include <linux/module.h>
18 #include <linux/of_irq.h>
19 #include <linux/platform_device.h>
20 #include <linux/spinlock.h>
21 #include <linux/regmap.h>
22 #include <linux/soc/qcom/smem.h>
23 #include <linux/soc/qcom/smem_state.h>
26 * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
27 * for communicating single bit state information to remote processors.
29 * The implementation is based on two sections of shared memory; the first
30 * holding the state bits and the second holding a matrix of subscription bits.
32 * The state bits are structured in entries of 32 bits, each belonging to one
33 * system in the SoC. The entry belonging to the local system is considered
34 * read-write, while the rest should be considered read-only.
36 * The subscription matrix consists of N bitmaps per entry, denoting interest
37 * in updates of the entry for each of the N hosts. Upon updating a state bit
38 * each host's subscription bitmap should be queried and the remote system
39 * should be interrupted if they request so.
41 * The subscription matrix is laid out in entry-major order:
42 * entry0: [host0 ... hostN]
45 * entryM: [host0 ... hostN]
47 * A third, optional, shared memory region might contain information regarding
48 * the number of entries in the state bitmap as well as number of columns in
49 * the subscription matrix.
53 * Shared memory identifiers, used to acquire handles to respective memory
56 #define SMEM_SMSM_SHARED_STATE 85
57 #define SMEM_SMSM_CPU_INTR_MASK 333
58 #define SMEM_SMSM_SIZE_INFO 419
61 * Default sizes, in case SMEM_SMSM_SIZE_INFO is not found.
63 #define SMSM_DEFAULT_NUM_ENTRIES 8
64 #define SMSM_DEFAULT_NUM_HOSTS 3
70 * struct qcom_smsm - smsm driver context
71 * @dev: smsm device pointer
72 * @local_host: column in the subscription matrix representing this system
73 * @num_hosts: number of columns in the subscription matrix
74 * @num_entries: number of entries in the state map and rows in the subscription
76 * @local_state: pointer to the local processor's state bits
77 * @subscription: pointer to local processor's row in subscription matrix
78 * @state: smem state handle
79 * @lock: spinlock for read-modify-write of the outgoing state
80 * @entries: context for each of the entries
81 * @hosts: context for each of the hosts
93 struct qcom_smem_state
*state
;
97 struct smsm_entry
*entries
;
98 struct smsm_host
*hosts
;
102 * struct smsm_entry - per remote processor entry context
103 * @smsm: back-reference to driver context
104 * @domain: IRQ domain for this entry, if representing a remote system
105 * @irq_enabled: bitmap of which state bits IRQs are enabled
106 * @irq_rising: bitmap tracking if rising bits should be propagated
107 * @irq_falling: bitmap tracking if falling bits should be propagated
108 * @last_value: snapshot of state bits last time the interrupts where propagated
109 * @remote_state: pointer to this entry's state bits
110 * @subscription: pointer to a row in the subscription matrix representing this
114 struct qcom_smsm
*smsm
;
116 struct irq_domain
*domain
;
117 DECLARE_BITMAP(irq_enabled
, 32);
118 DECLARE_BITMAP(irq_rising
, 32);
119 DECLARE_BITMAP(irq_falling
, 32);
127 * struct smsm_host - representation of a remote host
128 * @ipc_regmap: regmap for outgoing interrupt
129 * @ipc_offset: offset in @ipc_regmap for outgoing interrupt
130 * @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt
133 struct regmap
*ipc_regmap
;
139 * smsm_update_bits() - change bit in outgoing entry and inform subscribers
140 * @data: smsm context pointer
141 * @offset: bit in the entry
144 * Used to set and clear the bits in the outgoing/local entry and inform
145 * subscribers about the change.
147 static int smsm_update_bits(void *data
, u32 mask
, u32 value
)
149 struct qcom_smsm
*smsm
= data
;
150 struct smsm_host
*hostp
;
157 spin_lock_irqsave(&smsm
->lock
, flags
);
159 /* Update the entry */
160 val
= orig
= readl(smsm
->local_state
);
164 /* Don't signal if we didn't change the value */
165 changes
= val
^ orig
;
167 spin_unlock_irqrestore(&smsm
->lock
, flags
);
171 /* Write out the new value */
172 writel(val
, smsm
->local_state
);
173 spin_unlock_irqrestore(&smsm
->lock
, flags
);
175 /* Make sure the value update is ordered before any kicks */
178 /* Iterate over all hosts to check whom wants a kick */
179 for (host
= 0; host
< smsm
->num_hosts
; host
++) {
180 hostp
= &smsm
->hosts
[host
];
182 val
= readl(smsm
->subscription
+ host
);
183 if (val
& changes
&& hostp
->ipc_regmap
) {
184 regmap_write(hostp
->ipc_regmap
,
186 BIT(hostp
->ipc_bit
));
194 static const struct qcom_smem_state_ops smsm_state_ops
= {
195 .update_bits
= smsm_update_bits
,
199 * smsm_intr() - cascading IRQ handler for SMSM
201 * @data: entry related to this IRQ
203 * This function cascades an incoming interrupt from a remote system, based on
204 * the state bits and configuration.
206 static irqreturn_t
smsm_intr(int irq
, void *data
)
208 struct smsm_entry
*entry
= data
;
214 val
= readl(entry
->remote_state
);
215 changed
= val
^ entry
->last_value
;
216 entry
->last_value
= val
;
218 for_each_set_bit(i
, entry
->irq_enabled
, 32) {
219 if (!(changed
& BIT(i
)))
223 if (test_bit(i
, entry
->irq_rising
)) {
224 irq_pin
= irq_find_mapping(entry
->domain
, i
);
225 handle_nested_irq(irq_pin
);
228 if (test_bit(i
, entry
->irq_falling
)) {
229 irq_pin
= irq_find_mapping(entry
->domain
, i
);
230 handle_nested_irq(irq_pin
);
239 * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
240 * @irqd: IRQ handle to be masked
242 * This un-subscribes the local CPU from interrupts upon changes to the defines
243 * status bit. The bit is also cleared from cascading.
245 static void smsm_mask_irq(struct irq_data
*irqd
)
247 struct smsm_entry
*entry
= irq_data_get_irq_chip_data(irqd
);
248 irq_hw_number_t irq
= irqd_to_hwirq(irqd
);
249 struct qcom_smsm
*smsm
= entry
->smsm
;
252 if (entry
->subscription
) {
253 val
= readl(entry
->subscription
+ smsm
->local_host
);
255 writel(val
, entry
->subscription
+ smsm
->local_host
);
258 clear_bit(irq
, entry
->irq_enabled
);
262 * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
263 * @irqd: IRQ handle to be unmasked
266 * This subscribes the local CPU to interrupts upon changes to the defined
267 * status bit. The bit is also marked for cascading.
270 static void smsm_unmask_irq(struct irq_data
*irqd
)
272 struct smsm_entry
*entry
= irq_data_get_irq_chip_data(irqd
);
273 irq_hw_number_t irq
= irqd_to_hwirq(irqd
);
274 struct qcom_smsm
*smsm
= entry
->smsm
;
277 set_bit(irq
, entry
->irq_enabled
);
279 if (entry
->subscription
) {
280 val
= readl(entry
->subscription
+ smsm
->local_host
);
282 writel(val
, entry
->subscription
+ smsm
->local_host
);
287 * smsm_set_irq_type() - updates the requested IRQ type for the cascading
288 * @irqd: consumer interrupt handle
289 * @type: requested flags
291 static int smsm_set_irq_type(struct irq_data
*irqd
, unsigned int type
)
293 struct smsm_entry
*entry
= irq_data_get_irq_chip_data(irqd
);
294 irq_hw_number_t irq
= irqd_to_hwirq(irqd
);
296 if (!(type
& IRQ_TYPE_EDGE_BOTH
))
299 if (type
& IRQ_TYPE_EDGE_RISING
)
300 set_bit(irq
, entry
->irq_rising
);
302 clear_bit(irq
, entry
->irq_rising
);
304 if (type
& IRQ_TYPE_EDGE_FALLING
)
305 set_bit(irq
, entry
->irq_falling
);
307 clear_bit(irq
, entry
->irq_falling
);
312 static struct irq_chip smsm_irq_chip
= {
314 .irq_mask
= smsm_mask_irq
,
315 .irq_unmask
= smsm_unmask_irq
,
316 .irq_set_type
= smsm_set_irq_type
,
320 * smsm_irq_map() - sets up a mapping for a cascaded IRQ
321 * @d: IRQ domain representing an entry
322 * @irq: IRQ to set up
325 static int smsm_irq_map(struct irq_domain
*d
,
329 struct smsm_entry
*entry
= d
->host_data
;
331 irq_set_chip_and_handler(irq
, &smsm_irq_chip
, handle_level_irq
);
332 irq_set_chip_data(irq
, entry
);
333 irq_set_nested_thread(irq
, 1);
338 static const struct irq_domain_ops smsm_irq_ops
= {
340 .xlate
= irq_domain_xlate_twocell
,
344 * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
345 * @smsm: smsm driver context
346 * @host_id: index of the remote host to be resolved
348 * Parses device tree to acquire the information needed for sending the
349 * outgoing interrupts to a remote host - identified by @host_id.
351 static int smsm_parse_ipc(struct qcom_smsm
*smsm
, unsigned host_id
)
353 struct device_node
*syscon
;
354 struct device_node
*node
= smsm
->dev
->of_node
;
355 struct smsm_host
*host
= &smsm
->hosts
[host_id
];
359 snprintf(key
, sizeof(key
), "qcom,ipc-%d", host_id
);
360 syscon
= of_parse_phandle(node
, key
, 0);
364 host
->ipc_regmap
= syscon_node_to_regmap(syscon
);
365 if (IS_ERR(host
->ipc_regmap
))
366 return PTR_ERR(host
->ipc_regmap
);
368 ret
= of_property_read_u32_index(node
, key
, 1, &host
->ipc_offset
);
370 dev_err(smsm
->dev
, "no offset in %s\n", key
);
374 ret
= of_property_read_u32_index(node
, key
, 2, &host
->ipc_bit
);
376 dev_err(smsm
->dev
, "no bit in %s\n", key
);
384 * smsm_inbound_entry() - parse DT and set up an entry representing a remote system
385 * @smsm: smsm driver context
386 * @entry: entry context to be set up
387 * @node: dt node containing the entry's properties
389 static int smsm_inbound_entry(struct qcom_smsm
*smsm
,
390 struct smsm_entry
*entry
,
391 struct device_node
*node
)
396 irq
= irq_of_parse_and_map(node
, 0);
398 dev_err(smsm
->dev
, "failed to parse smsm interrupt\n");
402 ret
= devm_request_threaded_irq(smsm
->dev
, irq
,
405 "smsm", (void *)entry
);
407 dev_err(smsm
->dev
, "failed to request interrupt\n");
411 entry
->domain
= irq_domain_add_linear(node
, 32, &smsm_irq_ops
, entry
);
412 if (!entry
->domain
) {
413 dev_err(smsm
->dev
, "failed to add irq_domain\n");
421 * smsm_get_size_info() - parse the optional memory segment for sizes
422 * @smsm: smsm driver context
424 * Attempt to acquire the number of hosts and entries from the optional shared
425 * memory location. Not being able to find this segment should indicate that
426 * we're on a older system where these values was hard coded to
427 * SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS.
429 * Returns 0 on success, negative errno on failure.
431 static int smsm_get_size_info(struct qcom_smsm
*smsm
)
441 info
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_SIZE_INFO
, &size
);
442 if (IS_ERR(info
) && PTR_ERR(info
) != -ENOENT
) {
443 if (PTR_ERR(info
) != -EPROBE_DEFER
)
444 dev_err(smsm
->dev
, "unable to retrieve smsm size info\n");
445 return PTR_ERR(info
);
446 } else if (IS_ERR(info
) || size
!= sizeof(*info
)) {
447 dev_warn(smsm
->dev
, "no smsm size info, using defaults\n");
448 smsm
->num_entries
= SMSM_DEFAULT_NUM_ENTRIES
;
449 smsm
->num_hosts
= SMSM_DEFAULT_NUM_HOSTS
;
453 smsm
->num_entries
= info
->num_entries
;
454 smsm
->num_hosts
= info
->num_hosts
;
457 "found custom size of smsm: %d entries %d hosts\n",
458 smsm
->num_entries
, smsm
->num_hosts
);
463 static int qcom_smsm_probe(struct platform_device
*pdev
)
465 struct device_node
*local_node
;
466 struct device_node
*node
;
467 struct smsm_entry
*entry
;
468 struct qcom_smsm
*smsm
;
475 smsm
= devm_kzalloc(&pdev
->dev
, sizeof(*smsm
), GFP_KERNEL
);
478 smsm
->dev
= &pdev
->dev
;
479 spin_lock_init(&smsm
->lock
);
481 ret
= smsm_get_size_info(smsm
);
485 smsm
->entries
= devm_kcalloc(&pdev
->dev
,
487 sizeof(struct smsm_entry
),
492 smsm
->hosts
= devm_kcalloc(&pdev
->dev
,
494 sizeof(struct smsm_host
),
499 local_node
= of_find_node_with_property(of_node_get(pdev
->dev
.of_node
),
500 "#qcom,smem-state-cells");
502 dev_err(&pdev
->dev
, "no state entry\n");
506 of_property_read_u32(pdev
->dev
.of_node
,
510 /* Parse the host properties */
511 for (id
= 0; id
< smsm
->num_hosts
; id
++) {
512 ret
= smsm_parse_ipc(smsm
, id
);
517 /* Acquire the main SMSM state vector */
518 ret
= qcom_smem_alloc(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_SHARED_STATE
,
519 smsm
->num_entries
* sizeof(u32
));
520 if (ret
< 0 && ret
!= -EEXIST
) {
521 dev_err(&pdev
->dev
, "unable to allocate shared state entry\n");
525 states
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_SHARED_STATE
, NULL
);
526 if (IS_ERR(states
)) {
527 dev_err(&pdev
->dev
, "Unable to acquire shared state entry\n");
528 return PTR_ERR(states
);
531 /* Acquire the list of interrupt mask vectors */
532 size
= smsm
->num_entries
* smsm
->num_hosts
* sizeof(u32
);
533 ret
= qcom_smem_alloc(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_CPU_INTR_MASK
, size
);
534 if (ret
< 0 && ret
!= -EEXIST
) {
535 dev_err(&pdev
->dev
, "unable to allocate smsm interrupt mask\n");
539 intr_mask
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SMEM_SMSM_CPU_INTR_MASK
, NULL
);
540 if (IS_ERR(intr_mask
)) {
541 dev_err(&pdev
->dev
, "unable to acquire shared memory interrupt mask\n");
542 return PTR_ERR(intr_mask
);
545 /* Setup the reference to the local state bits */
546 smsm
->local_state
= states
+ smsm
->local_host
;
547 smsm
->subscription
= intr_mask
+ smsm
->local_host
* smsm
->num_hosts
;
549 /* Register the outgoing state */
550 smsm
->state
= qcom_smem_state_register(local_node
, &smsm_state_ops
, smsm
);
551 if (IS_ERR(smsm
->state
)) {
552 dev_err(smsm
->dev
, "failed to register qcom_smem_state\n");
553 return PTR_ERR(smsm
->state
);
556 /* Register handlers for remote processor entries of interest. */
557 for_each_available_child_of_node(pdev
->dev
.of_node
, node
) {
558 if (!of_property_read_bool(node
, "interrupt-controller"))
561 ret
= of_property_read_u32(node
, "reg", &id
);
562 if (ret
|| id
>= smsm
->num_entries
) {
563 dev_err(&pdev
->dev
, "invalid reg of entry\n");
566 goto unwind_interfaces
;
568 entry
= &smsm
->entries
[id
];
571 entry
->remote_state
= states
+ id
;
573 /* Setup subscription pointers and unsubscribe to any kicks */
574 entry
->subscription
= intr_mask
+ id
* smsm
->num_hosts
;
575 writel(0, entry
->subscription
+ smsm
->local_host
);
577 ret
= smsm_inbound_entry(smsm
, entry
, node
);
579 goto unwind_interfaces
;
582 platform_set_drvdata(pdev
, smsm
);
587 for (id
= 0; id
< smsm
->num_entries
; id
++)
588 if (smsm
->entries
[id
].domain
)
589 irq_domain_remove(smsm
->entries
[id
].domain
);
591 qcom_smem_state_unregister(smsm
->state
);
596 static int qcom_smsm_remove(struct platform_device
*pdev
)
598 struct qcom_smsm
*smsm
= platform_get_drvdata(pdev
);
601 for (id
= 0; id
< smsm
->num_entries
; id
++)
602 if (smsm
->entries
[id
].domain
)
603 irq_domain_remove(smsm
->entries
[id
].domain
);
605 qcom_smem_state_unregister(smsm
->state
);
610 static const struct of_device_id qcom_smsm_of_match
[] = {
611 { .compatible
= "qcom,smsm" },
614 MODULE_DEVICE_TABLE(of
, qcom_smsm_of_match
);
616 static struct platform_driver qcom_smsm_driver
= {
617 .probe
= qcom_smsm_probe
,
618 .remove
= qcom_smsm_remove
,
621 .of_match_table
= qcom_smsm_of_match
,
624 module_platform_driver(qcom_smsm_driver
);
626 MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");
627 MODULE_LICENSE("GPL v2");