diff options
author | Logan Gunthorpe <logang@deltatee.com> | 2017-03-02 18:24:34 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-12 13:23:37 -0400 |
commit | 52eabba5bcdb2853dec6ef007ba427b092034738 (patch) | |
tree | 959b69df05f188f33ae2d7e5ba53158fd50f2f83 | |
parent | 5d8e1881f431cf470170813eb048e6a384340904 (diff) |
switchtec: Add IOCTLs to the Switchtec driver
Add a couple of special IOCTLs to:
* Inform userspace of firmware partition locations
* Pass event counts and allow userspace to wait on events
* Translate PFF numbers used by the switch to port numbers
[Dan Carpenter <dan.carpenter@oracle.com>: fix off-by-one in
ioctl_event_ctl()]
Tested-by: Krishna Dhulipala <krishnad@fb.com>
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Stephen Bates <stephen.bates@microsemi.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Wei Zhang <wzhang@fb.com>
Reviewed-by: Jens Axboe <axboe@fb.com>
-rw-r--r-- | Documentation/ioctl/ioctl-number.txt | 1 | ||||
-rw-r--r-- | Documentation/switchtec.txt | 27 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/pci/switch/switchtec.c | 481 | ||||
-rw-r--r-- | include/uapi/linux/switchtec_ioctl.h | 132 |
5 files changed, 642 insertions, 0 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 08244bea5048..0682bd3eaa8a 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
@@ -191,6 +191,7 @@ Code Seq#(hex) Include File Comments | |||
191 | 'W' 00-1F linux/watchdog.h conflict! | 191 | 'W' 00-1F linux/watchdog.h conflict! |
192 | 'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) | 192 | 'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) |
193 | 'W' 00-3F sound/asound.h conflict! | 193 | 'W' 00-3F sound/asound.h conflict! |
194 | 'W' 40-5F drivers/pci/switch/switchtec.c | ||
194 | 'X' all fs/xfs/xfs_fs.h conflict! | 195 | 'X' all fs/xfs/xfs_fs.h conflict! |
195 | and fs/xfs/linux-2.6/xfs_ioctl32.h | 196 | and fs/xfs/linux-2.6/xfs_ioctl32.h |
196 | and include/linux/falloc.h | 197 | and include/linux/falloc.h |
diff --git a/Documentation/switchtec.txt b/Documentation/switchtec.txt index 4bced4c78446..a0a9c7b3d4d5 100644 --- a/Documentation/switchtec.txt +++ b/Documentation/switchtec.txt | |||
@@ -51,3 +51,30 @@ The char device has the following semantics: | |||
51 | 51 | ||
52 | * The poll call will also be supported for userspace applications that | 52 | * The poll call will also be supported for userspace applications that |
53 | need to do other things while waiting for the command to complete. | 53 | need to do other things while waiting for the command to complete. |
54 | |||
55 | The following IOCTLs are also supported by the device: | ||
56 | |||
57 | * SWITCHTEC_IOCTL_FLASH_INFO - Retrieve firmware length and number | ||
58 | of partitions in the device. | ||
59 | |||
60 | * SWITCHTEC_IOCTL_FLASH_PART_INFO - Retrieve address and lengeth for | ||
61 | any specified partition in flash. | ||
62 | |||
63 | * SWITCHTEC_IOCTL_EVENT_SUMMARY - Read a structure of bitmaps | ||
64 | indicating all uncleared events. | ||
65 | |||
66 | * SWITCHTEC_IOCTL_EVENT_CTL - Get the current count, clear and set flags | ||
67 | for any event. This ioctl takes in a switchtec_ioctl_event_ctl struct | ||
68 | with the event_id, index and flags set (index being the partition or PFF | ||
69 | number for non-global events). It returns whether the event has | ||
70 | occurred, the number of times and any event specific data. The flags | ||
71 | can be used to clear the count or enable and disable actions to | ||
72 | happen when the event occurs. | ||
73 | By using the SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL flag, | ||
74 | you can set an event to trigger a poll command to return with | ||
75 | POLLPRI. In this way, userspace can wait for events to occur. | ||
76 | |||
77 | * SWITCHTEC_IOCTL_PFF_TO_PORT and SWITCHTEC_IOCTL_PORT_TO_PFF convert | ||
78 | between PCI Function Framework number (used by the event system) | ||
79 | and Switchtec Logic Port ID and Partition number (which is more | ||
80 | user friendly). | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 76ccc5a805dd..3744019a8853 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -9664,6 +9664,7 @@ S: Maintained | |||
9664 | F: Documentation/switchtec.txt | 9664 | F: Documentation/switchtec.txt |
9665 | F: Documentation/ABI/testing/sysfs-class-switchtec | 9665 | F: Documentation/ABI/testing/sysfs-class-switchtec |
9666 | F: drivers/pci/switch/switchtec* | 9666 | F: drivers/pci/switch/switchtec* |
9667 | F: include/uapi/linux/switchtec_ioctl.h | ||
9667 | 9668 | ||
9668 | PCI DRIVER FOR NVIDIA TEGRA | 9669 | PCI DRIVER FOR NVIDIA TEGRA |
9669 | M: Thierry Reding <thierry.reding@gmail.com> | 9670 | M: Thierry Reding <thierry.reding@gmail.com> |
diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 15ff61c7718b..cc6e085008fb 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c | |||
@@ -13,6 +13,8 @@ | |||
13 | * | 13 | * |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/switchtec_ioctl.h> | ||
17 | |||
16 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
17 | #include <linux/module.h> | 19 | #include <linux/module.h> |
18 | #include <linux/fs.h> | 20 | #include <linux/fs.h> |
@@ -778,6 +780,431 @@ static unsigned int switchtec_dev_poll(struct file *filp, poll_table *wait) | |||
778 | return ret; | 780 | return ret; |
779 | } | 781 | } |
780 | 782 | ||
783 | static int ioctl_flash_info(struct switchtec_dev *stdev, | ||
784 | struct switchtec_ioctl_flash_info __user *uinfo) | ||
785 | { | ||
786 | struct switchtec_ioctl_flash_info info = {0}; | ||
787 | struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; | ||
788 | |||
789 | info.flash_length = ioread32(&fi->flash_length); | ||
790 | info.num_partitions = SWITCHTEC_IOCTL_NUM_PARTITIONS; | ||
791 | |||
792 | if (copy_to_user(uinfo, &info, sizeof(info))) | ||
793 | return -EFAULT; | ||
794 | |||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, | ||
799 | struct partition_info __iomem *pi) | ||
800 | { | ||
801 | info->address = ioread32(&pi->address); | ||
802 | info->length = ioread32(&pi->length); | ||
803 | } | ||
804 | |||
805 | static int ioctl_flash_part_info(struct switchtec_dev *stdev, | ||
806 | struct switchtec_ioctl_flash_part_info __user *uinfo) | ||
807 | { | ||
808 | struct switchtec_ioctl_flash_part_info info = {0}; | ||
809 | struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; | ||
810 | u32 active_addr = -1; | ||
811 | |||
812 | if (copy_from_user(&info, uinfo, sizeof(info))) | ||
813 | return -EFAULT; | ||
814 | |||
815 | switch (info.flash_partition) { | ||
816 | case SWITCHTEC_IOCTL_PART_CFG0: | ||
817 | active_addr = ioread32(&fi->active_cfg); | ||
818 | set_fw_info_part(&info, &fi->cfg0); | ||
819 | break; | ||
820 | case SWITCHTEC_IOCTL_PART_CFG1: | ||
821 | active_addr = ioread32(&fi->active_cfg); | ||
822 | set_fw_info_part(&info, &fi->cfg1); | ||
823 | break; | ||
824 | case SWITCHTEC_IOCTL_PART_IMG0: | ||
825 | active_addr = ioread32(&fi->active_img); | ||
826 | set_fw_info_part(&info, &fi->img0); | ||
827 | break; | ||
828 | case SWITCHTEC_IOCTL_PART_IMG1: | ||
829 | active_addr = ioread32(&fi->active_img); | ||
830 | set_fw_info_part(&info, &fi->img1); | ||
831 | break; | ||
832 | case SWITCHTEC_IOCTL_PART_NVLOG: | ||
833 | set_fw_info_part(&info, &fi->nvlog); | ||
834 | break; | ||
835 | case SWITCHTEC_IOCTL_PART_VENDOR0: | ||
836 | set_fw_info_part(&info, &fi->vendor[0]); | ||
837 | break; | ||
838 | case SWITCHTEC_IOCTL_PART_VENDOR1: | ||
839 | set_fw_info_part(&info, &fi->vendor[1]); | ||
840 | break; | ||
841 | case SWITCHTEC_IOCTL_PART_VENDOR2: | ||
842 | set_fw_info_part(&info, &fi->vendor[2]); | ||
843 | break; | ||
844 | case SWITCHTEC_IOCTL_PART_VENDOR3: | ||
845 | set_fw_info_part(&info, &fi->vendor[3]); | ||
846 | break; | ||
847 | case SWITCHTEC_IOCTL_PART_VENDOR4: | ||
848 | set_fw_info_part(&info, &fi->vendor[4]); | ||
849 | break; | ||
850 | case SWITCHTEC_IOCTL_PART_VENDOR5: | ||
851 | set_fw_info_part(&info, &fi->vendor[5]); | ||
852 | break; | ||
853 | case SWITCHTEC_IOCTL_PART_VENDOR6: | ||
854 | set_fw_info_part(&info, &fi->vendor[6]); | ||
855 | break; | ||
856 | case SWITCHTEC_IOCTL_PART_VENDOR7: | ||
857 | set_fw_info_part(&info, &fi->vendor[7]); | ||
858 | break; | ||
859 | default: | ||
860 | return -EINVAL; | ||
861 | } | ||
862 | |||
863 | if (info.address == active_addr) | ||
864 | info.active = 1; | ||
865 | |||
866 | if (copy_to_user(uinfo, &info, sizeof(info))) | ||
867 | return -EFAULT; | ||
868 | |||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | static int ioctl_event_summary(struct switchtec_dev *stdev, | ||
873 | struct switchtec_user *stuser, | ||
874 | struct switchtec_ioctl_event_summary __user *usum) | ||
875 | { | ||
876 | struct switchtec_ioctl_event_summary s = {0}; | ||
877 | int i; | ||
878 | u32 reg; | ||
879 | |||
880 | s.global = ioread32(&stdev->mmio_sw_event->global_summary); | ||
881 | s.part_bitmap = ioread32(&stdev->mmio_sw_event->part_event_bitmap); | ||
882 | s.local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary); | ||
883 | |||
884 | for (i = 0; i < stdev->partition_count; i++) { | ||
885 | reg = ioread32(&stdev->mmio_part_cfg_all[i].part_event_summary); | ||
886 | s.part[i] = reg; | ||
887 | } | ||
888 | |||
889 | for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { | ||
890 | reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); | ||
891 | if (reg != MICROSEMI_VENDOR_ID) | ||
892 | break; | ||
893 | |||
894 | reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary); | ||
895 | s.pff[i] = reg; | ||
896 | } | ||
897 | |||
898 | if (copy_to_user(usum, &s, sizeof(s))) | ||
899 | return -EFAULT; | ||
900 | |||
901 | stuser->event_cnt = atomic_read(&stdev->event_cnt); | ||
902 | |||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | static u32 __iomem *global_ev_reg(struct switchtec_dev *stdev, | ||
907 | size_t offset, int index) | ||
908 | { | ||
909 | return (void __iomem *)stdev->mmio_sw_event + offset; | ||
910 | } | ||
911 | |||
912 | static u32 __iomem *part_ev_reg(struct switchtec_dev *stdev, | ||
913 | size_t offset, int index) | ||
914 | { | ||
915 | return (void __iomem *)&stdev->mmio_part_cfg_all[index] + offset; | ||
916 | } | ||
917 | |||
918 | static u32 __iomem *pff_ev_reg(struct switchtec_dev *stdev, | ||
919 | size_t offset, int index) | ||
920 | { | ||
921 | return (void __iomem *)&stdev->mmio_pff_csr[index] + offset; | ||
922 | } | ||
923 | |||
924 | #define EV_GLB(i, r)[i] = {offsetof(struct sw_event_regs, r), global_ev_reg} | ||
925 | #define EV_PAR(i, r)[i] = {offsetof(struct part_cfg_regs, r), part_ev_reg} | ||
926 | #define EV_PFF(i, r)[i] = {offsetof(struct pff_csr_regs, r), pff_ev_reg} | ||
927 | |||
928 | const struct event_reg { | ||
929 | size_t offset; | ||
930 | u32 __iomem *(*map_reg)(struct switchtec_dev *stdev, | ||
931 | size_t offset, int index); | ||
932 | } event_regs[] = { | ||
933 | EV_GLB(SWITCHTEC_IOCTL_EVENT_STACK_ERROR, stack_error_event_hdr), | ||
934 | EV_GLB(SWITCHTEC_IOCTL_EVENT_PPU_ERROR, ppu_error_event_hdr), | ||
935 | EV_GLB(SWITCHTEC_IOCTL_EVENT_ISP_ERROR, isp_error_event_hdr), | ||
936 | EV_GLB(SWITCHTEC_IOCTL_EVENT_SYS_RESET, sys_reset_event_hdr), | ||
937 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_EXC, fw_exception_hdr), | ||
938 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NMI, fw_nmi_hdr), | ||
939 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL, fw_non_fatal_hdr), | ||
940 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_FATAL, fw_fatal_hdr), | ||
941 | EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP, twi_mrpc_comp_hdr), | ||
942 | EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC, | ||
943 | twi_mrpc_comp_async_hdr), | ||
944 | EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP, cli_mrpc_comp_hdr), | ||
945 | EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC, | ||
946 | cli_mrpc_comp_async_hdr), | ||
947 | EV_GLB(SWITCHTEC_IOCTL_EVENT_GPIO_INT, gpio_interrupt_hdr), | ||
948 | EV_PAR(SWITCHTEC_IOCTL_EVENT_PART_RESET, part_reset_hdr), | ||
949 | EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr), | ||
950 | EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr), | ||
951 | EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr), | ||
952 | EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr), | ||
953 | EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr), | ||
954 | EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr), | ||
955 | EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr), | ||
956 | EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr), | ||
957 | EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr), | ||
958 | EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr), | ||
959 | EV_PFF(SWITCHTEC_IOCTL_EVENT_POWER_MGMT, power_mgmt_hdr), | ||
960 | EV_PFF(SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING, tlp_throttling_hdr), | ||
961 | EV_PFF(SWITCHTEC_IOCTL_EVENT_FORCE_SPEED, force_speed_hdr), | ||
962 | EV_PFF(SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT, credit_timeout_hdr), | ||
963 | EV_PFF(SWITCHTEC_IOCTL_EVENT_LINK_STATE, link_state_hdr), | ||
964 | }; | ||
965 | |||
966 | static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev, | ||
967 | int event_id, int index) | ||
968 | { | ||
969 | size_t off; | ||
970 | |||
971 | if (event_id < 0 || event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) | ||
972 | return ERR_PTR(-EINVAL); | ||
973 | |||
974 | off = event_regs[event_id].offset; | ||
975 | |||
976 | if (event_regs[event_id].map_reg == part_ev_reg) { | ||
977 | if (index == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) | ||
978 | index = stdev->partition; | ||
979 | else if (index < 0 || index >= stdev->partition_count) | ||
980 | return ERR_PTR(-EINVAL); | ||
981 | } else if (event_regs[event_id].map_reg == pff_ev_reg) { | ||
982 | if (index < 0 || index >= stdev->pff_csr_count) | ||
983 | return ERR_PTR(-EINVAL); | ||
984 | } | ||
985 | |||
986 | return event_regs[event_id].map_reg(stdev, off, index); | ||
987 | } | ||
988 | |||
989 | static int event_ctl(struct switchtec_dev *stdev, | ||
990 | struct switchtec_ioctl_event_ctl *ctl) | ||
991 | { | ||
992 | int i; | ||
993 | u32 __iomem *reg; | ||
994 | u32 hdr; | ||
995 | |||
996 | reg = event_hdr_addr(stdev, ctl->event_id, ctl->index); | ||
997 | if (IS_ERR(reg)) | ||
998 | return PTR_ERR(reg); | ||
999 | |||
1000 | hdr = ioread32(reg); | ||
1001 | for (i = 0; i < ARRAY_SIZE(ctl->data); i++) | ||
1002 | ctl->data[i] = ioread32(®[i + 1]); | ||
1003 | |||
1004 | ctl->occurred = hdr & SWITCHTEC_EVENT_OCCURRED; | ||
1005 | ctl->count = (hdr >> 5) & 0xFF; | ||
1006 | |||
1007 | if (!(ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR)) | ||
1008 | hdr &= ~SWITCHTEC_EVENT_CLEAR; | ||
1009 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL) | ||
1010 | hdr |= SWITCHTEC_EVENT_EN_IRQ; | ||
1011 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL) | ||
1012 | hdr &= ~SWITCHTEC_EVENT_EN_IRQ; | ||
1013 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG) | ||
1014 | hdr |= SWITCHTEC_EVENT_EN_LOG; | ||
1015 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG) | ||
1016 | hdr &= ~SWITCHTEC_EVENT_EN_LOG; | ||
1017 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI) | ||
1018 | hdr |= SWITCHTEC_EVENT_EN_CLI; | ||
1019 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI) | ||
1020 | hdr &= ~SWITCHTEC_EVENT_EN_CLI; | ||
1021 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL) | ||
1022 | hdr |= SWITCHTEC_EVENT_FATAL; | ||
1023 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL) | ||
1024 | hdr &= ~SWITCHTEC_EVENT_FATAL; | ||
1025 | |||
1026 | if (ctl->flags) | ||
1027 | iowrite32(hdr, reg); | ||
1028 | |||
1029 | ctl->flags = 0; | ||
1030 | if (hdr & SWITCHTEC_EVENT_EN_IRQ) | ||
1031 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL; | ||
1032 | if (hdr & SWITCHTEC_EVENT_EN_LOG) | ||
1033 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG; | ||
1034 | if (hdr & SWITCHTEC_EVENT_EN_CLI) | ||
1035 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI; | ||
1036 | if (hdr & SWITCHTEC_EVENT_FATAL) | ||
1037 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL; | ||
1038 | |||
1039 | return 0; | ||
1040 | } | ||
1041 | |||
1042 | static int ioctl_event_ctl(struct switchtec_dev *stdev, | ||
1043 | struct switchtec_ioctl_event_ctl __user *uctl) | ||
1044 | { | ||
1045 | int ret; | ||
1046 | int nr_idxs; | ||
1047 | struct switchtec_ioctl_event_ctl ctl; | ||
1048 | |||
1049 | if (copy_from_user(&ctl, uctl, sizeof(ctl))) | ||
1050 | return -EFAULT; | ||
1051 | |||
1052 | if (ctl.event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) | ||
1053 | return -EINVAL; | ||
1054 | |||
1055 | if (ctl.flags & SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED) | ||
1056 | return -EINVAL; | ||
1057 | |||
1058 | if (ctl.index == SWITCHTEC_IOCTL_EVENT_IDX_ALL) { | ||
1059 | if (event_regs[ctl.event_id].map_reg == global_ev_reg) | ||
1060 | nr_idxs = 1; | ||
1061 | else if (event_regs[ctl.event_id].map_reg == part_ev_reg) | ||
1062 | nr_idxs = stdev->partition_count; | ||
1063 | else if (event_regs[ctl.event_id].map_reg == pff_ev_reg) | ||
1064 | nr_idxs = stdev->pff_csr_count; | ||
1065 | else | ||
1066 | return -EINVAL; | ||
1067 | |||
1068 | for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { | ||
1069 | ret = event_ctl(stdev, &ctl); | ||
1070 | if (ret < 0) | ||
1071 | return ret; | ||
1072 | } | ||
1073 | } else { | ||
1074 | ret = event_ctl(stdev, &ctl); | ||
1075 | if (ret < 0) | ||
1076 | return ret; | ||
1077 | } | ||
1078 | |||
1079 | if (copy_to_user(uctl, &ctl, sizeof(ctl))) | ||
1080 | return -EFAULT; | ||
1081 | |||
1082 | return 0; | ||
1083 | } | ||
1084 | |||
1085 | static int ioctl_pff_to_port(struct switchtec_dev *stdev, | ||
1086 | struct switchtec_ioctl_pff_port *up) | ||
1087 | { | ||
1088 | int i, part; | ||
1089 | u32 reg; | ||
1090 | struct part_cfg_regs *pcfg; | ||
1091 | struct switchtec_ioctl_pff_port p; | ||
1092 | |||
1093 | if (copy_from_user(&p, up, sizeof(p))) | ||
1094 | return -EFAULT; | ||
1095 | |||
1096 | p.port = -1; | ||
1097 | for (part = 0; part < stdev->partition_count; part++) { | ||
1098 | pcfg = &stdev->mmio_part_cfg_all[part]; | ||
1099 | p.partition = part; | ||
1100 | |||
1101 | reg = ioread32(&pcfg->usp_pff_inst_id); | ||
1102 | if (reg == p.pff) { | ||
1103 | p.port = 0; | ||
1104 | break; | ||
1105 | } | ||
1106 | |||
1107 | reg = ioread32(&pcfg->vep_pff_inst_id); | ||
1108 | if (reg == p.pff) { | ||
1109 | p.port = SWITCHTEC_IOCTL_PFF_VEP; | ||
1110 | break; | ||
1111 | } | ||
1112 | |||
1113 | for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { | ||
1114 | reg = ioread32(&pcfg->dsp_pff_inst_id[i]); | ||
1115 | if (reg != p.pff) | ||
1116 | continue; | ||
1117 | |||
1118 | p.port = i + 1; | ||
1119 | break; | ||
1120 | } | ||
1121 | |||
1122 | if (p.port != -1) | ||
1123 | break; | ||
1124 | } | ||
1125 | |||
1126 | if (copy_to_user(up, &p, sizeof(p))) | ||
1127 | return -EFAULT; | ||
1128 | |||
1129 | return 0; | ||
1130 | } | ||
1131 | |||
1132 | static int ioctl_port_to_pff(struct switchtec_dev *stdev, | ||
1133 | struct switchtec_ioctl_pff_port *up) | ||
1134 | { | ||
1135 | struct switchtec_ioctl_pff_port p; | ||
1136 | struct part_cfg_regs *pcfg; | ||
1137 | |||
1138 | if (copy_from_user(&p, up, sizeof(p))) | ||
1139 | return -EFAULT; | ||
1140 | |||
1141 | if (p.partition == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) | ||
1142 | pcfg = stdev->mmio_part_cfg; | ||
1143 | else if (p.partition < stdev->partition_count) | ||
1144 | pcfg = &stdev->mmio_part_cfg_all[p.partition]; | ||
1145 | else | ||
1146 | return -EINVAL; | ||
1147 | |||
1148 | switch (p.port) { | ||
1149 | case 0: | ||
1150 | p.pff = ioread32(&pcfg->usp_pff_inst_id); | ||
1151 | break; | ||
1152 | case SWITCHTEC_IOCTL_PFF_VEP: | ||
1153 | p.pff = ioread32(&pcfg->vep_pff_inst_id); | ||
1154 | break; | ||
1155 | default: | ||
1156 | if (p.port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) | ||
1157 | return -EINVAL; | ||
1158 | p.pff = ioread32(&pcfg->dsp_pff_inst_id[p.port - 1]); | ||
1159 | break; | ||
1160 | } | ||
1161 | |||
1162 | if (copy_to_user(up, &p, sizeof(p))) | ||
1163 | return -EFAULT; | ||
1164 | |||
1165 | return 0; | ||
1166 | } | ||
1167 | |||
1168 | static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd, | ||
1169 | unsigned long arg) | ||
1170 | { | ||
1171 | struct switchtec_user *stuser = filp->private_data; | ||
1172 | struct switchtec_dev *stdev = stuser->stdev; | ||
1173 | int rc; | ||
1174 | void __user *argp = (void __user *)arg; | ||
1175 | |||
1176 | rc = lock_mutex_and_test_alive(stdev); | ||
1177 | if (rc) | ||
1178 | return rc; | ||
1179 | |||
1180 | switch (cmd) { | ||
1181 | case SWITCHTEC_IOCTL_FLASH_INFO: | ||
1182 | rc = ioctl_flash_info(stdev, argp); | ||
1183 | break; | ||
1184 | case SWITCHTEC_IOCTL_FLASH_PART_INFO: | ||
1185 | rc = ioctl_flash_part_info(stdev, argp); | ||
1186 | break; | ||
1187 | case SWITCHTEC_IOCTL_EVENT_SUMMARY: | ||
1188 | rc = ioctl_event_summary(stdev, stuser, argp); | ||
1189 | break; | ||
1190 | case SWITCHTEC_IOCTL_EVENT_CTL: | ||
1191 | rc = ioctl_event_ctl(stdev, argp); | ||
1192 | break; | ||
1193 | case SWITCHTEC_IOCTL_PFF_TO_PORT: | ||
1194 | rc = ioctl_pff_to_port(stdev, argp); | ||
1195 | break; | ||
1196 | case SWITCHTEC_IOCTL_PORT_TO_PFF: | ||
1197 | rc = ioctl_port_to_pff(stdev, argp); | ||
1198 | break; | ||
1199 | default: | ||
1200 | rc = -ENOTTY; | ||
1201 | break; | ||
1202 | } | ||
1203 | |||
1204 | mutex_unlock(&stdev->mrpc_mutex); | ||
1205 | return rc; | ||
1206 | } | ||
1207 | |||
781 | static const struct file_operations switchtec_fops = { | 1208 | static const struct file_operations switchtec_fops = { |
782 | .owner = THIS_MODULE, | 1209 | .owner = THIS_MODULE, |
783 | .open = switchtec_dev_open, | 1210 | .open = switchtec_dev_open, |
@@ -785,6 +1212,8 @@ static const struct file_operations switchtec_fops = { | |||
785 | .write = switchtec_dev_write, | 1212 | .write = switchtec_dev_write, |
786 | .read = switchtec_dev_read, | 1213 | .read = switchtec_dev_read, |
787 | .poll = switchtec_dev_poll, | 1214 | .poll = switchtec_dev_poll, |
1215 | .unlocked_ioctl = switchtec_dev_ioctl, | ||
1216 | .compat_ioctl = switchtec_dev_ioctl, | ||
788 | }; | 1217 | }; |
789 | 1218 | ||
790 | static void stdev_release(struct device *dev) | 1219 | static void stdev_release(struct device *dev) |
@@ -871,11 +1300,52 @@ err_put: | |||
871 | return ERR_PTR(rc); | 1300 | return ERR_PTR(rc); |
872 | } | 1301 | } |
873 | 1302 | ||
1303 | static int mask_event(struct switchtec_dev *stdev, int eid, int idx) | ||
1304 | { | ||
1305 | size_t off = event_regs[eid].offset; | ||
1306 | u32 __iomem *hdr_reg; | ||
1307 | u32 hdr; | ||
1308 | |||
1309 | hdr_reg = event_regs[eid].map_reg(stdev, off, idx); | ||
1310 | hdr = ioread32(hdr_reg); | ||
1311 | |||
1312 | if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) | ||
1313 | return 0; | ||
1314 | |||
1315 | dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); | ||
1316 | hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); | ||
1317 | iowrite32(hdr, hdr_reg); | ||
1318 | |||
1319 | return 1; | ||
1320 | } | ||
1321 | |||
1322 | static int mask_all_events(struct switchtec_dev *stdev, int eid) | ||
1323 | { | ||
1324 | int idx; | ||
1325 | int count = 0; | ||
1326 | |||
1327 | if (event_regs[eid].map_reg == part_ev_reg) { | ||
1328 | for (idx = 0; idx < stdev->partition_count; idx++) | ||
1329 | count += mask_event(stdev, eid, idx); | ||
1330 | } else if (event_regs[eid].map_reg == pff_ev_reg) { | ||
1331 | for (idx = 0; idx < stdev->pff_csr_count; idx++) { | ||
1332 | if (!stdev->pff_local[idx]) | ||
1333 | continue; | ||
1334 | count += mask_event(stdev, eid, idx); | ||
1335 | } | ||
1336 | } else { | ||
1337 | count += mask_event(stdev, eid, 0); | ||
1338 | } | ||
1339 | |||
1340 | return count; | ||
1341 | } | ||
1342 | |||
874 | static irqreturn_t switchtec_event_isr(int irq, void *dev) | 1343 | static irqreturn_t switchtec_event_isr(int irq, void *dev) |
875 | { | 1344 | { |
876 | struct switchtec_dev *stdev = dev; | 1345 | struct switchtec_dev *stdev = dev; |
877 | u32 reg; | 1346 | u32 reg; |
878 | irqreturn_t ret = IRQ_NONE; | 1347 | irqreturn_t ret = IRQ_NONE; |
1348 | int eid, event_count = 0; | ||
879 | 1349 | ||
880 | reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr); | 1350 | reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr); |
881 | if (reg & SWITCHTEC_EVENT_OCCURRED) { | 1351 | if (reg & SWITCHTEC_EVENT_OCCURRED) { |
@@ -885,6 +1355,17 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) | |||
885 | iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); | 1355 | iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); |
886 | } | 1356 | } |
887 | 1357 | ||
1358 | for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) | ||
1359 | event_count += mask_all_events(stdev, eid); | ||
1360 | |||
1361 | if (event_count) { | ||
1362 | atomic_inc(&stdev->event_cnt); | ||
1363 | wake_up_interruptible(&stdev->event_wq); | ||
1364 | dev_dbg(&stdev->dev, "%s: %d events\n", __func__, | ||
1365 | event_count); | ||
1366 | return IRQ_HANDLED; | ||
1367 | } | ||
1368 | |||
888 | return ret; | 1369 | return ret; |
889 | } | 1370 | } |
890 | 1371 | ||
diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h new file mode 100644 index 000000000000..3e824e1a6495 --- /dev/null +++ b/include/uapi/linux/switchtec_ioctl.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * Microsemi Switchtec PCIe Driver | ||
3 | * Copyright (c) 2017, Microsemi Corporation | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef _UAPI_LINUX_SWITCHTEC_IOCTL_H | ||
17 | #define _UAPI_LINUX_SWITCHTEC_IOCTL_H | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | |||
21 | #define SWITCHTEC_IOCTL_PART_CFG0 0 | ||
22 | #define SWITCHTEC_IOCTL_PART_CFG1 1 | ||
23 | #define SWITCHTEC_IOCTL_PART_IMG0 2 | ||
24 | #define SWITCHTEC_IOCTL_PART_IMG1 3 | ||
25 | #define SWITCHTEC_IOCTL_PART_NVLOG 4 | ||
26 | #define SWITCHTEC_IOCTL_PART_VENDOR0 5 | ||
27 | #define SWITCHTEC_IOCTL_PART_VENDOR1 6 | ||
28 | #define SWITCHTEC_IOCTL_PART_VENDOR2 7 | ||
29 | #define SWITCHTEC_IOCTL_PART_VENDOR3 8 | ||
30 | #define SWITCHTEC_IOCTL_PART_VENDOR4 9 | ||
31 | #define SWITCHTEC_IOCTL_PART_VENDOR5 10 | ||
32 | #define SWITCHTEC_IOCTL_PART_VENDOR6 11 | ||
33 | #define SWITCHTEC_IOCTL_PART_VENDOR7 12 | ||
34 | #define SWITCHTEC_IOCTL_NUM_PARTITIONS 13 | ||
35 | |||
36 | struct switchtec_ioctl_flash_info { | ||
37 | __u64 flash_length; | ||
38 | __u32 num_partitions; | ||
39 | __u32 padding; | ||
40 | }; | ||
41 | |||
42 | struct switchtec_ioctl_flash_part_info { | ||
43 | __u32 flash_partition; | ||
44 | __u32 address; | ||
45 | __u32 length; | ||
46 | __u32 active; | ||
47 | }; | ||
48 | |||
49 | struct switchtec_ioctl_event_summary { | ||
50 | __u64 global; | ||
51 | __u64 part_bitmap; | ||
52 | __u32 local_part; | ||
53 | __u32 padding; | ||
54 | __u32 part[48]; | ||
55 | __u32 pff[48]; | ||
56 | }; | ||
57 | |||
58 | #define SWITCHTEC_IOCTL_EVENT_STACK_ERROR 0 | ||
59 | #define SWITCHTEC_IOCTL_EVENT_PPU_ERROR 1 | ||
60 | #define SWITCHTEC_IOCTL_EVENT_ISP_ERROR 2 | ||
61 | #define SWITCHTEC_IOCTL_EVENT_SYS_RESET 3 | ||
62 | #define SWITCHTEC_IOCTL_EVENT_FW_EXC 4 | ||
63 | #define SWITCHTEC_IOCTL_EVENT_FW_NMI 5 | ||
64 | #define SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL 6 | ||
65 | #define SWITCHTEC_IOCTL_EVENT_FW_FATAL 7 | ||
66 | #define SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP 8 | ||
67 | #define SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC 9 | ||
68 | #define SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP 10 | ||
69 | #define SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC 11 | ||
70 | #define SWITCHTEC_IOCTL_EVENT_GPIO_INT 12 | ||
71 | #define SWITCHTEC_IOCTL_EVENT_PART_RESET 13 | ||
72 | #define SWITCHTEC_IOCTL_EVENT_MRPC_COMP 14 | ||
73 | #define SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC 15 | ||
74 | #define SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP 16 | ||
75 | #define SWITCHTEC_IOCTL_EVENT_AER_IN_P2P 17 | ||
76 | #define SWITCHTEC_IOCTL_EVENT_AER_IN_VEP 18 | ||
77 | #define SWITCHTEC_IOCTL_EVENT_DPC 19 | ||
78 | #define SWITCHTEC_IOCTL_EVENT_CTS 20 | ||
79 | #define SWITCHTEC_IOCTL_EVENT_HOTPLUG 21 | ||
80 | #define SWITCHTEC_IOCTL_EVENT_IER 22 | ||
81 | #define SWITCHTEC_IOCTL_EVENT_THRESH 23 | ||
82 | #define SWITCHTEC_IOCTL_EVENT_POWER_MGMT 24 | ||
83 | #define SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING 25 | ||
84 | #define SWITCHTEC_IOCTL_EVENT_FORCE_SPEED 26 | ||
85 | #define SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT 27 | ||
86 | #define SWITCHTEC_IOCTL_EVENT_LINK_STATE 28 | ||
87 | #define SWITCHTEC_IOCTL_MAX_EVENTS 29 | ||
88 | |||
89 | #define SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX -1 | ||
90 | #define SWITCHTEC_IOCTL_EVENT_IDX_ALL -2 | ||
91 | |||
92 | #define SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR (1 << 0) | ||
93 | #define SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL (1 << 1) | ||
94 | #define SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG (1 << 2) | ||
95 | #define SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI (1 << 3) | ||
96 | #define SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL (1 << 4) | ||
97 | #define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL (1 << 5) | ||
98 | #define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG (1 << 6) | ||
99 | #define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI (1 << 7) | ||
100 | #define SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL (1 << 8) | ||
101 | #define SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED (~0x1ff) | ||
102 | |||
103 | struct switchtec_ioctl_event_ctl { | ||
104 | __u32 event_id; | ||
105 | __s32 index; | ||
106 | __u32 flags; | ||
107 | __u32 occurred; | ||
108 | __u32 count; | ||
109 | __u32 data[5]; | ||
110 | }; | ||
111 | |||
112 | #define SWITCHTEC_IOCTL_PFF_VEP 100 | ||
113 | struct switchtec_ioctl_pff_port { | ||
114 | __u32 pff; | ||
115 | __u32 partition; | ||
116 | __u32 port; | ||
117 | }; | ||
118 | |||
119 | #define SWITCHTEC_IOCTL_FLASH_INFO \ | ||
120 | _IOR('W', 0x40, struct switchtec_ioctl_flash_info) | ||
121 | #define SWITCHTEC_IOCTL_FLASH_PART_INFO \ | ||
122 | _IOWR('W', 0x41, struct switchtec_ioctl_flash_part_info) | ||
123 | #define SWITCHTEC_IOCTL_EVENT_SUMMARY \ | ||
124 | _IOR('W', 0x42, struct switchtec_ioctl_event_summary) | ||
125 | #define SWITCHTEC_IOCTL_EVENT_CTL \ | ||
126 | _IOWR('W', 0x43, struct switchtec_ioctl_event_ctl) | ||
127 | #define SWITCHTEC_IOCTL_PFF_TO_PORT \ | ||
128 | _IOWR('W', 0x44, struct switchtec_ioctl_pff_port) | ||
129 | #define SWITCHTEC_IOCTL_PORT_TO_PFF \ | ||
130 | _IOWR('W', 0x45, struct switchtec_ioctl_pff_port) | ||
131 | |||
132 | #endif | ||