diff options
Diffstat (limited to 'drivers/misc/cxl/pci.c')
-rw-r--r-- | drivers/misc/cxl/pci.c | 236 |
1 files changed, 18 insertions, 218 deletions
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 429d6de1dde7..9c5a21fee835 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c | |||
@@ -55,8 +55,6 @@ | |||
55 | pci_read_config_byte(dev, vsec + 0xa, dest) | 55 | pci_read_config_byte(dev, vsec + 0xa, dest) |
56 | #define CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val) \ | 56 | #define CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val) \ |
57 | pci_write_config_byte(dev, vsec + 0xa, val) | 57 | pci_write_config_byte(dev, vsec + 0xa, val) |
58 | #define CXL_WRITE_VSEC_MODE_CONTROL_BUS(bus, devfn, vsec, val) \ | ||
59 | pci_bus_write_config_byte(bus, devfn, vsec + 0xa, val) | ||
60 | #define CXL_VSEC_PROTOCOL_MASK 0xe0 | 58 | #define CXL_VSEC_PROTOCOL_MASK 0xe0 |
61 | #define CXL_VSEC_PROTOCOL_1024TB 0x80 | 59 | #define CXL_VSEC_PROTOCOL_1024TB 0x80 |
62 | #define CXL_VSEC_PROTOCOL_512TB 0x40 | 60 | #define CXL_VSEC_PROTOCOL_512TB 0x40 |
@@ -800,234 +798,36 @@ static int setup_cxl_bars(struct pci_dev *dev) | |||
800 | return 0; | 798 | return 0; |
801 | } | 799 | } |
802 | 800 | ||
803 | #ifdef CONFIG_CXL_BIMODAL | 801 | /* pciex node: ibm,opal-m64-window = <0x3d058 0x0 0x3d058 0x0 0x8 0x0>; */ |
804 | 802 | static int switch_card_to_cxl(struct pci_dev *dev) | |
805 | struct cxl_switch_work { | ||
806 | struct pci_dev *dev; | ||
807 | struct work_struct work; | ||
808 | int vsec; | ||
809 | int mode; | ||
810 | }; | ||
811 | |||
812 | static void switch_card_to_cxl(struct work_struct *work) | ||
813 | { | 803 | { |
814 | struct cxl_switch_work *switch_work = | 804 | int vsec; |
815 | container_of(work, struct cxl_switch_work, work); | ||
816 | struct pci_dev *dev = switch_work->dev; | ||
817 | struct pci_bus *bus = dev->bus; | ||
818 | struct pci_controller *hose = pci_bus_to_host(bus); | ||
819 | struct pci_dev *bridge; | ||
820 | struct pnv_php_slot *php_slot; | ||
821 | unsigned int devfn; | ||
822 | u8 val; | 805 | u8 val; |
823 | int rc; | 806 | int rc; |
824 | 807 | ||
825 | dev_info(&bus->dev, "cxl: Preparing for mode switch...\n"); | 808 | dev_info(&dev->dev, "switch card to CXL\n"); |
826 | bridge = list_first_entry_or_null(&hose->bus->devices, struct pci_dev, | ||
827 | bus_list); | ||
828 | if (!bridge) { | ||
829 | dev_WARN(&bus->dev, "cxl: Couldn't find root port!\n"); | ||
830 | goto err_dev_put; | ||
831 | } | ||
832 | 809 | ||
833 | php_slot = pnv_php_find_slot(pci_device_to_OF_node(bridge)); | 810 | if (!(vsec = find_cxl_vsec(dev))) { |
834 | if (!php_slot) { | 811 | dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n"); |
835 | dev_err(&bus->dev, "cxl: Failed to find slot hotplug " | ||
836 | "information. You may need to upgrade " | ||
837 | "skiboot. Aborting.\n"); | ||
838 | goto err_dev_put; | ||
839 | } | ||
840 | |||
841 | rc = CXL_READ_VSEC_MODE_CONTROL(dev, switch_work->vsec, &val); | ||
842 | if (rc) { | ||
843 | dev_err(&bus->dev, "cxl: Failed to read CAPI mode control: %i\n", rc); | ||
844 | goto err_dev_put; | ||
845 | } | ||
846 | devfn = dev->devfn; | ||
847 | |||
848 | /* Release the reference obtained in cxl_check_and_switch_mode() */ | ||
849 | pci_dev_put(dev); | ||
850 | |||
851 | dev_dbg(&bus->dev, "cxl: Removing PCI devices from kernel\n"); | ||
852 | pci_lock_rescan_remove(); | ||
853 | pci_hp_remove_devices(bridge->subordinate); | ||
854 | pci_unlock_rescan_remove(); | ||
855 | |||
856 | /* Switch the CXL protocol on the card */ | ||
857 | if (switch_work->mode == CXL_BIMODE_CXL) { | ||
858 | dev_info(&bus->dev, "cxl: Switching card to CXL mode\n"); | ||
859 | val &= ~CXL_VSEC_PROTOCOL_MASK; | ||
860 | val |= CXL_VSEC_PROTOCOL_256TB | CXL_VSEC_PROTOCOL_ENABLE; | ||
861 | rc = pnv_cxl_enable_phb_kernel_api(hose, true); | ||
862 | if (rc) { | ||
863 | dev_err(&bus->dev, "cxl: Failed to enable kernel API" | ||
864 | " on real PHB, aborting\n"); | ||
865 | goto err_free_work; | ||
866 | } | ||
867 | } else { | ||
868 | dev_WARN(&bus->dev, "cxl: Switching card to PCI mode not supported!\n"); | ||
869 | goto err_free_work; | ||
870 | } | ||
871 | |||
872 | rc = CXL_WRITE_VSEC_MODE_CONTROL_BUS(bus, devfn, switch_work->vsec, val); | ||
873 | if (rc) { | ||
874 | dev_err(&bus->dev, "cxl: Failed to configure CXL protocol: %i\n", rc); | ||
875 | goto err_free_work; | ||
876 | } | ||
877 | |||
878 | /* | ||
879 | * The CAIA spec (v1.1, Section 10.6 Bi-modal Device Support) states | ||
880 | * we must wait 100ms after this mode switch before touching PCIe config | ||
881 | * space. | ||
882 | */ | ||
883 | msleep(100); | ||
884 | |||
885 | /* | ||
886 | * Hot reset to cause the card to come back in cxl mode. A | ||
887 | * OPAL_RESET_PCI_LINK would be sufficient, but currently lacks support | ||
888 | * in skiboot, so we use a hot reset instead. | ||
889 | * | ||
890 | * We call pci_set_pcie_reset_state() on the bridge, as a CAPI card is | ||
891 | * guaranteed to sit directly under the root port, and setting the reset | ||
892 | * state on a device directly under the root port is equivalent to doing | ||
893 | * it on the root port iself. | ||
894 | */ | ||
895 | dev_info(&bus->dev, "cxl: Configuration write complete, resetting card\n"); | ||
896 | pci_set_pcie_reset_state(bridge, pcie_hot_reset); | ||
897 | pci_set_pcie_reset_state(bridge, pcie_deassert_reset); | ||
898 | |||
899 | dev_dbg(&bus->dev, "cxl: Offlining slot\n"); | ||
900 | rc = pnv_php_set_slot_power_state(&php_slot->slot, OPAL_PCI_SLOT_OFFLINE); | ||
901 | if (rc) { | ||
902 | dev_err(&bus->dev, "cxl: OPAL offlining call failed: %i\n", rc); | ||
903 | goto err_free_work; | ||
904 | } | ||
905 | |||
906 | dev_dbg(&bus->dev, "cxl: Onlining and probing slot\n"); | ||
907 | rc = pnv_php_set_slot_power_state(&php_slot->slot, OPAL_PCI_SLOT_ONLINE); | ||
908 | if (rc) { | ||
909 | dev_err(&bus->dev, "cxl: OPAL onlining call failed: %i\n", rc); | ||
910 | goto err_free_work; | ||
911 | } | ||
912 | |||
913 | pci_lock_rescan_remove(); | ||
914 | pci_hp_add_devices(bridge->subordinate); | ||
915 | pci_unlock_rescan_remove(); | ||
916 | |||
917 | dev_info(&bus->dev, "cxl: CAPI mode switch completed\n"); | ||
918 | kfree(switch_work); | ||
919 | return; | ||
920 | |||
921 | err_dev_put: | ||
922 | /* Release the reference obtained in cxl_check_and_switch_mode() */ | ||
923 | pci_dev_put(dev); | ||
924 | err_free_work: | ||
925 | kfree(switch_work); | ||
926 | } | ||
927 | |||
928 | int cxl_check_and_switch_mode(struct pci_dev *dev, int mode, int vsec) | ||
929 | { | ||
930 | struct cxl_switch_work *work; | ||
931 | u8 val; | ||
932 | int rc; | ||
933 | |||
934 | if (!cpu_has_feature(CPU_FTR_HVMODE)) | ||
935 | return -ENODEV; | 812 | return -ENODEV; |
936 | |||
937 | if (!vsec) { | ||
938 | vsec = find_cxl_vsec(dev); | ||
939 | if (!vsec) { | ||
940 | dev_info(&dev->dev, "CXL VSEC not found\n"); | ||
941 | return -ENODEV; | ||
942 | } | ||
943 | } | 813 | } |
944 | 814 | ||
945 | rc = CXL_READ_VSEC_MODE_CONTROL(dev, vsec, &val); | 815 | if ((rc = CXL_READ_VSEC_MODE_CONTROL(dev, vsec, &val))) { |
946 | if (rc) { | 816 | dev_err(&dev->dev, "failed to read current mode control: %i", rc); |
947 | dev_err(&dev->dev, "Failed to read current mode control: %i", rc); | ||
948 | return rc; | 817 | return rc; |
949 | } | 818 | } |
950 | 819 | val &= ~CXL_VSEC_PROTOCOL_MASK; | |
951 | if (mode == CXL_BIMODE_PCI) { | 820 | val |= CXL_VSEC_PROTOCOL_256TB | CXL_VSEC_PROTOCOL_ENABLE; |
952 | if (!(val & CXL_VSEC_PROTOCOL_ENABLE)) { | 821 | if ((rc = CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val))) { |
953 | dev_info(&dev->dev, "Card is already in PCI mode\n"); | 822 | dev_err(&dev->dev, "failed to enable CXL protocol: %i", rc); |
954 | return 0; | 823 | return rc; |
955 | } | ||
956 | /* | ||
957 | * TODO: Before it's safe to switch the card back to PCI mode | ||
958 | * we need to disable the CAPP and make sure any cachelines the | ||
959 | * card holds have been flushed out. Needs skiboot support. | ||
960 | */ | ||
961 | dev_WARN(&dev->dev, "CXL mode switch to PCI unsupported!\n"); | ||
962 | return -EIO; | ||
963 | } | ||
964 | |||
965 | if (val & CXL_VSEC_PROTOCOL_ENABLE) { | ||
966 | dev_info(&dev->dev, "Card is already in CXL mode\n"); | ||
967 | return 0; | ||
968 | } | 824 | } |
969 | |||
970 | dev_info(&dev->dev, "Card is in PCI mode, scheduling kernel thread " | ||
971 | "to switch to CXL mode\n"); | ||
972 | |||
973 | work = kmalloc(sizeof(struct cxl_switch_work), GFP_KERNEL); | ||
974 | if (!work) | ||
975 | return -ENOMEM; | ||
976 | |||
977 | pci_dev_get(dev); | ||
978 | work->dev = dev; | ||
979 | work->vsec = vsec; | ||
980 | work->mode = mode; | ||
981 | INIT_WORK(&work->work, switch_card_to_cxl); | ||
982 | |||
983 | schedule_work(&work->work); | ||
984 | |||
985 | /* | 825 | /* |
986 | * We return a failure now to abort the driver init. Once the | 826 | * The CAIA spec (v0.12 11.6 Bi-modal Device Support) states |
987 | * link has been cycled and the card is in cxl mode we will | 827 | * we must wait 100ms after this mode switch before touching |
988 | * come back (possibly using the generic cxl driver), but | 828 | * PCIe config space. |
989 | * return success as the card should then be in cxl mode. | ||
990 | * | ||
991 | * TODO: What if the card comes back in PCI mode even after | ||
992 | * the switch? Don't want to spin endlessly. | ||
993 | */ | 829 | */ |
994 | return -EBUSY; | 830 | msleep(100); |
995 | } | ||
996 | EXPORT_SYMBOL_GPL(cxl_check_and_switch_mode); | ||
997 | |||
998 | #endif /* CONFIG_CXL_BIMODAL */ | ||
999 | |||
1000 | static int setup_cxl_protocol_area(struct pci_dev *dev) | ||
1001 | { | ||
1002 | u8 val; | ||
1003 | int rc; | ||
1004 | int vsec = find_cxl_vsec(dev); | ||
1005 | |||
1006 | if (!vsec) { | ||
1007 | dev_info(&dev->dev, "CXL VSEC not found\n"); | ||
1008 | return -ENODEV; | ||
1009 | } | ||
1010 | |||
1011 | rc = CXL_READ_VSEC_MODE_CONTROL(dev, vsec, &val); | ||
1012 | if (rc) { | ||
1013 | dev_err(&dev->dev, "Failed to read current mode control: %i\n", rc); | ||
1014 | return rc; | ||
1015 | } | ||
1016 | |||
1017 | if (!(val & CXL_VSEC_PROTOCOL_ENABLE)) { | ||
1018 | dev_err(&dev->dev, "Card not in CAPI mode!\n"); | ||
1019 | return -EIO; | ||
1020 | } | ||
1021 | |||
1022 | if ((val & CXL_VSEC_PROTOCOL_MASK) != CXL_VSEC_PROTOCOL_256TB) { | ||
1023 | val &= ~CXL_VSEC_PROTOCOL_MASK; | ||
1024 | val |= CXL_VSEC_PROTOCOL_256TB; | ||
1025 | rc = CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val); | ||
1026 | if (rc) { | ||
1027 | dev_err(&dev->dev, "Failed to set CXL protocol area: %i\n", rc); | ||
1028 | return rc; | ||
1029 | } | ||
1030 | } | ||
1031 | 831 | ||
1032 | return 0; | 832 | return 0; |
1033 | } | 833 | } |
@@ -1724,7 +1524,7 @@ static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev) | |||
1724 | if ((rc = setup_cxl_bars(dev))) | 1524 | if ((rc = setup_cxl_bars(dev))) |
1725 | return rc; | 1525 | return rc; |
1726 | 1526 | ||
1727 | if ((rc = setup_cxl_protocol_area(dev))) | 1527 | if ((rc = switch_card_to_cxl(dev))) |
1728 | return rc; | 1528 | return rc; |
1729 | 1529 | ||
1730 | if ((rc = cxl_update_image_control(adapter))) | 1530 | if ((rc = cxl_update_image_control(adapter))) |