Karthikeyan Ramasubramanian | 97a1028 | 2016-09-16 12:01:18 -0600 | [diff] [blame] | 1 | /* drivers/gpio/gpio-msm-smp2p.c |
| 2 | * |
| 3 | * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. |
| 4 | * |
| 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. |
| 8 | * |
| 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. |
| 13 | */ |
| 14 | #include <linux/module.h> |
| 15 | #include <linux/platform_device.h> |
| 16 | #include <linux/bitmap.h> |
| 17 | #include <linux/of.h> |
| 18 | #include <linux/gpio.h> |
| 19 | #include <linux/interrupt.h> |
| 20 | #include <linux/irq.h> |
| 21 | #include <linux/irqdomain.h> |
| 22 | #include <linux/slab.h> |
| 23 | #include <linux/list.h> |
| 24 | #include <linux/ipc_logging.h> |
| 25 | #include "../soc/qcom/smp2p_private_api.h" |
| 26 | #include "../soc/qcom/smp2p_private.h" |
| 27 | |
| 28 | /* GPIO device - one per SMP2P entry. */ |
| 29 | struct smp2p_chip_dev { |
| 30 | struct list_head entry_list; |
| 31 | char name[SMP2P_MAX_ENTRY_NAME]; |
| 32 | int remote_pid; |
| 33 | bool is_inbound; |
| 34 | bool is_open; |
| 35 | bool in_shadow; |
| 36 | uint32_t shadow_value; |
| 37 | struct work_struct shadow_work; |
| 38 | spinlock_t shadow_lock; |
| 39 | struct notifier_block out_notifier; |
| 40 | struct notifier_block in_notifier; |
| 41 | struct msm_smp2p_out *out_handle; |
| 42 | |
| 43 | struct gpio_chip gpio; |
| 44 | struct irq_domain *irq_domain; |
| 45 | int irq_base; |
| 46 | |
| 47 | spinlock_t irq_lock; |
| 48 | DECLARE_BITMAP(irq_enabled, SMP2P_BITS_PER_ENTRY); |
| 49 | DECLARE_BITMAP(irq_rising_edge, SMP2P_BITS_PER_ENTRY); |
| 50 | DECLARE_BITMAP(irq_falling_edge, SMP2P_BITS_PER_ENTRY); |
| 51 | }; |
| 52 | |
| 53 | static struct platform_driver smp2p_gpio_driver; |
| 54 | static struct lock_class_key smp2p_gpio_lock_class; |
| 55 | static struct irq_chip smp2p_gpio_irq_chip; |
| 56 | static DEFINE_SPINLOCK(smp2p_entry_lock_lha1); |
| 57 | static LIST_HEAD(smp2p_entry_list); |
| 58 | |
| 59 | /* Used for mapping edge to name for logging. */ |
| 60 | static const char * const edge_names[] = { |
| 61 | "-", |
| 62 | "0->1", |
| 63 | "1->0", |
| 64 | "-", |
| 65 | }; |
| 66 | |
| 67 | /* Used for mapping edge to value for logging. */ |
| 68 | static const char * const edge_name_rising[] = { |
| 69 | "-", |
| 70 | "0->1", |
| 71 | }; |
| 72 | |
| 73 | /* Used for mapping edge to value for logging. */ |
| 74 | static const char * const edge_name_falling[] = { |
| 75 | "-", |
| 76 | "1->0", |
| 77 | }; |
| 78 | |
| 79 | static int smp2p_gpio_to_irq(struct gpio_chip *cp, |
| 80 | unsigned int offset); |
| 81 | |
| 82 | /** |
| 83 | * smp2p_get_value - Retrieves GPIO value. |
| 84 | * |
| 85 | * @cp: GPIO chip pointer |
| 86 | * @offset: Pin offset |
| 87 | * @returns: >=0: value of GPIO Pin; < 0 for error |
| 88 | * |
| 89 | * Error codes: |
| 90 | * -ENODEV - chip/entry invalid |
| 91 | * -ENETDOWN - valid entry, but entry not yet created |
| 92 | */ |
| 93 | static int smp2p_get_value(struct gpio_chip *cp, |
| 94 | unsigned int offset) |
| 95 | { |
| 96 | struct smp2p_chip_dev *chip; |
| 97 | int ret = 0; |
| 98 | uint32_t data; |
| 99 | |
| 100 | if (!cp) |
| 101 | return -ENODEV; |
| 102 | |
| 103 | chip = container_of(cp, struct smp2p_chip_dev, gpio); |
| 104 | if (!chip->is_open) |
| 105 | return -ENETDOWN; |
| 106 | |
| 107 | if (chip->is_inbound) |
| 108 | ret = msm_smp2p_in_read(chip->remote_pid, chip->name, &data); |
| 109 | else |
| 110 | ret = msm_smp2p_out_read(chip->out_handle, &data); |
| 111 | |
| 112 | if (!ret) |
| 113 | ret = (data & (1 << offset)) ? 1 : 0; |
| 114 | |
| 115 | return ret; |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * smp2p_set_value - Sets GPIO value. |
| 120 | * |
| 121 | * @cp: GPIO chip pointer |
| 122 | * @offset: Pin offset |
| 123 | * @value: New value |
| 124 | */ |
| 125 | static void smp2p_set_value(struct gpio_chip *cp, unsigned int offset, |
| 126 | int value) |
| 127 | { |
| 128 | struct smp2p_chip_dev *chip; |
| 129 | uint32_t data_set; |
| 130 | uint32_t data_clear; |
| 131 | bool send_irq; |
| 132 | int ret; |
| 133 | unsigned long flags; |
| 134 | |
| 135 | if (!cp) |
| 136 | return; |
| 137 | |
| 138 | chip = container_of(cp, struct smp2p_chip_dev, gpio); |
| 139 | |
| 140 | if (chip->is_inbound) { |
| 141 | SMP2P_INFO("%s: '%s':%d virq %d invalid operation\n", |
| 142 | __func__, chip->name, chip->remote_pid, |
| 143 | chip->irq_base + offset); |
| 144 | return; |
| 145 | } |
| 146 | |
| 147 | if (value & SMP2P_GPIO_NO_INT) { |
| 148 | value &= ~SMP2P_GPIO_NO_INT; |
| 149 | send_irq = false; |
| 150 | } else { |
| 151 | send_irq = true; |
| 152 | } |
| 153 | |
| 154 | if (value) { |
| 155 | data_set = 1 << offset; |
| 156 | data_clear = 0; |
| 157 | } else { |
| 158 | data_set = 0; |
| 159 | data_clear = 1 << offset; |
| 160 | } |
| 161 | |
| 162 | spin_lock_irqsave(&chip->shadow_lock, flags); |
| 163 | if (!chip->is_open) { |
| 164 | chip->in_shadow = true; |
| 165 | chip->shadow_value &= ~data_clear; |
| 166 | chip->shadow_value |= data_set; |
| 167 | spin_unlock_irqrestore(&chip->shadow_lock, flags); |
| 168 | return; |
| 169 | } |
| 170 | |
| 171 | if (chip->in_shadow) { |
| 172 | chip->in_shadow = false; |
| 173 | chip->shadow_value &= ~data_clear; |
| 174 | chip->shadow_value |= data_set; |
| 175 | ret = msm_smp2p_out_modify(chip->out_handle, |
| 176 | chip->shadow_value, 0x0, send_irq); |
| 177 | chip->shadow_value = 0x0; |
| 178 | } else { |
| 179 | ret = msm_smp2p_out_modify(chip->out_handle, |
| 180 | data_set, data_clear, send_irq); |
| 181 | } |
| 182 | spin_unlock_irqrestore(&chip->shadow_lock, flags); |
| 183 | |
| 184 | if (ret) |
| 185 | SMP2P_GPIO("'%s':%d gpio %d set to %d failed (%d)\n", |
| 186 | chip->name, chip->remote_pid, |
| 187 | chip->gpio.base + offset, value, ret); |
| 188 | else |
| 189 | SMP2P_GPIO("'%s':%d gpio %d set to %d\n", |
| 190 | chip->name, chip->remote_pid, |
| 191 | chip->gpio.base + offset, value); |
| 192 | } |
| 193 | |
| 194 | /** |
| 195 | * smp2p_direction_input - Sets GPIO direction to input. |
| 196 | * |
| 197 | * @cp: GPIO chip pointer |
| 198 | * @offset: Pin offset |
| 199 | * @returns: 0 for success; < 0 for failure |
| 200 | */ |
| 201 | static int smp2p_direction_input(struct gpio_chip *cp, unsigned int offset) |
| 202 | { |
| 203 | struct smp2p_chip_dev *chip; |
| 204 | |
| 205 | if (!cp) |
| 206 | return -ENODEV; |
| 207 | |
| 208 | chip = container_of(cp, struct smp2p_chip_dev, gpio); |
| 209 | if (!chip->is_inbound) |
| 210 | return -EPERM; |
| 211 | |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | /** |
| 216 | * smp2p_direction_output - Sets GPIO direction to output. |
| 217 | * |
| 218 | * @cp: GPIO chip pointer |
| 219 | * @offset: Pin offset |
| 220 | * @value: Direction |
| 221 | * @returns: 0 for success; < 0 for failure |
| 222 | */ |
| 223 | static int smp2p_direction_output(struct gpio_chip *cp, |
| 224 | unsigned int offset, int value) |
| 225 | { |
| 226 | struct smp2p_chip_dev *chip; |
| 227 | |
| 228 | if (!cp) |
| 229 | return -ENODEV; |
| 230 | |
| 231 | chip = container_of(cp, struct smp2p_chip_dev, gpio); |
| 232 | if (chip->is_inbound) |
| 233 | return -EPERM; |
| 234 | |
| 235 | return 0; |
| 236 | } |
| 237 | |
| 238 | /** |
| 239 | * smp2p_gpio_to_irq - Convert GPIO pin to virtual IRQ pin. |
| 240 | * |
| 241 | * @cp: GPIO chip pointer |
| 242 | * @offset: Pin offset |
| 243 | * @returns: >0 for virtual irq value; < 0 for failure |
| 244 | */ |
| 245 | static int smp2p_gpio_to_irq(struct gpio_chip *cp, unsigned int offset) |
| 246 | { |
| 247 | struct smp2p_chip_dev *chip; |
| 248 | |
| 249 | chip = container_of(cp, struct smp2p_chip_dev, gpio); |
| 250 | if (!cp || chip->irq_base <= 0) |
| 251 | return -ENODEV; |
| 252 | |
| 253 | return chip->irq_base + offset; |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * smp2p_gpio_irq_mask_helper - Mask/Unmask interrupt. |
| 258 | * |
| 259 | * @d: IRQ data |
| 260 | * @mask: true to mask (disable), false to unmask (enable) |
| 261 | */ |
| 262 | static void smp2p_gpio_irq_mask_helper(struct irq_data *d, bool mask) |
| 263 | { |
| 264 | struct smp2p_chip_dev *chip; |
| 265 | int offset; |
| 266 | unsigned long flags; |
| 267 | |
| 268 | chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq); |
| 269 | if (!chip || chip->irq_base <= 0) |
| 270 | return; |
| 271 | |
| 272 | offset = d->irq - chip->irq_base; |
| 273 | spin_lock_irqsave(&chip->irq_lock, flags); |
| 274 | if (mask) |
| 275 | clear_bit(offset, chip->irq_enabled); |
| 276 | else |
| 277 | set_bit(offset, chip->irq_enabled); |
| 278 | spin_unlock_irqrestore(&chip->irq_lock, flags); |
| 279 | } |
| 280 | |
| 281 | /** |
| 282 | * smp2p_gpio_irq_mask - Mask interrupt. |
| 283 | * |
| 284 | * @d: IRQ data |
| 285 | */ |
| 286 | static void smp2p_gpio_irq_mask(struct irq_data *d) |
| 287 | { |
| 288 | smp2p_gpio_irq_mask_helper(d, true); |
| 289 | } |
| 290 | |
| 291 | /** |
| 292 | * smp2p_gpio_irq_unmask - Unmask interrupt. |
| 293 | * |
| 294 | * @d: IRQ data |
| 295 | */ |
| 296 | static void smp2p_gpio_irq_unmask(struct irq_data *d) |
| 297 | { |
| 298 | smp2p_gpio_irq_mask_helper(d, false); |
| 299 | } |
| 300 | |
| 301 | /** |
| 302 | * smp2p_gpio_irq_set_type - Set interrupt edge type. |
| 303 | * |
| 304 | * @d: IRQ data |
| 305 | * @type: Edge type for interrupt |
| 306 | * @returns 0 for success; < 0 for failure |
| 307 | */ |
| 308 | static int smp2p_gpio_irq_set_type(struct irq_data *d, unsigned int type) |
| 309 | { |
| 310 | struct smp2p_chip_dev *chip; |
| 311 | int offset; |
| 312 | unsigned long flags; |
| 313 | int ret = 0; |
| 314 | |
| 315 | chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq); |
| 316 | if (!chip) |
| 317 | return -ENODEV; |
| 318 | |
| 319 | if (chip->irq_base <= 0) { |
| 320 | SMP2P_ERR("%s: '%s':%d virqbase %d invalid\n", |
| 321 | __func__, chip->name, chip->remote_pid, |
| 322 | chip->irq_base); |
| 323 | return -ENODEV; |
| 324 | } |
| 325 | |
| 326 | offset = d->irq - chip->irq_base; |
| 327 | |
| 328 | spin_lock_irqsave(&chip->irq_lock, flags); |
| 329 | clear_bit(offset, chip->irq_rising_edge); |
| 330 | clear_bit(offset, chip->irq_falling_edge); |
| 331 | switch (type) { |
| 332 | case IRQ_TYPE_EDGE_RISING: |
| 333 | set_bit(offset, chip->irq_rising_edge); |
| 334 | break; |
| 335 | |
| 336 | case IRQ_TYPE_EDGE_FALLING: |
| 337 | set_bit(offset, chip->irq_falling_edge); |
| 338 | break; |
| 339 | |
| 340 | case IRQ_TYPE_NONE: |
| 341 | case IRQ_TYPE_DEFAULT: |
| 342 | case IRQ_TYPE_EDGE_BOTH: |
| 343 | set_bit(offset, chip->irq_rising_edge); |
| 344 | set_bit(offset, chip->irq_falling_edge); |
| 345 | break; |
| 346 | |
| 347 | default: |
| 348 | SMP2P_ERR("%s: unsupported interrupt type 0x%x\n", |
| 349 | __func__, type); |
| 350 | ret = -EINVAL; |
| 351 | break; |
| 352 | } |
| 353 | spin_unlock_irqrestore(&chip->irq_lock, flags); |
| 354 | return ret; |
| 355 | } |
| 356 | |
| 357 | /** |
| 358 | * smp2p_irq_map - Creates or updates binding of virtual IRQ |
| 359 | * |
| 360 | * @domain_ptr: Interrupt domain pointer |
| 361 | * @virq: Virtual IRQ |
| 362 | * @hw: Hardware IRQ (same as virq for nomap) |
| 363 | * @returns: 0 for success |
| 364 | */ |
| 365 | static int smp2p_irq_map(struct irq_domain *domain_ptr, unsigned int virq, |
| 366 | irq_hw_number_t hw) |
| 367 | { |
| 368 | struct smp2p_chip_dev *chip; |
| 369 | |
| 370 | chip = domain_ptr->host_data; |
| 371 | if (!chip) { |
| 372 | SMP2P_ERR("%s: invalid domain ptr\n", __func__); |
| 373 | return -ENODEV; |
| 374 | } |
| 375 | |
| 376 | /* map chip structures to device */ |
| 377 | irq_set_lockdep_class(virq, &smp2p_gpio_lock_class); |
| 378 | irq_set_chip_and_handler(virq, &smp2p_gpio_irq_chip, |
| 379 | handle_level_irq); |
| 380 | irq_set_chip_data(virq, chip); |
| 381 | |
| 382 | return 0; |
| 383 | } |
| 384 | |
| 385 | static struct irq_chip smp2p_gpio_irq_chip = { |
| 386 | .name = "smp2p_gpio", |
| 387 | .irq_mask = smp2p_gpio_irq_mask, |
| 388 | .irq_unmask = smp2p_gpio_irq_unmask, |
| 389 | .irq_set_type = smp2p_gpio_irq_set_type, |
| 390 | }; |
| 391 | |
| 392 | /* No-map interrupt Domain */ |
| 393 | static const struct irq_domain_ops smp2p_irq_domain_ops = { |
| 394 | .map = smp2p_irq_map, |
| 395 | }; |
| 396 | |
| 397 | /** |
| 398 | * msm_summary_irq_handler - Handles inbound entry change notification. |
| 399 | * |
| 400 | * @chip: GPIO chip pointer |
| 401 | * @entry: Change notification data |
| 402 | * |
| 403 | * Whenever an entry changes, this callback is triggered to determine |
| 404 | * which bits changed and if the corresponding interrupts need to be |
| 405 | * triggered. |
| 406 | */ |
| 407 | static void msm_summary_irq_handler(struct smp2p_chip_dev *chip, |
| 408 | struct msm_smp2p_update_notif *entry) |
| 409 | { |
| 410 | int i; |
| 411 | uint32_t cur_val; |
| 412 | uint32_t prev_val; |
| 413 | uint32_t edge; |
| 414 | unsigned long flags; |
| 415 | bool trigger_interrupt; |
| 416 | bool irq_rising; |
| 417 | bool irq_falling; |
| 418 | |
| 419 | cur_val = entry->current_value; |
| 420 | prev_val = entry->previous_value; |
| 421 | |
| 422 | if (chip->irq_base <= 0) |
| 423 | return; |
| 424 | |
| 425 | SMP2P_GPIO("'%s':%d GPIO Summary IRQ Change %08x->%08x\n", |
| 426 | chip->name, chip->remote_pid, prev_val, cur_val); |
| 427 | |
| 428 | for (i = 0; i < SMP2P_BITS_PER_ENTRY; ++i) { |
| 429 | spin_lock_irqsave(&chip->irq_lock, flags); |
| 430 | trigger_interrupt = false; |
| 431 | edge = (prev_val & 0x1) << 1 | (cur_val & 0x1); |
| 432 | irq_rising = test_bit(i, chip->irq_rising_edge); |
| 433 | irq_falling = test_bit(i, chip->irq_falling_edge); |
| 434 | |
| 435 | if (test_bit(i, chip->irq_enabled)) { |
| 436 | if (edge == 0x1 && irq_rising) |
| 437 | /* 0->1 transition */ |
| 438 | trigger_interrupt = true; |
| 439 | else if (edge == 0x2 && irq_falling) |
| 440 | /* 1->0 transition */ |
| 441 | trigger_interrupt = true; |
| 442 | } else { |
| 443 | SMP2P_GPIO( |
| 444 | "'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s disabled\n", |
| 445 | chip->name, chip->remote_pid, i, |
| 446 | chip->irq_base + i, |
| 447 | edge_name_rising[irq_rising], |
| 448 | edge_name_falling[irq_falling], |
| 449 | edge_names[edge]); |
| 450 | } |
| 451 | spin_unlock_irqrestore(&chip->irq_lock, flags); |
| 452 | |
| 453 | if (trigger_interrupt) { |
| 454 | SMP2P_INFO( |
| 455 | "'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s triggering\n", |
| 456 | chip->name, chip->remote_pid, i, |
| 457 | chip->irq_base + i, |
| 458 | edge_name_rising[irq_rising], |
| 459 | edge_name_falling[irq_falling], |
| 460 | edge_names[edge]); |
| 461 | (void)generic_handle_irq(chip->irq_base + i); |
| 462 | } |
| 463 | |
| 464 | cur_val >>= 1; |
| 465 | prev_val >>= 1; |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | /** |
| 470 | * Adds an interrupt domain based upon the DT node. |
| 471 | * |
| 472 | * @chip: pointer to GPIO chip |
| 473 | * @node: pointer to Device Tree node |
| 474 | */ |
| 475 | static void smp2p_add_irq_domain(struct smp2p_chip_dev *chip, |
| 476 | struct device_node *node) |
| 477 | { |
| 478 | int irq_base; |
| 479 | |
| 480 | /* map GPIO pins to interrupts */ |
| 481 | chip->irq_domain = irq_domain_add_linear(node, SMP2P_BITS_PER_ENTRY, |
| 482 | &smp2p_irq_domain_ops, chip); |
| 483 | if (!chip->irq_domain) { |
| 484 | SMP2P_ERR("%s: unable to create interrupt domain '%s':%d\n", |
| 485 | __func__, chip->name, chip->remote_pid); |
| 486 | goto domain_fail; |
| 487 | } |
| 488 | |
| 489 | /* alloc a contiguous set of virt irqs from anywhere in the irq space */ |
| 490 | irq_base = irq_alloc_descs_from(0, SMP2P_BITS_PER_ENTRY, of_node_to_nid( |
| 491 | irq_domain_get_of_node(chip->irq_domain))); |
| 492 | if (irq_base < 0) { |
| 493 | SMP2P_ERR("alloc virt irqs failed:%d name:%s pid%d\n", irq_base, |
| 494 | chip->name, chip->remote_pid); |
| 495 | goto irq_alloc_fail; |
| 496 | } |
| 497 | |
| 498 | /* map the allocated irqs to gpios */ |
| 499 | irq_domain_associate_many(chip->irq_domain, irq_base, 0, |
| 500 | SMP2P_BITS_PER_ENTRY); |
| 501 | |
| 502 | chip->irq_base = irq_base; |
| 503 | SMP2P_DBG("create mapping:%d naem:%s pid:%d\n", chip->irq_base, |
| 504 | chip->name, chip->remote_pid); |
| 505 | return; |
| 506 | |
| 507 | irq_alloc_fail: |
| 508 | irq_domain_remove(chip->irq_domain); |
| 509 | domain_fail: |
| 510 | return; |
| 511 | } |
| 512 | |
| 513 | /** |
| 514 | * Notifier function passed into smp2p API for out bound entries. |
| 515 | * |
| 516 | * @self: Pointer to calling notifier block |
| 517 | * @event: Event |
| 518 | * @data: Event-specific data |
| 519 | * @returns: 0 |
| 520 | */ |
| 521 | static int smp2p_gpio_out_notify(struct notifier_block *self, |
| 522 | unsigned long event, void *data) |
| 523 | { |
| 524 | struct smp2p_chip_dev *chip; |
| 525 | |
| 526 | chip = container_of(self, struct smp2p_chip_dev, out_notifier); |
| 527 | |
| 528 | switch (event) { |
| 529 | case SMP2P_OPEN: |
| 530 | chip->is_open = 1; |
| 531 | SMP2P_GPIO("%s: Opened out '%s':%d in_shadow[%d]\n", __func__, |
| 532 | chip->name, chip->remote_pid, chip->in_shadow); |
| 533 | if (chip->in_shadow) |
| 534 | schedule_work(&chip->shadow_work); |
| 535 | break; |
| 536 | case SMP2P_ENTRY_UPDATE: |
| 537 | break; |
| 538 | default: |
| 539 | SMP2P_ERR("%s: Unknown event\n", __func__); |
| 540 | break; |
| 541 | } |
| 542 | return 0; |
| 543 | } |
| 544 | |
| 545 | /** |
| 546 | * Notifier function passed into smp2p API for in bound entries. |
| 547 | * |
| 548 | * @self: Pointer to calling notifier block |
| 549 | * @event: Event |
| 550 | * @data: Event-specific data |
| 551 | * @returns: 0 |
| 552 | */ |
| 553 | static int smp2p_gpio_in_notify(struct notifier_block *self, |
| 554 | unsigned long event, void *data) |
| 555 | { |
| 556 | struct smp2p_chip_dev *chip; |
| 557 | |
| 558 | chip = container_of(self, struct smp2p_chip_dev, in_notifier); |
| 559 | |
| 560 | switch (event) { |
| 561 | case SMP2P_OPEN: |
| 562 | chip->is_open = 1; |
| 563 | SMP2P_GPIO("%s: Opened in '%s':%d\n", __func__, |
| 564 | chip->name, chip->remote_pid); |
| 565 | break; |
| 566 | case SMP2P_ENTRY_UPDATE: |
| 567 | msm_summary_irq_handler(chip, data); |
| 568 | break; |
| 569 | default: |
| 570 | SMP2P_ERR("%s: Unknown event\n", __func__); |
| 571 | break; |
| 572 | } |
| 573 | return 0; |
| 574 | } |
| 575 | |
| 576 | /** |
| 577 | * smp2p_gpio_shadow_worker - Handles shadow updates of an entry. |
| 578 | * |
| 579 | * @work: Work Item scheduled to handle the shadow updates. |
| 580 | */ |
| 581 | static void smp2p_gpio_shadow_worker(struct work_struct *work) |
| 582 | { |
| 583 | struct smp2p_chip_dev *chip; |
| 584 | int ret; |
| 585 | unsigned long flags; |
| 586 | |
| 587 | chip = container_of(work, struct smp2p_chip_dev, shadow_work); |
| 588 | spin_lock_irqsave(&chip->shadow_lock, flags); |
| 589 | if (chip->in_shadow) { |
| 590 | ret = msm_smp2p_out_modify(chip->out_handle, |
| 591 | chip->shadow_value, 0x0, true); |
| 592 | |
| 593 | if (ret) |
| 594 | SMP2P_GPIO("'%s':%d shadow val[0x%x] failed(%d)\n", |
| 595 | chip->name, chip->remote_pid, |
| 596 | chip->shadow_value, ret); |
| 597 | else |
| 598 | SMP2P_GPIO("'%s':%d shadow val[0x%x]\n", |
| 599 | chip->name, chip->remote_pid, |
| 600 | chip->shadow_value); |
| 601 | chip->shadow_value = 0; |
| 602 | chip->in_shadow = false; |
| 603 | } |
| 604 | spin_unlock_irqrestore(&chip->shadow_lock, flags); |
| 605 | } |
| 606 | |
| 607 | /** |
| 608 | * Device tree probe function. |
| 609 | * |
| 610 | * @pdev: Pointer to device tree data. |
| 611 | * @returns: 0 on success; -ENODEV otherwise |
| 612 | * |
| 613 | * Called for each smp2pgpio entry in the device tree. |
| 614 | */ |
| 615 | static int smp2p_gpio_probe(struct platform_device *pdev) |
| 616 | { |
| 617 | struct device_node *node; |
| 618 | char *key; |
| 619 | struct smp2p_chip_dev *chip; |
| 620 | const char *name_tmp; |
| 621 | unsigned long flags; |
| 622 | bool is_test_entry = false; |
| 623 | int ret; |
| 624 | |
| 625 | chip = kzalloc(sizeof(struct smp2p_chip_dev), GFP_KERNEL); |
| 626 | if (!chip) { |
| 627 | SMP2P_ERR("%s: out of memory\n", __func__); |
| 628 | ret = -ENOMEM; |
| 629 | goto fail; |
| 630 | } |
| 631 | spin_lock_init(&chip->irq_lock); |
| 632 | spin_lock_init(&chip->shadow_lock); |
| 633 | INIT_WORK(&chip->shadow_work, smp2p_gpio_shadow_worker); |
| 634 | |
| 635 | /* parse device tree */ |
| 636 | node = pdev->dev.of_node; |
| 637 | key = "qcom,entry-name"; |
| 638 | ret = of_property_read_string(node, key, &name_tmp); |
| 639 | if (ret) { |
| 640 | SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key); |
| 641 | goto fail; |
| 642 | } |
| 643 | strlcpy(chip->name, name_tmp, sizeof(chip->name)); |
| 644 | |
| 645 | key = "qcom,remote-pid"; |
| 646 | ret = of_property_read_u32(node, key, &chip->remote_pid); |
| 647 | if (ret) { |
| 648 | SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key); |
| 649 | goto fail; |
| 650 | } |
| 651 | |
| 652 | key = "qcom,is-inbound"; |
| 653 | chip->is_inbound = of_property_read_bool(node, key); |
| 654 | |
| 655 | /* create virtual GPIO controller */ |
| 656 | chip->gpio.label = chip->name; |
| 657 | chip->gpio.parent = &pdev->dev; |
| 658 | chip->gpio.owner = THIS_MODULE; |
| 659 | chip->gpio.direction_input = smp2p_direction_input, |
| 660 | chip->gpio.get = smp2p_get_value; |
| 661 | chip->gpio.direction_output = smp2p_direction_output, |
| 662 | chip->gpio.set = smp2p_set_value; |
| 663 | chip->gpio.to_irq = smp2p_gpio_to_irq, |
| 664 | chip->gpio.base = -1; /* use dynamic GPIO pin allocation */ |
| 665 | chip->gpio.ngpio = SMP2P_BITS_PER_ENTRY; |
| 666 | ret = gpiochip_add(&chip->gpio); |
| 667 | if (ret) { |
| 668 | SMP2P_ERR("%s: unable to register GPIO '%s' ret %d\n", |
| 669 | __func__, chip->name, ret); |
| 670 | goto fail; |
| 671 | } |
| 672 | |
| 673 | /* |
| 674 | * Test entries opened by GPIO Test conflict with loopback |
| 675 | * support, so the test entries must be explicitly opened |
| 676 | * in the unit test framework. |
| 677 | */ |
| 678 | if (strcmp("smp2p", chip->name) == 0) |
| 679 | is_test_entry = true; |
| 680 | |
| 681 | if (!chip->is_inbound) { |
| 682 | chip->out_notifier.notifier_call = smp2p_gpio_out_notify; |
| 683 | if (!is_test_entry) { |
| 684 | ret = msm_smp2p_out_open(chip->remote_pid, chip->name, |
| 685 | &chip->out_notifier, |
| 686 | &chip->out_handle); |
| 687 | if (ret < 0) |
| 688 | goto error; |
| 689 | } |
| 690 | } else { |
| 691 | chip->in_notifier.notifier_call = smp2p_gpio_in_notify; |
| 692 | if (!is_test_entry) { |
| 693 | ret = msm_smp2p_in_register(chip->remote_pid, |
| 694 | chip->name, |
| 695 | &chip->in_notifier); |
| 696 | if (ret < 0) |
| 697 | goto error; |
| 698 | } |
| 699 | } |
| 700 | |
| 701 | spin_lock_irqsave(&smp2p_entry_lock_lha1, flags); |
| 702 | list_add(&chip->entry_list, &smp2p_entry_list); |
| 703 | spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags); |
| 704 | |
| 705 | /* |
| 706 | * Create interrupt domain - note that chip can't be removed from the |
| 707 | * interrupt domain, so chip cannot be deleted after this point. |
| 708 | */ |
| 709 | if (chip->is_inbound) |
| 710 | smp2p_add_irq_domain(chip, node); |
| 711 | else |
| 712 | chip->irq_base = -1; |
| 713 | |
| 714 | SMP2P_GPIO("%s: added %s%s entry '%s':%d gpio %d irq %d", |
| 715 | __func__, |
| 716 | is_test_entry ? "test " : "", |
| 717 | chip->is_inbound ? "in" : "out", |
| 718 | chip->name, chip->remote_pid, |
| 719 | chip->gpio.base, chip->irq_base); |
| 720 | |
| 721 | return 0; |
| 722 | error: |
| 723 | gpiochip_remove(&chip->gpio); |
| 724 | |
| 725 | fail: |
| 726 | kfree(chip); |
| 727 | return ret; |
| 728 | } |
| 729 | |
| 730 | /** |
| 731 | * smp2p_gpio_open_close - Opens or closes entry. |
| 732 | * |
| 733 | * @entry: Entry to open or close |
| 734 | * @do_open: true = open port; false = close |
| 735 | */ |
| 736 | static void smp2p_gpio_open_close(struct smp2p_chip_dev *entry, |
| 737 | bool do_open) |
| 738 | { |
| 739 | int ret; |
| 740 | |
| 741 | if (do_open) { |
| 742 | /* open entry */ |
| 743 | if (entry->is_inbound) |
| 744 | ret = msm_smp2p_in_register(entry->remote_pid, |
| 745 | entry->name, &entry->in_notifier); |
| 746 | else |
| 747 | ret = msm_smp2p_out_open(entry->remote_pid, |
| 748 | entry->name, &entry->out_notifier, |
| 749 | &entry->out_handle); |
| 750 | SMP2P_GPIO("%s: opened %s '%s':%d ret %d\n", |
| 751 | __func__, |
| 752 | entry->is_inbound ? "in" : "out", |
| 753 | entry->name, entry->remote_pid, |
| 754 | ret); |
| 755 | } else { |
| 756 | /* close entry */ |
| 757 | if (entry->is_inbound) |
| 758 | ret = msm_smp2p_in_unregister(entry->remote_pid, |
| 759 | entry->name, &entry->in_notifier); |
| 760 | else |
| 761 | ret = msm_smp2p_out_close(&entry->out_handle); |
| 762 | entry->is_open = false; |
| 763 | SMP2P_GPIO("%s: closed %s '%s':%d ret %d\n", |
| 764 | __func__, |
| 765 | entry->is_inbound ? "in" : "out", |
| 766 | entry->name, entry->remote_pid, ret); |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | /** |
| 771 | * smp2p_gpio_open_test_entry - Opens or closes test entries for unit testing. |
| 772 | * |
| 773 | * @name: Name of the entry |
| 774 | * @remote_pid: Remote processor ID |
| 775 | * @do_open: true = open port; false = close |
| 776 | */ |
| 777 | void smp2p_gpio_open_test_entry(const char *name, int remote_pid, bool do_open) |
| 778 | { |
| 779 | struct smp2p_chip_dev *entry; |
| 780 | struct smp2p_chip_dev *start_entry; |
| 781 | unsigned long flags; |
| 782 | |
| 783 | spin_lock_irqsave(&smp2p_entry_lock_lha1, flags); |
| 784 | if (list_empty(&smp2p_entry_list)) { |
| 785 | spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags); |
| 786 | return; |
| 787 | } |
| 788 | start_entry = list_first_entry(&smp2p_entry_list, |
| 789 | struct smp2p_chip_dev, |
| 790 | entry_list); |
| 791 | entry = start_entry; |
| 792 | do { |
| 793 | if (!strcmp(entry->name, name) |
| 794 | && entry->remote_pid == remote_pid) { |
| 795 | /* found entry to change */ |
| 796 | spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags); |
| 797 | smp2p_gpio_open_close(entry, do_open); |
| 798 | spin_lock_irqsave(&smp2p_entry_lock_lha1, flags); |
| 799 | } |
| 800 | list_rotate_left(&smp2p_entry_list); |
| 801 | entry = list_first_entry(&smp2p_entry_list, |
| 802 | struct smp2p_chip_dev, |
| 803 | entry_list); |
| 804 | } while (entry != start_entry); |
| 805 | spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags); |
| 806 | } |
| 807 | |
| 808 | static const struct of_device_id msm_smp2p_match_table[] = { |
| 809 | {.compatible = "qcom,smp2pgpio", }, |
| 810 | {}, |
| 811 | }; |
| 812 | |
| 813 | static struct platform_driver smp2p_gpio_driver = { |
| 814 | .probe = smp2p_gpio_probe, |
| 815 | .driver = { |
| 816 | .name = "smp2pgpio", |
| 817 | .owner = THIS_MODULE, |
| 818 | .of_match_table = msm_smp2p_match_table, |
| 819 | }, |
| 820 | }; |
| 821 | |
| 822 | static int smp2p_init(void) |
| 823 | { |
| 824 | INIT_LIST_HEAD(&smp2p_entry_list); |
| 825 | return platform_driver_register(&smp2p_gpio_driver); |
| 826 | } |
| 827 | module_init(smp2p_init); |
| 828 | |
| 829 | static void __exit smp2p_exit(void) |
| 830 | { |
| 831 | platform_driver_unregister(&smp2p_gpio_driver); |
| 832 | } |
| 833 | module_exit(smp2p_exit); |
| 834 | |
| 835 | MODULE_DESCRIPTION("SMP2P GPIO"); |
| 836 | MODULE_LICENSE("GPL v2"); |