From: Sujit Reddy Thumma <sthumma@codeaurora.org>
To: Seungwon Jeon <tgih.jun@samsung.com>
Cc: 'Vinayak Holikatti' <vinholikatti@gmail.com>,
'Santosh Y' <santoshsy@gmail.com>,
"'James E.J. Bottomley'" <JBottomley@parallels.com>,
linux-scsi@vger.kernel.org, linux-arm-msm@vger.kernel.org,
'Dolev Raviv' <draviv@codeaurora.org>
Subject: Re: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
Date: Thu, 18 Jul 2013 09:18:38 +0530 [thread overview]
Message-ID: <51E76596.5090509@codeaurora.org> (raw)
In-Reply-To: <001a01ce82c5$8bd47d50$a37d77f0$%jun@samsung.com>
>>>> + * ufshcd_wait_for_register - wait for register value to change
>>>> + * @hba - per-adapter interface
>>>> + * @reg - mmio register offset
>>>> + * @mask - mask to apply to read register value
>>>> + * @val - wait condition
>>>> + * @interval_us - polling interval in microsecs
>>>> + * @timeout_ms - timeout in millisecs
>>>> + *
>>>> + * Returns final register value after iteration
>>>> + */
>>>> +static u32 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
>>>> + u32 val, unsigned long interval_us, unsigned long timeout_ms)
>>> I feel like this function's role is to wait for clearing register (specially, doorbells).
>>> If you really don't intend to apply other all register, I think it would better to change the
>> function name.
>>> ex> ufshcd_wait_for_clear_doorbell or ufshcd_wait_for_clear_reg?
>>
>> Although, this API is introduced in this patch and used only for
>> clearing the doorbell, I have intentionally made it generic to avoid
>> duplication of wait condition code in future (not just clearing but
>> also for setting a bit). For example, when we write to HCE.ENABLE we
>> wait for it turn to 1.
>>
>>
>>> And if you like it, it could be more simple like below
>>>
>>> static bool ufshcd_wait_for_clear_reg(struct ufs_hba *hba, u32 reg, u32 mask,
>>> unsigned long interval_us,
>>> unsigned int timeout_ms)
>>> {
>>> unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
>>
>> Jiffies on 100Hz systems have minimum granularity of 10ms. So we really
>> wait for more 10ms even though the timeout_ms < 10ms.
> Yes. Real timeout depends on system.
> But normally actual wait will be maintained until 'ufshcd_readl' is done.
> Timeout is valid for failure case. If we don't need to apply a strict timeout value, it's not bad.
Hmm.. make sense. Will take care in the next patchset.
>
>>
>>> /* wakeup within 50us of expiry */
>>> const unsigned int expiry = 50;
>>>
>>> while (ufshcd_readl(hba, reg) & mask) {
>>> usleep_range(interval_us, interval_us + expiry);
>>> if (time_after(jiffies, timeout)) {
>>> if (ufshcd_readl(hba, reg) & mask)
>>> return false;
>>
>> I really want the caller to decide on what to do after the timeout.
>> This helps in error handling cases where we try to clear off the entire
>> door-bell register but only a few of the bits are cleared after timeout.
> I don't know what we can do with the report of the partial success on clearing bit.
> It's just failure case. Isn't enough with false/true?
The point is, if a bit can't be cleared it can be regarded as a
permanent failure (only for that request), otherwise, it can be
retried with the same tag value.
>
>>
>>> else
>>> return true;
>>> }
>>> }
>>>
>>> return true;
>>> }
>>>> +{
>>>> + u32 tmp;
>>>> + ktime_t start;
>>>> + unsigned long diff;
>>>> +
>>>> + tmp = ufshcd_readl(hba, reg);
>>>> +
>>>> + if ((val & mask) != val) {
>>>> + dev_err(hba->dev, "%s: Invalid wait condition 0x%x\n",
>>>> + __func__, val);
>>>> + goto out;
>>>> + }
>>>> +
>>>> + start = ktime_get();
>>>> + while ((tmp & mask) != val) {
>>>> + /* wakeup within 50us of expiry */
>>>> + usleep_range(interval_us, interval_us + 50);
>>>> + tmp = ufshcd_readl(hba, reg);
>>>> + diff = ktime_to_ms(ktime_sub(ktime_get(), start));
>>>> + if (diff > timeout_ms) {
>>>> + tmp = ufshcd_readl(hba, reg);
>>>> + break;
>>>> + }
>>>> + }
>>>> +out:
>>>> + return tmp;
>>>> +}
>>>> +
..
>>>> +static int
>>>> +ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
>>>> +{
>>>> + int err = 0;
>>>> + unsigned long flags;
>>>> + u32 reg;
>>>> + u32 mask = 1 << tag;
>>>> +
>>>> + /* clear outstanding transaction before retry */
>>>> + spin_lock_irqsave(hba->host->host_lock, flags);
>>>> + ufshcd_utrl_clear(hba, tag);
>>>> + spin_unlock_irqrestore(hba->host->host_lock, flags);
>>>> +
>>>> + /*
>>>> + * wait for for h/w to clear corresponding bit in door-bell.
>>>> + * max. wait is 1 sec.
>>>> + */
>>>> + reg = ufshcd_wait_for_register(hba,
>>>> + REG_UTP_TRANSFER_REQ_DOOR_BELL,
>>>> + mask, 0, 1000, 1000);
>>> 4th argument should be (~mask) instead of '0', right?
>>
>> True, but not really for this implementation. It breaks the check in
>> in wait_for_register -
>> if ((val & mask) != val)
>> dev_err(...);
> Hmm, it seems complicated to use.
> Ok. Is right the following about val as 4th argument?
> - clear: val should be '0' regardless corresponding bit.
> - set: val should be same with mask.
> If the related comment is added, it will be helpful.
Thinking again it looks like it is complicated. How about changing
the check to -
val = val & mask; /* ignore the bits we don't intend to wait on */
while (ufshcd_readl() & mask != val) {
sleep
}
Usage will be ~mask for clearing the bits, mask for setting the bits
in the fourth argument.
>
>>
>>> Actually, mask value involves the corresponding bit to be cleared.
>>> So, 4th argument may be unnecessary.
>>
>> As I described above, the wait_for_register can also be used to
>> check if the value is set or not. In which case, we need 4th argument.
>>
>>>
>>>> + if ((reg & mask) == mask)
>>>> + err = -ETIMEDOUT;
>>> Also, checking the result can be involved in ufshcd_wait_for_register.
> Any comment?
Sorry I missed this. But the point was the same. To make
wait_for_register() just to wait a definite time and not return any
error condition when the bits don't turn as expected.
>
>>>
>>>> +
>>>> + return err;
>>>> +}
>>>> +
>>>> +/**
>>>> + * ufshcd_dev_cmd_completion() - handles device management command responses
>>>> + * @hba: per adapter instance
>>>> + * @lrbp: pointer to local reference block
>>>> + */
>>>> +static int
>>>> +ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
>>>> +{
>>>> + int resp;
>>>> + int err = 0;
>>>> +
>>>> + resp = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
>>>> +
>>>> + switch (resp) {
>>>> + case UPIU_TRANSACTION_NOP_IN:
>>>> + if (hba->dev_cmd.type != DEV_CMD_TYPE_NOP) {
>>>> + err = -EINVAL;
>>>> + dev_err(hba->dev, "%s: unexpected response %x\n",
>>>> + __func__, resp);
>>>> + }
>>>> + break;
>>>> + case UPIU_TRANSACTION_REJECT_UPIU:
>>>> + /* TODO: handle Reject UPIU Response */
>>>> + err = -EPERM;
>>>> + dev_err(hba->dev, "%s: Reject UPIU not fully implemented\n",
>>>> + __func__);
>>>> + break;
>>>> + default:
>>>> + err = -EINVAL;
>>>> + dev_err(hba->dev, "%s: Invalid device management cmd response: %x\n",
>>>> + __func__, resp);
>>>> + break;
>>>> + }
>>>> +
>>>> + return err;
>>>> +}
>>>> +
>>>> +/**
>>>> + * ufshcd_get_dev_cmd_tag - Get device management command tag
>>>> + * @hba: per-adapter instance
>>>> + * @tag: pointer to variable with available slot value
>>>> + *
>>>> + * Get a free slot and lock it until device management command
>>>> + * completes.
>>>> + *
>>>> + * Returns false if free slot is unavailable for locking, else
>>>> + * return true with tag value in @tag.
>>>> + */
>>>> +static bool ufshcd_get_dev_cmd_tag(struct ufs_hba *hba, int *tag_out)
>>>> +{
>>>> + int tag;
>>>> + bool ret = false;
>>>> + unsigned long tmp;
>>>> +
>>>> + if (!tag_out)
>>>> + goto out;
>>>> +
>>>> + do {
>>>> + tmp = ~hba->lrb_in_use;
>>>> + tag = find_last_bit(&tmp, hba->nutrs);
>>>> + if (tag >= hba->nutrs)
>>>> + goto out;
>>>> + } while (test_and_set_bit_lock(tag, &hba->lrb_in_use));
>>>> +
>>>> + *tag_out = tag;
>>>> + ret = true;
>>>> +out:
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static inline void ufshcd_put_dev_cmd_tag(struct ufs_hba *hba, int tag)
>>>> +{
>>>> + clear_bit_unlock(tag, &hba->lrb_in_use);
>>>> +}
>>>> +
>>>> +/**
>>>> + * ufshcd_exec_dev_cmd - API for sending device management requests
>>>> + * @hba - UFS hba
>>>> + * @cmd_type - specifies the type (NOP, Query...)
>>>> + * @timeout - time in seconds
>>>> + *
>>>> + * NOTE: There is only one available tag for device management commands. Thus
>>>> + * synchronisation is the responsibilty of the user.
>>>> + */
>>>> +static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
>>>> + enum dev_cmd_type cmd_type, int timeout)
>>>> +{
>>>> + struct ufshcd_lrb *lrbp;
>>>> + int err;
>>>> + int tag;
>>>> + struct completion wait;
>>>> + unsigned long flags;
>>>> +
>>>> + /*
>>>> + * Get free slot, sleep if slots are unavailable.
>>>> + * Even though we use wait_event() which sleeps indefinitely,
>>>> + * the maximum wait time is bounded by SCSI request timeout.
>>>> + */
>>>> + wait_event(hba->dev_cmd.tag_wq, ufshcd_get_dev_cmd_tag(hba, &tag));
>>>> +
>>>> + init_completion(&wait);
>>>> + lrbp = &hba->lrb[tag];
>>>> + WARN_ON(lrbp->cmd);
>>>> + err = ufshcd_compose_dev_cmd(hba, lrbp, cmd_type, tag);
>>>> + if (unlikely(err))
>>>> + goto out_put_tag;
>>>> +
>>>> + hba->dev_cmd.complete = &wait;
>>>> +
>>>> + spin_lock_irqsave(hba->host->host_lock, flags);
>>>> + ufshcd_send_command(hba, tag);
>>>> + spin_unlock_irqrestore(hba->host->host_lock, flags);
>>>> +
>>>> + err = ufshcd_wait_for_dev_cmd(hba, lrbp, timeout);
>>>> +
>>> <snip>
>>>> + if (err == -ETIMEDOUT) {
>>>> + if (!ufshcd_clear_cmd(hba, tag))
>>>> + err = -EAGAIN;
>>>> + } else if (!err) {
>>>> + spin_lock_irqsave(hba->host->host_lock, flags);
>>>> + err = ufshcd_dev_cmd_completion(hba, lrbp);
>>>> + spin_unlock_irqrestore(hba->host->host_lock, flags);
>>>> + }
>>> </snip>
>>> I think sniped part can be involved in ufshcd_wait_for_dev_cmd.
>>> How do you think about that?
>>
>> Yes, it can be moved.
>>
>>>
>>>> +
>>>> +out_put_tag:
>>>> + ufshcd_put_dev_cmd_tag(hba, tag);
>>>> + wake_up(&hba->dev_cmd.tag_wq);
>>>> + return err;
>>>> +}
>>>> +
>>>> /**
>>>> * ufshcd_memory_alloc - allocate memory for host memory space data structures
>>>> * @hba: per adapter instance
>>>> @@ -774,8 +1075,8 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
>>>> cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
>>>>
>>>> hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
>>>> - hba->lrb[i].ucd_cmd_ptr =
>>>> - (struct utp_upiu_cmd *)(cmd_descp + i);
>>>> + hba->lrb[i].ucd_req_ptr =
>>>> + (struct utp_upiu_req *)(cmd_descp + i);
>>>> hba->lrb[i].ucd_rsp_ptr =
>>>> (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
>>>> hba->lrb[i].ucd_prdt_ptr =
>>>> @@ -961,6 +1262,38 @@ out:
>>>> }
>>>>
>>>> /**
>>>> + * ufshcd_validate_dev_connection() - Check device connection status
>>>> + * @hba: per-adapter instance
>>>> + *
>>>> + * Send NOP OUT UPIU and wait for NOP IN response to check whether the
>>>> + * device Transport Protocol (UTP) layer is ready after a reset.
>>>> + * If the UTP layer at the device side is not initialized, it may
>>>> + * not respond with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT
>>>> + * and we retry sending NOP OUT for %NOP_OUT_RETRIES iterations.
>>>> + */
>>>> +static int ufshcd_validate_dev_connection(struct ufs_hba *hba)
>>> I think ufshcd_verify_dev_init is close to standard description.
>>>
>>
>> Okay.
>>
>>>> +{
>>>> + int err = 0;
>>>> + int retries;
>>>> +
>>>> + mutex_lock(&hba->dev_cmd.lock);
>>>> + for (retries = NOP_OUT_RETRIES; retries > 0; retries--) {
>>>> + err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
>>>> + NOP_OUT_TIMEOUT);
>>>> +
>>>> + if (!err || err == -ETIMEDOUT)
>>>> + break;
>>>> +
>>>> + dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err);
>>>> + }
>>>> + mutex_unlock(&hba->dev_cmd.lock);
>>>> +
>>>> + if (err)
>>>> + dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err);
>>>> + return err;
>>>> +}
>>>> +
>>>> +/**
>>>> * ufshcd_do_reset - reset the host controller
>>>> * @hba: per adapter instance
>>>> *
>>>> @@ -986,13 +1319,20 @@ static int ufshcd_do_reset(struct ufs_hba *hba)
>>>> for (tag = 0; tag < hba->nutrs; tag++) {
>>>> if (test_bit(tag, &hba->outstanding_reqs)) {
>>>> lrbp = &hba->lrb[tag];
>>>> - scsi_dma_unmap(lrbp->cmd);
>>>> - lrbp->cmd->result = DID_RESET << 16;
>>>> - lrbp->cmd->scsi_done(lrbp->cmd);
>>>> - lrbp->cmd = NULL;
>>>> + if (lrbp->cmd) {
>>>> + scsi_dma_unmap(lrbp->cmd);
>>>> + lrbp->cmd->result = DID_RESET << 16;
>>>> + lrbp->cmd->scsi_done(lrbp->cmd);
>>>> + lrbp->cmd = NULL;
>>>> + clear_bit_unlock(tag, &hba->lrb_in_use);
>>>> + }
>>> I know above.
>>> But there is no relation to this patch.
>>> Can be it moved to scsi: ufs: Fix device and host reset methods?
>>
>> Yes, but it is basically to avoid NULL pointer derefernce in case
>> someone picks this patch but not the other one.
>>
>>>
>>>> }
>>>> }
>>>>
>>>> + /* complete device management command */
>>>> + if (hba->dev_cmd.complete)
>>>> + complete(hba->dev_cmd.complete);
>>>> +
>>>> /* clear outstanding request/task bit maps */
>>>> hba->outstanding_reqs = 0;
>>>> hba->outstanding_tasks = 0;
>>>> @@ -1199,27 +1539,37 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
>>>>
>>>> switch (ocs) {
>>>> case OCS_SUCCESS:
>>>> -
>>>> /* check if the returned transfer response is valid */
>>> As replaced with new function, comment isn't valid.
>>> Remove or "get the TR response transaction type" seems proper.
>>
>> Okay.
>>
>>>
>>>> - result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
>>>> - if (result) {
>>>> + result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
>>>> +
>>>> + switch (result) {
>>>> + case UPIU_TRANSACTION_RESPONSE:
>>>> + /*
>>>> + * get the response UPIU result to extract
>>>> + * the SCSI command status
>>>> + */
>>>> + result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
>>>> +
>>>> + /*
>>>> + * get the result based on SCSI status response
>>>> + * to notify the SCSI midlayer of the command status
>>>> + */
>>>> + scsi_status = result & MASK_SCSI_STATUS;
>>>> + result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
>>>> + break;
>>>> + case UPIU_TRANSACTION_REJECT_UPIU:
>>>> + /* TODO: handle Reject UPIU Response */
>>>> + result = DID_ERROR << 16;
>>>> + dev_err(hba->dev,
>>>> + "Reject UPIU not fully implemented\n");
>>>> + break;
>>>> + default:
>>>> + result = DID_ERROR << 16;
>>>> dev_err(hba->dev,
>>>> - "Invalid response = %x\n", result);
>>>> + "Unexpected request response code = %x\n",
>>>> + result);
>>>> break;
>>>> }
>>>> -
>>>> - /*
>>>> - * get the response UPIU result to extract
>>>> - * the SCSI command status
>>>> - */
>>>> - result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
>>>> -
>>>> - /*
>>>> - * get the result based on SCSI status response
>>>> - * to notify the SCSI midlayer of the command status
>>>> - */
>>>> - scsi_status = result & MASK_SCSI_STATUS;
>>>> - result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
>>>> break;
>>>> case OCS_ABORTED:
>>>> result |= DID_ABORT << 16;
>>>> @@ -1259,28 +1609,37 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
>>>> */
>>>> static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
>>>> {
>>>> - struct ufshcd_lrb *lrb;
>>>> + struct ufshcd_lrb *lrbp;
>>>> + struct scsi_cmnd *cmd;
>>>> unsigned long completed_reqs;
>>>> u32 tr_doorbell;
>>>> int result;
>>>> int index;
>>>> + bool int_aggr_reset = false;
>>>>
>>>> - lrb = hba->lrb;
>>>> tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
>>>> completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
>>>>
>>>> for (index = 0; index < hba->nutrs; index++) {
>>>> if (test_bit(index, &completed_reqs)) {
>>>> -
>>>> - result = ufshcd_transfer_rsp_status(hba, &lrb[index]);
>>>> -
>>>> - if (lrb[index].cmd) {
>>>> - scsi_dma_unmap(lrb[index].cmd);
>>>> - lrb[index].cmd->result = result;
>>>> - lrb[index].cmd->scsi_done(lrb[index].cmd);
>>>> -
>>>> + lrbp = &hba->lrb[index];
>>>> + cmd = lrbp->cmd;
>>>> + /* Don't reset counters for interrupt cmd */
>>>> + int_aggr_reset |= !lrbp->intr_cmd;
>>>> +
>>>> + if (cmd) {
>>>> + result = ufshcd_transfer_rsp_status(hba, lrbp);
>>>> + scsi_dma_unmap(cmd);
>>>> + cmd->result = result;
>>>> /* Mark completed command as NULL in LRB */
>>>> - lrb[index].cmd = NULL;
>>>> + lrbp->cmd = NULL;
>>>> + clear_bit_unlock(index, &hba->lrb_in_use);
>>>> + /* Do not touch lrbp after scsi done */
>>>> + cmd->scsi_done(cmd);
>>>> + } else if (lrbp->command_type ==
>>>> + UTP_CMD_TYPE_DEV_MANAGE) {
>>>> + if (hba->dev_cmd.complete)
>>>> + complete(hba->dev_cmd.complete);
>>>> }
>>>> } /* end of if */
>>>> } /* end of for */
>>>> @@ -1288,8 +1647,12 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
>>>> /* clear corresponding bits of completed commands */
>>>> hba->outstanding_reqs ^= completed_reqs;
>>>>
>>>> + /* we might have free'd some tags above */
>>>> + wake_up(&hba->dev_cmd.tag_wq);
>>>> +
>>>> /* Reset interrupt aggregation counters */
>>>> - ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
>>>> + if (int_aggr_reset)
>>>> + ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
>>> Do you assume that interrupt command(device management command) is completed alone?
>>> Of course, interrupt command is not counted in aggregation unlike regular command.
>>> We need to consider that interrupt command comes along with regular command?
>>> If right, ufshcd_config_int_aggr should not be skipped.
>>
>> True. We are not skipping it. int_aggr_reset will be false only when
>> there is single interrupt command completed.
>>
>> Check the above part which reads -
>> for_all_completed_commands() {
>> int_aggr_reset |= !lrbp->intr_cmd;
>> }
>>
>> Even if one of the command in the iteration is not interrupt command
>> (lrbp->intr_cmd is false) then int_aggr_reset is always true.
> Clear!
> But I confused it with your comment line. (/* Don't reset counters for interrupt cmd */)
> How about changing like below?
> 'Don't skip to reset counters if a regular command is present'
Makes sense.
--
Regards,
Sujit
next prev parent reply other threads:[~2013-07-18 3:48 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-07-09 9:15 [PATCH V3 0/2] Add suport for internal request (NOP and Query Request) Sujit Reddy Thumma
2013-07-09 9:15 ` [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU Sujit Reddy Thumma
2013-07-09 10:40 ` merez
2013-07-10 13:28 ` Seungwon Jeon
2013-07-11 9:38 ` Sujit Reddy Thumma
2013-07-17 8:13 ` Seungwon Jeon
2013-07-18 3:48 ` Sujit Reddy Thumma [this message]
2013-07-19 13:47 ` Seungwon Jeon
2013-07-19 18:26 ` Sujit Reddy Thumma
2013-07-09 9:15 ` [PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization Sujit Reddy Thumma
2013-07-09 10:40 ` merez
2013-07-10 13:29 ` Seungwon Jeon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=51E76596.5090509@codeaurora.org \
--to=sthumma@codeaurora.org \
--cc=JBottomley@parallels.com \
--cc=draviv@codeaurora.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=santoshsy@gmail.com \
--cc=tgih.jun@samsung.com \
--cc=vinholikatti@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).