1806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/* 2806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * AMD 76x Memory Controller kernel module 3806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * (C) 2003 Linux Networx (http://lnxi.com) 4806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * This file may be distributed under the terms of the 5806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * GNU General Public License. 6806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 7806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * Written by Thayne Harbaugh 8806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * Based on work by Dan Hollis <goemon at anime dot net> and others. 9806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * http://www.anime.net/~goemon/linux-ecc/ 10806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 11806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * $Id: edac_amd76x.c,v 1.4.2.5 2005/10/05 00:43:44 dsp_llnl Exp $ 12806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 13806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 14806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 15806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#include <linux/module.h> 16806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#include <linux/init.h> 17806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#include <linux/pci.h> 18806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#include <linux/pci_ids.h> 19c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi Mitake#include <linux/edac.h> 2020bcb7a81dee21bfa3408f03f46b2891c9b5c84bDouglas Thompson#include "edac_core.h" 21806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 22152ba3942276c2a240703669ae4a3099e0a79451Michal Marek#define AMD76X_REVISION " Ver: 2.0.2" 23929a40ec324e947d4ad14cc1ced785c104c560e2Doug Thompson#define EDAC_MOD_STR "amd76x_edac" 2437f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson 25537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson#define amd76x_printk(level, fmt, arg...) \ 26e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson edac_printk(level, "amd76x", fmt, ##arg) 27537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson 28537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson#define amd76x_mc_printk(mci, level, fmt, arg...) \ 29e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson edac_mc_chipset_printk(mci, level, "amd76x", fmt, ##arg) 30537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson 31806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#define AMD76X_NR_CSROWS 8 32806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#define AMD76X_NR_CHANS 1 33806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#define AMD76X_NR_DIMMS 4 34806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 35806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/* AMD 76x register addresses - device 0 function 0 - PCI bridge */ 36e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson 37806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#define AMD76X_ECC_MODE_STATUS 0x48 /* Mode and status of ECC (32b) 38806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 39806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 31:16 reserved 40806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 15:14 SERR enabled: x1=ue 1x=ce 41806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 13 reserved 42806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 12 diag: disabled, enabled 43806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 11:10 mode: dis, EC, ECC, ECC+scrub 44806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 9:8 status: x1=ue 1x=ce 45806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 7:4 UE cs row 46806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 3:0 CE cs row 47806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 48e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson 49806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#define AMD76X_DRAM_MODE_STATUS 0x58 /* DRAM Mode and status (32b) 50806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 51806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 31:26 clock disable 5 - 0 52806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 25 SDRAM init 53806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 24 reserved 54806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 23 mode register service 55806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 22:21 suspend to RAM 56806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 20 burst refresh enable 57806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 19 refresh disable 58806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 18 reserved 59806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 17:16 cycles-per-refresh 60806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 15:8 reserved 61806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 7:0 x4 mode enable 7 - 0 62806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 63e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson 64806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox#define AMD76X_MEM_BASE_ADDR 0xC0 /* Memory base address (8 x 32b) 65806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 66806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 31:23 chip-select base 67806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 22:16 reserved 68806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 15:7 chip-select mask 69806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 6:3 reserved 70806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 2:1 address mode 71806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 0 chip-select enable 72806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 73806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 74806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstruct amd76x_error_info { 75806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox u32 ecc_mode_status; 76806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox}; 77806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 78806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxenum amd76x_chips { 79806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox AMD761 = 0, 80806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox AMD762 81806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox}; 82806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 83806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstruct amd76x_dev_info { 84806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox const char *ctl_name; 85806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox}; 86806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 87806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic const struct amd76x_dev_info amd76x_devs[] = { 88e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson [AMD761] = { 89052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson .ctl_name = "AMD761"}, 90e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson [AMD762] = { 91052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson .ctl_name = "AMD762"}, 92806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox}; 93806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 94456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiangstatic struct edac_pci_ctl_info *amd76x_pci; 95456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang 96806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/** 97806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * amd76x_get_error_info - fetch error information 98806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @mci: Memory controller 99806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @info: Info to fill in 100806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 101806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * Fetch and store the AMD76x ECC status. Clear pending status 102806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * on the chip so that further errors will be reported 103806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 104e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic void amd76x_get_error_info(struct mem_ctl_info *mci, 105052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson struct amd76x_error_info *info) 106806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 10737f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson struct pci_dev *pdev; 10837f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson 10937f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson pdev = to_pci_dev(mci->dev); 11037f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, 111052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson &info->ecc_mode_status); 112806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 113806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (info->ecc_mode_status & BIT(8)) 11437f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson pci_write_bits32(pdev, AMD76X_ECC_MODE_STATUS, 11567cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson (u32) BIT(8), (u32) BIT(8)); 116806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 117806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (info->ecc_mode_status & BIT(9)) 11837f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson pci_write_bits32(pdev, AMD76X_ECC_MODE_STATUS, 11967cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson (u32) BIT(9), (u32) BIT(9)); 120806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 121806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 122806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/** 123806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * amd76x_process_error_info - Error check 124806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @mci: Memory controller 125806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @info: Previously fetched information from chip 126806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @handle_errors: 1 if we should do recovery 127806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 128806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * Process the chip state and decide if an error has occurred. 129806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * A return of 1 indicates an error. Also if handle_errors is true 130806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * then attempt to handle and clean up after the error 131806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 132e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Petersonstatic int amd76x_process_error_info(struct mem_ctl_info *mci, 133052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson struct amd76x_error_info *info, 134052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson int handle_errors) 135806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 136806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox int error_found; 137806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox u32 row; 138806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 139806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox error_found = 0; 140806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 141806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox /* 14267cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson * Check for an uncorrectable error 143806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 144806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (info->ecc_mode_status & BIT(8)) { 145806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox error_found = 1; 146806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 147806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (handle_errors) { 148806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox row = (info->ecc_mode_status >> 4) & 0xf; 149e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson edac_mc_handle_ue(mci, mci->csrows[row].first_page, 0, 150052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson row, mci->ctl_name); 151806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox } 152806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox } 153806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 154806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox /* 15567cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson * Check for a correctable error 156806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 157806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (info->ecc_mode_status & BIT(9)) { 158806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox error_found = 1; 159806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 160806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (handle_errors) { 161806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox row = info->ecc_mode_status & 0xf; 162e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson edac_mc_handle_ce(mci, mci->csrows[row].first_page, 0, 163052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson 0, row, 0, mci->ctl_name); 164806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox } 165806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox } 166e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson 167806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox return error_found; 168806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 169806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 170806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/** 171806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * amd76x_check - Poll the controller 172806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @mci: Memory controller 173806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 174806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * Called by the poll handlers this function reads the status 175806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * from the controller and checks for errors. 176806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 177806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic void amd76x_check(struct mem_ctl_info *mci) 178806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 179806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox struct amd76x_error_info info; 180537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf3("%s()\n", __func__); 181806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox amd76x_get_error_info(mci, &info); 182806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox amd76x_process_error_info(mci, &info, 1); 183806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 184806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 1851318952514d5651c453d89989595a9df3b37267bDoug Thompsonstatic void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, 186052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson enum edac_type edac_mode) 1871318952514d5651c453d89989595a9df3b37267bDoug Thompson{ 1881318952514d5651c453d89989595a9df3b37267bDoug Thompson struct csrow_info *csrow; 1891318952514d5651c453d89989595a9df3b37267bDoug Thompson u32 mba, mba_base, mba_mask, dms; 1901318952514d5651c453d89989595a9df3b37267bDoug Thompson int index; 1911318952514d5651c453d89989595a9df3b37267bDoug Thompson 1921318952514d5651c453d89989595a9df3b37267bDoug Thompson for (index = 0; index < mci->nr_csrows; index++) { 1931318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow = &mci->csrows[index]; 1941318952514d5651c453d89989595a9df3b37267bDoug Thompson 1951318952514d5651c453d89989595a9df3b37267bDoug Thompson /* find the DRAM Chip Select Base address and mask */ 1961318952514d5651c453d89989595a9df3b37267bDoug Thompson pci_read_config_dword(pdev, 197052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson AMD76X_MEM_BASE_ADDR + (index * 4), &mba); 1981318952514d5651c453d89989595a9df3b37267bDoug Thompson 1991318952514d5651c453d89989595a9df3b37267bDoug Thompson if (!(mba & BIT(0))) 2001318952514d5651c453d89989595a9df3b37267bDoug Thompson continue; 2011318952514d5651c453d89989595a9df3b37267bDoug Thompson 2021318952514d5651c453d89989595a9df3b37267bDoug Thompson mba_base = mba & 0xff800000UL; 2031318952514d5651c453d89989595a9df3b37267bDoug Thompson mba_mask = ((mba & 0xff80) << 16) | 0x7fffffUL; 2041318952514d5651c453d89989595a9df3b37267bDoug Thompson pci_read_config_dword(pdev, AMD76X_DRAM_MODE_STATUS, &dms); 2051318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->first_page = mba_base >> PAGE_SHIFT; 2061318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->nr_pages = (mba_mask + 1) >> PAGE_SHIFT; 2071318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->last_page = csrow->first_page + csrow->nr_pages - 1; 2081318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->page_mask = mba_mask >> PAGE_SHIFT; 2091318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->grain = csrow->nr_pages << PAGE_SHIFT; 2101318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->mtype = MEM_RDDR; 2111318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN; 2121318952514d5651c453d89989595a9df3b37267bDoug Thompson csrow->edac_mode = edac_mode; 2131318952514d5651c453d89989595a9df3b37267bDoug Thompson } 2141318952514d5651c453d89989595a9df3b37267bDoug Thompson} 2151318952514d5651c453d89989595a9df3b37267bDoug Thompson 216806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/** 217806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * amd76x_probe1 - Perform set up for detected device 218806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @pdev; PCI device detected 219806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @dev_idx: Device type index 220806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 221806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * We have found an AMD76x and now need to set up the memory 222806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * controller status reporting. We configure and set up the 223806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * memory controller reporting and claim the device. 224806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 225806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic int amd76x_probe1(struct pci_dev *pdev, int dev_idx) 226806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 2271318952514d5651c453d89989595a9df3b37267bDoug Thompson static const enum edac_type ems_modes[] = { 228806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox EDAC_NONE, 229806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox EDAC_EC, 230806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox EDAC_SECDED, 231806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox EDAC_SECDED 232806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox }; 2331318952514d5651c453d89989595a9df3b37267bDoug Thompson struct mem_ctl_info *mci = NULL; 234806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox u32 ems; 235806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox u32 ems_mode; 236749ede57443b2a7ede2db105145f21047efcea6aDave Peterson struct amd76x_error_info discard; 237806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 238537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf0("%s()\n", __func__); 239806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &ems); 240806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox ems_mode = (ems >> 10) & 0x3; 241b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson mci = edac_mc_alloc(0, AMD76X_NR_CSROWS, AMD76X_NR_CHANS, 0); 242806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 243806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox if (mci == NULL) { 2441318952514d5651c453d89989595a9df3b37267bDoug Thompson return -ENOMEM; 245806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox } 246806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 247537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf0("%s(): mci = %p\n", __func__, mci); 24837f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson mci->dev = &pdev->dev; 249806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox mci->mtype_cap = MEM_FLAG_RDDR; 250806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; 251806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox mci->edac_cap = ems_mode ? 252052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson (EDAC_FLAG_EC | EDAC_FLAG_SECDED) : EDAC_FLAG_NONE; 253680cbbbb0e336b04b74be48b8ddd870537f1e226Dave Peterson mci->mod_name = EDAC_MOD_STR; 25437f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson mci->mod_ver = AMD76X_REVISION; 255806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox mci->ctl_name = amd76x_devs[dev_idx].ctl_name; 256c4192705fec85219086231a1c0fa61e8776e2c3bDave Jiang mci->dev_name = pci_name(pdev); 257806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox mci->edac_check = amd76x_check; 258806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox mci->ctl_page_to_phys = NULL; 259806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 2601318952514d5651c453d89989595a9df3b37267bDoug Thompson amd76x_init_csrows(mci, pdev, ems_modes[ems_mode]); 26167cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson amd76x_get_error_info(mci, &discard); /* clear counters */ 262806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 2632d7bbb91c8df26c60d223205a087507430024177Doug Thompson /* Here we assume that we will never see multiple instances of this 2642d7bbb91c8df26c60d223205a087507430024177Doug Thompson * type of memory controller. The ID is therefore hardcoded to 0. 2652d7bbb91c8df26c60d223205a087507430024177Doug Thompson */ 266b8f6f9755248026f21282e25cac49a1af698056cDoug Thompson if (edac_mc_add_mc(mci)) { 267537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf3("%s(): failed edac_mc_add_mc()\n", __func__); 268806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox goto fail; 269806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox } 270806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 271456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang /* allocating generic PCI control info */ 272456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang amd76x_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); 273456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang if (!amd76x_pci) { 274456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang printk(KERN_WARNING 275456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang "%s(): Unable to create PCI control\n", 276456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang __func__); 277456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang printk(KERN_WARNING 278456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang "%s(): PCI error report via EDAC not setup\n", 279456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang __func__); 280456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang } 281456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang 282806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox /* get this far and it's successful */ 283537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf3("%s(): success\n", __func__); 284806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox return 0; 285806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 286052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompsonfail: 2871318952514d5651c453d89989595a9df3b37267bDoug Thompson edac_mc_free(mci); 2881318952514d5651c453d89989595a9df3b37267bDoug Thompson return -ENODEV; 289806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 290806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 291806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/* returns count (>= 0), or negative on error */ 292806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic int __devinit amd76x_init_one(struct pci_dev *pdev, 293052dfb45ccb5ea354a426b52556bcfee75b9d2f5Douglas Thompson const struct pci_device_id *ent) 294806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 295537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf0("%s()\n", __func__); 296806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 297ee6583f6e8f8dad4a53985dbabcd7c242d66a6b6Roman Fietze /* don't need to call pci_enable_device() */ 298806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox return amd76x_probe1(pdev, ent->driver_data); 299806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 300806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 301806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox/** 302806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * amd76x_remove_one - driver shutdown 303806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * @pdev: PCI device being handed back 304806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * 305806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * Called when the driver is unloaded. Find the matching mci 306806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * structure for the device then delete the mci and free the 307806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox * resources. 308806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox */ 309806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic void __devexit amd76x_remove_one(struct pci_dev *pdev) 310806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 311806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox struct mem_ctl_info *mci; 312806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 313537fba28928c01b7db1580627450691a4bb0b9b3Dave Peterson debugf0("%s()\n", __func__); 314806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 315456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang if (amd76x_pci) 316456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang edac_pci_release_generic_ctl(amd76x_pci); 317456a2f9552e7849475f4aea1a9aa4c0e54b3dddaDave Jiang 31837f04581abac20444e5b7106c1e1f28bec5b989cDoug Thompson if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL) 319806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox return; 32018dbc337af5d6efd30cb9291e74722c8ad134fd3Dave Peterson 321806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox edac_mc_free(mci); 322806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 323806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 324806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic const struct pci_device_id amd76x_pci_tbl[] __devinitdata = { 325e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson { 32667cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson PCI_VEND_DEV(AMD, FE_GATE_700C), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 32767cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson AMD762}, 328e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson { 32967cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson PCI_VEND_DEV(AMD, FE_GATE_700E), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 33067cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson AMD761}, 331e7ecd8910293564d357dbaf18eb179e06fa35fd0Dave Peterson { 33267cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson 0, 33367cb2b61225601ef1ead842c7a012bb6da19f847Douglas Thompson } /* 0 terminated list. */ 334806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox}; 335806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 336806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan CoxMODULE_DEVICE_TABLE(pci, amd76x_pci_tbl); 337806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 338806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic struct pci_driver amd76x_driver = { 339680cbbbb0e336b04b74be48b8ddd870537f1e226Dave Peterson .name = EDAC_MOD_STR, 340806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox .probe = amd76x_init_one, 341806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox .remove = __devexit_p(amd76x_remove_one), 342806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox .id_table = amd76x_pci_tbl, 343806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox}; 344806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 345da9bb1d27b21cb24cbb6a2efb5d3c464d357a01eAlan Coxstatic int __init amd76x_init(void) 346806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 347c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi Mitake /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 348c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi Mitake opstate_init(); 349c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi Mitake 350806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox return pci_register_driver(&amd76x_driver); 351806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 352806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 353806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxstatic void __exit amd76x_exit(void) 354806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox{ 355806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox pci_unregister_driver(&amd76x_driver); 356806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox} 357806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 358806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxmodule_init(amd76x_init); 359806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Coxmodule_exit(amd76x_exit); 360806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan Cox 361806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan CoxMODULE_LICENSE("GPL"); 362806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan CoxMODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh"); 363806c35f5057a64d3061ee4e2b1023bf6f6d328e2Alan CoxMODULE_DESCRIPTION("MC support for AMD 76x memory controllers"); 364c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi Mitake 365c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi Mitakemodule_param(edac_op_state, int, 0444); 366c3c52bce6993c6d37af2c2de9b482a7013d646a7Hitoshi MitakeMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 367