Skip to contentSkip to conversation

ufs: fix bugs in probing and removing driver paths

PATCH New Akinobu Mita 8 participants 72 messages Jul 20, 2013 – Sep 9, 2013 SCSI

lore
Share & tools
lore
#0 lore raw

v2 cover ufs: fix bugs in probing and removing driver paths

The changes in this patch set are almost same as the previous one that I send on Jul 8. But the previous version wasn't cleanly applied to the upstream kernel because it depend on my local work in progress patches. So this version fixes it and it also includes another bug fix in the same area.

Akinobu Mita (3):
ufshcd-pci: release ioremapped region during removing driver
ufs: don't disable_irq() if the IRQ can be shared among devices
ufs: don't stop controller before scsi_remove_host()

 drivers/scsi/ufs/ufshcd-pci.c    | 38 +++++++++-----------------------------
 drivers/scsi/ufs/ufshcd-pltfrm.c |  1 -
 drivers/scsi/ufs/ufshcd.c        |  2 +-
 3 files changed, 10 insertions(+), 31 deletions(-)
Cc Seungwon Jeon <[email protected]>
Cc Vinayak Holikatti <[email protected]>
Cc Santosh Y <[email protected]>
Cc "James E.J. Bottomley" <[email protected]>
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH v2 1/3 ufshcd-pci: release ioremapped region during removing driver

Before commit 2953f850c3b80bdca004967c83733365d8aa0aa2 ("[SCSI] ufs:
use devres functions for ufshcd"), UFSHCI register was ioremapped by each glue-driver (ufshcd-pltfrm and ufshcd-pci) during probing and it was iounmapped by core-driver during removing driver. The commit converted ufshcd-pltfrm to use devres functions, but it didn't convert ufshcd-pci.

Therefore, the change causes ufshcd-pci driver not to iounmap UFSHCI register region during removing driver. This fixes it by converting ufshcd-pci to use devres functions.

Cc Seungwon Jeon <[email protected]>
Cc Vinayak Holikatti <[email protected]>
Cc Santosh Y <[email protected]>
Cc "James E.J. Bottomley" <[email protected]>
---
 drivers/scsi/ufs/ufshcd-pci.c | 37 +++++++++----------------------------
 1 file changed, 9 insertions(+), 28 deletions(-)
drivers/scsi/ufs/ufshcd-pci.c +9 -28
index 48be39a..de4c52b 100644
@@ -93,10 +93,7 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
 
 	disable_irq(pdev->irq);
 	ufshcd_remove(hba);
-	pci_release_regions(pdev);
 	pci_set_drvdata(pdev, NULL);
-	pci_clear_master(pdev);
-	pci_disable_device(pdev);
 }
 
 /**
@@ -133,53 +130,37 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	void __iomem *mmio_base;
 	int err;
 
-	err = pci_enable_device(pdev);
+	err = pcim_enable_device(pdev);
 	if (err) {
-		dev_err(&pdev->dev, "pci_enable_device failed\n");
-		goto out_error;
+		dev_err(&pdev->dev, "pcim_enable_device failed\n");
+		return err;
 	}
 
 	pci_set_master(pdev);
 
-
-	err = pci_request_regions(pdev, UFSHCD);
+	err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD);
 	if (err < 0) {
-		dev_err(&pdev->dev, "request regions failed\n");
-		goto out_disable;
+		dev_err(&pdev->dev, "request and iomap failed\n");
+		return err;
 	}
 
-	mmio_base = pci_ioremap_bar(pdev, 0);
-	if (!mmio_base) {
-		dev_err(&pdev->dev, "memory map failed\n");
-		err = -ENOMEM;
-		goto out_release_regions;
-	}
+	mmio_base = pcim_iomap_table(pdev)[0];
 
 	err = ufshcd_set_dma_mask(pdev);
 	if (err) {
 		dev_err(&pdev->dev, "set dma mask failed\n");
-		goto out_iounmap;
+		return err;
 	}
 
 	err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq);
 	if (err) {
 		dev_err(&pdev->dev, "Initialization failed\n");
-		goto out_iounmap;
+		return err;
 	}
 
 	pci_set_drvdata(pdev, hba);
 
 	return 0;
-
-out_iounmap:
-	iounmap(mmio_base);
-out_release_regions:
-	pci_release_regions(pdev);
-out_disable:
-	pci_clear_master(pdev);
-	pci_disable_device(pdev);
-out_error:
-	return err;
 }
 
 static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
Signature · 1 line
1.8.3.1
#2 lore raw

PATCH 1/7 scsi: ufs: amend the ocs handling with fatal error

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

---
 drivers/scsi/ufs/ufshcd.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +1 -2
index b743bd6..4cf3a2d 100644
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
 	switch (ocs) {
 	case OCS_SUCCESS:
-
+	case OCS_FATAL_ERROR:
 		/* check if the returned transfer response is valid */
 		result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
 		if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 	case OCS_MISMATCH_DATA_BUF_SIZE:
 	case OCS_MISMATCH_RESP_UPIU_SIZE:
 	case OCS_PEER_COMM_FAILURE:
-	case OCS_FATAL_ERROR:
 	default:
 		result |= DID_ERROR << 16;
 		dev_err(hba->dev,
Signature · 1 line
1.7.0.4
#3 lore raw

Re: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 18 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

---
  drivers/scsi/ufs/ufshcd.c |    3 +--
  1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) · line 1204

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +1 -1
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
  	switch (ocs) {
  	case OCS_SUCCESS:
-
+	case OCS_FATAL_ERROR:
  		/* check if the returned transfer response is valid */
  		result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Regards,
Subhash

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +0 -1
  		if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  	case OCS_MISMATCH_DATA_BUF_SIZE:
  	case OCS_MISMATCH_RESP_UPIU_SIZE:
  	case OCS_PEER_COMM_FAILURE:
-	case OCS_FATAL_ERROR:
  	default:
  		result |= DID_ERROR << 16;
  		dev_err(hba->dev,
#4 lore raw

RE: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 20 lines · Subhash Jadavani
Quoted 19 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

---
  drivers/scsi/ufs/ufshcd.c |    3 +--
  1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
Quoted 14 lines

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) · line 1204

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +1 -1
  	switch (ocs) {
  	case OCS_SUCCESS:
-
+	case OCS_FATAL_ERROR:
  		/* check if the returned transfer response is valid */
  		result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Response data of 'ucd_rsp_ptr' will be updated by UFS device if response UPIU is completed. There is nowhere driver clears 'ucd_rsp_ptr', though.
But if it's really needed, it can be another patch?

Thanks,
Seungwon Jeon

#5 lore raw

Re: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 34 lines · Seungwon Jeon
Quoted 20 lines · Subhash Jadavani
Quoted 19 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

---
   drivers/scsi/ufs/ufshcd.c |    3 +--
   1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
Quoted 13 lines

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) · line 1204

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +1 -1
   	switch (ocs) {
   	case OCS_SUCCESS:
-
+	case OCS_FATAL_ERROR:
   		/* check if the returned transfer response is valid */
   		result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Quoted 2 lines

Response data of 'ucd_rsp_ptr' will be updated by UFS device if response UPIU is completed. There is nowhere driver clears 'ucd_rsp_ptr', though.

Correct. But with OCS_FATAL_ERROR error, i don't think its gaurenteed that response UPIU would be updated.

Quoted 1 line

But if it's really needed, it can be another patch?

Yes, its ok to have it as another patch.

Quoted 4 lines

Thanks,
Seungwon Jeon

#6 lore raw

Re: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

In reply to Subhash Jadavani
Quoted 8 lines · Subhash Jadavani
Quoted 7 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

It looks like you are assuming that there will be some kind of response from the device. But the spec. mentions - "OCS_FATAL ERROR: within host controller that is not covered by the error conditions described above in this table."

So spec. left everything to implementers on how to trigger this error. Also, it says "within host controller" so ufshcd_is_valid_req_rsp() might fail as well.

I couldn't understand why there is a need for a host controller to interpret the SCSI command status and raise an OCS_FATAL_ERROR?

If it is clarified by the spec. then we can have generic implementation otherwise I would prefer to make this specific to those host controllers that raise OCS_FATAL_ERROR for CHECK_CONDITION.

Quoted 41 lines
Quoted 11 lines
---
  drivers/scsi/ufs/ufshcd.c |    3 +--
  1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, · line 1204

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +1 -1
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
      switch (ocs) {
      case OCS_SUCCESS:
-
+    case OCS_FATAL_ERROR:
          /* check if the returned transfer response is valid */
          result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Regards,
Subhash

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -1
          if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
      case OCS_MISMATCH_DATA_BUF_SIZE:
      case OCS_MISMATCH_RESP_UPIU_SIZE:
      case OCS_PEER_COMM_FAILURE:
-    case OCS_FATAL_ERROR:
      default:
          result |= DID_ERROR << 16;
          dev_err(hba->dev,
Signature · 2 lines
Regards,
Sujit
#7 lore raw

RE: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 10 lines · Sujit Reddy Thumma
Quoted 8 lines · Subhash Jadavani
Quoted 7 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

Quoted 4 lines

It looks like you are assuming that there will be some kind of response from the device. But the spec. mentions - "OCS_FATAL ERROR: within host controller that is not covered by the error conditions described above in this table."

Yes, error interrupt doesn't happen actually.

Quoted 7 lines

So spec. left everything to implementers on how to trigger this error. Also, it says "within host controller" so ufshcd_is_valid_req_rsp() might fail as well.

I couldn't understand why there is a need for a host controller to interpret the SCSI command status and raise an OCS_FATAL_ERROR?

I feel like OCS values are related to syntax problem except OCS_FATAL_ERROR.
Basically, host controller may need to refer the Response field[Target Success/Target Failure] of response UPIU. It's a overall result from response UPIU.
When Response field is 'Target Failure', if host controller updates the OCS field with 'SUCCESS', it's not proper behavior. In this case host may refer the Status field of response UPIU to decide the OCS result.
Of course, it's not clear thing and could depends on host's implementation,
because there is no obvious description for that.
But if we consider the way to report UTP error from UFSHCI 8.2.4,
we can check the Response UPIU for more detailed error condition regardless OCS value.
Could you check your host side?

Thanks,
Seungwon Jeon

Quoted 55 lines

If it is clarified by the spec. then we can have generic implementation otherwise I would prefer to make this specific to those host controllers that raise OCS_FATAL_ERROR for CHECK_CONDITION.

Quoted 30 lines
Quoted 11 lines
---
  drivers/scsi/ufs/ufshcd.c |    3 +--
  1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, · line 1204

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +1 -1
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
      switch (ocs) {
      case OCS_SUCCESS:
-
+    case OCS_FATAL_ERROR:
          /* check if the returned transfer response is valid */
          result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Regards,
Subhash

Quoted 11 lines
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -1
          if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
      case OCS_MISMATCH_DATA_BUF_SIZE:
      case OCS_MISMATCH_RESP_UPIU_SIZE:
      case OCS_PEER_COMM_FAILURE:
-    case OCS_FATAL_ERROR:
      default:
          result |= DID_ERROR << 16;
          dev_err(hba->dev,

--
Regards,
Sujit
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#8 lore raw

Re: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 22 lines · Seungwon Jeon
Quoted 9 lines · Sujit Reddy Thumma
Quoted 8 lines · Subhash Jadavani
Quoted 7 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

Quoted 4 lines

It looks like you are assuming that there will be some kind of response from the device. But the spec. mentions - "OCS_FATAL ERROR: within host controller that is not covered by the error conditions described above in this table."

Yes, error interrupt doesn't happen actually.

Quoted 6 lines

So spec. left everything to implementers on how to trigger this error. Also, it says "within host controller" so ufshcd_is_valid_req_rsp() might fail as well.

I couldn't understand why there is a need for a host controller to interpret the SCSI command status and raise an OCS_FATAL_ERROR?

Quoted 10 lines

I feel like OCS values are related to syntax problem except OCS_FATAL_ERROR.
Basically, host controller may need to refer the Response field[Target Success/Target Failure] of response UPIU. It's a overall result from response UPIU.
When Response field is 'Target Failure', if host controller updates the OCS field with 'SUCCESS', it's not proper behavior. In this case host may refer the Status field of response UPIU to decide the OCS result.
Of course, it's not clear thing and could depends on host's implementation,
because there is no obvious description for that.
But if we consider the way to report UTP error from UFSHCI 8.2.4,
we can check the Response UPIU for more detailed error condition regardless OCS value.
Could you check your host side?

This is what our host documentation says: " SW may check the contents of Response UPIU when OCS !=0 for debug purposes. It may (or may not) help to understand the error scenario better. This is needed only for debug. When system is stable, OCS should be 0." So this clearly means that we shouldn't rely on the response UPIU if the OCS is non-zero hence simply ignore it.

Quoted 1 line

"When Response field is 'Target Failure', if host controller updates the OCS field with 'SUCCESS', it's not proper behavior."

I doubt whether your above understanding is true from Host controller specification point of view. Host controller would not decode the “Response” field of the “response UPIU”, it would only decode the “Transaction Type” & “Task Tag” (and may be “LUN”) from the response UPIU in order to find out the correct slot in the transfer/task request list. Even 7.3.2.3 in UFSHCI spec also mentions the same.

I guess with this i don't see a need for this patch unless you feel otherwise.

Regards,
Subhash

Quoted 54 lines

Thanks,
Seungwon Jeon

Quoted 50 lines

If it is clarified by the spec. then we can have generic implementation otherwise I would prefer to make this specific to those host controllers that raise OCS_FATAL_ERROR for CHECK_CONDITION.

Quoted 28 lines
Quoted 10 lines
---
   drivers/scsi/ufs/ufshcd.c |    3 +--
   1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, · line 1204

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +1 -1
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
       switch (ocs) {
       case OCS_SUCCESS:
-
+    case OCS_FATAL_ERROR:
           /* check if the returned transfer response is valid */
           result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Regards,
Subhash

Quoted 10 lines
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -1
           if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
       case OCS_MISMATCH_DATA_BUF_SIZE:
       case OCS_MISMATCH_RESP_UPIU_SIZE:
       case OCS_PEER_COMM_FAILURE:
-    case OCS_FATAL_ERROR:
       default:
           result |= DID_ERROR << 16;
           dev_err(hba->dev,

--
Regards,
Sujit
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#9 lore raw

RE: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 36 lines · Subhash Jadavani
Quoted 16 lines · Seungwon Jeon
Quoted 9 lines · Sujit Reddy Thumma
Quoted 8 lines · Subhash Jadavani
Quoted 7 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

Quoted 4 lines

It looks like you are assuming that there will be some kind of response from the device. But the spec. mentions - "OCS_FATAL ERROR: within host controller that is not covered by the error conditions described above in this table."

Yes, error interrupt doesn't happen actually.

Quoted 8 lines
Quoted 6 lines

So spec. left everything to implementers on how to trigger this error. Also, it says "within host controller" so ufshcd_is_valid_req_rsp() might fail as well.

I couldn't understand why there is a need for a host controller to interpret the SCSI command status and raise an OCS_FATAL_ERROR?

I feel like OCS values are related to syntax problem except OCS_FATAL_ERROR. Basically, host controller may need to refer the Response field[Target Success/Target Failure] of

response UPIU.

Quoted 2 lines

It's a overall result from response UPIU.
When Response field is 'Target Failure', if host controller updates the OCS field with 'SUCCESS',

it's not proper behavior.

Quoted 6 lines

In this case host may refer the Status field of response UPIU to decide the OCS result. Of course, it's not clear thing and could depends on host's implementation, because there is no obvious description for that.
But if we consider the way to report UTP error from UFSHCI 8.2.4,
we can check the Response UPIU for more detailed error condition regardless OCS value. Could you check your host side?

Quoted 6 lines

This is what our host documentation says: " SW may check the contents of Response UPIU when OCS !=0 for debug purposes. It may (or may not) help to understand the error scenario better. This is needed only for debug. When system is stable, OCS should be 0." So this clearly means that we shouldn't rely on the response UPIU if the OCS is non-zero hence simply ignore it.

Isn't there any description for FATAL ERROR of OCS?
Can I take like below?
Even though Response field is 'Target Failure', OCS field can get a success result.
I just has focused what spec. says below:
- FATAL ERROR within host controller that is not covered by the error conditions described above in this table.
- The field contains the status code for the request.
- Host software may need to check Transfer Response UPIU's Response, and possibly Sense Data to find out more details for the cause of the error. Because host doesn't know and decide the error status, host could need to check.

Quoted 11 lines
Quoted 2 lines

"When Response field is 'Target Failure', if host controller updates

the OCS field with 'SUCCESS', it's not proper behavior."

I doubt whether your above understanding is true from Host controller specification point of view. Host controller would not decode the “Response” field of the “response UPIU”, it would only decode the “Transaction Type” & “Task Tag” (and may be “LUN”) from the response UPIU in order to find out the correct slot in the transfer/task request list. Even 7.3.2.3 in UFSHCI spec also mentions the same.

Quoted 2 lines

I guess with this i don't see a need for this patch unless you feel otherwise.

It may depend on host's implementation. HCI spec. doesn't guide for OCS in detail. Anyway, it's not clear just now.

Thanks,
Seungwon Jeon

Quoted 59 lines

Regards,
Subhash

Quoted 54 lines

Thanks,
Seungwon Jeon

Quoted 50 lines

If it is clarified by the spec. then we can have generic implementation otherwise I would prefer to make this specific to those host controllers that raise OCS_FATAL_ERROR for CHECK_CONDITION.

Quoted 28 lines
Quoted 10 lines
---
   drivers/scsi/ufs/ufshcd.c |    3 +--
   1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, · line 1204

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +1 -1
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
       switch (ocs) {
       case OCS_SUCCESS:
-
+    case OCS_FATAL_ERROR:
           /* check if the returned transfer response is valid */
           result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Regards,
Subhash

Quoted 10 lines
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -1
           if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
       case OCS_MISMATCH_DATA_BUF_SIZE:
       case OCS_MISMATCH_RESP_UPIU_SIZE:
       case OCS_PEER_COMM_FAILURE:
-    case OCS_FATAL_ERROR:
       default:
           result |= DID_ERROR << 16;
           dev_err(hba->dev,

--
Regards,
Sujit
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#10 lore raw

Re: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

Quoted 42 lines · Seungwon Jeon
Quoted 35 lines · Subhash Jadavani
Quoted 16 lines · Seungwon Jeon
Quoted 9 lines · Sujit Reddy Thumma
Quoted 8 lines · Subhash Jadavani
Quoted 7 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

Quoted 4 lines

It looks like you are assuming that there will be some kind of response from the device. But the spec. mentions - "OCS_FATAL ERROR: within host controller that is not covered by the error conditions described above in this table."

Yes, error interrupt doesn't happen actually.

Quoted 8 lines
Quoted 6 lines

So spec. left everything to implementers on how to trigger this error. Also, it says "within host controller" so ufshcd_is_valid_req_rsp() might fail as well.

I couldn't understand why there is a need for a host controller to interpret the SCSI command status and raise an OCS_FATAL_ERROR?

I feel like OCS values are related to syntax problem except OCS_FATAL_ERROR. Basically, host controller may need to refer the Response field[Target Success/Target Failure] of

response UPIU.

Quoted 2 lines

It's a overall result from response UPIU.
When Response field is 'Target Failure', if host controller updates the OCS field with 'SUCCESS',

it's not proper behavior.

Quoted 6 lines

In this case host may refer the Status field of response UPIU to decide the OCS result. Of course, it's not clear thing and could depends on host's implementation, because there is no obvious description for that.
But if we consider the way to report UTP error from UFSHCI 8.2.4,
we can check the Response UPIU for more detailed error condition regardless OCS value. Could you check your host side?

Quoted 6 lines

This is what our host documentation says: " SW may check the contents of Response UPIU when OCS !=0 for debug purposes. It may (or may not) help to understand the error scenario better. This is needed only for debug. When system is stable, OCS should be 0." So this clearly means that we shouldn't rely on the response UPIU if the OCS is non-zero hence simply ignore it.

Quoted 3 lines

Isn't there any description for FATAL ERROR of OCS?
Can I take like below?
Even though Response field is 'Target Failure', OCS field can get a success result.

Yes, that's true. OCS could give success result for response field can be 'Target Failure'

Quoted 5 lines

I just has focused what spec. says below:
- FATAL ERROR within host controller that is not covered by the error conditions described above in this table.
- The field contains the status code for the request.
- Host software may need to check Transfer Response UPIU's Response, and possibly Sense Data to find out more details for the cause of the error. Because host doesn't know and decide the error status, host could need to check.

No, i don't think it should be interpreted to read the response UPIU when OCS is equal to FATAL ERROR.

Quoted 74 lines
Quoted 11 lines
Quoted 2 lines

"When Response field is 'Target Failure', if host controller updates

the OCS field with 'SUCCESS', it's not proper behavior."

I doubt whether your above understanding is true from Host controller specification point of view. Host controller would not decode the “Response” field of the “response UPIU”, it would only decode the “Transaction Type” & “Task Tag” (and may be “LUN”) from the response UPIU in order to find out the correct slot in the transfer/task request list. Even 7.3.2.3 in UFSHCI spec also mentions the same.

Quoted 2 lines

I guess with this i don't see a need for this patch unless you feel otherwise.

It may depend on host's implementation. HCI spec. doesn't guide for OCS in detail. Anyway, it's not clear just now.

Thanks,
Seungwon Jeon

Quoted 55 lines

Regards,
Subhash

Quoted 52 lines

Thanks,
Seungwon Jeon

Quoted 49 lines

If it is clarified by the spec. then we can have generic implementation otherwise I would prefer to make this specific to those host controllers that raise OCS_FATAL_ERROR for CHECK_CONDITION.

Quoted 28 lines
Quoted 10 lines
---
    drivers/scsi/ufs/ufshcd.c |    3 +--
    1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +0 -0
index b743bd6..4cf3a2d 100644

drivers/scsi/ufs/ufshcd.c ufshcd_transfer_rsp_status(struct ufs_hba *hba, · line 1204

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +1 -1
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
        switch (ocs) {
        case OCS_SUCCESS:
-
+    case OCS_FATAL_ERROR:
            /* check if the returned transfer response is valid */
            result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);

I don't see the response UPIU data of the last command response is cleared anywhere in the driver. This means its quite possible that if the current command failed (and if it is using the same tag as the last succeeded command) with the OCS_FATAL_ERROR then response UPIU (pointed by lrbp->ucd_rsp_ptr) content may be still the same as the last one. If we ensure to clear the response UPIU data after every command completion then only we can rely on the response UPIU content in case of fatal errors.

Regards,
Subhash

Quoted 10 lines
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -1
            if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba,
 struct ufshcd_lrb *lrbp)
        case OCS_MISMATCH_DATA_BUF_SIZE:
        case OCS_MISMATCH_RESP_UPIU_SIZE:
        case OCS_PEER_COMM_FAILURE:
-    case OCS_FATAL_ERROR:
        default:
            result |= DID_ERROR << 16;
            dev_err(hba->dev,

--
Regards,
Sujit
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Quoted 8 lines
Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#11 lore raw

Re: [PATCH 1/7] scsi: ufs: amend the ocs handling with fatal error

In reply to Seungwon Jeon
Quoted 7 lines · Seungwon Jeon

Fatal error in OCS(overall command status) field indicates error conditions which is not covered by UFSHCI.
It means that host cannot define the result of command status and therefore host may need to check transfer response UPIU's response and status field.
It was actually found that 'CHECK CONDITION' is stored in status field of UPIU where OCS is 'FATAL ERROR'.

It seems specific to the host controller you are checking with. It doesn't seem right to interpret the scsi status -- even though the handling isn't mentioned in the spec for FATAL ERROR.

Quoted 31 lines
---
 drivers/scsi/ufs/ufshcd.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +1 -3
index b743bd6..4cf3a2d 100644
@@ -1199,7 +1199,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
        switch (ocs) {
        case OCS_SUCCESS:
-
+       case OCS_FATAL_ERROR:
                /* check if the returned transfer response is valid */
                result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
                if (result) {
@@ -1229,7 +1229,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
        case OCS_MISMATCH_DATA_BUF_SIZE:
        case OCS_MISMATCH_RESP_UPIU_SIZE:
        case OCS_PEER_COMM_FAILURE:
-       case OCS_FATAL_ERROR:
        default:
                result |= DID_ERROR << 16;
                dev_err(hba->dev,
--

1.7.0.4

Signature · 1 line
~Santosh
#12 lore raw

PATCH v2 2/3 ufs: don't disable_irq() if the IRQ can be shared among devices

In reply to Akinobu Mita

When removing the UFS driver, disable_irq() is called and the IRQ is not enabled again. Unfortunately, the IRQ is requested with IRQF_SHARED and it can be shared among several devices. So disabling the IRQ in this way is just breaking other devices which are sharing the IRQ.

Cc Vinayak Holikatti <[email protected]>
Cc Santosh Y <[email protected]>
Cc "James E.J. Bottomley" <[email protected]>
---
 drivers/scsi/ufs/ufshcd-pci.c    | 1 -
 drivers/scsi/ufs/ufshcd-pltfrm.c | 1 -
 2 files changed, 2 deletions(-)
drivers/scsi/ufs/ufshcd-pci.c +0 -1
index de4c52b..2349c0e 100644
@@ -91,7 +91,6 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
 {
 	struct ufs_hba *hba = pci_get_drvdata(pdev);
 
-	disable_irq(pdev->irq);
 	ufshcd_remove(hba);
 	pci_set_drvdata(pdev, NULL);
 }
drivers/scsi/ufs/ufshcd-pltfrm.c +0 -1
index c42db40..94ba40c 100644
@@ -144,7 +144,6 @@ static int ufshcd_pltfrm_remove(struct platform_device *pdev)
 {
 	struct ufs_hba *hba =  platform_get_drvdata(pdev);
 
-	disable_irq(hba->irq);
 	ufshcd_remove(hba);
 	return 0;
 }
Signature · 1 line
1.8.3.1
#13 lore raw

cover scsi: ufs: some fixes and updates

This path series contain driver's fixes and updates.

Seungwon Jeon (7):
scsi: ufs: amend the ocs handling with fatal error
scsi: ufs: find out sense data over scsi status values
scsi: ufs: fix the setting interrupt aggregation counter
scsi: ufs: add dme configuration primitives
scsi: ufs: add unipro attribute IDs
scsi: ufs: add operation for the uic power mode change
scsi: ufs: configure the attribute for power mode

 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |  325 +++++++++++++++++++++++++++++++++++++++------
 drivers/scsi/ufs/ufshcd.h |   54 ++++++++
 drivers/scsi/ufs/ufshci.h |   22 +++-
 drivers/scsi/ufs/unipro.h |  151 +++++++++++++++++++++
 5 files changed, 507 insertions(+), 46 deletions(-)

Thanks,
Seungwon Jeon

#14 lore raw

v2 cover scsi: ufs: some fixes and updates

This path series contain driver's fixes and updates.

Changes in v2:
- [scsi: ufs: amend the ocs handling with fatal error] is excluded. Host behavior needs to be defined clearly in UFSHCI specification.
- Some minor changes are applied with comments from Subhash, Sujit and Santosh.

Seungwon Jeon (6):
scsi: ufs: find out sense data over scsi status values
scsi: ufs: fix the setting interrupt aggregation counter
scsi: ufs: add dme configuration primitives
scsi: ufs: add unipro attribute IDs
scsi: ufs: add operation for the uic power mode change
scsi: ufs: configure the attribute for power mode

 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |  336 ++++++++++++++++++++++++++++++++++++++-------
 drivers/scsi/ufs/ufshcd.h |   54 +++++++
 drivers/scsi/ufs/ufshci.h |   22 +++-
 drivers/scsi/ufs/unipro.h |  151 ++++++++++++++++++++
 5 files changed, 513 insertions(+), 51 deletions(-)

Thanks,
Seungwon Jeon

#15 lore raw

Re: [PATCH v2 0/6] scsi: ufs: some fixes and updates

Hi Jeon,
Can you specify your dependencies pleas. I was having trouble applying it above scsi/misc branch with the following patches:

Sujit Reddy Thumma (4):
scsi: ufs: Fix broken task management command implementation
scsi: ufs: Fix hardware race conditions while aborting a command
scsi: ufs: Fix device and host reset methods
scsi: ufs: Improve UFS fatal error handling

Quoted 32 lines

This path series contain driver's fixes and updates.

Changes in v2:
- [scsi: ufs: amend the ocs handling with fatal error] is excluded. Host behavior needs to be defined clearly in UFSHCI specification.
- Some minor changes are applied with comments from Subhash, Sujit and Santosh.

Seungwon Jeon (6):
scsi: ufs: find out sense data over scsi status values
scsi: ufs: fix the setting interrupt aggregation counter
scsi: ufs: add dme configuration primitives
scsi: ufs: add unipro attribute IDs
scsi: ufs: add operation for the uic power mode change
scsi: ufs: configure the attribute for power mode

 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |  336
++++++++++++++++++++++++++++++++++++++-------
 drivers/scsi/ufs/ufshcd.h |   54 +++++++
 drivers/scsi/ufs/ufshci.h |   22 +++-
 drivers/scsi/ufs/unipro.h |  151 ++++++++++++++++++++
 5 files changed, 513 insertions(+), 51 deletions(-)

Thanks,
Seungwon Jeon

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
#16 lore raw

v3 cover scsi: ufs: some fixes and updates

In reply to Seungwon Jeon

This path series contain driver's fixes and updates.

Changes in v3:
- Rebased with scsi-next

Changes in v2:
- [scsi: ufs: amend the ocs handling with fatal error] is excluded. Host behavior needs to be defined clearly in UFSHCI specification.
- Some minor changes are applied with comments from Subhash, Sujit and Santosh.

Seungwon Jeon (6):
scsi: ufs: find out sense data over scsi status values
scsi: ufs: fix the setting interrupt aggregation counter
scsi: ufs: add dme configuration primitives
scsi: ufs: add unipro attribute IDs
scsi: ufs: add operation for the uic power mode change
scsi: ufs: configure the attribute for power mode

 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |  328 ++++++++++++++++++++++++++++++++++++++-------
 drivers/scsi/ufs/ufshcd.h |   54 ++++++++
 drivers/scsi/ufs/ufshci.h |   22 +++-
 drivers/scsi/ufs/unipro.h |  151 +++++++++++++++++++++
 5 files changed, 507 insertions(+), 49 deletions(-)

Thanks,
Seungwon Jeon

#17 lore raw

Re: [PATCH v3 0/6] scsi: ufs: some fixes and updates

Hi Santosh,

These patches looks good to me (Have already gave Reviewed-by to all patches). Please check if its fine to be pushed to scsi-next?

Regards,
Subhash

Quoted 32 lines · Seungwon Jeon

This path series contain driver's fixes and updates.

Changes in v3:
- Rebased with scsi-next

Changes in v2:
- [scsi: ufs: amend the ocs handling with fatal error] is excluded. Host behavior needs to be defined clearly in UFSHCI specification.
- Some minor changes are applied with comments from Subhash, Sujit and Santosh.

Seungwon Jeon (6):
scsi: ufs: find out sense data over scsi status values
scsi: ufs: fix the setting interrupt aggregation counter
scsi: ufs: add dme configuration primitives
scsi: ufs: add unipro attribute IDs
scsi: ufs: add operation for the uic power mode change
scsi: ufs: configure the attribute for power mode

  drivers/scsi/ufs/ufs.h    |    1 +
  drivers/scsi/ufs/ufshcd.c |  328 ++++++++++++++++++++++++++++++++++++++-------
  drivers/scsi/ufs/ufshcd.h |   54 ++++++++
  drivers/scsi/ufs/ufshci.h |   22 +++-
  drivers/scsi/ufs/unipro.h |  151 +++++++++++++++++++++
  5 files changed, 507 insertions(+), 49 deletions(-)

Thanks,
Seungwon Jeon

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#18 lore raw

PATCH v2 3/3 ufs: don't stop controller before scsi_remove_host()

In reply to Akinobu Mita

scsi_remove_host() sends SYNCHRONIZE CACHE commands for write cache enabled scsi disk devices. So stopping controller working shouldn't be done before scsi_remove_host().

Cc Vinayak Holikatti <[email protected]>
Cc Santosh Y <[email protected]>
Cc "James E.J. Bottomley" <[email protected]>
---
 drivers/scsi/ufs/ufshcd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/ufs/ufshcd.c +1 -1
index b743bd6..bd4d418 100644
@@ -1657,11 +1657,11 @@ EXPORT_SYMBOL_GPL(ufshcd_resume);
  */
 void ufshcd_remove(struct ufs_hba *hba)
 {
+	scsi_remove_host(hba->host);
 	/* disable interrupts */
 	ufshcd_disable_intr(hba, hba->intr_mask);
 	ufshcd_hba_stop(hba);
 
-	scsi_remove_host(hba->host);
 	scsi_host_put(hba->host);
 }
 EXPORT_SYMBOL_GPL(ufshcd_remove);
Signature · 1 line
1.8.3.1
#19 lore raw

PATCH 2/7 scsi: ufs: find out sense data over scsi status values

In reply to Akinobu Mita

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
 2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
 	MASK_SCSI_STATUS	= 0xFF,
 	MASK_TASK_RESPONSE	= 0xFF00,
 	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
 };
 
 /* Task management service response */
drivers/scsi/ufs/ufshcd.c +19 -8
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
 	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  *		Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
 	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
 		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
 		memcpy(lrbp->sense_buffer,
 			lrbp->ucd_rsp_ptr->sense_data,
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
 			  SAM_STAT_CHECK_CONDITION;
 		ufshcd_copy_sense_data(lrbp);
 		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
-		break;
 	case SAM_STAT_TASK_SET_FULL:
-
 		/*
 		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
 		 * depth needs to be adjusted to the exact number of
 		 * outstanding commands the LUN can handle at any given time.
 		 */
 		ufshcd_adjust_lun_qdepth(lrbp->cmd);
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
 	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		result |= scsi_status;
+		ufshcd_copy_sense_data(lrbp);
 		break;
 	default:
 		result |= DID_ERROR << 16;
Signature · 1 line
1.7.0.4
#20 lore raw

Re: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Quoted 1 line · Seungwon Jeon

Except for 'GOOD' and 'CHECK CONDITION', other status value

This is what UFS device spec says: "A GOOD status indicates successful SCSI completion and therefore no Sense Data will be returned." So please change the commit text accordingly.

Quoted 1 line

in Response UPIU may or may contain sense data. If a non-zero

s/"may or may contain ..."/"may or may not contain"

Quoted 47 lines

value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
  drivers/scsi/ufs/ufs.h    |    1 +
  drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
  2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
  	MASK_SCSI_STATUS	= 0xFF,
  	MASK_TASK_RESPONSE	= 0xFF00,
  	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
  };
 
  /* Task management service response */
drivers/scsi/ufs/ufshcd.c +14 -0
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
  	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
  }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
  /**
   * ufshcd_config_int_aggr - Configure interrupt aggregation values.
   *		Currently there is no use case where we want to configure

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) · line 316

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +2 -1
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
  static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
  {
  	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {

why if(!data_seg_len)? I guess you want to copy the sense data tot sense buffer only if the data segment length is non zero so in that case it should be "if(data_seg_len)".

Quoted 28 lines
drivers/scsi/ufs/ufshcd.c +3 -7
  		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
  		memcpy(lrbp->sense_buffer,
  			lrbp->ucd_rsp_ptr->sense_data,
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
  			  SAM_STAT_CHECK_CONDITION;
  		ufshcd_copy_sense_data(lrbp);
  		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
-		break;
  	case SAM_STAT_TASK_SET_FULL:
-
  		/*
  		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
  		 * depth needs to be adjusted to the exact number of
  		 * outstanding commands the LUN can handle at any given time.
  		 */
  		ufshcd_adjust_lun_qdepth(lrbp->cmd);
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
  	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		result |= scsi_status;
+		ufshcd_copy_sense_data(lrbp);
  		break;
  	default:
  		result |= DID_ERROR << 16;
#21 lore raw

RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Quoted 5 lines · Subhash Jadavani
Quoted 1 line · Seungwon Jeon

Except for 'GOOD' and 'CHECK CONDITION', other status value

This is what UFS device spec says: "A GOOD status indicates successful SCSI completion and therefore no Sense Data will be returned." So please change the commit text accordingly.

Ok. It may seem vague.

Quoted 3 lines
Quoted 1 line

in Response UPIU may or may contain sense data. If a non-zero

s/"may or may contain ..."/"may or may not contain"

Yes.

Quoted 49 lines
Quoted 49 lines

value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
  drivers/scsi/ufs/ufs.h    |    1 +
  drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
  2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
  	MASK_SCSI_STATUS	= 0xFF,
  	MASK_TASK_RESPONSE	= 0xFF00,
  	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
  };
 
  /* Task management service response */
drivers/scsi/ufs/ufshcd.c +14 -0
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
  	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
  }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
  /**
   * ufshcd_config_int_aggr - Configure interrupt aggregation values.
   *		Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
  static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
  {
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) · line 316

Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +2 -1
  	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {

why if(!data_seg_len)? I guess you want to copy the sense data tot sense buffer only if the data segment length is non zero so in that case it should be "if(data_seg_len)".

Correct.

Thanks,
Seungwon Jeon

#22 lore raw

Re: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

In reply to Seungwon Jeon
Quoted 59 lines · Seungwon Jeon

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
  drivers/scsi/ufs/ufs.h    |    1 +
  drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
  2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
  	MASK_SCSI_STATUS	= 0xFF,
  	MASK_TASK_RESPONSE	= 0xFF00,
  	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
  };
 
  /* Task management service response */
drivers/scsi/ufs/ufshcd.c +16 -1
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
  	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
  }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
  /**
   * ufshcd_config_int_aggr - Configure interrupt aggregation values.
   *		Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
  static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
  {
  	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
  		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
  		memcpy(lrbp->sense_buffer,
  			lrbp->ucd_rsp_ptr->sense_data,

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status) · line 1171

Quoted 1 line
drivers/scsi/ufs/ufshcd.c +0 -0
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
  			  SAM_STAT_CHECK_CONDITION;

The whole function is doing things wrong. It is redundant to interpret the scsi_status at the LLD layer and make decisions while otherwise the SCSI layer anyway knows how handle them properly.

Please see scsi_decide_disposition().

Ideally, this is what we should do -
if (response_upiu_sense_data_len > 0 && lrbp->sense_buffer) {
ufshcd_copy_sense_data();
}
result = set_host_byte(DID_OK) |

		set_msg_byte(COMMAND_COMPLETE) |
		(scsi_status & 0xff);
Quoted 24 lines
drivers/scsi/ufs/ufshcd.c +3 -7
  		ufshcd_copy_sense_data(lrbp);
  		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
-		break;
  	case SAM_STAT_TASK_SET_FULL:
-
  		/*
  		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
  		 * depth needs to be adjusted to the exact number of
  		 * outstanding commands the LUN can handle at any given time.
  		 */
  		ufshcd_adjust_lun_qdepth(lrbp->cmd);
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
  	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		result |= scsi_status;
+		ufshcd_copy_sense_data(lrbp);
  		break;
  	default:
  		result |= DID_ERROR << 16;
Signature · 2 lines
Regards,
Sujit
#23 lore raw

RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Quoted 57 lines · Sujit Reddy Thumma
Quoted 56 lines · Seungwon Jeon

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
  drivers/scsi/ufs/ufs.h    |    1 +
  drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
  2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
  	MASK_SCSI_STATUS	= 0xFF,
  	MASK_TASK_RESPONSE	= 0xFF00,
  	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
  };
 
  /* Task management service response */
drivers/scsi/ufs/ufshcd.c +16 -1
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
  	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
  }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
  /**
   * ufshcd_config_int_aggr - Configure interrupt aggregation values.
   *		Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
  static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
  {
  	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
  		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status) · line 1171

Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +0 -0
  		memcpy(lrbp->sense_buffer,
  			lrbp->ucd_rsp_ptr->sense_data,
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
  			  SAM_STAT_CHECK_CONDITION;

The whole function is doing things wrong. It is redundant to interpret the scsi_status at the LLD layer and make decisions while otherwise the SCSI layer anyway knows how handle them properly.

Yes, scsi status can be set simply here.

Quoted 8 lines

Please see scsi_decide_disposition().

Ideally, this is what we should do -
if (response_upiu_sense_data_len > 0 && lrbp->sense_buffer) {
ufshcd_copy_sense_data();
}
result = set_host_byte(DID_OK) |

How can we determine DID_OK in ufshcd_scsi_cmd_status?
I think we can add this only in case of OCS_SUCCESS in ufshcd_transfer_rsp_status.

Quoted 1 line

set_msg_byte(COMMAND_COMPLETE) |

I'm not sure that we need to set COMMAND_COMPLETE? Message is related to SCSI-2.
I didn't get this information on SAM, SBC and SPC. Do you have any idea?

Thanks,
Seungwon Jeon

Quoted 34 lines
		(scsi_status & 0xff);
Quoted 14 lines
drivers/scsi/ufs/ufshcd.c +0 -4
  		ufshcd_copy_sense_data(lrbp);
  		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
-		break;
  	case SAM_STAT_TASK_SET_FULL:
-
  		/*
  		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
  		 * depth needs to be adjusted to the exact number of
  		 * outstanding commands the LUN can handle at any given time.
  		 */
  		ufshcd_adjust_lun_qdepth(lrbp->cmd);

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status) · line 1187

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +3 -3
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
  	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		result |= scsi_status;
+		ufshcd_copy_sense_data(lrbp);
  		break;
  	default:
  		result |= DID_ERROR << 16;

--
Regards,
Sujit
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#24 lore raw

Re: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

In reply to Seungwon Jeon
Quoted 49 lines · Seungwon Jeon

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
 2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
        MASK_SCSI_STATUS        = 0xFF,
        MASK_TASK_RESPONSE      = 0xFF00,
        MASK_RSP_UPIU_RESULT    = 0xFFFF,
+       MASK_RSP_UPIU_DATA_SEG_LEN      = 0xFFFF,
 };
 
 /* Task management service response */
drivers/scsi/ufs/ufshcd.c +14 -0
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
        return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *                             from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+               MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  *             Currently there is no use case where we want to configure

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) · line 319

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +2 -1
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
        int len;
-       if (lrbp->sense_buffer) {
+       if (lrbp->sense_buffer &&
+           !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
                len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
                memcpy(lrbp->sense_buffer,
                        lrbp->ucd_rsp_ptr->sense_data,

Also please change the SCSI_SENSE_BUFFERSIZE to 18-bytes as per the spec, *in case* if wrong 'data segment length' is returned, there won't be a need to memcpy extra bytes ;-).

#define UFS_SENSE_BUFFERSIZE 18
memcpy(lrbp->sense_buffer,

			lrbp->ucd_rsp_ptr->sense_data,
			min_t(int, len, UFS_SENSE_BUFFERSIZE));
Quoted 29 lines
drivers/scsi/ufs/ufshcd.c +3 -8
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
                          SAM_STAT_CHECK_CONDITION;
                ufshcd_copy_sense_data(lrbp);
                break;
-       case SAM_STAT_BUSY:
-               result |= SAM_STAT_BUSY;
-               break;
        case SAM_STAT_TASK_SET_FULL:
-
                /*
                 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
                 * depth needs to be adjusted to the exact number of
                 * outstanding commands the LUN can handle at any given time.
                 */
                ufshcd_adjust_lun_qdepth(lrbp->cmd);
-               result |= SAM_STAT_TASK_SET_FULL;
-               break;
+       case SAM_STAT_BUSY:
        case SAM_STAT_TASK_ABORTED:
-               result |= SAM_STAT_TASK_ABORTED;
+               result |= scsi_status;
+               ufshcd_copy_sense_data(lrbp);
                break;
        default:
                result |= DID_ERROR << 16;
--

1.7.0.4

Signature · 1 line
~Santosh
#25 lore raw

RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Quoted 64 lines · Santosh Y
Quoted 49 lines · Seungwon Jeon

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
 2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
        MASK_SCSI_STATUS        = 0xFF,
        MASK_TASK_RESPONSE      = 0xFF00,
        MASK_RSP_UPIU_RESULT    = 0xFFFF,
+       MASK_RSP_UPIU_DATA_SEG_LEN      = 0xFFFF,
 };
 
 /* Task management service response */
drivers/scsi/ufs/ufshcd.c +14 -0
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
        return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *                             from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+               MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  *             Currently there is no use case where we want to configure

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) · line 319

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +2 -1
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
        int len;
-       if (lrbp->sense_buffer) {
+       if (lrbp->sense_buffer &&
+           !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
                len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
                memcpy(lrbp->sense_buffer,
                        lrbp->ucd_rsp_ptr->sense_data,

Also please change the SCSI_SENSE_BUFFERSIZE to 18-bytes as per the spec, *in case* if wrong 'data segment length' is returned, there won't be a need to memcpy extra bytes ;-).

Quoted 4 lines

#define UFS_SENSE_BUFFERSIZE 18
memcpy(lrbp->sense_buffer,

			lrbp->ucd_rsp_ptr->sense_data,
			min_t(int, len, UFS_SENSE_BUFFERSIZE));

Ok. I'll check.
But considering the extension with spec. revision, it's not bad to be kept.
Current: [#define SCSI_SENSE_BUFFERSIZE 96]

Thanks,
Seungwon Jeon

Quoted 40 lines
Quoted 28 lines
drivers/scsi/ufs/ufshcd.c +3 -8
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
                          SAM_STAT_CHECK_CONDITION;
                ufshcd_copy_sense_data(lrbp);
                break;
-       case SAM_STAT_BUSY:
-               result |= SAM_STAT_BUSY;
-               break;
        case SAM_STAT_TASK_SET_FULL:
-
                /*
                 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
                 * depth needs to be adjusted to the exact number of
                 * outstanding commands the LUN can handle at any given time.
                 */
                ufshcd_adjust_lun_qdepth(lrbp->cmd);
-               result |= SAM_STAT_TASK_SET_FULL;
-               break;
+       case SAM_STAT_BUSY:
        case SAM_STAT_TASK_ABORTED:
-               result |= SAM_STAT_TASK_ABORTED;
+               result |= scsi_status;
+               ufshcd_copy_sense_data(lrbp);
                break;
        default:
                result |= DID_ERROR << 16;
--
Quoted 3 lines

1.7.0.4

--
~Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#26 lore raw

RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

I don't know how well UFS keeps up with SCSI architecture, but SCSI has expanded from the old 18-byte fixed format sense data to a variable-length descriptor format sense data, in which an 8 byte header precedes a variable number of descriptors. This was devised to support 8-byte LBAs in the INFORMATION field and COMMAND-SPECIFIC INFORMATION field for >2 TiB drives.

A few other new features:

1. Extended INQUIRY Data VPD page
MAXIMUM SUPPORTED SENSE DATA LENGTH field:
Report how much sense data the device server is capable of returning
2. Control Extensions mode page
MAXIMUM SENSE DATA LENGTH field:
Specify how much sense data the device server is allowed to return
3. Descriptor format sense data header
SDAT_OVFL bit:
Reports that the device server had to drop some descriptors to fit in the specified length
4. "Direct-access block device sense data descriptor" Devised for the use by Base feature set compliant designs. Combined with the 8 byte header, results in 32 bytes of sense data. Always carries an INFORMATION field, COMMAND-SPECIFIC INFORMATION field, sense-key specific information, field replaceable unit code, and ILI bit.

Quoted 135 lines

-----Original Message-----
From: [email protected] [mailto:linux-scsi- [email protected]] On Behalf Of Seungwon Jeon
Sent: Tuesday, 30 July, 2013 8:03 AM
To: 'Santosh Y'
Cc: [email protected]; 'Vinayak Holikatti'; 'James E.J. Bottomley'
Subject: RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Quoted 1 line · Santosh Y

On Fri, Jul 26, 2013 at 7:16 PM, Seungwon Jeon <[email protected]>

Quoted 22 lines · wrote:
Quoted 22 lines

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
 2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
        MASK_SCSI_STATUS        = 0xFF,
        MASK_TASK_RESPONSE      = 0xFF00,
        MASK_RSP_UPIU_RESULT    = 0xFFFF,
+       MASK_RSP_UPIU_DATA_SEG_LEN      = 0xFFFF,
 };
 
Quoted 6 lines

drivers/scsi/ufs/ufshcd.c ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp

Quoted 6 lines
drivers/scsi/ufs/ufs.h +0 -0
 /* Task management service response */
drivers/scsi/ufs/ufshcd.c +0 -0
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp

*ucd_rsp_ptr)

Quoted 1 line

drivers/scsi/ufs/ufshcd.c ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp · line 218

Quoted 1 line
drivers/scsi/ufs/ufshcd.c +0 -0
        return be32_to_cpu(ucd_rsp_ptr->header.dword_1) &

MASK_RSP_UPIU_RESULT;

Quoted 12 lines
Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +10 -0
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *                             from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba

Quoted 8 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+               MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  *             Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba

*hba, unsigned int task_tag)

Quoted 10 lines

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) · line 319

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +2 -1
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
        int len;
-       if (lrbp->sense_buffer) {
+       if (lrbp->sense_buffer &&
+           !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
                len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
                memcpy(lrbp->sense_buffer,
                        lrbp->ucd_rsp_ptr->sense_data,
Quoted 8 lines

Also please change the SCSI_SENSE_BUFFERSIZE to 18-bytes as per the spec, *in case* if wrong 'data segment length' is returned, there won't be a need to memcpy extra bytes ;-).

#define UFS_SENSE_BUFFERSIZE 18
memcpy(lrbp->sense_buffer,

			lrbp->ucd_rsp_ptr->sense_data,
			min_t(int, len, UFS_SENSE_BUFFERSIZE));

Ok. I'll check.
But considering the extension with spec. revision, it's not bad to be kept.
Current: [#define SCSI_SENSE_BUFFERSIZE 96]

Thanks,
Seungwon Jeon

Quoted 3 lines

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb

Quoted 3 lines
drivers/scsi/ufs/ufshcd.c +0 -0
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb

*lrbp, int scsi_status)

Quoted 10 lines

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -4
                          SAM_STAT_CHECK_CONDITION;
                ufshcd_copy_sense_data(lrbp);
                break;
-       case SAM_STAT_BUSY:
-               result |= SAM_STAT_BUSY;
-               break;
        case SAM_STAT_TASK_SET_FULL:
-
                /*
                 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN

queue

Quoted 21 lines
Quoted 15 lines
drivers/scsi/ufs/ufshcd.c +3 -4
                 * depth needs to be adjusted to the exact number of
                 * outstanding commands the LUN can handle at any given time.
                 */
                ufshcd_adjust_lun_qdepth(lrbp->cmd);
-               result |= SAM_STAT_TASK_SET_FULL;
-               break;
+       case SAM_STAT_BUSY:
        case SAM_STAT_TASK_ABORTED:
-               result |= SAM_STAT_TASK_ABORTED;
+               result |= scsi_status;
+               ufshcd_copy_sense_data(lrbp);
                break;
        default:
                result |= DID_ERROR << 16;
--
Quoted 3 lines

1.7.0.4

Quoted 6 lines

--
~Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#27 lore raw

RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Hi Elliott,

Thank you for information.
It might be considered, if UFS has the extension someday.

Thanks,
Seungwon Jeon

Quoted 168 lines · Robert

I don't know how well UFS keeps up with SCSI architecture, but SCSI has expanded from the old 18-byte fixed format sense data to a variable-length descriptor format sense data, in which an 8 byte header precedes a variable number of descriptors. This was devised to support 8-byte LBAs in the INFORMATION field and COMMAND-SPECIFIC INFORMATION field for >2 TiB drives.

A few other new features:

1. Extended INQUIRY Data VPD page
MAXIMUM SUPPORTED SENSE DATA LENGTH field:
Report how much sense data the device server is capable of returning
2. Control Extensions mode page
MAXIMUM SENSE DATA LENGTH field:
Specify how much sense data the device server is allowed to return
3. Descriptor format sense data header
SDAT_OVFL bit:
Reports that the device server had to drop some descriptors to fit in the specified length
4. "Direct-access block device sense data descriptor" Devised for the use by Base feature set compliant designs. Combined with the 8 byte header, results in 32 bytes of sense data. Always carries an INFORMATION field, COMMAND-SPECIFIC INFORMATION field, sense-key specific information, field replaceable unit code, and ILI bit.

Quoted 131 lines

-----Original Message-----
From: [email protected] [mailto:linux-scsi- [email protected]] On Behalf Of Seungwon Jeon
Sent: Tuesday, 30 July, 2013 8:03 AM
To: 'Santosh Y'
Cc: [email protected]; 'Vinayak Holikatti'; 'James E.J. Bottomley'
Subject: RE: [PATCH 2/7] scsi: ufs: find out sense data over scsi status values

Quoted 1 line · Santosh Y

On Fri, Jul 26, 2013 at 7:16 PM, Seungwon Jeon <[email protected]>

Quoted 22 lines · wrote:
Quoted 22 lines

Except for 'GOOD' and 'CHECK CONDITION', other status value in Response UPIU may or may contain sense data. If a non-zero value is in the Data Segment Length field, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   27 +++++++++++++++++++--------
 2 files changed, 20 insertions(+), 8 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
        MASK_SCSI_STATUS        = 0xFF,
        MASK_TASK_RESPONSE      = 0xFF00,
        MASK_RSP_UPIU_RESULT    = 0xFFFF,
+       MASK_RSP_UPIU_DATA_SEG_LEN      = 0xFFFF,
 };
 
Quoted 6 lines

drivers/scsi/ufs/ufshcd.c ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp

Quoted 6 lines
drivers/scsi/ufs/ufs.h +0 -0
 /* Task management service response */
drivers/scsi/ufs/ufshcd.c +0 -0
index 4cf3a2d..688ae0e 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp

*ucd_rsp_ptr)

Quoted 1 line

drivers/scsi/ufs/ufshcd.c ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp · line 218

Quoted 1 line
drivers/scsi/ufs/ufshcd.c +0 -0
        return be32_to_cpu(ucd_rsp_ptr->header.dword_1) &

MASK_RSP_UPIU_RESULT;

Quoted 12 lines
Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +10 -0
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *                             from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba

Quoted 8 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+               MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  *             Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba

*hba, unsigned int task_tag)

Quoted 10 lines

drivers/scsi/ufs/ufshcd.c void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) · line 319

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +2 -1
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
        int len;
-       if (lrbp->sense_buffer) {
+       if (lrbp->sense_buffer &&
+           !ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
                len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
                memcpy(lrbp->sense_buffer,
                        lrbp->ucd_rsp_ptr->sense_data,
Quoted 8 lines

Also please change the SCSI_SENSE_BUFFERSIZE to 18-bytes as per the spec, *in case* if wrong 'data segment length' is returned, there won't be a need to memcpy extra bytes ;-).

#define UFS_SENSE_BUFFERSIZE 18
memcpy(lrbp->sense_buffer,

			lrbp->ucd_rsp_ptr->sense_data,
			min_t(int, len, UFS_SENSE_BUFFERSIZE));

Ok. I'll check.
But considering the extension with spec. revision, it's not bad to be kept.
Current: [#define SCSI_SENSE_BUFFERSIZE 96]

Thanks,
Seungwon Jeon

Quoted 3 lines

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb

Quoted 3 lines
drivers/scsi/ufs/ufshcd.c +0 -0
@@ -1156,21 +1171,17 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb

*lrbp, int scsi_status)

Quoted 10 lines

drivers/scsi/ufs/ufshcd.c ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +0 -4
                          SAM_STAT_CHECK_CONDITION;
                ufshcd_copy_sense_data(lrbp);
                break;
-       case SAM_STAT_BUSY:
-               result |= SAM_STAT_BUSY;
-               break;
        case SAM_STAT_TASK_SET_FULL:
-
                /*
                 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN

queue

Quoted 21 lines
Quoted 15 lines
drivers/scsi/ufs/ufshcd.c +3 -4
                 * depth needs to be adjusted to the exact number of
                 * outstanding commands the LUN can handle at any given time.
                 */
                ufshcd_adjust_lun_qdepth(lrbp->cmd);
-               result |= SAM_STAT_TASK_SET_FULL;
-               break;
+       case SAM_STAT_BUSY:
        case SAM_STAT_TASK_ABORTED:
-               result |= SAM_STAT_TASK_ABORTED;
+               result |= scsi_status;
+               ufshcd_copy_sense_data(lrbp);
                break;
        default:
                result |= DID_ERROR << 16;
--
Quoted 3 lines

1.7.0.4

Quoted 6 lines

--
~Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#28 lore raw

PATCH v2 1/6 scsi: ufs: find out sense data over scsi status values

In reply to Seungwon Jeon

Unlike 'GOOD' and 'CHECK CONDITION', other status values in Response UPIU may or may not contain sense data. That is returning sense data isn't obvious. So, in this case the Data Segment Length field should be checked. If a non-zero value, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   37 ++++++++++++++++++++++---------------
 2 files changed, 23 insertions(+), 15 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index 139bc06..737c31b 100644
@@ -114,6 +114,7 @@ enum {
 	MASK_SCSI_STATUS	= 0xFF,
 	MASK_TASK_RESPONSE	= 0xFF00,
 	MASK_RSP_UPIU_RESULT	= 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
 };
 
 /* Task management service response */
drivers/scsi/ufs/ufshcd.c +22 -15
index b743bd6..529a0fc 100644
@@ -218,6 +218,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
 	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline unsigned int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_config_int_aggr - Configure interrupt aggregation values.
  *		Currently there is no use case where we want to configure
@@ -298,7 +312,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
 	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
 		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
 		memcpy(lrbp->sense_buffer,
 			lrbp->ucd_rsp_ptr->sense_data,
@@ -1145,32 +1160,24 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
 	int result = 0;
 
 	switch (scsi_status) {
-	case SAM_STAT_GOOD:
-		result |= DID_OK << 16 |
-			  COMMAND_COMPLETE << 8 |
-			  SAM_STAT_GOOD;
-		break;
 	case SAM_STAT_CHECK_CONDITION:
+		ufshcd_copy_sense_data(lrbp);
+	case SAM_STAT_GOOD:
 		result |= DID_OK << 16 |
 			  COMMAND_COMPLETE << 8 |
-			  SAM_STAT_CHECK_CONDITION;
-		ufshcd_copy_sense_data(lrbp);
-		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
+			  scsi_status;
 		break;
 	case SAM_STAT_TASK_SET_FULL:
-
 		/*
 		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
 		 * depth needs to be adjusted to the exact number of
 		 * outstanding commands the LUN can handle at any given time.
 		 */
 		ufshcd_adjust_lun_qdepth(lrbp->cmd);
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
 	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		ufshcd_copy_sense_data(lrbp);
+		result |= scsi_status;
 		break;
 	default:
 		result |= DID_ERROR << 16;
Signature · 1 line
1.7.0.4
#29 lore raw

PATCH v2 2/6 scsi: ufs: fix the setting interrupt aggregation counter

In reply to Seungwon Jeon

IACTH(Interrupt aggregation counter threshold) value is allowed up to 0x1F and current setting value is the maximum. This value is related with NUTRS(max:0x20) of HCI's capability. Considering HCI controller doesn't support the maximum, IACTH setting should be adjusted with possible value.
For that, existing 'ufshcd_config_int_aggr' is split into two part [reset, configure].

---
 drivers/scsi/ufs/ufshcd.c |   53 +++++++++++++++++++++-----------------------
 drivers/scsi/ufs/ufshci.h |    4 +-
 2 files changed, 27 insertions(+), 30 deletions(-)
drivers/scsi/ufs/ufshcd.c +25 -28
index 529a0fc..2263795 100644
@@ -43,6 +43,9 @@
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT	500
 
+/* Interrupt aggregation default timeout, unit: 40us */
+#define INT_AGGR_DEF_TO	0x02
+
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
 	UFSHCD_MAX_ID		= 1,
@@ -65,12 +68,6 @@ enum {
 	UFSHCD_INT_CLEAR,
 };
 
-/* Interrupt aggregation options */
-enum {
-	INT_AGGR_RESET,
-	INT_AGGR_CONFIG,
-};
-
 /**
  * ufshcd_get_intr_mask - Get the interrupt bit mask
  * @hba - Pointer to adapter instance
@@ -233,30 +230,30 @@ ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
 }
 
 /**
- * ufshcd_config_int_aggr - Configure interrupt aggregation values.
- *		Currently there is no use case where we want to configure
- *		interrupt aggregation dynamically. So to configure interrupt
- *		aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
- *		INT_AGGR_TIMEOUT_VALUE are used.
+ * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
  * @hba: per adapter instance
- * @option: Interrupt aggregation option
  */
 static inline void
-ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+ufshcd_reset_intr_aggr(struct ufs_hba *hba)
 {
-	switch (option) {
-	case INT_AGGR_RESET:
-		ufshcd_writel(hba, INT_AGGR_ENABLE |
-			      INT_AGGR_COUNTER_AND_TIMER_RESET,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	case INT_AGGR_CONFIG:
-		ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
-			      INT_AGGR_COUNTER_THRESHOLD_VALUE |
-			      INT_AGGR_TIMEOUT_VALUE,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	}
+	ufshcd_writel(hba, INT_AGGR_ENABLE |
+		      INT_AGGR_COUNTER_AND_TIMER_RESET,
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
+ * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
+ * @hba: per adapter instance
+ * @cntr_thld: Interrupt aggregation counter threshold
+ * @timeout: Interrupt aggregation timeout value
+ */
+static inline void
+ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 timeout)
+{
+	ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
+		      INT_AGGR_TIMEOUT_VAL(timeout),
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 }
 
 /**
@@ -853,7 +850,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
 	/* Configure interrupt aggregation */
-	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+	ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
 
 	/* Configure UTRL and UTMRL base address registers */
 	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -1296,7 +1293,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 	hba->outstanding_reqs ^= completed_reqs;
 
 	/* Reset interrupt aggregation counters */
-	ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+	ufshcd_reset_intr_aggr(hba);
 }
 
 /**
drivers/scsi/ufs/ufshci.h +2 -2
index d5c5f14..a8f69cc 100644
@@ -226,8 +226,8 @@ enum {
 
 #define MASK_UIC_COMMAND_RESULT			0xFF
 
-#define INT_AGGR_COUNTER_THRESHOLD_VALUE	(0x1F << 8)
-#define INT_AGGR_TIMEOUT_VALUE			(0x02)
+#define INT_AGGR_COUNTER_THLD_VAL(c)	(((c) & 0x1F) << 8)
+#define INT_AGGR_TIMEOUT_VAL(t)		(((t) & 0xFF) << 0)
 
 /* Interrupt disable masks */
 enum {
Signature · 1 line
1.7.0.4
#30 lore raw

PATCH v2 3/6 scsi: ufs: add dme configuration primitives

In reply to Seungwon Jeon

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
 drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshci.h |    6 +++
 3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +88 -0
index 2263795..7fc262e 100644
@@ -188,6 +188,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
  * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
  * @ucd_rsp_ptr: pointer to response UPIU
  *
@@ -821,6 +833,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1253,6 +1339,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
 	if (hba->active_uic_cmd) {
 		hba->active_uic_cmd->argument2 |=
 			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);
 		complete(&hba->active_uic_cmd->done);
 	}
 }
drivers/scsi/ufs/ufshcd.h +51 -0

61 lines not on this page · raw message

drivers/scsi/ufs/ufshci.h +6 -0
index a8f69cc..df4901e 100644
@@ -191,6 +191,12 @@ enum {
 #define CONFIG_RESULT_CODE_MASK		0xFF
 #define GENERIC_ERROR_CODE_MASK		0xFF
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
 /* UIC Commands */
 enum {
 	UIC_CMD_DME_GET			= 0x01,
Signature · 1 line
1.7.0.4
#31 lore raw

PATCH v2 4/6 scsi: ufs: add unipro attribute IDs

In reply to Seungwon Jeon

'drivers/scsi/ufs/unipro.h' is added. Attributes in the layers of the UNIPRO stack can be read and written via the DME.

---
 drivers/scsi/ufs/unipro.h |  130 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/ufs/unipro.h
drivers/scsi/ufs/unipro.h new file +130 -0
new file mode 100644
index 0000000..3a710eb
@@ -0,0 +1,130 @@
+/*
+ * drivers/scsi/ufs/unipro.h
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UNIPRO_H_
+#define _UNIPRO_H_
+
+/*
+ * PHY Adpater attributes
+ */
+#define PA_ACTIVETXDATALANES	0x1560
+#define PA_ACTIVERXDATALANES	0x1580
+#define PA_TXTRAILINGCLOCKS	0x1564
+#define PA_PHY_TYPE		0x1500
+#define PA_AVAILTXDATALANES	0x1520
+#define PA_AVAILRXDATALANES	0x1540
+#define PA_MINRXTRAILINGCLOCKS	0x1543
+#define PA_TXPWRSTATUS		0x1567
+#define PA_RXPWRSTATUS		0x1582
+#define PA_TXFORCECLOCK		0x1562
+#define PA_TXPWRMODE		0x1563
+#define PA_LEGACYDPHYESCDL	0x1570
+#define PA_MAXTXSPEEDFAST	0x1521
+#define PA_MAXTXSPEEDSLOW	0x1522
+#define PA_MAXRXSPEEDFAST	0x1541
+#define PA_MAXRXSPEEDSLOW	0x1542
+#define PA_TXLINKSTARTUPHS	0x1544
+#define PA_TXSPEEDFAST		0x1565
+#define PA_TXSPEEDSLOW		0x1566
+#define PA_REMOTEVERINFO	0x15A0
+#define PA_TXGEAR		0x1568
+#define PA_TXTERMINATION	0x1569
+#define PA_HSSERIES		0x156A
+#define PA_PWRMODE		0x1571
+#define PA_RXGEAR		0x1583
+#define PA_RXTERMINATION	0x1584
+#define PA_MAXRXPWMGEAR		0x1586
+#define PA_MAXRXHSGEAR		0x1587
+#define PA_RXHSUNTERMCAP	0x15A5
+#define PA_RXLSTERMCAP		0x15A6
+#define PA_PACPREQTIMEOUT	0x1590
+#define PA_PACPREQEOBTIMEOUT	0x1591
+#define PA_HIBERN8TIME		0x15A7
+#define PA_LOCALVERINFO		0x15A9
+#define PA_TACTIVATE		0x15A8
+#define PA_PACPFRAMECOUNT	0x15C0
+#define PA_PACPERRORCOUNT	0x15C1
+#define PA_PHYTESTCONTROL	0x15C2
+#define PA_PWRMODEUSERDATA0	0x15B0
+#define PA_PWRMODEUSERDATA1	0x15B1
+#define PA_PWRMODEUSERDATA2	0x15B2
+#define PA_PWRMODEUSERDATA3	0x15B3
+#define PA_PWRMODEUSERDATA4	0x15B4
+#define PA_PWRMODEUSERDATA5	0x15B5
+#define PA_PWRMODEUSERDATA6	0x15B6
+#define PA_PWRMODEUSERDATA7	0x15B7
+#define PA_PWRMODEUSERDATA8	0x15B8
+#define PA_PWRMODEUSERDATA9	0x15B9
+#define PA_PWRMODEUSERDATA10	0x15BA
+#define PA_PWRMODEUSERDATA11	0x15BB
+#define PA_CONNECTEDTXDATALANES	0x1561
+#define PA_CONNECTEDRXDATALANES	0x1581
+#define PA_LOGICALLANEMAP	0x15A1
+#define PA_SLEEPNOCONFIGTIME	0x15A2
+#define PA_STALLNOCONFIGTIME	0x15A3
+#define PA_SAVECONFIGTIME	0x15A4
+
+/*
+ * Data Link Layer Attributes
+ */
+#define DL_TC0TXFCTHRESHOLD	0x2040
+#define DL_FC0PROTTIMEOUTVAL	0x2041
+#define DL_TC0REPLAYTIMEOUTVAL	0x2042
+#define DL_AFC0REQTIMEOUTVAL	0x2043
+#define DL_AFC0CREDITTHRESHOLD	0x2044
+#define DL_TC0OUTACKTHRESHOLD	0x2045
+#define DL_TC1TXFCTHRESHOLD	0x2060
+#define DL_FC1PROTTIMEOUTVAL	0x2061
+#define DL_TC1REPLAYTIMEOUTVAL	0x2062
+#define DL_AFC1REQTIMEOUTVAL	0x2063
+#define DL_AFC1CREDITTHRESHOLD	0x2064
+#define DL_TC1OUTACKTHRESHOLD	0x2065
+#define DL_TXPREEMPTIONCAP	0x2000
+#define DL_TC0TXMAXSDUSIZE	0x2001
+#define DL_TC0RXINITCREDITVAL	0x2002
+#define DL_TC0TXBUFFERSIZE	0x2005
+#define DL_PEERTC0PRESENT	0x2046
+#define DL_PEERTC0RXINITCREVAL	0x2047
+#define DL_TC1TXMAXSDUSIZE	0x2003
+#define DL_TC1RXINITCREDITVAL	0x2004
+#define DL_TC1TXBUFFERSIZE	0x2006
+#define DL_PEERTC1PRESENT	0x2066
+#define DL_PEERTC1RXINITCREVAL	0x2067
+
+/*
+ * Network Layer Attributes
+ */
+#define N_DEVICEID		0x3000
+#define N_DEVICEID_VALID	0x3001
+#define N_TC0TXMAXSDUSIZE	0x3020
+#define N_TC1TXMAXSDUSIZE	0x3021
+
+/*
+ * Transport Layer Attributes
+ */
+#define T_NUMCPORTS		0x4000
+#define T_NUMTESTFEATURES	0x4001
+#define T_CONNECTIONSTATE	0x4020
+#define T_PEERDEVICEID		0x4021
+#define T_PEERCPORTID		0x4022
+#define T_TRAFFICCLASS		0x4023
+#define T_PROTOCOLID		0x4024
+#define T_CPORTFLAGS		0x4025
+#define T_TXTOKENVALUE		0x4026
+#define T_RXTOKENVALUE		0x4027
+#define T_LOCALBUFFERSPACE	0x4028
+#define T_PEERBUFFERSPACE	0x4029
+#define T_CREDITSTOSEND		0x402A
+#define T_CPORTMODE		0x402B
+#define T_TC0TXMAXSDUSIZE	0x4060
+#define T_TC1TXMAXSDUSIZE	0x4061
+
+#endif /* _UNIPRO_H_ */
Signature · 1 line
1.7.0.4
#32 lore raw

PATCH v2 5/6 scsi: ufs: add operation for the uic power mode change

In reply to Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
 drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/scsi/ufs/ufshcd.h |    3 ++
 drivers/scsi/ufs/ufshci.h |   12 ++++++
 3 files changed, 95 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +80 -4
index 7fc262e..8f25df4 100644
@@ -36,9 +36,11 @@
 #include <linux/async.h>
 
 #include "ufshcd.h"
+#include "unipro.h"
 
 #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
 				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
 				 UFSHCD_ERROR_MASK)
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT	500
@@ -359,6 +361,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
  * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
  * @hba: per adapter instance
  * @uic_cmd: UIC command
@@ -907,6 +921,64 @@ out:
 EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
 /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x uic error %d\n",
+			mode, ret);
+		goto out;
+	}
+
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x completion timeout\n",
+			mode);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1333,16 +1405,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 /**
  * ufshcd_uic_cmd_compl - handle completion of uic command
  * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
  */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
 {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
 		hba->active_uic_cmd->argument2 |=
 			ufshcd_get_uic_cmd_result(hba);
 		hba->active_uic_cmd->argument3 =
 			ufshcd_get_dme_attr_val(hba);
 		complete(&hba->active_uic_cmd->done);
 	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
 }
 
 /**
@@ -1444,8 +1520,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
 	if (hba->errors)
 		ufshcd_err_handler(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
 	if (intr_status & UTP_TASK_REQ_COMPL)
 		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index 50bcd29..3956579 100644
@@ -142,6 +142,7 @@ struct ufshcd_lrb {
  * @uic_cmd_mutex: mutex for uic command
  * @ufshcd_tm_wait_queue: wait queue for task management
  * @tm_condition: condition variable for task management
+ * @pwr_done: completion for power mode change
  * @ufshcd_state: UFSHCD states
  * @intr_mask: Interrupt Mask Bits
  * @feh_workq: Work queue for fatal controller error handling
@@ -180,6 +181,8 @@ struct ufs_hba {
 	wait_queue_head_t ufshcd_tm_wait_queue;
 	unsigned long tm_condition;
 
+	struct completion *pwr_done;
+
 	u32 ufshcd_state;
 	u32 intr_mask;
 
drivers/scsi/ufs/ufshci.h +12 -0
index df4901e..d92fd9c 100644
@@ -124,6 +124,9 @@ enum {
 #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
 #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
 #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
 				DEVICE_FATAL_ERROR |\
 				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
 #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
 #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
 /* HCE - Host Controller Enable 34h */
 #define CONTROLLER_ENABLE	UFS_BIT(0)
 #define CONTROLLER_DISABLE	0x0
Signature · 1 line
1.7.0.4
#33 lore raw

PATCH v2 6/6 scsi: ufs: configure the attribute for power mode

In reply to Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
 drivers/scsi/ufs/ufshcd.c |   74 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/scsi/ufs/unipro.h |   21 +++++++++++++
 2 files changed, 93 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +72 -2
index 8f25df4..54fdbab 100644
@@ -979,6 +979,70 @@ out:
 }
 
 /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int i, ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);
+
+	return ret;
+}
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1755,8 +1819,14 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
 	int ret;
 
 	ret = ufshcd_link_startup(hba);
-	if (!ret)
-		scsi_scan_host(hba->host);
+	if (ret)
+		goto out;
+
+	ufshcd_config_max_pwr_mode(hba);
+
+	scsi_scan_host(hba->host);
+out:
+	return;
 }
 
 static struct scsi_host_template ufshcd_driver_template = {
drivers/scsi/ufs/unipro.h +21 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
 #define PA_STALLNOCONFIGTIME	0x15A3
 #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
 /*
  * Data Link Layer Attributes
  */
@@ -127,4 +142,10 @@
 #define T_TC0TXMAXSDUSIZE	0x4060
 #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
 #endif /* _UNIPRO_H_ */
Signature · 1 line
1.7.0.4
#34 lore raw

PATCH v3 1/6 scsi: ufs: find out sense data over scsi status values

In reply to Seungwon Jeon

Unlike 'GOOD' and 'CHECK CONDITION', other status values in Response UPIU may or may not contain sense data. That is returning sense data isn't obvious. So, in this case the Data Segment Length field should be checked. If a non-zero value, it means that UPIU has Sense Data in the Data Segment area.

---
 drivers/scsi/ufs/ufs.h    |    1 +
 drivers/scsi/ufs/ufshcd.c |   37 ++++++++++++++++++++++---------------
 2 files changed, 23 insertions(+), 15 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index bce09a6..7210500 100644
@@ -177,6 +177,7 @@ enum {
 	MASK_TASK_RESPONSE              = 0xFF00,
 	MASK_RSP_UPIU_RESULT            = 0xFFFF,
 	MASK_QUERY_DATA_SEG_LEN         = 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
 	MASK_RSP_EXCEPTION_EVENT        = 0x10000,
 };
 
drivers/scsi/ufs/ufshcd.c +22 -15
index 1b99c0a..6ff16c9 100644
@@ -310,6 +310,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
 	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline unsigned int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
  * ufshcd_is_exception_event - Check if the device raised an exception event
  * @ucd_rsp_ptr: pointer to response UPIU
@@ -405,7 +419,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
 	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
 		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len);
 		memcpy(lrbp->sense_buffer,
 			lrbp->ucd_rsp_ptr->sr.sense_data,
@@ -1789,32 +1804,24 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
 	int result = 0;
 
 	switch (scsi_status) {
-	case SAM_STAT_GOOD:
-		result |= DID_OK << 16 |
-			  COMMAND_COMPLETE << 8 |
-			  SAM_STAT_GOOD;
-		break;
 	case SAM_STAT_CHECK_CONDITION:
+		ufshcd_copy_sense_data(lrbp);
+	case SAM_STAT_GOOD:
 		result |= DID_OK << 16 |
 			  COMMAND_COMPLETE << 8 |
-			  SAM_STAT_CHECK_CONDITION;
-		ufshcd_copy_sense_data(lrbp);
-		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
+			  scsi_status;
 		break;
 	case SAM_STAT_TASK_SET_FULL:
-
 		/*
 		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
 		 * depth needs to be adjusted to the exact number of
 		 * outstanding commands the LUN can handle at any given time.
 		 */
 		ufshcd_adjust_lun_qdepth(lrbp->cmd);
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
 	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		ufshcd_copy_sense_data(lrbp);
+		result |= scsi_status;
 		break;
 	default:
 		result |= DID_ERROR << 16;
Signature · 1 line
1.7.0.4
Reviewed-by on v3 1/6
#35 lore raw

Re: [PATCH v3 1/6] scsi: ufs: find out sense data over scsi status values

Looks good to me.

Quoted 99 lines · Seungwon Jeon

Unlike 'GOOD' and 'CHECK CONDITION', other status values in Response UPIU may or may not contain sense data. That is returning sense data isn't obvious. So, in this case the Data Segment Length field should be checked. If a non-zero value, it means that UPIU has Sense Data in the Data Segment area.

---
  drivers/scsi/ufs/ufs.h    |    1 +
  drivers/scsi/ufs/ufshcd.c |   37 ++++++++++++++++++++++---------------
  2 files changed, 23 insertions(+), 15 deletions(-)
drivers/scsi/ufs/ufs.h +1 -0
index bce09a6..7210500 100644
@@ -177,6 +177,7 @@ enum {
  	MASK_TASK_RESPONSE              = 0xFF00,
  	MASK_RSP_UPIU_RESULT            = 0xFFFF,
  	MASK_QUERY_DATA_SEG_LEN         = 0xFFFF,
+	MASK_RSP_UPIU_DATA_SEG_LEN	= 0xFFFF,
  	MASK_RSP_EXCEPTION_EVENT        = 0x10000,
  };
 
drivers/scsi/ufs/ufshcd.c +22 -15
index 1b99c0a..6ff16c9 100644
@@ -310,6 +310,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
  	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
  }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *				from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline unsigned int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+		MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
  /**
   * ufshcd_is_exception_event - Check if the device raised an exception event
   * @ucd_rsp_ptr: pointer to response UPIU
@@ -405,7 +419,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
  static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
  {
  	int len;
-	if (lrbp->sense_buffer) {
+	if (lrbp->sense_buffer &&
+	    ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
  		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len);
  		memcpy(lrbp->sense_buffer,
  			lrbp->ucd_rsp_ptr->sr.sense_data,
@@ -1789,32 +1804,24 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
  	int result = 0;
 
  	switch (scsi_status) {
-	case SAM_STAT_GOOD:
-		result |= DID_OK << 16 |
-			  COMMAND_COMPLETE << 8 |
-			  SAM_STAT_GOOD;
-		break;
  	case SAM_STAT_CHECK_CONDITION:
+		ufshcd_copy_sense_data(lrbp);
+	case SAM_STAT_GOOD:
  		result |= DID_OK << 16 |
  			  COMMAND_COMPLETE << 8 |
-			  SAM_STAT_CHECK_CONDITION;
-		ufshcd_copy_sense_data(lrbp);
-		break;
-	case SAM_STAT_BUSY:
-		result |= SAM_STAT_BUSY;
+			  scsi_status;
  		break;
  	case SAM_STAT_TASK_SET_FULL:
-
  		/*
  		 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
  		 * depth needs to be adjusted to the exact number of
  		 * outstanding commands the LUN can handle at any given time.
  		 */
  		ufshcd_adjust_lun_qdepth(lrbp->cmd);
-		result |= SAM_STAT_TASK_SET_FULL;
-		break;
+	case SAM_STAT_BUSY:
  	case SAM_STAT_TASK_ABORTED:
-		result |= SAM_STAT_TASK_ABORTED;
+		ufshcd_copy_sense_data(lrbp);
+		result |= scsi_status;
  		break;
  	default:
  		result |= DID_ERROR << 16;
Tested-by
#36 lore raw

RE: [PATCH v3 1/6] scsi: ufs: find out sense data over scsi status values

QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

= > -----Original Message-----
= > From: [email protected] [mailto:linux-scsi-
= > [email protected]] On Behalf Of Subhash Jadavani
= > Sent: Tuesday, August 27, 2013 11:53 AM
= > To: Seungwon Jeon
= > Cc: [email protected]; 'Vinayak Holikatti'; 'Santosh Y'; 'James E.J. = > Bottomley'
= > Subject: Re: [PATCH v3 1/6] scsi: ufs: find out sense data over scsi status = > values
= >
= >
= > Looks good to me.
= > Reviewed-by: Subhash Jadavani <[email protected]>
= >
= > On 8/26/2013 8:10 PM, Seungwon Jeon wrote:
= > > Unlike 'GOOD' and 'CHECK CONDITION', other status values in Response = > > UPIU may or may not contain sense data. That is returning sense data = > > isn't obvious. So, in this case the Data Segment Length field should = > > be checked. If a non-zero value, it means that UPIU has Sense Data in = > > the Data Segment area.
= > >
= > > Signed-off-by: Seungwon Jeon <[email protected]>
= > > Reviewed-by: Subhash Jadavani <[email protected]>
= > > ---

= > >   drivers/scsi/ufs/ufs.h    |    1 +
= > >   drivers/scsi/ufs/ufshcd.c |   37 ++++++++++++++++++++++---------------
= > >   2 files changed, 23 insertions(+), 15 deletions(-)

= > >
= > > diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index = > > bce09a6..7210500 100644
= > > --- a/drivers/scsi/ufs/ufs.h
= > > +++ b/drivers/scsi/ufs/ufs.h
= > > @@ -177,6 +177,7 @@ enum {

= > >   	MASK_TASK_RESPONSE              = 0xFF00,
= > >   	MASK_RSP_UPIU_RESULT            = 0xFFFF,
= > >   	MASK_QUERY_DATA_SEG_LEN         = 0xFFFF,

= > > + MASK_RSP_UPIU_DATA_SEG_LEN = 0xFFFF,
= > > MASK_RSP_EXCEPTION_EVENT = 0x10000,
= > > };
= > >
= > > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c = > > index 1b99c0a..6ff16c9 100644
= > > --- a/drivers/scsi/ufs/ufshcd.c
= > > +++ b/drivers/scsi/ufs/ufshcd.c
= > > @@ -310,6 +310,20 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp = > *ucd_rsp_ptr)
= > > return be32_to_cpu(ucd_rsp_ptr->header.dword_1) &
= > MASK_RSP_UPIU_RESULT;
= > > }
= > >
= > > +/*
= > > + * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
= > > + * from response UPIU
= > > + * @ucd_rsp_ptr: pointer to response UPIU
= > > + *
= > > + * Return the data segment length.
= > > + */
= > > +static inline unsigned int
= > > +ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr) {
= > > + return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
= > > + MASK_RSP_UPIU_DATA_SEG_LEN;
= > > +}
= > > +
= > > /**
= > > * ufshcd_is_exception_event - Check if the device raised an exception
= > event
= > > * @ucd_rsp_ptr: pointer to response UPIU @@ -405,7 +419,8 @@ void
= > > ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)

= > >   static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
= > >   {
= > >   	int len;
= > > -	if (lrbp->sense_buffer) {
= > > +	if (lrbp->sense_buffer &&
= > > +	    ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
= > >   		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len);
= > >   		memcpy(lrbp->sense_buffer,
= > >   			lrbp->ucd_rsp_ptr->sr.sense_data, @@ -1789,32

= > +1804,24 @@
= > > ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
= > > int result = 0;
= > >
= > > switch (scsi_status) {
= > > - case SAM_STAT_GOOD:
= > > - result |= DID_OK << 16 |
= > > - COMMAND_COMPLETE << 8 |
= > > - SAM_STAT_GOOD;
= > > - break;
= > > case SAM_STAT_CHECK_CONDITION:
= > > + ufshcd_copy_sense_data(lrbp);
= > > + case SAM_STAT_GOOD:
= > > result |= DID_OK << 16 |
= > > COMMAND_COMPLETE << 8 |
= > > - SAM_STAT_CHECK_CONDITION;
= > > - ufshcd_copy_sense_data(lrbp);
= > > - break;
= > > - case SAM_STAT_BUSY:
= > > - result |= SAM_STAT_BUSY;
= > > + scsi_status;
= > > break;
= > > case SAM_STAT_TASK_SET_FULL:
= > > -
= > > /*
= > > * If a LUN reports SAM_STAT_TASK_SET_FULL, then the
= > LUN queue
= > > * depth needs to be adjusted to the exact number of
= > > * outstanding commands the LUN can handle at any given
= > time.
= > > */
= > > ufshcd_adjust_lun_qdepth(lrbp->cmd);
= > > - result |= SAM_STAT_TASK_SET_FULL;
= > > - break;
= > > + case SAM_STAT_BUSY:
= > > case SAM_STAT_TASK_ABORTED:
= > > - result |= SAM_STAT_TASK_ABORTED;
= > > + ufshcd_copy_sense_data(lrbp);
= > > + result |= scsi_status;

= > >   		break;
= > >   	default:
= > >   		result |= DID_ERROR << 16;

= >
= > --
= > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the = > body of a message to [email protected] More majordomo info = > at http://vger.kernel.org/majordomo-info.html

#37 lore raw

PATCH v3 2/6 scsi: ufs: fix the setting interrupt aggregation counter

In reply to Seungwon Jeon

IACTH(Interrupt aggregation counter threshold) value is allowed up to 0x1F and current setting value is the maximum. This value is related with NUTRS(max:0x20) of HCI's capability. Considering HCI controller doesn't support the maximum, IACTH setting should be adjusted with possible value.
For that, existing 'ufshcd_config_int_aggr' is split into two part [reset, configure].

---
 drivers/scsi/ufs/ufshcd.c |   53 +++++++++++++++++++++-----------------------
 drivers/scsi/ufs/ufshci.h |    4 +-
 2 files changed, 27 insertions(+), 30 deletions(-)
drivers/scsi/ufs/ufshcd.c +25 -28
index 6ff16c9..c90b88a 100644
@@ -59,6 +59,9 @@
 /* Expose the flag value from utp_upiu_query.value */
 #define MASK_QUERY_UPIU_FLAG_LOC 0xFF
 
+/* Interrupt aggregation default timeout, unit: 40us */
+#define INT_AGGR_DEF_TO	0x02
+
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
 	UFSHCD_MAX_ID		= 1,
@@ -94,12 +97,6 @@ enum {
 	UFSHCD_INT_CLEAR,
 };
 
-/* Interrupt aggregation options */
-enum {
-	INT_AGGR_RESET,
-	INT_AGGR_CONFIG,
-};
-
 #define ufshcd_set_eh_in_progress(h) \
 	(h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
 #define ufshcd_eh_in_progress(h) \
@@ -340,30 +337,30 @@ static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr)
 }
 
 /**
- * ufshcd_config_int_aggr - Configure interrupt aggregation values.
- *		Currently there is no use case where we want to configure
- *		interrupt aggregation dynamically. So to configure interrupt
- *		aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
- *		INT_AGGR_TIMEOUT_VALUE are used.
+ * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
  * @hba: per adapter instance
- * @option: Interrupt aggregation option
  */
 static inline void
-ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+ufshcd_reset_intr_aggr(struct ufs_hba *hba)
 {
-	switch (option) {
-	case INT_AGGR_RESET:
-		ufshcd_writel(hba, INT_AGGR_ENABLE |
-			      INT_AGGR_COUNTER_AND_TIMER_RESET,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	case INT_AGGR_CONFIG:
-		ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
-			      INT_AGGR_COUNTER_THRESHOLD_VALUE |
-			      INT_AGGR_TIMEOUT_VALUE,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	}
+	ufshcd_writel(hba, INT_AGGR_ENABLE |
+		      INT_AGGR_COUNTER_AND_TIMER_RESET,
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
+ * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
+ * @hba: per adapter instance
+ * @cnt: Interrupt aggregation counter threshold
+ * @tmout: Interrupt aggregation timeout value
+ */
+static inline void
+ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout)
+{
+	ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
+		      INT_AGGR_TIMEOUT_VAL(tmout),
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 }
 
 /**
@@ -1523,7 +1520,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
 	/* Configure interrupt aggregation */
-	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+	ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
 
 	/* Configure UTRL and UTMRL base address registers */
 	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -1971,7 +1968,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 
 	/* Reset interrupt aggregation counters */
 	if (int_aggr_reset)
-		ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+		ufshcd_reset_intr_aggr(hba);
 }
 
 /**
drivers/scsi/ufs/ufshci.h +2 -2
index f1e1b74..739ae3a 100644
@@ -226,8 +226,8 @@ enum {
 
 #define MASK_UIC_COMMAND_RESULT			0xFF
 
-#define INT_AGGR_COUNTER_THRESHOLD_VALUE	(0x1F << 8)
-#define INT_AGGR_TIMEOUT_VALUE			(0x02)
+#define INT_AGGR_COUNTER_THLD_VAL(c)	(((c) & 0x1F) << 8)
+#define INT_AGGR_TIMEOUT_VAL(t)		(((t) & 0xFF) << 0)
 
 /* Interrupt disable masks */
 enum {
Signature · 1 line
1.7.0.4
Reviewed-by on v3 2/6
#38 lore raw

Re: [PATCH v3 2/6] scsi: ufs: fix the setting interrupt aggregation counter

Looks good to me.

Quoted 126 lines · Seungwon Jeon

IACTH(Interrupt aggregation counter threshold) value is allowed up to 0x1F and current setting value is the maximum. This value is related with NUTRS(max:0x20) of HCI's capability. Considering HCI controller doesn't support the maximum, IACTH setting should be adjusted with possible value.
For that, existing 'ufshcd_config_int_aggr' is split into two part [reset, configure].

---
  drivers/scsi/ufs/ufshcd.c |   53 +++++++++++++++++++++-----------------------
  drivers/scsi/ufs/ufshci.h |    4 +-
  2 files changed, 27 insertions(+), 30 deletions(-)
drivers/scsi/ufs/ufshcd.c +25 -28
index 6ff16c9..c90b88a 100644
@@ -59,6 +59,9 @@
  /* Expose the flag value from utp_upiu_query.value */
  #define MASK_QUERY_UPIU_FLAG_LOC 0xFF
 
+/* Interrupt aggregation default timeout, unit: 40us */
+#define INT_AGGR_DEF_TO	0x02
+
  enum {
  	UFSHCD_MAX_CHANNEL	= 0,
  	UFSHCD_MAX_ID		= 1,
@@ -94,12 +97,6 @@ enum {
  	UFSHCD_INT_CLEAR,
  };
 
-/* Interrupt aggregation options */
-enum {
-	INT_AGGR_RESET,
-	INT_AGGR_CONFIG,
-};
-
  #define ufshcd_set_eh_in_progress(h) \
  	(h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
  #define ufshcd_eh_in_progress(h) \
@@ -340,30 +337,30 @@ static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr)
  }
 
  /**
- * ufshcd_config_int_aggr - Configure interrupt aggregation values.
- *		Currently there is no use case where we want to configure
- *		interrupt aggregation dynamically. So to configure interrupt
- *		aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
- *		INT_AGGR_TIMEOUT_VALUE are used.
+ * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
   * @hba: per adapter instance
- * @option: Interrupt aggregation option
   */
  static inline void
-ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+ufshcd_reset_intr_aggr(struct ufs_hba *hba)
  {
-	switch (option) {
-	case INT_AGGR_RESET:
-		ufshcd_writel(hba, INT_AGGR_ENABLE |
-			      INT_AGGR_COUNTER_AND_TIMER_RESET,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	case INT_AGGR_CONFIG:
-		ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
-			      INT_AGGR_COUNTER_THRESHOLD_VALUE |
-			      INT_AGGR_TIMEOUT_VALUE,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	}
+	ufshcd_writel(hba, INT_AGGR_ENABLE |
+		      INT_AGGR_COUNTER_AND_TIMER_RESET,
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
+ * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
+ * @hba: per adapter instance
+ * @cnt: Interrupt aggregation counter threshold
+ * @tmout: Interrupt aggregation timeout value
+ */
+static inline void
+ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout)
+{
+	ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
+		      INT_AGGR_TIMEOUT_VAL(tmout),
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
  }
 
  /**
@@ -1523,7 +1520,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
  	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
  	/* Configure interrupt aggregation */
-	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+	ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
 
  	/* Configure UTRL and UTMRL base address registers */
  	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -1971,7 +1968,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 
  	/* Reset interrupt aggregation counters */
  	if (int_aggr_reset)
-		ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+		ufshcd_reset_intr_aggr(hba);
  }
 
  /**
drivers/scsi/ufs/ufshci.h +2 -2
index f1e1b74..739ae3a 100644
@@ -226,8 +226,8 @@ enum {
 
  #define MASK_UIC_COMMAND_RESULT			0xFF
 
-#define INT_AGGR_COUNTER_THRESHOLD_VALUE	(0x1F << 8)
-#define INT_AGGR_TIMEOUT_VALUE			(0x02)
+#define INT_AGGR_COUNTER_THLD_VAL(c)	(((c) & 0x1F) << 8)
+#define INT_AGGR_TIMEOUT_VAL(t)		(((t) & 0xFF) << 0)
 
  /* Interrupt disable masks */
  enum {
Tested-by
#39 lore raw

RE: [PATCH v3 2/6] scsi: ufs: fix the setting interrupt aggregation counter

QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

= > -----Original Message-----
= > From: [email protected] [mailto:linux-scsi-
= > [email protected]] On Behalf Of Subhash Jadavani
= > Sent: Tuesday, August 27, 2013 12:02 PM
= > To: Seungwon Jeon
= > Cc: [email protected]; 'Vinayak Holikatti'; 'Santosh Y'; 'James E.J. = > Bottomley'
= > Subject: Re: [PATCH v3 2/6] scsi: ufs: fix the setting interrupt aggregation = > counter
= >
= >
= > Looks good to me.
= > Reviewed-by: Subhash Jadavani <[email protected]>
= >
= > On 8/26/2013 8:10 PM, Seungwon Jeon wrote:
= > > IACTH(Interrupt aggregation counter threshold) value is allowed up to = > > 0x1F and current setting value is the maximum.
= > > This value is related with NUTRS(max:0x20) of HCI's capability. = > > Considering HCI controller doesn't support the maximum, IACTH setting = > > should be adjusted with possible value.
= > > For that, existing 'ufshcd_config_int_aggr' is split into two part = > > [reset, configure].
= > >
= > > Signed-off-by: Seungwon Jeon <[email protected]>
= > > Reviewed-by: Subhash Jadavani <[email protected]>
= > > ---
= > > drivers/scsi/ufs/ufshcd.c | 53 +++++++++++++++++++++------------------
= > -----
= > > drivers/scsi/ufs/ufshci.h | 4 +-
= > > 2 files changed, 27 insertions(+), 30 deletions(-)
= > >
= > > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c = > > index 6ff16c9..c90b88a 100644
= > > --- a/drivers/scsi/ufs/ufshcd.c
= > > +++ b/drivers/scsi/ufs/ufshcd.c
= > > @@ -59,6 +59,9 @@
= > > /* Expose the flag value from utp_upiu_query.value */
= > > #define MASK_QUERY_UPIU_FLAG_LOC 0xFF
= > >
= > > +/* Interrupt aggregation default timeout, unit: 40us */
= > > +#define INT_AGGR_DEF_TO 0x02
= > > +

= > >   enum {
= > >   	UFSHCD_MAX_CHANNEL	= 0,
= > >   	UFSHCD_MAX_ID		= 1,

= > > @@ -94,12 +97,6 @@ enum {
= > > UFSHCD_INT_CLEAR,
= > > };
= > >
= > > -/* Interrupt aggregation options */
= > > -enum {
= > > - INT_AGGR_RESET,
= > > - INT_AGGR_CONFIG,
= > > -};
= > > -

= > >   #define ufshcd_set_eh_in_progress(h) \
= > >   	(h->eh_flags |= UFSHCD_EH_IN_PROGRESS)
= > >   #define ufshcd_eh_in_progress(h) \

= > > @@ -340,30 +337,30 @@ static inline bool
= > ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr)
= > > }
= > >
= > > /**
= > > - * ufshcd_config_int_aggr - Configure interrupt aggregation values.
= > > - * Currently there is no use case where we want to configure
= > > - * interrupt aggregation dynamically. So to configure interrupt
= > > - * aggregation, #define
= > INT_AGGR_COUNTER_THRESHOLD_VALUE and
= > > - * INT_AGGR_TIMEOUT_VALUE are used.
= > > + * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
= > > * @hba: per adapter instance
= > > - * @option: Interrupt aggregation option
= > > */
= > > static inline void
= > > -ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
= > > +ufshcd_reset_intr_aggr(struct ufs_hba *hba)
= > > {
= > > - switch (option) {
= > > - case INT_AGGR_RESET:
= > > - ufshcd_writel(hba, INT_AGGR_ENABLE |
= > > - INT_AGGR_COUNTER_AND_TIMER_RESET,
= > > -
= > REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
= > > - break;
= > > - case INT_AGGR_CONFIG:
= > > - ufshcd_writel(hba, INT_AGGR_ENABLE |
= > INT_AGGR_PARAM_WRITE |
= > > - INT_AGGR_COUNTER_THRESHOLD_VALUE |
= > > - INT_AGGR_TIMEOUT_VALUE,
= > > -
= > REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
= > > - break;
= > > - }
= > > + ufshcd_writel(hba, INT_AGGR_ENABLE |
= > > + INT_AGGR_COUNTER_AND_TIMER_RESET,
= > > + REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
= > > +}
= > > +
= > > +/**
= > > + * ufshcd_config_intr_aggr - Configure interrupt aggregation values. = > > + * @hba: per adapter instance
= > > + * @cnt: Interrupt aggregation counter threshold
= > > + * @tmout: Interrupt aggregation timeout value */ static inline void = > > +ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout) {
= > > + ufshcd_writel(hba, INT_AGGR_ENABLE |
= > INT_AGGR_PARAM_WRITE |

= > > +		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
= > > +		      INT_AGGR_TIMEOUT_VAL(tmout),
= > > +		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);

= > > }
= > >
= > > /**
= > > @@ -1523,7 +1520,7 @@ static int ufshcd_make_hba_operational(struct = > ufs_hba *hba)
= > > ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
= > >
= > > /* Configure interrupt aggregation */
= > > - ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
= > > + ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
= > >
= > > /* Configure UTRL and UTMRL base address registers */
= > > ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
= > > @@ -1971,7 +1968,7 @@ static void ufshcd_transfer_req_compl(struct = > > ufs_hba *hba)
= > >
= > > /* Reset interrupt aggregation counters */
= > > if (int_aggr_reset)
= > > - ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
= > > + ufshcd_reset_intr_aggr(hba);
= > > }
= > >
= > > /**
= > > diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h = > > index f1e1b74..739ae3a 100644
= > > --- a/drivers/scsi/ufs/ufshci.h
= > > +++ b/drivers/scsi/ufs/ufshci.h
= > > @@ -226,8 +226,8 @@ enum {
= > >
= > > #define MASK_UIC_COMMAND_RESULT 0xFF
= > >
= > > -#define INT_AGGR_COUNTER_THRESHOLD_VALUE (0x1F << 8)
= > > -#define INT_AGGR_TIMEOUT_VALUE (0x02)
= > > +#define INT_AGGR_COUNTER_THLD_VAL(c) (((c) & 0x1F) << 8)
= > > +#define INT_AGGR_TIMEOUT_VAL(t) (((t) & 0xFF) << 0)
= > >
= > > /* Interrupt disable masks */
= > > enum {
= >
= > --
= > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the = > body of a message to [email protected] More majordomo info = > at http://vger.kernel.org/majordomo-info.html

#40 lore raw

PATCH v3 3/6 scsi: ufs: add dme configuration primitives

In reply to Seungwon Jeon

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
 drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshci.h |    6 +++
 3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +88 -0
index c90b88a..00bfc1a 100644
@@ -285,6 +285,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
  * ufshcd_get_req_rsp - returns the TR response transaction type
  * @ucd_rsp_ptr: pointer to response UPIU
  */
@@ -1440,6 +1452,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
  * ufshcd_complete_dev_init() - checks device readiness
  * hba: per-adapter instance
  *
@@ -1912,6 +1998,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
 	if (hba->active_uic_cmd) {
 		hba->active_uic_cmd->argument2 |=
 			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);
 		complete(&hba->active_uic_cmd->done);
 	}
 }
drivers/scsi/ufs/ufshcd.h +51 -0

55 lines not on this page · raw message

drivers/scsi/ufs/ufshci.h +6 -0
index 739ae3a..1e1fe26 100644
@@ -191,6 +191,12 @@ enum {
 #define CONFIG_RESULT_CODE_MASK		0xFF
 #define GENERIC_ERROR_CODE_MASK		0xFF
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
 /* UIC Commands */
 enum {
 	UIC_CMD_DME_GET			= 0x01,
Signature · 1 line
1.7.0.4
Reviewed-by on v3 3/6
#41 lore raw

Re: [PATCH v3 3/6] scsi: ufs: add dme configuration primitives

Looks good to me.

Quoted 203 lines · Seungwon Jeon

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
  drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
  drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
  drivers/scsi/ufs/ufshci.h |    6 +++
  3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +88 -0
index c90b88a..00bfc1a 100644
@@ -285,6 +285,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
   * ufshcd_get_req_rsp - returns the TR response transaction type
   * @ucd_rsp_ptr: pointer to response UPIU
   */
@@ -1440,6 +1452,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
   * ufshcd_complete_dev_init() - checks device readiness
   * hba: per-adapter instance
   *
@@ -1912,6 +1998,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
  	if (hba->active_uic_cmd) {
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);
  		complete(&hba->active_uic_cmd->done);
  	}
  }
drivers/scsi/ufs/ufshcd.h +51 -0
index 8f5624e..ab1518d 100644
@@ -275,4 +275,55 @@ static inline void check_upiu_size(void)
  extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
  extern int ufshcd_runtime_resume(struct ufs_hba *hba);
  extern int ufshcd_runtime_idle(struct ufs_hba *hba);
+extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u8 attr_set, u32 mib_val, u8 peer);
+extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u32 *mib_val, u8 peer);
+
+/* UIC command interfaces for DME primitives */
+#define DME_LOCAL	0
+#define DME_PEER	1
+#define ATTR_SET_NOR	0	/* NORMAL */
+#define ATTR_SET_ST	1	/* STATIC */
+
+static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
+				 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
+				    u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
+				      u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 attr_sel,
+					 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_get(struct ufs_hba *hba,
+				 u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
+				      u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
+}
+
  #endif /* End of Header */
drivers/scsi/ufs/ufshci.h +6 -0
index 739ae3a..1e1fe26 100644
@@ -191,6 +191,12 @@ enum {
  #define CONFIG_RESULT_CODE_MASK		0xFF
  #define GENERIC_ERROR_CODE_MASK		0xFF
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
  /* UIC Commands */
  enum {
  	UIC_CMD_DME_GET			= 0x01,
Tested-by
#42 lore raw

RE: [PATCH v3 3/6] scsi: ufs: add dme configuration primitives

QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

= > -----Original Message-----
= > From: [email protected] [mailto:linux-scsi-
= > [email protected]] On Behalf Of Subhash Jadavani
= > Sent: Tuesday, August 27, 2013 12:16 PM
= > To: Seungwon Jeon
= > Cc: [email protected]; 'Vinayak Holikatti'; 'Santosh Y'; 'James E.J. = > Bottomley'
= > Subject: Re: [PATCH v3 3/6] scsi: ufs: add dme configuration primitives = >
= >
= > Looks good to me.
= > Reviewed-by: Subhash Jadavani <[email protected]>
= >
= > On 8/26/2013 8:10 PM, Seungwon Jeon wrote:
= > > Implements to support GET and SET operations of the DME.
= > > These operations are used to configure the behavior of the UNIPRO. = > > Along with basic operation, {Peer/AttrSetType} can be mixed.
= > >
= > > Signed-off-by: Seungwon Jeon <[email protected]>
= > > Reviewed-by: Subhash Jadavani <[email protected]>
= > > ---
= > > drivers/scsi/ufs/ufshcd.c | 88
= > +++++++++++++++++++++++++++++++++++++++++++++

= > >   drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
= > >   drivers/scsi/ufs/ufshci.h |    6 +++
= > >   3 files changed, 145 insertions(+), 0 deletions(-)

= > >
= > > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c = > > index c90b88a..00bfc1a 100644
= > > --- a/drivers/scsi/ufs/ufshcd.c
= > > +++ b/drivers/scsi/ufs/ufshcd.c
= > > @@ -285,6 +285,18 @@ static inline int
= > ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
= > > }
= > >
= > > /**
= > > + * ufshcd_get_dme_attr_val - Get the value of attribute returned by = > > +UIC command
= > > + * @hba: Pointer to adapter instance
= > > + *
= > > + * This function gets UIC command argument3
= > > + * Returns 0 on success, non zero value on error */ static inline = > > +u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba) {
= > > + return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3); }
= > > +
= > > +/**

= > >    * ufshcd_get_req_rsp - returns the TR response transaction type
= > >    * @ucd_rsp_ptr: pointer to response UPIU
= > >    */

= > > @@ -1440,6 +1452,80 @@ static int ufshcd_dme_link_startup(struct = > ufs_hba *hba)
= > > }
= > >
= > > /**
= > > + * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET = > > + * @hba: per adapter instance
= > > + * @attr_sel: uic command argument1
= > > + * @attr_set: attribute set type as uic command argument2 = > > + * @mib_val: setting value as uic command argument3 = > > + * @peer: indicate whether peer or local
= > > + *
= > > + * Returns 0 on success, non-zero value on failure */ int = > > +ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
= > > + u8 attr_set, u32 mib_val, u8 peer) {
= > > + struct uic_command uic_cmd = {0};
= > > + static const char *const action[] = {
= > > + "dme-set",
= > > + "dme-peer-set"
= > > + };
= > > + const char *set = action[!!peer];
= > > + int ret;
= > > +
= > > + uic_cmd.command = peer ?
= > > + UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
= > > + uic_cmd.argument1 = attr_sel;
= > > + uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
= > > + uic_cmd.argument3 = mib_val;
= > > +
= > > + ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
= > > + if (ret)
= > > + dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x error code
= > %d\n",
= > > + set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
= > > +
= > > + return ret;
= > > +}
= > > +EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
= > > +
= > > +/**
= > > + * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET = > > + * @hba: per adapter instance
= > > + * @attr_sel: uic command argument1
= > > + * @mib_val: the value of the attribute as returned by the UIC = > > +command
= > > + * @peer: indicate whether peer or local
= > > + *
= > > + * Returns 0 on success, non-zero value on failure */ int = > > +ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
= > > + u32 *mib_val, u8 peer)
= > > +{
= > > + struct uic_command uic_cmd = {0};
= > > + static const char *const action[] = {
= > > + "dme-get",
= > > + "dme-peer-get"
= > > + };
= > > + const char *get = action[!!peer];
= > > + int ret;
= > > +
= > > + uic_cmd.command = peer ?
= > > + UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
= > > + uic_cmd.argument1 = attr_sel;
= > > +
= > > + ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
= > > + if (ret) {
= > > + dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
= > > + get, UIC_GET_ATTR_ID(attr_sel), ret);
= > > + goto out;
= > > + }
= > > +
= > > + if (mib_val)
= > > + *mib_val = uic_cmd.argument3;
= > > +out:
= > > + return ret;
= > > +}
= > > +EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
= > > +
= > > +/**

= > >    * ufshcd_complete_dev_init() - checks device readiness
= > >    * hba: per-adapter instance
= > >    *
= > > @@ -1912,6 +1998,8 @@ static void ufshcd_uic_cmd_compl(struct
= > ufs_hba *hba)
= > >   	if (hba->active_uic_cmd) {
= > >   		hba->active_uic_cmd->argument2 |=
= > >   			ufshcd_get_uic_cmd_result(hba);
= > > +		hba->active_uic_cmd->argument3 =
= > > +			ufshcd_get_dme_attr_val(hba);
= > >   		complete(&hba->active_uic_cmd->done);
= > >   	}
= > >   }

= > > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h = > > index 8f5624e..ab1518d 100644
= > > --- a/drivers/scsi/ufs/ufshcd.h
= > > +++ b/drivers/scsi/ufs/ufshcd.h
= > > @@ -275,4 +275,55 @@ static inline void check_upiu_size(void)

= > >   extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
= > >   extern int ufshcd_runtime_resume(struct ufs_hba *hba);
= > >   extern int ufshcd_runtime_idle(struct ufs_hba *hba);

= > > +extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
= > > + u8 attr_set, u32 mib_val, u8 peer); extern int
= > > +ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
= > > + u32 *mib_val, u8 peer);
= > > +
= > > +/* UIC command interfaces for DME primitives */
= > > +#define DME_LOCAL 0
= > > +#define DME_PEER 1
= > > +#define ATTR_SET_NOR 0 /* NORMAL */
= > > +#define ATTR_SET_ST 1 /* STATIC */
= > > +
= > > +static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
= > > + u32 mib_val)
= > > +{
= > > + return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
= > > + mib_val, DME_LOCAL);
= > > +}
= > > +
= > > +static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
= > > + u32 mib_val)
= > > +{
= > > + return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
= > > + mib_val, DME_LOCAL);
= > > +}
= > > +
= > > +static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
= > > + u32 mib_val)
= > > +{
= > > + return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
= > > + mib_val, DME_PEER);
= > > +}
= > > +
= > > +static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 = > attr_sel,
= > > + u32 mib_val)
= > > +{
= > > + return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
= > > + mib_val, DME_PEER);
= > > +}
= > > +
= > > +static inline int ufshcd_dme_get(struct ufs_hba *hba,
= > > + u32 attr_sel, u32 *mib_val)
= > > +{
= > > + return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL); }
= > > +
= > > +static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
= > > + u32 attr_sel, u32 *mib_val) {
= > > + return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER); }
= > > +
= > > #endif /* End of Header */
= > > diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h = > > index 739ae3a..1e1fe26 100644
= > > --- a/drivers/scsi/ufs/ufshci.h
= > > +++ b/drivers/scsi/ufs/ufshci.h
= > > @@ -191,6 +191,12 @@ enum {
= > > #define CONFIG_RESULT_CODE_MASK 0xFF
= > > #define GENERIC_ERROR_CODE_MASK 0xFF
= > >
= > > +#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\
= > > + ((sel) & 0xFFFF))
= > > +#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)
= > > +#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16)
= > > +#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF)
= > > +

= > >   /* UIC Commands */
= > >   enum {
= > >   	UIC_CMD_DME_GET			= 0x01,

= >
= > --
= > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the = > body of a message to [email protected] More majordomo info = > at http://vger.kernel.org/majordomo-info.html

#43 lore raw

PATCH v3 4/6 scsi: ufs: add unipro attribute IDs

In reply to Seungwon Jeon

'drivers/scsi/ufs/unipro.h' is added. Attributes in the layers of the UNIPRO stack can be read and written via the DME.

---
 drivers/scsi/ufs/unipro.h |  130 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/ufs/unipro.h
drivers/scsi/ufs/unipro.h new file +130 -0
new file mode 100644
index 0000000..3a710eb
@@ -0,0 +1,130 @@
+/*
+ * drivers/scsi/ufs/unipro.h
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UNIPRO_H_
+#define _UNIPRO_H_
+
+/*
+ * PHY Adpater attributes
+ */
+#define PA_ACTIVETXDATALANES	0x1560
+#define PA_ACTIVERXDATALANES	0x1580
+#define PA_TXTRAILINGCLOCKS	0x1564
+#define PA_PHY_TYPE		0x1500
+#define PA_AVAILTXDATALANES	0x1520
+#define PA_AVAILRXDATALANES	0x1540
+#define PA_MINRXTRAILINGCLOCKS	0x1543
+#define PA_TXPWRSTATUS		0x1567
+#define PA_RXPWRSTATUS		0x1582
+#define PA_TXFORCECLOCK		0x1562
+#define PA_TXPWRMODE		0x1563
+#define PA_LEGACYDPHYESCDL	0x1570
+#define PA_MAXTXSPEEDFAST	0x1521
+#define PA_MAXTXSPEEDSLOW	0x1522
+#define PA_MAXRXSPEEDFAST	0x1541
+#define PA_MAXRXSPEEDSLOW	0x1542
+#define PA_TXLINKSTARTUPHS	0x1544
+#define PA_TXSPEEDFAST		0x1565
+#define PA_TXSPEEDSLOW		0x1566
+#define PA_REMOTEVERINFO	0x15A0
+#define PA_TXGEAR		0x1568
+#define PA_TXTERMINATION	0x1569
+#define PA_HSSERIES		0x156A
+#define PA_PWRMODE		0x1571
+#define PA_RXGEAR		0x1583
+#define PA_RXTERMINATION	0x1584
+#define PA_MAXRXPWMGEAR		0x1586
+#define PA_MAXRXHSGEAR		0x1587
+#define PA_RXHSUNTERMCAP	0x15A5
+#define PA_RXLSTERMCAP		0x15A6
+#define PA_PACPREQTIMEOUT	0x1590
+#define PA_PACPREQEOBTIMEOUT	0x1591
+#define PA_HIBERN8TIME		0x15A7
+#define PA_LOCALVERINFO		0x15A9
+#define PA_TACTIVATE		0x15A8
+#define PA_PACPFRAMECOUNT	0x15C0
+#define PA_PACPERRORCOUNT	0x15C1
+#define PA_PHYTESTCONTROL	0x15C2
+#define PA_PWRMODEUSERDATA0	0x15B0
+#define PA_PWRMODEUSERDATA1	0x15B1
+#define PA_PWRMODEUSERDATA2	0x15B2
+#define PA_PWRMODEUSERDATA3	0x15B3
+#define PA_PWRMODEUSERDATA4	0x15B4
+#define PA_PWRMODEUSERDATA5	0x15B5
+#define PA_PWRMODEUSERDATA6	0x15B6
+#define PA_PWRMODEUSERDATA7	0x15B7
+#define PA_PWRMODEUSERDATA8	0x15B8
+#define PA_PWRMODEUSERDATA9	0x15B9
+#define PA_PWRMODEUSERDATA10	0x15BA
+#define PA_PWRMODEUSERDATA11	0x15BB
+#define PA_CONNECTEDTXDATALANES	0x1561
+#define PA_CONNECTEDRXDATALANES	0x1581
+#define PA_LOGICALLANEMAP	0x15A1
+#define PA_SLEEPNOCONFIGTIME	0x15A2
+#define PA_STALLNOCONFIGTIME	0x15A3
+#define PA_SAVECONFIGTIME	0x15A4
+
+/*
+ * Data Link Layer Attributes
+ */
+#define DL_TC0TXFCTHRESHOLD	0x2040
+#define DL_FC0PROTTIMEOUTVAL	0x2041
+#define DL_TC0REPLAYTIMEOUTVAL	0x2042
+#define DL_AFC0REQTIMEOUTVAL	0x2043
+#define DL_AFC0CREDITTHRESHOLD	0x2044
+#define DL_TC0OUTACKTHRESHOLD	0x2045
+#define DL_TC1TXFCTHRESHOLD	0x2060
+#define DL_FC1PROTTIMEOUTVAL	0x2061
+#define DL_TC1REPLAYTIMEOUTVAL	0x2062
+#define DL_AFC1REQTIMEOUTVAL	0x2063
+#define DL_AFC1CREDITTHRESHOLD	0x2064
+#define DL_TC1OUTACKTHRESHOLD	0x2065
+#define DL_TXPREEMPTIONCAP	0x2000
+#define DL_TC0TXMAXSDUSIZE	0x2001
+#define DL_TC0RXINITCREDITVAL	0x2002
+#define DL_TC0TXBUFFERSIZE	0x2005
+#define DL_PEERTC0PRESENT	0x2046
+#define DL_PEERTC0RXINITCREVAL	0x2047
+#define DL_TC1TXMAXSDUSIZE	0x2003
+#define DL_TC1RXINITCREDITVAL	0x2004
+#define DL_TC1TXBUFFERSIZE	0x2006
+#define DL_PEERTC1PRESENT	0x2066
+#define DL_PEERTC1RXINITCREVAL	0x2067
+
+/*
+ * Network Layer Attributes
+ */
+#define N_DEVICEID		0x3000
+#define N_DEVICEID_VALID	0x3001
+#define N_TC0TXMAXSDUSIZE	0x3020
+#define N_TC1TXMAXSDUSIZE	0x3021
+
+/*
+ * Transport Layer Attributes
+ */
+#define T_NUMCPORTS		0x4000
+#define T_NUMTESTFEATURES	0x4001
+#define T_CONNECTIONSTATE	0x4020
+#define T_PEERDEVICEID		0x4021
+#define T_PEERCPORTID		0x4022
+#define T_TRAFFICCLASS		0x4023
+#define T_PROTOCOLID		0x4024
+#define T_CPORTFLAGS		0x4025
+#define T_TXTOKENVALUE		0x4026
+#define T_RXTOKENVALUE		0x4027
+#define T_LOCALBUFFERSPACE	0x4028
+#define T_PEERBUFFERSPACE	0x4029
+#define T_CREDITSTOSEND		0x402A
+#define T_CPORTMODE		0x402B
+#define T_TC0TXMAXSDUSIZE	0x4060
+#define T_TC1TXMAXSDUSIZE	0x4061
+
+#endif /* _UNIPRO_H_ */
Signature · 1 line
1.7.0.4
Reviewed-by on v3 4/6
#44 lore raw

Re: [PATCH v3 4/6] scsi: ufs: add unipro attribute IDs

Looks good to me.

Quoted 147 lines · Seungwon Jeon

'drivers/scsi/ufs/unipro.h' is added. Attributes in the layers of the UNIPRO stack can be read and written via the DME.

---
  drivers/scsi/ufs/unipro.h |  130 +++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 130 insertions(+), 0 deletions(-)

create mode 100644 drivers/scsi/ufs/unipro.h

drivers/scsi/ufs/unipro.h new file +130 -0
new file mode 100644
index 0000000..3a710eb
@@ -0,0 +1,130 @@
+/*
+ * drivers/scsi/ufs/unipro.h
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UNIPRO_H_
+#define _UNIPRO_H_
+
+/*
+ * PHY Adpater attributes
+ */
+#define PA_ACTIVETXDATALANES	0x1560
+#define PA_ACTIVERXDATALANES	0x1580
+#define PA_TXTRAILINGCLOCKS	0x1564
+#define PA_PHY_TYPE		0x1500
+#define PA_AVAILTXDATALANES	0x1520
+#define PA_AVAILRXDATALANES	0x1540
+#define PA_MINRXTRAILINGCLOCKS	0x1543
+#define PA_TXPWRSTATUS		0x1567
+#define PA_RXPWRSTATUS		0x1582
+#define PA_TXFORCECLOCK		0x1562
+#define PA_TXPWRMODE		0x1563
+#define PA_LEGACYDPHYESCDL	0x1570
+#define PA_MAXTXSPEEDFAST	0x1521
+#define PA_MAXTXSPEEDSLOW	0x1522
+#define PA_MAXRXSPEEDFAST	0x1541
+#define PA_MAXRXSPEEDSLOW	0x1542
+#define PA_TXLINKSTARTUPHS	0x1544
+#define PA_TXSPEEDFAST		0x1565
+#define PA_TXSPEEDSLOW		0x1566
+#define PA_REMOTEVERINFO	0x15A0
+#define PA_TXGEAR		0x1568
+#define PA_TXTERMINATION	0x1569
+#define PA_HSSERIES		0x156A
+#define PA_PWRMODE		0x1571
+#define PA_RXGEAR		0x1583
+#define PA_RXTERMINATION	0x1584
+#define PA_MAXRXPWMGEAR		0x1586
+#define PA_MAXRXHSGEAR		0x1587
+#define PA_RXHSUNTERMCAP	0x15A5
+#define PA_RXLSTERMCAP		0x15A6
+#define PA_PACPREQTIMEOUT	0x1590
+#define PA_PACPREQEOBTIMEOUT	0x1591
+#define PA_HIBERN8TIME		0x15A7
+#define PA_LOCALVERINFO		0x15A9
+#define PA_TACTIVATE		0x15A8
+#define PA_PACPFRAMECOUNT	0x15C0
+#define PA_PACPERRORCOUNT	0x15C1
+#define PA_PHYTESTCONTROL	0x15C2
+#define PA_PWRMODEUSERDATA0	0x15B0
+#define PA_PWRMODEUSERDATA1	0x15B1
+#define PA_PWRMODEUSERDATA2	0x15B2
+#define PA_PWRMODEUSERDATA3	0x15B3
+#define PA_PWRMODEUSERDATA4	0x15B4
+#define PA_PWRMODEUSERDATA5	0x15B5
+#define PA_PWRMODEUSERDATA6	0x15B6
+#define PA_PWRMODEUSERDATA7	0x15B7
+#define PA_PWRMODEUSERDATA8	0x15B8
+#define PA_PWRMODEUSERDATA9	0x15B9
+#define PA_PWRMODEUSERDATA10	0x15BA
+#define PA_PWRMODEUSERDATA11	0x15BB
+#define PA_CONNECTEDTXDATALANES	0x1561
+#define PA_CONNECTEDRXDATALANES	0x1581
+#define PA_LOGICALLANEMAP	0x15A1
+#define PA_SLEEPNOCONFIGTIME	0x15A2
+#define PA_STALLNOCONFIGTIME	0x15A3
+#define PA_SAVECONFIGTIME	0x15A4
+
+/*
+ * Data Link Layer Attributes
+ */
+#define DL_TC0TXFCTHRESHOLD	0x2040
+#define DL_FC0PROTTIMEOUTVAL	0x2041
+#define DL_TC0REPLAYTIMEOUTVAL	0x2042
+#define DL_AFC0REQTIMEOUTVAL	0x2043
+#define DL_AFC0CREDITTHRESHOLD	0x2044
+#define DL_TC0OUTACKTHRESHOLD	0x2045
+#define DL_TC1TXFCTHRESHOLD	0x2060
+#define DL_FC1PROTTIMEOUTVAL	0x2061
+#define DL_TC1REPLAYTIMEOUTVAL	0x2062
+#define DL_AFC1REQTIMEOUTVAL	0x2063
+#define DL_AFC1CREDITTHRESHOLD	0x2064
+#define DL_TC1OUTACKTHRESHOLD	0x2065
+#define DL_TXPREEMPTIONCAP	0x2000
+#define DL_TC0TXMAXSDUSIZE	0x2001
+#define DL_TC0RXINITCREDITVAL	0x2002
+#define DL_TC0TXBUFFERSIZE	0x2005
+#define DL_PEERTC0PRESENT	0x2046
+#define DL_PEERTC0RXINITCREVAL	0x2047
+#define DL_TC1TXMAXSDUSIZE	0x2003
+#define DL_TC1RXINITCREDITVAL	0x2004
+#define DL_TC1TXBUFFERSIZE	0x2006
+#define DL_PEERTC1PRESENT	0x2066
+#define DL_PEERTC1RXINITCREVAL	0x2067
+
+/*
+ * Network Layer Attributes
+ */
+#define N_DEVICEID		0x3000
+#define N_DEVICEID_VALID	0x3001
+#define N_TC0TXMAXSDUSIZE	0x3020
+#define N_TC1TXMAXSDUSIZE	0x3021
+
+/*
+ * Transport Layer Attributes
+ */
+#define T_NUMCPORTS		0x4000
+#define T_NUMTESTFEATURES	0x4001
+#define T_CONNECTIONSTATE	0x4020
+#define T_PEERDEVICEID		0x4021
+#define T_PEERCPORTID		0x4022
+#define T_TRAFFICCLASS		0x4023
+#define T_PROTOCOLID		0x4024
+#define T_CPORTFLAGS		0x4025
+#define T_TXTOKENVALUE		0x4026
+#define T_RXTOKENVALUE		0x4027
+#define T_LOCALBUFFERSPACE	0x4028
+#define T_PEERBUFFERSPACE	0x4029
+#define T_CREDITSTOSEND		0x402A
+#define T_CPORTMODE		0x402B
+#define T_TC0TXMAXSDUSIZE	0x4060
+#define T_TC1TXMAXSDUSIZE	0x4061
+
+#endif /* _UNIPRO_H_ */
Tested-by
#45 lore raw

RE: [PATCH v3 4/6] scsi: ufs: add unipro attribute IDs

QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

= > -----Original Message-----
= > From: [email protected] [mailto:linux-scsi-
= > [email protected]] On Behalf Of Subhash Jadavani
= > Sent: Tuesday, August 27, 2013 12:15 PM
= > To: Seungwon Jeon
= > Cc: [email protected]; 'Vinayak Holikatti'; 'Santosh Y'; 'James E.J. = > Bottomley'
= > Subject: Re: [PATCH v3 4/6] scsi: ufs: add unipro attribute IDs = >
= >
= > Looks good to me.
= > Reviewed-by: Subhash Jadavani <[email protected]>
= >
= > On 8/26/2013 8:10 PM, Seungwon Jeon wrote:
= > > 'drivers/scsi/ufs/unipro.h' is added.
= > > Attributes in the layers of the UNIPRO stack can be read and written = > > via the DME.
= > >
= > > Signed-off-by: Seungwon Jeon <[email protected]>
= > > Reviewed-by: Subhash Jadavani <[email protected]>
= > > ---
= > > drivers/scsi/ufs/unipro.h | 130
= > +++++++++++++++++++++++++++++++++++++++++++++
= > > 1 files changed, 130 insertions(+), 0 deletions(-)
= > > create mode 100644 drivers/scsi/ufs/unipro.h
= > >
= > > diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h new = > > file mode 100644 index 0000000..3a710eb
= > > --- /dev/null
= > > +++ b/drivers/scsi/ufs/unipro.h
= > > @@ -0,0 +1,130 @@
= > > +/*
= > > + * drivers/scsi/ufs/unipro.h
= > > + *
= > > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
= > > + *
= > > + * This program 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; either version 2 of the License, or = > > + * (at your option) any later version.
= > > + */
= > > +
= > > +#ifndef _UNIPRO_H_
= > > +#define _UNIPRO_H_
= > > +
= > > +/*
= > > + * PHY Adpater attributes
= > > + */
= > > +#define PA_ACTIVETXDATALANES 0x1560
= > > +#define PA_ACTIVERXDATALANES 0x1580
= > > +#define PA_TXTRAILINGCLOCKS 0x1564
= > > +#define PA_PHY_TYPE 0x1500
= > > +#define PA_AVAILTXDATALANES 0x1520
= > > +#define PA_AVAILRXDATALANES 0x1540
= > > +#define PA_MINRXTRAILINGCLOCKS 0x1543
= > > +#define PA_TXPWRSTATUS 0x1567
= > > +#define PA_RXPWRSTATUS 0x1582
= > > +#define PA_TXFORCECLOCK 0x1562
= > > +#define PA_TXPWRMODE 0x1563
= > > +#define PA_LEGACYDPHYESCDL 0x1570
= > > +#define PA_MAXTXSPEEDFAST 0x1521
= > > +#define PA_MAXTXSPEEDSLOW 0x1522
= > > +#define PA_MAXRXSPEEDFAST 0x1541
= > > +#define PA_MAXRXSPEEDSLOW 0x1542
= > > +#define PA_TXLINKSTARTUPHS 0x1544
= > > +#define PA_TXSPEEDFAST 0x1565
= > > +#define PA_TXSPEEDSLOW 0x1566
= > > +#define PA_REMOTEVERINFO 0x15A0
= > > +#define PA_TXGEAR 0x1568
= > > +#define PA_TXTERMINATION 0x1569
= > > +#define PA_HSSERIES 0x156A
= > > +#define PA_PWRMODE 0x1571
= > > +#define PA_RXGEAR 0x1583
= > > +#define PA_RXTERMINATION 0x1584
= > > +#define PA_MAXRXPWMGEAR 0x1586
= > > +#define PA_MAXRXHSGEAR 0x1587
= > > +#define PA_RXHSUNTERMCAP 0x15A5
= > > +#define PA_RXLSTERMCAP 0x15A6
= > > +#define PA_PACPREQTIMEOUT 0x1590
= > > +#define PA_PACPREQEOBTIMEOUT 0x1591
= > > +#define PA_HIBERN8TIME 0x15A7
= > > +#define PA_LOCALVERINFO 0x15A9
= > > +#define PA_TACTIVATE 0x15A8
= > > +#define PA_PACPFRAMECOUNT 0x15C0
= > > +#define PA_PACPERRORCOUNT 0x15C1
= > > +#define PA_PHYTESTCONTROL 0x15C2
= > > +#define PA_PWRMODEUSERDATA0 0x15B0
= > > +#define PA_PWRMODEUSERDATA1 0x15B1
= > > +#define PA_PWRMODEUSERDATA2 0x15B2
= > > +#define PA_PWRMODEUSERDATA3 0x15B3
= > > +#define PA_PWRMODEUSERDATA4 0x15B4
= > > +#define PA_PWRMODEUSERDATA5 0x15B5
= > > +#define PA_PWRMODEUSERDATA6 0x15B6
= > > +#define PA_PWRMODEUSERDATA7 0x15B7
= > > +#define PA_PWRMODEUSERDATA8 0x15B8
= > > +#define PA_PWRMODEUSERDATA9 0x15B9
= > > +#define PA_PWRMODEUSERDATA10 0x15BA
= > > +#define PA_PWRMODEUSERDATA11 0x15BB
= > > +#define PA_CONNECTEDTXDATALANES 0x1561
= > > +#define PA_CONNECTEDRXDATALANES 0x1581
= > > +#define PA_LOGICALLANEMAP 0x15A1
= > > +#define PA_SLEEPNOCONFIGTIME 0x15A2
= > > +#define PA_STALLNOCONFIGTIME 0x15A3
= > > +#define PA_SAVECONFIGTIME 0x15A4
= > > +
= > > +/*
= > > + * Data Link Layer Attributes
= > > + */
= > > +#define DL_TC0TXFCTHRESHOLD 0x2040
= > > +#define DL_FC0PROTTIMEOUTVAL 0x2041
= > > +#define DL_TC0REPLAYTIMEOUTVAL 0x2042
= > > +#define DL_AFC0REQTIMEOUTVAL 0x2043
= > > +#define DL_AFC0CREDITTHRESHOLD 0x2044
= > > +#define DL_TC0OUTACKTHRESHOLD 0x2045
= > > +#define DL_TC1TXFCTHRESHOLD 0x2060
= > > +#define DL_FC1PROTTIMEOUTVAL 0x2061
= > > +#define DL_TC1REPLAYTIMEOUTVAL 0x2062
= > > +#define DL_AFC1REQTIMEOUTVAL 0x2063
= > > +#define DL_AFC1CREDITTHRESHOLD 0x2064
= > > +#define DL_TC1OUTACKTHRESHOLD 0x2065
= > > +#define DL_TXPREEMPTIONCAP 0x2000
= > > +#define DL_TC0TXMAXSDUSIZE 0x2001
= > > +#define DL_TC0RXINITCREDITVAL 0x2002
= > > +#define DL_TC0TXBUFFERSIZE 0x2005
= > > +#define DL_PEERTC0PRESENT 0x2046
= > > +#define DL_PEERTC0RXINITCREVAL 0x2047
= > > +#define DL_TC1TXMAXSDUSIZE 0x2003
= > > +#define DL_TC1RXINITCREDITVAL 0x2004
= > > +#define DL_TC1TXBUFFERSIZE 0x2006
= > > +#define DL_PEERTC1PRESENT 0x2066
= > > +#define DL_PEERTC1RXINITCREVAL 0x2067
= > > +
= > > +/*
= > > + * Network Layer Attributes
= > > + */
= > > +#define N_DEVICEID 0x3000
= > > +#define N_DEVICEID_VALID 0x3001
= > > +#define N_TC0TXMAXSDUSIZE 0x3020
= > > +#define N_TC1TXMAXSDUSIZE 0x3021
= > > +
= > > +/*
= > > + * Transport Layer Attributes
= > > + */
= > > +#define T_NUMCPORTS 0x4000
= > > +#define T_NUMTESTFEATURES 0x4001
= > > +#define T_CONNECTIONSTATE 0x4020
= > > +#define T_PEERDEVICEID 0x4021
= > > +#define T_PEERCPORTID 0x4022
= > > +#define T_TRAFFICCLASS 0x4023
= > > +#define T_PROTOCOLID 0x4024
= > > +#define T_CPORTFLAGS 0x4025
= > > +#define T_TXTOKENVALUE 0x4026
= > > +#define T_RXTOKENVALUE 0x4027
= > > +#define T_LOCALBUFFERSPACE 0x4028
= > > +#define T_PEERBUFFERSPACE 0x4029
= > > +#define T_CREDITSTOSEND 0x402A
= > > +#define T_CPORTMODE 0x402B
= > > +#define T_TC0TXMAXSDUSIZE 0x4060
= > > +#define T_TC1TXMAXSDUSIZE 0x4061
= > > +
= > > +#endif /* _UNIPRO_H_ */
= >
= > --
= > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the = > body of a message to [email protected] More majordomo info = > at http://vger.kernel.org/majordomo-info.html

#46 lore raw

PATCH v3 5/6 scsi: ufs: add operation for the uic power mode change

In reply to Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
 drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/scsi/ufs/ufshcd.h |    3 ++
 drivers/scsi/ufs/ufshci.h |   12 ++++++
 3 files changed, 95 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +80 -4
index 00bfc1a..c67118d 100644
@@ -36,9 +36,11 @@
 #include <linux/async.h>
 
 #include "ufshcd.h"
+#include "unipro.h"
 
 #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
 				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
 				 UFSHCD_ERROR_MASK)
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT	500
@@ -520,6 +522,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
  * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
  * @hba: per adapter instance
  * @uic_cmd: UIC command
@@ -1526,6 +1540,64 @@ out:
 EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
 /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x uic error %d\n",
+			mode, ret);
+		goto out;
+	}
+
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x completion timeout\n",
+			mode);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
  * ufshcd_complete_dev_init() - checks device readiness
  * hba: per-adapter instance
  *
@@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 /**
  * ufshcd_uic_cmd_compl - handle completion of uic command
  * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
  */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
 {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
 		hba->active_uic_cmd->argument2 |=
 			ufshcd_get_uic_cmd_result(hba);
 		hba->active_uic_cmd->argument3 =
 			ufshcd_get_dme_attr_val(hba);
 		complete(&hba->active_uic_cmd->done);
 	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
 }
 
 /**
@@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
 	if (hba->errors)
 		ufshcd_check_errors(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
 	if (intr_status & UTP_TASK_REQ_COMPL)
 		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index ab1518d..ecff83d 100644
@@ -178,6 +178,7 @@ struct ufs_dev_cmd {
  * @tm_tag_wq: wait queue for free task management slots
  * @tm_condition: condition variable for task management
  * @tm_slots_in_use: bit map of task management request slots in use
+ * @pwr_done: completion for power mode change
  * @ufshcd_state: UFSHCD states
  * @eh_flags: Error handling flags
  * @intr_mask: Interrupt Mask Bits
@@ -227,6 +228,8 @@ struct ufs_hba {
 	unsigned long tm_condition;
 	unsigned long tm_slots_in_use;
 
+	struct completion *pwr_done;
+
 	u32 ufshcd_state;
 	u32 eh_flags;
 	u32 intr_mask;
drivers/scsi/ufs/ufshci.h +12 -0
index 1e1fe26..0475c66 100644
@@ -124,6 +124,9 @@ enum {
 #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
 #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
 #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
 				DEVICE_FATAL_ERROR |\
 				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
 #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
 #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
 /* HCE - Host Controller Enable 34h */
 #define CONTROLLER_ENABLE	UFS_BIT(0)
 #define CONTROLLER_DISABLE	0x0
Signature · 1 line
1.7.0.4
#47 lore raw

Re: [PATCH v3 5/6] scsi: ufs: add operation for the uic power mode change

Quoted 83 lines · Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
  drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
  drivers/scsi/ufs/ufshcd.h |    3 ++
  drivers/scsi/ufs/ufshci.h |   12 ++++++
  3 files changed, 95 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +47 -0
index 00bfc1a..c67118d 100644
@@ -36,9 +36,11 @@
  #include <linux/async.h>
 
  #include "ufshcd.h"
+#include "unipro.h"
 
  #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
  				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
  				 UFSHCD_ERROR_MASK)
  /* UIC command timeout, unit: ms */
  #define UIC_CMD_TIMEOUT	500
@@ -520,6 +522,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
   * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
   * @hba: per adapter instance
   * @uic_cmd: UIC command
@@ -1526,6 +1540,64 @@ out:
  EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
  /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x uic error %d\n",
+			mode, ret);
+		goto out;
+	}
+

drivers/scsi/ufs/ufshcd.c out: · line 1585

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x completion timeout\n",
+			mode);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);

I have a doubt on the relation between UIC Power Mode Status (UPMS) bit & UIC Power Mode Change Request Status (UPMCRS) field.

Once we send the DME_SET(PA_PWRMODE) command, we will get the UIC command completion interrupt (with IS.UCCS bit set) which just indicate that the attribute is written but power mode change procedure will complete only after some delay which is indicated via IS.UPMS status bit and then we would read the HCS.UPMCRS field to know the status of power mode change.

This is my exact doubt: Currently UFSHCD driver first clears the IS.UPMS bit (ufshcd_intr() first clears the current interrupts in REG_INTERRUPT_STATUS and then calls the ufshcd_sl_intr() handler, and in your current patch we are anyway not reading the UPMCRS field from interrupt contex ) and then read the HCS.UPMCRS field. Will HCS.UPMCRS field retain its status value even after IS.UPMS bit is cleared? UFSHCI specification doesn't clearly mention this so either we have to get the clarity from JEDEC or from your ufs host controller vendor on the behaviour. I am checking with our ufs host controller vendor as well and will let you know what is the expected behaviour. BTW, i guess you might have already tested this patch to be working on your UFS host controller?

Quoted 103 lines
drivers/scsi/ufs/ufshcd.c +23 -4
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
   * ufshcd_complete_dev_init() - checks device readiness
   * hba: per-adapter instance
   *
@@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  /**
   * ufshcd_uic_cmd_compl - handle completion of uic command
   * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
   */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
  {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
  		hba->active_uic_cmd->argument3 =
  			ufshcd_get_dme_attr_val(hba);
  		complete(&hba->active_uic_cmd->done);
  	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
  }
 
  /**
@@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
  	if (hba->errors)
  		ufshcd_check_errors(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
  	if (intr_status & UTP_TASK_REQ_COMPL)
  		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index ab1518d..ecff83d 100644
@@ -178,6 +178,7 @@ struct ufs_dev_cmd {
   * @tm_tag_wq: wait queue for free task management slots
   * @tm_condition: condition variable for task management
   * @tm_slots_in_use: bit map of task management request slots in use
+ * @pwr_done: completion for power mode change
   * @ufshcd_state: UFSHCD states
   * @eh_flags: Error handling flags
   * @intr_mask: Interrupt Mask Bits
@@ -227,6 +228,8 @@ struct ufs_hba {
  	unsigned long tm_condition;
  	unsigned long tm_slots_in_use;
 
+	struct completion *pwr_done;
+
  	u32 ufshcd_state;
  	u32 eh_flags;
  	u32 intr_mask;
drivers/scsi/ufs/ufshci.h +12 -0
index 1e1fe26..0475c66 100644
@@ -124,6 +124,9 @@ enum {
  #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
  #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
  #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
  				DEVICE_FATAL_ERROR |\
  				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
  #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
  #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
  /* HCE - Host Controller Enable 34h */
  #define CONTROLLER_ENABLE	UFS_BIT(0)
  #define CONTROLLER_DISABLE	0x0
#48 lore raw

RE: [PATCH v3 5/6] scsi: ufs: add operation for the uic power mode change

Quoted 105 lines · Subhash Jadavani
Quoted 83 lines · Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
  drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
  drivers/scsi/ufs/ufshcd.h |    3 ++
  drivers/scsi/ufs/ufshci.h |   12 ++++++
  3 files changed, 95 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +47 -0
index 00bfc1a..c67118d 100644
@@ -36,9 +36,11 @@
  #include <linux/async.h>
 
  #include "ufshcd.h"
+#include "unipro.h"
 
  #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
  				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
  				 UFSHCD_ERROR_MASK)
  /* UIC command timeout, unit: ms */
  #define UIC_CMD_TIMEOUT	500
@@ -520,6 +522,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
   * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
   * @hba: per adapter instance
   * @uic_cmd: UIC command
@@ -1526,6 +1540,64 @@ out:
  EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
  /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x uic error %d\n",
+			mode, ret);
+		goto out;
+	}
+

drivers/scsi/ufs/ufshcd.c out: · line 1585

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x completion timeout\n",
+			mode);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);

I have a doubt on the relation between UIC Power Mode Status (UPMS) bit & UIC Power Mode Change Request Status (UPMCRS) field.

Once we send the DME_SET(PA_PWRMODE) command, we will get the UIC command completion interrupt (with IS.UCCS bit set) which just indicate that the attribute is written but power mode change procedure will complete only after some delay which is indicated via IS.UPMS status bit and then we would read the HCS.UPMCRS field to know the status of power mode change.

Quoted 11 lines

This is my exact doubt: Currently UFSHCD driver first clears the IS.UPMS bit (ufshcd_intr() first clears the current interrupts in REG_INTERRUPT_STATUS and then calls the ufshcd_sl_intr() handler, and in your current patch we are anyway not reading the UPMCRS field from interrupt contex ) and then read the HCS.UPMCRS field. Will HCS.UPMCRS field retain its status value even after IS.UPMS bit is cleared? UFSHCI specification doesn't clearly mention this so either we have to get the clarity from JEDEC or from your ufs host controller vendor on the behaviour. I am checking with our ufs host controller vendor as well and will let you know what is the expected behaviour. BTW, i guess you might have already tested this patch to be working on your UFS host controller?

Hi Subhash,

Yes, It's already tested and works fine.
IS.UPMS is a indication to inform the result of 'power mode change'(UPMCRS). So clearing the interrupt would not affect this result. Generally it seems not common idea.
Anyway, if your host has different behavior, please let me know.

Thanks,
Seungwon Jeon

Quoted 109 lines
Quoted 94 lines
drivers/scsi/ufs/ufshcd.c +23 -4
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
   * ufshcd_complete_dev_init() - checks device readiness
   * hba: per-adapter instance
   *
@@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  /**
   * ufshcd_uic_cmd_compl - handle completion of uic command
   * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
   */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
  {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
  		hba->active_uic_cmd->argument3 =
  			ufshcd_get_dme_attr_val(hba);
  		complete(&hba->active_uic_cmd->done);
  	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
  }
 
  /**
@@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
  	if (hba->errors)
  		ufshcd_check_errors(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
  	if (intr_status & UTP_TASK_REQ_COMPL)
  		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index ab1518d..ecff83d 100644
@@ -178,6 +178,7 @@ struct ufs_dev_cmd {
   * @tm_tag_wq: wait queue for free task management slots
   * @tm_condition: condition variable for task management
   * @tm_slots_in_use: bit map of task management request slots in use
+ * @pwr_done: completion for power mode change
   * @ufshcd_state: UFSHCD states
   * @eh_flags: Error handling flags
   * @intr_mask: Interrupt Mask Bits
@@ -227,6 +228,8 @@ struct ufs_hba {
  	unsigned long tm_condition;
  	unsigned long tm_slots_in_use;
 
+	struct completion *pwr_done;
+
  	u32 ufshcd_state;
  	u32 eh_flags;
  	u32 intr_mask;
drivers/scsi/ufs/ufshci.h +5 -0
index 1e1fe26..0475c66 100644
@@ -124,6 +124,9 @@ enum {
  #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
  #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
  #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
  				DEVICE_FATAL_ERROR |\
  				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
  #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
  #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,

drivers/scsi/ufs/ufshci.h enum { · line 159

Quoted 10 lines
drivers/scsi/ufs/ufshci.h +7 -0
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
  /* HCE - Host Controller Enable 34h */
  #define CONTROLLER_ENABLE	UFS_BIT(0)
  #define CONTROLLER_DISABLE	0x0

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#49 lore raw

Re: [PATCH v3 5/6] scsi: ufs: add operation for the uic power mode change

Quoted 116 lines · Seungwon Jeon
Quoted 104 lines · Subhash Jadavani
Quoted 83 lines · Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
   drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
   drivers/scsi/ufs/ufshcd.h |    3 ++
   drivers/scsi/ufs/ufshci.h |   12 ++++++
   3 files changed, 95 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +47 -0
index 00bfc1a..c67118d 100644
@@ -36,9 +36,11 @@
   #include <linux/async.h>
 
   #include "ufshcd.h"
+#include "unipro.h"
 
   #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
   				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
   				 UFSHCD_ERROR_MASK)
   /* UIC command timeout, unit: ms */
   #define UIC_CMD_TIMEOUT	500
@@ -520,6 +522,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
   }
 
   /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
    * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
    * @hba: per adapter instance
    * @uic_cmd: UIC command
@@ -1526,6 +1540,64 @@ out:
   EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
   /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x uic error %d\n",
+			mode, ret);
+		goto out;
+	}
+

drivers/scsi/ufs/ufshcd.c out: · line 1585

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x completion timeout\n",
+			mode);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);

I have a doubt on the relation between UIC Power Mode Status (UPMS) bit & UIC Power Mode Change Request Status (UPMCRS) field.

Once we send the DME_SET(PA_PWRMODE) command, we will get the UIC command completion interrupt (with IS.UCCS bit set) which just indicate that the attribute is written but power mode change procedure will complete only after some delay which is indicated via IS.UPMS status bit and then we would read the HCS.UPMCRS field to know the status of power mode change.

Quoted 11 lines

This is my exact doubt: Currently UFSHCD driver first clears the IS.UPMS bit (ufshcd_intr() first clears the current interrupts in REG_INTERRUPT_STATUS and then calls the ufshcd_sl_intr() handler, and in your current patch we are anyway not reading the UPMCRS field from interrupt contex ) and then read the HCS.UPMCRS field. Will HCS.UPMCRS field retain its status value even after IS.UPMS bit is cleared? UFSHCI specification doesn't clearly mention this so either we have to get the clarity from JEDEC or from your ufs host controller vendor on the behaviour. I am checking with our ufs host controller vendor as well and will let you know what is the expected behaviour. BTW, i guess you might have already tested this patch to be working on your UFS host controller?

Quoted 5 lines

Hi Subhash,

Yes, It's already tested and works fine.
IS.UPMS is a indication to inform the result of 'power mode change'(UPMCRS). So clearing the interrupt would not affect this result.

Thanks for clarifying.

Quoted 1 line

Generally it seems not common idea.

Infact on SDHCi controller, there is a case where "auto-cmd error status" register fields are only valid when the "auto-cmd error" interrupt bit is set in interrupt status register. Clearing status bit also clears the "auto-cmd error status" register fields automatically. So i was in doubt whether similar could be the case here or not.

Quoted 1 line

Anyway, if your host has different behavior, please let me know.

Sure, checking on this. Would let you know soon. BTW, UFSHCI specification doesn't mention it clearly so it could be implementation dependent as well and if it is the case, we might have to change it later to accomodate the different implementation. So for now, you have my Reviewed-by: Subhash Jadavani <[email protected]>

Quoted 115 lines

Thanks,
Seungwon Jeon

Quoted 91 lines
Quoted 91 lines
drivers/scsi/ufs/ufshcd.c +23 -4
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
    * ufshcd_complete_dev_init() - checks device readiness
    * hba: per-adapter instance
    *
@@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
   /**
    * ufshcd_uic_cmd_compl - handle completion of uic command
    * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
    */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
   {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
   		hba->active_uic_cmd->argument2 |=
   			ufshcd_get_uic_cmd_result(hba);
   		hba->active_uic_cmd->argument3 =
   			ufshcd_get_dme_attr_val(hba);
   		complete(&hba->active_uic_cmd->done);
   	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
   }
 
   /**
@@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
   	if (hba->errors)
   		ufshcd_check_errors(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
   	if (intr_status & UTP_TASK_REQ_COMPL)
   		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index ab1518d..ecff83d 100644
@@ -178,6 +178,7 @@ struct ufs_dev_cmd {
    * @tm_tag_wq: wait queue for free task management slots
    * @tm_condition: condition variable for task management
    * @tm_slots_in_use: bit map of task management request slots in use
+ * @pwr_done: completion for power mode change
    * @ufshcd_state: UFSHCD states
    * @eh_flags: Error handling flags
    * @intr_mask: Interrupt Mask Bits
@@ -227,6 +228,8 @@ struct ufs_hba {
   	unsigned long tm_condition;
   	unsigned long tm_slots_in_use;
 
+	struct completion *pwr_done;
+
   	u32 ufshcd_state;
   	u32 eh_flags;
   	u32 intr_mask;
drivers/scsi/ufs/ufshci.h +3 -0
index 1e1fe26..0475c66 100644
@@ -124,6 +124,9 @@ enum {
   #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
   #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
   #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
   				DEVICE_FATAL_ERROR |\
   				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
   #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
   #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
Quoted 16 lines

drivers/scsi/ufs/ufshci.h enum { · line 159

Quoted 12 lines
drivers/scsi/ufs/ufshci.h +9 -0
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
   /* HCE - Host Controller Enable 34h */
   #define CONTROLLER_ENABLE	UFS_BIT(0)
   #define CONTROLLER_DISABLE	0x0

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#50 lore raw

RE: [PATCH v3 5/6] scsi: ufs: add operation for the uic power mode change

Quoted 124 lines · Subhash Jadavani
Quoted 116 lines · Seungwon Jeon
Quoted 104 lines · Subhash Jadavani
Quoted 83 lines · Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
   drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
   drivers/scsi/ufs/ufshcd.h |    3 ++
   drivers/scsi/ufs/ufshci.h |   12 ++++++
   3 files changed, 95 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +47 -0
index 00bfc1a..c67118d 100644
@@ -36,9 +36,11 @@
   #include <linux/async.h>
 
   #include "ufshcd.h"
+#include "unipro.h"
 
   #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
   				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
   				 UFSHCD_ERROR_MASK)
   /* UIC command timeout, unit: ms */
   #define UIC_CMD_TIMEOUT	500
@@ -520,6 +522,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
   }
 
   /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
    * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
    * @hba: per adapter instance
    * @uic_cmd: UIC command
@@ -1526,6 +1540,64 @@ out:
   EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
   /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x uic error %d\n",
+			mode, ret);
+		goto out;
+	}
+

drivers/scsi/ufs/ufshcd.c out: · line 1585

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev,
+			"pwr mode change with mode 0x%x completion timeout\n",
+			mode);
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);

I have a doubt on the relation between UIC Power Mode Status (UPMS) bit & UIC Power Mode Change Request Status (UPMCRS) field.

Once we send the DME_SET(PA_PWRMODE) command, we will get the UIC command completion interrupt (with IS.UCCS bit set) which just indicate that the attribute is written but power mode change procedure will complete only after some delay which is indicated via IS.UPMS status bit and then we would read the HCS.UPMCRS field to know the status of power mode change.

Quoted 11 lines

This is my exact doubt: Currently UFSHCD driver first clears the IS.UPMS bit (ufshcd_intr() first clears the current interrupts in REG_INTERRUPT_STATUS and then calls the ufshcd_sl_intr() handler, and in your current patch we are anyway not reading the UPMCRS field from interrupt contex ) and then read the HCS.UPMCRS field. Will HCS.UPMCRS field retain its status value even after IS.UPMS bit is cleared? UFSHCI specification doesn't clearly mention this so either we have to get the clarity from JEDEC or from your ufs host controller vendor on the behaviour. I am checking with our ufs host controller vendor as well and will let you know what is the expected behaviour. BTW, i guess you might have already tested this patch to be working on your UFS host controller?

Quoted 5 lines

Hi Subhash,

Yes, It's already tested and works fine.
IS.UPMS is a indication to inform the result of 'power mode change'(UPMCRS). So clearing the interrupt would not affect this result.

Thanks for clarifying.

Quoted 7 lines
Quoted 1 line

Generally it seems not common idea.

Infact on SDHCi controller, there is a case where "auto-cmd error status" register fields are only valid when the "auto-cmd error" interrupt bit is set in interrupt status register. Clearing status bit also clears the "auto-cmd error status" register fields automatically. So i was in doubt whether similar could be the case here or not.

It's interesting thing to me.

Quoted 8 lines
Quoted 2 lines

Anyway, if your host has different behavior, please let me know.

Sure, checking on this. Would let you know soon. BTW, UFSHCI specification doesn't mention it clearly so it could be implementation dependent as well and if it is the case, we might have to change it later to accomodate the different implementation. So for now, you have my Reviewed-by: Subhash Jadavani <[email protected]>

Thank you for review.

Seungwon Jeon

Quoted 121 lines
Quoted 112 lines

Thanks,
Seungwon Jeon

Quoted 91 lines
Quoted 91 lines
drivers/scsi/ufs/ufshcd.c +23 -4
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
    * ufshcd_complete_dev_init() - checks device readiness
    * hba: per-adapter instance
    *
@@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
   /**
    * ufshcd_uic_cmd_compl - handle completion of uic command
    * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
    */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
   {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
   		hba->active_uic_cmd->argument2 |=
   			ufshcd_get_uic_cmd_result(hba);
   		hba->active_uic_cmd->argument3 =
   			ufshcd_get_dme_attr_val(hba);
   		complete(&hba->active_uic_cmd->done);
   	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
   }
 
   /**
@@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
   	if (hba->errors)
   		ufshcd_check_errors(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
   	if (intr_status & UTP_TASK_REQ_COMPL)
   		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index ab1518d..ecff83d 100644
@@ -178,6 +178,7 @@ struct ufs_dev_cmd {
    * @tm_tag_wq: wait queue for free task management slots
    * @tm_condition: condition variable for task management
    * @tm_slots_in_use: bit map of task management request slots in use
+ * @pwr_done: completion for power mode change
    * @ufshcd_state: UFSHCD states
    * @eh_flags: Error handling flags
    * @intr_mask: Interrupt Mask Bits
@@ -227,6 +228,8 @@ struct ufs_hba {
   	unsigned long tm_condition;
   	unsigned long tm_slots_in_use;
 
+	struct completion *pwr_done;
+
   	u32 ufshcd_state;
   	u32 eh_flags;
   	u32 intr_mask;
drivers/scsi/ufs/ufshci.h +3 -0
index 1e1fe26..0475c66 100644
@@ -124,6 +124,9 @@ enum {
   #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
   #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
   #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
   				DEVICE_FATAL_ERROR |\
   				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
   #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
   #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
Quoted 16 lines

drivers/scsi/ufs/ufshci.h enum { · line 159

Quoted 12 lines
drivers/scsi/ufs/ufshci.h +9 -0
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
   /* HCE - Host Controller Enable 34h */
   #define CONTROLLER_ENABLE	UFS_BIT(0)
   #define CONTROLLER_DISABLE	0x0

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Tested-by
#51 lore raw

RE: [PATCH v3 5/6] scsi: ufs: add operation for the uic power mode change

QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

= > -----Original Message-----
= > From: [email protected] [mailto:linux-scsi-
= > [email protected]] On Behalf Of Seungwon Jeon
= > Sent: Tuesday, August 27, 2013 2:58 PM
= > To: 'Subhash Jadavani'
= > Cc: [email protected]; 'Vinayak Holikatti'; 'Santosh Y'; 'James E.J. = > Bottomley'
= > Subject: RE: [PATCH v3 5/6] scsi: ufs: add operation for the uic power = > mode change
= >
= > On Tue, August 27, 2013, Subhash Jadavani wrote:
= > > On 8/27/2013 4:58 PM, Seungwon Jeon wrote:
= > > > On Tue, August 27, 2013, Subhash Jadavani wrote:
= > > >> On 8/26/2013 8:10 PM, Seungwon Jeon wrote:
= > > >>> Setting PA_PWRMode using DME_SET triggers the power mode
= > change.
= > > >>> And then the result will be given by the HCS.UPMCRS.
= > > >>> This operation should be done atomically.
= > > >>>
= > > >>> Signed-off-by: Seungwon Jeon <[email protected]>
= > > >>> ---
= > > >>> drivers/scsi/ufs/ufshcd.c | 84
= > ++++++++++++++++++++++++++++++++++++++++++--

= > > >>>    drivers/scsi/ufs/ufshcd.h |    3 ++
= > > >>>    drivers/scsi/ufs/ufshci.h |   12 ++++++
= > > >>>    3 files changed, 95 insertions(+), 4 deletions(-)

= > > >>>
= > > >>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c = > > >>> index 00bfc1a..c67118d 100644
= > > >>> --- a/drivers/scsi/ufs/ufshcd.c
= > > >>> +++ b/drivers/scsi/ufs/ufshcd.c
= > > >>> @@ -36,9 +36,11 @@
= > > >>> #include <linux/async.h>
= > > >>>
= > > >>> #include "ufshcd.h"
= > > >>> +#include "unipro.h"
= > > >>>
= > > >>> #define UFSHCD_ENABLE_INTRS
= > (UTP_TRANSFER_REQ_COMPL |\
= > > >>> UTP_TASK_REQ_COMPL |\
= > > >>> + UIC_POWER_MODE |\

= > > >>>    				 UFSHCD_ERROR_MASK)
= > > >>>    /* UIC command timeout, unit: ms */
= > > >>>    #define UIC_CMD_TIMEOUT	500

= > > >>> @@ -520,6 +522,18 @@ static inline bool
= > ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
= > > >>> }
= > > >>>
= > > >>> /**
= > > >>> + * ufshcd_get_upmcrs - Get the power mode change request status = > > >>> + * @hba: Pointer to adapter instance
= > > >>> + *
= > > >>> + * This function gets the UPMCRS field of HCS register = > > >>> + * Returns value of UPMCRS field
= > > >>> + */
= > > >>> +static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) {
= > > >>> + return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
= > }
= > > >>> +
= > > >>> +/**
= > > >>> * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro
= > layers
= > > >>> * @hba: per adapter instance
= > > >>> * @uic_cmd: UIC command
= > > >>> @@ -1526,6 +1540,64 @@ out:
= > > >>> EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
= > > >>>
= > > >>> /**
= > > >>> + * ufshcd_uic_change_pwr_mode - Perform the UIC power mode = > chage
= > > >>> + * using DME_SET primitives.
= > > >>> + * @hba: per adapter instance
= > > >>> + * @mode: powr mode value
= > > >>> + *
= > > >>> + * Returns 0 on success, non-zero value on failure */ int = > > >>> +ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) {
= > > >>> + struct uic_command uic_cmd = {0};
= > > >>> + struct completion pwr_done;
= > > >>> + unsigned long flags;
= > > >>> + u8 status;
= > > >>> + int ret;
= > > >>> +
= > > >>> + uic_cmd.command = UIC_CMD_DME_SET;
= > > >>> + uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
= > > >>> + uic_cmd.argument3 = mode;
= > > >>> + init_completion(&pwr_done);
= > > >>> +
= > > >>> + mutex_lock(&hba->uic_cmd_mutex);
= > > >>> +
= > > >>> + spin_lock_irqsave(hba->host->host_lock, flags);
= > > >>> + hba->pwr_done = &pwr_done;
= > > >>> + spin_unlock_irqrestore(hba->host->host_lock, flags);
= > > >>> + ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
= > > >>> + if (ret) {
= > > >>> + dev_err(hba->dev,
= > > >>> + "pwr mode change with mode 0x%x uic error
= > %d\n",
= > > >>> + mode, ret);
= > > >>> + goto out;
= > > >>> + }
= > > >>> +
= > > >>> + if (!wait_for_completion_timeout(hba->pwr_done,
= > > >>> +
= > msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
= > > >>> + dev_err(hba->dev,
= > > >>> + "pwr mode change with mode 0x%x completion
= > timeout\n",
= > > >>> + mode);
= > > >>> + ret = -ETIMEDOUT;
= > > >>> + goto out;
= > > >>> + }
= > > >>> +
= > > >>> + status = ufshcd_get_upmcrs(hba);
= > > >> I have a doubt on the relation between UIC Power Mode Status = > (UPMS)
= > > >> bit & UIC Power Mode Change Request Status (UPMCRS) field. = > > >>
= > > >> Once we send the DME_SET(PA_PWRMODE) command, we will get = > the UIC
= > > >> command completion interrupt (with IS.UCCS bit set) which just = > > >> indicate that the attribute is written but power mode change = > > >> procedure will complete only after some delay which is indicated = > > >> via IS.UPMS status bit and then we would read the HCS.UPMCRS field = > > >> to know the status of power mode change.
= > > >>
= > > >> This is my exact doubt: Currently UFSHCD driver first clears the = > > >> IS.UPMS bit (ufshcd_intr() first clears the current interrupts in = > > >> REG_INTERRUPT_STATUS and then calls the ufshcd_sl_intr() handler, = > > >> and in your current patch we are anyway not reading the UPMCRS = > > >> field from interrupt contex ) and then read the HCS.UPMCRS field. = > > >> Will HCS.UPMCRS field retain its status value even after IS.UPMS = > > >> bit is cleared? UFSHCI specification doesn't clearly mention this = > > >> so either we have to get the clarity from JEDEC or from your ufs = > > >> host controller vendor on the behaviour. I am checking with our ufs = > > >> host controller vendor as well and will let you know what is the = > > >> expected behaviour. BTW, i guess you might have already tested this = > patch to be working on your UFS host controller?
= > > > Hi Subhash,
= > > >
= > > > Yes, It's already tested and works fine.
= > > > IS.UPMS is a indication to inform the result of 'power mode = > change'(UPMCRS).
= > > > So clearing the interrupt would not affect this result. = > > Thanks for clarifying.
= > >
= > > > Generally it seems not common idea.
= > >
= > > Infact on SDHCi controller, there is a case where "auto-cmd error = > > status" register fields are only valid when the "auto-cmd error" = > > interrupt bit is set in interrupt status register. Clearing status bit = > > also clears the "auto-cmd error status" register fields automatically. = > > So i was in doubt whether similar could be the case here or not. = >
= > It's interesting thing to me.
= >
= > >
= > > > Anyway, if your host has different behavior, please let me know. = > >
= > > Sure, checking on this. Would let you know soon. BTW, UFSHCI = > > specification doesn't mention it clearly so it could be implementation = > > dependent as well and if it is the case, we might have to change it = > > later to accomodate the different implementation. So for now, you have = > > my Reviewed-by: Subhash Jadavani <[email protected]> = >
= > Thank you for review.
= >
= > Seungwon Jeon
= >
= > >
= > > >
= > > > Thanks,
= > > > Seungwon Jeon
= > > >
= > > >>> + if (status != PWR_LOCAL) {
= > > >>> + dev_err(hba->dev,
= > > >>> + "pwr mode change failed, host umpcrs:0x%x\n",
= > > >>> + status);
= > > >>> + ret = (status != PWR_OK) ? status : -1;
= > > >>> + }
= > > >>> +out:
= > > >>> + spin_lock_irqsave(hba->host->host_lock, flags);
= > > >>> + hba->pwr_done = NULL;
= > > >>> + spin_unlock_irqrestore(hba->host->host_lock, flags);
= > > >>> + mutex_unlock(&hba->uic_cmd_mutex);
= > > >>> + return ret;
= > > >>> +}
= > > >>> +
= > > >>> +/**

= > > >>>     * ufshcd_complete_dev_init() - checks device readiness
= > > >>>     * hba: per-adapter instance
= > > >>>     *

= > > >>> @@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct = > ufs_hba *hba, struct ufshcd_lrb *lrbp)
= > > >>> /**
= > > >>> * ufshcd_uic_cmd_compl - handle completion of uic command
= > > >>> * @hba: per adapter instance
= > > >>> + * @intr_status: interrupt status generated by the controller
= > > >>> */
= > > >>> -static void ufshcd_uic_cmd_compl(struct ufs_hba *hba) = > > >>> +static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 = > > >>> +intr_status)
= > > >>> {
= > > >>> - if (hba->active_uic_cmd) {
= > > >>> + if ((intr_status & UIC_COMMAND_COMPL) && hba-
= > >active_uic_cmd) {

= > > >>>    		hba->active_uic_cmd->argument2 |=
= > > >>>    			ufshcd_get_uic_cmd_result(hba);
= > > >>>    		hba->active_uic_cmd->argument3 =
= > > >>>    			ufshcd_get_dme_attr_val(hba);
= > > >>>    		complete(&hba->active_uic_cmd->done);
= > > >>>    	}

= > > >>> +
= > > >>> + if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
= > > >>> + complete(hba->pwr_done);
= > > >>> }
= > > >>>
= > > >>> /**
= > > >>> @@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba = > *hba, u32 intr_status)
= > > >>> if (hba->errors)
= > > >>> ufshcd_check_errors(hba);
= > > >>>
= > > >>> - if (intr_status & UIC_COMMAND_COMPL)
= > > >>> - ufshcd_uic_cmd_compl(hba);
= > > >>> + if (intr_status & UFSHCD_UIC_MASK)
= > > >>> + ufshcd_uic_cmd_compl(hba, intr_status);
= > > >>>
= > > >>> if (intr_status & UTP_TASK_REQ_COMPL)
= > > >>> ufshcd_tmc_handler(hba);
= > > >>> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h = > > >>> index ab1518d..ecff83d 100644
= > > >>> --- a/drivers/scsi/ufs/ufshcd.h
= > > >>> +++ b/drivers/scsi/ufs/ufshcd.h
= > > >>> @@ -178,6 +178,7 @@ struct ufs_dev_cmd {

= > > >>>     * @tm_tag_wq: wait queue for free task management slots
= > > >>>     * @tm_condition: condition variable for task management
= > > >>>     * @tm_slots_in_use: bit map of task management request slots
= > > >>> in use
= > > >>> + * @pwr_done: completion for power mode change
= > > >>>     * @ufshcd_state: UFSHCD states
= > > >>>     * @eh_flags: Error handling flags
= > > >>>     * @intr_mask: Interrupt Mask Bits @@ -227,6 +228,8 @@ struct

= > > >>> ufs_hba {
= > > >>> unsigned long tm_condition;
= > > >>> unsigned long tm_slots_in_use;
= > > >>>
= > > >>> + struct completion *pwr_done;
= > > >>> +

= > > >>>    	u32 ufshcd_state;
= > > >>>    	u32 eh_flags;
= > > >>>    	u32 intr_mask;

= > > >>> diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h = > > >>> index 1e1fe26..0475c66 100644
= > > >>> --- a/drivers/scsi/ufs/ufshci.h
= > > >>> +++ b/drivers/scsi/ufs/ufshci.h
= > > >>> @@ -124,6 +124,9 @@ enum {
= > > >>> #define CONTROLLER_FATAL_ERROR
= > UFS_BIT(16)
= > > >>> #define SYSTEM_BUS_FATAL_ERROR
= > UFS_BIT(17)
= > > >>>
= > > >>> +#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL
= > |\
= > > >>> + UIC_POWER_MODE)
= > > >>> +

= > > >>>    #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
= > > >>>    				DEVICE_FATAL_ERROR |\
= > > >>>    				CONTROLLER_FATAL_ERROR |\

= > > >>> @@ -142,6 +145,15 @@ enum {
= > > >>> #define DEVICE_ERROR_INDICATOR UFS_BIT(5)
= > > >>> #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK
= > UFS_MASK(0x7, 8)
= > > >>>
= > > >>> +enum {
= > > >>> + PWR_OK = 0x0,
= > > >>> + PWR_LOCAL = 0x01,
= > > >>> + PWR_REMOTE = 0x02,
= > > >>> + PWR_BUSY = 0x03,
= > > >>> + PWR_ERROR_CAP = 0x04,
= > > >>> + PWR_FATAL_ERROR = 0x05,
= > > >>> +};
= > > >>> +

= > > >>>    /* HCE - Host Controller Enable 34h */
= > > >>>    #define CONTROLLER_ENABLE	UFS_BIT(0)
= > > >>>    #define CONTROLLER_DISABLE	0x0

= > > >> --
= > > >> To unsubscribe from this list: send the line "unsubscribe = > > >> linux-scsi" in the body of a message to [email protected] = > > >> More majordomo info at http://vger.kernel.org/majordomo-
= > info.html
= > > > --
= > > > To unsubscribe from this list: send the line "unsubscribe
= > > > linux-scsi" in the body of a message to [email protected] = > > > More majordomo info at http://vger.kernel.org/majordomo-info.html
= > >
= > > --
= > > To unsubscribe from this list: send the line "unsubscribe linux-scsi" = > > in the body of a message to [email protected] More
= > majordomo
= > > info at http://vger.kernel.org/majordomo-info.html
= >
= > --
= > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the = > body of a message to [email protected] More majordomo info = > at http://vger.kernel.org/majordomo-info.html

#52 lore raw

PATCH v3 6/6 scsi: ufs: configure the attribute for power mode

In reply to Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
 drivers/scsi/ufs/ufshcd.c |   66 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/unipro.h |   21 ++++++++++++++
 2 files changed, 87 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +66 -0
index c67118d..2ae845c 100644
@@ -1598,6 +1598,70 @@ out:
 }
 
 /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);
+
+	return ret;
+}
+
+/**
  * ufshcd_complete_dev_init() - checks device readiness
  * hba: per-adapter instance
  *
@@ -2942,6 +3006,8 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
 	if (ret)
 		goto out;
 
+	ufshcd_config_max_pwr_mode(hba);
+
 	ret = ufshcd_verify_dev_init(hba);
 	if (ret)
 		goto out;
drivers/scsi/ufs/unipro.h +21 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
 #define PA_STALLNOCONFIGTIME	0x15A3
 #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
 /*
  * Data Link Layer Attributes
  */
@@ -127,4 +142,10 @@
 #define T_TC0TXMAXSDUSIZE	0x4060
 #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
 #endif /* _UNIPRO_H_ */
Signature · 1 line
1.7.0.4
#53 lore raw

Re: [PATCH v3 6/6] scsi: ufs: configure the attribute for power mode

Quoted 38 lines · Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
  drivers/scsi/ufs/ufshcd.c |   66 +++++++++++++++++++++++++++++++++++++++++++++
  drivers/scsi/ufs/unipro.h |   21 ++++++++++++++
  2 files changed, 87 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +18 -0
index c67118d..2ae845c 100644
@@ -1598,6 +1598,70 @@ out:
  }
 
  /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+

drivers/scsi/ufs/ufshcd.c out: · line 1628

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}

As per UniPro specification, PA_MaxRxHsGear valid values are 0 (NO, HS), 1 (HS_Gear1), 2 (HS_Gear2) & 3 (HS_Gear3). But UFS device specification (v1.1) only mandates to support Gear-1 and Gear-2 is optional. Gear-3 is no where mentioned in the specifcation so it means unsupported gear. So i guess after reading PA_MaxRxHsGear attribute, we have to ensure that we only set the Gear-1 or Gear-2 as mandated by UFS 1.1 spec.

Also, for PA_MaxRxPWMGear both UniPro and UFS device specification support the gear-1 to gear-7. so i guess we should have some safely check to ensure that gear is not 0 or greater than 7.

Quoted 21 lines
drivers/scsi/ufs/ufshcd.c +21 -0
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);

drivers/scsi/ufs/ufshcd.c out: · line 1659

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);

I guess it would be helpful to print the powermode, Gear, Lanes & Rate information after error. Also, i guess the gear change is one time operation, won't it be useful to print this information so we know what is the host-device configuration?

Quoted 54 lines
drivers/scsi/ufs/ufshcd.c +7 -0
+
+	return ret;
+}
+
+/**
   * ufshcd_complete_dev_init() - checks device readiness
   * hba: per-adapter instance
   *
@@ -2942,6 +3006,8 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
  	if (ret)
  		goto out;
 
+	ufshcd_config_max_pwr_mode(hba);
+
  	ret = ufshcd_verify_dev_init(hba);
  	if (ret)
  		goto out;
drivers/scsi/ufs/unipro.h +21 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
  #define PA_STALLNOCONFIGTIME	0x15A3
  #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
  /*
   * Data Link Layer Attributes
   */
@@ -127,4 +142,10 @@
  #define T_TC0TXMAXSDUSIZE	0x4060
  #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
  #endif /* _UNIPRO_H_ */
#54 lore raw

Re: [PATCH v3 6/6] scsi: ufs: configure the attribute for power mode

Quoted 53 lines · Subhash Jadavani
Quoted 40 lines · Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
  drivers/scsi/ufs/ufshcd.c |   66 
+++++++++++++++++++++++++++++++++++++++++++++
  drivers/scsi/ufs/unipro.h |   21 ++++++++++++++
  2 files changed, 87 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +18 -0
index c67118d..2ae845c 100644
@@ -1598,6 +1598,70 @@ out:
  }
    /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *    maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+    enum {RX = 0, TX = 1};
+    u32 lanes[] = {1, 1};
+    u32 gear[] = {1, 1};
+    u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+    int ret;
+
+    /* Get the connected lane count */
+    ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), 
 &lanes[RX]);
+    ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), 
 &lanes[TX]);
+

drivers/scsi/ufs/ufshcd.c out: · line 1629

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+    /*
+     * First, get the maximum gears of HS speed.
+     * If a zero value, it means there is no HSGEAR capability.
+     * Then, get the maximum gears of PWM speed.
+     */
+    ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+    if (!gear[RX]) {
+        ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+        pwr[RX] = SLOWAUTO_MODE;
+    }

As per UniPro specification, PA_MaxRxHsGear valid values are 0 (NO, HS), 1 (HS_Gear1), 2 (HS_Gear2) & 3 (HS_Gear3). But UFS device specification (v1.1) only mandates to support Gear-1 and Gear-2 is optional. Gear-3 is no where mentioned in the specifcation so it means unsupported gear. So i guess after reading PA_MaxRxHsGear attribute, we have to ensure that we only set the Gear-1 or Gear-2 as mandated by UFS 1.1 spec.

Quoted 1 line

Also, for PA_MaxRxPWMGear both UniPro and UFS device specification support the gear-1 to gear-7. so i guess we should have some safely check to ensure that gear is not 0 or greater than 7.

Sorry, please ignore this comment. PA_MaxRxHsGear and PA_MaxRxPWMGear are automatically updated after the capability negotiation between host and device during link start up so they must be supporting the mutually agreed gears.

Quoted 25 lines
Quoted 25 lines
drivers/scsi/ufs/ufshcd.c +24 -0
+
+    ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+    if (!gear[TX]) {
+        ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+                    &gear[TX]);
+        pwr[TX] = SLOWAUTO_MODE;
+    }
+
+    /*
+     * Configure attributes for power mode change with below.
+     * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+     * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+     * - PA_HSSERIES
+     */
+    ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+    ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+    if (pwr[RX] == FASTAUTO_MODE)
+        ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+    ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+    ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+    if (pwr[TX] == FASTAUTO_MODE)
+        ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
+
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c out: · line 1659

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +7 -0
+    if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+        ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+    ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+    if (ret)
+        dev_err(hba->dev,
+            "pwr_mode: power mode change failed %d\n", ret);

I guess it would be helpful to print the powermode, Gear, Lanes & Rate information after error. Also, i guess the gear change is one time operation, won't it be useful to print this information so we know what is the host-device configuration?

This is just a debug enhancement so its upto you whether you want to fix it now or not. In any case, you will still have my Reviewed-by: Subhash Jadavani <[email protected]>

Quoted 58 lines
Quoted 53 lines
drivers/scsi/ufs/ufshcd.c +5 -0
+
+    return ret;
+}
+
+/**
   * ufshcd_complete_dev_init() - checks device readiness
   * hba: per-adapter instance
   *
drivers/scsi/ufs/ufshcd.c +1 -0
@@ -2942,6 +3006,8 @@ static void ufshcd_async_scan(void *data, 
 async_cookie_t cookie)
      if (ret)
          goto out;
  +    ufshcd_config_max_pwr_mode(hba);
+
      ret = ufshcd_verify_dev_init(hba);
      if (ret)
          goto out;
drivers/scsi/ufs/unipro.h +19 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
  #define PA_STALLNOCONFIGTIME    0x15A3
  #define PA_SAVECONFIGTIME    0x15A4
  +/* PA power modes */
+enum {
+    FAST_MODE    = 1,
+    SLOW_MODE    = 2,
+    FASTAUTO_MODE    = 4,
+    SLOWAUTO_MODE    = 5,
+    UNCHANGED    = 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+    PA_HS_MODE_A    = 1,
+    PA_HS_MODE_B    = 2,
+};
+
  /*
   * Data Link Layer Attributes
   */
@@ -127,4 +142,10 @@
  #define T_TC0TXMAXSDUSIZE    0x4060
  #define T_TC1TXMAXSDUSIZE    0x4061
  +/* Boolean attribute values */
+enum {
+    FALSE = 0,
+    TRUE,
+};
+
  #endif /* _UNIPRO_H_ */
Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#55 lore raw

PATCH 1/1 scsi: ufs: export the helper functions for vender probe/remove

In reply to Seungwon Jeon

This change provides the common routines for driver's probe/remove. Especially host driver including specific operations can invoke the initial routine at the own probing phase and pass its operations to ufshcd's common part.

---
 drivers/scsi/ufs/ufshcd-pltfrm.c |   49 +++++++++++++++++++++++++++----------
 drivers/scsi/ufs/ufshcd-pltfrm.h |   19 ++++++++++++++
 2 files changed, 55 insertions(+), 13 deletions(-)
 create mode 100644 drivers/scsi/ufs/ufshcd-pltfrm.h
drivers/scsi/ufs/ufshcd-pltfrm.c +36 -13
index 9c94052..4900597 100644
@@ -38,6 +38,7 @@
 #include <linux/of.h>
 
 #include "ufshcd.h"
+#include "ufshcd-pltfrm.h"
 
 static const struct of_device_id ufs_of_match[];
 static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
@@ -137,12 +138,13 @@ static int ufshcd_pltfrm_runtime_idle(struct device *dev)
 #endif /* CONFIG_PM_RUNTIME */
 
 /**
- * ufshcd_pltfrm_probe - probe routine of the driver
+ * ufshcd_pltfrm_init - common routine for the driver's initialization
  * @pdev: pointer to Platform device handle
  *
  * Returns 0 on success, non-zero value on failure
  */
-static int ufshcd_pltfrm_probe(struct platform_device *pdev)
+int ufshcd_pltfrm_init(struct platform_device *pdev,
+		       const struct ufs_hba_variant_ops *vops)
 {
 	struct ufs_hba *hba;
 	void __iomem *mmio_base;
@@ -166,14 +168,13 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
 
 	err = ufshcd_alloc_host(dev, &hba);
 	if (err) {
-		dev_err(&pdev->dev, "Allocation failed\n");
+		dev_err(dev, "Allocation failed\n");
 		goto out;
 	}
 
-	hba->vops = get_variant_ops(&pdev->dev);
-
-	pm_runtime_set_active(&pdev->dev);
-	pm_runtime_enable(&pdev->dev);
+	hba->vops = vops;
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
 
 	err = ufshcd_init(hba, mmio_base, irq);
 	if (err) {
@@ -186,24 +187,46 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
 	return 0;
 
 out_disable_rpm:
-	pm_runtime_disable(&pdev->dev);
-	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
 out:
 	return err;
 }
+EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
 
 /**
- * ufshcd_pltfrm_remove - remove platform driver routine
+ * ufshcd_pltfrm_exit - common routine for the driver's exit
  * @pdev: pointer to platform device handle
- *
- * Returns 0 on success, non-zero value on failure
  */
-static int ufshcd_pltfrm_remove(struct platform_device *pdev)
+void ufshcd_pltfrm_exit(struct platform_device *pdev)
 {
 	struct ufs_hba *hba =  platform_get_drvdata(pdev);
 
 	pm_runtime_get_sync(&(pdev)->dev);
 	ufshcd_remove(hba);
+}
+EXPORT_SYMBOL_GPL(ufshcd_pltfrm_exit);
+
+/**
+ * ufshcd_pltfrm_probe - probe the platform driver
+ * @pdev: pointer to Platform device handle
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_pltfrm_probe(struct platform_device *pdev)
+{
+	return ufshcd_pltfrm_init(pdev, get_variant_ops(&pdev->dev));
+}
+
+/**
+ * ufshcd_pltfrm_remove - remove the platform driver
+ * @pdev: pointer to platform device handle
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_pltfrm_remove(struct platform_device *pdev)
+{
+	ufshcd_pltfrm_exit(pdev);
 	return 0;
 }
 
drivers/scsi/ufs/ufshcd-pltfrm.h new file +19 -0
new file mode 100644
index 0000000..6adab05
@@ -0,0 +1,19 @@
+/*
+ * Universal Flash Storage Host controller Platform bus based glue driver
+ *
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ *
+ * This program 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; either version 2 of the License, or
+ * at your option) any later version.
+ */
+
+#ifndef _UFSHCD_PLTFRM_H_
+#define _UFSHCD_PLTFRM_H_
+
+extern int ufshcd_pltfrm_init(struct platform_device *pdev,
+			      const struct ufs_hba_variant_ops *vops);
+extern void ufshcd_pltfrm_exit(struct platform_device *pdev);
+
+#endif /* _UFSHCD_PLTFRM_H_ */
Signature · 1 line
1.7.0.4
#56 lore raw

PATCH 3/7 scsi: ufs: fix the setting interrupt aggregation counter

In reply to Akinobu Mita

LACTH(Interrupt aggregation counter threshold) value is allowed up to 0x1F and current setting value is the maximum. This value is related with NUTRS(max:0x20) of HCI's capability. Considering HCI controller doesn't support the maximum, LATCH setting should be adjusted with possible value.
For that, existing 'ufshcd_config_int_aggr' is split into two part [reset, configure].

---
 drivers/scsi/ufs/ufshcd.c |   53 +++++++++++++++++++++-----------------------
 drivers/scsi/ufs/ufshci.h |    4 +-
 2 files changed, 27 insertions(+), 30 deletions(-)
drivers/scsi/ufs/ufshcd.c +25 -28
index 688ae0e..7152ec4 100644
@@ -43,6 +43,9 @@
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT	500
 
+/* Interrupt aggregation default timeout, unit: 40us */
+#define INT_AGGR_DEF_TO	0x02
+
 enum {
 	UFSHCD_MAX_CHANNEL	= 0,
 	UFSHCD_MAX_ID		= 1,
@@ -65,12 +68,6 @@ enum {
 	UFSHCD_INT_CLEAR,
 };
 
-/* Interrupt aggregation options */
-enum {
-	INT_AGGR_RESET,
-	INT_AGGR_CONFIG,
-};
-
 /**
  * ufshcd_get_intr_mask - Get the interrupt bit mask
  * @hba - Pointer to adapter instance
@@ -233,30 +230,30 @@ ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
 }
 
 /**
- * ufshcd_config_int_aggr - Configure interrupt aggregation values.
- *		Currently there is no use case where we want to configure
- *		interrupt aggregation dynamically. So to configure interrupt
- *		aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
- *		INT_AGGR_TIMEOUT_VALUE are used.
+ * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
  * @hba: per adapter instance
- * @option: Interrupt aggregation option
  */
 static inline void
-ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+ufshcd_reset_intr_aggr(struct ufs_hba *hba)
 {
-	switch (option) {
-	case INT_AGGR_RESET:
-		ufshcd_writel(hba, INT_AGGR_ENABLE |
-			      INT_AGGR_COUNTER_AND_TIMER_RESET,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	case INT_AGGR_CONFIG:
-		ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
-			      INT_AGGR_COUNTER_THRESHOLD_VALUE |
-			      INT_AGGR_TIMEOUT_VALUE,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	}
+	ufshcd_writel(hba, INT_AGGR_ENABLE |
+		      INT_AGGR_COUNTER_AND_TIMER_RESET,
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
+ * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
+ * @hba: per adapter instance
+ * @cntr_thld: Interrupt aggregation counter threshold
+ * @timeout: Interrupt aggregation timeout value
+ */
+static inline void
+ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 timeout)
+{
+	ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
+		      INT_AGGR_TIMEOUT_VAL(timeout),
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 }
 
 /**
@@ -853,7 +850,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
 	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
 	/* Configure interrupt aggregation */
-	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+	ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
 
 	/* Configure UTRL and UTMRL base address registers */
 	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -1299,7 +1296,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 	hba->outstanding_reqs ^= completed_reqs;
 
 	/* Reset interrupt aggregation counters */
-	ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+	ufshcd_reset_intr_aggr(hba);
 }
 
 /**
drivers/scsi/ufs/ufshci.h +2 -2
index d5c5f14..a8f69cc 100644
@@ -226,8 +226,8 @@ enum {
 
 #define MASK_UIC_COMMAND_RESULT			0xFF
 
-#define INT_AGGR_COUNTER_THRESHOLD_VALUE	(0x1F << 8)
-#define INT_AGGR_TIMEOUT_VALUE			(0x02)
+#define INT_AGGR_COUNTER_THLD_VAL(c)	(((c) & 0x1F) << 8)
+#define INT_AGGR_TIMEOUT_VAL(t)		(((t) & 0xFF) << 0)
 
 /* Interrupt disable masks */
 enum {
Signature · 1 line
1.7.0.4
Reviewed-by on v1 3/7
#57 lore raw

Re: [PATCH 3/7] scsi: ufs: fix the setting interrupt aggregation counter

Quoted 1 line · Seungwon Jeon

LACTH(Interrupt aggregation counter threshold) value is allowed

I guess you mean "IACTH" not "LACTH". Other than this, change looks good to me.

Quoted 124 lines

up to 0x1F and current setting value is the maximum. This value is related with NUTRS(max:0x20) of HCI's capability. Considering HCI controller doesn't support the maximum, LATCH setting should be adjusted with possible value.
For that, existing 'ufshcd_config_int_aggr' is split into two part [reset, configure].

---
  drivers/scsi/ufs/ufshcd.c |   53 +++++++++++++++++++++-----------------------
  drivers/scsi/ufs/ufshci.h |    4 +-
  2 files changed, 27 insertions(+), 30 deletions(-)
drivers/scsi/ufs/ufshcd.c +25 -28
index 688ae0e..7152ec4 100644
@@ -43,6 +43,9 @@
  /* UIC command timeout, unit: ms */
  #define UIC_CMD_TIMEOUT	500
 
+/* Interrupt aggregation default timeout, unit: 40us */
+#define INT_AGGR_DEF_TO	0x02
+
  enum {
  	UFSHCD_MAX_CHANNEL	= 0,
  	UFSHCD_MAX_ID		= 1,
@@ -65,12 +68,6 @@ enum {
  	UFSHCD_INT_CLEAR,
  };
 
-/* Interrupt aggregation options */
-enum {
-	INT_AGGR_RESET,
-	INT_AGGR_CONFIG,
-};
-
  /**
   * ufshcd_get_intr_mask - Get the interrupt bit mask
   * @hba - Pointer to adapter instance
@@ -233,30 +230,30 @@ ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
  }
 
  /**
- * ufshcd_config_int_aggr - Configure interrupt aggregation values.
- *		Currently there is no use case where we want to configure
- *		interrupt aggregation dynamically. So to configure interrupt
- *		aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
- *		INT_AGGR_TIMEOUT_VALUE are used.
+ * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
   * @hba: per adapter instance
- * @option: Interrupt aggregation option
   */
  static inline void
-ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+ufshcd_reset_intr_aggr(struct ufs_hba *hba)
  {
-	switch (option) {
-	case INT_AGGR_RESET:
-		ufshcd_writel(hba, INT_AGGR_ENABLE |
-			      INT_AGGR_COUNTER_AND_TIMER_RESET,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	case INT_AGGR_CONFIG:
-		ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
-			      INT_AGGR_COUNTER_THRESHOLD_VALUE |
-			      INT_AGGR_TIMEOUT_VALUE,
-			      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-		break;
-	}
+	ufshcd_writel(hba, INT_AGGR_ENABLE |
+		      INT_AGGR_COUNTER_AND_TIMER_RESET,
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
+ * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
+ * @hba: per adapter instance
+ * @cntr_thld: Interrupt aggregation counter threshold
+ * @timeout: Interrupt aggregation timeout value
+ */
+static inline void
+ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 timeout)
+{
+	ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
+		      INT_AGGR_TIMEOUT_VAL(timeout),
+		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
  }
 
  /**
@@ -853,7 +850,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
  	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
  	/* Configure interrupt aggregation */
-	ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+	ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
 
  	/* Configure UTRL and UTMRL base address registers */
  	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -1299,7 +1296,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
  	hba->outstanding_reqs ^= completed_reqs;
 
  	/* Reset interrupt aggregation counters */
-	ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+	ufshcd_reset_intr_aggr(hba);
  }
 
  /**
drivers/scsi/ufs/ufshci.h +2 -2
index d5c5f14..a8f69cc 100644
@@ -226,8 +226,8 @@ enum {
 
  #define MASK_UIC_COMMAND_RESULT			0xFF
 
-#define INT_AGGR_COUNTER_THRESHOLD_VALUE	(0x1F << 8)
-#define INT_AGGR_TIMEOUT_VALUE			(0x02)
+#define INT_AGGR_COUNTER_THLD_VAL(c)	(((c) & 0x1F) << 8)
+#define INT_AGGR_TIMEOUT_VAL(t)		(((t) & 0xFF) << 0)
 
  /* Interrupt disable masks */
  enum {
#58 lore raw

RE: [PATCH 3/7] scsi: ufs: fix the setting interrupt aggregation counter

Quoted 6 lines · Subhash Jadavani
Quoted 1 line · Seungwon Jeon

LACTH(Interrupt aggregation counter threshold) value is allowed

I guess you mean "IACTH" not "LACTH". Other than this, change looks good to me.

Right, Typo will be fixed.

Thanks,
Seungwon Jeon

#59 lore raw

PATCH 4/7 scsi: ufs: add dme configuration primitives

In reply to Akinobu Mita

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
 drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
 drivers/scsi/ufs/ufshci.h |    6 +++
 3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +88 -0
index 7152ec4..8277c40 100644
@@ -188,6 +188,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
  * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
  * @ucd_rsp_ptr: pointer to response UPIU
  *
@@ -821,6 +833,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate wherter peer or non-peer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate wherter peer or non-peer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1256,6 +1342,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
 	if (hba->active_uic_cmd) {
 		hba->active_uic_cmd->argument2 |=
 			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);
 		complete(&hba->active_uic_cmd->done);
 	}
 }
drivers/scsi/ufs/ufshcd.h +51 -0

61 lines not on this page · raw message

drivers/scsi/ufs/ufshci.h +6 -0
index a8f69cc..df4901e 100644
@@ -191,6 +191,12 @@ enum {
 #define CONFIG_RESULT_CODE_MASK		0xFF
 #define GENERIC_ERROR_CODE_MASK		0xFF
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
 /* UIC Commands */
 enum {
 	UIC_CMD_DME_GET			= 0x01,
Signature · 1 line
1.7.0.4
#60 lore raw

Re: [PATCH 4/7] scsi: ufs: add dme configuration primitives

Quoted 36 lines · Seungwon Jeon

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
  drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
  drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
  drivers/scsi/ufs/ufshci.h |    6 +++
  3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +12 -0
index 7152ec4..8277c40 100644
@@ -188,6 +188,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
   * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
   * @ucd_rsp_ptr: pointer to response UPIU
   *

drivers/scsi/ufs/ufshcd.c static int ufshcd_dme_link_startup(struct ufs_hba *hba) · line 841

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +6 -0
@@ -821,6 +833,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate wherter peer or non-peer

typo: s/wtherter/whether
This would sound better: s/non-peer/local

Do above for ufshcd_dme_get_attr() function as well.

Quoted 14 lines
drivers/scsi/ufs/ufshcd.c +14 -0
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+

drivers/scsi/ufs/ufshcd.c static int ufshcd_dme_link_startup(struct ufs_hba *hba) · line 865

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), ret);

Its also good to print the "mib_val" which we were trying to set. It might be possible that DME_SET failed because we are trying to set out of range value to MIB attribute.

Quoted 48 lines
drivers/scsi/ufs/ufshcd.c +44 -0
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate wherter peer or non-peer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
   * ufshcd_make_hba_operational - Make UFS controller operational
   * @hba: per adapter instance
   *

drivers/scsi/ufs/ufshcd.c static void ufshcd_uic_cmd_compl(struct ufs_hba *hba) · line 1346

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +2 -0
@@ -1256,6 +1342,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
  	if (hba->active_uic_cmd) {
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);

We can optimize here by reading the dme_attribute value only if active_uic_cmd->command is set to DME_GET or DME_PEER_GET. For all other DME commands, meaning of this is "reserved" for read. Also, i guess reading of the argument2 and argument3 registers can be moved to ufshcd_wait_for_uic_cmd() function (process context) intead of interrupt context.

Quoted 87 lines
drivers/scsi/ufs/ufshcd.c +0 -0
  		complete(&hba->active_uic_cmd->done);
  	}
  }
drivers/scsi/ufs/ufshcd.h +51 -0
index 49590ee..50bcd29 100644
@@ -199,6 +199,11 @@ int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
  			unsigned int);
  void ufshcd_remove(struct ufs_hba *);
 
+extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u8 attr_set, u32 mib_val, u8 peer);
+extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u32 *mib_val, u8 peer);
+
  /**
   * ufshcd_hba_stop - Send controller to reset state
   * @hba: per adapter instance
@@ -208,4 +213,50 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
  	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
  }
 
+/* UIC command interfaces for DME primitives */
+#define DME_LOCAL	0
+#define DME_PEER	1
+#define ATTR_SET_NOR	0	/* NORMAL */
+#define ATTR_SET_ST	1	/* STATIC */
+
+static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
+				 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
+				    u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
+				      u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 attr_sel,
+					 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_get(struct ufs_hba *hba,
+				 u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
+				      u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
+}
+
  #endif /* End of Header */
drivers/scsi/ufs/ufshci.h +6 -0
index a8f69cc..df4901e 100644
@@ -191,6 +191,12 @@ enum {
  #define CONFIG_RESULT_CODE_MASK		0xFF
  #define GENERIC_ERROR_CODE_MASK		0xFF
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
  /* UIC Commands */
  enum {
  	UIC_CMD_DME_GET			= 0x01,
#61 lore raw

RE: [PATCH 4/7] scsi: ufs: add dme configuration primitives

Quoted 39 lines · Subhash Jadavani
Quoted 38 lines · Seungwon Jeon

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
  drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
  drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
  drivers/scsi/ufs/ufshci.h |    6 +++
  3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +12 -0
index 7152ec4..8277c40 100644
@@ -188,6 +188,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
   * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
   * @ucd_rsp_ptr: pointer to response UPIU
   *
@@ -821,6 +833,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
  }
 
Quoted 10 lines

drivers/scsi/ufs/ufshcd.c static int ufshcd_dme_link_startup(struct ufs_hba *hba) · line 841

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +6 -0
  /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate wherter peer or non-peer

typo: s/wtherter/whether
This would sound better: s/non-peer/local

Ok.

Quoted 22 lines

Do above for ufshcd_dme_get_attr() function as well.

Quoted 20 lines
drivers/scsi/ufs/ufshcd.c +20 -0
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
+
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c static int ufshcd_dme_link_startup(struct ufs_hba *hba) · line 865

Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), ret);

Its also good to print the "mib_val" which we were trying to set. It might be possible that DME_SET failed because we are trying to set out of range value to MIB attribute.

I'll add it.

Quoted 50 lines
Quoted 50 lines
drivers/scsi/ufs/ufshcd.c +44 -0
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate wherter peer or non-peer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
   * ufshcd_make_hba_operational - Make UFS controller operational
   * @hba: per adapter instance
   *
@@ -1256,6 +1342,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
  	if (hba->active_uic_cmd) {
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c static void ufshcd_uic_cmd_compl(struct ufs_hba *hba) · line 1346

Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +2 -0
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);

We can optimize here by reading the dme_attribute value only if active_uic_cmd->command is set to DME_GET or DME_PEER_GET. For all other DME commands, meaning of this is "reserved" for read.

Right, you have a point.
But when considering the access of 'hba->active_uic_cmd->command" every time and additional 'branch statement', it looks like no difference in terms of the optimization.
Reading the dme_attribute value could be trivial thing.

Quoted 3 lines

Also, i guess reading of the argument2 and argument3 registers can be moved to ufshcd_wait_for_uic_cmd() function (process context) intead of interrupt context.

Yes, it may be possible.
But I wanted to update the result of uic command as soon as IS.UCCS occurs and simplify error handling.

Thanks,
Seungwon Jeon

Quoted 93 lines
Quoted 78 lines
drivers/scsi/ufs/ufshcd.c +0 -0
  		complete(&hba->active_uic_cmd->done);
  	}
  }
drivers/scsi/ufs/ufshcd.h +51 -0
index 49590ee..50bcd29 100644
@@ -199,6 +199,11 @@ int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
  			unsigned int);
  void ufshcd_remove(struct ufs_hba *);
 
+extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u8 attr_set, u32 mib_val, u8 peer);
+extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u32 *mib_val, u8 peer);
+
  /**
   * ufshcd_hba_stop - Send controller to reset state
   * @hba: per adapter instance
@@ -208,4 +213,50 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
  	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
  }
 
+/* UIC command interfaces for DME primitives */
+#define DME_LOCAL	0
+#define DME_PEER	1
+#define ATTR_SET_NOR	0	/* NORMAL */
+#define ATTR_SET_ST	1	/* STATIC */
+
+static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
+				 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
+				    u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
+				      u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 attr_sel,
+					 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_get(struct ufs_hba *hba,
+				 u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
+				      u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
+}
+
  #endif /* End of Header */
drivers/scsi/ufs/ufshci.h +0 -0
index a8f69cc..df4901e 100644
@@ -191,6 +191,12 @@ enum {
  #define CONFIG_RESULT_CODE_MASK		0xFF
  #define GENERIC_ERROR_CODE_MASK		0xFF

drivers/scsi/ufs/ufshci.h enum { · line 202

Quoted 10 lines
drivers/scsi/ufs/ufshci.h +6 -0
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
  /* UIC Commands */
  enum {
  	UIC_CMD_DME_GET			= 0x01,

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#62 lore raw

Re: [PATCH 4/7] scsi: ufs: add dme configuration primitives

Quoted 49 lines · Seungwon Jeon
Quoted 39 lines · Subhash Jadavani
Quoted 38 lines · Seungwon Jeon

Implements to support GET and SET operations of the DME. These operations are used to configure the behavior of the UNIPRO. Along with basic operation, {Peer/AttrSetType} can be mixed.

---
   drivers/scsi/ufs/ufshcd.c |   88 +++++++++++++++++++++++++++++++++++++++++++++
   drivers/scsi/ufs/ufshcd.h |   51 ++++++++++++++++++++++++++
   drivers/scsi/ufs/ufshci.h |    6 +++
   3 files changed, 145 insertions(+), 0 deletions(-)
drivers/scsi/ufs/ufshcd.c +12 -0
index 7152ec4..8277c40 100644
@@ -188,6 +188,18 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
   }
 
   /**
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
    * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
    * @ucd_rsp_ptr: pointer to response UPIU
    *
@@ -821,6 +833,80 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
   }
 
Quoted 9 lines

drivers/scsi/ufs/ufshcd.c static int ufshcd_dme_link_startup(struct ufs_hba *hba) · line 841

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +6 -0
   /**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate wherter peer or non-peer

typo: s/wtherter/whether
This would sound better: s/non-peer/local

Quoted 1 line

Ok.

thanks.

Quoted 29 lines
Quoted 21 lines

Do above for ufshcd_dme_get_attr() function as well.

Quoted 19 lines
drivers/scsi/ufs/ufshcd.c +19 -0
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			u8 attr_set, u32 mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-set",
+		"dme-peer-set"
+	};
+	const char *set = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+	uic_cmd.argument1 = attr_sel;
+	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+	uic_cmd.argument3 = mib_val;
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c static int ufshcd_dme_link_startup(struct ufs_hba *hba) · line 865

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +5 -0
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret)
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			set, UIC_GET_ATTR_ID(attr_sel), ret);

Its also good to print the "mib_val" which we were trying to set. It might be possible that DME_SET failed because we are trying to set out of range value to MIB attribute.

Quoted 1 line

I'll add it.

thanks

Quoted 57 lines
Quoted 49 lines
Quoted 49 lines
drivers/scsi/ufs/ufshcd.c +44 -0
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate wherter peer or non-peer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			u32 *mib_val, u8 peer)
+{
+	struct uic_command uic_cmd = {0};
+	static const char *const action[] = {
+		"dme-get",
+		"dme-peer-get"
+	};
+	const char *get = action[!!peer];
+	int ret;
+
+	uic_cmd.command = peer ?
+		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+	uic_cmd.argument1 = attr_sel;
+
+	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+			get, UIC_GET_ATTR_ID(attr_sel), ret);
+		goto out;
+	}
+
+	if (mib_val)
+		*mib_val = uic_cmd.argument3;
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
    * ufshcd_make_hba_operational - Make UFS controller operational
    * @hba: per adapter instance
    *
@@ -1256,6 +1342,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c static void ufshcd_uic_cmd_compl(struct ufs_hba *hba) · line 1346

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +2 -0
   	if (hba->active_uic_cmd) {
   		hba->active_uic_cmd->argument2 |=
   			ufshcd_get_uic_cmd_result(hba);
+		hba->active_uic_cmd->argument3 =
+			ufshcd_get_dme_attr_val(hba);

We can optimize here by reading the dme_attribute value only if active_uic_cmd->command is set to DME_GET or DME_PEER_GET. For all other DME commands, meaning of this is "reserved" for read.

Quoted 4 lines

Right, you have a point.
But when considering the access of 'hba->active_uic_cmd->command" every time and additional 'branch statement', it looks like no difference in terms of the optimization.
Reading the dme_attribute value could be trivial thing.

I would consider the peripheral register to be time heavy (as it may run on slower peripheral bus) compared to variable read (from cache/RAM) & branch statement. But i leave it upto you and it would be fine anyway.

Quoted 6 lines
Quoted 4 lines

Also, i guess reading of the argument2 and argument3 registers can be moved to ufshcd_wait_for_uic_cmd() function (process context) intead of interrupt context.

Yes, it may be possible.
But I wanted to update the result of uic command as soon as IS.UCCS occurs and simplify error handling.

Ok.

Quoted 95 lines

Thanks,
Seungwon Jeon

Quoted 91 lines
Quoted 77 lines
drivers/scsi/ufs/ufshcd.c +0 -0
   		complete(&hba->active_uic_cmd->done);
   	}
   }
drivers/scsi/ufs/ufshcd.h +51 -0
index 49590ee..50bcd29 100644
@@ -199,6 +199,11 @@ int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * ,
   			unsigned int);
   void ufshcd_remove(struct ufs_hba *);
 
+extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u8 attr_set, u32 mib_val, u8 peer);
+extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+			       u32 *mib_val, u8 peer);
+
   /**
    * ufshcd_hba_stop - Send controller to reset state
    * @hba: per adapter instance
@@ -208,4 +213,50 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
   	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
   }
 
+/* UIC command interfaces for DME primitives */
+#define DME_LOCAL	0
+#define DME_PEER	1
+#define ATTR_SET_NOR	0	/* NORMAL */
+#define ATTR_SET_ST	1	/* STATIC */
+
+static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
+				 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel,
+				    u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel,
+				      u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 attr_sel,
+					 u32 mib_val)
+{
+	return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST,
+				   mib_val, DME_PEER);
+}
+
+static inline int ufshcd_dme_get(struct ufs_hba *hba,
+				 u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL);
+}
+
+static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
+				      u32 attr_sel, u32 *mib_val)
+{
+	return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
+}
+
   #endif /* End of Header */
drivers/scsi/ufs/ufshci.h +0 -0
index a8f69cc..df4901e 100644
@@ -191,6 +191,12 @@ enum {
   #define CONFIG_RESULT_CODE_MASK		0xFF
   #define GENERIC_ERROR_CODE_MASK		0xFF

drivers/scsi/ufs/ufshci.h enum { · line 202

Quoted 10 lines
drivers/scsi/ufs/ufshci.h +6 -0
 
+#define UIC_ARG_MIB_SEL(attr, sel)	((((attr) & 0xFFFF) << 16) |\
+					 ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr)		UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t)		(((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v)		(((v) >> 16) & 0xFFFF)
+
   /* UIC Commands */
   enum {
   	UIC_CMD_DME_GET			= 0x01,

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#63 lore raw

PATCH 5/7 scsi: ufs: add unipro attribute IDs

In reply to Akinobu Mita

'drivers/scsi/ufs/unipro.h' is added. Attributes in the layers of the UNIPRO stack can be read and written via the DME.

---
 drivers/scsi/ufs/unipro.h |  130 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/ufs/unipro.h
drivers/scsi/ufs/unipro.h new file +130 -0
new file mode 100644
index 0000000..3a710eb
@@ -0,0 +1,130 @@
+/*
+ * drivers/scsi/ufs/unipro.h
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UNIPRO_H_
+#define _UNIPRO_H_
+
+/*
+ * PHY Adpater attributes
+ */
+#define PA_ACTIVETXDATALANES	0x1560
+#define PA_ACTIVERXDATALANES	0x1580
+#define PA_TXTRAILINGCLOCKS	0x1564
+#define PA_PHY_TYPE		0x1500
+#define PA_AVAILTXDATALANES	0x1520
+#define PA_AVAILRXDATALANES	0x1540
+#define PA_MINRXTRAILINGCLOCKS	0x1543
+#define PA_TXPWRSTATUS		0x1567
+#define PA_RXPWRSTATUS		0x1582
+#define PA_TXFORCECLOCK		0x1562
+#define PA_TXPWRMODE		0x1563
+#define PA_LEGACYDPHYESCDL	0x1570
+#define PA_MAXTXSPEEDFAST	0x1521
+#define PA_MAXTXSPEEDSLOW	0x1522
+#define PA_MAXRXSPEEDFAST	0x1541
+#define PA_MAXRXSPEEDSLOW	0x1542
+#define PA_TXLINKSTARTUPHS	0x1544
+#define PA_TXSPEEDFAST		0x1565
+#define PA_TXSPEEDSLOW		0x1566
+#define PA_REMOTEVERINFO	0x15A0
+#define PA_TXGEAR		0x1568
+#define PA_TXTERMINATION	0x1569
+#define PA_HSSERIES		0x156A
+#define PA_PWRMODE		0x1571
+#define PA_RXGEAR		0x1583
+#define PA_RXTERMINATION	0x1584
+#define PA_MAXRXPWMGEAR		0x1586
+#define PA_MAXRXHSGEAR		0x1587
+#define PA_RXHSUNTERMCAP	0x15A5
+#define PA_RXLSTERMCAP		0x15A6
+#define PA_PACPREQTIMEOUT	0x1590
+#define PA_PACPREQEOBTIMEOUT	0x1591
+#define PA_HIBERN8TIME		0x15A7
+#define PA_LOCALVERINFO		0x15A9
+#define PA_TACTIVATE		0x15A8
+#define PA_PACPFRAMECOUNT	0x15C0
+#define PA_PACPERRORCOUNT	0x15C1
+#define PA_PHYTESTCONTROL	0x15C2
+#define PA_PWRMODEUSERDATA0	0x15B0
+#define PA_PWRMODEUSERDATA1	0x15B1
+#define PA_PWRMODEUSERDATA2	0x15B2
+#define PA_PWRMODEUSERDATA3	0x15B3
+#define PA_PWRMODEUSERDATA4	0x15B4
+#define PA_PWRMODEUSERDATA5	0x15B5
+#define PA_PWRMODEUSERDATA6	0x15B6
+#define PA_PWRMODEUSERDATA7	0x15B7
+#define PA_PWRMODEUSERDATA8	0x15B8
+#define PA_PWRMODEUSERDATA9	0x15B9
+#define PA_PWRMODEUSERDATA10	0x15BA
+#define PA_PWRMODEUSERDATA11	0x15BB
+#define PA_CONNECTEDTXDATALANES	0x1561
+#define PA_CONNECTEDRXDATALANES	0x1581
+#define PA_LOGICALLANEMAP	0x15A1
+#define PA_SLEEPNOCONFIGTIME	0x15A2
+#define PA_STALLNOCONFIGTIME	0x15A3
+#define PA_SAVECONFIGTIME	0x15A4
+
+/*
+ * Data Link Layer Attributes
+ */
+#define DL_TC0TXFCTHRESHOLD	0x2040
+#define DL_FC0PROTTIMEOUTVAL	0x2041
+#define DL_TC0REPLAYTIMEOUTVAL	0x2042
+#define DL_AFC0REQTIMEOUTVAL	0x2043
+#define DL_AFC0CREDITTHRESHOLD	0x2044
+#define DL_TC0OUTACKTHRESHOLD	0x2045
+#define DL_TC1TXFCTHRESHOLD	0x2060
+#define DL_FC1PROTTIMEOUTVAL	0x2061
+#define DL_TC1REPLAYTIMEOUTVAL	0x2062
+#define DL_AFC1REQTIMEOUTVAL	0x2063
+#define DL_AFC1CREDITTHRESHOLD	0x2064
+#define DL_TC1OUTACKTHRESHOLD	0x2065
+#define DL_TXPREEMPTIONCAP	0x2000
+#define DL_TC0TXMAXSDUSIZE	0x2001
+#define DL_TC0RXINITCREDITVAL	0x2002
+#define DL_TC0TXBUFFERSIZE	0x2005
+#define DL_PEERTC0PRESENT	0x2046
+#define DL_PEERTC0RXINITCREVAL	0x2047
+#define DL_TC1TXMAXSDUSIZE	0x2003
+#define DL_TC1RXINITCREDITVAL	0x2004
+#define DL_TC1TXBUFFERSIZE	0x2006
+#define DL_PEERTC1PRESENT	0x2066
+#define DL_PEERTC1RXINITCREVAL	0x2067
+
+/*
+ * Network Layer Attributes
+ */
+#define N_DEVICEID		0x3000
+#define N_DEVICEID_VALID	0x3001
+#define N_TC0TXMAXSDUSIZE	0x3020
+#define N_TC1TXMAXSDUSIZE	0x3021
+
+/*
+ * Transport Layer Attributes
+ */
+#define T_NUMCPORTS		0x4000
+#define T_NUMTESTFEATURES	0x4001
+#define T_CONNECTIONSTATE	0x4020
+#define T_PEERDEVICEID		0x4021
+#define T_PEERCPORTID		0x4022
+#define T_TRAFFICCLASS		0x4023
+#define T_PROTOCOLID		0x4024
+#define T_CPORTFLAGS		0x4025
+#define T_TXTOKENVALUE		0x4026
+#define T_RXTOKENVALUE		0x4027
+#define T_LOCALBUFFERSPACE	0x4028
+#define T_PEERBUFFERSPACE	0x4029
+#define T_CREDITSTOSEND		0x402A
+#define T_CPORTMODE		0x402B
+#define T_TC0TXMAXSDUSIZE	0x4060
+#define T_TC1TXMAXSDUSIZE	0x4061
+
+#endif /* _UNIPRO_H_ */
Signature · 1 line
1.7.0.4
Reviewed-by on v1 5/7
#64 lore raw

Re: [PATCH 5/7] scsi: ufs: add unipro attribute IDs

Looks good to me.

Quoted 146 lines · Seungwon Jeon

'drivers/scsi/ufs/unipro.h' is added. Attributes in the layers of the UNIPRO stack can be read and written via the DME.

---
  drivers/scsi/ufs/unipro.h |  130 +++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 130 insertions(+), 0 deletions(-)

create mode 100644 drivers/scsi/ufs/unipro.h

drivers/scsi/ufs/unipro.h new file +130 -0
new file mode 100644
index 0000000..3a710eb
@@ -0,0 +1,130 @@
+/*
+ * drivers/scsi/ufs/unipro.h
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UNIPRO_H_
+#define _UNIPRO_H_
+
+/*
+ * PHY Adpater attributes
+ */
+#define PA_ACTIVETXDATALANES	0x1560
+#define PA_ACTIVERXDATALANES	0x1580
+#define PA_TXTRAILINGCLOCKS	0x1564
+#define PA_PHY_TYPE		0x1500
+#define PA_AVAILTXDATALANES	0x1520
+#define PA_AVAILRXDATALANES	0x1540
+#define PA_MINRXTRAILINGCLOCKS	0x1543
+#define PA_TXPWRSTATUS		0x1567
+#define PA_RXPWRSTATUS		0x1582
+#define PA_TXFORCECLOCK		0x1562
+#define PA_TXPWRMODE		0x1563
+#define PA_LEGACYDPHYESCDL	0x1570
+#define PA_MAXTXSPEEDFAST	0x1521
+#define PA_MAXTXSPEEDSLOW	0x1522
+#define PA_MAXRXSPEEDFAST	0x1541
+#define PA_MAXRXSPEEDSLOW	0x1542
+#define PA_TXLINKSTARTUPHS	0x1544
+#define PA_TXSPEEDFAST		0x1565
+#define PA_TXSPEEDSLOW		0x1566
+#define PA_REMOTEVERINFO	0x15A0
+#define PA_TXGEAR		0x1568
+#define PA_TXTERMINATION	0x1569
+#define PA_HSSERIES		0x156A
+#define PA_PWRMODE		0x1571
+#define PA_RXGEAR		0x1583
+#define PA_RXTERMINATION	0x1584
+#define PA_MAXRXPWMGEAR		0x1586
+#define PA_MAXRXHSGEAR		0x1587
+#define PA_RXHSUNTERMCAP	0x15A5
+#define PA_RXLSTERMCAP		0x15A6
+#define PA_PACPREQTIMEOUT	0x1590
+#define PA_PACPREQEOBTIMEOUT	0x1591
+#define PA_HIBERN8TIME		0x15A7
+#define PA_LOCALVERINFO		0x15A9
+#define PA_TACTIVATE		0x15A8
+#define PA_PACPFRAMECOUNT	0x15C0
+#define PA_PACPERRORCOUNT	0x15C1
+#define PA_PHYTESTCONTROL	0x15C2
+#define PA_PWRMODEUSERDATA0	0x15B0
+#define PA_PWRMODEUSERDATA1	0x15B1
+#define PA_PWRMODEUSERDATA2	0x15B2
+#define PA_PWRMODEUSERDATA3	0x15B3
+#define PA_PWRMODEUSERDATA4	0x15B4
+#define PA_PWRMODEUSERDATA5	0x15B5
+#define PA_PWRMODEUSERDATA6	0x15B6
+#define PA_PWRMODEUSERDATA7	0x15B7
+#define PA_PWRMODEUSERDATA8	0x15B8
+#define PA_PWRMODEUSERDATA9	0x15B9
+#define PA_PWRMODEUSERDATA10	0x15BA
+#define PA_PWRMODEUSERDATA11	0x15BB
+#define PA_CONNECTEDTXDATALANES	0x1561
+#define PA_CONNECTEDRXDATALANES	0x1581
+#define PA_LOGICALLANEMAP	0x15A1
+#define PA_SLEEPNOCONFIGTIME	0x15A2
+#define PA_STALLNOCONFIGTIME	0x15A3
+#define PA_SAVECONFIGTIME	0x15A4
+
+/*
+ * Data Link Layer Attributes
+ */
+#define DL_TC0TXFCTHRESHOLD	0x2040
+#define DL_FC0PROTTIMEOUTVAL	0x2041
+#define DL_TC0REPLAYTIMEOUTVAL	0x2042
+#define DL_AFC0REQTIMEOUTVAL	0x2043
+#define DL_AFC0CREDITTHRESHOLD	0x2044
+#define DL_TC0OUTACKTHRESHOLD	0x2045
+#define DL_TC1TXFCTHRESHOLD	0x2060
+#define DL_FC1PROTTIMEOUTVAL	0x2061
+#define DL_TC1REPLAYTIMEOUTVAL	0x2062
+#define DL_AFC1REQTIMEOUTVAL	0x2063
+#define DL_AFC1CREDITTHRESHOLD	0x2064
+#define DL_TC1OUTACKTHRESHOLD	0x2065
+#define DL_TXPREEMPTIONCAP	0x2000
+#define DL_TC0TXMAXSDUSIZE	0x2001
+#define DL_TC0RXINITCREDITVAL	0x2002
+#define DL_TC0TXBUFFERSIZE	0x2005
+#define DL_PEERTC0PRESENT	0x2046
+#define DL_PEERTC0RXINITCREVAL	0x2047
+#define DL_TC1TXMAXSDUSIZE	0x2003
+#define DL_TC1RXINITCREDITVAL	0x2004
+#define DL_TC1TXBUFFERSIZE	0x2006
+#define DL_PEERTC1PRESENT	0x2066
+#define DL_PEERTC1RXINITCREVAL	0x2067
+
+/*
+ * Network Layer Attributes
+ */
+#define N_DEVICEID		0x3000
+#define N_DEVICEID_VALID	0x3001
+#define N_TC0TXMAXSDUSIZE	0x3020
+#define N_TC1TXMAXSDUSIZE	0x3021
+
+/*
+ * Transport Layer Attributes
+ */
+#define T_NUMCPORTS		0x4000
+#define T_NUMTESTFEATURES	0x4001
+#define T_CONNECTIONSTATE	0x4020
+#define T_PEERDEVICEID		0x4021
+#define T_PEERCPORTID		0x4022
+#define T_TRAFFICCLASS		0x4023
+#define T_PROTOCOLID		0x4024
+#define T_CPORTFLAGS		0x4025
+#define T_TXTOKENVALUE		0x4026
+#define T_RXTOKENVALUE		0x4027
+#define T_LOCALBUFFERSPACE	0x4028
+#define T_PEERBUFFERSPACE	0x4029
+#define T_CREDITSTOSEND		0x402A
+#define T_CPORTMODE		0x402B
+#define T_TC0TXMAXSDUSIZE	0x4060
+#define T_TC1TXMAXSDUSIZE	0x4061
+
+#endif /* _UNIPRO_H_ */
#65 lore raw

PATCH 6/7 scsi: ufs: add operation for the uic power mode change

In reply to Akinobu Mita

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
 drivers/scsi/ufs/ufshcd.c |   80 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/scsi/ufs/ufshcd.h |    3 ++
 drivers/scsi/ufs/ufshci.h |   12 +++++++
 3 files changed, 91 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +76 -4
index 8277c40..ffda72d 100644
@@ -36,9 +36,11 @@
 #include <linux/async.h>
 
 #include "ufshcd.h"
+#include "unipro.h"
 
 #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
 				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
 				 UFSHCD_ERROR_MASK)
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT	500
@@ -359,6 +361,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
  * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
  * @hba: per adapter instance
  * @uic_cmd: UIC command
@@ -907,6 +921,60 @@ out:
 EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
 /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "pwr mode change uic error %d\n", ret);
+		goto out;
+	}
+
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev, "pwr mode change completion timeout\n");
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1336,16 +1404,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 /**
  * ufshcd_uic_cmd_compl - handle completion of uic command
  * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
  */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
 {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
 		hba->active_uic_cmd->argument2 |=
 			ufshcd_get_uic_cmd_result(hba);
 		hba->active_uic_cmd->argument3 =
 			ufshcd_get_dme_attr_val(hba);
 		complete(&hba->active_uic_cmd->done);
 	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
 }
 
 /**
@@ -1447,8 +1519,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
 	if (hba->errors)
 		ufshcd_err_handler(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
 	if (intr_status & UTP_TASK_REQ_COMPL)
 		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +3 -0
index 50bcd29..246c0e1 100644
@@ -142,6 +142,7 @@ struct ufshcd_lrb {
  * @uic_cmd_mutex: mutex for uic command
  * @ufshcd_tm_wait_queue: wait queue for task management
  * @tm_condition: condition variable for task management
+ * @pwr_done: completion for powr mode change
  * @ufshcd_state: UFSHCD states
  * @intr_mask: Interrupt Mask Bits
  * @feh_workq: Work queue for fatal controller error handling
@@ -180,6 +181,8 @@ struct ufs_hba {
 	wait_queue_head_t ufshcd_tm_wait_queue;
 	unsigned long tm_condition;
 
+	struct completion *pwr_done;
+
 	u32 ufshcd_state;
 	u32 intr_mask;
 
drivers/scsi/ufs/ufshci.h +12 -0
index df4901e..d92fd9c 100644
@@ -124,6 +124,9 @@ enum {
 #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
 #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
 #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
 				DEVICE_FATAL_ERROR |\
 				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
 #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
 #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
 /* HCE - Host Controller Enable 34h */
 #define CONTROLLER_ENABLE	UFS_BIT(0)
 #define CONTROLLER_DISABLE	0x0
Signature · 1 line
1.7.0.4
#66 lore raw

Re: [PATCH 6/7] scsi: ufs: add operation for the uic power mode change

Change looks good except few minor comments.

Quoted 68 lines · Seungwon Jeon

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
  drivers/scsi/ufs/ufshcd.c |   80 ++++++++++++++++++++++++++++++++++++++++++--
  drivers/scsi/ufs/ufshcd.h |    3 ++
  drivers/scsi/ufs/ufshci.h |   12 +++++++
  3 files changed, 91 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +32 -0
index 8277c40..ffda72d 100644
@@ -36,9 +36,11 @@
  #include <linux/async.h>
 
  #include "ufshcd.h"
+#include "unipro.h"
 
  #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
  				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
  				 UFSHCD_ERROR_MASK)
  /* UIC command timeout, unit: ms */
  #define UIC_CMD_TIMEOUT	500
@@ -359,6 +361,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
   * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
   * @hba: per adapter instance
   * @uic_cmd: UIC command
@@ -907,6 +921,60 @@ out:
  EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
  /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;

drivers/scsi/ufs/ufshcd.c out: · line 951

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "pwr mode change uic error %d\n", ret);

we should also print the power "mode" which we were trying to set here.

drivers/scsi/ufs/ufshcd.c out: · line 957

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +6 -0
+		goto out;
+	}
+
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev, "pwr mode change completion timeout\n");

we should also print the power "mode" which we were trying to set here.

Quoted 62 lines
drivers/scsi/ufs/ufshcd.c +28 -4
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
   * ufshcd_make_hba_operational - Make UFS controller operational
   * @hba: per adapter instance
   *
@@ -1336,16 +1404,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  /**
   * ufshcd_uic_cmd_compl - handle completion of uic command
   * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
   */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
  {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
  		hba->active_uic_cmd->argument3 =
  			ufshcd_get_dme_attr_val(hba);
  		complete(&hba->active_uic_cmd->done);
  	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
  }
 
  /**
@@ -1447,8 +1519,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
  	if (hba->errors)
  		ufshcd_err_handler(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
  	if (intr_status & UTP_TASK_REQ_COMPL)
  		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +0 -0
index 50bcd29..246c0e1 100644

drivers/scsi/ufs/ufshcd.h struct ufshcd_lrb { · line 145

Quoted 4 lines
drivers/scsi/ufs/ufshcd.h +1 -0
@@ -142,6 +142,7 @@ struct ufshcd_lrb {
   * @uic_cmd_mutex: mutex for uic command
   * @ufshcd_tm_wait_queue: wait queue for task management
   * @tm_condition: condition variable for task management
+ * @pwr_done: completion for powr mode change

typo: s/powr/power

Quoted 42 lines
drivers/scsi/ufs/ufshcd.h +2 -0
   * @ufshcd_state: UFSHCD states
   * @intr_mask: Interrupt Mask Bits
   * @feh_workq: Work queue for fatal controller error handling
@@ -180,6 +181,8 @@ struct ufs_hba {
  	wait_queue_head_t ufshcd_tm_wait_queue;
  	unsigned long tm_condition;
 
+	struct completion *pwr_done;
+
  	u32 ufshcd_state;
  	u32 intr_mask;
 
drivers/scsi/ufs/ufshci.h +12 -0
index df4901e..d92fd9c 100644
@@ -124,6 +124,9 @@ enum {
  #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
  #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
  #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
  				DEVICE_FATAL_ERROR |\
  				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
  #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
  #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
  /* HCE - Host Controller Enable 34h */
  #define CONTROLLER_ENABLE	UFS_BIT(0)
  #define CONTROLLER_DISABLE	0x0
#67 lore raw

RE: [PATCH 6/7] scsi: ufs: add operation for the uic power mode change

Quoted 1 line · Subhash Jadavani

Change looks good except few minor comments.

Thank you for comments.

Quoted 83 lines · Seungwon Jeon
Quoted 68 lines

Setting PA_PWRMode using DME_SET triggers the power mode change. And then the result will be given by the HCS.UPMCRS. This operation should be done atomically.

---
  drivers/scsi/ufs/ufshcd.c |   80 ++++++++++++++++++++++++++++++++++++++++++--
  drivers/scsi/ufs/ufshcd.h |    3 ++
  drivers/scsi/ufs/ufshci.h |   12 +++++++
  3 files changed, 91 insertions(+), 4 deletions(-)
drivers/scsi/ufs/ufshcd.c +32 -0
index 8277c40..ffda72d 100644
@@ -36,9 +36,11 @@
  #include <linux/async.h>
 
  #include "ufshcd.h"
+#include "unipro.h"
 
  #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
  				 UTP_TASK_REQ_COMPL |\
+				 UIC_POWER_MODE |\
  				 UFSHCD_ERROR_MASK)
  /* UIC command timeout, unit: ms */
  #define UIC_CMD_TIMEOUT	500
@@ -359,6 +361,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
  }
 
  /**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
+/**
   * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
   * @hba: per adapter instance
   * @uic_cmd: UIC command
@@ -907,6 +921,60 @@ out:
  EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
 
  /**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *				using DME_SET primitives.
+ * @hba: per adapter instance
+ * @mode: powr mode value
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
+{
+	struct uic_command uic_cmd = {0};
+	struct completion pwr_done;
+	unsigned long flags;
+	u8 status;
+	int ret;
+
+	uic_cmd.command = UIC_CMD_DME_SET;
+	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+	uic_cmd.argument3 = mode;

drivers/scsi/ufs/ufshcd.c out: · line 951

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	init_completion(&pwr_done);
+
+	mutex_lock(&hba->uic_cmd_mutex);
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = &pwr_done;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+	if (ret) {
+		dev_err(hba->dev, "pwr mode change uic error %d\n", ret);

we should also print the power "mode" which we were trying to set here.

Quoted 8 lines

drivers/scsi/ufs/ufshcd.c out: · line 957

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +6 -0
+		goto out;
+	}
+
+	if (!wait_for_completion_timeout(hba->pwr_done,
+					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+		dev_err(hba->dev, "pwr mode change completion timeout\n");

we should also print the power "mode" which we were trying to set here.

Ok with above.

Quoted 56 lines
Quoted 56 lines
drivers/scsi/ufs/ufshcd.c +28 -4
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = ufshcd_get_upmcrs(hba);
+	if (status != PWR_LOCAL) {
+		dev_err(hba->dev,
+			"pwr mode change failed, host umpcrs:0x%x\n",
+			status);
+		ret = (status != PWR_OK) ? status : -1;
+	}
+out:
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->pwr_done = NULL;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	mutex_unlock(&hba->uic_cmd_mutex);
+	return ret;
+}
+
+/**
   * ufshcd_make_hba_operational - Make UFS controller operational
   * @hba: per adapter instance
   *
@@ -1336,16 +1404,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
  /**
   * ufshcd_uic_cmd_compl - handle completion of uic command
   * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
   */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
  {
-	if (hba->active_uic_cmd) {
+	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
  		hba->active_uic_cmd->argument2 |=
  			ufshcd_get_uic_cmd_result(hba);
  		hba->active_uic_cmd->argument3 =
  			ufshcd_get_dme_attr_val(hba);
  		complete(&hba->active_uic_cmd->done);
  	}
+
+	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+		complete(hba->pwr_done);
  }
 
  /**
@@ -1447,8 +1519,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
  	if (hba->errors)
  		ufshcd_err_handler(hba);
 
-	if (intr_status & UIC_COMMAND_COMPL)
-		ufshcd_uic_cmd_compl(hba);
+	if (intr_status & UFSHCD_UIC_MASK)
+		ufshcd_uic_cmd_compl(hba, intr_status);
 
Quoted 13 lines

drivers/scsi/ufs/ufshcd.h struct ufshcd_lrb { · line 145

Quoted 11 lines
drivers/scsi/ufs/ufshcd.c +0 -0
  	if (intr_status & UTP_TASK_REQ_COMPL)
  		ufshcd_tmc_handler(hba);
drivers/scsi/ufs/ufshcd.h +1 -0
index 50bcd29..246c0e1 100644
@@ -142,6 +142,7 @@ struct ufshcd_lrb {
   * @uic_cmd_mutex: mutex for uic command
   * @ufshcd_tm_wait_queue: wait queue for task management
   * @tm_condition: condition variable for task management
+ * @pwr_done: completion for powr mode change

typo: s/powr/power

Ok.

Thanks,
Seungwon Jeon

Quoted 48 lines
Quoted 33 lines
drivers/scsi/ufs/ufshcd.h +2 -0
   * @ufshcd_state: UFSHCD states
   * @intr_mask: Interrupt Mask Bits
   * @feh_workq: Work queue for fatal controller error handling
@@ -180,6 +181,8 @@ struct ufs_hba {
  	wait_queue_head_t ufshcd_tm_wait_queue;
  	unsigned long tm_condition;
 
+	struct completion *pwr_done;
+
  	u32 ufshcd_state;
  	u32 intr_mask;
 
drivers/scsi/ufs/ufshci.h +5 -0
index df4901e..d92fd9c 100644
@@ -124,6 +124,9 @@ enum {
  #define CONTROLLER_FATAL_ERROR			UFS_BIT(16)
  #define SYSTEM_BUS_FATAL_ERROR			UFS_BIT(17)
 
+#define UFSHCD_UIC_MASK		(UIC_COMMAND_COMPL |\
+				 UIC_POWER_MODE)
+
  #define UFSHCD_ERROR_MASK	(UIC_ERROR |\
  				DEVICE_FATAL_ERROR |\
  				CONTROLLER_FATAL_ERROR |\
@@ -142,6 +145,15 @@ enum {
  #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
  #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
 
+enum {
+	PWR_OK		= 0x0,

drivers/scsi/ufs/ufshci.h enum { · line 159

Quoted 10 lines
drivers/scsi/ufs/ufshci.h +7 -0
+	PWR_LOCAL	= 0x01,
+	PWR_REMOTE	= 0x02,
+	PWR_BUSY	= 0x03,
+	PWR_ERROR_CAP	= 0x04,
+	PWR_FATAL_ERROR	= 0x05,
+};
+
  /* HCE - Host Controller Enable 34h */
  #define CONTROLLER_ENABLE	UFS_BIT(0)
  #define CONTROLLER_DISABLE	0x0

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#68 lore raw

PATCH 7/7 scsi: ufs: configure the attribute for power mode

In reply to Akinobu Mita

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
 drivers/scsi/ufs/ufshcd.c |   74 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/scsi/ufs/unipro.h |   21 +++++++++++++
 2 files changed, 93 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +72 -2
index ffda72d..ebdb9ff 100644
@@ -975,6 +975,70 @@ out:
 }
 
 /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int i, ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);
+
+	return ret;
+}
+
+/**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
  *
@@ -1754,8 +1818,14 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
 	int ret;
 
 	ret = ufshcd_link_startup(hba);
-	if (!ret)
-		scsi_scan_host(hba->host);
+	if (ret)
+		goto out;
+
+	ufshcd_config_max_pwr_mode(hba);
+
+	scsi_scan_host(hba->host);
+out:
+	return;
 }
 
 static struct scsi_host_template ufshcd_driver_template = {
drivers/scsi/ufs/unipro.h +21 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
 #define PA_STALLNOCONFIGTIME	0x15A3
 #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
 /*
  * Data Link Layer Attributes
  */
@@ -127,4 +142,10 @@
 #define T_TC0TXMAXSDUSIZE	0x4060
 #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
 #endif /* _UNIPRO_H_ */
Signature · 1 line
1.7.0.4
Reviewed-by on v1 7/7
#69 lore raw

Re: [PATCH 7/7] scsi: ufs: configure the attribute for power mode

Change looks good (except one minor question).

Quoted 60 lines · Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
  drivers/scsi/ufs/ufshcd.c |   74 +++++++++++++++++++++++++++++++++++++++++++-
  drivers/scsi/ufs/unipro.h |   21 +++++++++++++
  2 files changed, 93 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +41 -0
index ffda72d..ebdb9ff 100644
@@ -975,6 +975,70 @@ out:
  }
 
  /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int i, ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES

drivers/scsi/ufs/ufshcd.c out: · line 1028

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);

Does this mean TX-RX Termination can be enabled only in HS gear mode? Somehow i couldn't find this

Quoted 70 lines
drivers/scsi/ufs/ufshcd.c +21 -2
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);
+
+	return ret;
+}
+
+/**
   * ufshcd_make_hba_operational - Make UFS controller operational
   * @hba: per adapter instance
   *
@@ -1754,8 +1818,14 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
  	int ret;
 
  	ret = ufshcd_link_startup(hba);
-	if (!ret)
-		scsi_scan_host(hba->host);
+	if (ret)
+		goto out;
+
+	ufshcd_config_max_pwr_mode(hba);
+
+	scsi_scan_host(hba->host);
+out:
+	return;
  }
 
  static struct scsi_host_template ufshcd_driver_template = {
drivers/scsi/ufs/unipro.h +21 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
  #define PA_STALLNOCONFIGTIME	0x15A3
  #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
  /*
   * Data Link Layer Attributes
   */
@@ -127,4 +142,10 @@
  #define T_TC0TXMAXSDUSIZE	0x4060
  #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
  #endif /* _UNIPRO_H_ */
#70 lore raw

RE: [PATCH 7/7] scsi: ufs: configure the attribute for power mode

Quoted 70 lines · Subhash Jadavani

Change looks good (except one minor question).

Quoted 65 lines · Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
  drivers/scsi/ufs/ufshcd.c |   74 +++++++++++++++++++++++++++++++++++++++++++-
  drivers/scsi/ufs/unipro.h |   21 +++++++++++++
  2 files changed, 93 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +46 -0
index ffda72d..ebdb9ff 100644
@@ -975,6 +975,70 @@ out:
  }
 
  /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int i, ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c out: · line 1028

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +5 -0
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);

Does this mean TX-RX Termination can be enabled only in HS gear mode? Somehow i couldn't find this

Basically, 'termination' in HS Mode and not termination in LS mode. Spec. doesn't say clearly, though.
If you have any idea, please let me know.

Thanks,
Seungwon Jeon

Quoted 76 lines
Quoted 61 lines
drivers/scsi/ufs/ufshcd.c +21 -2
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);
+
+	return ret;
+}
+
+/**
   * ufshcd_make_hba_operational - Make UFS controller operational
   * @hba: per adapter instance
   *
@@ -1754,8 +1818,14 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
  	int ret;
 
  	ret = ufshcd_link_startup(hba);
-	if (!ret)
-		scsi_scan_host(hba->host);
+	if (ret)
+		goto out;
+
+	ufshcd_config_max_pwr_mode(hba);
+
+	scsi_scan_host(hba->host);
+out:
+	return;
  }
 
  static struct scsi_host_template ufshcd_driver_template = {
drivers/scsi/ufs/unipro.h +15 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
  #define PA_STALLNOCONFIGTIME	0x15A3
  #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
  /*
   * Data Link Layer Attributes
   */

drivers/scsi/ufs/unipro.h line 151

Quoted 10 lines
drivers/scsi/ufs/unipro.h +6 -0
@@ -127,4 +142,10 @@
  #define T_TC0TXMAXSDUSIZE	0x4060
  #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
  #endif /* _UNIPRO_H_ */

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Reviewed-by
#71 lore raw

Re: [PATCH 7/7] scsi: ufs: configure the attribute for power mode

Quoted 78 lines · Seungwon Jeon
Quoted 69 lines · Subhash Jadavani

Change looks good (except one minor question).

Quoted 64 lines · Seungwon Jeon

UIC attributes can be set with using DME_SET command for power mode change. For configuration the link capability attributes are used, which is updated after successful link startup.

---
   drivers/scsi/ufs/ufshcd.c |   74 +++++++++++++++++++++++++++++++++++++++++++-
   drivers/scsi/ufs/unipro.h |   21 +++++++++++++
   2 files changed, 93 insertions(+), 2 deletions(-)
drivers/scsi/ufs/ufshcd.c +45 -0
index ffda72d..ebdb9ff 100644
@@ -975,6 +975,70 @@ out:
   }
 
   /**
+ * ufshcd_config_max_pwr_mode - Set & Change power mode with
+ *	maximum capability attribute information.
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
+{
+	enum {RX = 0, TX = 1};
+	u32 lanes[] = {1, 1};
+	u32 gear[] = {1, 1};
+	u8 pwr[] = {FASTAUTO_MODE, FASTAUTO_MODE};
+	int i, ret;
+
+	/* Get the connected lane count */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+
+	/*
+	 * First, get the maximum gears of HS speed.
+	 * If a zero value, it means there is no HSGEAR capability.
+	 * Then, get the maximum gears of PWM speed.
+	 */
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
+	if (!gear[RX]) {
+		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+		pwr[RX] = SLOWAUTO_MODE;
+	}
+
+	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[TX]);
+	if (!gear[TX]) {
+		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
+				    &gear[TX]);
+		pwr[TX] = SLOWAUTO_MODE;
+	}
+
+	/*
+	 * Configure attributes for power mode change with below.
+	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
+	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
+	 * - PA_HSSERIES
+	 */
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), gear[RX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), lanes[RX]);
+	if (pwr[RX] == FASTAUTO_MODE)
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c out: · line 1028

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +6 -0
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
+
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), lanes[TX]);
+	if (pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);

Does this mean TX-RX Termination can be enabled only in HS gear mode? Somehow i couldn't find this

Quoted 3 lines

Basically, 'termination' in HS Mode and not termination in LS mode. Spec. doesn't say clearly, though.
If you have any idea, please let me know.

Thanks for the info. I don't have much idea as of now. BTW, this patch looks good to me.

Quoted 77 lines

Thanks,
Seungwon Jeon

Quoted 74 lines
Quoted 60 lines
drivers/scsi/ufs/ufshcd.c +21 -2
+
+	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
+	if (ret)
+		dev_err(hba->dev,
+			"pwr_mode: power mode change failed %d\n", ret);
+
+	return ret;
+}
+
+/**
    * ufshcd_make_hba_operational - Make UFS controller operational
    * @hba: per adapter instance
    *
@@ -1754,8 +1818,14 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
   	int ret;
 
   	ret = ufshcd_link_startup(hba);
-	if (!ret)
-		scsi_scan_host(hba->host);
+	if (ret)
+		goto out;
+
+	ufshcd_config_max_pwr_mode(hba);
+
+	scsi_scan_host(hba->host);
+out:
+	return;
   }
 
   static struct scsi_host_template ufshcd_driver_template = {
drivers/scsi/ufs/unipro.h +15 -0
index 3a710eb..0bb8041 100644
@@ -72,6 +72,21 @@
   #define PA_STALLNOCONFIGTIME	0x15A3
   #define PA_SAVECONFIGTIME	0x15A4
 
+/* PA power modes */
+enum {
+	FAST_MODE	= 1,
+	SLOW_MODE	= 2,
+	FASTAUTO_MODE	= 4,
+	SLOWAUTO_MODE	= 5,
+	UNCHANGED	= 7,
+};
+
+/* PA TX/RX Frequency Series */
+enum {
+	PA_HS_MODE_A	= 1,
+	PA_HS_MODE_B	= 2,
+};
+
   /*
    * Data Link Layer Attributes
    */

drivers/scsi/ufs/unipro.h line 151

Quoted 10 lines
drivers/scsi/ufs/unipro.h +6 -0
@@ -127,4 +142,10 @@
   #define T_TC0TXMAXSDUSIZE	0x4060
   #define T_TC1TXMAXSDUSIZE	0x4061
 
+/* Boolean attribute values */
+enum {
+	FALSE = 0,
+	TRUE,
+};
+
   #endif /* _UNIPRO_H_ */

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html