diff options
Diffstat (limited to 'drivers/pci/switch')
| -rw-r--r-- | drivers/pci/switch/switchtec.c | 95 | 
1 files changed, 78 insertions, 17 deletions
| diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 0b301f8be9ed..38c2b036fb8e 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -45,6 +45,7 @@ enum mrpc_state {  	MRPC_QUEUED,  	MRPC_RUNNING,  	MRPC_DONE, +	MRPC_IO_ERROR,  };  struct switchtec_user { @@ -66,6 +67,19 @@ struct switchtec_user {  	int event_cnt;  }; +/* + * The MMIO reads to the device_id register should always return the device ID + * of the device, otherwise the firmware is probably stuck or unreachable + * due to a firmware reset which clears PCI state including the BARs and Memory + * Space Enable bits. + */ +static int is_firmware_running(struct switchtec_dev *stdev) +{ +	u32 device = ioread32(&stdev->mmio_sys_info->device_id); + +	return stdev->pdev->device == device; +} +  static struct switchtec_user *stuser_create(struct switchtec_dev *stdev)  {  	struct switchtec_user *stuser; @@ -113,6 +127,7 @@ static void stuser_set_state(struct switchtec_user *stuser,  		[MRPC_QUEUED] = "QUEUED",  		[MRPC_RUNNING] = "RUNNING",  		[MRPC_DONE] = "DONE", +		[MRPC_IO_ERROR] = "IO_ERROR",  	};  	stuser->state = state; @@ -184,9 +199,26 @@ static int mrpc_queue_cmd(struct switchtec_user *stuser)  	return 0;  } +static void mrpc_cleanup_cmd(struct switchtec_dev *stdev) +{ +	/* requires the mrpc_mutex to already be held when called */ + +	struct switchtec_user *stuser = list_entry(stdev->mrpc_queue.next, +						   struct switchtec_user, list); + +	stuser->cmd_done = true; +	wake_up_interruptible(&stuser->cmd_comp); +	list_del_init(&stuser->list); +	stuser_put(stuser); +	stdev->mrpc_busy = 0; + +	mrpc_cmd_submit(stdev); +} +  static void mrpc_complete_cmd(struct switchtec_dev *stdev)  {  	/* requires the mrpc_mutex to already be held when called */ +  	struct switchtec_user *stuser;  	if (list_empty(&stdev->mrpc_queue)) @@ -206,7 +238,8 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev)  	stuser_set_state(stuser, MRPC_DONE);  	stuser->return_code = 0; -	if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE) +	if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE && +	    stuser->status != SWITCHTEC_MRPC_STATUS_ERROR)  		goto out;  	if (stdev->dma_mrpc) @@ -223,13 +256,7 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev)  		memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data,  			      stuser->read_len);  out: -	stuser->cmd_done = true; -	wake_up_interruptible(&stuser->cmd_comp); -	list_del_init(&stuser->list); -	stuser_put(stuser); -	stdev->mrpc_busy = 0; - -	mrpc_cmd_submit(stdev); +	mrpc_cleanup_cmd(stdev);  }  static void mrpc_event_work(struct work_struct *work) @@ -246,6 +273,23 @@ static void mrpc_event_work(struct work_struct *work)  	mutex_unlock(&stdev->mrpc_mutex);  } +static void mrpc_error_complete_cmd(struct switchtec_dev *stdev) +{ +	/* requires the mrpc_mutex to already be held when called */ + +	struct switchtec_user *stuser; + +	if (list_empty(&stdev->mrpc_queue)) +		return; + +	stuser = list_entry(stdev->mrpc_queue.next, +			    struct switchtec_user, list); + +	stuser_set_state(stuser, MRPC_IO_ERROR); + +	mrpc_cleanup_cmd(stdev); +} +  static void mrpc_timeout_work(struct work_struct *work)  {  	struct switchtec_dev *stdev; @@ -257,6 +301,11 @@ static void mrpc_timeout_work(struct work_struct *work)  	mutex_lock(&stdev->mrpc_mutex); +	if (!is_firmware_running(stdev)) { +		mrpc_error_complete_cmd(stdev); +		goto out; +	} +  	if (stdev->dma_mrpc)  		status = stdev->dma_mrpc->status;  	else @@ -327,7 +376,7 @@ static ssize_t field ## _show(struct device *dev, \  		return io_string_show(buf, &si->gen4.field, \  				      sizeof(si->gen4.field)); \  	else \ -		return -ENOTSUPP; \ +		return -EOPNOTSUPP; \  } \  \  static DEVICE_ATTR_RO(field) @@ -544,6 +593,11 @@ static ssize_t switchtec_dev_read(struct file *filp, char __user *data,  	if (rc)  		return rc; +	if (stuser->state == MRPC_IO_ERROR) { +		mutex_unlock(&stdev->mrpc_mutex); +		return -EIO; +	} +  	if (stuser->state != MRPC_DONE) {  		mutex_unlock(&stdev->mrpc_mutex);  		return -EBADE; @@ -569,7 +623,8 @@ static ssize_t switchtec_dev_read(struct file *filp, char __user *data,  out:  	mutex_unlock(&stdev->mrpc_mutex); -	if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE) +	if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE || +	    stuser->status == SWITCHTEC_MRPC_STATUS_ERROR)  		return size;  	else if (stuser->status == SWITCHTEC_MRPC_STATUS_INTERRUPTED)  		return -ENXIO; @@ -613,7 +668,7 @@ static int ioctl_flash_info(struct switchtec_dev *stdev,  		info.flash_length = ioread32(&fi->gen4.flash_length);  		info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN4;  	} else { -		return -ENOTSUPP; +		return -EOPNOTSUPP;  	}  	if (copy_to_user(uinfo, &info, sizeof(info))) @@ -821,7 +876,7 @@ static int ioctl_flash_part_info(struct switchtec_dev *stdev,  		if (ret)  			return ret;  	} else { -		return -ENOTSUPP; +		return -EOPNOTSUPP;  	}  	if (copy_to_user(uinfo, &info, sizeof(info))) @@ -969,6 +1024,9 @@ static int event_ctl(struct switchtec_dev *stdev,  		return PTR_ERR(reg);  	hdr = ioread32(reg); +	if (hdr & SWITCHTEC_EVENT_NOT_SUPP) +		return -EOPNOTSUPP; +  	for (i = 0; i < ARRAY_SIZE(ctl->data); i++)  		ctl->data[i] = ioread32(®[i + 1]); @@ -1041,7 +1099,7 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev,  		for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) {  			ctl.flags = event_flags;  			ret = event_ctl(stdev, &ctl); -			if (ret < 0) +			if (ret < 0 && ret != -EOPNOTSUPP)  				return ret;  		}  	} else { @@ -1078,7 +1136,7 @@ static int ioctl_pff_to_port(struct switchtec_dev *stdev,  			break;  		} -		reg = ioread32(&pcfg->vep_pff_inst_id); +		reg = ioread32(&pcfg->vep_pff_inst_id) & 0xFF;  		if (reg == p.pff) {  			p.port = SWITCHTEC_IOCTL_PFF_VEP;  			break; @@ -1124,7 +1182,7 @@ static int ioctl_port_to_pff(struct switchtec_dev *stdev,  		p.pff = ioread32(&pcfg->usp_pff_inst_id);  		break;  	case SWITCHTEC_IOCTL_PFF_VEP: -		p.pff = ioread32(&pcfg->vep_pff_inst_id); +		p.pff = ioread32(&pcfg->vep_pff_inst_id) & 0xFF;  		break;  	default:  		if (p.port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) @@ -1348,6 +1406,9 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx)  	hdr_reg = event_regs[eid].map_reg(stdev, off, idx);  	hdr = ioread32(hdr_reg); +	if (hdr & SWITCHTEC_EVENT_NOT_SUPP) +		return 0; +  	if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ))  		return 0; @@ -1498,7 +1559,7 @@ static void init_pff(struct switchtec_dev *stdev)  	if (reg < stdev->pff_csr_count)  		stdev->pff_local[reg] = 1; -	reg = ioread32(&pcfg->vep_pff_inst_id); +	reg = ioread32(&pcfg->vep_pff_inst_id) & 0xFF;  	if (reg < stdev->pff_csr_count)  		stdev->pff_local[reg] = 1; @@ -1556,7 +1617,7 @@ static int switchtec_init_pci(struct switchtec_dev *stdev,  	else if (stdev->gen == SWITCHTEC_GEN4)  		part_id = &stdev->mmio_sys_info->gen4.partition_id;  	else -		return -ENOTSUPP; +		return -EOPNOTSUPP;  	stdev->partition = ioread8(part_id);  	stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count); |