| 
									
										
										
										
											2009-04-22 16:52:09 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *    Enables/disables PCIe ECRC checking. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    (C) Copyright 2009 Hewlett-Packard Development Company, L.P. | 
					
						
							|  |  |  |  *    Andrew Patterson <andrew.patterson@hp.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  *    it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  *    the Free Software Foundation; version 2 of the License. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  *    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 
					
						
							|  |  |  |  *    General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  *    along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 
					
						
							|  |  |  |  *    02111-1307, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/moduleparam.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/pci_regs.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include "../../pci.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ECRC_POLICY_DEFAULT 0		/* ECRC set by BIOS */
 | 
					
						
							|  |  |  | #define ECRC_POLICY_OFF     1		/* ECRC off for performance */
 | 
					
						
							|  |  |  | #define ECRC_POLICY_ON      2		/* ECRC on for data integrity */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ecrc_policy = ECRC_POLICY_DEFAULT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *ecrc_policy_str[] = { | 
					
						
							|  |  |  | 	[ECRC_POLICY_DEFAULT] = "bios", | 
					
						
							|  |  |  | 	[ECRC_POLICY_OFF] = "off", | 
					
						
							|  |  |  | 	[ECRC_POLICY_ON] = "on" | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * enable_ercr_checking - enable PCIe ECRC checking for a device | 
					
						
							|  |  |  |  * @dev: the PCI device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 on success, or negative on failure. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int enable_ecrc_checking(struct pci_dev *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int pos; | 
					
						
							|  |  |  | 	u32 reg32; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-11 14:37:24 +09:00
										 |  |  | 	if (!pci_is_pcie(dev)) | 
					
						
							| 
									
										
										
										
											2009-04-22 16:52:09 -06:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 
					
						
							|  |  |  | 	if (!pos) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); | 
					
						
							|  |  |  | 	if (reg32 & PCI_ERR_CAP_ECRC_GENC) | 
					
						
							|  |  |  | 		reg32 |= PCI_ERR_CAP_ECRC_GENE; | 
					
						
							|  |  |  | 	if (reg32 & PCI_ERR_CAP_ECRC_CHKC) | 
					
						
							|  |  |  | 		reg32 |= PCI_ERR_CAP_ECRC_CHKE; | 
					
						
							|  |  |  | 	pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * disable_ercr_checking - disables PCIe ECRC checking for a device | 
					
						
							|  |  |  |  * @dev: the PCI device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 on success, or negative on failure. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int disable_ecrc_checking(struct pci_dev *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int pos; | 
					
						
							|  |  |  | 	u32 reg32; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-11 14:37:24 +09:00
										 |  |  | 	if (!pci_is_pcie(dev)) | 
					
						
							| 
									
										
										
										
											2009-04-22 16:52:09 -06:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 
					
						
							|  |  |  | 	if (!pos) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); | 
					
						
							|  |  |  | 	reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); | 
					
						
							|  |  |  | 	pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy | 
					
						
							|  |  |  |  * @dev: the PCI device | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void pcie_set_ecrc_checking(struct pci_dev *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (ecrc_policy) { | 
					
						
							|  |  |  | 	case ECRC_POLICY_DEFAULT: | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	case ECRC_POLICY_OFF: | 
					
						
							|  |  |  | 		disable_ecrc_checking(dev); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case ECRC_POLICY_ON: | 
					
						
							| 
									
										
										
										
											2009-06-28 09:26:40 -07:00
										 |  |  | 		enable_ecrc_checking(dev); | 
					
						
							| 
									
										
										
										
											2009-04-22 16:52:09 -06:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * pcie_ecrc_get_policy - parse kernel command-line ecrc option | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void pcie_ecrc_get_policy(char *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) | 
					
						
							|  |  |  | 		if (!strncmp(str, ecrc_policy_str[i], | 
					
						
							|  |  |  | 			     strlen(ecrc_policy_str[i]))) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	if (i >= ARRAY_SIZE(ecrc_policy_str)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ecrc_policy = i; | 
					
						
							|  |  |  | } |