linux/arch/mips/loongson64/common/cs5536/cs5536_isa.c
<<
>>
Prefs
   1/*
   2 * the ISA Virtual Support Module of AMD CS5536
   3 *
   4 * Copyright (C) 2007 Lemote, Inc.
   5 * Author : jlliu, liujl@lemote.com
   6 *
   7 * Copyright (C) 2009 Lemote, Inc.
   8 * Author: Wu Zhangjin, wuzhangjin@gmail.com
   9 *
  10 * This program is free software; you can redistribute  it and/or modify it
  11 * under  the terms of  the GNU General  Public License as published by the
  12 * Free Software Foundation;  either version 2 of the  License, or (at your
  13 * option) any later version.
  14 */
  15
  16#include <linux/pci.h>
  17#include <cs5536/cs5536.h>
  18#include <cs5536/cs5536_pci.h>
  19
  20/* common variables for PCI_ISA_READ/WRITE_BAR */
  21static const u32 divil_msr_reg[6] = {
  22        DIVIL_MSR_REG(DIVIL_LBAR_SMB), DIVIL_MSR_REG(DIVIL_LBAR_GPIO),
  23        DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), DIVIL_MSR_REG(DIVIL_LBAR_IRQ),
  24        DIVIL_MSR_REG(DIVIL_LBAR_PMS), DIVIL_MSR_REG(DIVIL_LBAR_ACPI),
  25};
  26
  27static const u32 soft_bar_flag[6] = {
  28        SOFT_BAR_SMB_FLAG, SOFT_BAR_GPIO_FLAG, SOFT_BAR_MFGPT_FLAG,
  29        SOFT_BAR_IRQ_FLAG, SOFT_BAR_PMS_FLAG, SOFT_BAR_ACPI_FLAG,
  30};
  31
  32static const u32 sb_msr_reg[6] = {
  33        SB_MSR_REG(SB_R0), SB_MSR_REG(SB_R1), SB_MSR_REG(SB_R2),
  34        SB_MSR_REG(SB_R3), SB_MSR_REG(SB_R4), SB_MSR_REG(SB_R5),
  35};
  36
  37static const u32 bar_space_range[6] = {
  38        CS5536_SMB_RANGE, CS5536_GPIO_RANGE, CS5536_MFGPT_RANGE,
  39        CS5536_IRQ_RANGE, CS5536_PMS_RANGE, CS5536_ACPI_RANGE,
  40};
  41
  42static const int bar_space_len[6] = {
  43        CS5536_SMB_LENGTH, CS5536_GPIO_LENGTH, CS5536_MFGPT_LENGTH,
  44        CS5536_IRQ_LENGTH, CS5536_PMS_LENGTH, CS5536_ACPI_LENGTH,
  45};
  46
  47/*
  48 * enable the divil module bar space.
  49 *
  50 * For all the DIVIL module LBAR, you should control the DIVIL LBAR reg
  51 * and the RCONFx(0~5) reg to use the modules.
  52 */
  53static void divil_lbar_enable(void)
  54{
  55        u32 hi, lo;
  56        int offset;
  57
  58        /*
  59         * The DIVIL IRQ is not used yet. and make the RCONF0 reserved.
  60         */
  61
  62        for (offset = DIVIL_LBAR_SMB; offset <= DIVIL_LBAR_PMS; offset++) {
  63                _rdmsr(DIVIL_MSR_REG(offset), &hi, &lo);
  64                hi |= 0x01;
  65                _wrmsr(DIVIL_MSR_REG(offset), hi, lo);
  66        }
  67}
  68
  69/*
  70 * disable the divil module bar space.
  71 */
  72static void divil_lbar_disable(void)
  73{
  74        u32 hi, lo;
  75        int offset;
  76
  77        for (offset = DIVIL_LBAR_SMB; offset <= DIVIL_LBAR_PMS; offset++) {
  78                _rdmsr(DIVIL_MSR_REG(offset), &hi, &lo);
  79                hi &= ~0x01;
  80                _wrmsr(DIVIL_MSR_REG(offset), hi, lo);
  81        }
  82}
  83
  84/*
  85 * BAR write: write value to the n BAR
  86 */
  87
  88void pci_isa_write_bar(int n, u32 value)
  89{
  90        u32 hi = 0, lo = value;
  91
  92        if (value == PCI_BAR_RANGE_MASK) {
  93                _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
  94                lo |= soft_bar_flag[n];
  95                _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
  96        } else if (value & 0x01) {
  97                /* NATIVE reg */
  98                hi = 0x0000f001;
  99                lo &= bar_space_range[n];
 100                _wrmsr(divil_msr_reg[n], hi, lo);
 101
 102                /* RCONFx is 4bytes in units for I/O space */
 103                hi = ((value & 0x000ffffc) << 12) |
 104                    ((bar_space_len[n] - 4) << 12) | 0x01;
 105                lo = ((value & 0x000ffffc) << 12) | 0x01;
 106                _wrmsr(sb_msr_reg[n], hi, lo);
 107        }
 108}
 109
 110/*
 111 * BAR read: read the n BAR
 112 */
 113
 114u32 pci_isa_read_bar(int n)
 115{
 116        u32 conf_data = 0;
 117        u32 hi, lo;
 118
 119        _rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
 120        if (lo & soft_bar_flag[n]) {
 121                conf_data = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO;
 122                lo &= ~soft_bar_flag[n];
 123                _wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
 124        } else {
 125                _rdmsr(divil_msr_reg[n], &hi, &lo);
 126                conf_data = lo & bar_space_range[n];
 127                conf_data |= 0x01;
 128                conf_data &= ~0x02;
 129        }
 130        return conf_data;
 131}
 132
 133/*
 134 * isa_write: ISA write transfer
 135 *
 136 * We assume that this is not a bus master transfer.
 137 */
 138void pci_isa_write_reg(int reg, u32 value)
 139{
 140        u32 hi = 0, lo = value;
 141        u32 temp;
 142
 143        switch (reg) {
 144        case PCI_COMMAND:
 145                if (value & PCI_COMMAND_IO)
 146                        divil_lbar_enable();
 147                else
 148                        divil_lbar_disable();
 149                break;
 150        case PCI_STATUS:
 151                _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
 152                temp = lo & 0x0000ffff;
 153                if ((value & PCI_STATUS_SIG_TARGET_ABORT) &&
 154                    (lo & SB_TAS_ERR_EN))
 155                        temp |= SB_TAS_ERR_FLAG;
 156
 157                if ((value & PCI_STATUS_REC_TARGET_ABORT) &&
 158                    (lo & SB_TAR_ERR_EN))
 159                        temp |= SB_TAR_ERR_FLAG;
 160
 161                if ((value & PCI_STATUS_REC_MASTER_ABORT)
 162                    && (lo & SB_MAR_ERR_EN))
 163                        temp |= SB_MAR_ERR_FLAG;
 164
 165                if ((value & PCI_STATUS_DETECTED_PARITY)
 166                    && (lo & SB_PARE_ERR_EN))
 167                        temp |= SB_PARE_ERR_FLAG;
 168
 169                lo = temp;
 170                _wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
 171                break;
 172        case PCI_CACHE_LINE_SIZE:
 173                value &= 0x0000ff00;
 174                _rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
 175                hi &= 0xffffff00;
 176                hi |= (value >> 8);
 177                _wrmsr(SB_MSR_REG(SB_CTRL), hi, lo);
 178                break;
 179        case PCI_BAR0_REG:
 180                pci_isa_write_bar(0, value);
 181                break;
 182        case PCI_BAR1_REG:
 183                pci_isa_write_bar(1, value);
 184                break;
 185        case PCI_BAR2_REG:
 186                pci_isa_write_bar(2, value);
 187                break;
 188        case PCI_BAR3_REG:
 189                pci_isa_write_bar(3, value);
 190                break;
 191        case PCI_BAR4_REG:
 192                pci_isa_write_bar(4, value);
 193                break;
 194        case PCI_BAR5_REG:
 195                pci_isa_write_bar(5, value);
 196                break;
 197        case PCI_UART1_INT_REG:
 198                _rdmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), &hi, &lo);
 199                /* disable uart1 interrupt in PIC */
 200                lo &= ~(0xf << 24);
 201                if (value)      /* enable uart1 interrupt in PIC */
 202                        lo |= (CS5536_UART1_INTR << 24);
 203                _wrmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), hi, lo);
 204                break;
 205        case PCI_UART2_INT_REG:
 206                _rdmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), &hi, &lo);
 207                /* disable uart2 interrupt in PIC */
 208                lo &= ~(0xf << 28);
 209                if (value)      /* enable uart2 interrupt in PIC */
 210                        lo |= (CS5536_UART2_INTR << 28);
 211                _wrmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), hi, lo);
 212                break;
 213        case PCI_ISA_FIXUP_REG:
 214                if (value) {
 215                        /* enable the TARGET ABORT/MASTER ABORT etc. */
 216                        _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
 217                        lo |= 0x00000063;
 218                        _wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
 219                }
 220
 221        default:
 222                /* ALL OTHER PCI CONFIG SPACE HEADER IS NOT IMPLEMENTED. */
 223                break;
 224        }
 225}
 226
 227/*
 228 * isa_read: ISA read transfers
 229 *
 230 * We assume that this is not a bus master transfer.
 231 */
 232u32 pci_isa_read_reg(int reg)
 233{
 234        u32 conf_data = 0;
 235        u32 hi, lo;
 236
 237        switch (reg) {
 238        case PCI_VENDOR_ID:
 239                conf_data =
 240                    CFG_PCI_VENDOR_ID(CS5536_ISA_DEVICE_ID, CS5536_VENDOR_ID);
 241                break;
 242        case PCI_COMMAND:
 243                /* we just check the first LBAR for the IO enable bit, */
 244                /* maybe we should changed later. */
 245                _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_SMB), &hi, &lo);
 246                if (hi & 0x01)
 247                        conf_data |= PCI_COMMAND_IO;
 248                break;
 249        case PCI_STATUS:
 250                conf_data |= PCI_STATUS_66MHZ;
 251                conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
 252                conf_data |= PCI_STATUS_FAST_BACK;
 253
 254                _rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
 255                if (lo & SB_TAS_ERR_FLAG)
 256                        conf_data |= PCI_STATUS_SIG_TARGET_ABORT;
 257                if (lo & SB_TAR_ERR_FLAG)
 258                        conf_data |= PCI_STATUS_REC_TARGET_ABORT;
 259                if (lo & SB_MAR_ERR_FLAG)
 260                        conf_data |= PCI_STATUS_REC_MASTER_ABORT;
 261                if (lo & SB_PARE_ERR_FLAG)
 262                        conf_data |= PCI_STATUS_DETECTED_PARITY;
 263                break;
 264        case PCI_CLASS_REVISION:
 265                _rdmsr(GLCP_MSR_REG(GLCP_CHIP_REV_ID), &hi, &lo);
 266                conf_data = lo & 0x000000ff;
 267                conf_data |= (CS5536_ISA_CLASS_CODE << 8);
 268                break;
 269        case PCI_CACHE_LINE_SIZE:
 270                _rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
 271                hi &= 0x000000f8;
 272                conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi);
 273                break;
 274                /*
 275                 * we only use the LBAR of DIVIL, no RCONF used.
 276                 * all of them are IO space.
 277                 */
 278        case PCI_BAR0_REG:
 279                return pci_isa_read_bar(0);
 280                break;
 281        case PCI_BAR1_REG:
 282                return pci_isa_read_bar(1);
 283                break;
 284        case PCI_BAR2_REG:
 285                return pci_isa_read_bar(2);
 286                break;
 287        case PCI_BAR3_REG:
 288                break;
 289        case PCI_BAR4_REG:
 290                return pci_isa_read_bar(4);
 291                break;
 292        case PCI_BAR5_REG:
 293                return pci_isa_read_bar(5);
 294                break;
 295        case PCI_CARDBUS_CIS:
 296                conf_data = PCI_CARDBUS_CIS_POINTER;
 297                break;
 298        case PCI_SUBSYSTEM_VENDOR_ID:
 299                conf_data =
 300                    CFG_PCI_VENDOR_ID(CS5536_ISA_SUB_ID, CS5536_SUB_VENDOR_ID);
 301                break;
 302        case PCI_ROM_ADDRESS:
 303                conf_data = PCI_EXPANSION_ROM_BAR;
 304                break;
 305        case PCI_CAPABILITY_LIST:
 306                conf_data = PCI_CAPLIST_POINTER;
 307                break;
 308        case PCI_INTERRUPT_LINE:
 309                /* no interrupt used here */
 310                conf_data = CFG_PCI_INTERRUPT_LINE(0x00, 0x00);
 311                break;
 312        default:
 313                break;
 314        }
 315
 316        return conf_data;
 317}
 318
 319/*
 320 * The mfgpt timer interrupt is running early, so we must keep the south bridge
 321 * mmio always enabled. Otherwise we may race with the PCI configuration which
 322 * may temporarily disable it. When that happens and the timer interrupt fires,
 323 * we are not able to clear it and the system will hang.
 324 */
 325static void cs5536_isa_mmio_always_on(struct pci_dev *dev)
 326{
 327        dev->mmio_always_on = 1;
 328}
 329DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
 330        PCI_CLASS_BRIDGE_ISA, 8, cs5536_isa_mmio_always_on);
 331