Commit f2f89c32 authored by Xiang Chen's avatar Xiang Chen Committed by Martin K. Petersen

scsi: hisi_sas: workaround v2 hw SATA IO timeout issue

The v2 SAS controller needs more time to detect channel idle
and send setup link request than SATA disk does, so it is
difficult for the SAS controller to setup an STP link. Therefore
it may cause some IO timeouts.

We need to periodically configure the SAS controller so it
doesn't receive STP setup requests from SATA disks for a while,
so IO can be sent during this period.
Signed-off-by: default avatarXiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: default avatarJohn Garry <john.garry@huawei.com>
Reviewed-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent e2934ed1
...@@ -215,6 +215,7 @@ ...@@ -215,6 +215,7 @@
#define RX_IDAF_DWORD5 (PORT_BASE + 0xd8) #define RX_IDAF_DWORD5 (PORT_BASE + 0xd8)
#define RX_IDAF_DWORD6 (PORT_BASE + 0xdc) #define RX_IDAF_DWORD6 (PORT_BASE + 0xdc)
#define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc) #define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc)
#define CON_CONTROL (PORT_BASE + 0x118)
#define DONE_RECEIVED_TIME (PORT_BASE + 0x11c) #define DONE_RECEIVED_TIME (PORT_BASE + 0x11c)
#define CHL_INT0 (PORT_BASE + 0x1b4) #define CHL_INT0 (PORT_BASE + 0x1b4)
#define CHL_INT0_HOTPLUG_TOUT_OFF 0 #define CHL_INT0_HOTPLUG_TOUT_OFF 0
...@@ -526,6 +527,8 @@ enum { ...@@ -526,6 +527,8 @@ enum {
#define SATA_PROTOCOL_FPDMA 0x8 #define SATA_PROTOCOL_FPDMA 0x8
#define SATA_PROTOCOL_ATAPI 0x10 #define SATA_PROTOCOL_ATAPI 0x10
static void hisi_sas_link_timeout_disable_link(unsigned long data);
static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off) static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
{ {
void __iomem *regs = hisi_hba->regs + off; void __iomem *regs = hisi_hba->regs + off;
...@@ -978,6 +981,50 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) ...@@ -978,6 +981,50 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba)
upper_32_bits(hisi_hba->initial_fis_dma)); upper_32_bits(hisi_hba->initial_fis_dma));
} }
static void hisi_sas_link_timeout_enable_link(unsigned long data)
{
struct hisi_hba *hisi_hba = (struct hisi_hba *)data;
int i, reg_val;
for (i = 0; i < hisi_hba->n_phy; i++) {
reg_val = hisi_sas_phy_read32(hisi_hba, i, CON_CONTROL);
if (!(reg_val & BIT(0))) {
hisi_sas_phy_write32(hisi_hba, i,
CON_CONTROL, 0x7);
break;
}
}
hisi_hba->timer.function = hisi_sas_link_timeout_disable_link;
mod_timer(&hisi_hba->timer, jiffies + msecs_to_jiffies(900));
}
static void hisi_sas_link_timeout_disable_link(unsigned long data)
{
struct hisi_hba *hisi_hba = (struct hisi_hba *)data;
int i, reg_val;
reg_val = hisi_sas_read32(hisi_hba, PHY_STATE);
for (i = 0; i < hisi_hba->n_phy && reg_val; i++) {
if (reg_val & BIT(i)) {
hisi_sas_phy_write32(hisi_hba, i,
CON_CONTROL, 0x6);
break;
}
}
hisi_hba->timer.function = hisi_sas_link_timeout_enable_link;
mod_timer(&hisi_hba->timer, jiffies + msecs_to_jiffies(100));
}
static void set_link_timer_quirk(struct hisi_hba *hisi_hba)
{
hisi_hba->timer.data = (unsigned long)hisi_hba;
hisi_hba->timer.function = hisi_sas_link_timeout_disable_link;
hisi_hba->timer.expires = jiffies + msecs_to_jiffies(1000);
add_timer(&hisi_hba->timer);
}
static int hw_init_v2_hw(struct hisi_hba *hisi_hba) static int hw_init_v2_hw(struct hisi_hba *hisi_hba)
{ {
struct device *dev = &hisi_hba->pdev->dev; struct device *dev = &hisi_hba->pdev->dev;
...@@ -2020,9 +2067,12 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) ...@@ -2020,9 +2067,12 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
if (phy->identify.device_type == SAS_END_DEVICE) if (phy->identify.device_type == SAS_END_DEVICE)
phy->identify.target_port_protocols = phy->identify.target_port_protocols =
SAS_PROTOCOL_SSP; SAS_PROTOCOL_SSP;
else if (phy->identify.device_type != SAS_PHY_UNUSED) else if (phy->identify.device_type != SAS_PHY_UNUSED) {
phy->identify.target_port_protocols = phy->identify.target_port_protocols =
SAS_PROTOCOL_SMP; SAS_PROTOCOL_SMP;
if (!timer_pending(&hisi_hba->timer))
set_link_timer_quirk(hisi_hba);
}
queue_work(hisi_hba->wq, &phy->phyup_ws); queue_work(hisi_hba->wq, &phy->phyup_ws);
end: end:
...@@ -2033,10 +2083,23 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) ...@@ -2033,10 +2083,23 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
return res; return res;
} }
static bool check_any_wideports_v2_hw(struct hisi_hba *hisi_hba)
{
u32 port_state;
port_state = hisi_sas_read32(hisi_hba, PORT_STATE);
if (port_state & 0x1ff)
return true;
return false;
}
static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba) static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
{ {
int res = 0; int res = 0;
u32 phy_state, sl_ctrl, txid_auto; u32 phy_state, sl_ctrl, txid_auto;
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
struct hisi_sas_port *port = phy->port;
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1); hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1);
...@@ -2046,6 +2109,10 @@ static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba) ...@@ -2046,6 +2109,10 @@ static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL); sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL,
sl_ctrl & ~SL_CONTROL_CTA_MSK); sl_ctrl & ~SL_CONTROL_CTA_MSK);
if (port && !get_wideport_bitmap_v2_hw(hisi_hba, port->id))
if (!check_any_wideports_v2_hw(hisi_hba) &&
timer_pending(&hisi_hba->timer))
del_timer(&hisi_hba->timer);
txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO); txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO);
hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO, hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
...@@ -2821,6 +2888,12 @@ static int hisi_sas_v2_probe(struct platform_device *pdev) ...@@ -2821,6 +2888,12 @@ static int hisi_sas_v2_probe(struct platform_device *pdev)
static int hisi_sas_v2_remove(struct platform_device *pdev) static int hisi_sas_v2_remove(struct platform_device *pdev)
{ {
struct sas_ha_struct *sha = platform_get_drvdata(pdev);
struct hisi_hba *hisi_hba = sha->lldd_ha;
if (timer_pending(&hisi_hba->timer))
del_timer(&hisi_hba->timer);
return hisi_sas_remove(pdev); return hisi_sas_remove(pdev);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment