linux/drivers/soc/qcom/smsm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2015, Sony Mobile Communications Inc.
   4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
   5 */
   6
   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>
  16
  17/*
  18 * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
  19 * for communicating single bit state information to remote processors.
  20 *
  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.
  23 *
  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.
  27 *
  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.
  32 *
  33 * The subscription matrix is laid out in entry-major order:
  34 * entry0: [host0 ... hostN]
  35 *      .
  36 *      .
  37 * entryM: [host0 ... hostN]
  38 *
  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.
  42 */
  43
  44/*
  45 * Shared memory identifiers, used to acquire handles to respective memory
  46 * region.
  47 */
  48#define SMEM_SMSM_SHARED_STATE          85
  49#define SMEM_SMSM_CPU_INTR_MASK         333
  50#define SMEM_SMSM_SIZE_INFO             419
  51
  52/*
  53 * Default sizes, in case SMEM_SMSM_SIZE_INFO is not found.
  54 */
  55#define SMSM_DEFAULT_NUM_ENTRIES        8
  56#define SMSM_DEFAULT_NUM_HOSTS          3
  57
  58struct smsm_entry;
  59struct smsm_host;
  60
  61/**
  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
  67 *              matrix
  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
  74 */
  75struct qcom_smsm {
  76        struct device *dev;
  77
  78        u32 local_host;
  79
  80        u32 num_hosts;
  81        u32 num_entries;
  82
  83        u32 *local_state;
  84        u32 *subscription;
  85        struct qcom_smem_state *state;
  86
  87        spinlock_t lock;
  88
  89        struct smsm_entry *entries;
  90        struct smsm_host *hosts;
  91};
  92
  93/**
  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
 103 *              entry
 104 */
 105struct smsm_entry {
 106        struct qcom_smsm *smsm;
 107
 108        struct irq_domain *domain;
 109        DECLARE_BITMAP(irq_enabled, 32);
 110        DECLARE_BITMAP(irq_rising, 32);
 111        DECLARE_BITMAP(irq_falling, 32);
 112        unsigned long last_value;
 113
 114        u32 *remote_state;
 115        u32 *subscription;
 116};
 117
 118/**
 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
 123 */
 124struct smsm_host {
 125        struct regmap *ipc_regmap;
 126        int ipc_offset;
 127        int ipc_bit;
 128};
 129
 130/**
 131 * smsm_update_bits() - change bit in outgoing entry and inform subscribers
 132 * @data:       smsm context pointer
 133 * @mask:       value mask
 134 * @value:      new value
 135 *
 136 * Used to set and clear the bits in the outgoing/local entry and inform
 137 * subscribers about the change.
 138 */
 139static int smsm_update_bits(void *data, u32 mask, u32 value)
 140{
 141        struct qcom_smsm *smsm = data;
 142        struct smsm_host *hostp;
 143        unsigned long flags;
 144        u32 changes;
 145        u32 host;
 146        u32 orig;
 147        u32 val;
 148
 149        spin_lock_irqsave(&smsm->lock, flags);
 150
 151        /* Update the entry */
 152        val = orig = readl(smsm->local_state);
 153        val &= ~mask;
 154        val |= value;
 155
 156        /* Don't signal if we didn't change the value */
 157        changes = val ^ orig;
 158        if (!changes) {
 159                spin_unlock_irqrestore(&smsm->lock, flags);
 160                goto done;
 161        }
 162
 163        /* Write out the new value */
 164        writel(val, smsm->local_state);
 165        spin_unlock_irqrestore(&smsm->lock, flags);
 166
 167        /* Make sure the value update is ordered before any kicks */
 168        wmb();
 169
 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];
 173
 174                val = readl(smsm->subscription + host);
 175                if (val & changes && hostp->ipc_regmap) {
 176                        regmap_write(hostp->ipc_regmap,
 177                                     hostp->ipc_offset,
 178                                     BIT(hostp->ipc_bit));
 179                }
 180        }
 181
 182done:
 183        return 0;
 184}
 185
 186static const struct qcom_smem_state_ops smsm_state_ops = {
 187        .update_bits = smsm_update_bits,
 188};
 189
 190/**
 191 * smsm_intr() - cascading IRQ handler for SMSM
 192 * @irq:        unused
 193 * @data:       entry related to this IRQ
 194 *
 195 * This function cascades an incoming interrupt from a remote system, based on
 196 * the state bits and configuration.
 197 */
 198static irqreturn_t smsm_intr(int irq, void *data)
 199{
 200        struct smsm_entry *entry = data;
 201        unsigned i;
 202        int irq_pin;
 203        u32 changed;
 204        u32 val;
 205
 206        val = readl(entry->remote_state);
 207        changed = val ^ xchg(&entry->last_value, val);
 208
 209        for_each_set_bit(i, entry->irq_enabled, 32) {
 210                if (!(changed & BIT(i)))
 211                        continue;
 212
 213                if (val & BIT(i)) {
 214                        if (test_bit(i, entry->irq_rising)) {
 215                                irq_pin = irq_find_mapping(entry->domain, i);
 216                                handle_nested_irq(irq_pin);
 217                        }
 218                } else {
 219                        if (test_bit(i, entry->irq_falling)) {
 220                                irq_pin = irq_find_mapping(entry->domain, i);
 221                                handle_nested_irq(irq_pin);
 222                        }
 223                }
 224        }
 225
 226        return IRQ_HANDLED;
 227}
 228
 229/**
 230 * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
 231 * @irqd:       IRQ handle to be masked
 232 *
 233 * This un-subscribes the local CPU from interrupts upon changes to the defines
 234 * status bit. The bit is also cleared from cascading.
 235 */
 236static void smsm_mask_irq(struct irq_data *irqd)
 237{
 238        struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
 239        irq_hw_number_t irq = irqd_to_hwirq(irqd);
 240        struct qcom_smsm *smsm = entry->smsm;
 241        u32 val;
 242
 243        if (entry->subscription) {
 244                val = readl(entry->subscription + smsm->local_host);
 245                val &= ~BIT(irq);
 246                writel(val, entry->subscription + smsm->local_host);
 247        }
 248
 249        clear_bit(irq, entry->irq_enabled);
 250}
 251
 252/**
 253 * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
 254 * @irqd:       IRQ handle to be unmasked
 255 *
 256 * This subscribes the local CPU to interrupts upon changes to the defined
 257 * status bit. The bit is also marked for cascading.
 258 */
 259static void smsm_unmask_irq(struct irq_data *irqd)
 260{
 261        struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
 262        irq_hw_number_t irq = irqd_to_hwirq(irqd);
 263        struct qcom_smsm *smsm = entry->smsm;
 264        u32 val;
 265
 266        /* Make sure our last cached state is up-to-date */
 267        if (readl(entry->remote_state) & BIT(irq))
 268                set_bit(irq, &entry->last_value);
 269        else
 270                clear_bit(irq, &entry->last_value);
 271
 272        set_bit(irq, entry->irq_enabled);
 273
 274        if (entry->subscription) {
 275                val = readl(entry->subscription + smsm->local_host);
 276                val |= BIT(irq);
 277                writel(val, entry->subscription + smsm->local_host);
 278        }
 279}
 280
 281/**
 282 * smsm_set_irq_type() - updates the requested IRQ type for the cascading
 283 * @irqd:       consumer interrupt handle
 284 * @type:       requested flags
 285 */
 286static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type)
 287{
 288        struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
 289        irq_hw_number_t irq = irqd_to_hwirq(irqd);
 290
 291        if (!(type & IRQ_TYPE_EDGE_BOTH))
 292                return -EINVAL;
 293
 294        if (type & IRQ_TYPE_EDGE_RISING)
 295                set_bit(irq, entry->irq_rising);
 296        else
 297                clear_bit(irq, entry->irq_rising);
 298
 299        if (type & IRQ_TYPE_EDGE_FALLING)
 300                set_bit(irq, entry->irq_falling);
 301        else
 302                clear_bit(irq, entry->irq_falling);
 303
 304        return 0;
 305}
 306
 307static int smsm_get_irqchip_state(struct irq_data *irqd,
 308                                  enum irqchip_irq_state which, bool *state)
 309{
 310        struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
 311        irq_hw_number_t irq = irqd_to_hwirq(irqd);
 312        u32 val;
 313
 314        if (which != IRQCHIP_STATE_LINE_LEVEL)
 315                return -EINVAL;
 316
 317        val = readl(entry->remote_state);
 318        *state = !!(val & BIT(irq));
 319
 320        return 0;
 321}
 322
 323static struct irq_chip smsm_irq_chip = {
 324        .name           = "smsm",
 325        .irq_mask       = smsm_mask_irq,
 326        .irq_unmask     = smsm_unmask_irq,
 327        .irq_set_type   = smsm_set_irq_type,
 328        .irq_get_irqchip_state = smsm_get_irqchip_state,
 329};
 330
 331/**
 332 * smsm_irq_map() - sets up a mapping for a cascaded IRQ
 333 * @d:          IRQ domain representing an entry
 334 * @irq:        IRQ to set up
 335 * @hw:         unused
 336 */
 337static int smsm_irq_map(struct irq_domain *d,
 338                        unsigned int irq,
 339                        irq_hw_number_t hw)
 340{
 341        struct smsm_entry *entry = d->host_data;
 342
 343        irq_set_chip_and_handler(irq, &smsm_irq_chip, handle_level_irq);
 344        irq_set_chip_data(irq, entry);
 345        irq_set_nested_thread(irq, 1);
 346
 347        return 0;
 348}
 349
 350static const struct irq_domain_ops smsm_irq_ops = {
 351        .map = smsm_irq_map,
 352        .xlate = irq_domain_xlate_twocell,
 353};
 354
 355/**
 356 * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
 357 * @smsm:       smsm driver context
 358 * @host_id:    index of the remote host to be resolved
 359 *
 360 * Parses device tree to acquire the information needed for sending the
 361 * outgoing interrupts to a remote host - identified by @host_id.
 362 */
 363static int smsm_parse_ipc(struct qcom_smsm *smsm, unsigned host_id)
 364{
 365        struct device_node *syscon;
 366        struct device_node *node = smsm->dev->of_node;
 367        struct smsm_host *host = &smsm->hosts[host_id];
 368        char key[16];
 369        int ret;
 370
 371        snprintf(key, sizeof(key), "qcom,ipc-%d", host_id);
 372        syscon = of_parse_phandle(node, key, 0);
 373        if (!syscon)
 374                return 0;
 375
 376        host->ipc_regmap = syscon_node_to_regmap(syscon);
 377        if (IS_ERR(host->ipc_regmap))
 378                return PTR_ERR(host->ipc_regmap);
 379
 380        ret = of_property_read_u32_index(node, key, 1, &host->ipc_offset);
 381        if (ret < 0) {
 382                dev_err(smsm->dev, "no offset in %s\n", key);
 383                return -EINVAL;
 384        }
 385
 386        ret = of_property_read_u32_index(node, key, 2, &host->ipc_bit);
 387        if (ret < 0) {
 388                dev_err(smsm->dev, "no bit in %s\n", key);
 389                return -EINVAL;
 390        }
 391
 392        return 0;
 393}
 394
 395/**
 396 * smsm_inbound_entry() - parse DT and set up an entry representing a remote system
 397 * @smsm:       smsm driver context
 398 * @entry:      entry context to be set up
 399 * @node:       dt node containing the entry's properties
 400 */
 401static int smsm_inbound_entry(struct qcom_smsm *smsm,
 402                              struct smsm_entry *entry,
 403                              struct device_node *node)
 404{
 405        int ret;
 406        int irq;
 407
 408        irq = irq_of_parse_and_map(node, 0);
 409        if (!irq) {
 410                dev_err(smsm->dev, "failed to parse smsm interrupt\n");
 411                return -EINVAL;
 412        }
 413
 414        ret = devm_request_threaded_irq(smsm->dev, irq,
 415                                        NULL, smsm_intr,
 416                                        IRQF_ONESHOT,
 417                                        "smsm", (void *)entry);
 418        if (ret) {
 419                dev_err(smsm->dev, "failed to request interrupt\n");
 420                return ret;
 421        }
 422
 423        entry->domain = irq_domain_add_linear(node, 32, &smsm_irq_ops, entry);
 424        if (!entry->domain) {
 425                dev_err(smsm->dev, "failed to add irq_domain\n");
 426                return -ENOMEM;
 427        }
 428
 429        return 0;
 430}
 431
 432/**
 433 * smsm_get_size_info() - parse the optional memory segment for sizes
 434 * @smsm:       smsm driver context
 435 *
 436 * Attempt to acquire the number of hosts and entries from the optional shared
 437 * memory location. Not being able to find this segment should indicate that
 438 * we're on a older system where these values was hard coded to
 439 * SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS.
 440 *
 441 * Returns 0 on success, negative errno on failure.
 442 */
 443static int smsm_get_size_info(struct qcom_smsm *smsm)
 444{
 445        size_t size;
 446        struct {
 447                u32 num_hosts;
 448                u32 num_entries;
 449                u32 reserved0;
 450                u32 reserved1;
 451        } *info;
 452
 453        info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SIZE_INFO, &size);
 454        if (IS_ERR(info) && PTR_ERR(info) != -ENOENT) {
 455                if (PTR_ERR(info) != -EPROBE_DEFER)
 456                        dev_err(smsm->dev, "unable to retrieve smsm size info\n");
 457                return PTR_ERR(info);
 458        } else if (IS_ERR(info) || size != sizeof(*info)) {
 459                dev_warn(smsm->dev, "no smsm size info, using defaults\n");
 460                smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES;
 461                smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS;
 462                return 0;
 463        }
 464
 465        smsm->num_entries = info->num_entries;
 466        smsm->num_hosts = info->num_hosts;
 467
 468        dev_dbg(smsm->dev,
 469                "found custom size of smsm: %d entries %d hosts\n",
 470                smsm->num_entries, smsm->num_hosts);
 471
 472        return 0;
 473}
 474
 475static int qcom_smsm_probe(struct platform_device *pdev)
 476{
 477        struct device_node *local_node;
 478        struct device_node *node;
 479        struct smsm_entry *entry;
 480        struct qcom_smsm *smsm;
 481        u32 *intr_mask;
 482        size_t size;
 483        u32 *states;
 484        u32 id;
 485        int ret;
 486
 487        smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL);
 488        if (!smsm)
 489                return -ENOMEM;
 490        smsm->dev = &pdev->dev;
 491        spin_lock_init(&smsm->lock);
 492
 493        ret = smsm_get_size_info(smsm);
 494        if (ret)
 495                return ret;
 496
 497        smsm->entries = devm_kcalloc(&pdev->dev,
 498                                     smsm->num_entries,
 499                                     sizeof(struct smsm_entry),
 500                                     GFP_KERNEL);
 501        if (!smsm->entries)
 502                return -ENOMEM;
 503
 504        smsm->hosts = devm_kcalloc(&pdev->dev,
 505                                   smsm->num_hosts,
 506                                   sizeof(struct smsm_host),
 507                                   GFP_KERNEL);
 508        if (!smsm->hosts)
 509                return -ENOMEM;
 510
 511        for_each_child_of_node(pdev->dev.of_node, local_node) {
 512                if (of_find_property(local_node, "#qcom,smem-state-cells", NULL))
 513                        break;
 514        }
 515        if (!local_node) {
 516                dev_err(&pdev->dev, "no state entry\n");
 517                return -EINVAL;
 518        }
 519
 520        of_property_read_u32(pdev->dev.of_node,
 521                             "qcom,local-host",
 522                             &smsm->local_host);
 523
 524        /* Parse the host properties */
 525        for (id = 0; id < smsm->num_hosts; id++) {
 526                ret = smsm_parse_ipc(smsm, id);
 527                if (ret < 0)
 528                        return ret;
 529        }
 530
 531        /* Acquire the main SMSM state vector */
 532        ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE,
 533                              smsm->num_entries * sizeof(u32));
 534        if (ret < 0 && ret != -EEXIST) {
 535                dev_err(&pdev->dev, "unable to allocate shared state entry\n");
 536                return ret;
 537        }
 538
 539        states = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE, NULL);
 540        if (IS_ERR(states)) {
 541                dev_err(&pdev->dev, "Unable to acquire shared state entry\n");
 542                return PTR_ERR(states);
 543        }
 544
 545        /* Acquire the list of interrupt mask vectors */
 546        size = smsm->num_entries * smsm->num_hosts * sizeof(u32);
 547        ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK, size);
 548        if (ret < 0 && ret != -EEXIST) {
 549                dev_err(&pdev->dev, "unable to allocate smsm interrupt mask\n");
 550                return ret;
 551        }
 552
 553        intr_mask = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK, NULL);
 554        if (IS_ERR(intr_mask)) {
 555                dev_err(&pdev->dev, "unable to acquire shared memory interrupt mask\n");
 556                return PTR_ERR(intr_mask);
 557        }
 558
 559        /* Setup the reference to the local state bits */
 560        smsm->local_state = states + smsm->local_host;
 561        smsm->subscription = intr_mask + smsm->local_host * smsm->num_hosts;
 562
 563        /* Register the outgoing state */
 564        smsm->state = qcom_smem_state_register(local_node, &smsm_state_ops, smsm);
 565        if (IS_ERR(smsm->state)) {
 566                dev_err(smsm->dev, "failed to register qcom_smem_state\n");
 567                return PTR_ERR(smsm->state);
 568        }
 569
 570        /* Register handlers for remote processor entries of interest. */
 571        for_each_available_child_of_node(pdev->dev.of_node, node) {
 572                if (!of_property_read_bool(node, "interrupt-controller"))
 573                        continue;
 574
 575                ret = of_property_read_u32(node, "reg", &id);
 576                if (ret || id >= smsm->num_entries) {
 577                        dev_err(&pdev->dev, "invalid reg of entry\n");
 578                        if (!ret)
 579                                ret = -EINVAL;
 580                        goto unwind_interfaces;
 581                }
 582                entry = &smsm->entries[id];
 583
 584                entry->smsm = smsm;
 585                entry->remote_state = states + id;
 586
 587                /* Setup subscription pointers and unsubscribe to any kicks */
 588                entry->subscription = intr_mask + id * smsm->num_hosts;
 589                writel(0, entry->subscription + smsm->local_host);
 590
 591                ret = smsm_inbound_entry(smsm, entry, node);
 592                if (ret < 0)
 593                        goto unwind_interfaces;
 594        }
 595
 596        platform_set_drvdata(pdev, smsm);
 597
 598        return 0;
 599
 600unwind_interfaces:
 601        for (id = 0; id < smsm->num_entries; id++)
 602                if (smsm->entries[id].domain)
 603                        irq_domain_remove(smsm->entries[id].domain);
 604
 605        qcom_smem_state_unregister(smsm->state);
 606
 607        return ret;
 608}
 609
 610static int qcom_smsm_remove(struct platform_device *pdev)
 611{
 612        struct qcom_smsm *smsm = platform_get_drvdata(pdev);
 613        unsigned id;
 614
 615        for (id = 0; id < smsm->num_entries; id++)
 616                if (smsm->entries[id].domain)
 617                        irq_domain_remove(smsm->entries[id].domain);
 618
 619        qcom_smem_state_unregister(smsm->state);
 620
 621        return 0;
 622}
 623
 624static const struct of_device_id qcom_smsm_of_match[] = {
 625        { .compatible = "qcom,smsm" },
 626        {}
 627};
 628MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
 629
 630static struct platform_driver qcom_smsm_driver = {
 631        .probe = qcom_smsm_probe,
 632        .remove = qcom_smsm_remove,
 633        .driver  = {
 634                .name  = "qcom-smsm",
 635                .of_match_table = qcom_smsm_of_match,
 636        },
 637};
 638module_platform_driver(qcom_smsm_driver);
 639
 640MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");
 641MODULE_LICENSE("GPL v2");
 642