| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Core driver for the pin config portions of the pin control subsystem |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * Written on behalf of Linaro for ST-Ericsson |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Author: Linus Walleij <linus.walleij@linaro.org> |
|---|
| 8 | | - * |
|---|
| 9 | | - * License terms: GNU General Public License (GPL) version 2 |
|---|
| 10 | 9 | */ |
|---|
| 11 | 10 | #define pr_fmt(fmt) "pinconfig core: " fmt |
|---|
| 12 | 11 | |
|---|
| .. | .. |
|---|
| 17 | 16 | #include <linux/slab.h> |
|---|
| 18 | 17 | #include <linux/debugfs.h> |
|---|
| 19 | 18 | #include <linux/seq_file.h> |
|---|
| 20 | | -#include <linux/uaccess.h> |
|---|
| 21 | 19 | #include <linux/pinctrl/machine.h> |
|---|
| 22 | 20 | #include <linux/pinctrl/pinctrl.h> |
|---|
| 23 | 21 | #include <linux/pinctrl/pinconf.h> |
|---|
| .. | .. |
|---|
| 366 | 364 | return 0; |
|---|
| 367 | 365 | } |
|---|
| 368 | 366 | |
|---|
| 369 | | -static int pinconf_pins_open(struct inode *inode, struct file *file) |
|---|
| 370 | | -{ |
|---|
| 371 | | - return single_open(file, pinconf_pins_show, inode->i_private); |
|---|
| 372 | | -} |
|---|
| 373 | | - |
|---|
| 374 | | -static int pinconf_groups_open(struct inode *inode, struct file *file) |
|---|
| 375 | | -{ |
|---|
| 376 | | - return single_open(file, pinconf_groups_show, inode->i_private); |
|---|
| 377 | | -} |
|---|
| 378 | | - |
|---|
| 379 | | -static const struct file_operations pinconf_pins_ops = { |
|---|
| 380 | | - .open = pinconf_pins_open, |
|---|
| 381 | | - .read = seq_read, |
|---|
| 382 | | - .llseek = seq_lseek, |
|---|
| 383 | | - .release = single_release, |
|---|
| 384 | | -}; |
|---|
| 385 | | - |
|---|
| 386 | | -static const struct file_operations pinconf_groups_ops = { |
|---|
| 387 | | - .open = pinconf_groups_open, |
|---|
| 388 | | - .read = seq_read, |
|---|
| 389 | | - .llseek = seq_lseek, |
|---|
| 390 | | - .release = single_release, |
|---|
| 391 | | -}; |
|---|
| 392 | | - |
|---|
| 393 | | -#define MAX_NAME_LEN 15 |
|---|
| 394 | | - |
|---|
| 395 | | -struct dbg_cfg { |
|---|
| 396 | | - enum pinctrl_map_type map_type; |
|---|
| 397 | | - char dev_name[MAX_NAME_LEN + 1]; |
|---|
| 398 | | - char state_name[MAX_NAME_LEN + 1]; |
|---|
| 399 | | - char pin_name[MAX_NAME_LEN + 1]; |
|---|
| 400 | | -}; |
|---|
| 401 | | - |
|---|
| 402 | | -/* |
|---|
| 403 | | - * Goal is to keep this structure as global in order to simply read the |
|---|
| 404 | | - * pinconf-config file after a write to check config is as expected |
|---|
| 405 | | - */ |
|---|
| 406 | | -static struct dbg_cfg pinconf_dbg_conf; |
|---|
| 407 | | - |
|---|
| 408 | | -/** |
|---|
| 409 | | - * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl |
|---|
| 410 | | - * map, of the dev/pin/state that was last written to pinconf-config file. |
|---|
| 411 | | - * @s: string filled in with config description |
|---|
| 412 | | - * @d: not used |
|---|
| 413 | | - */ |
|---|
| 414 | | -static int pinconf_dbg_config_print(struct seq_file *s, void *d) |
|---|
| 415 | | -{ |
|---|
| 416 | | - struct pinctrl_maps *maps_node; |
|---|
| 417 | | - const struct pinctrl_map *map; |
|---|
| 418 | | - const struct pinctrl_map *found = NULL; |
|---|
| 419 | | - struct pinctrl_dev *pctldev; |
|---|
| 420 | | - struct dbg_cfg *dbg = &pinconf_dbg_conf; |
|---|
| 421 | | - int i; |
|---|
| 422 | | - |
|---|
| 423 | | - mutex_lock(&pinctrl_maps_mutex); |
|---|
| 424 | | - |
|---|
| 425 | | - /* Parse the pinctrl map and look for the elected pin/state */ |
|---|
| 426 | | - for_each_maps(maps_node, i, map) { |
|---|
| 427 | | - if (map->type != dbg->map_type) |
|---|
| 428 | | - continue; |
|---|
| 429 | | - if (strcmp(map->dev_name, dbg->dev_name)) |
|---|
| 430 | | - continue; |
|---|
| 431 | | - if (strcmp(map->name, dbg->state_name)) |
|---|
| 432 | | - continue; |
|---|
| 433 | | - |
|---|
| 434 | | - if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { |
|---|
| 435 | | - /* We found the right pin */ |
|---|
| 436 | | - found = map; |
|---|
| 437 | | - break; |
|---|
| 438 | | - } |
|---|
| 439 | | - } |
|---|
| 440 | | - |
|---|
| 441 | | - if (!found) { |
|---|
| 442 | | - seq_printf(s, "No config found for dev/state/pin, expected:\n"); |
|---|
| 443 | | - seq_printf(s, "Searched dev:%s\n", dbg->dev_name); |
|---|
| 444 | | - seq_printf(s, "Searched state:%s\n", dbg->state_name); |
|---|
| 445 | | - seq_printf(s, "Searched pin:%s\n", dbg->pin_name); |
|---|
| 446 | | - seq_printf(s, "Use: modify config_pin <devname> "\ |
|---|
| 447 | | - "<state> <pinname> <value>\n"); |
|---|
| 448 | | - goto exit; |
|---|
| 449 | | - } |
|---|
| 450 | | - |
|---|
| 451 | | - pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name); |
|---|
| 452 | | - seq_printf(s, "Dev %s has config of %s in state %s:\n", |
|---|
| 453 | | - dbg->dev_name, dbg->pin_name, dbg->state_name); |
|---|
| 454 | | - pinconf_show_config(s, pctldev, found->data.configs.configs, |
|---|
| 455 | | - found->data.configs.num_configs); |
|---|
| 456 | | - |
|---|
| 457 | | -exit: |
|---|
| 458 | | - mutex_unlock(&pinctrl_maps_mutex); |
|---|
| 459 | | - |
|---|
| 460 | | - return 0; |
|---|
| 461 | | -} |
|---|
| 462 | | - |
|---|
| 463 | | -/** |
|---|
| 464 | | - * pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl |
|---|
| 465 | | - * map, of a dev/pin/state entry based on user entries to pinconf-config |
|---|
| 466 | | - * @user_buf: contains the modification request with expected format: |
|---|
| 467 | | - * modify <config> <devicename> <state> <name> <newvalue> |
|---|
| 468 | | - * modify is literal string, alternatives like add/delete not supported yet |
|---|
| 469 | | - * <config> is the configuration to be changed. Supported configs are |
|---|
| 470 | | - * "config_pin" or "config_group", alternatives like config_mux are not |
|---|
| 471 | | - * supported yet. |
|---|
| 472 | | - * <devicename> <state> <name> are values that should match the pinctrl-maps |
|---|
| 473 | | - * <newvalue> reflects the new config and is driver dependent |
|---|
| 474 | | - */ |
|---|
| 475 | | -static ssize_t pinconf_dbg_config_write(struct file *file, |
|---|
| 476 | | - const char __user *user_buf, size_t count, loff_t *ppos) |
|---|
| 477 | | -{ |
|---|
| 478 | | - struct pinctrl_maps *maps_node; |
|---|
| 479 | | - const struct pinctrl_map *map; |
|---|
| 480 | | - const struct pinctrl_map *found = NULL; |
|---|
| 481 | | - struct pinctrl_dev *pctldev; |
|---|
| 482 | | - const struct pinconf_ops *confops = NULL; |
|---|
| 483 | | - struct dbg_cfg *dbg = &pinconf_dbg_conf; |
|---|
| 484 | | - const struct pinctrl_map_configs *configs; |
|---|
| 485 | | - char config[MAX_NAME_LEN + 1]; |
|---|
| 486 | | - char buf[128]; |
|---|
| 487 | | - char *b = &buf[0]; |
|---|
| 488 | | - int buf_size; |
|---|
| 489 | | - char *token; |
|---|
| 490 | | - int i; |
|---|
| 491 | | - |
|---|
| 492 | | - /* Get userspace string and assure termination */ |
|---|
| 493 | | - buf_size = min(count, sizeof(buf) - 1); |
|---|
| 494 | | - if (copy_from_user(buf, user_buf, buf_size)) |
|---|
| 495 | | - return -EFAULT; |
|---|
| 496 | | - buf[buf_size] = 0; |
|---|
| 497 | | - |
|---|
| 498 | | - /* |
|---|
| 499 | | - * need to parse entry and extract parameters: |
|---|
| 500 | | - * modify configs_pin devicename state pinname newvalue |
|---|
| 501 | | - */ |
|---|
| 502 | | - |
|---|
| 503 | | - /* Get arg: 'modify' */ |
|---|
| 504 | | - token = strsep(&b, " "); |
|---|
| 505 | | - if (!token) |
|---|
| 506 | | - return -EINVAL; |
|---|
| 507 | | - if (strcmp(token, "modify")) |
|---|
| 508 | | - return -EINVAL; |
|---|
| 509 | | - |
|---|
| 510 | | - /* |
|---|
| 511 | | - * Get arg type: "config_pin" and "config_group" |
|---|
| 512 | | - * types are supported so far |
|---|
| 513 | | - */ |
|---|
| 514 | | - token = strsep(&b, " "); |
|---|
| 515 | | - if (!token) |
|---|
| 516 | | - return -EINVAL; |
|---|
| 517 | | - if (!strcmp(token, "config_pin")) |
|---|
| 518 | | - dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN; |
|---|
| 519 | | - else if (!strcmp(token, "config_group")) |
|---|
| 520 | | - dbg->map_type = PIN_MAP_TYPE_CONFIGS_GROUP; |
|---|
| 521 | | - else |
|---|
| 522 | | - return -EINVAL; |
|---|
| 523 | | - |
|---|
| 524 | | - /* get arg 'device_name' */ |
|---|
| 525 | | - token = strsep(&b, " "); |
|---|
| 526 | | - if (!token) |
|---|
| 527 | | - return -EINVAL; |
|---|
| 528 | | - if (strlen(token) >= MAX_NAME_LEN) |
|---|
| 529 | | - return -EINVAL; |
|---|
| 530 | | - strncpy(dbg->dev_name, token, MAX_NAME_LEN); |
|---|
| 531 | | - |
|---|
| 532 | | - /* get arg 'state_name' */ |
|---|
| 533 | | - token = strsep(&b, " "); |
|---|
| 534 | | - if (!token) |
|---|
| 535 | | - return -EINVAL; |
|---|
| 536 | | - if (strlen(token) >= MAX_NAME_LEN) |
|---|
| 537 | | - return -EINVAL; |
|---|
| 538 | | - strncpy(dbg->state_name, token, MAX_NAME_LEN); |
|---|
| 539 | | - |
|---|
| 540 | | - /* get arg 'pin_name' */ |
|---|
| 541 | | - token = strsep(&b, " "); |
|---|
| 542 | | - if (!token) |
|---|
| 543 | | - return -EINVAL; |
|---|
| 544 | | - if (strlen(token) >= MAX_NAME_LEN) |
|---|
| 545 | | - return -EINVAL; |
|---|
| 546 | | - strncpy(dbg->pin_name, token, MAX_NAME_LEN); |
|---|
| 547 | | - |
|---|
| 548 | | - /* get new_value of config' */ |
|---|
| 549 | | - token = strsep(&b, " "); |
|---|
| 550 | | - if (!token) |
|---|
| 551 | | - return -EINVAL; |
|---|
| 552 | | - if (strlen(token) >= MAX_NAME_LEN) |
|---|
| 553 | | - return -EINVAL; |
|---|
| 554 | | - strncpy(config, token, MAX_NAME_LEN); |
|---|
| 555 | | - |
|---|
| 556 | | - mutex_lock(&pinctrl_maps_mutex); |
|---|
| 557 | | - |
|---|
| 558 | | - /* Parse the pinctrl map and look for the selected dev/state/pin */ |
|---|
| 559 | | - for_each_maps(maps_node, i, map) { |
|---|
| 560 | | - if (strcmp(map->dev_name, dbg->dev_name)) |
|---|
| 561 | | - continue; |
|---|
| 562 | | - if (map->type != dbg->map_type) |
|---|
| 563 | | - continue; |
|---|
| 564 | | - if (strcmp(map->name, dbg->state_name)) |
|---|
| 565 | | - continue; |
|---|
| 566 | | - |
|---|
| 567 | | - /* we found the right pin / state, so overwrite config */ |
|---|
| 568 | | - if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { |
|---|
| 569 | | - found = map; |
|---|
| 570 | | - break; |
|---|
| 571 | | - } |
|---|
| 572 | | - } |
|---|
| 573 | | - |
|---|
| 574 | | - if (!found) { |
|---|
| 575 | | - count = -EINVAL; |
|---|
| 576 | | - goto exit; |
|---|
| 577 | | - } |
|---|
| 578 | | - |
|---|
| 579 | | - pctldev = get_pinctrl_dev_from_devname(found->ctrl_dev_name); |
|---|
| 580 | | - if (pctldev) |
|---|
| 581 | | - confops = pctldev->desc->confops; |
|---|
| 582 | | - |
|---|
| 583 | | - if (confops && confops->pin_config_dbg_parse_modify) { |
|---|
| 584 | | - configs = &found->data.configs; |
|---|
| 585 | | - for (i = 0; i < configs->num_configs; i++) { |
|---|
| 586 | | - confops->pin_config_dbg_parse_modify(pctldev, |
|---|
| 587 | | - config, |
|---|
| 588 | | - &configs->configs[i]); |
|---|
| 589 | | - } |
|---|
| 590 | | - } |
|---|
| 591 | | - |
|---|
| 592 | | -exit: |
|---|
| 593 | | - mutex_unlock(&pinctrl_maps_mutex); |
|---|
| 594 | | - |
|---|
| 595 | | - return count; |
|---|
| 596 | | -} |
|---|
| 597 | | - |
|---|
| 598 | | -static int pinconf_dbg_config_open(struct inode *inode, struct file *file) |
|---|
| 599 | | -{ |
|---|
| 600 | | - return single_open(file, pinconf_dbg_config_print, inode->i_private); |
|---|
| 601 | | -} |
|---|
| 602 | | - |
|---|
| 603 | | -static const struct file_operations pinconf_dbg_pinconfig_fops = { |
|---|
| 604 | | - .open = pinconf_dbg_config_open, |
|---|
| 605 | | - .write = pinconf_dbg_config_write, |
|---|
| 606 | | - .read = seq_read, |
|---|
| 607 | | - .llseek = seq_lseek, |
|---|
| 608 | | - .release = single_release, |
|---|
| 609 | | - .owner = THIS_MODULE, |
|---|
| 610 | | -}; |
|---|
| 367 | +DEFINE_SHOW_ATTRIBUTE(pinconf_pins); |
|---|
| 368 | +DEFINE_SHOW_ATTRIBUTE(pinconf_groups); |
|---|
| 611 | 369 | |
|---|
| 612 | 370 | void pinconf_init_device_debugfs(struct dentry *devroot, |
|---|
| 613 | 371 | struct pinctrl_dev *pctldev) |
|---|
| 614 | 372 | { |
|---|
| 615 | 373 | debugfs_create_file("pinconf-pins", S_IFREG | S_IRUGO, |
|---|
| 616 | | - devroot, pctldev, &pinconf_pins_ops); |
|---|
| 374 | + devroot, pctldev, &pinconf_pins_fops); |
|---|
| 617 | 375 | debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, |
|---|
| 618 | | - devroot, pctldev, &pinconf_groups_ops); |
|---|
| 619 | | - debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP), |
|---|
| 620 | | - devroot, pctldev, &pinconf_dbg_pinconfig_fops); |
|---|
| 376 | + devroot, pctldev, &pinconf_groups_fops); |
|---|
| 621 | 377 | } |
|---|
| 622 | 378 | |
|---|
| 623 | 379 | #endif |
|---|