diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2013-02-06 07:06:39 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-02-06 14:21:51 -0500 |
commit | 2703d4b2e673cc240ad06d79d131fd1d0f77d65d (patch) | |
tree | 0e5bd20e88e2de4912ba44de8e0b6764c4028791 | |
parent | 890537b3ac953ad2cc4f5ecb83744e967ae2aa31 (diff) |
mei: sperate interface and pci code into two files
leave misc file operations in the main
and move PCI related code into pci-me
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/misc/mei/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 354 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 3 | ||||
-rw-r--r-- | drivers/misc/mei/pci-me.c | 393 |
4 files changed, 409 insertions, 342 deletions
diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 9f719339e594..068f55354811 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile | |||
@@ -11,3 +11,4 @@ mei-objs += main.o | |||
11 | mei-objs += amthif.o | 11 | mei-objs += amthif.o |
12 | mei-objs += wd.o | 12 | mei-objs += wd.o |
13 | mei-objs += client.o | 13 | mei-objs += client.o |
14 | mei-objs += pci-me.o | ||
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 123c663509ef..018623c9a8e1 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c | |||
@@ -43,54 +43,6 @@ | |||
43 | #include "hw-me.h" | 43 | #include "hw-me.h" |
44 | #include "client.h" | 44 | #include "client.h" |
45 | 45 | ||
46 | /* AMT device is a singleton on the platform */ | ||
47 | static struct pci_dev *mei_pdev; | ||
48 | |||
49 | /* mei_pci_tbl - PCI Device ID Table */ | ||
50 | static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { | ||
51 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, | ||
52 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, | ||
53 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, | ||
54 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)}, | ||
55 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)}, | ||
56 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)}, | ||
57 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)}, | ||
58 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)}, | ||
59 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)}, | ||
60 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)}, | ||
61 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)}, | ||
62 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)}, | ||
63 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)}, | ||
64 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)}, | ||
65 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)}, | ||
66 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)}, | ||
67 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)}, | ||
68 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)}, | ||
69 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)}, | ||
70 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)}, | ||
71 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)}, | ||
72 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)}, | ||
73 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)}, | ||
74 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)}, | ||
75 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)}, | ||
76 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)}, | ||
77 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)}, | ||
78 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)}, | ||
79 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)}, | ||
80 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, | ||
81 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, | ||
82 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, | ||
83 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, | ||
84 | |||
85 | /* required last entry */ | ||
86 | {0, } | ||
87 | }; | ||
88 | |||
89 | MODULE_DEVICE_TABLE(pci, mei_pci_tbl); | ||
90 | |||
91 | static DEFINE_MUTEX(mei_mutex); | ||
92 | |||
93 | |||
94 | /** | 46 | /** |
95 | * mei_open - the open function | 47 | * mei_open - the open function |
96 | * | 48 | * |
@@ -101,15 +53,20 @@ static DEFINE_MUTEX(mei_mutex); | |||
101 | */ | 53 | */ |
102 | static int mei_open(struct inode *inode, struct file *file) | 54 | static int mei_open(struct inode *inode, struct file *file) |
103 | { | 55 | { |
56 | struct miscdevice *misc = file->private_data; | ||
57 | struct pci_dev *pdev; | ||
104 | struct mei_cl *cl; | 58 | struct mei_cl *cl; |
105 | struct mei_device *dev; | 59 | struct mei_device *dev; |
60 | |||
106 | int err; | 61 | int err; |
107 | 62 | ||
108 | err = -ENODEV; | 63 | err = -ENODEV; |
109 | if (!mei_pdev) | 64 | if (!misc->parent) |
110 | goto out; | 65 | goto out; |
111 | 66 | ||
112 | dev = pci_get_drvdata(mei_pdev); | 67 | pdev = container_of(misc->parent, struct pci_dev, dev); |
68 | |||
69 | dev = pci_get_drvdata(pdev); | ||
113 | if (!dev) | 70 | if (!dev) |
114 | goto out; | 71 | goto out; |
115 | 72 | ||
@@ -787,7 +744,6 @@ static const struct file_operations mei_fops = { | |||
787 | .llseek = no_llseek | 744 | .llseek = no_llseek |
788 | }; | 745 | }; |
789 | 746 | ||
790 | |||
791 | /* | 747 | /* |
792 | * Misc Device Struct | 748 | * Misc Device Struct |
793 | */ | 749 | */ |
@@ -797,302 +753,16 @@ static struct miscdevice mei_misc_device = { | |||
797 | .minor = MISC_DYNAMIC_MINOR, | 753 | .minor = MISC_DYNAMIC_MINOR, |
798 | }; | 754 | }; |
799 | 755 | ||
800 | /** | 756 | int mei_register(struct device *dev) |
801 | * mei_quirk_probe - probe for devices that doesn't valid ME interface | ||
802 | * @pdev: PCI device structure | ||
803 | * @ent: entry into pci_device_table | ||
804 | * | ||
805 | * returns true if ME Interface is valid, false otherwise | ||
806 | */ | ||
807 | static bool mei_quirk_probe(struct pci_dev *pdev, | ||
808 | const struct pci_device_id *ent) | ||
809 | { | 757 | { |
810 | u32 reg; | 758 | mei_misc_device.parent = dev; |
811 | if (ent->device == MEI_DEV_ID_PBG_1) { | 759 | return misc_register(&mei_misc_device); |
812 | pci_read_config_dword(pdev, 0x48, ®); | ||
813 | /* make sure that bit 9 is up and bit 10 is down */ | ||
814 | if ((reg & 0x600) == 0x200) { | ||
815 | dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); | ||
816 | return false; | ||
817 | } | ||
818 | } | ||
819 | return true; | ||
820 | } | ||
821 | /** | ||
822 | * mei_probe - Device Initialization Routine | ||
823 | * | ||
824 | * @pdev: PCI device structure | ||
825 | * @ent: entry in kcs_pci_tbl | ||
826 | * | ||
827 | * returns 0 on success, <0 on failure. | ||
828 | */ | ||
829 | static int mei_probe(struct pci_dev *pdev, | ||
830 | const struct pci_device_id *ent) | ||
831 | { | ||
832 | struct mei_device *dev; | ||
833 | int err; | ||
834 | |||
835 | mutex_lock(&mei_mutex); | ||
836 | |||
837 | if (!mei_quirk_probe(pdev, ent)) { | ||
838 | err = -ENODEV; | ||
839 | goto end; | ||
840 | } | ||
841 | |||
842 | if (mei_pdev) { | ||
843 | err = -EEXIST; | ||
844 | goto end; | ||
845 | } | ||
846 | /* enable pci dev */ | ||
847 | err = pci_enable_device(pdev); | ||
848 | if (err) { | ||
849 | dev_err(&pdev->dev, "failed to enable pci device.\n"); | ||
850 | goto end; | ||
851 | } | ||
852 | /* set PCI host mastering */ | ||
853 | pci_set_master(pdev); | ||
854 | /* pci request regions for mei driver */ | ||
855 | err = pci_request_regions(pdev, KBUILD_MODNAME); | ||
856 | if (err) { | ||
857 | dev_err(&pdev->dev, "failed to get pci regions.\n"); | ||
858 | goto disable_device; | ||
859 | } | ||
860 | /* allocates and initializes the mei dev structure */ | ||
861 | dev = mei_device_init(pdev); | ||
862 | if (!dev) { | ||
863 | err = -ENOMEM; | ||
864 | goto release_regions; | ||
865 | } | ||
866 | /* mapping IO device memory */ | ||
867 | dev->mem_addr = pci_iomap(pdev, 0, 0); | ||
868 | if (!dev->mem_addr) { | ||
869 | dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); | ||
870 | err = -ENOMEM; | ||
871 | goto free_device; | ||
872 | } | ||
873 | pci_enable_msi(pdev); | ||
874 | |||
875 | /* request and enable interrupt */ | ||
876 | if (pci_dev_msi_enabled(pdev)) | ||
877 | err = request_threaded_irq(pdev->irq, | ||
878 | NULL, | ||
879 | mei_interrupt_thread_handler, | ||
880 | IRQF_ONESHOT, KBUILD_MODNAME, dev); | ||
881 | else | ||
882 | err = request_threaded_irq(pdev->irq, | ||
883 | mei_interrupt_quick_handler, | ||
884 | mei_interrupt_thread_handler, | ||
885 | IRQF_SHARED, KBUILD_MODNAME, dev); | ||
886 | |||
887 | if (err) { | ||
888 | dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", | ||
889 | pdev->irq); | ||
890 | goto disable_msi; | ||
891 | } | ||
892 | |||
893 | if (mei_hw_init(dev)) { | ||
894 | dev_err(&pdev->dev, "init hw failure.\n"); | ||
895 | err = -ENODEV; | ||
896 | goto release_irq; | ||
897 | } | ||
898 | |||
899 | err = misc_register(&mei_misc_device); | ||
900 | if (err) | ||
901 | goto release_irq; | ||
902 | |||
903 | mei_pdev = pdev; | ||
904 | pci_set_drvdata(pdev, dev); | ||
905 | |||
906 | |||
907 | schedule_delayed_work(&dev->timer_work, HZ); | ||
908 | |||
909 | mutex_unlock(&mei_mutex); | ||
910 | |||
911 | pr_debug("initialization successful.\n"); | ||
912 | |||
913 | return 0; | ||
914 | |||
915 | release_irq: | ||
916 | mei_disable_interrupts(dev); | ||
917 | flush_scheduled_work(); | ||
918 | free_irq(pdev->irq, dev); | ||
919 | disable_msi: | ||
920 | pci_disable_msi(pdev); | ||
921 | pci_iounmap(pdev, dev->mem_addr); | ||
922 | free_device: | ||
923 | kfree(dev); | ||
924 | release_regions: | ||
925 | pci_release_regions(pdev); | ||
926 | disable_device: | ||
927 | pci_disable_device(pdev); | ||
928 | end: | ||
929 | mutex_unlock(&mei_mutex); | ||
930 | dev_err(&pdev->dev, "initialization failed.\n"); | ||
931 | return err; | ||
932 | } | 760 | } |
933 | 761 | ||
934 | /** | 762 | void mei_deregister(void) |
935 | * mei_remove - Device Removal Routine | ||
936 | * | ||
937 | * @pdev: PCI device structure | ||
938 | * | ||
939 | * mei_remove is called by the PCI subsystem to alert the driver | ||
940 | * that it should release a PCI device. | ||
941 | */ | ||
942 | static void mei_remove(struct pci_dev *pdev) | ||
943 | { | 763 | { |
944 | struct mei_device *dev; | ||
945 | |||
946 | if (mei_pdev != pdev) | ||
947 | return; | ||
948 | |||
949 | dev = pci_get_drvdata(pdev); | ||
950 | if (!dev) | ||
951 | return; | ||
952 | |||
953 | mutex_lock(&dev->device_lock); | ||
954 | |||
955 | cancel_delayed_work(&dev->timer_work); | ||
956 | |||
957 | mei_wd_stop(dev); | ||
958 | |||
959 | mei_pdev = NULL; | ||
960 | |||
961 | if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) { | ||
962 | dev->iamthif_cl.state = MEI_FILE_DISCONNECTING; | ||
963 | mei_cl_disconnect(&dev->iamthif_cl); | ||
964 | } | ||
965 | if (dev->wd_cl.state == MEI_FILE_CONNECTED) { | ||
966 | dev->wd_cl.state = MEI_FILE_DISCONNECTING; | ||
967 | mei_cl_disconnect(&dev->wd_cl); | ||
968 | } | ||
969 | |||
970 | /* Unregistering watchdog device */ | ||
971 | mei_watchdog_unregister(dev); | ||
972 | |||
973 | /* remove entry if already in list */ | ||
974 | dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); | ||
975 | |||
976 | if (dev->open_handle_count > 0) | ||
977 | dev->open_handle_count--; | ||
978 | mei_cl_unlink(&dev->wd_cl); | ||
979 | |||
980 | if (dev->open_handle_count > 0) | ||
981 | dev->open_handle_count--; | ||
982 | mei_cl_unlink(&dev->iamthif_cl); | ||
983 | |||
984 | dev->iamthif_current_cb = NULL; | ||
985 | dev->me_clients_num = 0; | ||
986 | |||
987 | mutex_unlock(&dev->device_lock); | ||
988 | |||
989 | flush_scheduled_work(); | ||
990 | |||
991 | /* disable interrupts */ | ||
992 | mei_disable_interrupts(dev); | ||
993 | |||
994 | free_irq(pdev->irq, dev); | ||
995 | pci_disable_msi(pdev); | ||
996 | pci_set_drvdata(pdev, NULL); | ||
997 | |||
998 | if (dev->mem_addr) | ||
999 | pci_iounmap(pdev, dev->mem_addr); | ||
1000 | |||
1001 | kfree(dev); | ||
1002 | |||
1003 | pci_release_regions(pdev); | ||
1004 | pci_disable_device(pdev); | ||
1005 | |||
1006 | misc_deregister(&mei_misc_device); | 764 | misc_deregister(&mei_misc_device); |
765 | mei_misc_device.parent = NULL; | ||
1007 | } | 766 | } |
1008 | #ifdef CONFIG_PM | ||
1009 | static int mei_pci_suspend(struct device *device) | ||
1010 | { | ||
1011 | struct pci_dev *pdev = to_pci_dev(device); | ||
1012 | struct mei_device *dev = pci_get_drvdata(pdev); | ||
1013 | int err; | ||
1014 | |||
1015 | if (!dev) | ||
1016 | return -ENODEV; | ||
1017 | mutex_lock(&dev->device_lock); | ||
1018 | |||
1019 | cancel_delayed_work(&dev->timer_work); | ||
1020 | |||
1021 | /* Stop watchdog if exists */ | ||
1022 | err = mei_wd_stop(dev); | ||
1023 | /* Set new mei state */ | ||
1024 | if (dev->dev_state == MEI_DEV_ENABLED || | ||
1025 | dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { | ||
1026 | dev->dev_state = MEI_DEV_POWER_DOWN; | ||
1027 | mei_reset(dev, 0); | ||
1028 | } | ||
1029 | mutex_unlock(&dev->device_lock); | ||
1030 | |||
1031 | free_irq(pdev->irq, dev); | ||
1032 | pci_disable_msi(pdev); | ||
1033 | |||
1034 | return err; | ||
1035 | } | ||
1036 | |||
1037 | static int mei_pci_resume(struct device *device) | ||
1038 | { | ||
1039 | struct pci_dev *pdev = to_pci_dev(device); | ||
1040 | struct mei_device *dev; | ||
1041 | int err; | ||
1042 | |||
1043 | dev = pci_get_drvdata(pdev); | ||
1044 | if (!dev) | ||
1045 | return -ENODEV; | ||
1046 | |||
1047 | pci_enable_msi(pdev); | ||
1048 | |||
1049 | /* request and enable interrupt */ | ||
1050 | if (pci_dev_msi_enabled(pdev)) | ||
1051 | err = request_threaded_irq(pdev->irq, | ||
1052 | NULL, | ||
1053 | mei_interrupt_thread_handler, | ||
1054 | IRQF_ONESHOT, KBUILD_MODNAME, dev); | ||
1055 | else | ||
1056 | err = request_threaded_irq(pdev->irq, | ||
1057 | mei_interrupt_quick_handler, | ||
1058 | mei_interrupt_thread_handler, | ||
1059 | IRQF_SHARED, KBUILD_MODNAME, dev); | ||
1060 | |||
1061 | if (err) { | ||
1062 | dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", | ||
1063 | pdev->irq); | ||
1064 | return err; | ||
1065 | } | ||
1066 | |||
1067 | mutex_lock(&dev->device_lock); | ||
1068 | dev->dev_state = MEI_DEV_POWER_UP; | ||
1069 | mei_reset(dev, 1); | ||
1070 | mutex_unlock(&dev->device_lock); | ||
1071 | |||
1072 | /* Start timer if stopped in suspend */ | ||
1073 | schedule_delayed_work(&dev->timer_work, HZ); | ||
1074 | |||
1075 | return err; | ||
1076 | } | ||
1077 | static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume); | ||
1078 | #define MEI_PM_OPS (&mei_pm_ops) | ||
1079 | #else | ||
1080 | #define MEI_PM_OPS NULL | ||
1081 | #endif /* CONFIG_PM */ | ||
1082 | /* | ||
1083 | * PCI driver structure | ||
1084 | */ | ||
1085 | static struct pci_driver mei_driver = { | ||
1086 | .name = KBUILD_MODNAME, | ||
1087 | .id_table = mei_pci_tbl, | ||
1088 | .probe = mei_probe, | ||
1089 | .remove = mei_remove, | ||
1090 | .shutdown = mei_remove, | ||
1091 | .driver.pm = MEI_PM_OPS, | ||
1092 | }; | ||
1093 | 767 | ||
1094 | module_pci_driver(mei_driver); | ||
1095 | 768 | ||
1096 | MODULE_AUTHOR("Intel Corporation"); | ||
1097 | MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); | ||
1098 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d6589d0d305a..3b2bca386e5a 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h | |||
@@ -401,6 +401,9 @@ bool mei_me_is_ready(struct mei_device *dev); | |||
401 | 401 | ||
402 | 402 | ||
403 | 403 | ||
404 | int mei_register(struct device *dev); | ||
405 | void mei_deregister(void); | ||
406 | |||
404 | #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d comp=%1d" | 407 | #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d comp=%1d" |
405 | #define MEI_HDR_PRM(hdr) \ | 408 | #define MEI_HDR_PRM(hdr) \ |
406 | (hdr)->host_addr, (hdr)->me_addr, \ | 409 | (hdr)->host_addr, (hdr)->me_addr, \ |
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c new file mode 100644 index 000000000000..eaed398bed6b --- /dev/null +++ b/drivers/misc/mei/pci-me.c | |||
@@ -0,0 +1,393 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | ||
4 | * Copyright (c) 2003-2012, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/device.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/fcntl.h> | ||
27 | #include <linux/aio.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/poll.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/ioctl.h> | ||
32 | #include <linux/cdev.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/uuid.h> | ||
35 | #include <linux/compat.h> | ||
36 | #include <linux/jiffies.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/miscdevice.h> | ||
39 | |||
40 | #include <linux/mei.h> | ||
41 | |||
42 | #include "mei_dev.h" | ||
43 | #include "hw-me.h" | ||
44 | #include "client.h" | ||
45 | |||
46 | /* AMT device is a singleton on the platform */ | ||
47 | static struct pci_dev *mei_pdev; | ||
48 | |||
49 | /* mei_pci_tbl - PCI Device ID Table */ | ||
50 | static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { | ||
51 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, | ||
52 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, | ||
53 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, | ||
54 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)}, | ||
55 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)}, | ||
56 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)}, | ||
57 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)}, | ||
58 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)}, | ||
59 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)}, | ||
60 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)}, | ||
61 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)}, | ||
62 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)}, | ||
63 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)}, | ||
64 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)}, | ||
65 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)}, | ||
66 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)}, | ||
67 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)}, | ||
68 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)}, | ||
69 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)}, | ||
70 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)}, | ||
71 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)}, | ||
72 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)}, | ||
73 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)}, | ||
74 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)}, | ||
75 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)}, | ||
76 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)}, | ||
77 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)}, | ||
78 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)}, | ||
79 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)}, | ||
80 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, | ||
81 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, | ||
82 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, | ||
83 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, | ||
84 | |||
85 | /* required last entry */ | ||
86 | {0, } | ||
87 | }; | ||
88 | |||
89 | MODULE_DEVICE_TABLE(pci, mei_pci_tbl); | ||
90 | |||
91 | static DEFINE_MUTEX(mei_mutex); | ||
92 | |||
93 | |||
94 | /** | ||
95 | * mei_quirk_probe - probe for devices that doesn't valid ME interface | ||
96 | * @pdev: PCI device structure | ||
97 | * @ent: entry into pci_device_table | ||
98 | * | ||
99 | * returns true if ME Interface is valid, false otherwise | ||
100 | */ | ||
101 | static bool mei_quirk_probe(struct pci_dev *pdev, | ||
102 | const struct pci_device_id *ent) | ||
103 | { | ||
104 | u32 reg; | ||
105 | if (ent->device == MEI_DEV_ID_PBG_1) { | ||
106 | pci_read_config_dword(pdev, 0x48, ®); | ||
107 | /* make sure that bit 9 is up and bit 10 is down */ | ||
108 | if ((reg & 0x600) == 0x200) { | ||
109 | dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); | ||
110 | return false; | ||
111 | } | ||
112 | } | ||
113 | return true; | ||
114 | } | ||
115 | /** | ||
116 | * mei_probe - Device Initialization Routine | ||
117 | * | ||
118 | * @pdev: PCI device structure | ||
119 | * @ent: entry in kcs_pci_tbl | ||
120 | * | ||
121 | * returns 0 on success, <0 on failure. | ||
122 | */ | ||
123 | static int mei_probe(struct pci_dev *pdev, | ||
124 | const struct pci_device_id *ent) | ||
125 | { | ||
126 | struct mei_device *dev; | ||
127 | int err; | ||
128 | |||
129 | mutex_lock(&mei_mutex); | ||
130 | |||
131 | if (!mei_quirk_probe(pdev, ent)) { | ||
132 | err = -ENODEV; | ||
133 | goto end; | ||
134 | } | ||
135 | |||
136 | if (mei_pdev) { | ||
137 | err = -EEXIST; | ||
138 | goto end; | ||
139 | } | ||
140 | /* enable pci dev */ | ||
141 | err = pci_enable_device(pdev); | ||
142 | if (err) { | ||
143 | dev_err(&pdev->dev, "failed to enable pci device.\n"); | ||
144 | goto end; | ||
145 | } | ||
146 | /* set PCI host mastering */ | ||
147 | pci_set_master(pdev); | ||
148 | /* pci request regions for mei driver */ | ||
149 | err = pci_request_regions(pdev, KBUILD_MODNAME); | ||
150 | if (err) { | ||
151 | dev_err(&pdev->dev, "failed to get pci regions.\n"); | ||
152 | goto disable_device; | ||
153 | } | ||
154 | /* allocates and initializes the mei dev structure */ | ||
155 | dev = mei_device_init(pdev); | ||
156 | if (!dev) { | ||
157 | err = -ENOMEM; | ||
158 | goto release_regions; | ||
159 | } | ||
160 | /* mapping IO device memory */ | ||
161 | dev->mem_addr = pci_iomap(pdev, 0, 0); | ||
162 | if (!dev->mem_addr) { | ||
163 | dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); | ||
164 | err = -ENOMEM; | ||
165 | goto free_device; | ||
166 | } | ||
167 | pci_enable_msi(pdev); | ||
168 | |||
169 | /* request and enable interrupt */ | ||
170 | if (pci_dev_msi_enabled(pdev)) | ||
171 | err = request_threaded_irq(pdev->irq, | ||
172 | NULL, | ||
173 | mei_interrupt_thread_handler, | ||
174 | IRQF_ONESHOT, KBUILD_MODNAME, dev); | ||
175 | else | ||
176 | err = request_threaded_irq(pdev->irq, | ||
177 | mei_interrupt_quick_handler, | ||
178 | mei_interrupt_thread_handler, | ||
179 | IRQF_SHARED, KBUILD_MODNAME, dev); | ||
180 | |||
181 | if (err) { | ||
182 | dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", | ||
183 | pdev->irq); | ||
184 | goto disable_msi; | ||
185 | } | ||
186 | |||
187 | if (mei_hw_init(dev)) { | ||
188 | dev_err(&pdev->dev, "init hw failure.\n"); | ||
189 | err = -ENODEV; | ||
190 | goto release_irq; | ||
191 | } | ||
192 | |||
193 | err = mei_register(&pdev->dev); | ||
194 | if (err) | ||
195 | goto release_irq; | ||
196 | |||
197 | mei_pdev = pdev; | ||
198 | pci_set_drvdata(pdev, dev); | ||
199 | |||
200 | |||
201 | schedule_delayed_work(&dev->timer_work, HZ); | ||
202 | |||
203 | mutex_unlock(&mei_mutex); | ||
204 | |||
205 | pr_debug("initialization successful.\n"); | ||
206 | |||
207 | return 0; | ||
208 | |||
209 | release_irq: | ||
210 | mei_disable_interrupts(dev); | ||
211 | flush_scheduled_work(); | ||
212 | free_irq(pdev->irq, dev); | ||
213 | disable_msi: | ||
214 | pci_disable_msi(pdev); | ||
215 | pci_iounmap(pdev, dev->mem_addr); | ||
216 | free_device: | ||
217 | kfree(dev); | ||
218 | release_regions: | ||
219 | pci_release_regions(pdev); | ||
220 | disable_device: | ||
221 | pci_disable_device(pdev); | ||
222 | end: | ||
223 | mutex_unlock(&mei_mutex); | ||
224 | dev_err(&pdev->dev, "initialization failed.\n"); | ||
225 | return err; | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * mei_remove - Device Removal Routine | ||
230 | * | ||
231 | * @pdev: PCI device structure | ||
232 | * | ||
233 | * mei_remove is called by the PCI subsystem to alert the driver | ||
234 | * that it should release a PCI device. | ||
235 | */ | ||
236 | static void mei_remove(struct pci_dev *pdev) | ||
237 | { | ||
238 | struct mei_device *dev; | ||
239 | |||
240 | if (mei_pdev != pdev) | ||
241 | return; | ||
242 | |||
243 | dev = pci_get_drvdata(pdev); | ||
244 | if (!dev) | ||
245 | return; | ||
246 | |||
247 | mutex_lock(&dev->device_lock); | ||
248 | |||
249 | cancel_delayed_work(&dev->timer_work); | ||
250 | |||
251 | mei_wd_stop(dev); | ||
252 | |||
253 | mei_pdev = NULL; | ||
254 | |||
255 | if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) { | ||
256 | dev->iamthif_cl.state = MEI_FILE_DISCONNECTING; | ||
257 | mei_cl_disconnect(&dev->iamthif_cl); | ||
258 | } | ||
259 | if (dev->wd_cl.state == MEI_FILE_CONNECTED) { | ||
260 | dev->wd_cl.state = MEI_FILE_DISCONNECTING; | ||
261 | mei_cl_disconnect(&dev->wd_cl); | ||
262 | } | ||
263 | |||
264 | /* Unregistering watchdog device */ | ||
265 | mei_watchdog_unregister(dev); | ||
266 | |||
267 | /* remove entry if already in list */ | ||
268 | dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); | ||
269 | |||
270 | if (dev->open_handle_count > 0) | ||
271 | dev->open_handle_count--; | ||
272 | mei_cl_unlink(&dev->wd_cl); | ||
273 | |||
274 | if (dev->open_handle_count > 0) | ||
275 | dev->open_handle_count--; | ||
276 | mei_cl_unlink(&dev->iamthif_cl); | ||
277 | |||
278 | dev->iamthif_current_cb = NULL; | ||
279 | dev->me_clients_num = 0; | ||
280 | |||
281 | mutex_unlock(&dev->device_lock); | ||
282 | |||
283 | flush_scheduled_work(); | ||
284 | |||
285 | /* disable interrupts */ | ||
286 | mei_disable_interrupts(dev); | ||
287 | |||
288 | free_irq(pdev->irq, dev); | ||
289 | pci_disable_msi(pdev); | ||
290 | pci_set_drvdata(pdev, NULL); | ||
291 | |||
292 | if (dev->mem_addr) | ||
293 | pci_iounmap(pdev, dev->mem_addr); | ||
294 | |||
295 | kfree(dev); | ||
296 | |||
297 | pci_release_regions(pdev); | ||
298 | pci_disable_device(pdev); | ||
299 | |||
300 | mei_deregister(); | ||
301 | |||
302 | } | ||
303 | #ifdef CONFIG_PM | ||
304 | static int mei_pci_suspend(struct device *device) | ||
305 | { | ||
306 | struct pci_dev *pdev = to_pci_dev(device); | ||
307 | struct mei_device *dev = pci_get_drvdata(pdev); | ||
308 | int err; | ||
309 | |||
310 | if (!dev) | ||
311 | return -ENODEV; | ||
312 | mutex_lock(&dev->device_lock); | ||
313 | |||
314 | cancel_delayed_work(&dev->timer_work); | ||
315 | |||
316 | /* Stop watchdog if exists */ | ||
317 | err = mei_wd_stop(dev); | ||
318 | /* Set new mei state */ | ||
319 | if (dev->dev_state == MEI_DEV_ENABLED || | ||
320 | dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { | ||
321 | dev->dev_state = MEI_DEV_POWER_DOWN; | ||
322 | mei_reset(dev, 0); | ||
323 | } | ||
324 | mutex_unlock(&dev->device_lock); | ||
325 | |||
326 | free_irq(pdev->irq, dev); | ||
327 | pci_disable_msi(pdev); | ||
328 | |||
329 | return err; | ||
330 | } | ||
331 | |||
332 | static int mei_pci_resume(struct device *device) | ||
333 | { | ||
334 | struct pci_dev *pdev = to_pci_dev(device); | ||
335 | struct mei_device *dev; | ||
336 | int err; | ||
337 | |||
338 | dev = pci_get_drvdata(pdev); | ||
339 | if (!dev) | ||
340 | return -ENODEV; | ||
341 | |||
342 | pci_enable_msi(pdev); | ||
343 | |||
344 | /* request and enable interrupt */ | ||
345 | if (pci_dev_msi_enabled(pdev)) | ||
346 | err = request_threaded_irq(pdev->irq, | ||
347 | NULL, | ||
348 | mei_interrupt_thread_handler, | ||
349 | IRQF_ONESHOT, KBUILD_MODNAME, dev); | ||
350 | else | ||
351 | err = request_threaded_irq(pdev->irq, | ||
352 | mei_interrupt_quick_handler, | ||
353 | mei_interrupt_thread_handler, | ||
354 | IRQF_SHARED, KBUILD_MODNAME, dev); | ||
355 | |||
356 | if (err) { | ||
357 | dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", | ||
358 | pdev->irq); | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | mutex_lock(&dev->device_lock); | ||
363 | dev->dev_state = MEI_DEV_POWER_UP; | ||
364 | mei_reset(dev, 1); | ||
365 | mutex_unlock(&dev->device_lock); | ||
366 | |||
367 | /* Start timer if stopped in suspend */ | ||
368 | schedule_delayed_work(&dev->timer_work, HZ); | ||
369 | |||
370 | return err; | ||
371 | } | ||
372 | static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume); | ||
373 | #define MEI_PM_OPS (&mei_pm_ops) | ||
374 | #else | ||
375 | #define MEI_PM_OPS NULL | ||
376 | #endif /* CONFIG_PM */ | ||
377 | /* | ||
378 | * PCI driver structure | ||
379 | */ | ||
380 | static struct pci_driver mei_driver = { | ||
381 | .name = KBUILD_MODNAME, | ||
382 | .id_table = mei_pci_tbl, | ||
383 | .probe = mei_probe, | ||
384 | .remove = mei_remove, | ||
385 | .shutdown = mei_remove, | ||
386 | .driver.pm = MEI_PM_OPS, | ||
387 | }; | ||
388 | |||
389 | module_pci_driver(mei_driver); | ||
390 | |||
391 | MODULE_AUTHOR("Intel Corporation"); | ||
392 | MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); | ||
393 | MODULE_LICENSE("GPL v2"); | ||