eeh.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:10k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * eeh.c
  3.  * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation
  4.  * 
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  * 
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  * 
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18.  */
  19. /* Change Activity:
  20.  * 2001/10/27 : engebret : Created.
  21.  * End Change Activity 
  22.  */
  23. #include <linux/init.h>
  24. #include <linux/pci.h>
  25. #include <linux/proc_fs.h>
  26. #include <linux/bootmem.h>
  27. #include <asm/paca.h>
  28. #include <asm/processor.h>
  29. #include <asm/naca.h>
  30. #include <asm/io.h>
  31. #include "pci.h"
  32. #define BUID_HI(buid) ((buid) >> 32)
  33. #define BUID_LO(buid) ((buid) & 0xffffffff)
  34. #define CONFIG_ADDR(busno, devfn) (((((busno) & 0xff) << 8) | ((devfn) & 0xf8)) << 8)
  35. unsigned long eeh_total_mmio_ffs;
  36. unsigned long eeh_false_positives;
  37. /* RTAS tokens */
  38. static int ibm_set_eeh_option;
  39. static int ibm_set_slot_reset;
  40. static int ibm_read_slot_reset_state;
  41. int eeh_implemented;
  42. #define EEH_MAX_OPTS 4096
  43. static char *eeh_opts;
  44. static int eeh_opts_last;
  45. static int eeh_check_opts_config(struct pci_dev *dev, int default_state);
  46. unsigned long eeh_token(unsigned long phb, unsigned long bus, unsigned long devfn, unsigned long offset)
  47. {
  48. if (phb > 0xff)
  49. panic("eeh_token: phb 0x%lx is too largen", phb);
  50. if (offset & 0x0fffffff00000000)
  51. panic("eeh_token: offset 0x%lx is out of rangen", offset);
  52. return ((IO_UNMAPPED_REGION_ID << 60) | (phb << 48UL) | ((bus & 0xff) << 40UL) | (devfn << 32UL) | (offset & 0xffffffff));
  53. }
  54. int eeh_get_state(unsigned long ea)
  55. {
  56. return 0;
  57. }
  58. /* Check for an eeh failure at the given token address.
  59.  * The given value has been read and it should be 1's (0xff, 0xffff or
  60.  * 0xffffffff).
  61.  *
  62.  * Probe to determine if an error actually occurred.  If not return val.
  63.  * Otherwise panic.
  64.  */
  65. unsigned long eeh_check_failure(void *token, unsigned long val)
  66. {
  67. unsigned long config_addr = (unsigned long)token >> 24; /* PPBBDDRR */
  68. unsigned long phbidx = (config_addr >> 24) & 0xff;
  69. struct pci_controller *phb;
  70. unsigned long ret, rets[2];
  71. config_addr &= 0xffff00;  /* 00BBDD00 */
  72. if (phbidx >= global_phb_number) {
  73. panic("EEH: checking token %p phb index of %ld is greater than max of %dn", token, phbidx, global_phb_number-1);
  74. }
  75. phb = phbtab[phbidx];
  76. ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
  77. config_addr, BUID_HI(phb->buid), BUID_LO(phb->buid));
  78. if (ret == 0 && rets[1] == 1 && rets[0] >= 2) {
  79. struct pci_dev *dev;
  80. int bus = ((unsigned long)token >> 40) & 0xffff; /* include PHB# in bus */
  81. int devfn = (config_addr >> 8) & 0xff;
  82. dev = pci_find_slot(bus, devfn);
  83. if (dev) {
  84. udbg_printf("EEH:  MMIO failure (%ld) on device:n  %s %sn",
  85.       rets[0], dev->slot_name, dev->name);
  86. printk("EEH:  MMIO failure (%ld) on device:n  %s %sn",
  87.       rets[0], dev->slot_name, dev->name);
  88. PPCDBG_ENTER_DEBUGGER();
  89. panic("EEH:  MMIO failure (%ld) on device:n  %s %sn",
  90.       rets[0], dev->slot_name, dev->name);
  91. } else {
  92. udbg_printf("EEH:  MMIO failure (%ld) on device buid %lx, config_addr %lxn", rets[0], phb->buid, config_addr);
  93. printk("EEH:  MMIO failure (%ld) on device buid %lx, config_addr %lxn", rets[0], phb->buid, config_addr);
  94. PPCDBG_ENTER_DEBUGGER();
  95. panic("EEH:  MMIO failure (%ld) on device buid %lx, config_addr %lxn", rets[0], phb->buid, config_addr);
  96. }
  97. }
  98. eeh_false_positives++;
  99. return val; /* good case */
  100. }
  101. void eeh_init(void)
  102. {
  103. extern char cmd_line[]; /* Very early cmd line parse.  Cheap, but works. */
  104. char *eeh_force_off = strstr(cmd_line, "eeh-force-off");
  105. char *eeh_force_on = strstr(cmd_line, "eeh-force-on");
  106. ibm_set_eeh_option = rtas_token("ibm,set-eeh-option");
  107. ibm_set_slot_reset = rtas_token("ibm,set-slot-reset");
  108. ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state");
  109. if (ibm_set_eeh_option != RTAS_UNKNOWN_SERVICE && naca->platform == PLATFORM_PSERIES_LPAR)
  110. eeh_implemented = 1;
  111. if (eeh_force_off > eeh_force_on) {
  112. /* User is forcing EEH off.  Be noisy if it is implemented. */
  113. if (eeh_implemented)
  114. printk("EEH: WARNING: PCI Enhanced I/O Error Handling is user disabledn");
  115. eeh_implemented = 0;
  116. return;
  117. }
  118. if (eeh_force_on > eeh_force_off)
  119. eeh_implemented = 1; /* User is forcing it on. */
  120. if (eeh_implemented)
  121. printk("EEH: PCI Enhanced I/O Error Handling Enabledn");
  122. }
  123. /* Given a PCI device check if eeh should be configured or not.
  124.  * This may look at firmware properties and/or kernel cmdline options.
  125.  */
  126. int is_eeh_configured(struct pci_dev *dev)
  127. {
  128. struct device_node *dn = pci_device_to_OF_node(dev);
  129. struct pci_controller *phb = PCI_GET_PHB_PTR(dev);
  130. unsigned long ret, rets[2];
  131. int eeh_capable;
  132. int default_state = 1; /* default enable EEH if we can. */
  133. if (dn == NULL || phb == NULL || !eeh_implemented)
  134. return 0;
  135. /* Hack: turn off eeh for display class devices by default.
  136.  * This fixes matrox accel framebuffer.
  137.  */
  138. if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
  139. default_state = 0;
  140. /* Ignore known PHBs and EADs bridges */
  141. if (dev->vendor == PCI_VENDOR_ID_IBM &&
  142.     (dev->device == 0x0102 || dev->device == 0x008b))
  143. default_state = 0;
  144. if (!eeh_check_opts_config(dev, default_state)) {
  145. if (default_state)
  146. printk("EEH: %s %s user requested to run without EEH.n", dev->slot_name, dev->name);
  147. return 0;
  148. }
  149. ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
  150. CONFIG_ADDR(dn->busno, dn->devfn),
  151. BUID_HI(phb->buid), BUID_LO(phb->buid));
  152. eeh_capable = (ret == 0 && rets[1] == 1);
  153. printk("EEH: %s %s is%s EEH capable.n", dev->slot_name, dev->name, eeh_capable ? "" : " not");
  154. return eeh_capable;
  155. }
  156. int eeh_set_option(struct pci_dev *dev, int option)
  157. {
  158. struct device_node *dn = pci_device_to_OF_node(dev);
  159. struct pci_controller *phb = PCI_GET_PHB_PTR(dev);
  160. if (dn == NULL || phb == NULL || phb->buid == 0 || !eeh_implemented)
  161. return -2;
  162. return rtas_call(ibm_set_eeh_option, 4, 1, NULL,
  163.  CONFIG_ADDR(dn->busno, dn->devfn),
  164.  BUID_HI(phb->buid), BUID_LO(phb->buid), option);
  165. }
  166. static int eeh_proc_falsepositive_read(char *page, char **start, off_t off,
  167.  int count, int *eof, void *data)
  168. {
  169. int len;
  170. len = sprintf(page, "eeh_false_positives=%ldn"
  171.       "eeh_total_mmio_ffs=%ldn",
  172.       eeh_false_positives, eeh_total_mmio_ffs);
  173. return len;
  174. }
  175. /* Implementation of /proc/ppc64/eeh
  176.  * For now it is one file showing false positives.
  177.  */
  178. static int __init eeh_init_proc(void)
  179. {
  180. struct proc_dir_entry *ent = create_proc_entry("ppc64/eeh", S_IRUGO, 0);
  181. if (ent) {
  182. ent->nlink = 1;
  183. ent->data = NULL;
  184. ent->read_proc = (void *)eeh_proc_falsepositive_read;
  185. }
  186. return 0;
  187. }
  188. /*
  189.  * Test if "dev" should be configured on or off.
  190.  * This processes the options literally from left to right.
  191.  * This lets the user specify stupid combinations of options,
  192.  * but at least the result should be very predictable.
  193.  */
  194. static int eeh_check_opts_config(struct pci_dev *dev, int default_state)
  195. {
  196. struct device_node *dn = pci_device_to_OF_node(dev);
  197. struct pci_controller *phb = PCI_GET_PHB_PTR(dev);
  198. char devname[32], classname[32], phbname[32];
  199. char *strs[8], *s;
  200. int nstrs, i;
  201. int ret = default_state;
  202. if (dn == NULL || phb == NULL)
  203. return 0;
  204. /* Build list of strings to match */
  205. nstrs = 0;
  206. s = (char *)get_property(dn, "ibm,loc-code", 0);
  207. if (s)
  208. strs[nstrs++] = s;
  209. sprintf(devname, "dev%04x:%04x", dev->vendor, dev->device);
  210. strs[nstrs++] = devname;
  211. sprintf(classname, "class%04x", dev->class);
  212. strs[nstrs++] = classname;
  213. sprintf(phbname, "pci@%lx", phb->buid);
  214. strs[nstrs++] = phbname;
  215. strs[nstrs++] = ""; /* yes, this matches the empty string */
  216. /* Now see if any string matches the eeh_opts list.
  217.  * The eeh_opts list entries start with + or -.
  218.  */
  219. for (s = eeh_opts; s && (s < (eeh_opts + eeh_opts_last)); s += strlen(s)+1) {
  220. for (i = 0; i < nstrs; i++) {
  221. if (strcasecmp(strs[i], s+1) == 0) {
  222. ret = (strs[i][0] == '+') ? 1 : 0;
  223. }
  224. }
  225. }
  226. return ret;
  227. }
  228. /* Handle kernel eeh-on & eeh-off cmd line options for eeh.
  229.  *
  230.  * We support:
  231.  * eeh-off=loc1,loc2,loc3...
  232.  *
  233.  * and this option can be repeated so
  234.  *      eeh-off=loc1,loc2 eeh-off=loc3
  235.  * is the same as eeh-off=loc1,loc2,loc3
  236.  *
  237.  * loc is an IBM location code that can be found in a manual or
  238.  * via openfirmware (or the Hardware Management Console).
  239.  *
  240.  * We also support these additional "loc" values:
  241.  *
  242.  * dev#:#    vendor:device id in hex (e.g. dev1022:2000)
  243.  * class#    class id in hex (e.g. class0200)
  244.  * pci@buid  all devices under phb (e.g. pci@fef00000)
  245.  *
  246.  * If no location code is specified all devices are assumed
  247.  * so eeh-off means eeh by default is off.
  248.  */
  249. /* This is implemented as a null separated list of strings.
  250.  * Each string looks like this:  "+X" or "-X"
  251.  * where X is a loc code, dev, class or pci string (as shown above)
  252.  * or empty which is used to indicate all.
  253.  *
  254.  * We interpret this option string list during the buswalk
  255.  * so that it will literally behave left-to-right even if
  256.  * some combinations don't make sense.  Give the user exactly
  257.  * what they want! :)
  258.  */
  259. static int __init eeh_parm(char *str, int state)
  260. {
  261. char *s, *cur, *curend;
  262. if (!eeh_opts) {
  263. eeh_opts = alloc_bootmem(EEH_MAX_OPTS);
  264. eeh_opts[eeh_opts_last++] = '+'; /* default */
  265. eeh_opts[eeh_opts_last++] = '';
  266. }
  267. if (*str == '') {
  268. eeh_opts[eeh_opts_last++] = state ? '+' : '-';
  269. eeh_opts[eeh_opts_last++] = '';
  270. return 1;
  271. }
  272. if (*str == '=')
  273. str++;
  274. for (s = str; s && *s != ''; s = curend) {
  275. cur = s;
  276. while (*cur == ',')
  277. cur++; /* ignore empties.  Don't treat as "all-on" or "all-off" */
  278. curend = strchr(cur, ',');
  279. if (!curend)
  280. curend = cur + strlen(cur);
  281. if (*cur) {
  282. int curlen = curend-cur;
  283. if (eeh_opts_last + curlen > EEH_MAX_OPTS-2) {
  284. printk("EEH: sorry...too many eeh cmd line optionsn");
  285. return 1;
  286. }
  287. eeh_opts[eeh_opts_last++] = state ? '+' : '-';
  288. strncpy(eeh_opts+eeh_opts_last, cur, curlen);
  289. eeh_opts_last += curlen;
  290. eeh_opts[eeh_opts_last++] = '';
  291. }
  292. }
  293. return 1;
  294. }
  295. static int __init eehoff_parm(char *str)
  296. {
  297. return eeh_parm(str, 0);
  298. }
  299. static int __init eehon_parm(char *str)
  300. {
  301. return eeh_parm(str, 1);
  302. }
  303. __initcall(eeh_init_proc);
  304. __setup("eeh-off", eehoff_parm);
  305. __setup("eeh-on", eehon_parm);