aic94xx_reg.c 10.9 KB
Newer Older
J
James Bottomley 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
/*
 * Aic94xx SAS/SATA driver register access.
 *
 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
 *
 * This file is licensed under GPLv2.
 *
 * This file is part of the aic94xx driver.
 *
 * The aic94xx driver 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.
 *
 * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <linux/pci.h>
#include "aic94xx_reg.h"
#include "aic94xx.h"

/* Writing to device address space.
 * Offset comes before value to remind that the operation of
 * this function is *offs = val.
 */
static inline void asd_write_byte(struct asd_ha_struct *asd_ha,
				  unsigned long offs, u8 val)
{
	if (unlikely(asd_ha->iospace))
		outb(val,
		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
	else
		writeb(val, asd_ha->io_handle[0].addr + offs);
	wmb();
}

static inline void asd_write_word(struct asd_ha_struct *asd_ha,
				  unsigned long offs, u16 val)
{
	if (unlikely(asd_ha->iospace))
		outw(val,
		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
	else
		writew(val, asd_ha->io_handle[0].addr + offs);
	wmb();
}

static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
				   unsigned long offs, u32 val)
{
	if (unlikely(asd_ha->iospace))
		outl(val,
		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
	else
		writel(val, asd_ha->io_handle[0].addr + offs);
	wmb();
}

/* Reading from device address space.
 */
static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
			       unsigned long offs)
{
	u8 val;
	if (unlikely(asd_ha->iospace))
		val = inb((unsigned long) asd_ha->io_handle[0].addr
			  + (offs & 0xFF));
	else
		val = readb(asd_ha->io_handle[0].addr + offs);
	rmb();
	return val;
}

static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
				unsigned long offs)
{
	u16 val;
	if (unlikely(asd_ha->iospace))
		val = inw((unsigned long)asd_ha->io_handle[0].addr
			  + (offs & 0xFF));
	else
		val = readw(asd_ha->io_handle[0].addr + offs);
	rmb();
	return val;
}

static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha,
				 unsigned long offs)
{
	u32 val;
	if (unlikely(asd_ha->iospace))
		val = inl((unsigned long) asd_ha->io_handle[0].addr
			  + (offs & 0xFF));
	else
		val = readl(asd_ha->io_handle[0].addr + offs);
	rmb();
	return val;
}

static inline u32 asd_mem_offs_swa(void)
{
	return 0;
}

static inline u32 asd_mem_offs_swc(void)
{
	return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
}

static inline u32 asd_mem_offs_swb(void)
{
	return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
}

/* We know that the register wanted is in the range
 * of the sliding window.
 */
#define ASD_READ_SW(ww, type, ord)                                     \
static inline type asd_read_##ww##_##ord (struct asd_ha_struct *asd_ha,\
					  u32 reg)                     \
{                                                                      \
	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];    \
	u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
	return asd_read_##ord (asd_ha, (unsigned long) map_offs);      \
}

#define ASD_WRITE_SW(ww, type, ord)                                    \
static inline void asd_write_##ww##_##ord (struct asd_ha_struct *asd_ha,\
				  u32 reg, type val)                   \
{                                                                      \
	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];    \
	u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
	asd_write_##ord (asd_ha, (unsigned long) map_offs, val);       \
}

ASD_READ_SW(swa, u8,  byte);
ASD_READ_SW(swa, u16, word);
ASD_READ_SW(swa, u32, dword);

ASD_READ_SW(swb, u8,  byte);
ASD_READ_SW(swb, u16, word);
ASD_READ_SW(swb, u32, dword);

ASD_READ_SW(swc, u8,  byte);
ASD_READ_SW(swc, u16, word);
ASD_READ_SW(swc, u32, dword);

ASD_WRITE_SW(swa, u8,  byte);
ASD_WRITE_SW(swa, u16, word);
ASD_WRITE_SW(swa, u32, dword);

ASD_WRITE_SW(swb, u8,  byte);
ASD_WRITE_SW(swb, u16, word);
ASD_WRITE_SW(swb, u32, dword);

ASD_WRITE_SW(swc, u8,  byte);
ASD_WRITE_SW(swc, u16, word);
ASD_WRITE_SW(swc, u32, dword);

/*
 * A word about sliding windows:
 * MBAR0 is divided into sliding windows A, C and B, in that order.
 * SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
 * SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
 * From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
 * SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
 * See asd_init_sw() in aic94xx_hwi.c
 *
 * We map the most common registers we'd access of the internal 4GB
 * host adapter memory space.  If a register/internal memory location
 * is wanted which is not mapped, we slide SWB, by paging it,
 * see asd_move_swb() in aic94xx_reg.c.
 */

/**
 * asd_move_swb -- move sliding window B
 * @asd_ha: pointer to host adapter structure
 * @reg: register desired to be within range of the new window
 */
static inline void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
{
	u32 base = reg & ~(MBAR0_SWB_SIZE-1);
	pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
	asd_ha->io_handle[0].swb_base = base;
}

static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
{
	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
	if (io_handle->swa_base <= reg
	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
		asd_write_swa_byte (asd_ha, reg,val);
	else if (io_handle->swb_base <= reg
		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
		asd_write_swb_byte (asd_ha, reg, val);
	else if (io_handle->swc_base <= reg
		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
		asd_write_swc_byte (asd_ha, reg, val);
	else {
		/* Ok, we have to move SWB */
		asd_move_swb(asd_ha, reg);
		asd_write_swb_byte (asd_ha, reg, val);
	}
}

#define ASD_WRITE_REG(type, ord)                                  \
void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
{                                                                 \
	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
	unsigned long flags;                                      \
	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
	spin_lock_irqsave(&asd_ha->iolock, flags);                \
	if (io_handle->swa_base <= reg                            \
	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
		asd_write_swa_##ord (asd_ha, reg,val);            \
	else if (io_handle->swb_base <= reg                       \
		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
		asd_write_swb_##ord (asd_ha, reg, val);           \
	else if (io_handle->swc_base <= reg                       \
		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
		asd_write_swc_##ord (asd_ha, reg, val);           \
	else {                                                    \
		/* Ok, we have to move SWB */                     \
		asd_move_swb(asd_ha, reg);                        \
		asd_write_swb_##ord (asd_ha, reg, val);           \
	}                                                         \
	spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
}

ASD_WRITE_REG(u8, byte);
ASD_WRITE_REG(u16,word);
ASD_WRITE_REG(u32,dword);

static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
{
	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
	u8 val;
	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
	if (io_handle->swa_base <= reg
	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
		val = asd_read_swa_byte (asd_ha, reg);
	else if (io_handle->swb_base <= reg
		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
		val = asd_read_swb_byte (asd_ha, reg);
	else if (io_handle->swc_base <= reg
		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
		val = asd_read_swc_byte (asd_ha, reg);
	else {
		/* Ok, we have to move SWB */
		asd_move_swb(asd_ha, reg);
		val = asd_read_swb_byte (asd_ha, reg);
	}
	return val;
}

#define ASD_READ_REG(type, ord)                                   \
type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg)   \
{                                                                 \
	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
	type val;                                                 \
	unsigned long flags;                                      \
	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
	spin_lock_irqsave(&asd_ha->iolock, flags);                \
	if (io_handle->swa_base <= reg                            \
	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
		val = asd_read_swa_##ord (asd_ha, reg);           \
	else if (io_handle->swb_base <= reg                       \
		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
		val = asd_read_swb_##ord (asd_ha, reg);           \
	else if (io_handle->swc_base <= reg                       \
		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
		val = asd_read_swc_##ord (asd_ha, reg);           \
	else {                                                    \
		/* Ok, we have to move SWB */                     \
		asd_move_swb(asd_ha, reg);                        \
		val = asd_read_swb_##ord (asd_ha, reg);           \
	}                                                         \
	spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
	return val;                                               \
}

ASD_READ_REG(u8, byte);
ASD_READ_REG(u16,word);
ASD_READ_REG(u32,dword);

/**
 * asd_read_reg_string -- read a string of bytes from io space memory
 * @asd_ha: pointer to host adapter structure
 * @dst: pointer to a destination buffer where data will be written to
 * @offs: start offset (register) to read from
 * @count: number of bytes to read
 */
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
			 u32 offs, int count)
{
	u8 *p = dst;
	unsigned long flags;

	spin_lock_irqsave(&asd_ha->iolock, flags);
	for ( ; count > 0; count--, offs++, p++)
		*p = __asd_read_reg_byte(asd_ha, offs);
	spin_unlock_irqrestore(&asd_ha->iolock, flags);
}

/**
 * asd_write_reg_string -- write a string of bytes to io space memory
 * @asd_ha: pointer to host adapter structure
 * @src: pointer to source buffer where data will be read from
 * @offs: start offset (register) to write to
 * @count: number of bytes to write
 */
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
			  u32 offs, int count)
{
	u8 *p = src;
	unsigned long flags;

	spin_lock_irqsave(&asd_ha->iolock, flags);
	for ( ; count > 0; count--, offs++, p++)
		__asd_write_reg_byte(asd_ha, offs, *p);
	spin_unlock_irqrestore(&asd_ha->iolock, flags);
}