162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Aic94xx SAS/SATA driver register access.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/pci.h>
1062306a36Sopenharmony_ci#include "aic94xx_reg.h"
1162306a36Sopenharmony_ci#include "aic94xx.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* Writing to device address space.
1462306a36Sopenharmony_ci * Offset comes before value to remind that the operation of
1562306a36Sopenharmony_ci * this function is *offs = val.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_cistatic void asd_write_byte(struct asd_ha_struct *asd_ha,
1862306a36Sopenharmony_ci			   unsigned long offs, u8 val)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
2162306a36Sopenharmony_ci		outb(val,
2262306a36Sopenharmony_ci		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
2362306a36Sopenharmony_ci	else
2462306a36Sopenharmony_ci		writeb(val, asd_ha->io_handle[0].addr + offs);
2562306a36Sopenharmony_ci	wmb();
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void asd_write_word(struct asd_ha_struct *asd_ha,
2962306a36Sopenharmony_ci			   unsigned long offs, u16 val)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
3262306a36Sopenharmony_ci		outw(val,
3362306a36Sopenharmony_ci		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
3462306a36Sopenharmony_ci	else
3562306a36Sopenharmony_ci		writew(val, asd_ha->io_handle[0].addr + offs);
3662306a36Sopenharmony_ci	wmb();
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void asd_write_dword(struct asd_ha_struct *asd_ha,
4062306a36Sopenharmony_ci			    unsigned long offs, u32 val)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
4362306a36Sopenharmony_ci		outl(val,
4462306a36Sopenharmony_ci		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
4562306a36Sopenharmony_ci	else
4662306a36Sopenharmony_ci		writel(val, asd_ha->io_handle[0].addr + offs);
4762306a36Sopenharmony_ci	wmb();
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Reading from device address space.
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic u8 asd_read_byte(struct asd_ha_struct *asd_ha, unsigned long offs)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	u8 val;
5562306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
5662306a36Sopenharmony_ci		val = inb((unsigned long) asd_ha->io_handle[0].addr
5762306a36Sopenharmony_ci			  + (offs & 0xFF));
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		val = readb(asd_ha->io_handle[0].addr + offs);
6062306a36Sopenharmony_ci	rmb();
6162306a36Sopenharmony_ci	return val;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic u16 asd_read_word(struct asd_ha_struct *asd_ha,
6562306a36Sopenharmony_ci			 unsigned long offs)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	u16 val;
6862306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
6962306a36Sopenharmony_ci		val = inw((unsigned long)asd_ha->io_handle[0].addr
7062306a36Sopenharmony_ci			  + (offs & 0xFF));
7162306a36Sopenharmony_ci	else
7262306a36Sopenharmony_ci		val = readw(asd_ha->io_handle[0].addr + offs);
7362306a36Sopenharmony_ci	rmb();
7462306a36Sopenharmony_ci	return val;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic u32 asd_read_dword(struct asd_ha_struct *asd_ha,
7862306a36Sopenharmony_ci			  unsigned long offs)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	u32 val;
8162306a36Sopenharmony_ci	if (unlikely(asd_ha->iospace))
8262306a36Sopenharmony_ci		val = inl((unsigned long) asd_ha->io_handle[0].addr
8362306a36Sopenharmony_ci			  + (offs & 0xFF));
8462306a36Sopenharmony_ci	else
8562306a36Sopenharmony_ci		val = readl(asd_ha->io_handle[0].addr + offs);
8662306a36Sopenharmony_ci	rmb();
8762306a36Sopenharmony_ci	return val;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic inline u32 asd_mem_offs_swa(void)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline u32 asd_mem_offs_swc(void)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic inline u32 asd_mem_offs_swb(void)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* We know that the register wanted is in the range
10662306a36Sopenharmony_ci * of the sliding window.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ci#define ASD_READ_SW(ww, type, ord)					\
10962306a36Sopenharmony_cistatic type asd_read_##ww##_##ord(struct asd_ha_struct *asd_ha,		\
11062306a36Sopenharmony_ci				   u32 reg)				\
11162306a36Sopenharmony_ci{									\
11262306a36Sopenharmony_ci	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];	\
11362306a36Sopenharmony_ci	u32 map_offs = (reg - io_handle->ww##_base) + asd_mem_offs_##ww();\
11462306a36Sopenharmony_ci	return asd_read_##ord(asd_ha, (unsigned long)map_offs);	\
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define ASD_WRITE_SW(ww, type, ord)					\
11862306a36Sopenharmony_cistatic void asd_write_##ww##_##ord(struct asd_ha_struct *asd_ha,	\
11962306a36Sopenharmony_ci				    u32 reg, type val)			\
12062306a36Sopenharmony_ci{									\
12162306a36Sopenharmony_ci	struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];	\
12262306a36Sopenharmony_ci	u32 map_offs = (reg - io_handle->ww##_base) + asd_mem_offs_##ww();\
12362306a36Sopenharmony_ci	asd_write_##ord(asd_ha, (unsigned long)map_offs, val);		\
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciASD_READ_SW(swa, u8,  byte);
12762306a36Sopenharmony_ciASD_READ_SW(swa, u16, word);
12862306a36Sopenharmony_ciASD_READ_SW(swa, u32, dword);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciASD_READ_SW(swb, u8,  byte);
13162306a36Sopenharmony_ciASD_READ_SW(swb, u16, word);
13262306a36Sopenharmony_ciASD_READ_SW(swb, u32, dword);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciASD_READ_SW(swc, u8,  byte);
13562306a36Sopenharmony_ciASD_READ_SW(swc, u16, word);
13662306a36Sopenharmony_ciASD_READ_SW(swc, u32, dword);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciASD_WRITE_SW(swa, u8,  byte);
13962306a36Sopenharmony_ciASD_WRITE_SW(swa, u16, word);
14062306a36Sopenharmony_ciASD_WRITE_SW(swa, u32, dword);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciASD_WRITE_SW(swb, u8,  byte);
14362306a36Sopenharmony_ciASD_WRITE_SW(swb, u16, word);
14462306a36Sopenharmony_ciASD_WRITE_SW(swb, u32, dword);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciASD_WRITE_SW(swc, u8,  byte);
14762306a36Sopenharmony_ciASD_WRITE_SW(swc, u16, word);
14862306a36Sopenharmony_ciASD_WRITE_SW(swc, u32, dword);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/*
15162306a36Sopenharmony_ci * A word about sliding windows:
15262306a36Sopenharmony_ci * MBAR0 is divided into sliding windows A, C and B, in that order.
15362306a36Sopenharmony_ci * SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
15462306a36Sopenharmony_ci * SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
15562306a36Sopenharmony_ci * From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
15662306a36Sopenharmony_ci * SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
15762306a36Sopenharmony_ci * See asd_init_sw() in aic94xx_hwi.c
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * We map the most common registers we'd access of the internal 4GB
16062306a36Sopenharmony_ci * host adapter memory space.  If a register/internal memory location
16162306a36Sopenharmony_ci * is wanted which is not mapped, we slide SWB, by paging it,
16262306a36Sopenharmony_ci * see asd_move_swb() in aic94xx_reg.c.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/**
16662306a36Sopenharmony_ci * asd_move_swb -- move sliding window B
16762306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure
16862306a36Sopenharmony_ci * @reg: register desired to be within range of the new window
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	u32 base = reg & ~(MBAR0_SWB_SIZE-1);
17362306a36Sopenharmony_ci	pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
17462306a36Sopenharmony_ci	asd_ha->io_handle[0].swb_base = base;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
18062306a36Sopenharmony_ci	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
18162306a36Sopenharmony_ci	if (io_handle->swa_base <= reg
18262306a36Sopenharmony_ci	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
18362306a36Sopenharmony_ci		asd_write_swa_byte (asd_ha, reg,val);
18462306a36Sopenharmony_ci	else if (io_handle->swb_base <= reg
18562306a36Sopenharmony_ci		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
18662306a36Sopenharmony_ci		asd_write_swb_byte (asd_ha, reg, val);
18762306a36Sopenharmony_ci	else if (io_handle->swc_base <= reg
18862306a36Sopenharmony_ci		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
18962306a36Sopenharmony_ci		asd_write_swc_byte (asd_ha, reg, val);
19062306a36Sopenharmony_ci	else {
19162306a36Sopenharmony_ci		/* Ok, we have to move SWB */
19262306a36Sopenharmony_ci		asd_move_swb(asd_ha, reg);
19362306a36Sopenharmony_ci		asd_write_swb_byte (asd_ha, reg, val);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#define ASD_WRITE_REG(type, ord)                                  \
19862306a36Sopenharmony_civoid asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
19962306a36Sopenharmony_ci{                                                                 \
20062306a36Sopenharmony_ci	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
20162306a36Sopenharmony_ci	unsigned long flags;                                      \
20262306a36Sopenharmony_ci	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
20362306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->iolock, flags);                \
20462306a36Sopenharmony_ci	if (io_handle->swa_base <= reg                            \
20562306a36Sopenharmony_ci	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
20662306a36Sopenharmony_ci		asd_write_swa_##ord (asd_ha, reg,val);            \
20762306a36Sopenharmony_ci	else if (io_handle->swb_base <= reg                       \
20862306a36Sopenharmony_ci		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
20962306a36Sopenharmony_ci		asd_write_swb_##ord (asd_ha, reg, val);           \
21062306a36Sopenharmony_ci	else if (io_handle->swc_base <= reg                       \
21162306a36Sopenharmony_ci		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
21262306a36Sopenharmony_ci		asd_write_swc_##ord (asd_ha, reg, val);           \
21362306a36Sopenharmony_ci	else {                                                    \
21462306a36Sopenharmony_ci		/* Ok, we have to move SWB */                     \
21562306a36Sopenharmony_ci		asd_move_swb(asd_ha, reg);                        \
21662306a36Sopenharmony_ci		asd_write_swb_##ord (asd_ha, reg, val);           \
21762306a36Sopenharmony_ci	}                                                         \
21862306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ciASD_WRITE_REG(u8, byte);
22262306a36Sopenharmony_ciASD_WRITE_REG(u16,word);
22362306a36Sopenharmony_ciASD_WRITE_REG(u32,dword);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
22862306a36Sopenharmony_ci	u8 val;
22962306a36Sopenharmony_ci	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
23062306a36Sopenharmony_ci	if (io_handle->swa_base <= reg
23162306a36Sopenharmony_ci	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)
23262306a36Sopenharmony_ci		val = asd_read_swa_byte (asd_ha, reg);
23362306a36Sopenharmony_ci	else if (io_handle->swb_base <= reg
23462306a36Sopenharmony_ci		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)
23562306a36Sopenharmony_ci		val = asd_read_swb_byte (asd_ha, reg);
23662306a36Sopenharmony_ci	else if (io_handle->swc_base <= reg
23762306a36Sopenharmony_ci		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)
23862306a36Sopenharmony_ci		val = asd_read_swc_byte (asd_ha, reg);
23962306a36Sopenharmony_ci	else {
24062306a36Sopenharmony_ci		/* Ok, we have to move SWB */
24162306a36Sopenharmony_ci		asd_move_swb(asd_ha, reg);
24262306a36Sopenharmony_ci		val = asd_read_swb_byte (asd_ha, reg);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	return val;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci#define ASD_READ_REG(type, ord)                                   \
24862306a36Sopenharmony_citype asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg)   \
24962306a36Sopenharmony_ci{                                                                 \
25062306a36Sopenharmony_ci	struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
25162306a36Sopenharmony_ci	type val;                                                 \
25262306a36Sopenharmony_ci	unsigned long flags;                                      \
25362306a36Sopenharmony_ci	BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);         \
25462306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->iolock, flags);                \
25562306a36Sopenharmony_ci	if (io_handle->swa_base <= reg                            \
25662306a36Sopenharmony_ci	    && reg < io_handle->swa_base + MBAR0_SWA_SIZE)        \
25762306a36Sopenharmony_ci		val = asd_read_swa_##ord (asd_ha, reg);           \
25862306a36Sopenharmony_ci	else if (io_handle->swb_base <= reg                       \
25962306a36Sopenharmony_ci		 && reg < io_handle->swb_base + MBAR0_SWB_SIZE)   \
26062306a36Sopenharmony_ci		val = asd_read_swb_##ord (asd_ha, reg);           \
26162306a36Sopenharmony_ci	else if (io_handle->swc_base <= reg                       \
26262306a36Sopenharmony_ci		 && reg < io_handle->swc_base + MBAR0_SWC_SIZE)   \
26362306a36Sopenharmony_ci		val = asd_read_swc_##ord (asd_ha, reg);           \
26462306a36Sopenharmony_ci	else {                                                    \
26562306a36Sopenharmony_ci		/* Ok, we have to move SWB */                     \
26662306a36Sopenharmony_ci		asd_move_swb(asd_ha, reg);                        \
26762306a36Sopenharmony_ci		val = asd_read_swb_##ord (asd_ha, reg);           \
26862306a36Sopenharmony_ci	}                                                         \
26962306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->iolock, flags);           \
27062306a36Sopenharmony_ci	return val;                                               \
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ciASD_READ_REG(u8, byte);
27462306a36Sopenharmony_ciASD_READ_REG(u16,word);
27562306a36Sopenharmony_ciASD_READ_REG(u32,dword);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/**
27862306a36Sopenharmony_ci * asd_read_reg_string -- read a string of bytes from io space memory
27962306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure
28062306a36Sopenharmony_ci * @dst: pointer to a destination buffer where data will be written to
28162306a36Sopenharmony_ci * @offs: start offset (register) to read from
28262306a36Sopenharmony_ci * @count: number of bytes to read
28362306a36Sopenharmony_ci */
28462306a36Sopenharmony_civoid asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
28562306a36Sopenharmony_ci			 u32 offs, int count)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	u8 *p = dst;
28862306a36Sopenharmony_ci	unsigned long flags;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->iolock, flags);
29162306a36Sopenharmony_ci	for ( ; count > 0; count--, offs++, p++)
29262306a36Sopenharmony_ci		*p = __asd_read_reg_byte(asd_ha, offs);
29362306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->iolock, flags);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/**
29762306a36Sopenharmony_ci * asd_write_reg_string -- write a string of bytes to io space memory
29862306a36Sopenharmony_ci * @asd_ha: pointer to host adapter structure
29962306a36Sopenharmony_ci * @src: pointer to source buffer where data will be read from
30062306a36Sopenharmony_ci * @offs: start offset (register) to write to
30162306a36Sopenharmony_ci * @count: number of bytes to write
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_civoid asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
30462306a36Sopenharmony_ci			  u32 offs, int count)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	u8 *p = src;
30762306a36Sopenharmony_ci	unsigned long flags;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	spin_lock_irqsave(&asd_ha->iolock, flags);
31062306a36Sopenharmony_ci	for ( ; count > 0; count--, offs++, p++)
31162306a36Sopenharmony_ci		__asd_write_reg_byte(asd_ha, offs, *p);
31262306a36Sopenharmony_ci	spin_unlock_irqrestore(&asd_ha->iolock, flags);
31362306a36Sopenharmony_ci}
314