mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
i2c: i2c-qcom-geni: Properly handle DMA safe buffers
We shouldn't attempt to DMA map the message buffers passed into this driver from the i2c core unless the message we're mapping have been properly setup for DMA. The i2c core indicates such a situation by setting the I2C_M_DMA_SAFE flag, so check for that flag before using DMA mode. We can also bounce the buffer if it isn't already mapped properly by using the i2c_get_dma_safe_msg_buf() APIs, so do that when we want to use DMA for a message. This fixes a problem where the kernel oopses cleaning pages for a buffer that's mapped into the vmalloc space. The pages are returned from request_firmware() and passed down directly to the i2c master to write to the i2c touchscreen device. Mapping vmalloc buffers with dma_map_single() won't work reliably, causing an oops like below: Unable to handle kernel paging request at virtual address ffffffc01391d000 ... Reported-by: Philip Chen <philipchen@chromium.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Reviewed-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
6bf4ca7fbc
commit
6697576788
1 changed files with 18 additions and 4 deletions
|
@ -367,20 +367,26 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
dma_addr_t rx_dma;
|
||||
enum geni_se_xfer_mode mode;
|
||||
unsigned long time_left = XFER_TIMEOUT;
|
||||
void *dma_buf;
|
||||
|
||||
gi2c->cur = msg;
|
||||
mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
|
||||
mode = GENI_SE_FIFO;
|
||||
dma_buf = i2c_get_dma_safe_msg_buf(msg, 32);
|
||||
if (dma_buf)
|
||||
mode = GENI_SE_DMA;
|
||||
|
||||
geni_se_select_mode(&gi2c->se, mode);
|
||||
writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
|
||||
geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
|
||||
if (mode == GENI_SE_DMA) {
|
||||
int ret;
|
||||
|
||||
ret = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len,
|
||||
ret = geni_se_rx_dma_prep(&gi2c->se, dma_buf, msg->len,
|
||||
&rx_dma);
|
||||
if (ret) {
|
||||
mode = GENI_SE_FIFO;
|
||||
geni_se_select_mode(&gi2c->se, mode);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,6 +399,7 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
if (gi2c->err)
|
||||
geni_i2c_rx_fsm_rst(gi2c);
|
||||
geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err);
|
||||
}
|
||||
return gi2c->err;
|
||||
}
|
||||
|
@ -403,20 +410,26 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
dma_addr_t tx_dma;
|
||||
enum geni_se_xfer_mode mode;
|
||||
unsigned long time_left;
|
||||
void *dma_buf;
|
||||
|
||||
gi2c->cur = msg;
|
||||
mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
|
||||
mode = GENI_SE_FIFO;
|
||||
dma_buf = i2c_get_dma_safe_msg_buf(msg, 32);
|
||||
if (dma_buf)
|
||||
mode = GENI_SE_DMA;
|
||||
|
||||
geni_se_select_mode(&gi2c->se, mode);
|
||||
writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
|
||||
geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
|
||||
if (mode == GENI_SE_DMA) {
|
||||
int ret;
|
||||
|
||||
ret = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len,
|
||||
ret = geni_se_tx_dma_prep(&gi2c->se, dma_buf, msg->len,
|
||||
&tx_dma);
|
||||
if (ret) {
|
||||
mode = GENI_SE_FIFO;
|
||||
geni_se_select_mode(&gi2c->se, mode);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,6 +445,7 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
if (gi2c->err)
|
||||
geni_i2c_tx_fsm_rst(gi2c);
|
||||
geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err);
|
||||
}
|
||||
return gi2c->err;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue