Commit c8cce6279f5c126cbf6b6054f1ea0148bfc19511
Committed by
Satyanarayana Sandhya
1 parent
158e06653b
Exists in
v3.2_SMARCT335xPSP_04.06.00.11
and in
3 other branches
ARM: OMAP2+: AM33xx: PM: Add support to wakeup via GPIO method for standby
Wakeup from standby mode is supported via GPIO method where peripherals can be configured as gpios while entering standby and wakeup happens through gpio interrupt. This patch provides an method to handle the same through a debugfs approach. User should know the IO pads to be configured and the trigger value to be written to them. The PAD offset & gpio configuration depends mainly on the wake-up source selected. Inside <debugfs-mount-dir>/omap_mux/board/ (Directory where these features are available) standby_gpio_pad_conf standby_gpio_pad_conf Expected input: pinmux_name=<value1>,<trigger> Pin-mux name that is to be setup as gpio during standby suspend with gpio interrupt trigger mode as per <trigger> field with value <value1>. Pin-mux name should be in "mode0_name.mode7_function_name" format. Internally the pin-mux offset is calculated from the pin-mux names. Invalid pin-mux names and values are ignored. Remember, - No spaces anywhere in the input. - <value1> field is a must - <trigger> field is a must and must be one of "rising", "falling" Example: echo uart0_rxd.gpio1_10=0x27,rising > standby_gpio_pad_conf sets up uart0_rxd.gpio1_10 for gpio mode with interrupt trigger as rising and pin-mux value as 0x27 when entering standby mode. During standby, If "standby_gpio_pad_conf" is configured, then the respective pin-mux value is saved, the gpio pin-mux mode is selected for the pin. Relevant gpio settings & interrupts are configured. During resume, the original values saved are restored back. User should make sure that the mux mode exists for the selected pin-mux and the trigger is proper. When here a duplicate header include (linux/io.h> is removed Signed-off-by: Hebbar Gururaja <gururaja.hebbar@ti.com>
Showing 3 changed files with 315 additions and 1 deletions Side-by-side Diff
arch/arm/mach-omap2/mux33xx.c
... | ... | @@ -20,8 +20,11 @@ |
20 | 20 | #include <linux/slab.h> |
21 | 21 | #include <linux/seq_file.h> |
22 | 22 | #include <linux/uaccess.h> |
23 | -#include <linux/io.h> | |
23 | +#include <linux/gpio.h> | |
24 | +#include <linux/interrupt.h> | |
25 | +#include <linux/suspend.h> | |
24 | 26 | |
27 | +#include "cm33xx.h" | |
25 | 28 | #include "control.h" |
26 | 29 | #include "mux.h" |
27 | 30 | |
... | ... | @@ -473,6 +476,17 @@ |
473 | 476 | static u32 susp_io_pad_conf_enabled; |
474 | 477 | static struct susp_io_pad_conf pad_array[MAX_IO_PADCONF]; |
475 | 478 | |
479 | +struct standby_gpio_pad_struct { | |
480 | + u32 enabled; | |
481 | + u32 gpio_request_success; | |
482 | + u32 pin_val; | |
483 | + u32 trigger; | |
484 | + u32 gpio_pin; | |
485 | + u32 curr_pin_mux; | |
486 | +}; | |
487 | + | |
488 | +static struct standby_gpio_pad_struct standby_gpio_array[MAX_IO_PADCONF]; | |
489 | + | |
476 | 490 | /* |
477 | 491 | * Expected input: 1/0 |
478 | 492 | * Example: "echo 1 > enable_suspend_io_pad_conf" enables IO PAD Config |
... | ... | @@ -653,6 +667,210 @@ |
653 | 667 | .release = single_release, |
654 | 668 | }; |
655 | 669 | |
670 | +static int standby_gpio_status_show(struct seq_file *s, void *unused) | |
671 | +{ | |
672 | + struct omap_mux *mux_arr = &am33xx_muxmodes[0]; | |
673 | + | |
674 | + int i; | |
675 | + | |
676 | + for (i = 0; i < MAX_IO_PADCONF;) { | |
677 | + int off, j, addr_match = 0; | |
678 | + char *trigger; | |
679 | + | |
680 | + if (standby_gpio_array[i].enabled) { | |
681 | + switch (standby_gpio_array[i].trigger) { | |
682 | + case IRQF_TRIGGER_RISING: | |
683 | + trigger = "rising"; | |
684 | + break; | |
685 | + | |
686 | + case IRQF_TRIGGER_FALLING: | |
687 | + trigger = "falling"; | |
688 | + break; | |
689 | + | |
690 | + case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING: | |
691 | + /* fall through */ | |
692 | + default: | |
693 | + trigger = "falling_rising"; | |
694 | + break; | |
695 | + } | |
696 | + | |
697 | + seq_printf(s, "%s.%s (0x%08x = 0x%02x), trigger = %s\n", | |
698 | + mux_arr->muxnames[0], | |
699 | + mux_arr->muxnames[OMAP_MUX_MODE7], | |
700 | + (unsigned int)(AM33XX_CONTROL_PADCONF_MUX_PBASE | |
701 | + + mux_arr->reg_offset), | |
702 | + standby_gpio_array[i].pin_val, | |
703 | + trigger); | |
704 | + | |
705 | + | |
706 | + } | |
707 | + | |
708 | + /* | |
709 | + * AM335x pin-mux register offset sequence is broken, meaning | |
710 | + * there is no pin-mux setting at some offset and at some | |
711 | + * offsets, the modes are not supposed to be changed. Because | |
712 | + * of this, the "am33xx_muxmodes" array above will not have any | |
713 | + * values at these indexes. Hence the standby_gpio_array & | |
714 | + * am33xx_muxmodes array will be out of sync at these index. | |
715 | + * Handle missing pin-mux entries accordingly by using a special | |
716 | + * array that indicate these offsets. | |
717 | + */ | |
718 | + | |
719 | + i++; | |
720 | + off = ((i * 4) + 0x800); | |
721 | + for (j = 0; j < ARRAY_SIZE(am335x_pin_mux_addr_to_skip); j++) { | |
722 | + if (off == am335x_pin_mux_addr_to_skip[j]) { | |
723 | + addr_match = 1; | |
724 | + break; | |
725 | + } | |
726 | + } | |
727 | + if (addr_match == 0) | |
728 | + mux_arr++; | |
729 | + } | |
730 | + | |
731 | + return 0; | |
732 | +} | |
733 | + | |
734 | +/* | |
735 | + * Expected input: pinmux_name=<value1>,<trigger> | |
736 | + * pinmux_name = Pin-mux name that is to be setup as gpio during standby | |
737 | + * suspend with gpio interrupt trigger mode as per <trigger> field | |
738 | + * with value <value1>. | |
739 | + * Pin-mux name should be in "mode0_name.mode7_function_name" | |
740 | + * format. Internally the pin-mux offset is calculated from the | |
741 | + * pin-mux names. Invalid pin-mux names and values are ignored. | |
742 | + * Remember, | |
743 | + * - No spaces anywhere in the input. | |
744 | + * - <value1> field is a must | |
745 | + * - <trigger> field is a must and must be one of "rising", | |
746 | + * "falling" | |
747 | + * | |
748 | + * Example: | |
749 | + * echo uart0_rxd.gpio1_10=0x27,rising > standby_gpio_pad_conf | |
750 | + * sets up uart0_rxd.gpio1_10 for gpio mode with interrupt trigger | |
751 | + * as rising and pin-mux value as 0x27 when entering standby mode. | |
752 | + */ | |
753 | +static ssize_t standby_gpio_pad_write(struct file *file, | |
754 | + const char __user *user_buf, | |
755 | + size_t count, loff_t *ppos) | |
756 | +{ | |
757 | + u32 trigger; | |
758 | + char *export_string, *token, *name; | |
759 | + | |
760 | + export_string = kzalloc(count + 1, GFP_KERNEL); | |
761 | + if (!export_string) | |
762 | + return -ENOMEM; | |
763 | + | |
764 | + if (copy_from_user(export_string, user_buf, count)) { | |
765 | + kfree(export_string); | |
766 | + return -EFAULT; | |
767 | + } | |
768 | + | |
769 | + export_string[count-1] = '\0'; /* force null terminator */ | |
770 | + token = export_string; | |
771 | + name = strsep(&token, "="); | |
772 | + if (name) { | |
773 | + struct omap_mux_partition *partition = NULL; | |
774 | + struct omap_mux *mux = NULL; | |
775 | + int mux_index, mux_mode, gpio_bank, gpio_pin, res, pin_val; | |
776 | + char *gpio_name; | |
777 | + | |
778 | + mux_mode = omap_mux_get_by_name(name, &partition, &mux); | |
779 | + if (mux_mode < 0) { | |
780 | + pr_err("%s: Invalid mux name (%s). Ignoring the" | |
781 | + " value\n", __func__, name); | |
782 | + goto err_out; | |
783 | + } | |
784 | + | |
785 | + name = strsep(&token, ","); | |
786 | + if (!name) { | |
787 | + pr_err("%s: Invalid value (%s). Ignoring\n", | |
788 | + __func__, token); | |
789 | + goto err_out; | |
790 | + } | |
791 | + | |
792 | + res = kstrtouint(name, 0, &pin_val); | |
793 | + if (res < 0) { | |
794 | + pr_err("%s: Invalid pin mux value (%s). Ignoring\n", | |
795 | + __func__, token); | |
796 | + goto err_out; | |
797 | + } | |
798 | + | |
799 | + if (token && !strncmp("rising", token, 6)) { | |
800 | + trigger = IRQF_TRIGGER_RISING; | |
801 | + } else if (token && !strncmp("falling", token, 7)) { | |
802 | + trigger = IRQF_TRIGGER_FALLING; | |
803 | + } else { | |
804 | + pr_err("%s: Invalid trigger (%s). Defaulting to" | |
805 | + " falling_rising\n", __func__, token); | |
806 | + trigger = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; | |
807 | + } | |
808 | + | |
809 | + /* confirm whether a gpio pin exists here */ | |
810 | + gpio_name = mux->muxnames[OMAP_MUX_MODE7]; | |
811 | + | |
812 | + if (!gpio_name) { | |
813 | + pr_err("%s: Invalid mux name (%s)\n", __func__, name); | |
814 | + goto err_out; | |
815 | + } else if (strncmp(gpio_name, "gpio", 4)) { | |
816 | + pr_err("%s: Invalid mux name found (%s)\n", | |
817 | + __func__, gpio_name); | |
818 | + goto err_out; | |
819 | + } | |
820 | + | |
821 | + /* | |
822 | + * parse the string name and get the gpio bank & pin number. | |
823 | + * gpio_name will be in the format of "gpioX_Y" where | |
824 | + * X = bank | |
825 | + * Y = pin number | |
826 | + */ | |
827 | + gpio_bank = *(gpio_name + 4) - '0'; | |
828 | + | |
829 | + gpio_name += 6; | |
830 | + res = kstrtoint(gpio_name, 10, &gpio_pin); | |
831 | + if (res < 0) { | |
832 | + pr_err("%s: Invalid gpio pin number (%s). Ignoring\n", | |
833 | + __func__, gpio_name); | |
834 | + goto err_out; | |
835 | + } | |
836 | + | |
837 | + mux_index = (mux->reg_offset - | |
838 | + AM33XX_CONTROL_PADCONF_GPMC_AD0_OFFSET) / 4; | |
839 | + | |
840 | + if (mux_index > MAX_IO_PADCONF) { | |
841 | + pr_err("%s: Invalid index (0x%x). Ignoring\n", | |
842 | + __func__, mux_index); | |
843 | + goto err_out; | |
844 | + } | |
845 | + | |
846 | + standby_gpio_array[mux_index].enabled = true; | |
847 | + standby_gpio_array[mux_index].pin_val = pin_val; | |
848 | + standby_gpio_array[mux_index].trigger = trigger; | |
849 | + standby_gpio_array[mux_index].gpio_pin = | |
850 | + ((gpio_bank * 32) + gpio_pin); | |
851 | + } else { | |
852 | + pr_err("%s: Invalid mux name (%s). Ignoring the entry\n", | |
853 | + __func__, export_string); | |
854 | + } | |
855 | + | |
856 | +err_out: | |
857 | + *ppos += count; | |
858 | + kfree(export_string); | |
859 | + return count; | |
860 | +} | |
861 | + | |
862 | +static int standby_gpio_pad_open(struct inode *inode, struct file *file) | |
863 | +{ | |
864 | + return single_open(file, standby_gpio_status_show, inode->i_private); | |
865 | +} | |
866 | + | |
867 | +static const struct file_operations standby_gpio_pad_conf_fops = { | |
868 | + .open = standby_gpio_pad_open, | |
869 | + .read = seq_read, | |
870 | + .write = standby_gpio_pad_write, | |
871 | + .release = single_release, | |
872 | +}; | |
873 | + | |
656 | 874 | void am33xx_mux_dbg_create_entry(struct dentry *mux_dbg_dir) |
657 | 875 | { |
658 | 876 | struct dentry *mux_dbg_suspend_io_conf_dir; |
... | ... | @@ -680,6 +898,10 @@ |
680 | 898 | mux_dbg_suspend_io_conf_dir, |
681 | 899 | &susp_io_pad_conf_enabled, |
682 | 900 | &susp_io_pad_fops); |
901 | + (void)debugfs_create_file("standby_gpio_pad_conf", S_IRUGO | S_IWUSR, | |
902 | + mux_dbg_dir, | |
903 | + &susp_io_pad_conf_enabled, | |
904 | + &standby_gpio_pad_conf_fops); | |
683 | 905 | } |
684 | 906 | |
685 | 907 | void am33xx_setup_pinmux_on_suspend(void) |
... | ... | @@ -730,6 +952,92 @@ |
730 | 952 | } else { |
731 | 953 | for (i = 0; i < ARRAY_SIZE(am33xx_lp_padconf); i++, temp++) |
732 | 954 | writel(temp->val, AM33XX_CTRL_REGADDR(temp->offset)); |
955 | + } | |
956 | +} | |
957 | + | |
958 | +/* | |
959 | + * Dummy GPIO interrupt Handler | |
960 | + */ | |
961 | +static irqreturn_t gpio_irq(int irq, void *dev_id) | |
962 | +{ | |
963 | + return IRQ_HANDLED; | |
964 | +} | |
965 | + | |
966 | +void am33xx_standby_setup(unsigned int state) | |
967 | +{ | |
968 | + u32 reg_off, i; | |
969 | + | |
970 | + if (state != PM_SUSPEND_STANDBY) | |
971 | + return; | |
972 | + | |
973 | + writel(0x2, AM33XX_CM_PER_GPIO1_CLKCTRL); | |
974 | + writel(0x2, AM33XX_CM_PER_GPIO2_CLKCTRL); | |
975 | + writel(0x2, AM33XX_CM_PER_GPIO3_CLKCTRL); | |
976 | + | |
977 | + reg_off = AM33XX_CONTROL_PADCONF_GPMC_AD0_OFFSET; | |
978 | + for (i = 0; i < MAX_IO_PADCONF; reg_off += 4, i++) { | |
979 | + if (standby_gpio_array[i].enabled) { | |
980 | + int ret, reg_val, irq; | |
981 | + u32 gpio_pin = standby_gpio_array[i].gpio_pin; | |
982 | + | |
983 | + reg_val = readl(AM33XX_CTRL_REGADDR(reg_off)); | |
984 | + standby_gpio_array[i].curr_pin_mux = reg_val; | |
985 | + reg_val = standby_gpio_array[i].pin_val; | |
986 | + writel(reg_val, AM33XX_CTRL_REGADDR(reg_off)); | |
987 | + | |
988 | + ret = gpio_request(gpio_pin, "pm_standby"); | |
989 | + if (ret) { | |
990 | + pr_err("%s: Error in gpio request (%d)\n", | |
991 | + __func__, ret); | |
992 | + continue; | |
993 | + } | |
994 | + irq = gpio_to_irq(gpio_pin); | |
995 | + if (irq < 0) { | |
996 | + gpio_free(gpio_pin); | |
997 | + pr_err("%s: gpio_to_irq failed (%d)\n", | |
998 | + __func__, irq); | |
999 | + continue; | |
1000 | + } | |
1001 | + ret = request_irq(irq, gpio_irq, | |
1002 | + standby_gpio_array[i].trigger, | |
1003 | + "pm_standby", NULL); | |
1004 | + if (ret) { | |
1005 | + gpio_free(gpio_pin); | |
1006 | + pr_err("%s: interrupt request failed (%d)\n", | |
1007 | + __func__, ret); | |
1008 | + continue; | |
1009 | + } | |
1010 | + | |
1011 | + standby_gpio_array[i].gpio_request_success = true; | |
1012 | + } | |
1013 | + } | |
1014 | + | |
1015 | +} | |
1016 | + | |
1017 | +void am33xx_standby_release(unsigned int state) | |
1018 | +{ | |
1019 | + u32 reg_off, i; | |
1020 | + | |
1021 | + if (state != PM_SUSPEND_STANDBY) | |
1022 | + return; | |
1023 | + | |
1024 | + reg_off = AM33XX_CONTROL_PADCONF_GPMC_AD0_OFFSET; | |
1025 | + for (i = 0; i < MAX_IO_PADCONF; reg_off += 4, i++) { | |
1026 | + u32 gpio_pin = standby_gpio_array[i].gpio_pin; | |
1027 | + | |
1028 | + if (standby_gpio_array[i].enabled) { | |
1029 | + writel(standby_gpio_array[i].curr_pin_mux, | |
1030 | + AM33XX_CTRL_REGADDR(reg_off)); | |
1031 | + | |
1032 | + if (standby_gpio_array[i].gpio_request_success == | |
1033 | + true) { | |
1034 | + int irq; | |
1035 | + | |
1036 | + irq = gpio_to_irq(gpio_pin); | |
1037 | + gpio_free(gpio_pin); | |
1038 | + free_irq(irq, 0); | |
1039 | + } | |
1040 | + } | |
733 | 1041 | } |
734 | 1042 | } |
735 | 1043 |
arch/arm/mach-omap2/mux33xx.h
... | ... | @@ -248,6 +248,10 @@ |
248 | 248 | struct dentry; |
249 | 249 | void am33xx_mux_dbg_create_entry(struct dentry *mux_dbg_dir); |
250 | 250 | void am33xx_setup_pinmux_on_suspend(void); |
251 | + | |
252 | +void am33xx_standby_setup(unsigned int state); | |
253 | +void am33xx_standby_release(unsigned int state); | |
254 | + | |
251 | 255 | #endif |
252 | 256 | |
253 | 257 | #endif |
arch/arm/mach-omap2/pm33xx.c
... | ... | @@ -80,12 +80,14 @@ |
80 | 80 | |
81 | 81 | am335x_save_padconf(); |
82 | 82 | am33xx_setup_pinmux_on_suspend(); |
83 | + am33xx_standby_setup(suspend_state); | |
83 | 84 | |
84 | 85 | return ret; |
85 | 86 | } |
86 | 87 | |
87 | 88 | static void am33xx_pm_finish(void) |
88 | 89 | { |
90 | + am33xx_standby_release(suspend_state); | |
89 | 91 | am335x_restore_padconf(); |
90 | 92 | } |
91 | 93 |