linux/drivers/scsi/aic94xx/aic94xx_reg.c
<<
>>
Prefs
   1/*
   2 * Aic94xx SAS/SATA driver register access.
   3 *
   4 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
   5 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
   6 *
   7 * This file is licensed under GPLv2.
   8 *
   9 * This file is part of the aic94xx driver.
  10 *
  11 * The aic94xx driver is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License as
  13 * published by the Free Software Foundation; version 2 of the
  14 * License.
  15 *
  16 * The aic94xx driver is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19 * General Public License for more details.
  20 *
  21 * You should have received a copy of the GNU General Public License
  22 * along with the aic94xx driver; if not, write to the Free Software
  23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  24 *
  25 */
  26
  27#include <linux/pci.h>
  28#include "aic94xx_reg.h"
  29#include "aic94xx.h"
  30
  31/* Writing to device address space.
  32 * Offset comes before value to remind that the operation of
  33 * this function is *offs = val.
  34 */
  35static void asd_write_byte(struct asd_ha_struct *asd_ha,
  36                           unsigned long offs, u8 val)
  37{
  38        if (unlikely(asd_ha->iospace))
  39                outb(val,
  40                     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
  41        else
  42                writeb(val, asd_ha->io_handle[0].addr + offs);
  43        wmb();
  44}
  45
  46static void asd_write_word(struct asd_ha_struct *asd_ha,
  47                           unsigned long offs, u16 val)
  48{
  49        if (unlikely(asd_ha->iospace))
  50                outw(val,
  51                     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
  52        else
  53                writew(val, asd_ha->io_handle[0].addr + offs);
  54        wmb();
  55}
  56
  57static void asd_write_dword(struct asd_ha_struct *asd_ha,
  58                            unsigned long offs, u32 val)
  59{
  60        if (unlikely(asd_ha->iospace))
  61                outl(val,
  62                     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
  63        else
  64                writel(val, asd_ha->io_handle[0].addr + offs);
  65        wmb();
  66}
  67
  68/* Reading from device address space.
  69 */
  70static u8 asd_read_byte(struct asd_ha_struct *asd_ha, unsigned long offs)
  71{
  72        u8 val;
  73        if (unlikely(asd_ha->iospace))
  74                val = inb((unsigned long) asd_ha->io_handle[0].addr
  75                          + (offs & 0xFF));
  76        else
  77                val = readb(asd_ha->io_handle[0].addr + offs);
  78        rmb();
  79        return val;
  80}
  81
  82static u16 asd_read_word(struct asd_ha_struct *asd_ha,
  83                         unsigned long offs)
  84{
  85        u16 val;
  86        if (unlikely(asd_ha->iospace))
  87                val = inw((unsigned long)asd_ha->io_handle[0].addr
  88                          + (offs & 0xFF));
  89        else
  90                val = readw(asd_ha->io_handle[0].addr + offs);
  91        rmb();
  92        return val;
  93}
  94
  95static u32 asd_read_dword(struct asd_ha_struct *asd_ha,
  96                          unsigned long offs)
  97{
  98        u32 val;
  99        if (unlikely(asd_ha->iospace))
 100                val = inl((unsigned long) asd_ha->io_handle[0].addr
 101                          + (offs & 0xFF));
 102        else
 103                val = readl(asd_ha->io_handle[0].addr + offs);
 104        rmb();
 105        return val;
 106}
 107
 108static inline u32 asd_mem_offs_swa(void)
 109{
 110        return 0;
 111}
 112
 113static inline u32 asd_mem_offs_swc(void)
 114{
 115        return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
 116}
 117
 118static inline u32 asd_mem_offs_swb(void)
 119{
 120        return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
 121}
 122
 123/* We know that the register wanted is in the range
 124 * of the sliding window.
 125 */
 126#define ASD_READ_SW(ww, type, ord)                                      \
 127static type asd_read_##ww##_##ord(struct asd_ha_struct *asd_ha,         \
 128                                   u32 reg)                             \
 129{                                                                       \
 130        struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];     \
 131        u32 map_offs = (reg - io_handle->ww##_base) + asd_mem_offs_##ww();\
 132        return asd_read_##ord(asd_ha, (unsigned long)map_offs); \
 133}
 134
 135#define ASD_WRITE_SW(ww, type, ord)                                     \
 136static void asd_write_##ww##_##ord(struct asd_ha_struct *asd_ha,        \
 137                                    u32 reg, type val)                  \
 138{                                                                       \
 139        struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];     \
 140        u32 map_offs = (reg - io_handle->ww##_base) + asd_mem_offs_##ww();\
 141        asd_write_##ord(asd_ha, (unsigned long)map_offs, val);          \
 142}
 143
 144ASD_READ_SW(swa, u8,  byte);
 145ASD_READ_SW(swa, u16, word);
 146ASD_READ_SW(swa, u32, dword);
 147
 148ASD_READ_SW(swb, u8,  byte);
 149ASD_READ_SW(swb, u16, word);
 150ASD_READ_SW(swb, u32, dword);
 151
 152ASD_READ_SW(swc, u8,  byte);
 153ASD_READ_SW(swc, u16, word);
 154ASD_READ_SW(swc, u32, dword);
 155
 156ASD_WRITE_SW(swa, u8,  byte);
 157ASD_WRITE_SW(swa, u16, word);
 158ASD_WRITE_SW(swa, u32, dword);
 159
 160ASD_WRITE_SW(swb, u8,  byte);
 161ASD_WRITE_SW(swb, u16, word);
 162ASD_WRITE_SW(swb, u32, dword);
 163
 164ASD_WRITE_SW(swc, u8,  byte);
 165ASD_WRITE_SW(swc, u16, word);
 166ASD_WRITE_SW(swc, u32, dword);
 167
 168/*
 169 * A word about sliding windows:
 170 * MBAR0 is divided into sliding windows A, C and B, in that order.
 171 * SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
 172 * SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
 173 * From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
 174 * SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
 175 * See asd_init_sw() in aic94xx_hwi.c
 176 *
 177 * We map the most common registers we'd access of the internal 4GB
 178 * host adapter memory space.  If a register/internal memory location
 179 * is wanted which is not mapped, we slide SWB, by paging it,
 180 * see asd_move_swb() in aic94xx_reg.c.
 181 */
 182
 183/**
 184 * asd_move_swb -- move sliding window B
 185 * @asd_ha: pointer to host adapter structure
 186 * @reg: register desired to be within range of the new window
 187 */
 188static void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
 189{
 190        u32 base = reg & ~(MBAR0_SWB_SIZE-1);
 191        pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
 192        asd_ha->io_handle[0].swb_base = base;
 193}
 194
 195static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
 196{
 197        struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
 198        BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
 199        if (io_handle->swa_base <= reg
 200            && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
 201                asd_write_swa_byte (asd_ha, reg,val);
 202        else if (io_handle->swb_base <= reg
 203                 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
 204                asd_write_swb_byte (asd_ha, reg, val);
 205        else if (io_handle->swc_base <= reg
 206                 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
 207                asd_write_swc_byte (asd_ha, reg, val);
 208        else {
 209                /* Ok, we have to move SWB */
 210                asd_move_swb(asd_ha, reg);
 211                asd_write_swb_byte (asd_ha, reg, val);
 212        }
 213}
 214
 215#define ASD_WRITE_REG(type, ord)                                  \
 216void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
 217{                                                                 \
 218        struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
 219        unsigned long flags;                                      \
 220        BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
 221        spin_lock_irqsave(&asd_ha->iolock, flags);                \
 222        if (io_handle->swa_base <= reg                            \
 223            && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
 224                asd_write_swa_##ord (asd_ha, reg,val);            \
 225        else if (io_handle->swb_base <= reg                       \
 226                 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
 227                asd_write_swb_##ord (asd_ha, reg, val);           \
 228        else if (io_handle->swc_base <= reg                       \
 229                 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
 230                asd_write_swc_##ord (asd_ha, reg, val);           \
 231        else {                                                    \
 232                /* Ok, we have to move SWB */                     \
 233                asd_move_swb(asd_ha, reg);                        \
 234                asd_write_swb_##ord (asd_ha, reg, val);           \
 235        }                                                         \
 236        spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
 237}
 238
 239ASD_WRITE_REG(u8, byte);
 240ASD_WRITE_REG(u16,word);
 241ASD_WRITE_REG(u32,dword);
 242
 243static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
 244{
 245        struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
 246        u8 val;
 247        BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
 248        if (io_handle->swa_base <= reg
 249            && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
 250                val = asd_read_swa_byte (asd_ha, reg);
 251        else if (io_handle->swb_base <= reg
 252                 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
 253                val = asd_read_swb_byte (asd_ha, reg);
 254        else if (io_handle->swc_base <= reg
 255                 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
 256                val = asd_read_swc_byte (asd_ha, reg);
 257        else {
 258                /* Ok, we have to move SWB */
 259                asd_move_swb(asd_ha, reg);
 260                val = asd_read_swb_byte (asd_ha, reg);
 261        }
 262        return val;
 263}
 264
 265#define ASD_READ_REG(type, ord)                                   \
 266type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg)   \
 267{                                                                 \
 268        struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
 269        type val;                                                 \
 270        unsigned long flags;                                      \
 271        BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
 272        spin_lock_irqsave(&asd_ha->iolock, flags);                \
 273        if (io_handle->swa_base <= reg                            \
 274            && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
 275                val = asd_read_swa_##ord (asd_ha, reg);           \
 276        else if (io_handle->swb_base <= reg                       \
 277                 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
 278                val = asd_read_swb_##ord (asd_ha, reg);           \
 279        else if (io_handle->swc_base <= reg                       \
 280                 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
 281                val = asd_read_swc_##ord (asd_ha, reg);           \
 282        else {                                                    \
 283                /* Ok, we have to move SWB */                     \
 284                asd_move_swb(asd_ha, reg);                        \
 285                val = asd_read_swb_##ord (asd_ha, reg);           \
 286        }                                                         \
 287        spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
 288        return val;                                               \
 289}
 290
 291ASD_READ_REG(u8, byte);
 292ASD_READ_REG(u16,word);
 293ASD_READ_REG(u32,dword);
 294
 295/**
 296 * asd_read_reg_string -- read a string of bytes from io space memory
 297 * @asd_ha: pointer to host adapter structure
 298 * @dst: pointer to a destination buffer where data will be written to
 299 * @offs: start offset (register) to read from
 300 * @count: number of bytes to read
 301 */
 302void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
 303                         u32 offs, int count)
 304{
 305        u8 *p = dst;
 306        unsigned long flags;
 307
 308        spin_lock_irqsave(&asd_ha->iolock, flags);
 309        for ( ; count > 0; count--, offs++, p++)
 310                *p = __asd_read_reg_byte(asd_ha, offs);
 311        spin_unlock_irqrestore(&asd_ha->iolock, flags);
 312}
 313
 314/**
 315 * asd_write_reg_string -- write a string of bytes to io space memory
 316 * @asd_ha: pointer to host adapter structure
 317 * @src: pointer to source buffer where data will be read from
 318 * @offs: start offset (register) to write to
 319 * @count: number of bytes to write
 320 */
 321void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
 322                          u32 offs, int count)
 323{
 324        u8 *p = src;
 325        unsigned long flags;
 326
 327        spin_lock_irqsave(&asd_ha->iolock, flags);
 328        for ( ; count > 0; count--, offs++, p++)
 329                __asd_write_reg_byte(asd_ha, offs, *p);
 330        spin_unlock_irqrestore(&asd_ha->iolock, flags);
 331}
 332