From 681d15ecd7c3fafb5c9b8c0305343a5abbf834d6 Mon Sep 17 00:00:00 2001 From: Asaf Vertz Date: Wed, 10 Dec 2014 10:00:36 +0200 Subject: [PATCH 001/360] dmaengine: imx-sdma: fix indentation Fixed a coding style error, switch and case should be at the same indent Signed-off-by: Asaf Vertz Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index d0df198f62e936..d2432c90a5667f 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1303,15 +1303,15 @@ static void sdma_load_firmware(const struct firmware *fw, void *context) if (header->ram_code_start + header->ram_code_size > fw->size) goto err_firmware; switch (header->version_major) { - case 1: - sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; - break; - case 2: - sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; - break; - default: - dev_err(sdma->dev, "unknown firmware version\n"); - goto err_firmware; + case 1: + sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; + break; + case 2: + sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2; + break; + default: + dev_err(sdma->dev, "unknown firmware version\n"); + goto err_firmware; } addr = (void *)header + header->script_addrs_start; From abf538ae0374ea827ac5fd51bf2a5184c50afd53 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Dec 2014 09:13:42 -0700 Subject: [PATCH 002/360] dmaengine: ioatdma: PQ err descriptors should callback with err results The err completion callback is missing from the error handler. Two reasons we never hit this. On Xeon because the hw err workaround, the completion happens on a NULL descriptor so we don't do callback on the PQ descriptor. On Atom we have DWBES support and thus the callback already happened or we don't halt on error, so that was take cared of. But this code needs to be corrected for future error handlers. Signed-off-by: Dave Jiang Acked-by Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma_v3.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 32eae38291e598..be307182231ea3 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -489,6 +489,7 @@ static void ioat3_eh(struct ioat2_dma_chan *ioat) struct ioat_chan_common *chan = &ioat->base; struct pci_dev *pdev = to_pdev(chan); struct ioat_dma_descriptor *hw; + struct dma_async_tx_descriptor *tx; u64 phys_complete; struct ioat_ring_ent *desc; u32 err_handled = 0; @@ -534,6 +535,16 @@ static void ioat3_eh(struct ioat2_dma_chan *ioat) dev_err(to_dev(chan), "%s: fatal error (%x:%x)\n", __func__, chanerr, err_handled); BUG(); + } else { /* cleanup the faulty descriptor */ + tx = &desc->txd; + if (tx->cookie) { + dma_cookie_complete(tx); + dma_descriptor_unmap(tx); + if (tx->callback) { + tx->callback(tx->callback_param); + tx->callback = NULL; + } + } } writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET); From 4bba7e9337e5573f83b2ccc235b7937d4d107d14 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:41:56 +0100 Subject: [PATCH 003/360] crypto: ux500: Use dmaengine_terminate_all API We are removing the dmaengine_device_control API, that shouldn't even have been exposed in the first place. Change the callers to use the proper API. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/crypto/ux500/cryp/cryp_core.c | 4 ++-- drivers/crypto/ux500/hash/hash_core.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c index f831bb952b2fd6..458d92158b12a3 100644 --- a/drivers/crypto/ux500/cryp/cryp_core.c +++ b/drivers/crypto/ux500/cryp/cryp_core.c @@ -606,12 +606,12 @@ static void cryp_dma_done(struct cryp_ctx *ctx) dev_dbg(ctx->device->dev, "[%s]: ", __func__); chan = ctx->device->dma.chan_mem2cryp; - dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); dma_unmap_sg(chan->device->dev, ctx->device->dma.sg_src, ctx->device->dma.sg_src_len, DMA_TO_DEVICE); chan = ctx->device->dma.chan_cryp2mem; - dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); dma_unmap_sg(chan->device->dev, ctx->device->dma.sg_dst, ctx->device->dma.sg_dst_len, DMA_FROM_DEVICE); } diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c index 70a20871e998cc..187a8fd7eee7f5 100644 --- a/drivers/crypto/ux500/hash/hash_core.c +++ b/drivers/crypto/ux500/hash/hash_core.c @@ -202,7 +202,7 @@ static void hash_dma_done(struct hash_ctx *ctx) struct dma_chan *chan; chan = ctx->device->dma.chan_mem2hash; - dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); dma_unmap_sg(chan->device->dev, ctx->device->dma.sg, ctx->device->dma.sg_len, DMA_TO_DEVICE); } From ceacbdbf65c4cf48a130db6152c6e03432c85ed1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:41:57 +0100 Subject: [PATCH 004/360] dmaengine: Make the destination abbreviation coherent The dmaengine header abbreviates destination as at least two different strings. Make a coherent use of a single one. Signed-off-by: Maxime Ripard Acked-by: Mark Brown Acked-by: Laurent Pinchart Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 2 +- drivers/dma/bcm2835-dma.c | 2 +- drivers/dma/edma.c | 2 +- drivers/dma/fsl-edma.c | 2 +- drivers/dma/nbpfaxi.c | 2 +- drivers/dma/omap-dma.c | 2 +- drivers/dma/pl330.c | 2 +- drivers/dma/sirf-dma.c | 2 +- include/linux/dmaengine.h | 8 ++++---- sound/soc/soc-generic-dmaengine-pcm.c | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index b60d77a22df673..ff67466c779c5e 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1229,7 +1229,7 @@ static int at_xdmac_device_slave_caps(struct dma_chan *dchan, { caps->src_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; - caps->dstn_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; + caps->dst_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = true; caps->cmd_terminate = true; diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 918b7b3f766f03..3feba6c258369c 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -569,7 +569,7 @@ static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); - caps->dstn_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + caps->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = false; caps->cmd_terminate = true; diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index b969206439b723..2b49fe6f04653a 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -998,7 +998,7 @@ static int edma_dma_device_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = EDMA_DMA_BUSWIDTHS; - caps->dstn_addr_widths = EDMA_DMA_BUSWIDTHS; + caps->dst_addr_widths = EDMA_DMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = true; caps->cmd_terminate = true; diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index e9ebb89e171195..ce6e960b78a7a6 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -784,7 +784,7 @@ static int fsl_dma_device_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = FSL_EDMA_BUSWIDTHS; - caps->dstn_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->dst_addr_widths = FSL_EDMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = true; caps->cmd_terminate = true; diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index d7d61e1a01c30f..3d993e78518087 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -1076,7 +1076,7 @@ static int nbpf_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = NBPF_DMA_BUSWIDTHS; - caps->dstn_addr_widths = NBPF_DMA_BUSWIDTHS; + caps->dst_addr_widths = NBPF_DMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = false; caps->cmd_terminate = true; diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index c0016a68b44659..ca4645c2763430 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -1098,7 +1098,7 @@ static int omap_dma_device_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = OMAP_DMA_BUSWIDTHS; - caps->dstn_addr_widths = OMAP_DMA_BUSWIDTHS; + caps->dst_addr_widths = OMAP_DMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = true; caps->cmd_terminate = true; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index bdf40b53003288..4a759c8718a8ba 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2627,7 +2627,7 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = PL330_DMA_BUSWIDTHS; - caps->dstn_addr_widths = PL330_DMA_BUSWIDTHS; + caps->dst_addr_widths = PL330_DMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = false; caps->cmd_terminate = true; diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 3492a5f91d3155..11c85fc450a1c7 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -652,7 +652,7 @@ static int sirfsoc_dma_device_slave_caps(struct dma_chan *dchan, struct dma_slave_caps *caps) { caps->src_addr_widths = SIRFSOC_DMA_BUSWIDTHS; - caps->dstn_addr_widths = SIRFSOC_DMA_BUSWIDTHS; + caps->dst_addr_widths = SIRFSOC_DMA_BUSWIDTHS; caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = true; caps->cmd_terminate = true; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 40cd75e21ea214..03a1febe87407e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -387,7 +387,7 @@ enum dma_residue_granularity { /* struct dma_slave_caps - expose capabilities of a slave channel only * * @src_addr_widths: bit mask of src addr widths the channel supports - * @dstn_addr_widths: bit mask of dstn addr widths the channel supports + * @dst_addr_widths: bit mask of dstn addr widths the channel supports * @directions: bit mask of slave direction the channel supported * since the enum dma_transfer_direction is not defined as bits for each * type of direction, the dma controller should fill (1 << ) and same @@ -398,7 +398,7 @@ enum dma_residue_granularity { */ struct dma_slave_caps { u32 src_addr_widths; - u32 dstn_addr_widths; + u32 dst_addr_widths; u32 directions; bool cmd_pause; bool cmd_terminate; @@ -639,10 +639,10 @@ struct dma_device { void (*device_free_chan_resources)(struct dma_chan *chan); struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)( - struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, size_t len, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_xor)( - struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, + struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src, unsigned int src_cnt, size_t len, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_xor_val)( struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt, diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index b329b84bc5af77..851f7afcd5dc3c 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -151,7 +151,7 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea hw.info |= SNDRV_PCM_INFO_BATCH; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - addr_widths = dma_caps.dstn_addr_widths; + addr_widths = dma_caps.dst_addr_widths; else addr_widths = dma_caps.src_addr_widths; } From d2f4f99db3e9ec8b063cf2e45704e2bb95428317 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:41:58 +0100 Subject: [PATCH 005/360] dmaengine: Rework dma_chan_get dma_chan_get uses a rather interesting error handling and code path. Change it to something more usual in the kernel. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index e057935e302319..a5da0e147560c5 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -222,31 +222,33 @@ static void balance_ref_count(struct dma_chan *chan) */ static int dma_chan_get(struct dma_chan *chan) { - int err = -ENODEV; struct module *owner = dma_chan_to_owner(chan); + int ret; + /* The channel is already in use, update client count */ if (chan->client_count) { __module_get(owner); - err = 0; - } else if (try_module_get(owner)) - err = 0; + goto out; + } - if (err == 0) - chan->client_count++; + if (!try_module_get(owner)) + return -ENODEV; /* allocate upon first client reference */ - if (chan->client_count == 1 && err == 0) { - int desc_cnt = chan->device->device_alloc_chan_resources(chan); - - if (desc_cnt < 0) { - err = desc_cnt; - chan->client_count = 0; - module_put(owner); - } else if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) - balance_ref_count(chan); - } + ret = chan->device->device_alloc_chan_resources(chan); + if (ret < 0) + goto err_out; - return err; + if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) + balance_ref_count(chan); + +out: + chan->client_count++; + return 0; + +err_out: + module_put(owner); + return ret; } /** From c4b54a648e682f678c338619df848233a6babc46 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:41:59 +0100 Subject: [PATCH 006/360] dmaengine: Make channel allocation callbacks optional Nowadays, some drivers don't have anything in there channel allocation callbacks anymore. Remove the BUG_ON if those callbacks aren't implemented, in order to allow drivers to not implement them. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a5da0e147560c5..b7f09f63a1600f 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -235,9 +235,11 @@ static int dma_chan_get(struct dma_chan *chan) return -ENODEV; /* allocate upon first client reference */ - ret = chan->device->device_alloc_chan_resources(chan); - if (ret < 0) - goto err_out; + if (chan->device->device_alloc_chan_resources) { + ret = chan->device->device_alloc_chan_resources(chan); + if (ret < 0) + goto err_out; + } if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) balance_ref_count(chan); @@ -259,11 +261,15 @@ static int dma_chan_get(struct dma_chan *chan) */ static void dma_chan_put(struct dma_chan *chan) { + /* This channel is not in use, bail out */ if (!chan->client_count) - return; /* this channel failed alloc_chan_resources */ + return; + chan->client_count--; module_put(dma_chan_to_owner(chan)); - if (chan->client_count == 0) + + /* This channel is not in use anymore, free it */ + if (!chan->client_count && chan->device->device_free_chan_resources) chan->device->device_free_chan_resources(chan); } @@ -818,8 +824,6 @@ int dma_async_device_register(struct dma_device *device) BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) && !device->device_prep_interleaved_dma); - BUG_ON(!device->device_alloc_chan_resources); - BUG_ON(!device->device_free_chan_resources); BUG_ON(!device->device_tx_status); BUG_ON(!device->device_issue_pending); BUG_ON(!device->dev); From 94a73e30dfe6722e9f4ef19f7892901d7d00eab1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:00 +0100 Subject: [PATCH 007/360] dmaengine: Introduce a device_config callback The fact that the channel configuration is done in device_control is rather misleading, since it's not really advertised as such, plus, the fact that the framework exposes a function of its own makes it not really intuitive, while we're losing the type checking whenever we pass that unsigned long argument. Add a device_config callback to dma_device, with a fallback on the old behaviour for now for existing drivers to opt in. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 03a1febe87407e..cf7c85d408ade7 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -608,6 +608,8 @@ struct dma_tx_state { * The function takes a buffer of size buf_len. The callback function will * be called after period_len bytes have been transferred. * @device_prep_interleaved_dma: Transfer expression in a generic way. + * @device_config: Pushes a new configuration to a channel, return 0 or an error + * code * @device_control: manipulate all pending operations on a channel, returns * zero or error code * @device_tx_status: poll for transaction completion, the optional @@ -674,6 +676,9 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags); + + int (*device_config)(struct dma_chan *chan, + struct dma_slave_config *config); int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg); @@ -697,6 +702,9 @@ static inline int dmaengine_device_control(struct dma_chan *chan, static inline int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config) { + if (chan->device->device_config) + return chan->device->device_config(chan, config); + return dmaengine_device_control(chan, DMA_SLAVE_CONFIG, (unsigned long)config); } From 23a3ea2f5bead4d3b16e119e9127a66234f41d53 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:01 +0100 Subject: [PATCH 008/360] dmaengine: split out pause/resume operations from device_control Split out the pause and resume operations to callbacks of their own. In order to preserve some backwark compatibility, the dmaengine_pause/dmaengine_resume are still falling back on dmaengine_device_control. Eventually, that will allow to get the device capabilities in a generic way, removing the need to implement device_slave_caps. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cf7c85d408ade7..01f27e8a69b76f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -612,6 +612,10 @@ struct dma_tx_state { * code * @device_control: manipulate all pending operations on a channel, returns * zero or error code + * @device_pause: Pauses any transfer happening on a channel. Returns + * 0 or an error code + * @device_resume: Resumes any transfer on a channel previously + * paused. Returns 0 or an error code * @device_tx_status: poll for transaction completion, the optional * txstate parameter can be supplied with a pointer to get a * struct with auxiliary transfer status information, otherwise the call @@ -681,6 +685,8 @@ struct dma_device { struct dma_slave_config *config); int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg); + int (*device_pause)(struct dma_chan *chan); + int (*device_resume)(struct dma_chan *chan); enum dma_status (*device_tx_status)(struct dma_chan *chan, dma_cookie_t cookie, @@ -795,11 +801,17 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan) static inline int dmaengine_pause(struct dma_chan *chan) { + if (chan->device->device_pause) + return chan->device->device_pause(chan); + return dmaengine_device_control(chan, DMA_PAUSE, 0); } static inline int dmaengine_resume(struct dma_chan *chan) { + if (chan->device->device_resume) + return chan->device->device_resume(chan); + return dmaengine_device_control(chan, DMA_RESUME, 0); } From 7fa0cf462daa6f6121b332b87833d7f5bdb515c0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:02 +0100 Subject: [PATCH 009/360] dmaengine: Add device_terminate_all callback Split out the terminate_all command from device_control to a dma_device callback. In order to preserve backward capability, still rely on device_control if no such callback has been implemented. Eventually, this will allow to create a generic dma_slave_caps callback. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 01f27e8a69b76f..ded5161653aa8c 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -616,6 +616,8 @@ struct dma_tx_state { * 0 or an error code * @device_resume: Resumes any transfer on a channel previously * paused. Returns 0 or an error code + * @device_terminate_all: Aborts all transfers on a channel. Returns 0 + * or an error code * @device_tx_status: poll for transaction completion, the optional * txstate parameter can be supplied with a pointer to get a * struct with auxiliary transfer status information, otherwise the call @@ -687,6 +689,7 @@ struct dma_device { unsigned long arg); int (*device_pause)(struct dma_chan *chan); int (*device_resume)(struct dma_chan *chan); + int (*device_terminate_all)(struct dma_chan *chan); enum dma_status (*device_tx_status)(struct dma_chan *chan, dma_cookie_t cookie, @@ -796,6 +799,9 @@ static inline int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_cap static inline int dmaengine_terminate_all(struct dma_chan *chan) { + if (chan->device->device_terminate_all) + return chan->device->device_terminate_all(chan); + return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); } From 4f8ef9f4140cc286d7d1cf9237da7a7439e4fc0b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:03 +0100 Subject: [PATCH 010/360] dmaengine: Remove the need to declare device_control In order to migrate the drivers without triggering a BUG_ON for the converted drivers, which would cause bisectability issues, we need to remove that check before removing the device_control function entirely. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index b7f09f63a1600f..cae120994d8fd3 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -819,8 +819,6 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_sg); BUG_ON(dma_has_cap(DMA_CYCLIC, device->cap_mask) && !device->device_prep_dma_cyclic); - BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && - !device->device_control); BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) && !device->device_prep_interleaved_dma); From cb8cea513c80db1dfe2dce468d2d0772005bb9a1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:04 +0100 Subject: [PATCH 011/360] dmaengine: Create a generic dma_slave_caps callback dma_slave_caps is very important to the generic layers that might interact with dmaengine, such as ASoC. Unfortunately, it has been added as yet another dma_device callback, and most of the existing drivers haven't implemented it, reducing its reliability. Introduce a generic behaviour to implement this, that rely on both the split of device_control to derive which functions are supported and on new variables to be set in the dma_device structure. These variables holds what used to be the capabilities, that were set per-channel. However, this proved to be a bit overkill, since every driver filling these so far were hardcoding it, disregarding which channel was actually given. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 41 +++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ded5161653aa8c..adf22089cc9326 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -594,6 +594,14 @@ struct dma_tx_state { * @fill_align: alignment shift for memset operations * @dev_id: unique device ID * @dev: struct device reference for dma mapping api + * @src_addr_widths: bit mask of src addr widths the device supports + * @dst_addr_widths: bit mask of dst addr widths the device supports + * @directions: bit mask of slave direction the device supports since + * the enum dma_transfer_direction is not defined as bits for + * each type of direction, the dma controller should fill (1 << + * ) and same should be checked by controller as well + * @residue_granularity: granularity of the transfer residue reported + * by tx_status * @device_alloc_chan_resources: allocate resources and return the * number of allocated descriptors * @device_free_chan_resources: release DMA channel's resources @@ -643,6 +651,11 @@ struct dma_device { int dev_id; struct device *dev; + u32 src_addr_widths; + u32 dst_addr_widths; + u32 directions; + enum dma_residue_granularity residue_granularity; + int (*device_alloc_chan_resources)(struct dma_chan *chan); void (*device_free_chan_resources)(struct dma_chan *chan); @@ -784,17 +797,37 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg( static inline int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) { + struct dma_device *device; + if (!chan || !caps) return -EINVAL; + device = chan->device; + /* check if the channel supports slave transactions */ - if (!test_bit(DMA_SLAVE, chan->device->cap_mask.bits)) + if (!test_bit(DMA_SLAVE, device->cap_mask.bits)) + return -ENXIO; + + if (device->device_slave_caps) + return device->device_slave_caps(chan, caps); + + /* + * Check whether it reports it uses the generic slave + * capabilities, if not, that means it doesn't support any + * kind of slave capabilities reporting. + */ + if (!device->directions) return -ENXIO; - if (chan->device->device_slave_caps) - return chan->device->device_slave_caps(chan, caps); + caps->src_addr_widths = device->src_addr_widths; + caps->dst_addr_widths = device->dst_addr_widths; + caps->directions = device->directions; + caps->residue_granularity = device->residue_granularity; + + caps->cmd_pause = !!device->device_pause; + caps->cmd_terminate = !!device->device_terminate_all; - return -ENXIO; + return 0; } static inline int dmaengine_terminate_all(struct dma_chan *chan) From bcd1b0b9015b746436d6c846a35a4310e23f44a7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:05 +0100 Subject: [PATCH 012/360] dmaengine: pl08x: Split device_control Split the device_control callback of the AMBA PL08x DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 156 ++++++++++++++++++++++----------------- 1 file changed, 90 insertions(+), 66 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 1364d00881dd56..4a5fd245014e66 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1386,32 +1386,6 @@ static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan, return pl08x_cctl(cctl); } -static int dma_set_runtime_config(struct dma_chan *chan, - struct dma_slave_config *config) -{ - struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); - struct pl08x_driver_data *pl08x = plchan->host; - - if (!plchan->slave) - return -EINVAL; - - /* Reject definitely invalid configurations */ - if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || - config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) - return -EINVAL; - - if (config->device_fc && pl08x->vd->pl080s) { - dev_err(&pl08x->adev->dev, - "%s: PL080S does not support peripheral flow control\n", - __func__); - return -EINVAL; - } - - plchan->cfg = *config; - - return 0; -} - /* * Slave transactions callback to the slave device to allow * synchronization of slave DMA signals with the DMAC enable @@ -1693,20 +1667,71 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_cyclic( return vchan_tx_prep(&plchan->vc, &txd->vd, flags); } -static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int pl08x_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + struct pl08x_driver_data *pl08x = plchan->host; + + if (!plchan->slave) + return -EINVAL; + + /* Reject definitely invalid configurations */ + if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + return -EINVAL; + + if (config->device_fc && pl08x->vd->pl080s) { + dev_err(&pl08x->adev->dev, + "%s: PL080S does not support peripheral flow control\n", + __func__); + return -EINVAL; + } + + plchan->cfg = *config; + + return 0; +} + +static int pl08x_terminate_all(struct dma_chan *chan) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; unsigned long flags; - int ret = 0; - /* Controls applicable to inactive channels */ - if (cmd == DMA_SLAVE_CONFIG) { - return dma_set_runtime_config(chan, - (struct dma_slave_config *)arg); + spin_lock_irqsave(&plchan->vc.lock, flags); + if (!plchan->phychan && !plchan->at) { + spin_unlock_irqrestore(&plchan->vc.lock, flags); + return 0; } + plchan->state = PL08X_CHAN_IDLE; + + if (plchan->phychan) { + /* + * Mark physical channel as free and free any slave + * signal + */ + pl08x_phy_free(plchan); + } + /* Dequeue jobs and free LLIs */ + if (plchan->at) { + pl08x_desc_free(&plchan->at->vd); + plchan->at = NULL; + } + /* Dequeue jobs not yet fired as well */ + pl08x_free_txd_list(pl08x, plchan); + + spin_unlock_irqrestore(&plchan->vc.lock, flags); + + return 0; +} + +static int pl08x_pause(struct dma_chan *chan) +{ + struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + unsigned long flags; + /* * Anything succeeds on channels with no physical allocation and * no queued transfers. @@ -1717,42 +1742,35 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, return 0; } - switch (cmd) { - case DMA_TERMINATE_ALL: - plchan->state = PL08X_CHAN_IDLE; + pl08x_pause_phy_chan(plchan->phychan); + plchan->state = PL08X_CHAN_PAUSED; - if (plchan->phychan) { - /* - * Mark physical channel as free and free any slave - * signal - */ - pl08x_phy_free(plchan); - } - /* Dequeue jobs and free LLIs */ - if (plchan->at) { - pl08x_desc_free(&plchan->at->vd); - plchan->at = NULL; - } - /* Dequeue jobs not yet fired as well */ - pl08x_free_txd_list(pl08x, plchan); - break; - case DMA_PAUSE: - pl08x_pause_phy_chan(plchan->phychan); - plchan->state = PL08X_CHAN_PAUSED; - break; - case DMA_RESUME: - pl08x_resume_phy_chan(plchan->phychan); - plchan->state = PL08X_CHAN_RUNNING; - break; - default: - /* Unknown command */ - ret = -ENXIO; - break; + spin_unlock_irqrestore(&plchan->vc.lock, flags); + + return 0; +} + +static int pl08x_resume(struct dma_chan *chan) +{ + struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + unsigned long flags; + + /* + * Anything succeeds on channels with no physical allocation and + * no queued transfers. + */ + spin_lock_irqsave(&plchan->vc.lock, flags); + if (!plchan->phychan && !plchan->at) { + spin_unlock_irqrestore(&plchan->vc.lock, flags); + return 0; } + pl08x_resume_phy_chan(plchan->phychan); + plchan->state = PL08X_CHAN_RUNNING; + spin_unlock_irqrestore(&plchan->vc.lock, flags); - return ret; + return 0; } bool pl08x_filter_id(struct dma_chan *chan, void *chan_id) @@ -2048,7 +2066,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->memcpy.device_prep_dma_interrupt = pl08x_prep_dma_interrupt; pl08x->memcpy.device_tx_status = pl08x_dma_tx_status; pl08x->memcpy.device_issue_pending = pl08x_issue_pending; - pl08x->memcpy.device_control = pl08x_control; + pl08x->memcpy.device_config = pl08x_config; + pl08x->memcpy.device_pause = pl08x_pause; + pl08x->memcpy.device_resume = pl08x_resume; + pl08x->memcpy.device_terminate_all = pl08x_terminate_all; /* Initialize slave engine */ dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask); @@ -2061,7 +2082,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->slave.device_issue_pending = pl08x_issue_pending; pl08x->slave.device_prep_slave_sg = pl08x_prep_slave_sg; pl08x->slave.device_prep_dma_cyclic = pl08x_prep_dma_cyclic; - pl08x->slave.device_control = pl08x_control; + pl08x->slave.device_config = pl08x_config; + pl08x->slave.device_pause = pl08x_pause; + pl08x->slave.device_resume = pl08x_resume; + pl08x->slave.device_terminate_all = pl08x_terminate_all; /* Get the platform data */ pl08x->pd = dev_get_platdata(&adev->dev); From 4facfe7f09f2b3f0799c924d9d6a0855a42c0833 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:06 +0100 Subject: [PATCH 013/360] dmaengine: hdmac: Split device_control Split the device_control callback of the Atmel HDMAC driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 121 +++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 48 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index ca9dd261328357..86450b3442f2f2 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -972,11 +972,13 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, return NULL; } -static int set_runtime_config(struct dma_chan *chan, - struct dma_slave_config *sconfig) +static int atc_config(struct dma_chan *chan, + struct dma_slave_config *sconfig) { struct at_dma_chan *atchan = to_at_dma_chan(chan); + dev_vdbg(chan2dev(chan), "%s\n", __func__); + /* Check if it is chan is configured for slave transfers */ if (!chan->private) return -EINVAL; @@ -989,9 +991,28 @@ static int set_runtime_config(struct dma_chan *chan, return 0; } +static int atc_pause(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma *atdma = to_at_dma(chan->device); + int chan_id = atchan->chan_common.chan_id; + unsigned long flags; -static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) + LIST_HEAD(list); + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + + spin_lock_irqsave(&atchan->lock, flags); + + dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id)); + set_bit(ATC_IS_PAUSED, &atchan->status); + + spin_unlock_irqrestore(&atchan->lock, flags); + + return 0; +} + +static int atc_resume(struct dma_chan *chan) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma *atdma = to_at_dma(chan->device); @@ -1000,60 +1021,61 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, LIST_HEAD(list); - dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd); + dev_vdbg(chan2dev(chan), "%s\n", __func__); - if (cmd == DMA_PAUSE) { - spin_lock_irqsave(&atchan->lock, flags); + if (!atc_chan_is_paused(atchan)) + return 0; - dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id)); - set_bit(ATC_IS_PAUSED, &atchan->status); + spin_lock_irqsave(&atchan->lock, flags); - spin_unlock_irqrestore(&atchan->lock, flags); - } else if (cmd == DMA_RESUME) { - if (!atc_chan_is_paused(atchan)) - return 0; + dma_writel(atdma, CHDR, AT_DMA_RES(chan_id)); + clear_bit(ATC_IS_PAUSED, &atchan->status); - spin_lock_irqsave(&atchan->lock, flags); + spin_unlock_irqrestore(&atchan->lock, flags); - dma_writel(atdma, CHDR, AT_DMA_RES(chan_id)); - clear_bit(ATC_IS_PAUSED, &atchan->status); + return 0; +} - spin_unlock_irqrestore(&atchan->lock, flags); - } else if (cmd == DMA_TERMINATE_ALL) { - struct at_desc *desc, *_desc; - /* - * This is only called when something went wrong elsewhere, so - * we don't really care about the data. Just disable the - * channel. We still have to poll the channel enable bit due - * to AHB/HSB limitations. - */ - spin_lock_irqsave(&atchan->lock, flags); +static int atc_terminate_all(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma *atdma = to_at_dma(chan->device); + int chan_id = atchan->chan_common.chan_id; + struct at_desc *desc, *_desc; + unsigned long flags; - /* disabling channel: must also remove suspend state */ - dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask); + LIST_HEAD(list); - /* confirm that this channel is disabled */ - while (dma_readl(atdma, CHSR) & atchan->mask) - cpu_relax(); + dev_vdbg(chan2dev(chan), "%s\n", __func__); - /* active_list entries will end up before queued entries */ - list_splice_init(&atchan->queue, &list); - list_splice_init(&atchan->active_list, &list); + /* + * This is only called when something went wrong elsewhere, so + * we don't really care about the data. Just disable the + * channel. We still have to poll the channel enable bit due + * to AHB/HSB limitations. + */ + spin_lock_irqsave(&atchan->lock, flags); - /* Flush all pending and queued descriptors */ - list_for_each_entry_safe(desc, _desc, &list, desc_node) - atc_chain_complete(atchan, desc); + /* disabling channel: must also remove suspend state */ + dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask); - clear_bit(ATC_IS_PAUSED, &atchan->status); - /* if channel dedicated to cyclic operations, free it */ - clear_bit(ATC_IS_CYCLIC, &atchan->status); + /* confirm that this channel is disabled */ + while (dma_readl(atdma, CHSR) & atchan->mask) + cpu_relax(); - spin_unlock_irqrestore(&atchan->lock, flags); - } else if (cmd == DMA_SLAVE_CONFIG) { - return set_runtime_config(chan, (struct dma_slave_config *)arg); - } else { - return -ENXIO; - } + /* active_list entries will end up before queued entries */ + list_splice_init(&atchan->queue, &list); + list_splice_init(&atchan->active_list, &list); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + atc_chain_complete(atchan, desc); + + clear_bit(ATC_IS_PAUSED, &atchan->status); + /* if channel dedicated to cyclic operations, free it */ + clear_bit(ATC_IS_CYCLIC, &atchan->status); + + spin_unlock_irqrestore(&atchan->lock, flags); return 0; } @@ -1505,7 +1527,10 @@ static int __init at_dma_probe(struct platform_device *pdev) /* controller can do slave DMA: can trigger cyclic transfers */ dma_cap_set(DMA_CYCLIC, atdma->dma_common.cap_mask); atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic; - atdma->dma_common.device_control = atc_control; + atdma->dma_common.device_config = atc_config; + atdma->dma_common.device_pause = atc_pause; + atdma->dma_common.device_resume = atc_resume; + atdma->dma_common.device_terminate_all = atc_terminate_all; } dma_writel(atdma, EN, AT_DMA_ENABLE); @@ -1622,7 +1647,7 @@ static void atc_suspend_cyclic(struct at_dma_chan *atchan) if (!atc_chan_is_paused(atchan)) { dev_warn(chan2dev(chan), "cyclic channel not paused, should be done by channel user\n"); - atc_control(chan, DMA_PAUSE, 0); + atc_pause(chan); } /* now preserve additional data for cyclic operations */ From 3d138877e8547e472863d975488bbf80f5a7be6c Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Mon, 17 Nov 2014 14:42:07 +0100 Subject: [PATCH 014/360] dmaengine: at_xdmac: split device_control Use newly introduced callbacks. Signed-off-by: Ludovic Desroches Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 94 +++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index ff67466c779c5e..c7f335088d1ead 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1107,58 +1107,75 @@ static void at_xdmac_issue_pending(struct dma_chan *chan) return; } -static int at_xdmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int at_xdmac_device_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); + int ret; + + dev_dbg(chan2dev(chan), "%s\n", __func__); + + spin_lock_bh(&atchan->lock); + ret = at_xdmac_set_slave_config(chan, config); + spin_unlock_bh(&atchan->lock); + + return ret; +} + +static int at_xdmac_device_pause(struct dma_chan *chan) { - struct at_xdmac_desc *desc, *_desc; struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); - int ret = 0; - dev_dbg(chan2dev(chan), "%s: cmd=%d\n", __func__, cmd); + dev_dbg(chan2dev(chan), "%s\n", __func__); spin_lock_bh(&atchan->lock); + at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask); + set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); + spin_unlock_bh(&atchan->lock); - switch (cmd) { - case DMA_PAUSE: - at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask); - set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); - break; + return 0; +} - case DMA_RESUME: - if (!at_xdmac_chan_is_paused(atchan)) - break; +static int at_xdmac_device_resume(struct dma_chan *chan) +{ + struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); + struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); - at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask); - clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); - break; + dev_dbg(chan2dev(chan), "%s\n", __func__); - case DMA_TERMINATE_ALL: - at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); - while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask) - cpu_relax(); + spin_lock_bh(&atchan->lock); + if (!at_xdmac_chan_is_paused(atchan)) + return 0; - /* Cancel all pending transfers. */ - list_for_each_entry_safe(desc, _desc, &atchan->xfers_list, xfer_node) - at_xdmac_remove_xfer(atchan, desc); + at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask); + clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); + spin_unlock_bh(&atchan->lock); + + return 0; +} + +static int at_xdmac_device_terminate_all(struct dma_chan *chan) +{ + struct at_xdmac_desc *desc, *_desc; + struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); + struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); - clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status); - break; + dev_dbg(chan2dev(chan), "%s\n", __func__); - case DMA_SLAVE_CONFIG: - ret = at_xdmac_set_slave_config(chan, - (struct dma_slave_config *)arg); - break; + spin_lock_bh(&atchan->lock); + at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); + while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask) + cpu_relax(); - default: - dev_err(chan2dev(chan), - "unmanaged or unknown dma control cmd: %d\n", cmd); - ret = -ENXIO; - } + /* Cancel all pending transfers. */ + list_for_each_entry_safe(desc, _desc, &atchan->xfers_list, xfer_node) + at_xdmac_remove_xfer(atchan, desc); + clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status); spin_unlock_bh(&atchan->lock); - return ret; + return 0; } static int at_xdmac_alloc_chan_resources(struct dma_chan *chan) @@ -1270,7 +1287,7 @@ static int atmel_xdmac_suspend(struct device *dev) if (at_xdmac_chan_is_cyclic(atchan)) { if (!at_xdmac_chan_is_paused(atchan)) - at_xdmac_control(chan, DMA_PAUSE, 0); + at_xdmac_device_pause(chan); atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA); atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC); @@ -1407,7 +1424,10 @@ static int at_xdmac_probe(struct platform_device *pdev) atxdmac->dma.device_prep_dma_cyclic = at_xdmac_prep_dma_cyclic; atxdmac->dma.device_prep_dma_memcpy = at_xdmac_prep_dma_memcpy; atxdmac->dma.device_prep_slave_sg = at_xdmac_prep_slave_sg; - atxdmac->dma.device_control = at_xdmac_control; + atxdmac->dma.device_config = at_xdmac_device_config; + atxdmac->dma.device_pause = at_xdmac_device_pause; + atxdmac->dma.device_resume = at_xdmac_device_resume; + atxdmac->dma.device_terminate_all = at_xdmac_device_terminate_all; atxdmac->dma.device_slave_caps = at_xdmac_device_slave_caps; /* Disable all chans and interrupts. */ From 39159bea78ed0565bfffb7100b396a56625f1d00 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:08 +0100 Subject: [PATCH 015/360] dmaengine: bcm2835: Split device_control Split the device_control callback of the Broadcom BCM2835 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 3feba6c258369c..13b05c1673b135 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -436,9 +436,11 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( return vchan_tx_prep(&c->vc, &d->vd, flags); } -static int bcm2835_dma_slave_config(struct bcm2835_chan *c, - struct dma_slave_config *cfg) +static int bcm2835_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *cfg) { + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + if ((cfg->direction == DMA_DEV_TO_MEM && cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || (cfg->direction == DMA_MEM_TO_DEV && @@ -452,8 +454,9 @@ static int bcm2835_dma_slave_config(struct bcm2835_chan *c, return 0; } -static int bcm2835_dma_terminate_all(struct bcm2835_chan *c) +static int bcm2835_dma_terminate_all(struct dma_chan *chan) { + struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); unsigned long flags; int timeout = 10000; @@ -495,24 +498,6 @@ static int bcm2835_dma_terminate_all(struct bcm2835_chan *c) return 0; } -static int bcm2835_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); - - switch (cmd) { - case DMA_SLAVE_CONFIG: - return bcm2835_dma_slave_config(c, - (struct dma_slave_config *)arg); - - case DMA_TERMINATE_ALL: - return bcm2835_dma_terminate_all(c); - - default: - return -ENXIO; - } -} - static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) { struct bcm2835_chan *c; @@ -615,9 +600,9 @@ static int bcm2835_dma_probe(struct platform_device *pdev) od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; od->ddev.device_tx_status = bcm2835_dma_tx_status; od->ddev.device_issue_pending = bcm2835_dma_issue_pending; - od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; - od->ddev.device_control = bcm2835_dma_control; + od->ddev.device_config = bcm2835_dma_slave_config; + od->ddev.device_terminate_all = bcm2835_dma_terminate_all; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); spin_lock_init(&od->lock); From 6782af118b6c86419303a5daa3cf5a813d89954a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:09 +0100 Subject: [PATCH 016/360] dmaengine: coh901318: Split device_control Split the device_control callback of the ST-Ericsson COH901318 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/coh901318.c | 137 ++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 77 deletions(-) diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index e88588d8ecd34b..418e4e4fb7ba77 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -2114,6 +2114,57 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int coh901318_terminate_all(struct dma_chan *chan) +{ + unsigned long flags; + struct coh901318_chan *cohc = to_coh901318_chan(chan); + struct coh901318_desc *cohd; + void __iomem *virtbase = cohc->base->virtbase; + + /* The remainder of this function terminates the transfer */ + coh901318_pause(chan); + spin_lock_irqsave(&cohc->lock, flags); + + /* Clear any pending BE or TC interrupt */ + if (cohc->id < 32) { + writel(1 << cohc->id, virtbase + COH901318_BE_INT_CLEAR1); + writel(1 << cohc->id, virtbase + COH901318_TC_INT_CLEAR1); + } else { + writel(1 << (cohc->id - 32), virtbase + + COH901318_BE_INT_CLEAR2); + writel(1 << (cohc->id - 32), virtbase + + COH901318_TC_INT_CLEAR2); + } + + enable_powersave(cohc); + + while ((cohd = coh901318_first_active_get(cohc))) { + /* release the lli allocation*/ + coh901318_lli_free(&cohc->base->pool, &cohd->lli); + + /* return desc to free-list */ + coh901318_desc_remove(cohd); + coh901318_desc_free(cohc, cohd); + } + + while ((cohd = coh901318_first_queued(cohc))) { + /* release the lli allocation*/ + coh901318_lli_free(&cohc->base->pool, &cohd->lli); + + /* return desc to free-list */ + coh901318_desc_remove(cohd); + coh901318_desc_free(cohc, cohd); + } + + + cohc->nbr_active_done = 0; + cohc->busy = 0; + + spin_unlock_irqrestore(&cohc->lock, flags); + + return 0; +} + static int coh901318_alloc_chan_resources(struct dma_chan *chan) { struct coh901318_chan *cohc = to_coh901318_chan(chan); @@ -2156,7 +2207,7 @@ coh901318_free_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&cohc->lock, flags); - dmaengine_terminate_all(chan); + coh901318_terminate_all(chan); } @@ -2540,80 +2591,6 @@ static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, cohc->ctrl = ctrl; } -static int -coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - unsigned long flags; - struct coh901318_chan *cohc = to_coh901318_chan(chan); - struct coh901318_desc *cohd; - void __iomem *virtbase = cohc->base->virtbase; - - if (cmd == DMA_SLAVE_CONFIG) { - struct dma_slave_config *config = - (struct dma_slave_config *) arg; - - coh901318_dma_set_runtimeconfig(chan, config); - return 0; - } - - if (cmd == DMA_PAUSE) { - coh901318_pause(chan); - return 0; - } - - if (cmd == DMA_RESUME) { - coh901318_resume(chan); - return 0; - } - - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; - - /* The remainder of this function terminates the transfer */ - coh901318_pause(chan); - spin_lock_irqsave(&cohc->lock, flags); - - /* Clear any pending BE or TC interrupt */ - if (cohc->id < 32) { - writel(1 << cohc->id, virtbase + COH901318_BE_INT_CLEAR1); - writel(1 << cohc->id, virtbase + COH901318_TC_INT_CLEAR1); - } else { - writel(1 << (cohc->id - 32), virtbase + - COH901318_BE_INT_CLEAR2); - writel(1 << (cohc->id - 32), virtbase + - COH901318_TC_INT_CLEAR2); - } - - enable_powersave(cohc); - - while ((cohd = coh901318_first_active_get(cohc))) { - /* release the lli allocation*/ - coh901318_lli_free(&cohc->base->pool, &cohd->lli); - - /* return desc to free-list */ - coh901318_desc_remove(cohd); - coh901318_desc_free(cohc, cohd); - } - - while ((cohd = coh901318_first_queued(cohc))) { - /* release the lli allocation*/ - coh901318_lli_free(&cohc->base->pool, &cohd->lli); - - /* return desc to free-list */ - coh901318_desc_remove(cohd); - coh901318_desc_free(cohc, cohd); - } - - - cohc->nbr_active_done = 0; - cohc->busy = 0; - - spin_unlock_irqrestore(&cohc->lock, flags); - - return 0; -} - void coh901318_base_init(struct dma_device *dma, const int *pick_chans, struct coh901318_base *base) { @@ -2717,7 +2694,10 @@ static int __init coh901318_probe(struct platform_device *pdev) base->dma_slave.device_prep_slave_sg = coh901318_prep_slave_sg; base->dma_slave.device_tx_status = coh901318_tx_status; base->dma_slave.device_issue_pending = coh901318_issue_pending; - base->dma_slave.device_control = coh901318_control; + base->dma_slave.device_config = coh901318_dma_set_runtimeconfig; + base->dma_slave.device_pause = coh901318_pause; + base->dma_slave.device_resume = coh901318_resume; + base->dma_slave.device_terminate_all = coh901318_terminate_all; base->dma_slave.dev = &pdev->dev; err = dma_async_device_register(&base->dma_slave); @@ -2737,7 +2717,10 @@ static int __init coh901318_probe(struct platform_device *pdev) base->dma_memcpy.device_prep_dma_memcpy = coh901318_prep_memcpy; base->dma_memcpy.device_tx_status = coh901318_tx_status; base->dma_memcpy.device_issue_pending = coh901318_issue_pending; - base->dma_memcpy.device_control = coh901318_control; + base->dma_memcpy.device_config = coh901318_dma_set_runtimeconfig; + base->dma_memcpy.device_pause = coh901318_pause; + base->dma_memcpy.device_resume = coh901318_resume; + base->dma_memcpy.device_terminate_all = coh901318_terminate_all; base->dma_memcpy.dev = &pdev->dev; /* * This controller can only access address at even 32bit boundaries, From 3b5a03a6646534f939e3a52c0c6ed1a6546c5104 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:10 +0100 Subject: [PATCH 017/360] dmaengine: cppi41: Split device_control Split the device_control callback of the TI CPPI41 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/cppi41.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index b743adf5646564..512cb8e2805e79 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -525,12 +525,6 @@ static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg( return &c->txd; } -static int cpp41_cfg_chan(struct cppi41_channel *c, - struct dma_slave_config *cfg) -{ - return 0; -} - static void cppi41_compute_td_desc(struct cppi41_desc *d) { d->pd0 = DESC_TYPE_TEARD << DESC_TYPE; @@ -647,28 +641,6 @@ static int cppi41_stop_chan(struct dma_chan *chan) return 0; } -static int cppi41_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct cppi41_channel *c = to_cpp41_chan(chan); - int ret; - - switch (cmd) { - case DMA_SLAVE_CONFIG: - ret = cpp41_cfg_chan(c, (struct dma_slave_config *) arg); - break; - - case DMA_TERMINATE_ALL: - ret = cppi41_stop_chan(chan); - break; - - default: - ret = -ENXIO; - break; - } - return ret; -} - static void cleanup_chans(struct cppi41_dd *cdd) { while (!list_empty(&cdd->ddev.channels)) { @@ -953,7 +925,7 @@ static int cppi41_dma_probe(struct platform_device *pdev) cdd->ddev.device_tx_status = cppi41_dma_tx_status; cdd->ddev.device_issue_pending = cppi41_dma_issue_pending; cdd->ddev.device_prep_slave_sg = cppi41_dma_prep_slave_sg; - cdd->ddev.device_control = cppi41_dma_control; + cdd->ddev.device_terminate_all = cppi41_stop_chan; cdd->ddev.dev = dev; INIT_LIST_HEAD(&cdd->ddev.channels); cpp41_dma_info.dma_cap = cdd->ddev.cap_mask; From 1d4c0b8cc37a3f1c9018ebdc808674ee13f1d489 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:11 +0100 Subject: [PATCH 018/360] dmaengine: jz4740: Split device_control Split the device_control callback of the JZ4740 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4740.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index bdeafeefa5f61b..4527a3ebeac446 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -210,7 +210,7 @@ static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst) } static int jz4740_dma_slave_config(struct dma_chan *c, - const struct dma_slave_config *config) + struct dma_slave_config *config) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); @@ -290,21 +290,6 @@ static int jz4740_dma_terminate_all(struct dma_chan *c) return 0; } -static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct dma_slave_config *config = (struct dma_slave_config *)arg; - - switch (cmd) { - case DMA_SLAVE_CONFIG: - return jz4740_dma_slave_config(chan, config); - case DMA_TERMINATE_ALL: - return jz4740_dma_terminate_all(chan); - default: - return -ENOSYS; - } -} - static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) { struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); @@ -561,7 +546,8 @@ static int jz4740_dma_probe(struct platform_device *pdev) dd->device_issue_pending = jz4740_dma_issue_pending; dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg; dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic; - dd->device_control = jz4740_dma_control; + dd->device_config = jz4740_dma_slave_config; + dd->device_terminate_all = jz4740_dma_terminate_all; dd->dev = &pdev->dev; INIT_LIST_HEAD(&dd->channels); From a4b0d348f60122eb45c50b3e79a8edaec6fee534 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:12 +0100 Subject: [PATCH 019/360] dmaengine: dw: Split device_control Split the device_control callback of the DesignWare DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 82 +++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 380478562b7d31..4bc307745ef1a6 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -955,8 +955,7 @@ static inline void convert_burst(u32 *maxburst) *maxburst = 0; } -static int -set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) +static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); @@ -973,16 +972,25 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return 0; } -static inline void dwc_chan_pause(struct dw_dma_chan *dwc) +static int dwc_pause(struct dma_chan *chan) { - u32 cfglo = channel_readl(dwc, CFG_LO); - unsigned int count = 20; /* timeout iterations */ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + unsigned long flags; + unsigned int count = 20; /* timeout iterations */ + u32 cfglo; + + spin_lock_irqsave(&dwc->lock, flags); + cfglo = channel_readl(dwc, CFG_LO); channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) udelay(2); dwc->paused = true; + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; } static inline void dwc_chan_resume(struct dw_dma_chan *dwc) @@ -994,53 +1002,48 @@ static inline void dwc_chan_resume(struct dw_dma_chan *dwc) dwc->paused = false; } -static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int dwc_resume(struct dma_chan *chan) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc, *_desc; unsigned long flags; - LIST_HEAD(list); - if (cmd == DMA_PAUSE) { - spin_lock_irqsave(&dwc->lock, flags); + if (!dwc->paused) + return 0; - dwc_chan_pause(dwc); + spin_lock_irqsave(&dwc->lock, flags); - spin_unlock_irqrestore(&dwc->lock, flags); - } else if (cmd == DMA_RESUME) { - if (!dwc->paused) - return 0; + dwc_chan_resume(dwc); - spin_lock_irqsave(&dwc->lock, flags); + spin_unlock_irqrestore(&dwc->lock, flags); - dwc_chan_resume(dwc); + return 0; +} - spin_unlock_irqrestore(&dwc->lock, flags); - } else if (cmd == DMA_TERMINATE_ALL) { - spin_lock_irqsave(&dwc->lock, flags); +static int dwc_terminate_all(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(list); + + spin_lock_irqsave(&dwc->lock, flags); - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - dwc_chan_disable(dw, dwc); + dwc_chan_disable(dw, dwc); - dwc_chan_resume(dwc); + dwc_chan_resume(dwc); - /* active_list entries will end up before queued entries */ - list_splice_init(&dwc->queue, &list); - list_splice_init(&dwc->active_list, &list); + /* active_list entries will end up before queued entries */ + list_splice_init(&dwc->queue, &list); + list_splice_init(&dwc->active_list, &list); - spin_unlock_irqrestore(&dwc->lock, flags); + spin_unlock_irqrestore(&dwc->lock, flags); - /* Flush all pending and queued descriptors */ - list_for_each_entry_safe(desc, _desc, &list, desc_node) - dwc_descriptor_complete(dwc, desc, false); - } else if (cmd == DMA_SLAVE_CONFIG) { - return set_runtime_config(chan, (struct dma_slave_config *)arg); - } else { - return -ENXIO; - } + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc, false); return 0; } @@ -1659,7 +1662,10 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; - dw->dma.device_control = dwc_control; + dw->dma.device_config = dwc_config; + dw->dma.device_pause = dwc_pause; + dw->dma.device_resume = dwc_resume; + dw->dma.device_terminate_all = dwc_terminate_all; dw->dma.device_tx_status = dwc_tx_status; dw->dma.device_issue_pending = dwc_issue_pending; From aa7c09b65beed65f007605853592342d9a615890 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:13 +0100 Subject: [PATCH 020/360] dmaengine: edma: Split device_control Split the device_control callback of the TI EDMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 50 ++++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 2b49fe6f04653a..8feb096786f814 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -244,8 +244,9 @@ static void edma_execute(struct edma_chan *echan) } } -static int edma_terminate_all(struct edma_chan *echan) +static int edma_terminate_all(struct dma_chan *chan) { + struct edma_chan *echan = to_edma_chan(chan); unsigned long flags; LIST_HEAD(head); @@ -273,9 +274,11 @@ static int edma_terminate_all(struct edma_chan *echan) return 0; } -static int edma_slave_config(struct edma_chan *echan, +static int edma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { + struct edma_chan *echan = to_edma_chan(chan); + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; @@ -285,8 +288,10 @@ static int edma_slave_config(struct edma_chan *echan, return 0; } -static int edma_dma_pause(struct edma_chan *echan) +static int edma_dma_pause(struct dma_chan *chan) { + struct edma_chan *echan = to_edma_chan(chan); + /* Pause/Resume only allowed with cyclic mode */ if (!echan->edesc || !echan->edesc->cyclic) return -EINVAL; @@ -295,8 +300,10 @@ static int edma_dma_pause(struct edma_chan *echan) return 0; } -static int edma_dma_resume(struct edma_chan *echan) +static int edma_dma_resume(struct dma_chan *chan) { + struct edma_chan *echan = to_edma_chan(chan); + /* Pause/Resume only allowed with cyclic mode */ if (!echan->edesc->cyclic) return -EINVAL; @@ -305,36 +312,6 @@ static int edma_dma_resume(struct edma_chan *echan) return 0; } -static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - int ret = 0; - struct dma_slave_config *config; - struct edma_chan *echan = to_edma_chan(chan); - - switch (cmd) { - case DMA_TERMINATE_ALL: - edma_terminate_all(echan); - break; - case DMA_SLAVE_CONFIG: - config = (struct dma_slave_config *)arg; - ret = edma_slave_config(echan, config); - break; - case DMA_PAUSE: - ret = edma_dma_pause(echan); - break; - - case DMA_RESUME: - ret = edma_dma_resume(echan); - break; - - default: - ret = -ENOSYS; - } - - return ret; -} - /* * A PaRAM set configuration abstraction used by other modes * @chan: Channel who's PaRAM set we're configuring @@ -1017,7 +994,10 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_free_chan_resources = edma_free_chan_resources; dma->device_issue_pending = edma_issue_pending; dma->device_tx_status = edma_tx_status; - dma->device_control = edma_control; + dma->device_config = edma_slave_config; + dma->device_pause = edma_dma_pause; + dma->device_resume = edma_dma_resume; + dma->device_terminate_all = edma_terminate_all; dma->device_slave_caps = edma_dma_device_slave_caps; dma->dev = dev; From 2258b67543eaed7b55afe62c84324a2bc7bd4c33 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:14 +0100 Subject: [PATCH 021/360] dmaengine: ep93xx: Split device_control Split the device_control callback of the Cirrus Logic EP93xx driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 41 +++++++--------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 7650470196c46c..a8bcbb5bc0edc6 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1164,13 +1164,14 @@ ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, /** * ep93xx_dma_terminate_all - terminate all transactions - * @edmac: channel + * @chan: channel * * Stops all DMA transactions. All descriptors are put back to the * @edmac->free_list and callbacks are _not_ called. */ -static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac) +static int ep93xx_dma_terminate_all(struct dma_chan *chan) { + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); struct ep93xx_dma_desc *desc, *_d; unsigned long flags; LIST_HEAD(list); @@ -1194,9 +1195,10 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac) return 0; } -static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac, +static int ep93xx_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *config) { + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); enum dma_slave_buswidth width; unsigned long flags; u32 addr, ctrl; @@ -1241,36 +1243,6 @@ static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac, return 0; } -/** - * ep93xx_dma_control - manipulate all pending operations on a channel - * @chan: channel - * @cmd: control command to perform - * @arg: optional argument - * - * Controls the channel. Function returns %0 in case of success or negative - * error in case of failure. - */ -static int ep93xx_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); - struct dma_slave_config *config; - - switch (cmd) { - case DMA_TERMINATE_ALL: - return ep93xx_dma_terminate_all(edmac); - - case DMA_SLAVE_CONFIG: - config = (struct dma_slave_config *)arg; - return ep93xx_dma_slave_config(edmac, config); - - default: - break; - } - - return -ENOSYS; -} - /** * ep93xx_dma_tx_status - check if a transaction is completed * @chan: channel @@ -1352,7 +1324,8 @@ static int __init ep93xx_dma_probe(struct platform_device *pdev) dma_dev->device_free_chan_resources = ep93xx_dma_free_chan_resources; dma_dev->device_prep_slave_sg = ep93xx_dma_prep_slave_sg; dma_dev->device_prep_dma_cyclic = ep93xx_dma_prep_dma_cyclic; - dma_dev->device_control = ep93xx_dma_control; + dma_dev->device_config = ep93xx_dma_slave_config; + dma_dev->device_terminate_all = ep93xx_dma_terminate_all; dma_dev->device_issue_pending = ep93xx_dma_issue_pending; dma_dev->device_tx_status = ep93xx_dma_tx_status; From d80f381f321ab739e8a702ecc882560a5838b1fb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:15 +0100 Subject: [PATCH 022/360] dmaengine: fsl-edma: Split device_control Split the device_control callback of the Freescale EDMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma.c | 106 ++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index ce6e960b78a7a6..d96a4aff2f4ee2 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -289,62 +289,69 @@ static void fsl_edma_free_desc(struct virt_dma_desc *vdesc) kfree(fsl_desc); } -static int fsl_edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int fsl_edma_terminate_all(struct dma_chan *chan) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - struct dma_slave_config *cfg = (void *)arg; unsigned long flags; LIST_HEAD(head); - switch (cmd) { - case DMA_TERMINATE_ALL: - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_chan->edesc = NULL; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + return 0; +} + +static int fsl_edma_pause(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { fsl_edma_disable_request(fsl_chan); - fsl_chan->edesc = NULL; - vchan_get_all_descriptors(&fsl_chan->vchan, &head); - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - vchan_dma_desc_free_list(&fsl_chan->vchan, &head); - return 0; - - case DMA_SLAVE_CONFIG: - fsl_chan->fsc.dir = cfg->direction; - if (cfg->direction == DMA_DEV_TO_MEM) { - fsl_chan->fsc.dev_addr = cfg->src_addr; - fsl_chan->fsc.addr_width = cfg->src_addr_width; - fsl_chan->fsc.burst = cfg->src_maxburst; - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); - } else if (cfg->direction == DMA_MEM_TO_DEV) { - fsl_chan->fsc.dev_addr = cfg->dst_addr; - fsl_chan->fsc.addr_width = cfg->dst_addr_width; - fsl_chan->fsc.burst = cfg->dst_maxburst; - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); - } else { - return -EINVAL; - } - return 0; + fsl_chan->status = DMA_PAUSED; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; +} - case DMA_PAUSE: - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - if (fsl_chan->edesc) { - fsl_edma_disable_request(fsl_chan); - fsl_chan->status = DMA_PAUSED; - } - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - return 0; - - case DMA_RESUME: - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - if (fsl_chan->edesc) { - fsl_edma_enable_request(fsl_chan); - fsl_chan->status = DMA_IN_PROGRESS; - } - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - return 0; +static int fsl_edma_resume(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; - default: - return -ENXIO; + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; +} + +static int fsl_edma_slave_config(struct dma_chan *chan, + struct dma_slave_config *cfg) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + + fsl_chan->fsc.dir = cfg->direction; + if (cfg->direction == DMA_DEV_TO_MEM) { + fsl_chan->fsc.dev_addr = cfg->src_addr; + fsl_chan->fsc.addr_width = cfg->src_addr_width; + fsl_chan->fsc.burst = cfg->src_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + fsl_chan->fsc.dev_addr = cfg->dst_addr; + fsl_chan->fsc.addr_width = cfg->dst_addr_width; + fsl_chan->fsc.burst = cfg->dst_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); + } else { + return -EINVAL; + } + return 0; } static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, @@ -917,7 +924,10 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status; fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic; - fsl_edma->dma_dev.device_control = fsl_edma_control; + fsl_edma->dma_dev.device_config = fsl_edma_slave_config; + fsl_edma->dma_dev.device_pause = fsl_edma_pause; + fsl_edma->dma_dev.device_resume = fsl_edma_resume; + fsl_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps; From 502c2ef26dba04128af260de0ca3e2940e57fc7a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:16 +0100 Subject: [PATCH 023/360] dmaengine: imx: Split device_control Split the device_control callback of the Freescale IMX DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 103 +++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 10bbc0a675b07c..02d1f73bd12b09 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -669,69 +669,67 @@ static void imxdma_tasklet(unsigned long data) } -static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int imxdma_terminate_all(struct dma_chan *chan) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); - struct dma_slave_config *dmaengine_cfg = (void *)arg; struct imxdma_engine *imxdma = imxdmac->imxdma; unsigned long flags; - unsigned int mode = 0; - - switch (cmd) { - case DMA_TERMINATE_ALL: - imxdma_disable_hw(imxdmac); - spin_lock_irqsave(&imxdma->lock, flags); - list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); - list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); - spin_unlock_irqrestore(&imxdma->lock, flags); - return 0; - case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { - imxdmac->per_address = dmaengine_cfg->src_addr; - imxdmac->watermark_level = dmaengine_cfg->src_maxburst; - imxdmac->word_size = dmaengine_cfg->src_addr_width; - } else { - imxdmac->per_address = dmaengine_cfg->dst_addr; - imxdmac->watermark_level = dmaengine_cfg->dst_maxburst; - imxdmac->word_size = dmaengine_cfg->dst_addr_width; - } - - switch (imxdmac->word_size) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - mode = IMX_DMA_MEMSIZE_8; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - mode = IMX_DMA_MEMSIZE_16; - break; - default: - case DMA_SLAVE_BUSWIDTH_4_BYTES: - mode = IMX_DMA_MEMSIZE_32; - break; - } + imxdma_disable_hw(imxdmac); - imxdmac->hw_chaining = 0; + spin_lock_irqsave(&imxdma->lock, flags); + list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free); + list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free); + spin_unlock_irqrestore(&imxdma->lock, flags); + return 0; +} - imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) | - ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | - CCR_REN; - imxdmac->ccr_to_device = - (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) | - ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN; - imx_dmav1_writel(imxdma, imxdmac->dma_request, - DMA_RSSR(imxdmac->channel)); +static int imxdma_config(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg) +{ + struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + struct imxdma_engine *imxdma = imxdmac->imxdma; + unsigned int mode = 0; - /* Set burst length */ - imx_dmav1_writel(imxdma, imxdmac->watermark_level * - imxdmac->word_size, DMA_BLR(imxdmac->channel)); + if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + imxdmac->per_address = dmaengine_cfg->src_addr; + imxdmac->watermark_level = dmaengine_cfg->src_maxburst; + imxdmac->word_size = dmaengine_cfg->src_addr_width; + } else { + imxdmac->per_address = dmaengine_cfg->dst_addr; + imxdmac->watermark_level = dmaengine_cfg->dst_maxburst; + imxdmac->word_size = dmaengine_cfg->dst_addr_width; + } - return 0; + switch (imxdmac->word_size) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + mode = IMX_DMA_MEMSIZE_8; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + mode = IMX_DMA_MEMSIZE_16; + break; default: - return -ENOSYS; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + mode = IMX_DMA_MEMSIZE_32; + break; } - return -EINVAL; + imxdmac->hw_chaining = 0; + + imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) | + ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | + CCR_REN; + imxdmac->ccr_to_device = + (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) | + ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN; + imx_dmav1_writel(imxdma, imxdmac->dma_request, + DMA_RSSR(imxdmac->channel)); + + /* Set burst length */ + imx_dmav1_writel(imxdma, imxdmac->watermark_level * + imxdmac->word_size, DMA_BLR(imxdmac->channel)); + + return 0; } static enum dma_status imxdma_tx_status(struct dma_chan *chan, @@ -1184,7 +1182,8 @@ static int __init imxdma_probe(struct platform_device *pdev) imxdma->dma_device.device_prep_dma_cyclic = imxdma_prep_dma_cyclic; imxdma->dma_device.device_prep_dma_memcpy = imxdma_prep_dma_memcpy; imxdma->dma_device.device_prep_interleaved_dma = imxdma_prep_dma_interleaved; - imxdma->dma_device.device_control = imxdma_control; + imxdma->dma_device.device_config = imxdma_config; + imxdma->dma_device.device_terminate_all = imxdma_terminate_all; imxdma->dma_device.device_issue_pending = imxdma_issue_pending; platform_set_drvdata(pdev, imxdma); From 7b350ab0fa338dae86a62d83efee21fab39fcdc6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:17 +0100 Subject: [PATCH 024/360] dmaengine: imx-sdma: Split device_control Split the device_control callback of the Freescale IMX SDMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 66 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index d0df198f62e936..1748a4bd475f1b 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -830,20 +830,29 @@ static int sdma_load_context(struct sdma_channel *sdmac) return ret; } -static void sdma_disable_channel(struct sdma_channel *sdmac) +static struct sdma_channel *to_sdma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct sdma_channel, chan); +} + +static int sdma_disable_channel(struct dma_chan *chan) { + struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP); sdmac->status = DMA_ERROR; + + return 0; } -static int sdma_config_channel(struct sdma_channel *sdmac) +static int sdma_config_channel(struct dma_chan *chan) { + struct sdma_channel *sdmac = to_sdma_chan(chan); int ret; - sdma_disable_channel(sdmac); + sdma_disable_channel(chan); sdmac->event_mask[0] = 0; sdmac->event_mask[1] = 0; @@ -935,11 +944,6 @@ static int sdma_request_channel(struct sdma_channel *sdmac) return ret; } -static struct sdma_channel *to_sdma_chan(struct dma_chan *chan) -{ - return container_of(chan, struct sdma_channel, chan); -} - static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx) { unsigned long flags; @@ -1004,7 +1008,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan) struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; - sdma_disable_channel(sdmac); + sdma_disable_channel(chan); if (sdmac->event_id0) sdma_event_disable(sdmac, sdmac->event_id0); @@ -1203,35 +1207,24 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( return NULL; } -static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int sdma_config(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg) { struct sdma_channel *sdmac = to_sdma_chan(chan); - struct dma_slave_config *dmaengine_cfg = (void *)arg; - - switch (cmd) { - case DMA_TERMINATE_ALL: - sdma_disable_channel(sdmac); - return 0; - case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { - sdmac->per_address = dmaengine_cfg->src_addr; - sdmac->watermark_level = dmaengine_cfg->src_maxburst * - dmaengine_cfg->src_addr_width; - sdmac->word_size = dmaengine_cfg->src_addr_width; - } else { - sdmac->per_address = dmaengine_cfg->dst_addr; - sdmac->watermark_level = dmaengine_cfg->dst_maxburst * - dmaengine_cfg->dst_addr_width; - sdmac->word_size = dmaengine_cfg->dst_addr_width; - } - sdmac->direction = dmaengine_cfg->direction; - return sdma_config_channel(sdmac); - default: - return -ENOSYS; - } - return -EINVAL; + if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + sdmac->per_address = dmaengine_cfg->src_addr; + sdmac->watermark_level = dmaengine_cfg->src_maxburst * + dmaengine_cfg->src_addr_width; + sdmac->word_size = dmaengine_cfg->src_addr_width; + } else { + sdmac->per_address = dmaengine_cfg->dst_addr; + sdmac->watermark_level = dmaengine_cfg->dst_maxburst * + dmaengine_cfg->dst_addr_width; + sdmac->word_size = dmaengine_cfg->dst_addr_width; + } + sdmac->direction = dmaengine_cfg->direction; + return sdma_config_channel(chan); } static enum dma_status sdma_tx_status(struct dma_chan *chan, @@ -1600,7 +1593,8 @@ static int sdma_probe(struct platform_device *pdev) sdma->dma_device.device_tx_status = sdma_tx_status; sdma->dma_device.device_prep_slave_sg = sdma_prep_slave_sg; sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic; - sdma->dma_device.device_control = sdma_control; + sdma->dma_device.device_config = sdma_config; + sdma->dma_device.device_terminate_all = sdma_disable_channel; sdma->dma_device.device_issue_pending = sdma_issue_pending; sdma->dma_device.dev->dma_parms = &sdma->dma_parms; dma_set_max_seg_size(sdma->dma_device.dev, 65535); From 71b5bd2a9cca544bfa67699066b299716c8b8e89 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:18 +0100 Subject: [PATCH 025/360] dmaengine: intel-mid-dma: Split device_control Split the device_control callback of the Intel MID DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/intel_mid_dma.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 1aab8130efa1c7..5aaead9b56f738 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -492,10 +492,10 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, return ret; } -static int dma_slave_control(struct dma_chan *chan, unsigned long arg) +static int intel_mid_dma_config(struct dma_chan *chan, + struct dma_slave_config *slave) { struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - struct dma_slave_config *slave = (struct dma_slave_config *)arg; struct intel_mid_dma_slave *mid_slave; BUG_ON(!midc); @@ -509,28 +509,14 @@ static int dma_slave_control(struct dma_chan *chan, unsigned long arg) midc->mid_slave = mid_slave; return 0; } -/** - * intel_mid_dma_device_control - DMA device control - * @chan: chan for DMA control - * @cmd: control cmd - * @arg: cmd arg value - * - * Perform DMA control command - */ -static int intel_mid_dma_device_control(struct dma_chan *chan, - enum dma_ctrl_cmd cmd, unsigned long arg) + +static int intel_mid_dma_terminate_all(struct dma_chan *chan) { struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); struct middma_device *mid = to_middma_device(chan->device); struct intel_mid_dma_desc *desc, *_desc; union intel_mid_dma_cfg_lo cfg_lo; - if (cmd == DMA_SLAVE_CONFIG) - return dma_slave_control(chan, arg); - - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; - spin_lock_bh(&midc->lock); if (midc->busy == false) { spin_unlock_bh(&midc->lock); @@ -1148,7 +1134,8 @@ static int mid_setup_dma(struct pci_dev *pdev) dma->common.device_prep_dma_memcpy = intel_mid_dma_prep_memcpy; dma->common.device_issue_pending = intel_mid_dma_issue_pending; dma->common.device_prep_slave_sg = intel_mid_dma_prep_slave_sg; - dma->common.device_control = intel_mid_dma_device_control; + dma->common.device_config = intel_mid_dma_config; + dma->common.device_terminate_all = intel_mid_dma_terminate_all; /*enable dma cntrl*/ iowrite32(REG_BIT0, dma->dma_base + DMA_CFG); From 701c1edbb4dc895d018312a7e4e5f2c673bf155c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:19 +0100 Subject: [PATCH 026/360] dmaengine: ipu-idmac: Split device_control Split the device_control callback of the IPU IDMAC driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/ipu/ipu_idmac.c | 96 ++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index c2b017ad139d3e..b54f62de92323e 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1398,76 +1398,81 @@ static void idmac_issue_pending(struct dma_chan *chan) */ } -static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int idmac_pause(struct dma_chan *chan) { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac *idmac = to_idmac(chan->device); struct ipu *ipu = to_ipu(idmac); struct list_head *list, *tmp; unsigned long flags; - int i; - switch (cmd) { - case DMA_PAUSE: - spin_lock_irqsave(&ipu->lock, flags); - ipu_ic_disable_task(ipu, chan->chan_id); + mutex_lock(&ichan->chan_mutex); - /* Return all descriptors into "prepared" state */ - list_for_each_safe(list, tmp, &ichan->queue) - list_del_init(list); + spin_lock_irqsave(&ipu->lock, flags); + ipu_ic_disable_task(ipu, chan->chan_id); - ichan->sg[0] = NULL; - ichan->sg[1] = NULL; + /* Return all descriptors into "prepared" state */ + list_for_each_safe(list, tmp, &ichan->queue) + list_del_init(list); - spin_unlock_irqrestore(&ipu->lock, flags); + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; - ichan->status = IPU_CHANNEL_INITIALIZED; - break; - case DMA_TERMINATE_ALL: - ipu_disable_channel(idmac, ichan, - ichan->status >= IPU_CHANNEL_ENABLED); + spin_unlock_irqrestore(&ipu->lock, flags); - tasklet_disable(&ipu->tasklet); + ichan->status = IPU_CHANNEL_INITIALIZED; - /* ichan->queue is modified in ISR, have to spinlock */ - spin_lock_irqsave(&ichan->lock, flags); - list_splice_init(&ichan->queue, &ichan->free_list); + mutex_unlock(&ichan->chan_mutex); - if (ichan->desc) - for (i = 0; i < ichan->n_tx_desc; i++) { - struct idmac_tx_desc *desc = ichan->desc + i; - if (list_empty(&desc->list)) - /* Descriptor was prepared, but not submitted */ - list_add(&desc->list, &ichan->free_list); + return 0; +} - async_tx_clear_ack(&desc->txd); - } +static int __idmac_terminate_all(struct dma_chan *chan) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + struct idmac *idmac = to_idmac(chan->device); + struct ipu *ipu = to_ipu(idmac); + unsigned long flags; + int i; - ichan->sg[0] = NULL; - ichan->sg[1] = NULL; - spin_unlock_irqrestore(&ichan->lock, flags); + ipu_disable_channel(idmac, ichan, + ichan->status >= IPU_CHANNEL_ENABLED); - tasklet_enable(&ipu->tasklet); + tasklet_disable(&ipu->tasklet); - ichan->status = IPU_CHANNEL_INITIALIZED; - break; - default: - return -ENOSYS; - } + /* ichan->queue is modified in ISR, have to spinlock */ + spin_lock_irqsave(&ichan->lock, flags); + list_splice_init(&ichan->queue, &ichan->free_list); + + if (ichan->desc) + for (i = 0; i < ichan->n_tx_desc; i++) { + struct idmac_tx_desc *desc = ichan->desc + i; + if (list_empty(&desc->list)) + /* Descriptor was prepared, but not submitted */ + list_add(&desc->list, &ichan->free_list); + + async_tx_clear_ack(&desc->txd); + } + + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; + spin_unlock_irqrestore(&ichan->lock, flags); + + tasklet_enable(&ipu->tasklet); + + ichan->status = IPU_CHANNEL_INITIALIZED; return 0; } -static int idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int idmac_terminate_all(struct dma_chan *chan) { struct idmac_channel *ichan = to_idmac_chan(chan); int ret; mutex_lock(&ichan->chan_mutex); - ret = __idmac_control(chan, cmd, arg); + ret = __idmac_terminate_all(chan); mutex_unlock(&ichan->chan_mutex); @@ -1568,7 +1573,7 @@ static void idmac_free_chan_resources(struct dma_chan *chan) mutex_lock(&ichan->chan_mutex); - __idmac_control(chan, DMA_TERMINATE_ALL, 0); + __idmac_terminate_all(chan); if (ichan->status > IPU_CHANNEL_FREE) { #ifdef DEBUG @@ -1622,7 +1627,8 @@ static int __init ipu_idmac_init(struct ipu *ipu) /* Compulsory for DMA_SLAVE fields */ dma->device_prep_slave_sg = idmac_prep_slave_sg; - dma->device_control = idmac_control; + dma->device_pause = idmac_pause; + dma->device_terminate_all = idmac_terminate_all; INIT_LIST_HEAD(&dma->channels); for (i = 0; i < IPU_CHANNELS_NUM; i++) { @@ -1655,7 +1661,7 @@ static void ipu_idmac_exit(struct ipu *ipu) for (i = 0; i < IPU_CHANNELS_NUM; i++) { struct idmac_channel *ichan = ipu->channel + i; - idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0); + idmac_terminate_all(&ichan->dma_chan); } dma_async_device_unregister(&idmac->dma); From db08425ebd51f3b4c73b0698ca3b0173ebd106be Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:20 +0100 Subject: [PATCH 027/360] dmaengine: k3: Split device_control Split the device_control callback of the Hisilicon K3 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 197 ++++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 90 deletions(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index a1de14ab2c511e..49be7f687c4cd1 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -441,7 +441,7 @@ static struct dma_async_tx_descriptor *k3_dma_prep_memcpy( num = 0; if (!c->ccfg) { - /* default is memtomem, without calling device_control */ + /* default is memtomem, without calling device_config */ c->ccfg = CX_CFG_SRCINCR | CX_CFG_DSTINCR | CX_CFG_EN; c->ccfg |= (0xf << 20) | (0xf << 24); /* burst = 16 */ c->ccfg |= (0x3 << 12) | (0x3 << 16); /* width = 64 bit */ @@ -523,112 +523,126 @@ static struct dma_async_tx_descriptor *k3_dma_prep_slave_sg( return vchan_tx_prep(&c->vc, &ds->vd, flags); } -static int k3_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int k3_dma_config(struct dma_chan *chan, + struct dma_slave_config *cfg) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + u32 maxburst = 0, val = 0; + enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + + if (cfg == NULL) + return -EINVAL; + c->dir = cfg->direction; + if (c->dir == DMA_DEV_TO_MEM) { + c->ccfg = CX_CFG_DSTINCR; + c->dev_addr = cfg->src_addr; + maxburst = cfg->src_maxburst; + width = cfg->src_addr_width; + } else if (c->dir == DMA_MEM_TO_DEV) { + c->ccfg = CX_CFG_SRCINCR; + c->dev_addr = cfg->dst_addr; + maxburst = cfg->dst_maxburst; + width = cfg->dst_addr_width; + } + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + case DMA_SLAVE_BUSWIDTH_2_BYTES: + case DMA_SLAVE_BUSWIDTH_4_BYTES: + case DMA_SLAVE_BUSWIDTH_8_BYTES: + val = __ffs(width); + break; + default: + val = 3; + break; + } + c->ccfg |= (val << 12) | (val << 16); + + if ((maxburst == 0) || (maxburst > 16)) + val = 16; + else + val = maxburst - 1; + c->ccfg |= (val << 20) | (val << 24); + c->ccfg |= CX_CFG_MEM2PER | CX_CFG_EN; + + /* specific request line */ + c->ccfg |= c->vc.chan.chan_id << 4; + + return 0; +} + +static int k3_dma_terminate_all(struct dma_chan *chan) { struct k3_dma_chan *c = to_k3_chan(chan); struct k3_dma_dev *d = to_k3_dma(chan->device); - struct dma_slave_config *cfg = (void *)arg; struct k3_dma_phy *p = c->phy; unsigned long flags; - u32 maxburst = 0, val = 0; - enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; LIST_HEAD(head); - switch (cmd) { - case DMA_SLAVE_CONFIG: - if (cfg == NULL) - return -EINVAL; - c->dir = cfg->direction; - if (c->dir == DMA_DEV_TO_MEM) { - c->ccfg = CX_CFG_DSTINCR; - c->dev_addr = cfg->src_addr; - maxburst = cfg->src_maxburst; - width = cfg->src_addr_width; - } else if (c->dir == DMA_MEM_TO_DEV) { - c->ccfg = CX_CFG_SRCINCR; - c->dev_addr = cfg->dst_addr; - maxburst = cfg->dst_maxburst; - width = cfg->dst_addr_width; - } - switch (width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - case DMA_SLAVE_BUSWIDTH_2_BYTES: - case DMA_SLAVE_BUSWIDTH_4_BYTES: - case DMA_SLAVE_BUSWIDTH_8_BYTES: - val = __ffs(width); - break; - default: - val = 3; - break; - } - c->ccfg |= (val << 12) | (val << 16); + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); - if ((maxburst == 0) || (maxburst > 16)) - val = 16; - else - val = maxburst - 1; - c->ccfg |= (val << 20) | (val << 24); - c->ccfg |= CX_CFG_MEM2PER | CX_CFG_EN; + /* Prevent this channel being scheduled */ + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); - /* specific request line */ - c->ccfg |= c->vc.chan.chan_id << 4; - break; + /* Clear the tx descriptor lists */ + spin_lock_irqsave(&c->vc.lock, flags); + vchan_get_all_descriptors(&c->vc, &head); + if (p) { + /* vchan is assigned to a pchan - stop the channel */ + k3_dma_terminate_chan(p, d); + c->phy = NULL; + p->vchan = NULL; + p->ds_run = p->ds_done = NULL; + } + spin_unlock_irqrestore(&c->vc.lock, flags); + vchan_dma_desc_free_list(&c->vc, &head); - case DMA_TERMINATE_ALL: - dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); + return 0; +} - /* Prevent this channel being scheduled */ - spin_lock(&d->lock); - list_del_init(&c->node); - spin_unlock(&d->lock); +static int k3_dma_pause(struct dma_chan *chan) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_dev *d = to_k3_dma(chan->device); + struct k3_dma_phy *p = c->phy; - /* Clear the tx descriptor lists */ - spin_lock_irqsave(&c->vc.lock, flags); - vchan_get_all_descriptors(&c->vc, &head); + dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); + if (c->status == DMA_IN_PROGRESS) { + c->status = DMA_PAUSED; if (p) { - /* vchan is assigned to a pchan - stop the channel */ - k3_dma_terminate_chan(p, d); - c->phy = NULL; - p->vchan = NULL; - p->ds_run = p->ds_done = NULL; + k3_dma_pause_dma(p, false); + } else { + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); } - spin_unlock_irqrestore(&c->vc.lock, flags); - vchan_dma_desc_free_list(&c->vc, &head); - break; + } - case DMA_PAUSE: - dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); - if (c->status == DMA_IN_PROGRESS) { - c->status = DMA_PAUSED; - if (p) { - k3_dma_pause_dma(p, false); - } else { - spin_lock(&d->lock); - list_del_init(&c->node); - spin_unlock(&d->lock); - } - } - break; + return 0; +} - case DMA_RESUME: - dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); - spin_lock_irqsave(&c->vc.lock, flags); - if (c->status == DMA_PAUSED) { - c->status = DMA_IN_PROGRESS; - if (p) { - k3_dma_pause_dma(p, true); - } else if (!list_empty(&c->vc.desc_issued)) { - spin_lock(&d->lock); - list_add_tail(&c->node, &d->chan_pending); - spin_unlock(&d->lock); - } +static int k3_dma_resume(struct dma_chan *chan) +{ + struct k3_dma_chan *c = to_k3_chan(chan); + struct k3_dma_dev *d = to_k3_dma(chan->device); + struct k3_dma_phy *p = c->phy; + unsigned long flags; + + dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); + spin_lock_irqsave(&c->vc.lock, flags); + if (c->status == DMA_PAUSED) { + c->status = DMA_IN_PROGRESS; + if (p) { + k3_dma_pause_dma(p, true); + } else if (!list_empty(&c->vc.desc_issued)) { + spin_lock(&d->lock); + list_add_tail(&c->node, &d->chan_pending); + spin_unlock(&d->lock); } - spin_unlock_irqrestore(&c->vc.lock, flags); - break; - default: - return -ENXIO; } + spin_unlock_irqrestore(&c->vc.lock, flags); + return 0; } @@ -720,7 +734,10 @@ static int k3_dma_probe(struct platform_device *op) d->slave.device_prep_dma_memcpy = k3_dma_prep_memcpy; d->slave.device_prep_slave_sg = k3_dma_prep_slave_sg; d->slave.device_issue_pending = k3_dma_issue_pending; - d->slave.device_control = k3_dma_control; + d->slave.device_config = k3_dma_config; + d->slave.device_pause = k3_dma_pause; + d->slave.device_resume = k3_dma_resume; + d->slave.device_terminate_all = k3_dma_terminate_all; d->slave.copy_align = DMA_ALIGN; /* init virtual channel */ From a0abd6719b73c995eac23aa8835a79f67681e872 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:21 +0100 Subject: [PATCH 028/360] dmaengine: mmp-pdma: Split device_control Split the device_control callback of the Marvell MMP PDMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 109 +++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 8b8952f35e6c90..8926f271904e45 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -683,68 +683,70 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, return NULL; } -static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int mmp_pdma_config(struct dma_chan *dchan, + struct dma_slave_config *cfg) { struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - struct dma_slave_config *cfg = (void *)arg; - unsigned long flags; u32 maxburst = 0, addr = 0; enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; if (!dchan) return -EINVAL; - switch (cmd) { - case DMA_TERMINATE_ALL: - disable_chan(chan->phy); - mmp_pdma_free_phy(chan); - spin_lock_irqsave(&chan->desc_lock, flags); - mmp_pdma_free_desc_list(chan, &chan->chain_pending); - mmp_pdma_free_desc_list(chan, &chan->chain_running); - spin_unlock_irqrestore(&chan->desc_lock, flags); - chan->idle = true; - break; - case DMA_SLAVE_CONFIG: - if (cfg->direction == DMA_DEV_TO_MEM) { - chan->dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; - maxburst = cfg->src_maxburst; - width = cfg->src_addr_width; - addr = cfg->src_addr; - } else if (cfg->direction == DMA_MEM_TO_DEV) { - chan->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; - maxburst = cfg->dst_maxburst; - width = cfg->dst_addr_width; - addr = cfg->dst_addr; - } - - if (width == DMA_SLAVE_BUSWIDTH_1_BYTE) - chan->dcmd |= DCMD_WIDTH1; - else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) - chan->dcmd |= DCMD_WIDTH2; - else if (width == DMA_SLAVE_BUSWIDTH_4_BYTES) - chan->dcmd |= DCMD_WIDTH4; - - if (maxburst == 8) - chan->dcmd |= DCMD_BURST8; - else if (maxburst == 16) - chan->dcmd |= DCMD_BURST16; - else if (maxburst == 32) - chan->dcmd |= DCMD_BURST32; - - chan->dir = cfg->direction; - chan->dev_addr = addr; - /* FIXME: drivers should be ported over to use the filter - * function. Once that's done, the following two lines can - * be removed. - */ - if (cfg->slave_id) - chan->drcmr = cfg->slave_id; - break; - default: - return -ENOSYS; + if (cfg->direction == DMA_DEV_TO_MEM) { + chan->dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; + maxburst = cfg->src_maxburst; + width = cfg->src_addr_width; + addr = cfg->src_addr; + } else if (cfg->direction == DMA_MEM_TO_DEV) { + chan->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + maxburst = cfg->dst_maxburst; + width = cfg->dst_addr_width; + addr = cfg->dst_addr; } + if (width == DMA_SLAVE_BUSWIDTH_1_BYTE) + chan->dcmd |= DCMD_WIDTH1; + else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) + chan->dcmd |= DCMD_WIDTH2; + else if (width == DMA_SLAVE_BUSWIDTH_4_BYTES) + chan->dcmd |= DCMD_WIDTH4; + + if (maxburst == 8) + chan->dcmd |= DCMD_BURST8; + else if (maxburst == 16) + chan->dcmd |= DCMD_BURST16; + else if (maxburst == 32) + chan->dcmd |= DCMD_BURST32; + + chan->dir = cfg->direction; + chan->dev_addr = addr; + /* FIXME: drivers should be ported over to use the filter + * function. Once that's done, the following two lines can + * be removed. + */ + if (cfg->slave_id) + chan->drcmr = cfg->slave_id; + + return 0; +} + +static int mmp_pdma_terminate_all(struct dma_chan *dchan) +{ + struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); + unsigned long flags; + + if (!dchan) + return -EINVAL; + + disable_chan(chan->phy); + mmp_pdma_free_phy(chan); + spin_lock_irqsave(&chan->desc_lock, flags); + mmp_pdma_free_desc_list(chan, &chan->chain_pending); + mmp_pdma_free_desc_list(chan, &chan->chain_running); + spin_unlock_irqrestore(&chan->desc_lock, flags); + chan->idle = true; + return 0; } @@ -1061,7 +1063,8 @@ static int mmp_pdma_probe(struct platform_device *op) pdev->device.device_prep_slave_sg = mmp_pdma_prep_slave_sg; pdev->device.device_prep_dma_cyclic = mmp_pdma_prep_dma_cyclic; pdev->device.device_issue_pending = mmp_pdma_issue_pending; - pdev->device.device_control = mmp_pdma_control; + pdev->device.device_config = mmp_pdma_config; + pdev->device.device_terminate_all = mmp_pdma_terminate_all; pdev->device.copy_align = PDMA_ALIGNMENT; if (pdev->dev->coherent_dma_mask) From f43a6fd400ba66b59bda41e72db2ae1bd5cd549b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:22 +0100 Subject: [PATCH 029/360] dmaengine: mmp-tdma: Split device_control Split the device_control callback of the Marvell MMP TDMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 82 ++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index bfb46957c3dcc8..a8a79b1763d560 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -164,33 +164,46 @@ static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac) tdmac->status = DMA_IN_PROGRESS; } -static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac) +static int mmp_tdma_disable_chan(struct dma_chan *chan) { + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, tdmac->reg_base + TDCR); tdmac->status = DMA_COMPLETE; + + return 0; } -static void mmp_tdma_resume_chan(struct mmp_tdma_chan *tdmac) +static int mmp_tdma_resume_chan(struct dma_chan *chan) { + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN, tdmac->reg_base + TDCR); tdmac->status = DMA_IN_PROGRESS; + + return 0; } -static void mmp_tdma_pause_chan(struct mmp_tdma_chan *tdmac) +static int mmp_tdma_pause_chan(struct dma_chan *chan) { + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, tdmac->reg_base + TDCR); tdmac->status = DMA_PAUSED; + + return 0; } -static int mmp_tdma_config_chan(struct mmp_tdma_chan *tdmac) +static int mmp_tdma_config_chan(struct dma_chan *chan) { + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); unsigned int tdcr = 0; - mmp_tdma_disable_chan(tdmac); + mmp_tdma_disable_chan(chan); if (tdmac->dir == DMA_MEM_TO_DEV) tdcr = TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC; @@ -452,42 +465,32 @@ static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( return NULL; } -static int mmp_tdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int mmp_tdma_terminate_all(struct dma_chan *chan) { struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); - struct dma_slave_config *dmaengine_cfg = (void *)arg; - int ret = 0; - - switch (cmd) { - case DMA_TERMINATE_ALL: - mmp_tdma_disable_chan(tdmac); - /* disable interrupt */ - mmp_tdma_enable_irq(tdmac, false); - break; - case DMA_PAUSE: - mmp_tdma_pause_chan(tdmac); - break; - case DMA_RESUME: - mmp_tdma_resume_chan(tdmac); - break; - case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { - tdmac->dev_addr = dmaengine_cfg->src_addr; - tdmac->burst_sz = dmaengine_cfg->src_maxburst; - tdmac->buswidth = dmaengine_cfg->src_addr_width; - } else { - tdmac->dev_addr = dmaengine_cfg->dst_addr; - tdmac->burst_sz = dmaengine_cfg->dst_maxburst; - tdmac->buswidth = dmaengine_cfg->dst_addr_width; - } - tdmac->dir = dmaengine_cfg->direction; - return mmp_tdma_config_chan(tdmac); - default: - ret = -ENOSYS; + + mmp_tdma_disable_chan(chan); + /* disable interrupt */ + mmp_tdma_enable_irq(tdmac, false); +} + +static int mmp_tdma_config(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + + if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + tdmac->dev_addr = dmaengine_cfg->src_addr; + tdmac->burst_sz = dmaengine_cfg->src_maxburst; + tdmac->buswidth = dmaengine_cfg->src_addr_width; + } else { + tdmac->dev_addr = dmaengine_cfg->dst_addr; + tdmac->burst_sz = dmaengine_cfg->dst_maxburst; + tdmac->buswidth = dmaengine_cfg->dst_addr_width; } + tdmac->dir = dmaengine_cfg->direction; - return ret; + return mmp_tdma_config_chan(chan); } static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan, @@ -668,7 +671,10 @@ static int mmp_tdma_probe(struct platform_device *pdev) tdev->device.device_prep_dma_cyclic = mmp_tdma_prep_dma_cyclic; tdev->device.device_tx_status = mmp_tdma_tx_status; tdev->device.device_issue_pending = mmp_tdma_issue_pending; - tdev->device.device_control = mmp_tdma_control; + tdev->device.device_config = mmp_tdma_config; + tdev->device.device_pause = mmp_tdma_pause_chan; + tdev->device.device_resume = mmp_tdma_resume_chan; + tdev->device.device_terminate_all = mmp_tdma_terminate_all; tdev->device.copy_align = TDMA_ALIGNMENT; dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); From ac850cc7da0c94215b41ac8d6e780fd60c93f983 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:23 +0100 Subject: [PATCH 030/360] dmaengine: moxart: Split device_control Split the device_control callback of the Moxart DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/moxart-dma.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c index 53032bac06e07f..15cab7d7952591 100644 --- a/drivers/dma/moxart-dma.c +++ b/drivers/dma/moxart-dma.c @@ -263,28 +263,6 @@ static int moxart_slave_config(struct dma_chan *chan, return 0; } -static int moxart_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - int ret = 0; - - switch (cmd) { - case DMA_PAUSE: - case DMA_RESUME: - return -EINVAL; - case DMA_TERMINATE_ALL: - moxart_terminate_all(chan); - break; - case DMA_SLAVE_CONFIG: - ret = moxart_slave_config(chan, (struct dma_slave_config *)arg); - break; - default: - ret = -ENOSYS; - } - - return ret; -} - static struct dma_async_tx_descriptor *moxart_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction dir, @@ -531,7 +509,8 @@ static void moxart_dma_init(struct dma_device *dma, struct device *dev) dma->device_free_chan_resources = moxart_free_chan_resources; dma->device_issue_pending = moxart_issue_pending; dma->device_tx_status = moxart_tx_status; - dma->device_control = moxart_control; + dma->device_config = moxart_slave_config; + dma->device_terminate_all = moxart_terminate_all; dma->dev = dev; INIT_LIST_HEAD(&dma->channels); From b7f7552bfacd4e279e8a4fe520cacd43c72ba799 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:24 +0100 Subject: [PATCH 031/360] dmaengine: fsl-dma: Split device_control Split the device_control callback of the Freescale Elo DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. While we're at it, remove the useless prep_sg callback. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 91 ++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 59 deletions(-) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 38821cdf862b6c..b891079a166d6f 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -941,37 +941,8 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan, return NULL; } -/** - * fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction - * @chan: DMA channel - * @sgl: scatterlist to transfer to/from - * @sg_len: number of entries in @scatterlist - * @direction: DMA direction - * @flags: DMAEngine flags - * @context: transaction context (ignored) - * - * Prepare a set of descriptors for a DMA_SLAVE transaction. Following the - * DMA_SLAVE API, this gets the device-specific information from the - * chan->private variable. - */ -static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg( - struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long flags, - void *context) -{ - /* - * This operation is not supported on the Freescale DMA controller - * - * However, we need to provide the function pointer to allow the - * device_control() method to work. - */ - return NULL; -} - -static int fsl_dma_device_control(struct dma_chan *dchan, - enum dma_ctrl_cmd cmd, unsigned long arg) +static int fsl_dma_device_terminate_all(struct dma_chan *dchan) { - struct dma_slave_config *config; struct fsldma_chan *chan; int size; @@ -980,45 +951,47 @@ static int fsl_dma_device_control(struct dma_chan *dchan, chan = to_fsl_chan(dchan); - switch (cmd) { - case DMA_TERMINATE_ALL: - spin_lock_bh(&chan->desc_lock); - - /* Halt the DMA engine */ - dma_halt(chan); + spin_lock_bh(&chan->desc_lock); - /* Remove and free all of the descriptors in the LD queue */ - fsldma_free_desc_list(chan, &chan->ld_pending); - fsldma_free_desc_list(chan, &chan->ld_running); - fsldma_free_desc_list(chan, &chan->ld_completed); - chan->idle = true; + /* Halt the DMA engine */ + dma_halt(chan); - spin_unlock_bh(&chan->desc_lock); - return 0; + /* Remove and free all of the descriptors in the LD queue */ + fsldma_free_desc_list(chan, &chan->ld_pending); + fsldma_free_desc_list(chan, &chan->ld_running); + fsldma_free_desc_list(chan, &chan->ld_completed); + chan->idle = true; - case DMA_SLAVE_CONFIG: - config = (struct dma_slave_config *)arg; + spin_unlock_bh(&chan->desc_lock); + return 0; +} - /* make sure the channel supports setting burst size */ - if (!chan->set_request_count) - return -ENXIO; +static int fsl_dma_device_config(struct dma_chan *dchan, + struct dma_slave_config *config) +{ + struct fsldma_chan *chan; + int size; - /* we set the controller burst size depending on direction */ - if (config->direction == DMA_MEM_TO_DEV) - size = config->dst_addr_width * config->dst_maxburst; - else - size = config->src_addr_width * config->src_maxburst; + if (!dchan) + return -EINVAL; - chan->set_request_count(chan, size); - return 0; + chan = to_fsl_chan(dchan); - default: + /* make sure the channel supports setting burst size */ + if (!chan->set_request_count) return -ENXIO; - } + /* we set the controller burst size depending on direction */ + if (config->direction == DMA_MEM_TO_DEV) + size = config->dst_addr_width * config->dst_maxburst; + else + size = config->src_addr_width * config->src_maxburst; + + chan->set_request_count(chan, size); return 0; } + /** * fsl_dma_memcpy_issue_pending - Issue the DMA start command * @chan : Freescale DMA channel @@ -1395,8 +1368,8 @@ static int fsldma_of_probe(struct platform_device *op) fdev->common.device_prep_dma_sg = fsl_dma_prep_sg; fdev->common.device_tx_status = fsl_tx_status; fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; - fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg; - fdev->common.device_control = fsl_dma_device_control; + fdev->common.device_config = fsl_dma_device_config; + fdev->common.device_terminate_all = fsl_dma_device_terminate_all; fdev->common.dev = &op->dev; dma_set_mask(&(op->dev), DMA_BIT_MASK(36)); From 95335f1ff395a2152f788cee8a7680cfbd76573a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:25 +0100 Subject: [PATCH 032/360] dmaengine: mpc512x: Split device_control Split the device_control callback of the Freescale MPC512x DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/mpc512x_dma.c | 111 ++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 01bec4023de2d5..57d2457545f3d4 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -800,79 +800,69 @@ mpc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } -static int mpc_dma_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int mpc_dma_device_config(struct dma_chan *chan, + struct dma_slave_config *cfg) { - struct mpc_dma_chan *mchan; - struct mpc_dma *mdma; - struct dma_slave_config *cfg; + struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan); unsigned long flags; - mchan = dma_chan_to_mpc_dma_chan(chan); - switch (cmd) { - case DMA_TERMINATE_ALL: - /* Disable channel requests */ - mdma = dma_chan_to_mpc_dma(chan); - - spin_lock_irqsave(&mchan->lock, flags); - - out_8(&mdma->regs->dmacerq, chan->chan_id); - list_splice_tail_init(&mchan->prepared, &mchan->free); - list_splice_tail_init(&mchan->queued, &mchan->free); - list_splice_tail_init(&mchan->active, &mchan->free); - - spin_unlock_irqrestore(&mchan->lock, flags); + /* + * Software constraints: + * - only transfers between a peripheral device and + * memory are supported; + * - only peripheral devices with 4-byte FIFO access register + * are supported; + * - minimal transfer chunk is 4 bytes and consequently + * source and destination addresses must be 4-byte aligned + * and transfer size must be aligned on (4 * maxburst) + * boundary; + * - during the transfer RAM address is being incremented by + * the size of minimal transfer chunk; + * - peripheral port's address is constant during the transfer. + */ - return 0; + if (cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES || + cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES || + !IS_ALIGNED(cfg->src_addr, 4) || + !IS_ALIGNED(cfg->dst_addr, 4)) { + return -EINVAL; + } - case DMA_SLAVE_CONFIG: - /* - * Software constraints: - * - only transfers between a peripheral device and - * memory are supported; - * - only peripheral devices with 4-byte FIFO access register - * are supported; - * - minimal transfer chunk is 4 bytes and consequently - * source and destination addresses must be 4-byte aligned - * and transfer size must be aligned on (4 * maxburst) - * boundary; - * - during the transfer RAM address is being incremented by - * the size of minimal transfer chunk; - * - peripheral port's address is constant during the transfer. - */ + spin_lock_irqsave(&mchan->lock, flags); - cfg = (void *)arg; + mchan->src_per_paddr = cfg->src_addr; + mchan->src_tcd_nunits = cfg->src_maxburst; + mchan->dst_per_paddr = cfg->dst_addr; + mchan->dst_tcd_nunits = cfg->dst_maxburst; - if (cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES || - cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES || - !IS_ALIGNED(cfg->src_addr, 4) || - !IS_ALIGNED(cfg->dst_addr, 4)) { - return -EINVAL; - } + /* Apply defaults */ + if (mchan->src_tcd_nunits == 0) + mchan->src_tcd_nunits = 1; + if (mchan->dst_tcd_nunits == 0) + mchan->dst_tcd_nunits = 1; - spin_lock_irqsave(&mchan->lock, flags); + spin_unlock_irqrestore(&mchan->lock, flags); - mchan->src_per_paddr = cfg->src_addr; - mchan->src_tcd_nunits = cfg->src_maxburst; - mchan->dst_per_paddr = cfg->dst_addr; - mchan->dst_tcd_nunits = cfg->dst_maxburst; + return 0; +} - /* Apply defaults */ - if (mchan->src_tcd_nunits == 0) - mchan->src_tcd_nunits = 1; - if (mchan->dst_tcd_nunits == 0) - mchan->dst_tcd_nunits = 1; +static int mpc_dma_device_terminate_all(struct dma_chan *chan) +{ + struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan); + struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan); + unsigned long flags; - spin_unlock_irqrestore(&mchan->lock, flags); + /* Disable channel requests */ + spin_lock_irqsave(&mchan->lock, flags); - return 0; + out_8(&mdma->regs->dmacerq, chan->chan_id); + list_splice_tail_init(&mchan->prepared, &mchan->free); + list_splice_tail_init(&mchan->queued, &mchan->free); + list_splice_tail_init(&mchan->active, &mchan->free); - default: - /* Unknown command */ - break; - } + spin_unlock_irqrestore(&mchan->lock, flags); - return -ENXIO; + return 0; } static int mpc_dma_probe(struct platform_device *op) @@ -963,7 +953,8 @@ static int mpc_dma_probe(struct platform_device *op) dma->device_tx_status = mpc_dma_tx_status; dma->device_prep_dma_memcpy = mpc_dma_prep_memcpy; dma->device_prep_slave_sg = mpc_dma_prep_slave_sg; - dma->device_control = mpc_dma_device_control; + dma->device_config = mpc_dma_device_config; + dma->device_terminate_all = mpc_dma_device_terminate_all; INIT_LIST_HEAD(&dma->channels); dma_cap_set(DMA_MEMCPY, dma->cap_mask); From 5c9d2e37ac2bce248e351a7cd784e7c56dffb8e8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:26 +0100 Subject: [PATCH 033/360] dmaengine: mxs: Split device_control Split the device_control callback of the Freescale MXS DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 59 +++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 5ea61201dbf02c..834041e5a7691f 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -202,8 +202,9 @@ static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) return container_of(chan, struct mxs_dma_chan, chan); } -static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) +static void mxs_dma_reset_chan(struct dma_chan *chan) { + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; @@ -250,8 +251,9 @@ static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) mxs_chan->status = DMA_COMPLETE; } -static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) +static void mxs_dma_enable_chan(struct dma_chan *chan) { + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; @@ -272,13 +274,16 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) mxs_chan->reset = false; } -static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) +static void mxs_dma_disable_chan(struct dma_chan *chan) { + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); + mxs_chan->status = DMA_COMPLETE; } -static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan) +static void mxs_dma_pause_chan(struct dma_chan *chan) { + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; @@ -293,8 +298,9 @@ static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan) mxs_chan->status = DMA_PAUSED; } -static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan) +static void mxs_dma_resume_chan(struct dma_chan *chan) { + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; @@ -383,7 +389,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) "%s: error in channel %d\n", __func__, chan); mxs_chan->status = DMA_ERROR; - mxs_dma_reset_chan(mxs_chan); + mxs_dma_reset_chan(mxs_chan->chan); } else if (mxs_chan->status != DMA_COMPLETE) { if (mxs_chan->flags & MXS_DMA_SG_LOOP) { mxs_chan->status = DMA_IN_PROGRESS; @@ -432,7 +438,7 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) if (ret) goto err_clk; - mxs_dma_reset_chan(mxs_chan); + mxs_dma_reset_chan(chan); dma_async_tx_descriptor_init(&mxs_chan->desc, chan); mxs_chan->desc.tx_submit = mxs_dma_tx_submit; @@ -456,7 +462,7 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - mxs_dma_disable_chan(mxs_chan); + mxs_dma_disable_chan(chan); free_irq(mxs_chan->chan_irq, mxs_dma); @@ -651,28 +657,14 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( return NULL; } -static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int mxs_dma_terminate_all(struct dma_chan *chan) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); - int ret = 0; - - switch (cmd) { - case DMA_TERMINATE_ALL: - mxs_dma_reset_chan(mxs_chan); - mxs_dma_disable_chan(mxs_chan); - break; - case DMA_PAUSE: - mxs_dma_pause_chan(mxs_chan); - break; - case DMA_RESUME: - mxs_dma_resume_chan(mxs_chan); - break; - default: - ret = -ENOSYS; - } - return ret; + mxs_dma_reset_chan(chan); + mxs_dma_disable_chan(chan); + + return 0; } static enum dma_status mxs_dma_tx_status(struct dma_chan *chan, @@ -701,13 +693,6 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan, return mxs_chan->status; } -static void mxs_dma_issue_pending(struct dma_chan *chan) -{ - struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); - - mxs_dma_enable_chan(mxs_chan); -} - static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) { int ret; @@ -860,8 +845,10 @@ static int __init mxs_dma_probe(struct platform_device *pdev) mxs_dma->dma_device.device_tx_status = mxs_dma_tx_status; mxs_dma->dma_device.device_prep_slave_sg = mxs_dma_prep_slave_sg; mxs_dma->dma_device.device_prep_dma_cyclic = mxs_dma_prep_dma_cyclic; - mxs_dma->dma_device.device_control = mxs_dma_control; - mxs_dma->dma_device.device_issue_pending = mxs_dma_issue_pending; + mxs_dma->dma_device.device_pause = mxs_dma_pause_chan; + mxs_dma->dma_device.device_resume = mxs_dma_resume_chan; + mxs_dma->dma_device.device_terminate_all = mxs_dma_terminate_all; + mxs_dma->dma_device.device_issue_pending = mxs_dma_enable_chan; ret = dma_async_device_register(&mxs_dma->dma_device); if (ret) { From e22aec0f0072164e7a2243059715c92ff56016c6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:27 +0100 Subject: [PATCH 034/360] dmaengine: nbpfaxi: Split device_control Split the device_control callback of the NBPF AXI DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 93 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 3d993e78518087..0202602597f7a2 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -565,13 +565,6 @@ static void nbpf_configure(struct nbpf_device *nbpf) nbpf_write(nbpf, NBPF_CTRL, NBPF_CTRL_LVINT); } -static void nbpf_pause(struct nbpf_channel *chan) -{ - nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_SETSUS); - /* See comment in nbpf_prep_one() */ - nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_CLREN); -} - /* Generic part */ /* DMA ENGINE functions */ @@ -837,54 +830,58 @@ static void nbpf_chan_idle(struct nbpf_channel *chan) } } -static int nbpf_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int nbpf_pause(struct dma_chan *dchan) { struct nbpf_channel *chan = nbpf_to_chan(dchan); - struct dma_slave_config *config; - dev_dbg(dchan->device->dev, "Entry %s(%d)\n", __func__, cmd); + dev_dbg(dchan->device->dev, "Entry %s\n", __func__); - switch (cmd) { - case DMA_TERMINATE_ALL: - dev_dbg(dchan->device->dev, "Terminating\n"); - nbpf_chan_halt(chan); - nbpf_chan_idle(chan); - break; + chan->paused = true; + nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_SETSUS); + /* See comment in nbpf_prep_one() */ + nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_CLREN); - case DMA_SLAVE_CONFIG: - if (!arg) - return -EINVAL; - config = (struct dma_slave_config *)arg; + return 0; +} - /* - * We could check config->slave_id to match chan->terminal here, - * but with DT they would be coming from the same source, so - * such a check would be superflous - */ +static int nbpf_terminate_all(struct dma_chan *dchan) +{ + struct nbpf_channel *chan = nbpf_to_chan(dchan); - chan->slave_dst_addr = config->dst_addr; - chan->slave_dst_width = nbpf_xfer_size(chan->nbpf, - config->dst_addr_width, 1); - chan->slave_dst_burst = nbpf_xfer_size(chan->nbpf, - config->dst_addr_width, - config->dst_maxburst); - chan->slave_src_addr = config->src_addr; - chan->slave_src_width = nbpf_xfer_size(chan->nbpf, - config->src_addr_width, 1); - chan->slave_src_burst = nbpf_xfer_size(chan->nbpf, - config->src_addr_width, - config->src_maxburst); - break; + dev_dbg(dchan->device->dev, "Entry %s\n", __func__); + dev_dbg(dchan->device->dev, "Terminating\n"); - case DMA_PAUSE: - chan->paused = true; - nbpf_pause(chan); - break; + nbpf_chan_halt(chan); + nbpf_chan_idle(chan); - default: - return -ENXIO; - } + return 0; +} + +static int nbpf_config(struct dma_chan *dchan, + struct dma_slave_config *config) +{ + struct nbpf_channel *chan = nbpf_to_chan(dchan); + + dev_dbg(dchan->device->dev, "Entry %s\n", __func__); + + /* + * We could check config->slave_id to match chan->terminal here, + * but with DT they would be coming from the same source, so + * such a check would be superflous + */ + + chan->slave_dst_addr = config->dst_addr; + chan->slave_dst_width = nbpf_xfer_size(chan->nbpf, + config->dst_addr_width, 1); + chan->slave_dst_burst = nbpf_xfer_size(chan->nbpf, + config->dst_addr_width, + config->dst_maxburst); + chan->slave_src_addr = config->src_addr; + chan->slave_src_width = nbpf_xfer_size(chan->nbpf, + config->src_addr_width, 1); + chan->slave_src_burst = nbpf_xfer_size(chan->nbpf, + config->src_addr_width, + config->src_maxburst); return 0; } @@ -1426,7 +1423,9 @@ static int nbpf_probe(struct platform_device *pdev) /* Compulsory for DMA_SLAVE fields */ dma_dev->device_prep_slave_sg = nbpf_prep_slave_sg; - dma_dev->device_control = nbpf_control; + dma_dev->device_config = nbpf_config; + dma_dev->device_pause = nbpf_pause; + dma_dev->device_terminate_all = nbpf_terminate_all; platform_set_drvdata(pdev, nbpf); From 78ea4fe7e7a9395498c45098dc7339fce23fa7e0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:28 +0100 Subject: [PATCH 035/360] dmaengine: omap: Split device_control Split the device_control callback of the TI OMAP DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/omap-dma.c | 51 +++++++++++++----------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index ca4645c2763430..c84fe4a35022bb 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -948,8 +948,10 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( return vchan_tx_prep(&c->vc, &d->vd, flags); } -static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg) +static int omap_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) { + struct omap_chan *c = to_omap_dma_chan(chan); + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; @@ -959,8 +961,9 @@ static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *c return 0; } -static int omap_dma_terminate_all(struct omap_chan *c) +static int omap_dma_terminate_all(struct dma_chan *chan) { + struct omap_chan *c = to_omap_dma_chan(chan); struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device); unsigned long flags; LIST_HEAD(head); @@ -996,8 +999,10 @@ static int omap_dma_terminate_all(struct omap_chan *c) return 0; } -static int omap_dma_pause(struct omap_chan *c) +static int omap_dma_pause(struct dma_chan *chan) { + struct omap_chan *c = to_omap_dma_chan(chan); + /* Pause/Resume only allowed with cyclic mode */ if (!c->cyclic) return -EINVAL; @@ -1010,8 +1015,10 @@ static int omap_dma_pause(struct omap_chan *c) return 0; } -static int omap_dma_resume(struct omap_chan *c) +static int omap_dma_resume(struct dma_chan *chan) { + struct omap_chan *c = to_omap_dma_chan(chan); + /* Pause/Resume only allowed with cyclic mode */ if (!c->cyclic) return -EINVAL; @@ -1029,37 +1036,6 @@ static int omap_dma_resume(struct omap_chan *c) return 0; } -static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct omap_chan *c = to_omap_dma_chan(chan); - int ret; - - switch (cmd) { - case DMA_SLAVE_CONFIG: - ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg); - break; - - case DMA_TERMINATE_ALL: - ret = omap_dma_terminate_all(c); - break; - - case DMA_PAUSE: - ret = omap_dma_pause(c); - break; - - case DMA_RESUME: - ret = omap_dma_resume(c); - break; - - default: - ret = -ENXIO; - break; - } - - return ret; -} - static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig) { struct omap_chan *c; @@ -1136,7 +1112,10 @@ static int omap_dma_probe(struct platform_device *pdev) od->ddev.device_issue_pending = omap_dma_issue_pending; od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic; - od->ddev.device_control = omap_dma_control; + od->ddev.device_config = omap_dma_config; + od->ddev.device_pause = omap_dma_pause; + od->ddev.device_resume = omap_dma_resume; + od->ddev.device_terminate_all = omap_dma_terminate_all; od->ddev.device_slave_caps = omap_dma_device_slave_caps; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); From 740aa95703c59d8b59adb78c65efa08714f66ebb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:29 +0100 Subject: [PATCH 036/360] dmaengine: pl330: Split device_control Split the device_control callback of the AMBA PL330 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 116 +++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 65 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 4a759c8718a8ba..5af72962edaddc 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2086,78 +2086,63 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) return 1; } -static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) +static int pl330_config(struct dma_chan *chan, + struct dma_slave_config *slave_config) +{ + struct dma_pl330_chan *pch = to_pchan(chan); + + if (slave_config->direction == DMA_MEM_TO_DEV) { + if (slave_config->dst_addr) + pch->fifo_addr = slave_config->dst_addr; + if (slave_config->dst_addr_width) + pch->burst_sz = __ffs(slave_config->dst_addr_width); + if (slave_config->dst_maxburst) + pch->burst_len = slave_config->dst_maxburst; + } else if (slave_config->direction == DMA_DEV_TO_MEM) { + if (slave_config->src_addr) + pch->fifo_addr = slave_config->src_addr; + if (slave_config->src_addr_width) + pch->burst_sz = __ffs(slave_config->src_addr_width); + if (slave_config->src_maxburst) + pch->burst_len = slave_config->src_maxburst; + } + + return 0; +} + +static int pl330_terminate_all(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); struct dma_pl330_desc *desc; unsigned long flags; struct pl330_dmac *pl330 = pch->dmac; - struct dma_slave_config *slave_config; LIST_HEAD(list); - switch (cmd) { - case DMA_TERMINATE_ALL: - pm_runtime_get_sync(pl330->ddma.dev); - spin_lock_irqsave(&pch->lock, flags); - - spin_lock(&pl330->lock); - _stop(pch->thread); - spin_unlock(&pl330->lock); - - pch->thread->req[0].desc = NULL; - pch->thread->req[1].desc = NULL; - pch->thread->req_running = -1; - - /* Mark all desc done */ - list_for_each_entry(desc, &pch->submitted_list, node) { - desc->status = FREE; - dma_cookie_complete(&desc->txd); - } - - list_for_each_entry(desc, &pch->work_list , node) { - desc->status = FREE; - dma_cookie_complete(&desc->txd); - } - - list_for_each_entry(desc, &pch->completed_list , node) { - desc->status = FREE; - dma_cookie_complete(&desc->txd); - } - - if (!list_empty(&pch->work_list)) - pm_runtime_put(pl330->ddma.dev); + spin_lock_irqsave(&pch->lock, flags); + spin_lock(&pl330->lock); + _stop(pch->thread); + spin_unlock(&pl330->lock); + + pch->thread->req[0].desc = NULL; + pch->thread->req[1].desc = NULL; + pch->thread->req_running = -1; + + /* Mark all desc done */ + list_for_each_entry(desc, &pch->submitted_list, node) { + desc->status = FREE; + dma_cookie_complete(&desc->txd); + } - list_splice_tail_init(&pch->submitted_list, &pl330->desc_pool); - list_splice_tail_init(&pch->work_list, &pl330->desc_pool); - list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); - spin_unlock_irqrestore(&pch->lock, flags); - pm_runtime_mark_last_busy(pl330->ddma.dev); - pm_runtime_put_autosuspend(pl330->ddma.dev); - break; - case DMA_SLAVE_CONFIG: - slave_config = (struct dma_slave_config *)arg; - - if (slave_config->direction == DMA_MEM_TO_DEV) { - if (slave_config->dst_addr) - pch->fifo_addr = slave_config->dst_addr; - if (slave_config->dst_addr_width) - pch->burst_sz = __ffs(slave_config->dst_addr_width); - if (slave_config->dst_maxburst) - pch->burst_len = slave_config->dst_maxburst; - } else if (slave_config->direction == DMA_DEV_TO_MEM) { - if (slave_config->src_addr) - pch->fifo_addr = slave_config->src_addr; - if (slave_config->src_addr_width) - pch->burst_sz = __ffs(slave_config->src_addr_width); - if (slave_config->src_maxburst) - pch->burst_len = slave_config->src_maxburst; - } - break; - default: - dev_err(pch->dmac->ddma.dev, "Not supported command.\n"); - return -ENXIO; + list_for_each_entry(desc, &pch->work_list , node) { + desc->status = FREE; + dma_cookie_complete(&desc->txd); } + list_splice_tail_init(&pch->submitted_list, &pl330->desc_pool); + list_splice_tail_init(&pch->work_list, &pl330->desc_pool); + list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); + spin_unlock_irqrestore(&pch->lock, flags); + return 0; } @@ -2793,7 +2778,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; - pd->device_control = pl330_control; + pd->device_config = pl330_config; + pd->device_terminate_all = pl330_terminate_all; pd->device_issue_pending = pl330_issue_pending; pd->device_slave_caps = pl330_dma_device_slave_caps; @@ -2847,7 +2833,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) /* Flush the channel */ if (pch->thread) { - pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); + pl330_terminate_all(&pch->chan); pl330_free_chan_resources(&pch->chan); } } @@ -2878,7 +2864,7 @@ static int pl330_remove(struct amba_device *adev) /* Flush the channel */ if (pch->thread) { - pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); + pl330_terminate_all(&pch->chan); pl330_free_chan_resources(&pch->chan); } } From 62ec8eb52d5a72058e04b3d48c6ab9233c3721ff Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:30 +0100 Subject: [PATCH 037/360] dmaengine: bam-dma: Split device_control Split the device_control callback of the Qualcomm BAM DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/qcom_bam_dma.c | 85 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c index 3122a99ec06b8d..d7a33b3ac46603 100644 --- a/drivers/dma/qcom_bam_dma.c +++ b/drivers/dma/qcom_bam_dma.c @@ -530,11 +530,18 @@ static void bam_free_chan(struct dma_chan *chan) * Sets slave configuration for channel * */ -static void bam_slave_config(struct bam_chan *bchan, - struct dma_slave_config *cfg) +static int bam_slave_config(struct dma_chan *chan, + struct dma_slave_config *cfg) { + struct bam_chan *bchan = to_bam_chan(chan); + unsigned long flag; + + spin_lock_irqsave(&bchan->vc.lock, flag); memcpy(&bchan->slave, cfg, sizeof(*cfg)); bchan->reconfigure = 1; + spin_unlock_irqrestore(&bchan->vc.lock, flag); + + return 0; } /** @@ -627,8 +634,9 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, * No callbacks are done * */ -static void bam_dma_terminate_all(struct bam_chan *bchan) +static int bam_dma_terminate_all(struct dma_chan *chan) { + struct bam_chan *bchan = to_bam_chan(chan); unsigned long flag; LIST_HEAD(head); @@ -643,56 +651,46 @@ static void bam_dma_terminate_all(struct bam_chan *bchan) spin_unlock_irqrestore(&bchan->vc.lock, flag); vchan_dma_desc_free_list(&bchan->vc, &head); + + return 0; } /** - * bam_control - DMA device control + * bam_pause - Pause DMA channel * @chan: dma channel - * @cmd: control cmd - * @arg: cmd argument * - * Perform DMA control command + */ +static int bam_pause(struct dma_chan *chan) +{ + struct bam_chan *bchan = to_bam_chan(chan); + struct bam_device *bdev = bchan->bdev; + unsigned long flag; + + spin_lock_irqsave(&bchan->vc.lock, flag); + writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT)); + bchan->paused = 1; + spin_unlock_irqrestore(&bchan->vc.lock, flag); + + return 0; +} + +/** + * bam_resume - Resume DMA channel operations + * @chan: dma channel * */ -static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int bam_resume(struct dma_chan *chan) { struct bam_chan *bchan = to_bam_chan(chan); struct bam_device *bdev = bchan->bdev; - int ret = 0; unsigned long flag; - switch (cmd) { - case DMA_PAUSE: - spin_lock_irqsave(&bchan->vc.lock, flag); - writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT)); - bchan->paused = 1; - spin_unlock_irqrestore(&bchan->vc.lock, flag); - break; - - case DMA_RESUME: - spin_lock_irqsave(&bchan->vc.lock, flag); - writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT)); - bchan->paused = 0; - spin_unlock_irqrestore(&bchan->vc.lock, flag); - break; - - case DMA_TERMINATE_ALL: - bam_dma_terminate_all(bchan); - break; - - case DMA_SLAVE_CONFIG: - spin_lock_irqsave(&bchan->vc.lock, flag); - bam_slave_config(bchan, (struct dma_slave_config *)arg); - spin_unlock_irqrestore(&bchan->vc.lock, flag); - break; - - default: - ret = -ENXIO; - break; - } + spin_lock_irqsave(&bchan->vc.lock, flag); + writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT)); + bchan->paused = 0; + spin_unlock_irqrestore(&bchan->vc.lock, flag); - return ret; + return 0; } /** @@ -1148,7 +1146,10 @@ static int bam_dma_probe(struct platform_device *pdev) bdev->common.device_alloc_chan_resources = bam_alloc_chan; bdev->common.device_free_chan_resources = bam_free_chan; bdev->common.device_prep_slave_sg = bam_prep_slave_sg; - bdev->common.device_control = bam_control; + bdev->common.device_config = bam_slave_config; + bdev->common.device_pause = bam_pause; + bdev->common.device_resume = bam_resume; + bdev->common.device_terminate_all = bam_dma_terminate_all; bdev->common.device_issue_pending = bam_issue_pending; bdev->common.device_tx_status = bam_tx_status; bdev->common.dev = bdev->dev; @@ -1187,7 +1188,7 @@ static int bam_dma_remove(struct platform_device *pdev) devm_free_irq(bdev->dev, bdev->irq, bdev); for (i = 0; i < bdev->num_channels; i++) { - bam_dma_terminate_all(&bdev->channels[i]); + bam_dma_terminate_all(&bdev->channels[i].vc.chan); tasklet_kill(&bdev->channels[i].vc.task); dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, From 39ad46009654cc0c8275ec00d937aeb1d186d4a7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:31 +0100 Subject: [PATCH 038/360] dmaengine: s3c24xx: Split device_control Split the device_control callback of the Samsung S3C24xxx DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/s3c24xx-dma.c | 75 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 6941a77521c3d8..231f76acebde59 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -384,20 +384,30 @@ static u32 s3c24xx_dma_getbytes_chan(struct s3c24xx_dma_chan *s3cchan) return tc * txd->width; } -static int s3c24xx_dma_set_runtime_config(struct s3c24xx_dma_chan *s3cchan, +static int s3c24xx_dma_set_runtime_config(struct dma_chan *chan, struct dma_slave_config *config) { - if (!s3cchan->slave) - return -EINVAL; + struct s3c24xx_dma_chan *s3cchan = to_s3c24xx_dma_chan(chan); + unsigned long flags; + int ret = 0; /* Reject definitely invalid configurations */ if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; + spin_lock_irqsave(&s3cchan->vc.lock, flags); + + if (!s3cchan->slave) { + ret = -EINVAL; + goto out; + } + s3cchan->cfg = *config; - return 0; +out: + spin_lock_irqrestore(&s3cchan->vc.lock, flags); + return ret; } /* @@ -703,53 +713,38 @@ static irqreturn_t s3c24xx_dma_irq(int irq, void *data) * The DMA ENGINE API */ -static int s3c24xx_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int s3c24xx_dma_terminate_all(struct dma_chan *chan) { struct s3c24xx_dma_chan *s3cchan = to_s3c24xx_dma_chan(chan); struct s3c24xx_dma_engine *s3cdma = s3cchan->host; unsigned long flags; - int ret = 0; spin_lock_irqsave(&s3cchan->vc.lock, flags); - switch (cmd) { - case DMA_SLAVE_CONFIG: - ret = s3c24xx_dma_set_runtime_config(s3cchan, - (struct dma_slave_config *)arg); - break; - case DMA_TERMINATE_ALL: - if (!s3cchan->phy && !s3cchan->at) { - dev_err(&s3cdma->pdev->dev, "trying to terminate already stopped channel %d\n", - s3cchan->id); - ret = -EINVAL; - break; - } - - s3cchan->state = S3C24XX_DMA_CHAN_IDLE; + if (!s3cchan->phy && !s3cchan->at) { + dev_err(&s3cdma->pdev->dev, "trying to terminate already stopped channel %d\n", + s3cchan->id); + return -EINVAL; + } - /* Mark physical channel as free */ - if (s3cchan->phy) - s3c24xx_dma_phy_free(s3cchan); + s3cchan->state = S3C24XX_DMA_CHAN_IDLE; - /* Dequeue current job */ - if (s3cchan->at) { - s3c24xx_dma_desc_free(&s3cchan->at->vd); - s3cchan->at = NULL; - } + /* Mark physical channel as free */ + if (s3cchan->phy) + s3c24xx_dma_phy_free(s3cchan); - /* Dequeue jobs not yet fired as well */ - s3c24xx_dma_free_txd_list(s3cdma, s3cchan); - break; - default: - /* Unknown command */ - ret = -ENXIO; - break; + /* Dequeue current job */ + if (s3cchan->at) { + s3c24xx_dma_desc_free(&s3cchan->at->vd); + s3cchan->at = NULL; } + /* Dequeue jobs not yet fired as well */ + s3c24xx_dma_free_txd_list(s3cdma, s3cchan); + spin_unlock_irqrestore(&s3cchan->vc.lock, flags); - return ret; + return 0; } static int s3c24xx_dma_alloc_chan_resources(struct dma_chan *chan) @@ -1300,7 +1295,8 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) s3cdma->memcpy.device_prep_dma_memcpy = s3c24xx_dma_prep_memcpy; s3cdma->memcpy.device_tx_status = s3c24xx_dma_tx_status; s3cdma->memcpy.device_issue_pending = s3c24xx_dma_issue_pending; - s3cdma->memcpy.device_control = s3c24xx_dma_control; + s3cdma->memcpy.device_config = s3c24xx_dma_set_runtime_config; + s3cdma->memcpy.device_terminate_all = s3c24xx_dma_terminate_all; /* Initialize slave engine for SoC internal dedicated peripherals */ dma_cap_set(DMA_SLAVE, s3cdma->slave.cap_mask); @@ -1315,7 +1311,8 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) s3cdma->slave.device_issue_pending = s3c24xx_dma_issue_pending; s3cdma->slave.device_prep_slave_sg = s3c24xx_dma_prep_slave_sg; s3cdma->slave.device_prep_dma_cyclic = s3c24xx_dma_prep_dma_cyclic; - s3cdma->slave.device_control = s3c24xx_dma_control; + s3cdma->slave.device_config = s3c24xx_dma_set_runtime_config; + s3cdma->slave.device_terminate_all = s3c24xx_dma_terminate_all; /* Register as many memcpy channels as there are physical channels */ ret = s3c24xx_dma_init_virtual_channels(s3cdma, &s3cdma->memcpy, From 4a533218fccf82d4e371aeae737ce2383175fd01 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:32 +0100 Subject: [PATCH 039/360] dmaengine: sa11x0: Split device_control Split the device_control callback of the SA-11x0 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/sa11x0-dma.c | 158 +++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 74 deletions(-) diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index 96bb62c39c41bf..e229c62d7bad55 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -669,8 +669,10 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic( return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } -static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) +static int sa11x0_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *cfg) { + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); dma_addr_t addr; enum dma_slave_buswidth width; @@ -704,8 +706,7 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c return 0; } -static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int sa11x0_dma_pause(struct dma_chan *chan) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); @@ -714,89 +715,95 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long flags; int ret; - switch (cmd) { - case DMA_SLAVE_CONFIG: - return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg); - - case DMA_TERMINATE_ALL: - dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); - /* Clear the tx descriptor lists */ - spin_lock_irqsave(&c->vc.lock, flags); - vchan_get_all_descriptors(&c->vc, &head); + dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); + spin_lock_irqsave(&c->vc.lock, flags); + if (c->status == DMA_IN_PROGRESS) { + c->status = DMA_PAUSED; p = c->phy; if (p) { - dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num); - /* vchan is assigned to a pchan - stop the channel */ - writel(DCSR_RUN | DCSR_IE | - DCSR_STRTA | DCSR_DONEA | - DCSR_STRTB | DCSR_DONEB, - p->base + DMA_DCSR_C); - - if (p->txd_load) { - if (p->txd_load != p->txd_done) - list_add_tail(&p->txd_load->vd.node, &head); - p->txd_load = NULL; - } - if (p->txd_done) { - list_add_tail(&p->txd_done->vd.node, &head); - p->txd_done = NULL; - } - c->phy = NULL; + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); + } else { spin_lock(&d->lock); - p->vchan = NULL; + list_del_init(&c->node); spin_unlock(&d->lock); - tasklet_schedule(&d->task); } - spin_unlock_irqrestore(&c->vc.lock, flags); - vchan_dma_desc_free_list(&c->vc, &head); - ret = 0; - break; + } + spin_unlock_irqrestore(&c->vc.lock, flags); - case DMA_PAUSE: - dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); - spin_lock_irqsave(&c->vc.lock, flags); - if (c->status == DMA_IN_PROGRESS) { - c->status = DMA_PAUSED; + return 0; +} - p = c->phy; - if (p) { - writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C); - } else { - spin_lock(&d->lock); - list_del_init(&c->node); - spin_unlock(&d->lock); - } - } - spin_unlock_irqrestore(&c->vc.lock, flags); - ret = 0; - break; +static int sa11x0_dma_resume(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + LIST_HEAD(head); + unsigned long flags; + int ret; - case DMA_RESUME: - dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); - spin_lock_irqsave(&c->vc.lock, flags); - if (c->status == DMA_PAUSED) { - c->status = DMA_IN_PROGRESS; - - p = c->phy; - if (p) { - writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S); - } else if (!list_empty(&c->vc.desc_issued)) { - spin_lock(&d->lock); - list_add_tail(&c->node, &d->chan_pending); - spin_unlock(&d->lock); - } + dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); + spin_lock_irqsave(&c->vc.lock, flags); + if (c->status == DMA_PAUSED) { + c->status = DMA_IN_PROGRESS; + + p = c->phy; + if (p) { + writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S); + } else if (!list_empty(&c->vc.desc_issued)) { + spin_lock(&d->lock); + list_add_tail(&c->node, &d->chan_pending); + spin_unlock(&d->lock); } - spin_unlock_irqrestore(&c->vc.lock, flags); - ret = 0; - break; + } + spin_unlock_irqrestore(&c->vc.lock, flags); - default: - ret = -ENXIO; - break; + return 0; +} + +static int sa11x0_dma_terminate_all(struct dma_chan *chan) +{ + struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); + struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); + struct sa11x0_dma_phy *p; + LIST_HEAD(head); + unsigned long flags; + int ret; + + dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); + /* Clear the tx descriptor lists */ + spin_lock_irqsave(&c->vc.lock, flags); + vchan_get_all_descriptors(&c->vc, &head); + + p = c->phy; + if (p) { + dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num); + /* vchan is assigned to a pchan - stop the channel */ + writel(DCSR_RUN | DCSR_IE | + DCSR_STRTA | DCSR_DONEA | + DCSR_STRTB | DCSR_DONEB, + p->base + DMA_DCSR_C); + + if (p->txd_load) { + if (p->txd_load != p->txd_done) + list_add_tail(&p->txd_load->vd.node, &head); + p->txd_load = NULL; + } + if (p->txd_done) { + list_add_tail(&p->txd_done->vd.node, &head); + p->txd_done = NULL; + } + c->phy = NULL; + spin_lock(&d->lock); + p->vchan = NULL; + spin_unlock(&d->lock); + tasklet_schedule(&d->task); } + spin_unlock_irqrestore(&c->vc.lock, flags); + vchan_dma_desc_free_list(&c->vc, &head); - return ret; + return 0; } struct sa11x0_dma_channel_desc { @@ -833,7 +840,10 @@ static int sa11x0_dma_init_dmadev(struct dma_device *dmadev, dmadev->dev = dev; dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources; dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources; - dmadev->device_control = sa11x0_dma_control; + dmadev->device_config = sa11x0_dma_slave_config; + dmadev->device_pause = sa11x0_dma_pause; + dmadev->device_resume = sa11x0_dma_resume; + dmadev->device_terminate_all = sa11x0_dma_terminate_all; dmadev->device_tx_status = sa11x0_dma_tx_status; dmadev->device_issue_pending = sa11x0_dma_issue_pending; From be60f94074cf1caf165c0494aa393bcd2e322af4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:33 +0100 Subject: [PATCH 040/360] dmaengine: sh: Split device_control Split the device_control callback of the Super-H DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 72 +++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 3a2adb131d4625..8ee383d339a513 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -729,57 +729,50 @@ static struct dma_async_tx_descriptor *shdma_prep_dma_cyclic( return desc; } -static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int shdma_terminate_all(struct dma_chan *chan) { struct shdma_chan *schan = to_shdma_chan(chan); struct shdma_dev *sdev = to_shdma_dev(chan->device); const struct shdma_ops *ops = sdev->ops; - struct dma_slave_config *config; unsigned long flags; - int ret; - switch (cmd) { - case DMA_TERMINATE_ALL: - spin_lock_irqsave(&schan->chan_lock, flags); - ops->halt_channel(schan); + spin_lock_irqsave(&schan->chan_lock, flags); + ops->halt_channel(schan); - if (ops->get_partial && !list_empty(&schan->ld_queue)) { - /* Record partial transfer */ - struct shdma_desc *desc = list_first_entry(&schan->ld_queue, - struct shdma_desc, node); - desc->partial = ops->get_partial(schan, desc); - } + if (ops->get_partial && !list_empty(&schan->ld_queue)) { + /* Record partial transfer */ + struct shdma_desc *desc = list_first_entry(&schan->ld_queue, + struct shdma_desc, node); + desc->partial = ops->get_partial(schan, desc); + } - spin_unlock_irqrestore(&schan->chan_lock, flags); + spin_unlock_irqrestore(&schan->chan_lock, flags); - shdma_chan_ld_cleanup(schan, true); - break; - case DMA_SLAVE_CONFIG: - /* - * So far only .slave_id is used, but the slave drivers are - * encouraged to also set a transfer direction and an address. - */ - if (!arg) - return -EINVAL; - /* - * We could lock this, but you shouldn't be configuring the - * channel, while using it... - */ - config = (struct dma_slave_config *)arg; - ret = shdma_setup_slave(schan, config->slave_id, - config->direction == DMA_DEV_TO_MEM ? - config->src_addr : config->dst_addr); - if (ret < 0) - return ret; - break; - default: - return -ENXIO; - } + shdma_chan_ld_cleanup(schan, true); return 0; } +static int shdma_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct shdma_chan *schan = to_shdma_chan(chan); + + /* + * So far only .slave_id is used, but the slave drivers are + * encouraged to also set a transfer direction and an address. + */ + if (!config) + return -EINVAL; + /* + * We could lock this, but you shouldn't be configuring the + * channel, while using it... + */ + return shdma_setup_slave(schan, config->slave_id, + config->direction == DMA_DEV_TO_MEM ? + config->src_addr : config->dst_addr); +} + static void shdma_issue_pending(struct dma_chan *chan) { struct shdma_chan *schan = to_shdma_chan(chan); @@ -1002,7 +995,8 @@ int shdma_init(struct device *dev, struct shdma_dev *sdev, /* Compulsory for DMA_SLAVE fields */ dma_dev->device_prep_slave_sg = shdma_prep_slave_sg; dma_dev->device_prep_dma_cyclic = shdma_prep_dma_cyclic; - dma_dev->device_control = shdma_control; + dma_dev->device_config = shdma_config; + dma_dev->device_terminate_all = shdma_terminate_all; dma_dev->dev = dev; From ed14a7c9fa345cd5334209cdab89fc45e731cad6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:34 +0100 Subject: [PATCH 041/360] dmaengine: sirf: Split device_control Split the device_control callback of the SiRF Prima 2 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/sirf-dma.c | 43 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 11c85fc450a1c7..fab9c9c1b421be 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -281,9 +281,10 @@ static dma_cookie_t sirfsoc_dma_tx_submit(struct dma_async_tx_descriptor *txd) return cookie; } -static int sirfsoc_dma_slave_config(struct sirfsoc_dma_chan *schan, - struct dma_slave_config *config) +static int sirfsoc_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) { + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); unsigned long flags; if ((config->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || @@ -297,8 +298,9 @@ static int sirfsoc_dma_slave_config(struct sirfsoc_dma_chan *schan, return 0; } -static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) +static int sirfsoc_dma_terminate_all(struct dma_chan *chan) { + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); int cid = schan->chan.chan_id; unsigned long flags; @@ -327,8 +329,9 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) return 0; } -static int sirfsoc_dma_pause_chan(struct sirfsoc_dma_chan *schan) +static int sirfsoc_dma_pause_chan(struct dma_chan *chan) { + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); int cid = schan->chan.chan_id; unsigned long flags; @@ -348,8 +351,9 @@ static int sirfsoc_dma_pause_chan(struct sirfsoc_dma_chan *schan) return 0; } -static int sirfsoc_dma_resume_chan(struct sirfsoc_dma_chan *schan) +static int sirfsoc_dma_resume_chan(struct dma_chan *chan) { + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); int cid = schan->chan.chan_id; unsigned long flags; @@ -369,30 +373,6 @@ static int sirfsoc_dma_resume_chan(struct sirfsoc_dma_chan *schan) return 0; } -static int sirfsoc_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct dma_slave_config *config; - struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); - - switch (cmd) { - case DMA_PAUSE: - return sirfsoc_dma_pause_chan(schan); - case DMA_RESUME: - return sirfsoc_dma_resume_chan(schan); - case DMA_TERMINATE_ALL: - return sirfsoc_dma_terminate_all(schan); - case DMA_SLAVE_CONFIG: - config = (struct dma_slave_config *)arg; - return sirfsoc_dma_slave_config(schan, config); - - default: - break; - } - - return -ENOSYS; -} - /* Alloc channel resources */ static int sirfsoc_dma_alloc_chan_resources(struct dma_chan *chan) { @@ -739,7 +719,10 @@ static int sirfsoc_dma_probe(struct platform_device *op) dma->device_alloc_chan_resources = sirfsoc_dma_alloc_chan_resources; dma->device_free_chan_resources = sirfsoc_dma_free_chan_resources; dma->device_issue_pending = sirfsoc_dma_issue_pending; - dma->device_control = sirfsoc_dma_control; + dma->device_config = sirfsoc_dma_slave_config; + dma->device_pause = sirfsoc_dma_pause_chan; + dma->device_resume = sirfsoc_dma_resume_chan; + dma->device_terminate_all = sirfsoc_dma_terminate_all; dma->device_tx_status = sirfsoc_dma_tx_status; dma->device_prep_interleaved_dma = sirfsoc_dma_prep_interleaved; dma->device_prep_dma_cyclic = sirfsoc_dma_prep_cyclic; From 826b15a7a8eda016e8a8e86d4dedb80840a8bbba Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:35 +0100 Subject: [PATCH 042/360] dmaengine: sun6i: Split device_control Split the device_control callback of the Allwinner A31 DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/sun6i-dma.c | 150 +++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 159f1736a16f7c..0e116399895fa3 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -355,38 +355,6 @@ static void sun6i_dma_free_desc(struct virt_dma_desc *vd) kfree(txd); } -static int sun6i_dma_terminate_all(struct sun6i_vchan *vchan) -{ - struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(vchan->vc.chan.device); - struct sun6i_pchan *pchan = vchan->phy; - unsigned long flags; - LIST_HEAD(head); - - spin_lock(&sdev->lock); - list_del_init(&vchan->node); - spin_unlock(&sdev->lock); - - spin_lock_irqsave(&vchan->vc.lock, flags); - - vchan_get_all_descriptors(&vchan->vc, &head); - - if (pchan) { - writel(DMA_CHAN_ENABLE_STOP, pchan->base + DMA_CHAN_ENABLE); - writel(DMA_CHAN_PAUSE_RESUME, pchan->base + DMA_CHAN_PAUSE); - - vchan->phy = NULL; - pchan->vchan = NULL; - pchan->desc = NULL; - pchan->done = NULL; - } - - spin_unlock_irqrestore(&vchan->vc.lock, flags); - - vchan_dma_desc_free_list(&vchan->vc, &head); - - return 0; -} - static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) { struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(vchan->vc.chan.device); @@ -675,57 +643,92 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg( return NULL; } -static int sun6i_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int sun6i_dma_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct sun6i_vchan *vchan = to_sun6i_vchan(chan); + + memcpy(&vchan->cfg, config, sizeof(*config)); + + return 0; +} + +static int sun6i_dma_pause(struct dma_chan *chan) +{ + struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device); + struct sun6i_vchan *vchan = to_sun6i_vchan(chan); + struct sun6i_pchan *pchan = vchan->phy; + + dev_dbg(chan2dev(chan), "vchan %p: pause\n", &vchan->vc); + + if (pchan) { + writel(DMA_CHAN_PAUSE_PAUSE, + pchan->base + DMA_CHAN_PAUSE); + } else { + spin_lock(&sdev->lock); + list_del_init(&vchan->node); + spin_unlock(&sdev->lock); + } + + return 0; +} + +static int sun6i_dma_resume(struct dma_chan *chan) { struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device); struct sun6i_vchan *vchan = to_sun6i_vchan(chan); struct sun6i_pchan *pchan = vchan->phy; unsigned long flags; - int ret = 0; - switch (cmd) { - case DMA_RESUME: - dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc); + dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc); - spin_lock_irqsave(&vchan->vc.lock, flags); + spin_lock_irqsave(&vchan->vc.lock, flags); - if (pchan) { - writel(DMA_CHAN_PAUSE_RESUME, - pchan->base + DMA_CHAN_PAUSE); - } else if (!list_empty(&vchan->vc.desc_issued)) { - spin_lock(&sdev->lock); - list_add_tail(&vchan->node, &sdev->pending); - spin_unlock(&sdev->lock); - } + if (pchan) { + writel(DMA_CHAN_PAUSE_RESUME, + pchan->base + DMA_CHAN_PAUSE); + } else if (!list_empty(&vchan->vc.desc_issued)) { + spin_lock(&sdev->lock); + list_add_tail(&vchan->node, &sdev->pending); + spin_unlock(&sdev->lock); + } - spin_unlock_irqrestore(&vchan->vc.lock, flags); - break; + spin_unlock_irqrestore(&vchan->vc.lock, flags); - case DMA_PAUSE: - dev_dbg(chan2dev(chan), "vchan %p: pause\n", &vchan->vc); + return 0; +} - if (pchan) { - writel(DMA_CHAN_PAUSE_PAUSE, - pchan->base + DMA_CHAN_PAUSE); - } else { - spin_lock(&sdev->lock); - list_del_init(&vchan->node); - spin_unlock(&sdev->lock); - } - break; - - case DMA_TERMINATE_ALL: - ret = sun6i_dma_terminate_all(vchan); - break; - case DMA_SLAVE_CONFIG: - memcpy(&vchan->cfg, (void *)arg, sizeof(struct dma_slave_config)); - break; - default: - ret = -ENXIO; - break; +static int sun6i_dma_terminate_all(struct dma_chan *chan) +{ + struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device); + struct sun6i_vchan *vchan = to_sun6i_vchan(chan); + struct sun6i_pchan *pchan = vchan->phy; + unsigned long flags; + LIST_HEAD(head); + + spin_lock(&sdev->lock); + list_del_init(&vchan->node); + spin_unlock(&sdev->lock); + + spin_lock_irqsave(&vchan->vc.lock, flags); + + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { + writel(DMA_CHAN_ENABLE_STOP, pchan->base + DMA_CHAN_ENABLE); + writel(DMA_CHAN_PAUSE_RESUME, pchan->base + DMA_CHAN_PAUSE); + + vchan->phy = NULL; + pchan->vchan = NULL; + pchan->desc = NULL; + pchan->done = NULL; } - return ret; + + spin_unlock_irqrestore(&vchan->vc.lock, flags); + + vchan_dma_desc_free_list(&vchan->vc, &head); + + return 0; } static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan, @@ -960,8 +963,11 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.device_issue_pending = sun6i_dma_issue_pending; sdc->slave.device_prep_slave_sg = sun6i_dma_prep_slave_sg; sdc->slave.device_prep_dma_memcpy = sun6i_dma_prep_dma_memcpy; - sdc->slave.device_control = sun6i_dma_control; sdc->slave.copy_align = 4; + sdc->slave.device_config = sun6i_dma_config; + sdc->slave.device_pause = sun6i_dma_pause; + sdc->slave.device_resume = sun6i_dma_resume; + sdc->slave.device_terminate_all = sun6i_dma_terminate_all; sdc->slave.dev = &pdev->dev; From 6f5bad03e894dba90707efd0ac8159d46f1bb157 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:36 +0100 Subject: [PATCH 043/360] dmaengine: d40: Split device_control Split the device_control callback of the ST-Ericsson DMA 40 driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 60 +++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 15d49461c0d2c4..e5a284865e9270 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1429,11 +1429,17 @@ static bool d40_tx_is_linked(struct d40_chan *d40c) return is_link; } -static int d40_pause(struct d40_chan *d40c) +static int d40_pause(struct dma_chan *chan) { + struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); int res = 0; unsigned long flags; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + if (!d40c->busy) return 0; @@ -1448,11 +1454,17 @@ static int d40_pause(struct d40_chan *d40c) return res; } -static int d40_resume(struct d40_chan *d40c) +static int d40_resume(struct dma_chan *chan) { + struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); int res = 0; unsigned long flags; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + if (!d40c->busy) return 0; @@ -2610,6 +2622,11 @@ static void d40_terminate_all(struct dma_chan *chan) struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); int ret; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + spin_lock_irqsave(&d40c->lock, flags); pm_runtime_get_sync(d40c->base->dev); @@ -2673,6 +2690,11 @@ static int d40_set_runtime_config(struct dma_chan *chan, u32 src_maxburst, dst_maxburst; int ret; + if (d40c->phy_chan == NULL) { + chan_err(d40c, "Channel is not allocated!\n"); + return -EINVAL; + } + src_addr_width = config->src_addr_width; src_maxburst = config->src_maxburst; dst_addr_width = config->dst_addr_width; @@ -2781,35 +2803,6 @@ static int d40_set_runtime_config(struct dma_chan *chan, return 0; } -static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); - - if (d40c->phy_chan == NULL) { - chan_err(d40c, "Channel is not allocated!\n"); - return -EINVAL; - } - - switch (cmd) { - case DMA_TERMINATE_ALL: - d40_terminate_all(chan); - return 0; - case DMA_PAUSE: - return d40_pause(d40c); - case DMA_RESUME: - return d40_resume(d40c); - case DMA_SLAVE_CONFIG: - return d40_set_runtime_config(chan, - (struct dma_slave_config *) arg); - default: - break; - } - - /* Other commands are unimplemented */ - return -ENXIO; -} - /* Initialization functions */ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, @@ -2870,7 +2863,10 @@ static void d40_ops_init(struct d40_base *base, struct dma_device *dev) dev->device_free_chan_resources = d40_free_chan_resources; dev->device_issue_pending = d40_issue_pending; dev->device_tx_status = d40_tx_status; - dev->device_control = d40_control; + dev->device_config = d40_set_runtime_config; + dev->device_pause = d40_pause; + dev->device_resume = d40_resume; + dev->device_terminate_all = d40_terminate_all; dev->dev = base->dev; } From 662f1ac3125fc00faa55cfdce6dd662850a6f8d8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:37 +0100 Subject: [PATCH 044/360] dmaengine: tegra20: Split device_control Split the device_control callback of the NVidia Tegra20 APB DMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index d8450c3f35f0ed..02f6013b83752d 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -827,25 +827,6 @@ static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, return ret; } -static int tegra_dma_device_control(struct dma_chan *dc, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - switch (cmd) { - case DMA_SLAVE_CONFIG: - return tegra_dma_slave_config(dc, - (struct dma_slave_config *)arg); - - case DMA_TERMINATE_ALL: - tegra_dma_terminate_all(dc); - return 0; - - default: - break; - } - - return -ENXIO; -} - static inline int get_bus_width(struct tegra_dma_channel *tdc, enum dma_slave_buswidth slave_bw) { @@ -1443,7 +1424,8 @@ static int tegra_dma_probe(struct platform_device *pdev) tegra_dma_free_chan_resources; tdma->dma_dev.device_prep_slave_sg = tegra_dma_prep_slave_sg; tdma->dma_dev.device_prep_dma_cyclic = tegra_dma_prep_dma_cyclic; - tdma->dma_dev.device_control = tegra_dma_device_control; + tdma->dma_dev.device_config = tegra_dma_slave_config; + tdma->dma_dev.device_terminate_all = tegra_dma_terminate_all; tdma->dma_dev.device_tx_status = tegra_dma_tx_status; tdma->dma_dev.device_issue_pending = tegra_dma_issue_pending; From ba7140462fe0bdf50da50221e3fe143f6a2b33e7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:38 +0100 Subject: [PATCH 045/360] dmaengine: xilinx: Split device_control Split the device_control callback of the Xilinx VDMA driver to make use of the newly introduced callbacks, that will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_vdma.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c index 4a3a8f3137b3c4..bdd2a5dd7220cb 100644 --- a/drivers/dma/xilinx/xilinx_vdma.c +++ b/drivers/dma/xilinx/xilinx_vdma.c @@ -1001,13 +1001,17 @@ xilinx_vdma_dma_prep_interleaved(struct dma_chan *dchan, * xilinx_vdma_terminate_all - Halt the channel and free descriptors * @chan: Driver specific VDMA Channel pointer */ -static void xilinx_vdma_terminate_all(struct xilinx_vdma_chan *chan) +static int xilinx_vdma_terminate_all(struct dma_chan *dchan) { + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + /* Halt the DMA engine */ xilinx_vdma_halt(chan); /* Remove and free all of the descriptors in the lists */ xilinx_vdma_free_descriptors(chan); + + return 0; } /** @@ -1075,27 +1079,6 @@ int xilinx_vdma_channel_set_config(struct dma_chan *dchan, } EXPORT_SYMBOL(xilinx_vdma_channel_set_config); -/** - * xilinx_vdma_device_control - Configure DMA channel of the device - * @dchan: DMA Channel pointer - * @cmd: DMA control command - * @arg: Channel configuration - * - * Return: '0' on success and failure value on error - */ -static int xilinx_vdma_device_control(struct dma_chan *dchan, - enum dma_ctrl_cmd cmd, unsigned long arg) -{ - struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); - - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; - - xilinx_vdma_terminate_all(chan); - - return 0; -} - /* ----------------------------------------------------------------------------- * Probe and remove */ @@ -1300,7 +1283,7 @@ static int xilinx_vdma_probe(struct platform_device *pdev) xilinx_vdma_free_chan_resources; xdev->common.device_prep_interleaved_dma = xilinx_vdma_dma_prep_interleaved; - xdev->common.device_control = xilinx_vdma_device_control; + xdev->common.device_terminate_all = xilinx_vdma_terminate_all; xdev->common.device_tx_status = xilinx_vdma_tx_status; xdev->common.device_issue_pending = xilinx_vdma_issue_pending; From 581dc2ccb1a801cb8b56a2d29e3346add7998200 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:39 +0100 Subject: [PATCH 046/360] dmaengine: mv_xor: Remove device_control The Marvell XOR engine doesn't allow any operations that use to be defined in device_control, it shouldn't need to be defined. Since it's going to be deprecated, remove it altogether. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/mv_xor.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index d7ac558c2c1c83..b03e8137b91888 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -928,14 +928,6 @@ mv_xor_xor_self_test(struct mv_xor_chan *mv_chan) return err; } -/* This driver does not implement any of the optional DMA operations. */ -static int -mv_xor_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - return -ENOSYS; -} - static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan) { struct dma_chan *chan, *_chan; @@ -1008,7 +1000,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_dev->device_free_chan_resources = mv_xor_free_chan_resources; dma_dev->device_tx_status = mv_xor_status; dma_dev->device_issue_pending = mv_xor_issue_pending; - dma_dev->device_control = mv_xor_control; dma_dev->dev = &pdev->dev; /* set prep routines based on capability */ From c91781b44e2bf4770f894c80cbe99e3404b690fb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:40 +0100 Subject: [PATCH 047/360] dmaengine: pch-dma: Rename device_control Rename the device_control callback of the Intel PCH DMA driver to terminate_all since it's all it's really doing. That will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/pch_dma.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 6e0e47d76b231e..35c143cb88da1c 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -665,16 +665,12 @@ static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan, return NULL; } -static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int pd_device_terminate_all(struct dma_chan *chan) { struct pch_dma_chan *pd_chan = to_pd_chan(chan); struct pch_dma_desc *desc, *_d; LIST_HEAD(list); - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; - spin_lock_irq(&pd_chan->lock); pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE); @@ -932,7 +928,7 @@ static int pch_dma_probe(struct pci_dev *pdev, pd->dma.device_tx_status = pd_tx_status; pd->dma.device_issue_pending = pd_issue_pending; pd->dma.device_prep_slave_sg = pd_prep_slave_sg; - pd->dma.device_control = pd_device_control; + pd->dma.device_terminate_all = pd_device_terminate_all; err = dma_async_device_register(&pd->dma); if (err) { From 2c55536ac3bf07416f8b7315d334a346a7ba3e30 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:41 +0100 Subject: [PATCH 048/360] dmaengine: td: Rename device_control Rename the device_control callback of the Timberdal DMA driver to terminate_all since it's all it's really doing. That will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/timb_dma.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 2407ccf1a64b29..c4c3d93fdd1bf2 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -561,8 +561,7 @@ static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan, return &td_desc->txd; } -static int td_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int td_terminate_all(struct dma_chan *chan) { struct timb_dma_chan *td_chan = container_of(chan, struct timb_dma_chan, chan); @@ -570,9 +569,6 @@ static int td_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; - /* first the easy part, put the queue into the free list */ spin_lock_bh(&td_chan->lock); list_for_each_entry_safe(td_desc, _td_desc, &td_chan->queue, @@ -697,7 +693,7 @@ static int td_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, td->dma.cap_mask); dma_cap_set(DMA_PRIVATE, td->dma.cap_mask); td->dma.device_prep_slave_sg = td_prep_slave_sg; - td->dma.device_control = td_control; + td->dma.device_terminate_all = td_terminate_all; td->dma.dev = &pdev->dev; From be16d8330aa199e888334a99774f68e4a350e841 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:42 +0100 Subject: [PATCH 049/360] dmaengine: txx9: Rename device_control Rename the device_control callback of the TXX9 DMA driver to terminate_all since it's all it's really doing. That will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/txx9dmac.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 0659ec9c44884d..8849318b32b7ab 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -901,17 +901,12 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return &first->txd; } -static int txx9dmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int txx9dmac_terminate_all(struct dma_chan *chan) { struct txx9dmac_chan *dc = to_txx9dmac_chan(chan); struct txx9dmac_desc *desc, *_desc; LIST_HEAD(list); - /* Only supports DMA_TERMINATE_ALL */ - if (cmd != DMA_TERMINATE_ALL) - return -EINVAL; - dev_vdbg(chan2dev(chan), "terminate_all\n"); spin_lock_bh(&dc->lock); @@ -1109,7 +1104,7 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev) dc->dma.dev = &pdev->dev; dc->dma.device_alloc_chan_resources = txx9dmac_alloc_chan_resources; dc->dma.device_free_chan_resources = txx9dmac_free_chan_resources; - dc->dma.device_control = txx9dmac_control; + dc->dma.device_terminate_all = txx9dmac_terminate_all; dc->dma.device_tx_status = txx9dmac_tx_status; dc->dma.device_issue_pending = txx9dmac_issue_pending; if (pdata && pdata->memcpy_chan == ch) { From 7664cfe00151dcd801cee0c52ebc8688e1899110 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:43 +0100 Subject: [PATCH 050/360] dmaengine: rapidio: tsi721: Rename device_control Rename the device_control callback of the TXX9 DMA driver to terminate_all since it's all it's really doing. That will eventually be used to retrieve slave capabilities. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/rapidio/devices/tsi721_dma.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c index f64c5decb747a8..47295940a86878 100644 --- a/drivers/rapidio/devices/tsi721_dma.c +++ b/drivers/rapidio/devices/tsi721_dma.c @@ -815,8 +815,7 @@ struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan, return txd; } -static int tsi721_device_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, - unsigned long arg) +static int tsi721_terminate_all(struct dma_chan *dchan) { struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); struct tsi721_tx_desc *desc, *_d; @@ -825,9 +824,6 @@ static int tsi721_device_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, dev_dbg(dchan->device->dev, "%s: Entry\n", __func__); - if (cmd != DMA_TERMINATE_ALL) - return -ENOSYS; - spin_lock_bh(&bdma_chan->lock); bdma_chan->active = false; @@ -901,7 +897,7 @@ int tsi721_register_dma(struct tsi721_device *priv) mport->dma.device_tx_status = tsi721_tx_status; mport->dma.device_issue_pending = tsi721_issue_pending; mport->dma.device_prep_slave_sg = tsi721_prep_rio_sg; - mport->dma.device_control = tsi721_device_control; + mport->dma.device_terminate_all = tsi721_terminate_all; err = dma_async_device_register(&mport->dma); if (err) From 8ac82f889c525243b3b3b64117bbd8f810aac29b Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Mon, 17 Nov 2014 14:42:44 +0100 Subject: [PATCH 051/360] dmaengine: at_xdmac: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Ludovic Desroches Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index c7f335088d1ead..8c799f66a6dcc8 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -174,6 +174,13 @@ #define AT_XDMAC_MAX_CHAN 0x20 +#define AT_XDMAC_DMA_BUSWIDTHS\ + (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |\ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |\ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) + enum atc_status { AT_XDMAC_CHAN_IS_CYCLIC = 0, AT_XDMAC_CHAN_IS_PAUSED, @@ -1234,27 +1241,6 @@ static void at_xdmac_free_chan_resources(struct dma_chan *chan) return; } -#define AT_XDMAC_DMA_BUSWIDTHS\ - (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\ - BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |\ - BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\ - BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |\ - BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) - -static int at_xdmac_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - - caps->src_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; - caps->dst_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = true; - caps->cmd_terminate = true; - caps->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - - return 0; -} - #ifdef CONFIG_PM static int atmel_xdmac_prepare(struct device *dev) { @@ -1428,7 +1414,10 @@ static int at_xdmac_probe(struct platform_device *pdev) atxdmac->dma.device_pause = at_xdmac_device_pause; atxdmac->dma.device_resume = at_xdmac_device_resume; atxdmac->dma.device_terminate_all = at_xdmac_device_terminate_all; - atxdmac->dma.device_slave_caps = at_xdmac_device_slave_caps; + atxdmac->dma.src_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; + atxdmac->dma.dst_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; + atxdmac->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + atxdmac->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; /* Disable all chans and interrupts. */ at_xdmac_off(atxdmac); From b574368024670ab654d3aa79df0ed5a754790efe Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:45 +0100 Subject: [PATCH 052/360] dmaengine: bcm2835: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 13b05c1673b135..0723096fb50ac1 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -550,18 +550,6 @@ static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, return chan; } -static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); - caps->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = false; - caps->cmd_terminate = true; - - return 0; -} - static int bcm2835_dma_probe(struct platform_device *pdev) { struct bcm2835_dmadev *od; @@ -603,6 +591,9 @@ static int bcm2835_dma_probe(struct platform_device *pdev) od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; od->ddev.device_config = bcm2835_dma_slave_config; od->ddev.device_terminate_all = bcm2835_dma_terminate_all; + od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + od->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); spin_lock_init(&od->lock); From f45c431148e1ba0d7b2b9f9d106ec7aab6d00830 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:46 +0100 Subject: [PATCH 053/360] dmaengine: fsl-edma: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index d96a4aff2f4ee2..09e2842d15ecff 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -787,18 +787,6 @@ static void fsl_edma_free_chan_resources(struct dma_chan *chan) fsl_chan->tcd_pool = NULL; } -static int fsl_dma_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = FSL_EDMA_BUSWIDTHS; - caps->dst_addr_widths = FSL_EDMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = true; - caps->cmd_terminate = true; - - return 0; -} - static int fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) { @@ -929,7 +917,10 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_edma->dma_dev.device_resume = fsl_edma_resume; fsl_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; - fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps; + + fsl_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; + fsl_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; + fsl_edma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); platform_set_drvdata(pdev, fsl_edma); From 9f59cd0519c3b68bae7d9bef65d32722ce653a93 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:47 +0100 Subject: [PATCH 054/360] dmaengine: edma: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 8feb096786f814..e95fa7dabc0c87 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -971,19 +971,6 @@ static void __init edma_chan_init(struct edma_cc *ecc, BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) -static int edma_dma_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = EDMA_DMA_BUSWIDTHS; - caps->dst_addr_widths = EDMA_DMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = true; - caps->cmd_terminate = true; - caps->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - - return 0; -} - static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, struct device *dev) { @@ -998,7 +985,12 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_pause = edma_dma_pause; dma->device_resume = edma_dma_resume; dma->device_terminate_all = edma_terminate_all; - dma->device_slave_caps = edma_dma_device_slave_caps; + + dma->src_addr_widths = EDMA_DMA_BUSWIDTHS; + dma->dst_addr_widths = EDMA_DMA_BUSWIDTHS; + dma->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dma->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + dma->dev = dev; /* From 03526d3a67fd2f171f56ce0576d1b7b76ae7e23e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:48 +0100 Subject: [PATCH 055/360] dmaengine: nbpfaxi: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 0202602597f7a2..46c013b527cd15 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -1069,18 +1069,6 @@ static void nbpf_free_chan_resources(struct dma_chan *dchan) } } -static int nbpf_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = NBPF_DMA_BUSWIDTHS; - caps->dst_addr_widths = NBPF_DMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = false; - caps->cmd_terminate = true; - - return 0; -} - static struct dma_chan *nbpf_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -1411,7 +1399,6 @@ static int nbpf_probe(struct platform_device *pdev) dma_dev->device_prep_dma_memcpy = nbpf_prep_memcpy; dma_dev->device_tx_status = nbpf_tx_status; dma_dev->device_issue_pending = nbpf_issue_pending; - dma_dev->device_slave_caps = nbpf_slave_caps; /* * If we drop support for unaligned MEMCPY buffer addresses and / or @@ -1427,6 +1414,10 @@ static int nbpf_probe(struct platform_device *pdev) dma_dev->device_pause = nbpf_pause; dma_dev->device_terminate_all = nbpf_terminate_all; + dma_dev->src_addr_widths = NBPF_DMA_BUSWIDTHS; + dma_dev->dst_addr_widths = NBPF_DMA_BUSWIDTHS; + dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + platform_set_drvdata(pdev, nbpf); ret = clk_prepare_enable(nbpf->clk); From 7d15b87dd805bb41cec913fa3935ee242cb6ad3c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:49 +0100 Subject: [PATCH 056/360] dmaengine: omap: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/omap-dma.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index c84fe4a35022bb..2654057d46bda6 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -1070,19 +1070,6 @@ static void omap_dma_free(struct omap_dmadev *od) BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) -static int omap_dma_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = OMAP_DMA_BUSWIDTHS; - caps->dst_addr_widths = OMAP_DMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = true; - caps->cmd_terminate = true; - caps->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - - return 0; -} - static int omap_dma_probe(struct platform_device *pdev) { struct omap_dmadev *od; @@ -1116,7 +1103,10 @@ static int omap_dma_probe(struct platform_device *pdev) od->ddev.device_pause = omap_dma_pause; od->ddev.device_resume = omap_dma_resume; od->ddev.device_terminate_all = omap_dma_terminate_all; - od->ddev.device_slave_caps = omap_dma_device_slave_caps; + od->ddev.src_addr_widths = OMAP_DMA_BUSWIDTHS; + od->ddev.dst_addr_widths = OMAP_DMA_BUSWIDTHS; + od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); INIT_LIST_HEAD(&od->pending); From dcabe456b4d4d04606268036d8ca5ce84aa84037 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:50 +0100 Subject: [PATCH 057/360] dmaengine: pl330: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 5af72962edaddc..027f1d7ea4fcc8 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2608,19 +2608,6 @@ static irqreturn_t pl330_irq_handler(int irq, void *data) BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) -static int pl330_dma_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = PL330_DMA_BUSWIDTHS; - caps->dst_addr_widths = PL330_DMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = false; - caps->cmd_terminate = true; - caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; - - return 0; -} - /* * Runtime PM callbacks are provided by amba/bus.c driver. * @@ -2781,7 +2768,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_config = pl330_config; pd->device_terminate_all = pl330_terminate_all; pd->device_issue_pending = pl330_issue_pending; - pd->device_slave_caps = pl330_dma_device_slave_caps; + pd->src_addr_widths = PL330_DMA_BUSWIDTHS; + pd->dst_addr_widths = PL330_DMA_BUSWIDTHS; + pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + pd->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; ret = dma_async_device_register(pd); if (ret) { From 07ffa6ba7a364e4ad486fac16d655d4be6dda480 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:51 +0100 Subject: [PATCH 058/360] dmaengine: sirf: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/sirf-dma.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index fab9c9c1b421be..d0086e9f20824e 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -628,18 +628,6 @@ EXPORT_SYMBOL(sirfsoc_dma_filter_id); BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) -static int sirfsoc_dma_device_slave_caps(struct dma_chan *dchan, - struct dma_slave_caps *caps) -{ - caps->src_addr_widths = SIRFSOC_DMA_BUSWIDTHS; - caps->dst_addr_widths = SIRFSOC_DMA_BUSWIDTHS; - caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - caps->cmd_pause = true; - caps->cmd_terminate = true; - - return 0; -} - static struct dma_chan *of_dma_sirfsoc_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -726,7 +714,9 @@ static int sirfsoc_dma_probe(struct platform_device *op) dma->device_tx_status = sirfsoc_dma_tx_status; dma->device_prep_interleaved_dma = sirfsoc_dma_prep_interleaved; dma->device_prep_dma_cyclic = sirfsoc_dma_prep_cyclic; - dma->device_slave_caps = sirfsoc_dma_device_slave_caps; + dma->src_addr_widths = SIRFSOC_DMA_BUSWIDTHS; + dma->dst_addr_widths = SIRFSOC_DMA_BUSWIDTHS; + dma->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); INIT_LIST_HEAD(&dma->channels); dma_cap_set(DMA_SLAVE, dma->cap_mask); From 1cac81b4383d6be337fb9e17540c360311a3548f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:52 +0100 Subject: [PATCH 059/360] dmaengine: sun6i: Declare slave capabilities for the generic code Now that the generic slave caps code can make use of the device assigned capabilities, instead of relying on a callback to be implemented. Make use of this code. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/sun6i-dma.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 0e116399895fa3..7ebcf9bec6984c 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -968,7 +968,15 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.device_pause = sun6i_dma_pause; sdc->slave.device_resume = sun6i_dma_resume; sdc->slave.device_terminate_all = sun6i_dma_terminate_all; - + sdc->slave.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + sdc->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + sdc->slave.directions = BIT(DMA_DEV_TO_MEM) | + BIT(DMA_MEM_TO_DEV); + sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; sdc->slave.dev = &pdev->dev; sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels, From ecc19d17868be9c9f8f00ed928791533c420f3e0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:53 +0100 Subject: [PATCH 060/360] dmaengine: Add a warning for drivers not using the generic slave caps retrieval For the slave caps retrieval to be really useful, most drivers need to implement it. Hence, we need to be slightly more aggressive, and trigger a warning at registration time for drivers that don't fill their caps infos in order to encourage them to implement it. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index cae120994d8fd3..30211f9791b771 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -826,6 +826,9 @@ int dma_async_device_register(struct dma_device *device) BUG_ON(!device->device_issue_pending); BUG_ON(!device->dev); + WARN(dma_has_cap(DMA_SLAVE, device->cap_mask) && !device->directions, + "this driver doesn't support generic slave capabilities reporting\n"); + /* note: this only matters in the * CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=n case */ From 2c44ad914c56f4e53ef43285b5e4fe3459109769 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:54 +0100 Subject: [PATCH 061/360] dmaengine: Remove device_control and device_slave_caps Now that device_control has been split into several functions, and device_slave_caps rendered useless, we can safely remove them. Signed-off-by: Maxime Ripard Acked-by: Laurent Pinchart Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 52 +++++---------------------------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index adf22089cc9326..6d34ce91036ca8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -188,25 +188,6 @@ enum dma_ctrl_flags { DMA_PREP_FENCE = (1 << 5), }; -/** - * enum dma_ctrl_cmd - DMA operations that can optionally be exercised - * on a running channel. - * @DMA_TERMINATE_ALL: terminate all ongoing transfers - * @DMA_PAUSE: pause ongoing transfers - * @DMA_RESUME: resume paused transfer - * @DMA_SLAVE_CONFIG: this command is only implemented by DMA controllers - * that need to runtime reconfigure the slave channels (as opposed to passing - * configuration data in statically from the platform). An additional - * argument of struct dma_slave_config must be passed in with this - * command. - */ -enum dma_ctrl_cmd { - DMA_TERMINATE_ALL, - DMA_PAUSE, - DMA_RESUME, - DMA_SLAVE_CONFIG, -}; - /** * enum sum_check_bits - bit position of pq_check_flags */ @@ -336,9 +317,8 @@ enum dma_slave_buswidth { * This struct is passed in as configuration data to a DMA engine * in order to set up a certain channel for DMA transport at runtime. * The DMA device/engine has to provide support for an additional - * command in the channel config interface, DMA_SLAVE_CONFIG - * and this struct will then be passed in as an argument to the - * DMA engine device_control() function. + * callback in the dma_device structure, device_config and this struct + * will then be passed in as an argument to the function. * * The rationale for adding configuration information to this struct is as * follows: if it is likely that more than one DMA slave controllers in @@ -618,8 +598,6 @@ struct dma_tx_state { * @device_prep_interleaved_dma: Transfer expression in a generic way. * @device_config: Pushes a new configuration to a channel, return 0 or an error * code - * @device_control: manipulate all pending operations on a channel, returns - * zero or error code * @device_pause: Pauses any transfer happening on a channel. Returns * 0 or an error code * @device_resume: Resumes any transfer on a channel previously @@ -631,7 +609,6 @@ struct dma_tx_state { * struct with auxiliary transfer status information, otherwise the call * will just return a simple status code * @device_issue_pending: push pending transactions to hardware - * @device_slave_caps: return the slave channel capabilities */ struct dma_device { @@ -698,8 +675,6 @@ struct dma_device { int (*device_config)(struct dma_chan *chan, struct dma_slave_config *config); - int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg); int (*device_pause)(struct dma_chan *chan); int (*device_resume)(struct dma_chan *chan); int (*device_terminate_all)(struct dma_chan *chan); @@ -708,27 +683,15 @@ struct dma_device { dma_cookie_t cookie, struct dma_tx_state *txstate); void (*device_issue_pending)(struct dma_chan *chan); - int (*device_slave_caps)(struct dma_chan *chan, struct dma_slave_caps *caps); }; -static inline int dmaengine_device_control(struct dma_chan *chan, - enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - if (chan->device->device_control) - return chan->device->device_control(chan, cmd, arg); - - return -ENOSYS; -} - static inline int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config) { if (chan->device->device_config) return chan->device->device_config(chan, config); - return dmaengine_device_control(chan, DMA_SLAVE_CONFIG, - (unsigned long)config); + return -ENOSYS; } static inline bool is_slave_direction(enum dma_transfer_direction direction) @@ -808,9 +771,6 @@ static inline int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_cap if (!test_bit(DMA_SLAVE, device->cap_mask.bits)) return -ENXIO; - if (device->device_slave_caps) - return device->device_slave_caps(chan, caps); - /* * Check whether it reports it uses the generic slave * capabilities, if not, that means it doesn't support any @@ -835,7 +795,7 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan) if (chan->device->device_terminate_all) return chan->device->device_terminate_all(chan); - return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); + return -ENOSYS; } static inline int dmaengine_pause(struct dma_chan *chan) @@ -843,7 +803,7 @@ static inline int dmaengine_pause(struct dma_chan *chan) if (chan->device->device_pause) return chan->device->device_pause(chan); - return dmaengine_device_control(chan, DMA_PAUSE, 0); + return -ENOSYS; } static inline int dmaengine_resume(struct dma_chan *chan) @@ -851,7 +811,7 @@ static inline int dmaengine_resume(struct dma_chan *chan) if (chan->device->device_resume) return chan->device->device_resume(chan); - return dmaengine_device_control(chan, DMA_RESUME, 0); + return -ENOSYS; } static inline enum dma_status dmaengine_tx_status(struct dma_chan *chan, From 1faab1f2e3be3a10197840648d03a31fd0a29e93 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 17 Nov 2014 14:42:55 +0100 Subject: [PATCH 062/360] Documentation: dmaengine: Update the documentation Now that we have splitted device_control and removed device_slave_caps in favor of a few dma_device variables, update the documentation accordingly. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- Documentation/dmaengine/provider.txt | 93 +++++++++++++++------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/Documentation/dmaengine/provider.txt b/Documentation/dmaengine/provider.txt index 766658ccf235da..2c391cfe37eb23 100644 --- a/Documentation/dmaengine/provider.txt +++ b/Documentation/dmaengine/provider.txt @@ -113,6 +113,31 @@ need to initialize a few fields in there: * channels: should be initialized as a list using the INIT_LIST_HEAD macro for example + * src_addr_widths: + - should contain a bitmask of the supported source transfer width + + * dst_addr_widths: + - should contain a bitmask of the supported destination transfer + width + + * directions: + - should contain a bitmask of the supported slave directions + (i.e. excluding mem2mem transfers) + + * residue_granularity: + - Granularity of the transfer residue reported to dma_set_residue. + - This can be either: + + Descriptor + -> Your device doesn't support any kind of residue + reporting. The framework will only know that a particular + transaction descriptor is done. + + Segment + -> Your device is able to report which chunks have been + transferred + + Burst + -> Your device is able to report which burst have been + transferred + * dev: should hold the pointer to the struct device associated to your current driver instance. @@ -274,48 +299,32 @@ supported. account the current period. - This function can be called in an interrupt context. - * device_control - - Used by client drivers to control and configure the channel it - has a handle on. - - Called with a command and an argument - + The command is one of the values listed by the enum - dma_ctrl_cmd. The valid commands are: - + DMA_PAUSE - + Pauses a transfer on the channel - + This command should operate synchronously on the channel, - pausing right away the work of the given channel - + DMA_RESUME - + Restarts a transfer on the channel - + This command should operate synchronously on the channel, - resuming right away the work of the given channel - + DMA_TERMINATE_ALL - + Aborts all the pending and ongoing transfers on the - channel - + This command should operate synchronously on the channel, - terminating right away all the channels - + DMA_SLAVE_CONFIG - + Reconfigures the channel with passed configuration - + This command should NOT perform synchronously, or on any - currently queued transfers, but only on subsequent ones - + In this case, the function will receive a - dma_slave_config structure pointer as an argument, that - will detail which configuration to use. - + Even though that structure contains a direction field, - this field is deprecated in favor of the direction - argument given to the prep_* functions - + FSLDMA_EXTERNAL_START - + TODO: Why does that even exist? - + The argument is an opaque unsigned long. This actually is a - pointer to a struct dma_slave_config that should be used only - in the DMA_SLAVE_CONFIG. - - * device_slave_caps - - Called through the framework by client drivers in order to have - an idea of what are the properties of the channel allocated to - them. - - Such properties are the buswidth, available directions, etc. - - Required for every generic layer doing DMA transfers, such as - ASoC. + * device_config + - Reconfigures the channel with the configuration given as + argument + - This command should NOT perform synchronously, or on any + currently queued transfers, but only on subsequent ones + - In this case, the function will receive a dma_slave_config + structure pointer as an argument, that will detail which + configuration to use. + - Even though that structure contains a direction field, this + field is deprecated in favor of the direction argument given to + the prep_* functions + + * device_pause + - Pauses a transfer on the channel + - This command should operate synchronously on the channel, + pausing right away the work of the given channel + + * device_resume + - Resumes a transfer on the channel + - This command should operate synchronously on the channel, + pausing right away the work of the given channel + + * device_terminate_all + - Aborts all the pending and ongoing transfers on the channel + - This command should operate synchronously on the channel, + terminating right away all the channels Misc notes (stuff that should be documented, but don't really know where to put them) From e0cad7a00d86d8ebfac4f2d682e308508620de96 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 7 Dec 2014 23:07:38 +0530 Subject: [PATCH 063/360] dmaengine: mxs-dma: fix the arg to mxs_dma_reset_chan() mxs_dma_reset_chan() expects struct dma_chan * as argument but we were providing struct dma_chan, so fix this Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 834041e5a7691f..a24af4fc24e917 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -389,7 +389,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) "%s: error in channel %d\n", __func__, chan); mxs_chan->status = DMA_ERROR; - mxs_dma_reset_chan(mxs_chan->chan); + mxs_dma_reset_chan(&mxs_chan->chan); } else if (mxs_chan->status != DMA_COMPLETE) { if (mxs_chan->flags & MXS_DMA_SG_LOOP) { mxs_chan->status = DMA_IN_PROGRESS; From 6c04cd4f579cc365e7904aa92c48b9a9f8c768b5 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 7 Dec 2014 23:12:31 +0530 Subject: [PATCH 064/360] dmaengine: omap: fix the assignment to .device_config Signed-off-by: Vinod Koul --- drivers/dma/omap-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 2654057d46bda6..7dd6dd12168195 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -1099,7 +1099,7 @@ static int omap_dma_probe(struct platform_device *pdev) od->ddev.device_issue_pending = omap_dma_issue_pending; od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic; - od->ddev.device_config = omap_dma_config; + od->ddev.device_config = omap_dma_slave_config; od->ddev.device_pause = omap_dma_pause; od->ddev.device_resume = omap_dma_resume; od->ddev.device_terminate_all = omap_dma_terminate_all; From 6269591b989878992be443f77caa9ca4738dfaaf Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 7 Dec 2014 23:18:01 +0530 Subject: [PATCH 065/360] Documentation: dmaengine: clarify dma_slave_config expectations dma_slave_config is expected to be set for slave operations Only, not for memcpy ones Signed-off-by: Vinod Koul --- Documentation/dmaengine/provider.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/dmaengine/provider.txt b/Documentation/dmaengine/provider.txt index 2c391cfe37eb23..05d2280190f13d 100644 --- a/Documentation/dmaengine/provider.txt +++ b/Documentation/dmaengine/provider.txt @@ -310,6 +310,10 @@ supported. - Even though that structure contains a direction field, this field is deprecated in favor of the direction argument given to the prep_* functions + - This call is mandatory for slave operations only. This should NOT be + set or expected to be set for memcpy operations. + If a driver support both, it should use this call for slave + operations only and not for memcpy ones. * device_pause - Pauses a transfer on the channel From a29c3956369b0a993fc41d4ec29289587bd16249 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Dec 2014 11:24:09 +0530 Subject: [PATCH 066/360] dmaengine: mxs-dma: fix incompatible pointer type build warns drivers/dma/mxs-dma.c: In function 'mxs_dma_probe': drivers/dma/mxs-dma.c:848:35: warning: assignment from incompatible pointer type [enabled by default] drivers/dma/mxs-dma.c:849:36: warning: assignment from incompatible pointer type [enabled by default] The function prototype expects return type 'int' whereas these where void Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index a24af4fc24e917..bf286aa6b227ef 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -281,7 +281,7 @@ static void mxs_dma_disable_chan(struct dma_chan *chan) mxs_chan->status = DMA_COMPLETE; } -static void mxs_dma_pause_chan(struct dma_chan *chan) +static int mxs_dma_pause_chan(struct dma_chan *chan) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -296,9 +296,10 @@ static void mxs_dma_pause_chan(struct dma_chan *chan) mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET); mxs_chan->status = DMA_PAUSED; + return 0; } -static void mxs_dma_resume_chan(struct dma_chan *chan) +static int mxs_dma_resume_chan(struct dma_chan *chan) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -313,6 +314,7 @@ static void mxs_dma_resume_chan(struct dma_chan *chan) mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_CLR); mxs_chan->status = DMA_IN_PROGRESS; + return 0; } static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) From f67bcc404249ed3fdedec250b24c705564802f21 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Dec 2014 11:25:50 +0530 Subject: [PATCH 067/360] dmaengine: mxs-dma: fix unused variable warn drivers/dma/mxs-dma.c: In function 'mxs_dma_terminate_all': drivers/dma/mxs-dma.c:662:23: warning: unused variable 'mxs_chan'[-Wunused-variable] Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index bf286aa6b227ef..599ffb5b784836 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -661,8 +661,6 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( static int mxs_dma_terminate_all(struct dma_chan *chan) { - struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); - mxs_dma_reset_chan(chan); mxs_dma_disable_chan(chan); From 35e639d1f7571a1f194999910e4b0be6a81356ea Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Dec 2014 11:27:08 +0530 Subject: [PATCH 068/360] dmaengine: ste_dma: fix incompatible pointer type warns drivers/dma/ste_dma40.c:2627:3: warning: 'return' with a value, in function returning void [enabled by default] drivers/dma/ste_dma40.c: In function 'd40_ops_init': drivers/dma/ste_dma40.c:2869:28: warning: assignment from incompatible pointer type [enabled by default] The function prototype expects return type 'int' whereas these where void Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index e5a284865e9270..68aca3334a17e4 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2616,7 +2616,7 @@ static void d40_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&d40c->lock, flags); } -static void d40_terminate_all(struct dma_chan *chan) +static int d40_terminate_all(struct dma_chan *chan) { unsigned long flags; struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); @@ -2644,6 +2644,7 @@ static void d40_terminate_all(struct dma_chan *chan) d40c->busy = false; spin_unlock_irqrestore(&d40c->lock, flags); + return 0; } static int From a7c439a45807f1d39eac2161291cfabdc87067f3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Dec 2014 11:30:17 +0530 Subject: [PATCH 069/360] dmaengine: tegra: fix incompatible pointer type warns drivers/dma/tegra20-apb-dma.c:1428:37: warning: assignment from incompatible pointer type [enabled by default] drivers/dma/ste_dma40.c: In function 'd40_terminate_all': The function prototype expects return type 'int' whereas these where void Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 02f6013b83752d..5695fb8a7a244e 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -723,7 +723,7 @@ static void tegra_dma_issue_pending(struct dma_chan *dc) return; } -static void tegra_dma_terminate_all(struct dma_chan *dc) +static int tegra_dma_terminate_all(struct dma_chan *dc) { struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); struct tegra_dma_sg_req *sgreq; @@ -736,7 +736,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) spin_lock_irqsave(&tdc->lock, flags); if (list_empty(&tdc->pending_sg_req)) { spin_unlock_irqrestore(&tdc->lock, flags); - return; + return 0; } if (!tdc->busy) @@ -777,6 +777,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) dma_desc->cb_count = 0; } spin_unlock_irqrestore(&tdc->lock, flags); + return 0; } static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, From 3e1152a2f6c2cb7edee9d7459e7fbf2fc6c57f51 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 22 Dec 2014 20:24:14 +0530 Subject: [PATCH 070/360] dmaengine: at_hdmac: update the driver comments driver comment refers to DMA_SLAVE_CONFIG which needs to be updated to .device_config Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac_regs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 2787aba60c6bde..d6bba6c636c2b9 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -232,7 +232,8 @@ enum atc_status { * @save_dscr: for cyclic operations, preserve next descriptor address in * the cyclic list on suspend/resume cycle * @remain_desc: to save remain desc length - * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG + * @dma_sconfig: configuration for slave transfers, passed via + * .device_config * @lock: serializes enqueue/dequeue operations to descriptors lists * @active_list: list of descriptors dmaengine is being running on * @queue: list of descriptors ready to be submitted to engine From 295d3e10e68a37ac2850b9da32659cfdcd351f8b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 22 Dec 2014 20:24:14 +0530 Subject: [PATCH 071/360] dmaengine: dw: update the driver comments driver comment refers to DMA_SLAVE_CONFIG which needs to be updated to .device_config Signed-off-by: Vinod Koul --- drivers/dma/dw/regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 848e232f7cc755..254a1db0368079 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -252,7 +252,7 @@ struct dw_dma_chan { u8 src_master; u8 dst_master; - /* configuration passed via DMA_SLAVE_CONFIG */ + /* configuration passed via .device_config */ struct dma_slave_config dma_sconfig; }; From b2be07d001e32a080dd381a58503826ebd6397ed Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 22 Dec 2014 20:24:14 +0530 Subject: [PATCH 072/360] dmaengine: ep93xx: update the driver comments driver comment refers to DMA_SLAVE_CONFIG which needs to be updated to .device_config Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index a8bcbb5bc0edc6..24e5290faa3201 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -144,7 +144,7 @@ struct ep93xx_dma_desc { * @queue: pending descriptors which are handled next * @free_list: list of free descriptors which can be used * @runtime_addr: physical address currently used as dest/src (M2M only). This - * is set via %DMA_SLAVE_CONFIG before slave operation is + * is set via .device_config before slave operation is * prepared * @runtime_ctrl: M2M runtime values for the control register. * From fbde286783f7c1e55e6c33575fa1a56f8ea77c90 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 22 Dec 2014 20:24:14 +0530 Subject: [PATCH 073/360] dmaengine: nbpfaxi: update the driver comments driver comment refers to DMA_PAUSE which needs to be updated to .device_pause Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 46c013b527cd15..88b77c98365d88 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -504,7 +504,7 @@ static int nbpf_prep_one(struct nbpf_link_desc *ldesc, * pauses DMA and reads out data received via DMA as well as those left * in the Rx FIFO. For this to work with the RAM side using burst * transfers we enable the SBE bit and terminate the transfer in our - * DMA_PAUSE handler. + * .device_pause handler. */ mem_xfer = nbpf_xfer_ds(chan->nbpf, size); From 9265eaed9ca8bc0c24d90f9bd18b15d33465dca5 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 21 Dec 2014 18:18:22 +0100 Subject: [PATCH 074/360] dmaengine: imx-dma.c: Remove unused function Remove the function is_imx21_dma() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 10bbc0a675b07c..0c4d35da15021b 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -230,11 +230,6 @@ static inline int is_imx1_dma(struct imxdma_engine *imxdma) return imxdma->devtype == IMX1_DMA; } -static inline int is_imx21_dma(struct imxdma_engine *imxdma) -{ - return imxdma->devtype == IMX21_DMA; -} - static inline int is_imx27_dma(struct imxdma_engine *imxdma) { return imxdma->devtype == IMX27_DMA; From 41bed23b78bf79ee68dbdff88c0461271a61f893 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 11 Nov 2014 14:01:31 -0200 Subject: [PATCH 075/360] mtd: gpmi: Remove noisy error message mx28evk board has a socket for NAND flash that comes with no NAND flash populated, and then we get this message on every boot: [ 1.657603] gpmi-nand 8000c000.gpmi-nand: driver registration failed: -19 which is not very helpful, so get rid of this error message. Signed-off-by: Fabio Estevam Signed-off-by: Brian Norris --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 4f3851a24bb2df..f33e6645731176 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -2029,7 +2029,6 @@ static int gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); exit_acquire_resources: - dev_err(this->dev, "driver registration failed: %d\n", ret); return ret; } From c96736092464cd653a7cada7f4e3ca1df656a760 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 2 Dec 2014 19:37:09 +0100 Subject: [PATCH 076/360] mtd: nand: gpmi: remove deprecated comment Now that we have raw functions properly implemented we can remove this FIXME. Signed-off-by: Boris Brezillon Acked-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index f33e6645731176..33f3c3c54dbc76 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1294,14 +1294,6 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an * ECC-based or raw view of the page is implicit in which function it calls * (there is a similar pair of ECC-based/raw functions for writing). - * - * FIXME: The following paragraph is incorrect, now that there exist - * ecc.read_oob_raw and ecc.write_oob_raw functions. - * - * Since MTD assumes the OOB is not covered by ECC, there is no pair of - * ECC-based/raw functions for reading or or writing the OOB. The fact that the - * caller wants an ECC-based or raw view of the page is not propagated down to - * this driver. */ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) From ed0215cc3b5292fc0f4af70e29dc61fa2d0aa76c Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 27 Nov 2014 15:18:49 +0100 Subject: [PATCH 077/360] mtd: gpmi: Remove "We support only one NAND chip" from bindings doc This sentence "We support only one NAND chip now" is not true any more. Multiple chips are supported. So lets remove this sentence to not confuse anyone. Signed-off-by: Stefan Roese Cc: Huang Shijie Cc: Brian Norris Signed-off-by: Brian Norris --- Documentation/devicetree/bindings/mtd/gpmi-nand.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt index a011fdf61dbfae..d02acaff3c35e9 100644 --- a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt @@ -1,7 +1,7 @@ * Freescale General-Purpose Media Interface (GPMI) The GPMI nand controller provides an interface to control the -NAND flash chips. We support only one NAND chip now. +NAND flash chips. Required properties: - compatible : should be "fsl,-gpmi-nand" From 534a729866f9edc9264340c5b96cb94878ffda00 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 Aug 2014 10:52:41 +0200 Subject: [PATCH 078/360] dmaengine: Add 16 bytes, 32 bytes and 64 bytes bus widths The widths are missing, add them. Signed-off-by: Laurent Pinchart Tested-by: Kuninori Morimoto Tested-by: Wolfram Sang --- include/linux/dmaengine.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 6d34ce91036ca8..b7724a5d466176 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -279,6 +279,9 @@ enum dma_slave_buswidth { DMA_SLAVE_BUSWIDTH_3_BYTES = 3, DMA_SLAVE_BUSWIDTH_4_BYTES = 4, DMA_SLAVE_BUSWIDTH_8_BYTES = 8, + DMA_SLAVE_BUSWIDTH_16_BYTES = 16, + DMA_SLAVE_BUSWIDTH_32_BYTES = 32, + DMA_SLAVE_BUSWIDTH_64_BYTES = 64, }; /** From c4d7635280f24cfa8c3ef34c122600d148749030 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 6 Jul 2014 16:07:20 +0200 Subject: [PATCH 079/360] dmaengine: rcar-dmac: Remove duplicate sentence from DT bindings DT bindings are complex enough without expressing the same information twice in a slightly different way. Remove the duplicate. Reported-by: Geert Uytterhoeven Signed-off-by: Laurent Pinchart Tested-by: Kuninori Morimoto Tested-by: Wolfram Sang --- Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt index df0f48bcf75af4..5fb13ada29a9aa 100644 --- a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt +++ b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt @@ -5,9 +5,6 @@ controller instances named DMAC capable of serving multiple clients. Channels can be dedicated to specific clients or shared between a large number of clients. -DMA clients are connected to the DMAC ports referenced by an 8-bit identifier -called MID/RID. - Each DMA client is connected to one dedicated port of the DMAC, identified by an 8-bit port number called the MID/RID. A DMA controller can thus serve up to 256 clients in total. When the number of hardware channels is lower than the From 87244fe5abdf1dbaf4e438d80cf641bf3c01d5cf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Jul 2014 00:42:19 +0200 Subject: [PATCH 080/360] dmaengine: rcar-dmac: Add Renesas R-Car Gen2 DMA Controller (DMAC) driver The DMAC is a general purpose multi-channel DMA controller that supports both slave and memcpy transfers. The driver currently supports the DMAC found in the r8a7790 and r8a7791 SoCs. Support for compatible DMA controllers (such as the audio DMAC) will be added later. Feature-wise, automatic hardware handling of descriptors chains isn't supported yet. LPAE support is implemented. Signed-off-by: Laurent Pinchart Tested-by: Kuninori Morimoto Tested-by: Wolfram Sang --- drivers/dma/Makefile | 2 +- drivers/dma/sh/Kconfig | 14 +- drivers/dma/sh/Makefile | 1 + drivers/dma/sh/rcar-dmac.c | 1503 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1518 insertions(+), 2 deletions(-) create mode 100644 drivers/dma/sh/rcar-dmac.c diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 2022b5451377d5..b290e6a611d0ce 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -19,7 +19,7 @@ obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_AT_XDMAC) += at_xdmac.o obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o -obj-$(CONFIG_SH_DMAE_BASE) += sh/ +obj-$(CONFIG_RENESAS_DMA) += sh/ obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ obj-$(CONFIG_IMX_SDMA) += imx-sdma.o diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index 0349125a2e20af..8190ad225a1b80 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -2,6 +2,10 @@ # DMA engine configuration for sh # +config RENESAS_DMA + bool + select DMA_ENGINE + # # DMA Engine Helpers # @@ -12,7 +16,7 @@ config SH_DMAE_BASE depends on !SUPERH || SH_DMA depends on !SH_DMA_API default y - select DMA_ENGINE + select RENESAS_DMA help Enable support for the Renesas SuperH DMA controllers. @@ -52,3 +56,11 @@ config RCAR_AUDMAC_PP depends on SH_DMAE_BASE help Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers. + +config RCAR_DMAC + tristate "Renesas R-Car Gen2 DMA Controller" + depends on ARCH_SHMOBILE || COMPILE_TEST + select RENESAS_DMA + help + This driver supports the general purpose DMA controller found in the + Renesas R-Car second generation SoCs. diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index 0a5cfdb76e45ca..2852f9db61a40a 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_SH_DMAE) += shdma.o obj-$(CONFIG_SUDMAC) += sudmac.o obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o +obj-$(CONFIG_RCAR_DMAC) += rcar-dmac.o diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c new file mode 100644 index 00000000000000..89d40f9730ba13 --- /dev/null +++ b/drivers/dma/sh/rcar-dmac.c @@ -0,0 +1,1503 @@ +/* + * Renesas R-Car Gen2 DMA Controller Driver + * + * Copyright (C) 2014 Renesas Electronics Inc. + * + * Author: Laurent Pinchart + * + * This is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" + +/* + * struct rcar_dmac_xfer_chunk - Descriptor for a hardware transfer + * @node: entry in the parent's chunks list + * @src_addr: device source address + * @dst_addr: device destination address + * @size: transfer size in bytes + */ +struct rcar_dmac_xfer_chunk { + struct list_head node; + + dma_addr_t src_addr; + dma_addr_t dst_addr; + u32 size; +}; + +/* + * struct rcar_dmac_desc - R-Car Gen2 DMA Transfer Descriptor + * @async_tx: base DMA asynchronous transaction descriptor + * @direction: direction of the DMA transfer + * @xfer_shift: log2 of the transfer size + * @chcr: value of the channel configuration register for this transfer + * @node: entry in the channel's descriptors lists + * @chunks: list of transfer chunks for this transfer + * @running: the transfer chunk being currently processed + * @size: transfer size in bytes + * @cyclic: when set indicates that the DMA transfer is cyclic + */ +struct rcar_dmac_desc { + struct dma_async_tx_descriptor async_tx; + enum dma_transfer_direction direction; + unsigned int xfer_shift; + u32 chcr; + + struct list_head node; + struct list_head chunks; + struct rcar_dmac_xfer_chunk *running; + + unsigned int size; + bool cyclic; +}; + +#define to_rcar_dmac_desc(d) container_of(d, struct rcar_dmac_desc, async_tx) + +/* + * struct rcar_dmac_desc_page - One page worth of descriptors + * @node: entry in the channel's pages list + * @descs: array of DMA descriptors + * @chunks: array of transfer chunk descriptors + */ +struct rcar_dmac_desc_page { + struct list_head node; + + union { + struct rcar_dmac_desc descs[0]; + struct rcar_dmac_xfer_chunk chunks[0]; + }; +}; + +#define RCAR_DMAC_DESCS_PER_PAGE \ + ((PAGE_SIZE - offsetof(struct rcar_dmac_desc_page, descs)) / \ + sizeof(struct rcar_dmac_desc)) +#define RCAR_DMAC_XFER_CHUNKS_PER_PAGE \ + ((PAGE_SIZE - offsetof(struct rcar_dmac_desc_page, chunks)) / \ + sizeof(struct rcar_dmac_xfer_chunk)) + +/* + * struct rcar_dmac_chan - R-Car Gen2 DMA Controller Channel + * @chan: base DMA channel object + * @iomem: channel I/O memory base + * @index: index of this channel in the controller + * @src_xfer_size: size (in bytes) of hardware transfers on the source side + * @dst_xfer_size: size (in bytes) of hardware transfers on the destination side + * @src_slave_addr: slave source memory address + * @dst_slave_addr: slave destination memory address + * @mid_rid: hardware MID/RID for the DMA client using this channel + * @lock: protects the channel CHCR register and the desc members + * @desc.free: list of free descriptors + * @desc.pending: list of pending descriptors (submitted with tx_submit) + * @desc.active: list of active descriptors (activated with issue_pending) + * @desc.done: list of completed descriptors + * @desc.wait: list of descriptors waiting for an ack + * @desc.running: the descriptor being processed (a member of the active list) + * @desc.chunks_free: list of free transfer chunk descriptors + * @desc.pages: list of pages used by allocated descriptors + */ +struct rcar_dmac_chan { + struct dma_chan chan; + void __iomem *iomem; + unsigned int index; + + unsigned int src_xfer_size; + unsigned int dst_xfer_size; + dma_addr_t src_slave_addr; + dma_addr_t dst_slave_addr; + int mid_rid; + + spinlock_t lock; + + struct { + struct list_head free; + struct list_head pending; + struct list_head active; + struct list_head done; + struct list_head wait; + struct rcar_dmac_desc *running; + + struct list_head chunks_free; + + struct list_head pages; + } desc; +}; + +#define to_rcar_dmac_chan(c) container_of(c, struct rcar_dmac_chan, chan) + +/* + * struct rcar_dmac - R-Car Gen2 DMA Controller + * @engine: base DMA engine object + * @dev: the hardware device + * @iomem: remapped I/O memory base + * @n_channels: number of available channels + * @channels: array of DMAC channels + * @modules: bitmask of client modules in use + */ +struct rcar_dmac { + struct dma_device engine; + struct device *dev; + void __iomem *iomem; + + unsigned int n_channels; + struct rcar_dmac_chan *channels; + + unsigned long modules[256 / BITS_PER_LONG]; +}; + +#define to_rcar_dmac(d) container_of(d, struct rcar_dmac, engine) + +/* ----------------------------------------------------------------------------- + * Registers + */ + +#define RCAR_DMAC_CHAN_OFFSET(i) (0x8000 + 0x80 * (i)) + +#define RCAR_DMAISTA 0x0020 +#define RCAR_DMASEC 0x0030 +#define RCAR_DMAOR 0x0060 +#define RCAR_DMAOR_PRI_FIXED (0 << 8) +#define RCAR_DMAOR_PRI_ROUND_ROBIN (3 << 8) +#define RCAR_DMAOR_AE (1 << 2) +#define RCAR_DMAOR_DME (1 << 0) +#define RCAR_DMACHCLR 0x0080 +#define RCAR_DMADPSEC 0x00a0 + +#define RCAR_DMASAR 0x0000 +#define RCAR_DMADAR 0x0004 +#define RCAR_DMATCR 0x0008 +#define RCAR_DMATCR_MASK 0x00ffffff +#define RCAR_DMATSR 0x0028 +#define RCAR_DMACHCR 0x000c +#define RCAR_DMACHCR_CAE (1 << 31) +#define RCAR_DMACHCR_CAIE (1 << 30) +#define RCAR_DMACHCR_DPM_DISABLED (0 << 28) +#define RCAR_DMACHCR_DPM_ENABLED (1 << 28) +#define RCAR_DMACHCR_DPM_REPEAT (2 << 28) +#define RCAR_DMACHCR_DPM_INFINITE (3 << 28) +#define RCAR_DMACHCR_RPT_SAR (1 << 27) +#define RCAR_DMACHCR_RPT_DAR (1 << 26) +#define RCAR_DMACHCR_RPT_TCR (1 << 25) +#define RCAR_DMACHCR_DPB (1 << 22) +#define RCAR_DMACHCR_DSE (1 << 19) +#define RCAR_DMACHCR_DSIE (1 << 18) +#define RCAR_DMACHCR_TS_1B ((0 << 20) | (0 << 3)) +#define RCAR_DMACHCR_TS_2B ((0 << 20) | (1 << 3)) +#define RCAR_DMACHCR_TS_4B ((0 << 20) | (2 << 3)) +#define RCAR_DMACHCR_TS_16B ((0 << 20) | (3 << 3)) +#define RCAR_DMACHCR_TS_32B ((1 << 20) | (0 << 3)) +#define RCAR_DMACHCR_TS_64B ((1 << 20) | (1 << 3)) +#define RCAR_DMACHCR_TS_8B ((1 << 20) | (3 << 3)) +#define RCAR_DMACHCR_DM_FIXED (0 << 14) +#define RCAR_DMACHCR_DM_INC (1 << 14) +#define RCAR_DMACHCR_DM_DEC (2 << 14) +#define RCAR_DMACHCR_SM_FIXED (0 << 12) +#define RCAR_DMACHCR_SM_INC (1 << 12) +#define RCAR_DMACHCR_SM_DEC (2 << 12) +#define RCAR_DMACHCR_RS_AUTO (4 << 8) +#define RCAR_DMACHCR_RS_DMARS (8 << 8) +#define RCAR_DMACHCR_IE (1 << 2) +#define RCAR_DMACHCR_TE (1 << 1) +#define RCAR_DMACHCR_DE (1 << 0) +#define RCAR_DMATCRB 0x0018 +#define RCAR_DMATSRB 0x0038 +#define RCAR_DMACHCRB 0x001c +#define RCAR_DMACHCRB_DCNT(n) ((n) << 24) +#define RCAR_DMACHCRB_DPTR(n) ((n) << 16) +#define RCAR_DMACHCRB_DRST (1 << 15) +#define RCAR_DMACHCRB_DTS (1 << 8) +#define RCAR_DMACHCRB_SLM_NORMAL (0 << 4) +#define RCAR_DMACHCRB_SLM_CLK(n) ((8 | (n)) << 4) +#define RCAR_DMACHCRB_PRI(n) ((n) << 0) +#define RCAR_DMARS 0x0040 +#define RCAR_DMABUFCR 0x0048 +#define RCAR_DMABUFCR_MBU(n) ((n) << 16) +#define RCAR_DMABUFCR_ULB(n) ((n) << 0) +#define RCAR_DMADPBASE 0x0050 +#define RCAR_DMADPBASE_MASK 0xfffffff0 +#define RCAR_DMADPBASE_SEL (1 << 0) +#define RCAR_DMADPCR 0x0054 +#define RCAR_DMADPCR_DIPT(n) ((n) << 24) +#define RCAR_DMAFIXSAR 0x0010 +#define RCAR_DMAFIXDAR 0x0014 +#define RCAR_DMAFIXDPBASE 0x0060 + +/* Hardcode the MEMCPY transfer size to 4 bytes. */ +#define RCAR_DMAC_MEMCPY_XFER_SIZE 4 + +/* ----------------------------------------------------------------------------- + * Device access + */ + +static void rcar_dmac_write(struct rcar_dmac *dmac, u32 reg, u32 data) +{ + if (reg == RCAR_DMAOR) + writew(data, dmac->iomem + reg); + else + writel(data, dmac->iomem + reg); +} + +static u32 rcar_dmac_read(struct rcar_dmac *dmac, u32 reg) +{ + if (reg == RCAR_DMAOR) + return readw(dmac->iomem + reg); + else + return readl(dmac->iomem + reg); +} + +static u32 rcar_dmac_chan_read(struct rcar_dmac_chan *chan, u32 reg) +{ + if (reg == RCAR_DMARS) + return readw(chan->iomem + reg); + else + return readl(chan->iomem + reg); +} + +static void rcar_dmac_chan_write(struct rcar_dmac_chan *chan, u32 reg, u32 data) +{ + if (reg == RCAR_DMARS) + writew(data, chan->iomem + reg); + else + writel(data, chan->iomem + reg); +} + +/* ----------------------------------------------------------------------------- + * Initialization and configuration + */ + +static bool rcar_dmac_chan_is_busy(struct rcar_dmac_chan *chan) +{ + u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR); + + return (chcr & (RCAR_DMACHCR_DE | RCAR_DMACHCR_TE)) == RCAR_DMACHCR_DE; +} + +static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_desc *desc = chan->desc.running; + struct rcar_dmac_xfer_chunk *chunk = desc->running; + + dev_dbg(chan->chan.device->dev, + "chan%u: queue chunk %p: %u@%pad -> %pad\n", + chan->index, chunk, chunk->size, &chunk->src_addr, + &chunk->dst_addr); + + WARN_ON_ONCE(rcar_dmac_chan_is_busy(chan)); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + rcar_dmac_chan_write(chan, RCAR_DMAFIXSAR, chunk->src_addr >> 32); + rcar_dmac_chan_write(chan, RCAR_DMAFIXDAR, chunk->dst_addr >> 32); +#endif + rcar_dmac_chan_write(chan, RCAR_DMASAR, chunk->src_addr & 0xffffffff); + rcar_dmac_chan_write(chan, RCAR_DMADAR, chunk->dst_addr & 0xffffffff); + + if (chan->mid_rid >= 0) + rcar_dmac_chan_write(chan, RCAR_DMARS, chan->mid_rid); + + rcar_dmac_chan_write(chan, RCAR_DMATCR, + chunk->size >> desc->xfer_shift); + + rcar_dmac_chan_write(chan, RCAR_DMACHCR, desc->chcr | RCAR_DMACHCR_DE | + RCAR_DMACHCR_IE); +} + +static int rcar_dmac_init(struct rcar_dmac *dmac) +{ + u16 dmaor; + + /* Clear all channels and enable the DMAC globally. */ + rcar_dmac_write(dmac, RCAR_DMACHCLR, 0x7fff); + rcar_dmac_write(dmac, RCAR_DMAOR, + RCAR_DMAOR_PRI_FIXED | RCAR_DMAOR_DME); + + dmaor = rcar_dmac_read(dmac, RCAR_DMAOR); + if ((dmaor & (RCAR_DMAOR_AE | RCAR_DMAOR_DME)) != RCAR_DMAOR_DME) { + dev_warn(dmac->dev, "DMAOR initialization failed.\n"); + return -EIO; + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Descriptors submission + */ + +static dma_cookie_t rcar_dmac_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct rcar_dmac_chan *chan = to_rcar_dmac_chan(tx->chan); + struct rcar_dmac_desc *desc = to_rcar_dmac_desc(tx); + unsigned long flags; + dma_cookie_t cookie; + + spin_lock_irqsave(&chan->lock, flags); + + cookie = dma_cookie_assign(tx); + + dev_dbg(chan->chan.device->dev, "chan%u: submit #%d@%p\n", + chan->index, tx->cookie, desc); + + list_add_tail(&desc->node, &chan->desc.pending); + desc->running = list_first_entry(&desc->chunks, + struct rcar_dmac_xfer_chunk, node); + + spin_unlock_irqrestore(&chan->lock, flags); + + return cookie; +} + +/* ----------------------------------------------------------------------------- + * Descriptors allocation and free + */ + +/* + * rcar_dmac_desc_alloc - Allocate a page worth of DMA descriptors + * @chan: the DMA channel + * @gfp: allocation flags + */ +static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp) +{ + struct rcar_dmac_desc_page *page; + LIST_HEAD(list); + unsigned int i; + + page = (void *)get_zeroed_page(gfp); + if (!page) + return -ENOMEM; + + for (i = 0; i < RCAR_DMAC_DESCS_PER_PAGE; ++i) { + struct rcar_dmac_desc *desc = &page->descs[i]; + + dma_async_tx_descriptor_init(&desc->async_tx, &chan->chan); + desc->async_tx.tx_submit = rcar_dmac_tx_submit; + INIT_LIST_HEAD(&desc->chunks); + + list_add_tail(&desc->node, &list); + } + + spin_lock_irq(&chan->lock); + list_splice_tail(&list, &chan->desc.free); + list_add_tail(&page->node, &chan->desc.pages); + spin_unlock_irq(&chan->lock); + + return 0; +} + +/* + * rcar_dmac_desc_put - Release a DMA transfer descriptor + * @chan: the DMA channel + * @desc: the descriptor + * + * Put the descriptor and its transfer chunk descriptors back in the channel's + * free descriptors lists. The descriptor's chunk will be reinitialized to an + * empty list as a result. + * + * The descriptor must have been removed from the channel's done list before + * calling this function. + * + * Locking: Must be called with the channel lock held. + */ +static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan, + struct rcar_dmac_desc *desc) +{ + list_splice_tail_init(&desc->chunks, &chan->desc.chunks_free); + list_add_tail(&desc->node, &chan->desc.free); +} + +static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_desc *desc, *_desc; + + list_for_each_entry_safe(desc, _desc, &chan->desc.wait, node) { + if (async_tx_test_ack(&desc->async_tx)) { + list_del(&desc->node); + rcar_dmac_desc_put(chan, desc); + } + } +} + +/* + * rcar_dmac_desc_get - Allocate a descriptor for a DMA transfer + * @chan: the DMA channel + * + * Locking: This function must be called in a non-atomic context. + * + * Return: A pointer to the allocated descriptor or NULL if no descriptor can + * be allocated. + */ +static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_desc *desc; + int ret; + + spin_lock_irq(&chan->lock); + + /* Recycle acked descriptors before attempting allocation. */ + rcar_dmac_desc_recycle_acked(chan); + + do { + if (list_empty(&chan->desc.free)) { + /* + * No free descriptors, allocate a page worth of them + * and try again, as someone else could race us to get + * the newly allocated descriptors. If the allocation + * fails return an error. + */ + spin_unlock_irq(&chan->lock); + ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT); + if (ret < 0) + return NULL; + spin_lock_irq(&chan->lock); + continue; + } + + desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, + node); + list_del(&desc->node); + } while (!desc); + + spin_unlock_irq(&chan->lock); + + return desc; +} + +/* + * rcar_dmac_xfer_chunk_alloc - Allocate a page worth of transfer chunks + * @chan: the DMA channel + * @gfp: allocation flags + */ +static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp) +{ + struct rcar_dmac_desc_page *page; + LIST_HEAD(list); + unsigned int i; + + page = (void *)get_zeroed_page(gfp); + if (!page) + return -ENOMEM; + + for (i = 0; i < RCAR_DMAC_XFER_CHUNKS_PER_PAGE; ++i) { + struct rcar_dmac_xfer_chunk *chunk = &page->chunks[i]; + + list_add_tail(&chunk->node, &list); + } + + spin_lock_irq(&chan->lock); + list_splice_tail(&list, &chan->desc.chunks_free); + list_add_tail(&page->node, &chan->desc.pages); + spin_unlock_irq(&chan->lock); + + return 0; +} + +/* + * rcar_dmac_xfer_chunk_get - Allocate a transfer chunk for a DMA transfer + * @chan: the DMA channel + * + * Locking: This function must be called in a non-atomic context. + * + * Return: A pointer to the allocated transfer chunk descriptor or NULL if no + * descriptor can be allocated. + */ +static struct rcar_dmac_xfer_chunk * +rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_xfer_chunk *chunk; + int ret; + + spin_lock_irq(&chan->lock); + + do { + if (list_empty(&chan->desc.chunks_free)) { + /* + * No free descriptors, allocate a page worth of them + * and try again, as someone else could race us to get + * the newly allocated descriptors. If the allocation + * fails return an error. + */ + spin_unlock_irq(&chan->lock); + ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT); + if (ret < 0) + return NULL; + spin_lock_irq(&chan->lock); + continue; + } + + chunk = list_first_entry(&chan->desc.chunks_free, + struct rcar_dmac_xfer_chunk, node); + list_del(&chunk->node); + } while (!chunk); + + spin_unlock_irq(&chan->lock); + + return chunk; +} + +/* ----------------------------------------------------------------------------- + * Stop and reset + */ + +static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan) +{ + u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR); + + chcr &= ~(RCAR_DMACHCR_IE | RCAR_DMACHCR_TE | RCAR_DMACHCR_DE); + rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr); +} + +static void rcar_dmac_chan_reinit(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(descs); + + spin_lock_irqsave(&chan->lock, flags); + + /* Move all non-free descriptors to the local lists. */ + list_splice_init(&chan->desc.pending, &descs); + list_splice_init(&chan->desc.active, &descs); + list_splice_init(&chan->desc.done, &descs); + list_splice_init(&chan->desc.wait, &descs); + + chan->desc.running = NULL; + + spin_unlock_irqrestore(&chan->lock, flags); + + list_for_each_entry_safe(desc, _desc, &descs, node) { + list_del(&desc->node); + rcar_dmac_desc_put(chan, desc); + } +} + +static void rcar_dmac_stop(struct rcar_dmac *dmac) +{ + rcar_dmac_write(dmac, RCAR_DMAOR, 0); +} + +static void rcar_dmac_abort(struct rcar_dmac *dmac) +{ + unsigned int i; + + /* Stop all channels. */ + for (i = 0; i < dmac->n_channels; ++i) { + struct rcar_dmac_chan *chan = &dmac->channels[i]; + + /* Stop and reinitialize the channel. */ + spin_lock(&chan->lock); + rcar_dmac_chan_halt(chan); + spin_unlock(&chan->lock); + + rcar_dmac_chan_reinit(chan); + } +} + +/* ----------------------------------------------------------------------------- + * Descriptors preparation + */ + +static void rcar_dmac_chan_configure_desc(struct rcar_dmac_chan *chan, + struct rcar_dmac_desc *desc) +{ + static const u32 chcr_ts[] = { + RCAR_DMACHCR_TS_1B, RCAR_DMACHCR_TS_2B, + RCAR_DMACHCR_TS_4B, RCAR_DMACHCR_TS_8B, + RCAR_DMACHCR_TS_16B, RCAR_DMACHCR_TS_32B, + RCAR_DMACHCR_TS_64B, + }; + + unsigned int xfer_size; + u32 chcr; + + switch (desc->direction) { + case DMA_DEV_TO_MEM: + chcr = RCAR_DMACHCR_DM_INC | RCAR_DMACHCR_SM_FIXED + | RCAR_DMACHCR_RS_DMARS; + xfer_size = chan->src_xfer_size; + break; + + case DMA_MEM_TO_DEV: + chcr = RCAR_DMACHCR_DM_FIXED | RCAR_DMACHCR_SM_INC + | RCAR_DMACHCR_RS_DMARS; + xfer_size = chan->dst_xfer_size; + break; + + case DMA_MEM_TO_MEM: + default: + chcr = RCAR_DMACHCR_DM_INC | RCAR_DMACHCR_SM_INC + | RCAR_DMACHCR_RS_AUTO; + xfer_size = RCAR_DMAC_MEMCPY_XFER_SIZE; + break; + } + + desc->xfer_shift = ilog2(xfer_size); + desc->chcr = chcr | chcr_ts[desc->xfer_shift]; +} + +/* + * rcar_dmac_chan_prep_sg - prepare transfer descriptors from an SG list + * + * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also + * converted to scatter-gather to guarantee consistent locking and a correct + * list manipulation. For slave DMA direction carries the usual meaning, and, + * logically, the SG list is RAM and the addr variable contains slave address, + * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM + * and the SG list contains only one element and points at the source buffer. + */ +static struct dma_async_tx_descriptor * +rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, dma_addr_t dev_addr, + enum dma_transfer_direction dir, unsigned long dma_flags, + bool cyclic) +{ + struct rcar_dmac_xfer_chunk *chunk; + struct rcar_dmac_desc *desc; + struct scatterlist *sg; + unsigned int max_chunk_size; + unsigned int full_size = 0; + unsigned int i; + + desc = rcar_dmac_desc_get(chan); + if (!desc) + return NULL; + + desc->async_tx.flags = dma_flags; + desc->async_tx.cookie = -EBUSY; + + desc->cyclic = cyclic; + desc->direction = dir; + + rcar_dmac_chan_configure_desc(chan, desc); + + max_chunk_size = (RCAR_DMATCR_MASK + 1) << desc->xfer_shift; + + /* + * Allocate and fill the transfer chunk descriptors. We own the only + * reference to the DMA descriptor, there's no need for locking. + */ + for_each_sg(sgl, sg, sg_len, i) { + dma_addr_t mem_addr = sg_dma_address(sg); + unsigned int len = sg_dma_len(sg); + + full_size += len; + + while (len) { + unsigned int size = min(len, max_chunk_size); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + /* + * Prevent individual transfers from crossing 4GB + * boundaries. + */ + if (dev_addr >> 32 != (dev_addr + size - 1) >> 32) + size = ALIGN(dev_addr, 1ULL << 32) - dev_addr; + if (mem_addr >> 32 != (mem_addr + size - 1) >> 32) + size = ALIGN(mem_addr, 1ULL << 32) - mem_addr; +#endif + + chunk = rcar_dmac_xfer_chunk_get(chan); + if (!chunk) { + rcar_dmac_desc_put(chan, desc); + return NULL; + } + + if (dir == DMA_DEV_TO_MEM) { + chunk->src_addr = dev_addr; + chunk->dst_addr = mem_addr; + } else { + chunk->src_addr = mem_addr; + chunk->dst_addr = dev_addr; + } + + chunk->size = size; + + dev_dbg(chan->chan.device->dev, + "chan%u: chunk %p/%p sgl %u@%p, %u/%u %pad -> %pad\n", + chan->index, chunk, desc, i, sg, size, len, + &chunk->src_addr, &chunk->dst_addr); + + mem_addr += size; + if (dir == DMA_MEM_TO_MEM) + dev_addr += size; + + len -= size; + + list_add_tail(&chunk->node, &desc->chunks); + } + } + + desc->size = full_size; + + return &desc->async_tx; +} + +/* ----------------------------------------------------------------------------- + * DMA engine operations + */ + +static int rcar_dmac_alloc_chan_resources(struct dma_chan *chan) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + int ret; + + INIT_LIST_HEAD(&rchan->desc.free); + INIT_LIST_HEAD(&rchan->desc.pending); + INIT_LIST_HEAD(&rchan->desc.active); + INIT_LIST_HEAD(&rchan->desc.done); + INIT_LIST_HEAD(&rchan->desc.wait); + INIT_LIST_HEAD(&rchan->desc.chunks_free); + INIT_LIST_HEAD(&rchan->desc.pages); + + /* Preallocate descriptors. */ + ret = rcar_dmac_xfer_chunk_alloc(rchan, GFP_KERNEL); + if (ret < 0) + return -ENOMEM; + + ret = rcar_dmac_desc_alloc(rchan, GFP_KERNEL); + if (ret < 0) + return -ENOMEM; + + return pm_runtime_get_sync(chan->device->dev); +} + +static void rcar_dmac_free_chan_resources(struct dma_chan *chan) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + struct rcar_dmac *dmac = to_rcar_dmac(chan->device); + struct rcar_dmac_desc_page *page, *_page; + + /* Protect against ISR */ + spin_lock_irq(&rchan->lock); + rcar_dmac_chan_halt(rchan); + spin_unlock_irq(&rchan->lock); + + /* Now no new interrupts will occur */ + + if (rchan->mid_rid >= 0) { + /* The caller is holding dma_list_mutex */ + clear_bit(rchan->mid_rid, dmac->modules); + rchan->mid_rid = -EINVAL; + } + + list_for_each_entry_safe(page, _page, &rchan->desc.pages, node) { + list_del(&page->node); + free_page((unsigned long)page); + } + + pm_runtime_put(chan->device->dev); +} + +static struct dma_async_tx_descriptor * +rcar_dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, unsigned long flags) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + struct scatterlist sgl; + + if (!len) + return NULL; + + sg_init_table(&sgl, 1); + sg_set_page(&sgl, pfn_to_page(PFN_DOWN(dma_src)), len, + offset_in_page(dma_src)); + sg_dma_address(&sgl) = dma_src; + sg_dma_len(&sgl) = len; + + return rcar_dmac_chan_prep_sg(rchan, &sgl, 1, dma_dest, + DMA_MEM_TO_MEM, flags, false); +} + +static struct dma_async_tx_descriptor * +rcar_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags, void *context) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + dma_addr_t dev_addr; + + /* Someone calling slave DMA on a generic channel? */ + if (rchan->mid_rid < 0 || !sg_len) { + dev_warn(chan->device->dev, + "%s: bad parameter: len=%d, id=%d\n", + __func__, sg_len, rchan->mid_rid); + return NULL; + } + + dev_addr = dir == DMA_DEV_TO_MEM + ? rchan->src_slave_addr : rchan->dst_slave_addr; + return rcar_dmac_chan_prep_sg(rchan, sgl, sg_len, dev_addr, + dir, flags, false); +} + +#define RCAR_DMAC_MAX_SG_LEN 32 + +static struct dma_async_tx_descriptor * +rcar_dmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, + size_t buf_len, size_t period_len, + enum dma_transfer_direction dir, unsigned long flags) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; + dma_addr_t dev_addr; + unsigned int sg_len; + unsigned int i; + + /* Someone calling slave DMA on a generic channel? */ + if (rchan->mid_rid < 0 || buf_len < period_len) { + dev_warn(chan->device->dev, + "%s: bad parameter: buf_len=%zu, period_len=%zu, id=%d\n", + __func__, buf_len, period_len, rchan->mid_rid); + return NULL; + } + + sg_len = buf_len / period_len; + if (sg_len > RCAR_DMAC_MAX_SG_LEN) { + dev_err(chan->device->dev, + "chan%u: sg length %d exceds limit %d", + rchan->index, sg_len, RCAR_DMAC_MAX_SG_LEN); + return NULL; + } + + /* + * Allocate the sg list dynamically as it would consume too much stack + * space. + */ + sgl = kcalloc(sg_len, sizeof(*sgl), GFP_NOWAIT); + if (!sgl) + return NULL; + + sg_init_table(sgl, sg_len); + + for (i = 0; i < sg_len; ++i) { + dma_addr_t src = buf_addr + (period_len * i); + + sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(src)), period_len, + offset_in_page(src)); + sg_dma_address(&sgl[i]) = src; + sg_dma_len(&sgl[i]) = period_len; + } + + dev_addr = dir == DMA_DEV_TO_MEM + ? rchan->src_slave_addr : rchan->dst_slave_addr; + desc = rcar_dmac_chan_prep_sg(rchan, sgl, sg_len, dev_addr, + dir, flags, true); + + kfree(sgl); + return desc; +} + +static int rcar_dmac_device_config(struct dma_chan *chan, + struct dma_slave_config *cfg) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + + /* + * We could lock this, but you shouldn't be configuring the + * channel, while using it... + */ + rchan->src_slave_addr = cfg->src_addr; + rchan->dst_slave_addr = cfg->dst_addr; + rchan->src_xfer_size = cfg->src_addr_width; + rchan->dst_xfer_size = cfg->dst_addr_width; + + return 0; +} + +static int rcar_dmac_chan_terminate_all(struct dma_chan *chan) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&rchan->lock, flags); + rcar_dmac_chan_halt(rchan); + spin_unlock_irqrestore(&rchan->lock, flags); + + /* + * FIXME: No new interrupt can occur now, but the IRQ thread might still + * be running. + */ + + rcar_dmac_chan_reinit(rchan); + + return 0; +} + +static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan, + dma_cookie_t cookie) +{ + struct rcar_dmac_desc *desc = chan->desc.running; + struct rcar_dmac_xfer_chunk *chunk; + unsigned int residue = 0; + + if (!desc) + return 0; + + /* + * If the cookie doesn't correspond to the currently running transfer + * then the descriptor hasn't been processed yet, and the residue is + * equal to the full descriptor size. + */ + if (cookie != desc->async_tx.cookie) + return desc->size; + + /* Compute the size of all chunks still to be transferred. */ + list_for_each_entry_reverse(chunk, &desc->chunks, node) { + if (chunk == desc->running) + break; + + residue += chunk->size; + } + + /* Add the residue for the current chunk. */ + residue += rcar_dmac_chan_read(chan, RCAR_DMATCR) << desc->xfer_shift; + + return residue; +} + +static enum dma_status rcar_dmac_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + enum dma_status status; + unsigned long flags; + unsigned int residue; + + status = dma_cookie_status(chan, cookie, txstate); + if (status == DMA_COMPLETE || !txstate) + return status; + + spin_lock_irqsave(&rchan->lock, flags); + residue = rcar_dmac_chan_get_residue(rchan, cookie); + spin_unlock_irqrestore(&rchan->lock, flags); + + dma_set_residue(txstate, residue); + + return status; +} + +static void rcar_dmac_issue_pending(struct dma_chan *chan) +{ + struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&rchan->lock, flags); + + if (list_empty(&rchan->desc.pending)) + goto done; + + /* Append the pending list to the active list. */ + list_splice_tail_init(&rchan->desc.pending, &rchan->desc.active); + + /* + * If no transfer is running pick the first descriptor from the active + * list and start the transfer. + */ + if (!rchan->desc.running) { + struct rcar_dmac_desc *desc; + + desc = list_first_entry(&rchan->desc.active, + struct rcar_dmac_desc, node); + rchan->desc.running = desc; + + rcar_dmac_chan_start_xfer(rchan); + } + +done: + spin_unlock_irqrestore(&rchan->lock, flags); +} + +/* ----------------------------------------------------------------------------- + * IRQ handling + */ + +static irqreturn_t rcar_dmac_isr_transfer_end(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_desc *desc = chan->desc.running; + struct rcar_dmac_xfer_chunk *chunk; + irqreturn_t ret = IRQ_WAKE_THREAD; + + if (WARN_ON_ONCE(!desc)) { + /* + * This should never happen, there should always be + * a running descriptor when a transfer ends. Warn and + * return. + */ + return IRQ_NONE; + } + + /* + * If we haven't completed the last transfer chunk simply move to the + * next one. Only wake the IRQ thread if the transfer is cyclic. + */ + chunk = desc->running; + if (!list_is_last(&chunk->node, &desc->chunks)) { + desc->running = list_next_entry(chunk, node); + if (!desc->cyclic) + ret = IRQ_HANDLED; + goto done; + } + + /* + * We've completed the last transfer chunk. If the transfer is cyclic, + * move back to the first one. + */ + if (desc->cyclic) { + desc->running = list_first_entry(&desc->chunks, + struct rcar_dmac_xfer_chunk, + node); + goto done; + } + + /* The descriptor is complete, move it to the done list. */ + list_move_tail(&desc->node, &chan->desc.done); + + /* Queue the next descriptor, if any. */ + if (!list_empty(&chan->desc.active)) + chan->desc.running = list_first_entry(&chan->desc.active, + struct rcar_dmac_desc, + node); + else + chan->desc.running = NULL; + +done: + if (chan->desc.running) + rcar_dmac_chan_start_xfer(chan); + + return ret; +} + +static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev) +{ + struct rcar_dmac_chan *chan = dev; + irqreturn_t ret = IRQ_NONE; + u32 chcr; + + spin_lock(&chan->lock); + + chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR); + rcar_dmac_chan_write(chan, RCAR_DMACHCR, + chcr & ~(RCAR_DMACHCR_TE | RCAR_DMACHCR_DE)); + + if (chcr & RCAR_DMACHCR_TE) + ret |= rcar_dmac_isr_transfer_end(chan); + + spin_unlock(&chan->lock); + + return ret; +} + +static irqreturn_t rcar_dmac_isr_channel_thread(int irq, void *dev) +{ + struct rcar_dmac_chan *chan = dev; + struct rcar_dmac_desc *desc; + + spin_lock_irq(&chan->lock); + + /* For cyclic transfers notify the user after every chunk. */ + if (chan->desc.running && chan->desc.running->cyclic) { + dma_async_tx_callback callback; + void *callback_param; + + desc = chan->desc.running; + callback = desc->async_tx.callback; + callback_param = desc->async_tx.callback_param; + + if (callback) { + spin_unlock_irq(&chan->lock); + callback(callback_param); + spin_lock_irq(&chan->lock); + } + } + + /* + * Call the callback function for all descriptors on the done list and + * move them to the ack wait list. + */ + while (!list_empty(&chan->desc.done)) { + desc = list_first_entry(&chan->desc.done, struct rcar_dmac_desc, + node); + dma_cookie_complete(&desc->async_tx); + list_del(&desc->node); + + if (desc->async_tx.callback) { + spin_unlock_irq(&chan->lock); + /* + * We own the only reference to this descriptor, we can + * safely dereference it without holding the channel + * lock. + */ + desc->async_tx.callback(desc->async_tx.callback_param); + spin_lock_irq(&chan->lock); + } + + list_add_tail(&desc->node, &chan->desc.wait); + } + + /* Recycle all acked descriptors. */ + rcar_dmac_desc_recycle_acked(chan); + + spin_unlock_irq(&chan->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t rcar_dmac_isr_error(int irq, void *data) +{ + struct rcar_dmac *dmac = data; + + if (!(rcar_dmac_read(dmac, RCAR_DMAOR) & RCAR_DMAOR_AE)) + return IRQ_NONE; + + /* + * An unrecoverable error occurred on an unknown channel. Halt the DMAC, + * abort transfers on all channels, and reinitialize the DMAC. + */ + rcar_dmac_stop(dmac); + rcar_dmac_abort(dmac); + rcar_dmac_init(dmac); + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * OF xlate and channel filter + */ + +static bool rcar_dmac_chan_filter(struct dma_chan *chan, void *arg) +{ + struct rcar_dmac *dmac = to_rcar_dmac(chan->device); + struct of_phandle_args *dma_spec = arg; + + /* + * FIXME: Using a filter on OF platforms is a nonsense. The OF xlate + * function knows from which device it wants to allocate a channel from, + * and would be perfectly capable of selecting the channel it wants. + * Forcing it to call dma_request_channel() and iterate through all + * channels from all controllers is just pointless. + */ + if (chan->device->device_config != rcar_dmac_device_config || + dma_spec->np != chan->device->dev->of_node) + return false; + + return !test_and_set_bit(dma_spec->args[0], dmac->modules); +} + +static struct dma_chan *rcar_dmac_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct rcar_dmac_chan *rchan; + struct dma_chan *chan; + dma_cap_mask_t mask; + + if (dma_spec->args_count != 1) + return NULL; + + /* Only slave DMA channels can be allocated via DT */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, rcar_dmac_chan_filter, dma_spec); + if (!chan) + return NULL; + + rchan = to_rcar_dmac_chan(chan); + rchan->mid_rid = dma_spec->args[0]; + + return chan; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +#ifdef CONFIG_PM_SLEEP +static int rcar_dmac_sleep_suspend(struct device *dev) +{ + /* + * TODO: Wait for the current transfer to complete and stop the device. + */ + return 0; +} + +static int rcar_dmac_sleep_resume(struct device *dev) +{ + /* TODO: Resume transfers, if any. */ + return 0; +} +#endif + +#ifdef CONFIG_PM +static int rcar_dmac_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int rcar_dmac_runtime_resume(struct device *dev) +{ + struct rcar_dmac *dmac = dev_get_drvdata(dev); + + return rcar_dmac_init(dmac); +} +#endif + +static const struct dev_pm_ops rcar_dmac_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rcar_dmac_sleep_suspend, rcar_dmac_sleep_resume) + SET_RUNTIME_PM_OPS(rcar_dmac_runtime_suspend, rcar_dmac_runtime_resume, + NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe and remove + */ + +static int rcar_dmac_chan_probe(struct rcar_dmac *dmac, + struct rcar_dmac_chan *rchan, + unsigned int index) +{ + struct platform_device *pdev = to_platform_device(dmac->dev); + struct dma_chan *chan = &rchan->chan; + char pdev_irqname[5]; + char *irqname; + int irq; + int ret; + + rchan->index = index; + rchan->iomem = dmac->iomem + RCAR_DMAC_CHAN_OFFSET(index); + rchan->mid_rid = -EINVAL; + + spin_lock_init(&rchan->lock); + + /* Request the channel interrupt. */ + sprintf(pdev_irqname, "ch%u", index); + irq = platform_get_irq_byname(pdev, pdev_irqname); + if (irq < 0) { + dev_err(dmac->dev, "no IRQ specified for channel %u\n", index); + return -ENODEV; + } + + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", + dev_name(dmac->dev), index); + if (!irqname) + return -ENOMEM; + + ret = devm_request_threaded_irq(dmac->dev, irq, rcar_dmac_isr_channel, + rcar_dmac_isr_channel_thread, 0, + irqname, rchan); + if (ret) { + dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", irq, ret); + return ret; + } + + /* + * Initialize the DMA engine channel and add it to the DMA engine + * channels list. + */ + chan->device = &dmac->engine; + dma_cookie_init(chan); + + list_add_tail(&chan->device_node, &dmac->engine.channels); + + return 0; +} + +static int rcar_dmac_parse_of(struct device *dev, struct rcar_dmac *dmac) +{ + struct device_node *np = dev->of_node; + int ret; + + ret = of_property_read_u32(np, "dma-channels", &dmac->n_channels); + if (ret < 0) { + dev_err(dev, "unable to read dma-channels property\n"); + return ret; + } + + if (dmac->n_channels <= 0 || dmac->n_channels >= 100) { + dev_err(dev, "invalid number of channels %u\n", + dmac->n_channels); + return -EINVAL; + } + + return 0; +} + +static int rcar_dmac_probe(struct platform_device *pdev) +{ + const enum dma_slave_buswidth widths = DMA_SLAVE_BUSWIDTH_1_BYTE | + DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES | + DMA_SLAVE_BUSWIDTH_8_BYTES | DMA_SLAVE_BUSWIDTH_16_BYTES | + DMA_SLAVE_BUSWIDTH_32_BYTES | DMA_SLAVE_BUSWIDTH_64_BYTES; + struct dma_device *engine; + struct rcar_dmac *dmac; + struct resource *mem; + unsigned int i; + char *irqname; + int irq; + int ret; + + dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); + if (!dmac) + return -ENOMEM; + + dmac->dev = &pdev->dev; + platform_set_drvdata(pdev, dmac); + + ret = rcar_dmac_parse_of(&pdev->dev, dmac); + if (ret < 0) + return ret; + + dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels, + sizeof(*dmac->channels), GFP_KERNEL); + if (!dmac->channels) + return -ENOMEM; + + /* Request resources. */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmac->iomem = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dmac->iomem)) + return PTR_ERR(dmac->iomem); + + irq = platform_get_irq_byname(pdev, "error"); + if (irq < 0) { + dev_err(&pdev->dev, "no error IRQ specified\n"); + return -ENODEV; + } + + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:error", + dev_name(dmac->dev)); + if (!irqname) + return -ENOMEM; + + ret = devm_request_irq(&pdev->dev, irq, rcar_dmac_isr_error, 0, + irqname, dmac); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", + irq, ret); + return ret; + } + + /* Enable runtime PM and initialize the device. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "runtime PM get sync failed (%d)\n", ret); + return ret; + } + + ret = rcar_dmac_init(dmac); + pm_runtime_put(&pdev->dev); + + if (ret) { + dev_err(&pdev->dev, "failed to reset device\n"); + goto error; + } + + /* Initialize the channels. */ + INIT_LIST_HEAD(&dmac->engine.channels); + + for (i = 0; i < dmac->n_channels; ++i) { + ret = rcar_dmac_chan_probe(dmac, &dmac->channels[i], i); + if (ret < 0) + goto error; + } + + /* Register the DMAC as a DMA provider for DT. */ + ret = of_dma_controller_register(pdev->dev.of_node, rcar_dmac_of_xlate, + NULL); + if (ret < 0) + goto error; + + /* + * Register the DMA engine device. + * + * Default transfer size of 32 bytes requires 32-byte alignment. + */ + engine = &dmac->engine; + dma_cap_set(DMA_MEMCPY, engine->cap_mask); + dma_cap_set(DMA_SLAVE, engine->cap_mask); + + engine->dev = &pdev->dev; + engine->copy_align = ilog2(RCAR_DMAC_MEMCPY_XFER_SIZE); + + engine->src_addr_widths = widths; + engine->dst_addr_widths = widths; + engine->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); + engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + engine->device_alloc_chan_resources = rcar_dmac_alloc_chan_resources; + engine->device_free_chan_resources = rcar_dmac_free_chan_resources; + engine->device_prep_dma_memcpy = rcar_dmac_prep_dma_memcpy; + engine->device_prep_slave_sg = rcar_dmac_prep_slave_sg; + engine->device_prep_dma_cyclic = rcar_dmac_prep_dma_cyclic; + engine->device_config = rcar_dmac_device_config; + engine->device_terminate_all = rcar_dmac_chan_terminate_all; + engine->device_tx_status = rcar_dmac_tx_status; + engine->device_issue_pending = rcar_dmac_issue_pending; + + ret = dma_async_device_register(engine); + if (ret < 0) + goto error; + + return 0; + +error: + of_dma_controller_free(pdev->dev.of_node); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int rcar_dmac_remove(struct platform_device *pdev) +{ + struct rcar_dmac *dmac = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&dmac->engine); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static void rcar_dmac_shutdown(struct platform_device *pdev) +{ + struct rcar_dmac *dmac = platform_get_drvdata(pdev); + + rcar_dmac_stop(dmac); +} + +static const struct of_device_id rcar_dmac_of_ids[] = { + { .compatible = "renesas,rcar-dmac", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rcar_dmac_of_ids); + +static struct platform_driver rcar_dmac_driver = { + .driver = { + .pm = &rcar_dmac_pm, + .name = "rcar-dmac", + .of_match_table = rcar_dmac_of_ids, + }, + .probe = rcar_dmac_probe, + .remove = rcar_dmac_remove, + .shutdown = rcar_dmac_shutdown, +}; + +module_platform_driver(rcar_dmac_driver); + +MODULE_DESCRIPTION("R-Car Gen2 DMA Controller Driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL v2"); From ccadee9b1e90dc6d3d97a20ac96cb1a82e0d5a1d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Jul 2014 23:15:48 +0200 Subject: [PATCH 081/360] dmaengine: rcar-dmac: Implement support for hardware descriptor lists The DMAC supports hardware-based auto-configuration from descriptor lists. This reduces the number of interrupts required for processing a DMA transfer. Support that mode in the driver. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/dma/sh/rcar-dmac.c | 304 ++++++++++++++++++++++++++++++------- 1 file changed, 253 insertions(+), 51 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 89d40f9730ba13..6e7cdab6182744 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -40,6 +41,19 @@ struct rcar_dmac_xfer_chunk { u32 size; }; +/* + * struct rcar_dmac_hw_desc - Hardware descriptor for a transfer chunk + * @sar: value of the SAR register (source address) + * @dar: value of the DAR register (destination address) + * @tcr: value of the TCR register (transfer count) + */ +struct rcar_dmac_hw_desc { + u32 sar; + u32 dar; + u32 tcr; + u32 reserved; +} __attribute__((__packed__)); + /* * struct rcar_dmac_desc - R-Car Gen2 DMA Transfer Descriptor * @async_tx: base DMA asynchronous transaction descriptor @@ -49,6 +63,10 @@ struct rcar_dmac_xfer_chunk { * @node: entry in the channel's descriptors lists * @chunks: list of transfer chunks for this transfer * @running: the transfer chunk being currently processed + * @nchunks: number of transfer chunks for this transfer + * @hwdescs.mem: hardware descriptors memory for the transfer + * @hwdescs.dma: device address of the hardware descriptors memory + * @hwdescs.size: size of the hardware descriptors in bytes * @size: transfer size in bytes * @cyclic: when set indicates that the DMA transfer is cyclic */ @@ -61,6 +79,13 @@ struct rcar_dmac_desc { struct list_head node; struct list_head chunks; struct rcar_dmac_xfer_chunk *running; + unsigned int nchunks; + + struct { + struct rcar_dmac_hw_desc *mem; + dma_addr_t dma; + size_t size; + } hwdescs; unsigned int size; bool cyclic; @@ -217,7 +242,8 @@ struct rcar_dmac { #define RCAR_DMATSRB 0x0038 #define RCAR_DMACHCRB 0x001c #define RCAR_DMACHCRB_DCNT(n) ((n) << 24) -#define RCAR_DMACHCRB_DPTR(n) ((n) << 16) +#define RCAR_DMACHCRB_DPTR_MASK (0xff << 16) +#define RCAR_DMACHCRB_DPTR_SHIFT 16 #define RCAR_DMACHCRB_DRST (1 << 15) #define RCAR_DMACHCRB_DTS (1 << 8) #define RCAR_DMACHCRB_SLM_NORMAL (0 << 4) @@ -289,30 +315,81 @@ static bool rcar_dmac_chan_is_busy(struct rcar_dmac_chan *chan) static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan) { struct rcar_dmac_desc *desc = chan->desc.running; - struct rcar_dmac_xfer_chunk *chunk = desc->running; - - dev_dbg(chan->chan.device->dev, - "chan%u: queue chunk %p: %u@%pad -> %pad\n", - chan->index, chunk, chunk->size, &chunk->src_addr, - &chunk->dst_addr); + u32 chcr = desc->chcr; WARN_ON_ONCE(rcar_dmac_chan_is_busy(chan)); + if (chan->mid_rid >= 0) + rcar_dmac_chan_write(chan, RCAR_DMARS, chan->mid_rid); + + if (desc->hwdescs.mem) { + dev_dbg(chan->chan.device->dev, + "chan%u: queue desc %p: %u@%pad\n", + chan->index, desc, desc->nchunks, &desc->hwdescs.dma); + #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - rcar_dmac_chan_write(chan, RCAR_DMAFIXSAR, chunk->src_addr >> 32); - rcar_dmac_chan_write(chan, RCAR_DMAFIXDAR, chunk->dst_addr >> 32); + rcar_dmac_chan_write(chan, RCAR_DMAFIXDPBASE, + desc->hwdescs.dma >> 32); #endif - rcar_dmac_chan_write(chan, RCAR_DMASAR, chunk->src_addr & 0xffffffff); - rcar_dmac_chan_write(chan, RCAR_DMADAR, chunk->dst_addr & 0xffffffff); + rcar_dmac_chan_write(chan, RCAR_DMADPBASE, + (desc->hwdescs.dma & 0xfffffff0) | + RCAR_DMADPBASE_SEL); + rcar_dmac_chan_write(chan, RCAR_DMACHCRB, + RCAR_DMACHCRB_DCNT(desc->nchunks - 1) | + RCAR_DMACHCRB_DRST); - if (chan->mid_rid >= 0) - rcar_dmac_chan_write(chan, RCAR_DMARS, chan->mid_rid); + /* + * Program the descriptor stage interrupt to occur after the end + * of the first stage. + */ + rcar_dmac_chan_write(chan, RCAR_DMADPCR, RCAR_DMADPCR_DIPT(1)); + + chcr |= RCAR_DMACHCR_RPT_SAR | RCAR_DMACHCR_RPT_DAR + | RCAR_DMACHCR_RPT_TCR | RCAR_DMACHCR_DPB; + + /* + * If the descriptor isn't cyclic enable normal descriptor mode + * and the transfer completion interrupt. + */ + if (!desc->cyclic) + chcr |= RCAR_DMACHCR_DPM_ENABLED | RCAR_DMACHCR_IE; + /* + * If the descriptor is cyclic and has a callback enable the + * descriptor stage interrupt in infinite repeat mode. + */ + else if (desc->async_tx.callback) + chcr |= RCAR_DMACHCR_DPM_INFINITE | RCAR_DMACHCR_DSIE; + /* + * Otherwise just select infinite repeat mode without any + * interrupt. + */ + else + chcr |= RCAR_DMACHCR_DPM_INFINITE; + } else { + struct rcar_dmac_xfer_chunk *chunk = desc->running; - rcar_dmac_chan_write(chan, RCAR_DMATCR, - chunk->size >> desc->xfer_shift); + dev_dbg(chan->chan.device->dev, + "chan%u: queue chunk %p: %u@%pad -> %pad\n", + chan->index, chunk, chunk->size, &chunk->src_addr, + &chunk->dst_addr); - rcar_dmac_chan_write(chan, RCAR_DMACHCR, desc->chcr | RCAR_DMACHCR_DE | - RCAR_DMACHCR_IE); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + rcar_dmac_chan_write(chan, RCAR_DMAFIXSAR, + chunk->src_addr >> 32); + rcar_dmac_chan_write(chan, RCAR_DMAFIXDAR, + chunk->dst_addr >> 32); +#endif + rcar_dmac_chan_write(chan, RCAR_DMASAR, + chunk->src_addr & 0xffffffff); + rcar_dmac_chan_write(chan, RCAR_DMADAR, + chunk->dst_addr & 0xffffffff); + rcar_dmac_chan_write(chan, RCAR_DMATCR, + chunk->size >> desc->xfer_shift); + + chcr |= RCAR_DMACHCR_DPM_DISABLED | RCAR_DMACHCR_IE; + } + + rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr | RCAR_DMACHCR_DE); } static int rcar_dmac_init(struct rcar_dmac *dmac) @@ -403,31 +480,58 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp) * @desc: the descriptor * * Put the descriptor and its transfer chunk descriptors back in the channel's - * free descriptors lists. The descriptor's chunk will be reinitialized to an - * empty list as a result. + * free descriptors lists, and free the hardware descriptors list memory. The + * descriptor's chunks list will be reinitialized to an empty list as a result. * - * The descriptor must have been removed from the channel's done list before - * calling this function. + * The descriptor must have been removed from the channel's lists before calling + * this function. * - * Locking: Must be called with the channel lock held. + * Locking: Must be called in non-atomic context. */ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan, struct rcar_dmac_desc *desc) { + if (desc->hwdescs.mem) { + dma_free_coherent(NULL, desc->hwdescs.size, desc->hwdescs.mem, + desc->hwdescs.dma); + desc->hwdescs.mem = NULL; + } + + spin_lock_irq(&chan->lock); list_splice_tail_init(&desc->chunks, &chan->desc.chunks_free); list_add_tail(&desc->node, &chan->desc.free); + spin_unlock_irq(&chan->lock); } static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan) { struct rcar_dmac_desc *desc, *_desc; + LIST_HEAD(list); - list_for_each_entry_safe(desc, _desc, &chan->desc.wait, node) { + /* + * We have to temporarily move all descriptors from the wait list to a + * local list as iterating over the wait list, even with + * list_for_each_entry_safe, isn't safe if we release the channel lock + * around the rcar_dmac_desc_put() call. + */ + spin_lock_irq(&chan->lock); + list_splice_init(&chan->desc.wait, &list); + spin_unlock_irq(&chan->lock); + + list_for_each_entry_safe(desc, _desc, &list, node) { if (async_tx_test_ack(&desc->async_tx)) { list_del(&desc->node); rcar_dmac_desc_put(chan, desc); } } + + if (list_empty(&list)) + return; + + /* Put the remaining descriptors back in the wait list. */ + spin_lock_irq(&chan->lock); + list_splice(&list, &chan->desc.wait); + spin_unlock_irq(&chan->lock); } /* @@ -444,11 +548,11 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan) struct rcar_dmac_desc *desc; int ret; - spin_lock_irq(&chan->lock); - /* Recycle acked descriptors before attempting allocation. */ rcar_dmac_desc_recycle_acked(chan); + spin_lock_irq(&chan->lock); + do { if (list_empty(&chan->desc.free)) { /* @@ -547,6 +651,28 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan) return chunk; } +static void rcar_dmac_alloc_hwdesc(struct rcar_dmac_chan *chan, + struct rcar_dmac_desc *desc) +{ + struct rcar_dmac_xfer_chunk *chunk; + struct rcar_dmac_hw_desc *hwdesc; + size_t size = desc->nchunks * sizeof(*hwdesc); + + hwdesc = dma_alloc_coherent(NULL, size, &desc->hwdescs.dma, GFP_NOWAIT); + if (!hwdesc) + return; + + desc->hwdescs.mem = hwdesc; + desc->hwdescs.size = size; + + list_for_each_entry(chunk, &desc->chunks, node) { + hwdesc->sar = chunk->src_addr; + hwdesc->dar = chunk->dst_addr; + hwdesc->tcr = chunk->size >> desc->xfer_shift; + hwdesc++; + } +} + /* ----------------------------------------------------------------------------- * Stop and reset */ @@ -555,7 +681,8 @@ static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan) { u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR); - chcr &= ~(RCAR_DMACHCR_IE | RCAR_DMACHCR_TE | RCAR_DMACHCR_DE); + chcr &= ~(RCAR_DMACHCR_DSE | RCAR_DMACHCR_DSIE | RCAR_DMACHCR_IE | + RCAR_DMACHCR_TE | RCAR_DMACHCR_DE); rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr); } @@ -666,8 +793,10 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, struct rcar_dmac_xfer_chunk *chunk; struct rcar_dmac_desc *desc; struct scatterlist *sg; + unsigned int nchunks = 0; unsigned int max_chunk_size; unsigned int full_size = 0; + bool highmem = false; unsigned int i; desc = rcar_dmac_desc_get(chan); @@ -706,6 +835,14 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, size = ALIGN(dev_addr, 1ULL << 32) - dev_addr; if (mem_addr >> 32 != (mem_addr + size - 1) >> 32) size = ALIGN(mem_addr, 1ULL << 32) - mem_addr; + + /* + * Check if either of the source or destination address + * can't be expressed in 32 bits. If so we can't use + * hardware descriptor lists. + */ + if (dev_addr >> 32 || mem_addr >> 32) + highmem = true; #endif chunk = rcar_dmac_xfer_chunk_get(chan); @@ -736,11 +873,26 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, len -= size; list_add_tail(&chunk->node, &desc->chunks); + nchunks++; } } + desc->nchunks = nchunks; desc->size = full_size; + /* + * Use hardware descriptor lists if possible when more than one chunk + * needs to be transferred (otherwise they don't make much sense). + * + * The highmem check currently covers the whole transfer. As an + * optimization we could use descriptor lists for consecutive lowmem + * chunks and direct manual mode for highmem chunks. Whether the + * performance improvement would be significant enough compared to the + * additional complexity remains to be investigated. + */ + if (!highmem && nchunks > 1) + rcar_dmac_alloc_hwdesc(chan, desc); + return &desc->async_tx; } @@ -940,8 +1092,10 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan, dma_cookie_t cookie) { struct rcar_dmac_desc *desc = chan->desc.running; + struct rcar_dmac_xfer_chunk *running = NULL; struct rcar_dmac_xfer_chunk *chunk; unsigned int residue = 0; + unsigned int dptr = 0; if (!desc) return 0; @@ -954,9 +1108,23 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan, if (cookie != desc->async_tx.cookie) return desc->size; + /* + * In descriptor mode the descriptor running pointer is not maintained + * by the interrupt handler, find the running descriptor from the + * descriptor pointer field in the CHCRB register. In non-descriptor + * mode just use the running descriptor pointer. + */ + if (desc->hwdescs.mem) { + dptr = (rcar_dmac_chan_read(chan, RCAR_DMACHCRB) & + RCAR_DMACHCRB_DPTR_MASK) >> RCAR_DMACHCRB_DPTR_SHIFT; + WARN_ON(dptr >= desc->nchunks); + } else { + running = desc->running; + } + /* Compute the size of all chunks still to be transferred. */ list_for_each_entry_reverse(chunk, &desc->chunks, node) { - if (chunk == desc->running) + if (chunk == running || ++dptr == desc->nchunks) break; residue += chunk->size; @@ -1025,42 +1193,71 @@ static void rcar_dmac_issue_pending(struct dma_chan *chan) * IRQ handling */ +static irqreturn_t rcar_dmac_isr_desc_stage_end(struct rcar_dmac_chan *chan) +{ + struct rcar_dmac_desc *desc = chan->desc.running; + unsigned int stage; + + if (WARN_ON(!desc || !desc->cyclic)) { + /* + * This should never happen, there should always be a running + * cyclic descriptor when a descriptor stage end interrupt is + * triggered. Warn and return. + */ + return IRQ_NONE; + } + + /* Program the interrupt pointer to the next stage. */ + stage = (rcar_dmac_chan_read(chan, RCAR_DMACHCRB) & + RCAR_DMACHCRB_DPTR_MASK) >> RCAR_DMACHCRB_DPTR_SHIFT; + rcar_dmac_chan_write(chan, RCAR_DMADPCR, RCAR_DMADPCR_DIPT(stage)); + + return IRQ_WAKE_THREAD; +} + static irqreturn_t rcar_dmac_isr_transfer_end(struct rcar_dmac_chan *chan) { struct rcar_dmac_desc *desc = chan->desc.running; - struct rcar_dmac_xfer_chunk *chunk; irqreturn_t ret = IRQ_WAKE_THREAD; if (WARN_ON_ONCE(!desc)) { /* - * This should never happen, there should always be - * a running descriptor when a transfer ends. Warn and - * return. + * This should never happen, there should always be a running + * descriptor when a transfer end interrupt is triggered. Warn + * and return. */ return IRQ_NONE; } /* - * If we haven't completed the last transfer chunk simply move to the - * next one. Only wake the IRQ thread if the transfer is cyclic. + * The transfer end interrupt isn't generated for each chunk when using + * descriptor mode. Only update the running chunk pointer in + * non-descriptor mode. */ - chunk = desc->running; - if (!list_is_last(&chunk->node, &desc->chunks)) { - desc->running = list_next_entry(chunk, node); - if (!desc->cyclic) - ret = IRQ_HANDLED; - goto done; - } + if (!desc->hwdescs.mem) { + /* + * If we haven't completed the last transfer chunk simply move + * to the next one. Only wake the IRQ thread if the transfer is + * cyclic. + */ + if (!list_is_last(&desc->running->node, &desc->chunks)) { + desc->running = list_next_entry(desc->running, node); + if (!desc->cyclic) + ret = IRQ_HANDLED; + goto done; + } - /* - * We've completed the last transfer chunk. If the transfer is cyclic, - * move back to the first one. - */ - if (desc->cyclic) { - desc->running = list_first_entry(&desc->chunks, + /* + * We've completed the last transfer chunk. If the transfer is + * cyclic, move back to the first one. + */ + if (desc->cyclic) { + desc->running = + list_first_entry(&desc->chunks, struct rcar_dmac_xfer_chunk, node); - goto done; + goto done; + } } /* The descriptor is complete, move it to the done list. */ @@ -1083,6 +1280,7 @@ static irqreturn_t rcar_dmac_isr_transfer_end(struct rcar_dmac_chan *chan) static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev) { + u32 mask = RCAR_DMACHCR_DSE | RCAR_DMACHCR_TE; struct rcar_dmac_chan *chan = dev; irqreturn_t ret = IRQ_NONE; u32 chcr; @@ -1090,8 +1288,12 @@ static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev) spin_lock(&chan->lock); chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR); - rcar_dmac_chan_write(chan, RCAR_DMACHCR, - chcr & ~(RCAR_DMACHCR_TE | RCAR_DMACHCR_DE)); + if (chcr & RCAR_DMACHCR_TE) + mask |= RCAR_DMACHCR_DE; + rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr & ~mask); + + if (chcr & RCAR_DMACHCR_DSE) + ret |= rcar_dmac_isr_desc_stage_end(chan); if (chcr & RCAR_DMACHCR_TE) ret |= rcar_dmac_isr_transfer_end(chan); @@ -1148,11 +1350,11 @@ static irqreturn_t rcar_dmac_isr_channel_thread(int irq, void *dev) list_add_tail(&desc->node, &chan->desc.wait); } + spin_unlock_irq(&chan->lock); + /* Recycle all acked descriptors. */ rcar_dmac_desc_recycle_acked(chan); - spin_unlock_irq(&chan->lock); - return IRQ_HANDLED; } From 1ed1315f9b63c45f57f607e7ad2dac066b101f16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 19 Jul 2014 00:05:14 +0200 Subject: [PATCH 082/360] dmaengine: rcar-dmac: Cache hardware descriptors memory Unlike DMA transfers descriptors that are preallocated and cached, memory used to store hardware descriptors is allocated and freed with the DMA coherent allocation API for every transfer. Besides degrading performances, this creates a CMA stress test that seems to cause issues. Running dmatest with the noverify option produces [ 50.066539] alloc_contig_range test_pages_isolated(6b845, 6b846) failed [ 50.235180] alloc_contig_range test_pages_isolated(6b848, 6b84e) failed [ 52.964584] alloc_contig_range test_pages_isolated(6b847, 6b848) failed [ 54.127113] alloc_contig_range test_pages_isolated(6b843, 6b844) failed [ 56.270253] alloc_contig_range test_pages_isolated(6b84c, 6b850) failed The root cause needs to be fixed, but in the meantime, as a workaround and a performance improvement, cache hardware descriptors. Signed-off-by: Laurent Pinchart Tested-by: Wolfram Sang --- drivers/dma/sh/rcar-dmac.c | 78 ++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 6e7cdab6182744..f71a3dc89048b6 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -64,6 +64,7 @@ struct rcar_dmac_hw_desc { * @chunks: list of transfer chunks for this transfer * @running: the transfer chunk being currently processed * @nchunks: number of transfer chunks for this transfer + * @hwdescs.use: whether the transfer descriptor uses hardware descriptors * @hwdescs.mem: hardware descriptors memory for the transfer * @hwdescs.dma: device address of the hardware descriptors memory * @hwdescs.size: size of the hardware descriptors in bytes @@ -82,6 +83,7 @@ struct rcar_dmac_desc { unsigned int nchunks; struct { + bool use; struct rcar_dmac_hw_desc *mem; dma_addr_t dma; size_t size; @@ -322,7 +324,7 @@ static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan) if (chan->mid_rid >= 0) rcar_dmac_chan_write(chan, RCAR_DMARS, chan->mid_rid); - if (desc->hwdescs.mem) { + if (desc->hwdescs.use) { dev_dbg(chan->chan.device->dev, "chan%u: queue desc %p: %u@%pad\n", chan->index, desc, desc->nchunks, &desc->hwdescs.dma); @@ -480,8 +482,8 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp) * @desc: the descriptor * * Put the descriptor and its transfer chunk descriptors back in the channel's - * free descriptors lists, and free the hardware descriptors list memory. The - * descriptor's chunks list will be reinitialized to an empty list as a result. + * free descriptors lists. The descriptor's chunks list will be reinitialized to + * an empty list as a result. * * The descriptor must have been removed from the channel's lists before calling * this function. @@ -491,12 +493,6 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp) static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan, struct rcar_dmac_desc *desc) { - if (desc->hwdescs.mem) { - dma_free_coherent(NULL, desc->hwdescs.size, desc->hwdescs.mem, - desc->hwdescs.dma); - desc->hwdescs.mem = NULL; - } - spin_lock_irq(&chan->lock); list_splice_tail_init(&desc->chunks, &chan->desc.chunks_free); list_add_tail(&desc->node, &chan->desc.free); @@ -651,20 +647,50 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan) return chunk; } -static void rcar_dmac_alloc_hwdesc(struct rcar_dmac_chan *chan, - struct rcar_dmac_desc *desc) +static void rcar_dmac_realloc_hwdesc(struct rcar_dmac_chan *chan, + struct rcar_dmac_desc *desc, size_t size) +{ + /* + * dma_alloc_coherent() allocates memory in page size increments. To + * avoid reallocating the hardware descriptors when the allocated size + * wouldn't change align the requested size to a multiple of the page + * size. + */ + size = PAGE_ALIGN(size); + + if (desc->hwdescs.size == size) + return; + + if (desc->hwdescs.mem) { + dma_free_coherent(NULL, desc->hwdescs.size, desc->hwdescs.mem, + desc->hwdescs.dma); + desc->hwdescs.mem = NULL; + desc->hwdescs.size = 0; + } + + if (!size) + return; + + desc->hwdescs.mem = dma_alloc_coherent(NULL, size, &desc->hwdescs.dma, + GFP_NOWAIT); + if (!desc->hwdescs.mem) + return; + + desc->hwdescs.size = size; +} + +static void rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, + struct rcar_dmac_desc *desc) { struct rcar_dmac_xfer_chunk *chunk; struct rcar_dmac_hw_desc *hwdesc; - size_t size = desc->nchunks * sizeof(*hwdesc); - hwdesc = dma_alloc_coherent(NULL, size, &desc->hwdescs.dma, GFP_NOWAIT); + rcar_dmac_realloc_hwdesc(chan, desc, desc->nchunks * sizeof(*hwdesc)); + + hwdesc = desc->hwdescs.mem; if (!hwdesc) return; - desc->hwdescs.mem = hwdesc; - desc->hwdescs.size = size; - list_for_each_entry(chunk, &desc->chunks, node) { hwdesc->sar = chunk->src_addr; hwdesc->dar = chunk->dst_addr; @@ -890,8 +916,9 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, * performance improvement would be significant enough compared to the * additional complexity remains to be investigated. */ - if (!highmem && nchunks > 1) - rcar_dmac_alloc_hwdesc(chan, desc); + desc->hwdescs.use = !highmem && nchunks > 1; + if (desc->hwdescs.use) + rcar_dmac_fill_hwdesc(chan, desc); return &desc->async_tx; } @@ -930,6 +957,8 @@ static void rcar_dmac_free_chan_resources(struct dma_chan *chan) struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); struct rcar_dmac *dmac = to_rcar_dmac(chan->device); struct rcar_dmac_desc_page *page, *_page; + struct rcar_dmac_desc *desc; + LIST_HEAD(list); /* Protect against ISR */ spin_lock_irq(&rchan->lock); @@ -944,6 +973,15 @@ static void rcar_dmac_free_chan_resources(struct dma_chan *chan) rchan->mid_rid = -EINVAL; } + list_splice(&rchan->desc.free, &list); + list_splice(&rchan->desc.pending, &list); + list_splice(&rchan->desc.active, &list); + list_splice(&rchan->desc.done, &list); + list_splice(&rchan->desc.wait, &list); + + list_for_each_entry(desc, &list, node) + rcar_dmac_realloc_hwdesc(rchan, desc, 0); + list_for_each_entry_safe(page, _page, &rchan->desc.pages, node) { list_del(&page->node); free_page((unsigned long)page); @@ -1114,7 +1152,7 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan, * descriptor pointer field in the CHCRB register. In non-descriptor * mode just use the running descriptor pointer. */ - if (desc->hwdescs.mem) { + if (desc->hwdescs.use) { dptr = (rcar_dmac_chan_read(chan, RCAR_DMACHCRB) & RCAR_DMACHCRB_DPTR_MASK) >> RCAR_DMACHCRB_DPTR_SHIFT; WARN_ON(dptr >= desc->nchunks); @@ -1234,7 +1272,7 @@ static irqreturn_t rcar_dmac_isr_transfer_end(struct rcar_dmac_chan *chan) * descriptor mode. Only update the running chunk pointer in * non-descriptor mode. */ - if (!desc->hwdescs.mem) { + if (!desc->hwdescs.use) { /* * If we haven't completed the last transfer chunk simply move * to the next one. Only wake the IRQ thread if the transfer is From ee4b876bbee2c5c53518110849f23c117eec099c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Tue, 25 Nov 2014 15:10:17 +0100 Subject: [PATCH 083/360] dmaengine: rcar-dmac: Handle hardware descriptor allocation failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the atomic DMA coherent pool is too small, disable use of hardware descriptor lists instead of crashing the system: ERROR: 256 KiB atomic DMA coherent pool is too small! Please increase it with coherent_pool= kernel parameter! Unable to handle kernel NULL pointer dereference at virtual address 00000004 Internal error: Oops: a07 [#1] PREEMPT SMP ARM PC is at rcar_dmac_chan_reinit+0x3c/0x160 LR is at _raw_spin_lock_irqsave+0x18/0x5c [<802132c0>] (rcar_dmac_chan_reinit) from [<80214818>] (rcar_dmac_isr_error+0x84/0xa0) [<80214818>] (rcar_dmac_isr_error) from [<80060484>] (handle_irq_event_percpu+0x50/0x150) [<80060484>] (handle_irq_event_percpu) from [<800605c0>] (handle_irq_event+0x3c/0x5c) [<800605c0>] (handle_irq_event) from [<8006350c>] (handle_fasteoi_irq+0xb8/0x198) [<8006350c>] (handle_fasteoi_irq) from [<8005fdb0>] (generic_handle_irq+0x20/0x30) [<8005fdb0>] (generic_handle_irq) from [<8000fcd0>] (handle_IRQ+0x50/0xc4) [<8000fcd0>] (handle_IRQ) from [<800092cc>] (gic_handle_irq+0x28/0x5c) [<800092cc>] (gic_handle_irq) from [<80012700>] (__irq_svc+0x40/0x70) Kernel panic - not syncing: Fatal exception in interrupt Signed-off-by: Jürg Billeter Signed-off-by: Laurent Pinchart --- drivers/dma/sh/rcar-dmac.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index f71a3dc89048b6..29dd09ad41ff29 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -679,8 +679,8 @@ static void rcar_dmac_realloc_hwdesc(struct rcar_dmac_chan *chan, desc->hwdescs.size = size; } -static void rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, - struct rcar_dmac_desc *desc) +static int rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, + struct rcar_dmac_desc *desc) { struct rcar_dmac_xfer_chunk *chunk; struct rcar_dmac_hw_desc *hwdesc; @@ -689,7 +689,7 @@ static void rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, hwdesc = desc->hwdescs.mem; if (!hwdesc) - return; + return -ENOMEM; list_for_each_entry(chunk, &desc->chunks, node) { hwdesc->sar = chunk->src_addr; @@ -697,6 +697,8 @@ static void rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, hwdesc->tcr = chunk->size >> desc->xfer_shift; hwdesc++; } + + return 0; } /* ----------------------------------------------------------------------------- @@ -917,8 +919,10 @@ rcar_dmac_chan_prep_sg(struct rcar_dmac_chan *chan, struct scatterlist *sgl, * additional complexity remains to be investigated. */ desc->hwdescs.use = !highmem && nchunks > 1; - if (desc->hwdescs.use) - rcar_dmac_fill_hwdesc(chan, desc); + if (desc->hwdescs.use) { + if (rcar_dmac_fill_hwdesc(chan, desc) < 0) + desc->hwdescs.use = false; + } return &desc->async_tx; } From 548cd3ab54da10f896daa7ca422236847a915734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bean=20Huo=20=E9=9C=8D=E6=96=8C=E6=96=8C=20=28beanhuo=29?= Date: Wed, 17 Dec 2014 07:35:45 +0000 Subject: [PATCH 084/360] mtd: spi-nor: Add quad I/O support for Micron SPI NOR This patch adds code which enables Quad I/O mode on Micron SPI NOR flashes. For Micron SPI NOR flash, enabling or disabling quad I/O protocol can be done By two methods, which are to use EVCR (Enhanced Volatile Configuration Register) and the ENTER QUAD I/O MODE command. There is no difference between these two methods. Unfortunately, for some Micron SPI NOR flashes, there no ENTER Quad I/O command (35h), such as n25q064. But for all current Micron SPI NOR, if it support quad I/O mode, using EVCR definitely be supported. It is a recommended method to enable Quad I/O mode by EVCR, Quad I/O protocol bit 7. When EVCR bit 7 is reset to 0, the SPI NOR flash will operate in quad I/O mode. This patch has been tested on N25Q512A and MT25TL256BAA1ESF. Micron SPI NOR of spi_nor_ids[] table all support this method. Signed-off-by: Bean Huo Acked-by: Marek Vasut Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 62 ++++++++++++++++++++++++++++++----- include/linux/mtd/spi-nor.h | 7 ++++ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0f8ec3c2d01581..ea196c18dec980 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -560,14 +560,14 @@ static const struct spi_device_id spi_nor_ids[] = { { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, /* Micron */ - { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, - { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, - { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, - { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) }, + { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SPI_NOR_QUAD_READ) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, @@ -891,6 +891,45 @@ static int spansion_quad_enable(struct spi_nor *nor) return 0; } +static int micron_quad_enable(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading EVCR\n", ret); + return ret; + } + + write_enable(nor); + + /* set EVCR, enable quad I/O */ + nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON; + ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0); + if (ret < 0) { + dev_err(nor->dev, "error while writing EVCR register\n"); + return ret; + } + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + /* read EVCR and check it */ + ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading EVCR\n", ret); + return ret; + } + if (val & EVCR_QUAD_EN_MICRON) { + dev_err(nor->dev, "Micron EVCR Quad bit not clear\n"); + return -EINVAL; + } + + return 0; +} + static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) { int status; @@ -903,6 +942,13 @@ static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) return -EINVAL; } return status; + case CFI_MFR_ST: + status = micron_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Micron quad-read not enabled\n"); + return -EINVAL; + } + return status; default: status = spansion_quad_enable(nor); if (status) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 63aeccf9ddc8cb..4720b86ee73dce 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -56,6 +56,10 @@ /* Used for Spansion flashes only. */ #define SPINOR_OP_BRWR 0x17 /* Bank register write */ +/* Used for Micron flashes only. */ +#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ +#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ + /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ @@ -67,6 +71,9 @@ #define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +/* Enhanced Volatile Configuration Register bits */ +#define EVCR_QUAD_EN_MICRON 0x80 /* Micron Quad I/O */ + /* Flag Status Register bits */ #define FSR_READY 0x80 From bd10c26a4efe23e97ee9878a825c7e7a3e594946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 1 Dec 2014 18:50:20 +0100 Subject: [PATCH 085/360] mtd: bcm47xxpart: support TRX data partition being UBI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index cc13ea5ce4d58f..26a4a4a4e8ab04 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -40,6 +40,7 @@ #define ML_MAGIC2 0x26594131 #define TRX_MAGIC 0x30524448 #define SQSH_MAGIC 0x71736873 /* shsq */ +#define UBI_EC_MAGIC 0x23494255 /* UBI# */ struct trx_header { uint32_t magic; @@ -50,7 +51,7 @@ struct trx_header { uint32_t offset[3]; } __packed; -static void bcm47xxpart_add_part(struct mtd_partition *part, char *name, +static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name, u64 offset, uint32_t mask_flags) { part->name = name; @@ -58,6 +59,26 @@ static void bcm47xxpart_add_part(struct mtd_partition *part, char *name, part->mask_flags = mask_flags; } +static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master, + size_t offset) +{ + uint32_t buf; + size_t bytes_read; + + if (mtd_read(master, offset, sizeof(buf), &bytes_read, + (uint8_t *)&buf) < 0) { + pr_err("mtd_read error while parsing (offset: 0x%X)!\n", + offset); + goto out_default; + } + + if (buf == UBI_EC_MAGIC) + return "ubi"; + +out_default: + return "rootfs"; +} + static int bcm47xxpart_parse(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) @@ -186,8 +207,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, * we want to have jffs2 (overlay) in the same mtd. */ if (trx->offset[i]) { + const char *name; + + name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]); bcm47xxpart_add_part(&parts[curr_part++], - "rootfs", + name, offset + trx->offset[i], 0); i++; From 16bd87b3a352b536ffdd7325e3178c4231f3cbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 8 Dec 2014 18:45:00 +0100 Subject: [PATCH 086/360] mtd: bcm47xxpart: lower minimal blocksize to 4Ki (from 64Ki) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices like Netgear WNR1000v3 or WGR614v10 have partitions aligned to 0x1000. Using bigger blocksize stopped us from detecting some parts. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 26a4a4a4e8ab04..4ad392892b70bf 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -94,8 +94,12 @@ static int bcm47xxpart_parse(struct mtd_info *master, int last_trx_part = -1; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; - if (blocksize <= 0x10000) - blocksize = 0x10000; + /* + * Some really old flashes (like AT45DB*) had smaller erasesize-s, but + * partitions were aligned to at least 0x1000 anyway. + */ + if (blocksize < 0x1000) + blocksize = 0x1000; /* Alloc */ parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, From 0b56d2d45e7c27581ca90f36b4317373f23e7622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 16 Dec 2014 09:50:25 +0100 Subject: [PATCH 087/360] mtd: bcm47xxpart: support SquashFS with an original magic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SquashFS is supposed to use magic defined as SQUASHFS_MAGIC. What we were supporting so far (SQSH_MAGIC) is something ZTE specific. This patch adds support for Xiaomi R1D. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 4ad392892b70bf..c0720c1ee4c960 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -15,6 +15,8 @@ #include #include +#include + /* * NAND flash on Netgear R6250 was verified to contain 15 partitions. * This will result in allocating too big array for some old devices, but the @@ -39,7 +41,7 @@ #define ML_MAGIC1 0x39685a42 #define ML_MAGIC2 0x26594131 #define TRX_MAGIC 0x30524448 -#define SQSH_MAGIC 0x71736873 /* shsq */ +#define SHSQ_MAGIC 0x71736873 /* shsq (weird ZTE H218N endianness) */ #define UBI_EC_MAGIC 0x23494255 /* UBI# */ struct trx_header { @@ -233,7 +235,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, } /* Squashfs on devices not using TRX */ - if (buf[0x000 / 4] == SQSH_MAGIC) { + if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC || + buf[0x000 / 4] == SHSQ_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "rootfs", offset, 0); continue; From 362376a7c65936835f39ff29b082a646d522efbc Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Wed, 3 Dec 2014 10:19:52 +0800 Subject: [PATCH 088/360] mtd: mtdblock: remove the needless mtdblks_lock The global lock mtdblks_lock was used to protect the original mtdblks array to avoid race conditions. As the mtdblks array was already gone, but the mtdblks_lock is left, and it causes latency when open/release dev. So we need to remove it here. Signed-off-by: Gu Zheng Signed-off-by: Brian Norris --- drivers/mtd/mtdblock.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 485ea751c7f9b6..bb4c14f83c75ac 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -45,8 +45,6 @@ struct mtdblk_dev { enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; }; -static DEFINE_MUTEX(mtdblks_lock); - /* * Cache stuff... * @@ -286,10 +284,8 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) pr_debug("mtdblock_open\n"); - mutex_lock(&mtdblks_lock); if (mtdblk->count) { mtdblk->count++; - mutex_unlock(&mtdblks_lock); return 0; } @@ -302,8 +298,6 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) mtdblk->cache_data = NULL; } - mutex_unlock(&mtdblks_lock); - pr_debug("ok\n"); return 0; @@ -315,8 +309,6 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd) pr_debug("mtdblock_release\n"); - mutex_lock(&mtdblks_lock); - mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); mutex_unlock(&mtdblk->cache_mutex); @@ -331,8 +323,6 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd) vfree(mtdblk->cache_data); } - mutex_unlock(&mtdblks_lock); - pr_debug("ok\n"); } From 3efe41be224c4441f2a872a25471a14d85ceb7c6 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 26 Nov 2014 01:01:08 -0800 Subject: [PATCH 089/360] mtd: implement common reboot notifier boilerplate cfi_cmdset_000{1,2}.c already implement their own reboot notifiers, and we're going to add one for NAND. Let's put the boilerplate in one place. Signed-off-by: Brian Norris Tested-by: Scott Branden --- drivers/mtd/mtdcore.c | 20 ++++++++++++++++++++ include/linux/mtd/mtd.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 4c611871d7e6b4..cbc0fc46d2d6eb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -365,6 +366,17 @@ static struct device_type mtd_devtype = { .release = mtd_release, }; +static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state, + void *cmd) +{ + struct mtd_info *mtd; + + mtd = container_of(n, struct mtd_info, reboot_notifier); + mtd->_reboot(mtd); + + return NOTIFY_DONE; +} + /** * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure @@ -565,6 +577,11 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, err = -ENODEV; } + if (mtd->_reboot) { + mtd->reboot_notifier.notifier_call = mtd_reboot_notifier; + register_reboot_notifier(&mtd->reboot_notifier); + } + return err; } EXPORT_SYMBOL_GPL(mtd_device_parse_register); @@ -579,6 +596,9 @@ int mtd_device_unregister(struct mtd_info *master) { int err; + if (master->_reboot) + unregister_reboot_notifier(&master->reboot_notifier); + err = del_mtd_partitions(master); if (err) return err; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 031ff3a9a0bdff..c06f5373d8700b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -227,6 +227,7 @@ struct mtd_info { int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); int (*_suspend) (struct mtd_info *mtd); void (*_resume) (struct mtd_info *mtd); + void (*_reboot) (struct mtd_info *mtd); /* * If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. From 72ea403669c7d49e8c407c61205b6d52438d39ab Mon Sep 17 00:00:00 2001 From: Scott Branden Date: Thu, 20 Nov 2014 11:18:05 -0800 Subject: [PATCH 090/360] mtd: nand: added nand_shutdown Add nand_shutdown to wait for current nand operations to finish and prevent further operations by changing the nand flash state to FL_SHUTDOWN. This is addressing a problem observed during reboot tests using UBIFS root file system: NAND erase operations that are in progress during system reboot/shutdown are causing partial erased blocks. Although UBI should be able to detect and recover from this error, this change will avoid the creation of partial erased blocks on reboot in the middle of a NAND erase operation. Signed-off-by: Scott Branden Tested-by: Scott Branden Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 41585dfb206f05..382354b5547bf3 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2944,6 +2944,16 @@ static void nand_resume(struct mtd_info *mtd) __func__); } +/** + * nand_shutdown - [MTD Interface] Finish the current NAND operation and + * prevent further operations + * @mtd: MTD device structure + */ +static void nand_shutdown(struct mtd_info *mtd) +{ + nand_get_device(mtd, FL_SHUTDOWN); +} + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip, int busw) { @@ -4146,6 +4156,7 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->_unlock = NULL; mtd->_suspend = nand_suspend; mtd->_resume = nand_resume; + mtd->_reboot = nand_shutdown; mtd->_block_isreserved = nand_block_isreserved; mtd->_block_isbad = nand_block_isbad; mtd->_block_markbad = nand_block_markbad; From 57f5ef14a5ffbf4bad7d761e844efede21a1c975 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 31 Dec 2014 10:41:54 +0200 Subject: [PATCH 091/360] mtd: nand: remove duplicate comment line Commit 7854d3f7495b1 ("mtd: spelling, capitalization, uniformity") added a correctly spelled line, but failed to remove the wrongly spelled one. Commit 064a7694b534 ("mtd: Fix typo mtd/tests") then fixed the spelling again, but left the duplication. Fixes: 7854d3f7495b1 ("mtd: spelling, capitalization, uniformity") Signed-off-by: Baruch Siach Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 382354b5547bf3..816b5c1fd41653 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -156,7 +156,6 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) } /** - * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * @mtd: MTD device structure * From 768c57c8d7df479aa27330d629b4e41f9c19b22c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Jan 2015 22:37:20 +0200 Subject: [PATCH 092/360] mtd: nftl: reorganize operations in condition check We need to compare ret variable for negative value. The current code assigns the boolean to the ret and prints it wrongly in the warning message. Reported-by: Andrey Karpov Cc: Giel van Schijndel Cc: Dimitri Gorokhovik Signed-off-by: Andy Shevchenko Signed-off-by: Brian Norris --- drivers/mtd/nftlmount.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 51b9d6af307f61..a5dfbfbebfcafe 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -89,9 +89,10 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize + + ret = nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + (char *)&h1); + if (ret < 0) { printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -109,8 +110,9 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* Finally reread to check ECC */ - if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf) < 0)) { + ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf); + if (ret < 0) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -228,9 +230,11 @@ device is already correct. The new DiskOnChip driver already scanned the bad block table. Just query it. if ((i & (SECTORSIZE - 1)) == 0) { /* read one sector for every SECTORSIZE of blocks */ - if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize + - i + SECTORSIZE, SECTORSIZE, &retlen, - buf)) < 0) { + ret = mtd->read(nftl->mbd.mtd, + block * nftl->EraseSize + i + + SECTORSIZE, SECTORSIZE, + &retlen, buf); + if (ret < 0) { printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", ret); kfree(nftl->ReplUnitTable); From 0c45e6016f1297bc71ed677697bacc111a63126d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Jan 2015 01:32:56 -0200 Subject: [PATCH 093/360] Documentation: fsl-quadspi: Add an entry for the imx6sx compatible string "fsl,imx6sx-qspi" is also a valid compatible string, so add an entry for it. Signed-off-by: Fabio Estevam Signed-off-by: Brian Norris --- Documentation/devicetree/bindings/mtd/fsl-quadspi.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt index 823d1341219563..4461dc71cb1009 100644 --- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -1,7 +1,7 @@ * Freescale Quad Serial Peripheral Interface(QuadSPI) Required properties: - - compatible : Should be "fsl,vf610-qspi" + - compatible : Should be "fsl,vf610-qspi" or "fsl,imx6sx-qspi" - reg : the first contains the register location and length, the second contains the memory mapping address and length - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" From 106effbe18fa481c9355604dafe8095c9e418319 Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Thu, 11 Dec 2014 10:38:06 +0100 Subject: [PATCH 094/360] mtd: map_ram: Enable mtdoops Signed-off-by: Alessio Igor Bogani Signed-off-by: Brian Norris --- drivers/mtd/chips/map_ram.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 991c2a1c05d364..afb43d5e178269 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -68,6 +68,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) mtd->_get_unmapped_area = mapram_unmapped_area; mtd->_read = mapram_read; mtd->_write = mapram_write; + mtd->_panic_write = mapram_write; mtd->_sync = mapram_nop; mtd->flags = MTD_CAP_RAM; mtd->writesize = 1; From 3fc1cf5f0af4b625adff03c610f188fa8bc89911 Mon Sep 17 00:00:00 2001 From: Joe Schultz Date: Thu, 25 Sep 2014 12:20:08 -0500 Subject: [PATCH 095/360] mtd: physmap_of: Add read-only fallback Previously, when probing a CFI chip which was write-protected at the hardware level, the probe would fail due to the fact it could not put the chip into QUERY mode. This would result in no MTD devices being created. Add a fallback to probe using the map_rom driver if the user-selected probe fails. Signed-off-by: Joe Schultz Signed-off-by: Aaron Sierra Signed-off-by: Brian Norris --- drivers/mtd/maps/physmap_of.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index f35cd2081314d7..ff26e979b1a17c 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -269,6 +269,16 @@ static int of_flash_probe(struct platform_device *dev) info->list[i].mtd = obsolete_probe(dev, &info->list[i].map); } + + /* Fall back to mapping region as ROM */ + if (!info->list[i].mtd) { + dev_warn(&dev->dev, + "do_map_probe() failed for type %s\n", + probe_type); + + info->list[i].mtd = do_map_probe("map_rom", + &info->list[i].map); + } mtd_list[i] = info->list[i].mtd; err = -ENXIO; From 6958024ad5cdd78deae4da47a8722b06317dff45 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 25 Sep 2014 12:20:24 -0500 Subject: [PATCH 096/360] mtd: map_rom: Support UBI on ROM UBI needs to know the physical erase block size, even on read-only devices, since it defines the on-device layout. Use a device-tree provided value to support previously written UBI on read-only NOR. UBI also needs a non-zero writebufsize, so we set it to one. Note: This was implemented because hardware write-protected CFI NOR cannot be probed for the physical erase block size. Signed-off-by: Joe Schultz Signed-off-by: Aaron Sierra [Brian: removed unneeded #ifdef, note 'optional' erase-size property] Signed-off-by: Brian Norris --- .../devicetree/bindings/mtd/mtd-physmap.txt | 5 +++++ drivers/mtd/chips/map_rom.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index 6b9f680cb579bd..4a0a48bf4ecb83 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -36,6 +36,11 @@ are defined: - vendor-id : Contains the flash chip's vendor id (1 byte). - device-id : Contains the flash chip's device id (1 byte). +For ROM compatible devices (and ROM fallback from cfi-flash), the following +additional (optional) property is defined: + + - erase-size : The chip's physical erase block size in bytes. + The device tree may optionally contain sub-nodes describing partitions of the address space. See partition.txt for more detail. diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 47a43cf7e5c60f..e67f73ab44c9db 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,15 @@ static struct mtd_chip_driver maprom_chipdrv = { .module = THIS_MODULE }; +static unsigned int default_erasesize(struct map_info *map) +{ + const __be32 *erase_size = NULL; + + erase_size = of_get_property(map->device_node, "erase-size", NULL); + + return !erase_size ? map->size : be32_to_cpu(*erase_size); +} + static struct mtd_info *map_rom_probe(struct map_info *map) { struct mtd_info *mtd; @@ -47,8 +57,9 @@ static struct mtd_info *map_rom_probe(struct map_info *map) mtd->_sync = maprom_nop; mtd->_erase = maprom_erase; mtd->flags = MTD_CAP_ROM; - mtd->erasesize = map->size; + mtd->erasesize = default_erasesize(map); mtd->writesize = 1; + mtd->writebufsize = 1; __module_get(THIS_MODULE); return mtd; From e4999bbe0802ddff82b3fcbbab3b6274b789aad7 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 11 Jan 2015 17:36:48 +0100 Subject: [PATCH 097/360] jffs2: compr_rubin: Remove unused function Remove the function pulledbits() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Reviewed-by: Richard Weinberger Signed-off-by: Brian Norris --- fs/jffs2/compr_rubin.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index 92e0644bf8673d..556de100ebd5a5 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -84,11 +84,6 @@ static inline int pullbit(struct pushpull *pp) return bit; } -static inline int pulledbits(struct pushpull *pp) -{ - return pp->ofs; -} - static void init_rubin(struct rubin_state *rs, int div, int *bits) { From 5ecd3ea188fd60e5f663a956dbe23d61a99f504a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Dec 2014 11:59:08 +0000 Subject: [PATCH 098/360] mtd: st_spi_fsm: Extend fsm_clear_fifo to handle unwanted bytes Under certain conditions, the SPI-FSM Controller can be left in a state where the data FIFO is not entirely empty. This can lead to problems where subsequent data transfers appear to have been shifted by a number of unidentified bytes. One simple example would be an errant FSM sequence which loaded more data to the FIFO than was read by the host. Another more interesting case results from an obscure artefact in the FSM Controller. When switching from data transfers in x4 or x2 mode to data transfers in x1 mode, extraneous bytes will appear in the FIFO, unless the previous data transfer was a multiple of 32 cycles (i.e. 8 bytes for x2, and 16 bytes for x4). This applies equally whether FSM is being operated directly by a S/W driver, or by the SPI boot-controller in FSM-Boot mode. Furthermore, data in the FIFO not only survive a transition between FSM-Boot and FSM, but also a S/W reset of IP block [1]. By taking certain precautions, it is possible to prevent the driver from causing this type of problem (e.g. ensuring that the host and programmed sequence agree on the transfer size, and restricting transfer sizes to multiples of 32-cycles [2]). However, at the point the driver is loaded, no assumptions can be made regarding the state of the FIFO. Even if previous S/W drivers have behaved correctly, it is impossible to control the number of transactions serviced by the controller operating in FSM-Boot. To address this problem, we ensure the FIFO is cleared during initialisation, before performing any FSM operations. Previously, the fsm_clear_fifo() code was capable of detecting and clearing any unwanted 32-bit words from the FIFO. This patch extends the capability to handle an arbitrary number of bytes present in the FIFO [3]. Now that the issue is better understood, we also remove the calls to fsm_clear_fifo() following the fsm_read() and fsm_write() operations. The process of actually clearing the FIFO deserves a mention. While the FIFO may contain any number of bytes, the SPI_FAST_SEQ_STA register only reports the number of complete 32-bit words present. Furthermore, data can only be drained from the FIFO by reading complete 32-bit words. With this in mind, a two stage process is used to the clear the FIFO: 1. Read any complete 32-bit words from the FIFO, as reported by the SPI_FAST_SEQ_STA register. 2. Mop up any remaining bytes. At this point, it is not known if there are 0, 1, 2, or 3 bytes in the FIFO. To handle all cases, a dummy FSM sequence is used to load one byte at a time, until a complete 32-bit word is formed; at most, 4 bytes will need to be loaded. [1] Although this issue has existed since early versions of the SPI-FSM controller, its full extent only emerged recently as a consequence of the targetpacks starting to use FSM-Boot(x4) as the default configuration. [2] The requirement to restrict transfers to multiples of 32 cycles was found empirically back when DUAL and QUAD mode support was added. The current analysis now gives a satisfactory explanation for this requirement. [3] Theoretically, it is possible for the FIFO to contain an arbitrary number of bits. However, since there are no known use-cases that leave incomplete bytes in the FIFO, only words and bytes are considered here. Signed-off-by: Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 95 ++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 54ffe5223e6429..bebc8b5637c057 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -663,6 +663,23 @@ static struct stfsm_seq stfsm_seq_write_status = { SEQ_CFG_STARTSEQ), }; +/* Dummy sequence to read one byte of data from flash into the FIFO */ +static const struct stfsm_seq stfsm_seq_load_fifo_byte = { + .data_size = TRANSFER_SIZE(1), + .seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(SPINOR_OP_RDID)), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_DATA_READ, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) { seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | @@ -695,22 +712,6 @@ static inline uint32_t stfsm_fifo_available(struct stfsm *fsm) return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f; } -static void stfsm_clear_fifo(struct stfsm *fsm) -{ - uint32_t avail; - - for (;;) { - avail = stfsm_fifo_available(fsm); - if (!avail) - break; - - while (avail) { - readl(fsm->base + SPI_FAST_SEQ_DATA_REG); - avail--; - } - } -} - static inline void stfsm_load_seq(struct stfsm *fsm, const struct stfsm_seq *seq) { @@ -772,6 +773,68 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) } } +/* + * Clear the data FIFO + * + * Typically, this is only required during driver initialisation, where no + * assumptions can be made regarding the state of the FIFO. + * + * The process of clearing the FIFO is complicated by fact that while it is + * possible for the FIFO to contain an arbitrary number of bytes [1], the + * SPI_FAST_SEQ_STA register only reports the number of complete 32-bit words + * present. Furthermore, data can only be drained from the FIFO by reading + * complete 32-bit words. + * + * With this in mind, a two stage process is used to the clear the FIFO: + * + * 1. Read any complete 32-bit words from the FIFO, as reported by the + * SPI_FAST_SEQ_STA register. + * + * 2. Mop up any remaining bytes. At this point, it is not known if there + * are 0, 1, 2, or 3 bytes in the FIFO. To handle all cases, a dummy FSM + * sequence is used to load one byte at a time, until a complete 32-bit + * word is formed; at most, 4 bytes will need to be loaded. + * + * [1] It is theoretically possible for the FIFO to contain an arbitrary number + * of bits. However, since there are no known use-cases that leave + * incomplete bytes in the FIFO, only words and bytes are considered here. + */ +static void stfsm_clear_fifo(struct stfsm *fsm) +{ + const struct stfsm_seq *seq = &stfsm_seq_load_fifo_byte; + uint32_t words, i; + + /* 1. Clear any 32-bit words */ + words = stfsm_fifo_available(fsm); + if (words) { + for (i = 0; i < words; i++) + readl(fsm->base + SPI_FAST_SEQ_DATA_REG); + dev_dbg(fsm->dev, "cleared %d words from FIFO\n", words); + } + + /* + * 2. Clear any remaining bytes + * - Load the FIFO, one byte at a time, until a complete 32-bit word + * is available. + */ + for (i = 0, words = 0; i < 4 && !words; i++) { + stfsm_load_seq(fsm, seq); + stfsm_wait_seq(fsm); + words = stfsm_fifo_available(fsm); + } + + /* - A single word must be available now */ + if (words != 1) { + dev_err(fsm->dev, "failed to clear bytes from the data FIFO\n"); + return; + } + + /* - Read the 32-bit word */ + readl(fsm->base + SPI_FAST_SEQ_DATA_REG); + + dev_dbg(fsm->dev, "cleared %d byte(s) from the data FIFO\n", 4 - i); +} + static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, uint32_t size) { From 69d5af8d016c803420258a476789ec6e7e7844dc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Dec 2014 11:59:09 +0000 Subject: [PATCH 099/360] mtd: st_spi_fsm: Obtain and use EMI clock ST's Common Clk Framework is now available. This patch ensures the FSM makes use of it by obtaining and enabling the EMI clock. If system fails to provide the EMI clock, we bomb out. Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 40 ++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index bebc8b5637c057..3451864b9f68c9 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "serial_flash_cmds.h" @@ -262,6 +263,7 @@ struct stfsm { struct mtd_info mtd; struct mutex lock; struct flash_info *info; + struct clk *clk; uint32_t configuration; uint32_t fifo_dir_delay; @@ -1906,8 +1908,7 @@ static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq) uint32_t emi_freq; uint32_t clk_div; - /* TODO: Make this dynamic */ - emi_freq = STFSM_DEFAULT_EMI_FREQ; + emi_freq = clk_get_rate(fsm->clk); /* * Calculate clk_div - values between 2 and 128 @@ -2057,6 +2058,18 @@ static int stfsm_probe(struct platform_device *pdev) return PTR_ERR(fsm->base); } + fsm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(fsm->clk)) { + dev_err(fsm->dev, "Couldn't find EMI clock.\n"); + return PTR_ERR(fsm->clk); + } + + ret = clk_prepare_enable(fsm->clk); + if (ret) { + dev_err(fsm->dev, "Failed to enable EMI clock.\n"); + return ret; + } + mutex_init(&fsm->lock); ret = stfsm_init(fsm); @@ -2121,6 +2134,28 @@ static int stfsm_remove(struct platform_device *pdev) return mtd_device_unregister(&fsm->mtd); } +#ifdef CONFIG_PM_SLEEP +static int stfsmfsm_suspend(struct device *dev) +{ + struct stfsm *fsm = dev_get_drvdata(dev); + + clk_disable_unprepare(fsm->clk); + + return 0; +} + +static int stfsmfsm_resume(struct device *dev) +{ + struct stfsm *fsm = dev_get_drvdata(dev); + + clk_prepare_enable(fsm->clk); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stfsm_pm_ops, stfsmfsm_suspend, stfsmfsm_resume); + static const struct of_device_id stfsm_match[] = { { .compatible = "st,spi-fsm", }, {}, @@ -2133,6 +2168,7 @@ static struct platform_driver stfsm_driver = { .driver = { .name = "st-spi-fsm", .of_match_table = stfsm_match, + .pm = &stfsm_pm_ops, }, }; module_platform_driver(stfsm_driver); From a9b679bfd2820f59a0d914dad148f1fb09d3084a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Dec 2014 11:59:12 +0000 Subject: [PATCH 100/360] mtd: st_spi_fsm: Fix [-Wsign-compare] build warning drivers/mtd/devices/st_spi_fsm.c:1647:17: warning: comparison between signed and unsigned integer expressions Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 3451864b9f68c9..3060025c8af477 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -1586,11 +1586,11 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, uint32_t size_lb; uint32_t size_mop; uint32_t tmp[4]; + uint32_t i; uint32_t page_buf[FLASH_PAGESIZE_32]; uint8_t *t = (uint8_t *)&tmp; const uint8_t *p; int ret; - int i; dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset); From 7c272ac5eefb329501aadbd9f21f86232c0f66a9 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Fri, 9 Jan 2015 09:32:35 -0600 Subject: [PATCH 101/360] mtd: denali: fix incorrect bitmask error in denali_setup_dma commit 3157d1ed2309 ("mtd: denali: remove unnecessary casts") introduced an error by using a wrong bitmask. A uint16_t cast was replaced with & 0xff, should be & 0xffff. Signed-off-by: Graham Moore Signed-off-by: Dinh Nguyen Signed-off-by: Brian Norris --- drivers/mtd/nand/denali.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index b3b7ca1bafb807..5e397fba419baf 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1041,7 +1041,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) index_addr(denali, mode | ((addr >> 16) << 8), 0x2200); /* 3. set memory low address bits 23:8 */ - index_addr(denali, mode | ((addr & 0xff) << 8), 0x2300); + index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300); /* 4. interrupt when complete, burst len = 64 bytes */ index_addr(denali, mode | 0x14000, 0x2400); From e182c570e9953859aee5cb016583217d9e68ea18 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 102/360] x86/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: Thomas Gleixner --- arch/x86/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 0d592e0a5b84fa..ace9dec050b17b 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -179,7 +179,7 @@ __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL)) asm volatile("call __get_user_%P3" \ : "=a" (__ret_gu), "=r" (__val_gu) \ : "0" (ptr), "i" (sizeof(*(ptr)))); \ - (x) = (__typeof__(*(ptr))) __val_gu; \ + (x) = (__force __typeof__(*(ptr))) __val_gu; \ __ret_gu; \ }) From 1ab5786ae45ace5d13517f21508607c5f795a822 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 103/360] alpha/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin --- arch/alpha/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/alpha/include/asm/uaccess.h b/arch/alpha/include/asm/uaccess.h index 766fdfde2b7aa0..a234de79157a4a 100644 --- a/arch/alpha/include/asm/uaccess.h +++ b/arch/alpha/include/asm/uaccess.h @@ -96,7 +96,7 @@ extern void __get_user_unknown(void); case 8: __get_user_64(ptr); break; \ default: __get_user_unknown(); break; \ } \ - (x) = (__typeof__(*(ptr))) __gu_val; \ + (x) = (__force __typeof__(*(ptr))) __gu_val; \ __gu_err; \ }) @@ -115,7 +115,7 @@ extern void __get_user_unknown(void); default: __get_user_unknown(); break; \ } \ } \ - (x) = (__typeof__(*(ptr))) __gu_val; \ + (x) = (__force __typeof__(*(ptr))) __gu_val; \ __gu_err; \ }) From 58fff51784cb5e1bcc06a1417be26eec4288507c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 104/360] arm64/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: Will Deacon --- arch/arm64/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 3bf8f4e99a511c..9a2069bbc00d77 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -147,7 +147,7 @@ do { \ default: \ BUILD_BUG(); \ } \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ } while (0) #define __get_user(x, ptr) \ From 7f0db2bec363bf1a4d6da2572491c634a34dcad8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 105/360] avr32/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: Hans-Christian Egtvedt --- arch/avr32/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/avr32/include/asm/uaccess.h b/arch/avr32/include/asm/uaccess.h index 245b2ee213c915..ccd07c456f9aaa 100644 --- a/arch/avr32/include/asm/uaccess.h +++ b/arch/avr32/include/asm/uaccess.h @@ -191,7 +191,7 @@ extern int __put_user_bad(void); default: __gu_err = __get_user_bad(); break; \ } \ \ - x = (typeof(*(ptr)))__gu_val; \ + x = (__force typeof(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -222,7 +222,7 @@ extern int __put_user_bad(void); } else { \ __gu_err = -EFAULT; \ } \ - x = (typeof(*(ptr)))__gu_val; \ + x = (__force typeof(*(ptr)))__gu_val; \ __gu_err; \ }) From f5e0c47ef3293bef8eab634e5849c2559ed48a39 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 106/360] blackfin/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: Steven Miao --- arch/blackfin/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h index 57701c3b8a591b..2dcc9303355df0 100644 --- a/arch/blackfin/include/asm/uaccess.h +++ b/arch/blackfin/include/asm/uaccess.h @@ -147,7 +147,7 @@ static inline int bad_user_access_length(void) } \ } else \ _err = -EFAULT; \ - x = (typeof(*(ptr)))_val; \ + x = (__force typeof(*(ptr)))_val; \ _err; \ }) From 92acb6c2ac16c7ed1383b04055966633607959cd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 107/360] cris/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin --- arch/cris/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/cris/include/asm/uaccess.h b/arch/cris/include/asm/uaccess.h index 914540801c5e0f..9cf5a23baed3fb 100644 --- a/arch/cris/include/asm/uaccess.h +++ b/arch/cris/include/asm/uaccess.h @@ -153,7 +153,7 @@ struct __large_struct { unsigned long buf[100]; }; ({ \ long __gu_err, __gu_val; \ __get_user_size(__gu_val,(ptr),(size),__gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -163,7 +163,7 @@ struct __large_struct { unsigned long buf[100]; }; const __typeof__(*(ptr)) *__gu_addr = (ptr); \ if (access_ok(VERIFY_READ,__gu_addr,size)) \ __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) From a6325e7256ea4a31e5382adb2ecfba406d42e289 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 108/360] ia64/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin --- arch/ia64/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ia64/include/asm/uaccess.h b/arch/ia64/include/asm/uaccess.h index 103bedc59644a6..967c312a6bafe9 100644 --- a/arch/ia64/include/asm/uaccess.h +++ b/arch/ia64/include/asm/uaccess.h @@ -197,7 +197,7 @@ extern void __get_user_unknown (void); case 8: __get_user_size(__gu_val, __gu_ptr, 8, __gu_err); break; \ default: __get_user_unknown(); break; \ } \ - (x) = (__typeof__(*(__gu_ptr))) __gu_val; \ + (x) = (__force __typeof__(*(__gu_ptr))) __gu_val; \ __gu_err; \ }) From a618337e8cd811f07bd660a857d5d9f0201f042b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 109/360] m32r/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin --- arch/m32r/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/m32r/include/asm/uaccess.h b/arch/m32r/include/asm/uaccess.h index 84fe7ba53035dd..d076942a7b2b2c 100644 --- a/arch/m32r/include/asm/uaccess.h +++ b/arch/m32r/include/asm/uaccess.h @@ -218,7 +218,7 @@ extern int fixup_exception(struct pt_regs *regs); unsigned long __gu_val; \ might_fault(); \ __get_user_size(__gu_val,(ptr),(size),__gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -230,7 +230,7 @@ extern int fixup_exception(struct pt_regs *regs); might_fault(); \ if (access_ok(VERIFY_READ,__gu_addr,size)) \ __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) From 8f4e4c1aef356ac4ce67caeffac29af4971fe048 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 110/360] metag/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: James Hogan --- arch/metag/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h index 0748b0a9798684..fd863fead4af18 100644 --- a/arch/metag/include/asm/uaccess.h +++ b/arch/metag/include/asm/uaccess.h @@ -135,7 +135,7 @@ extern long __get_user_bad(void); ({ \ long __gu_err, __gu_val; \ __get_user_size(__gu_val, (ptr), (size), __gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -145,7 +145,7 @@ extern long __get_user_bad(void); const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ if (access_ok(VERIFY_READ, __gu_addr, size)) \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) From 582a6783c56517b84a06da72e18e1e44a59558e6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 111/360] openrisc/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin --- arch/openrisc/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h index ab2e7a198a4cfe..a6bd07ca3d6c08 100644 --- a/arch/openrisc/include/asm/uaccess.h +++ b/arch/openrisc/include/asm/uaccess.h @@ -192,7 +192,7 @@ struct __large_struct { ({ \ long __gu_err, __gu_val; \ __get_user_size(__gu_val, (ptr), (size), __gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -202,7 +202,7 @@ struct __large_struct { const __typeof__(*(ptr)) * __gu_addr = (ptr); \ if (access_ok(VERIFY_READ, __gu_addr, size)) \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) From 079f0f269f2a64080d7fa45cf686b02662ff8e36 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 112/360] parisc/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: Helge Deller --- arch/parisc/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index a5cb070b54bf4a..6c79311acdafe4 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -104,7 +104,7 @@ struct exception_data { } \ } \ \ - (x) = (__typeof__(*(ptr))) __gu_val; \ + (x) = (__force __typeof__(*(ptr))) __gu_val; \ __gu_err; \ }) From cad1c0dfc8961abeb92e5afbfa275b6122501e5f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 113/360] sh/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin --- arch/sh/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h index 9486376605f4dc..a49635c512665d 100644 --- a/arch/sh/include/asm/uaccess.h +++ b/arch/sh/include/asm/uaccess.h @@ -60,7 +60,7 @@ struct __large_struct { unsigned long buf[100]; }; const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __chk_user_ptr(ptr); \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -71,7 +71,7 @@ struct __large_struct { unsigned long buf[100]; }; const __typeof__(*(ptr)) *__gu_addr = (ptr); \ if (likely(access_ok(VERIFY_READ, __gu_addr, (size)))) \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ - (x) = (__typeof__(*(ptr)))__gu_val; \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) From 9b7accb286982e0df140b3accae8216656b3ef37 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 114/360] sparc32/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: David S. Miller --- arch/sparc/include/asm/uaccess_32.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index 9634d086fc562f..8b571a0db0f6e6 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -164,7 +164,7 @@ case 2: __get_user_asm(__gu_val,uh,addr,__gu_ret); break; \ case 4: __get_user_asm(__gu_val,,addr,__gu_ret); break; \ case 8: __get_user_asm(__gu_val,d,addr,__gu_ret); break; \ default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \ -} } else { __gu_val = 0; __gu_ret = -EFAULT; } x = (type) __gu_val; __gu_ret; }) +} } else { __gu_val = 0; __gu_ret = -EFAULT; } x = (__force type) __gu_val; __gu_ret; }) #define __get_user_check_ret(x,addr,size,type,retval) ({ \ register unsigned long __gu_val __asm__ ("l1"); \ @@ -175,7 +175,7 @@ case 2: __get_user_asm_ret(__gu_val,uh,addr,retval); break; \ case 4: __get_user_asm_ret(__gu_val,,addr,retval); break; \ case 8: __get_user_asm_ret(__gu_val,d,addr,retval); break; \ default: if (__get_user_bad()) return retval; \ -} x = (type) __gu_val; } else return retval; }) +} x = (__force type) __gu_val; } else return retval; }) #define __get_user_nocheck(x,addr,size,type) ({ \ register int __gu_ret; \ @@ -186,7 +186,7 @@ case 2: __get_user_asm(__gu_val,uh,addr,__gu_ret); break; \ case 4: __get_user_asm(__gu_val,,addr,__gu_ret); break; \ case 8: __get_user_asm(__gu_val,d,addr,__gu_ret); break; \ default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \ -} x = (type) __gu_val; __gu_ret; }) +} x = (__force type) __gu_val; __gu_ret; }) #define __get_user_nocheck_ret(x,addr,size,type,retval) ({ \ register unsigned long __gu_val __asm__ ("l1"); \ @@ -196,7 +196,7 @@ case 2: __get_user_asm_ret(__gu_val,uh,addr,retval); break; \ case 4: __get_user_asm_ret(__gu_val,,addr,retval); break; \ case 8: __get_user_asm_ret(__gu_val,d,addr,retval); break; \ default: if (__get_user_bad()) return retval; \ -} x = (type) __gu_val; }) +} x = (__force type) __gu_val; }) #define __get_user_asm(x,size,addr,ret) \ __asm__ __volatile__( \ From 1d638efce8cc77c1ec838886e9a64f98aa58b780 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 115/360] sparc64/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: David S. Miller --- arch/sparc/include/asm/uaccess_64.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index c990a5e577f027..b80866d9076827 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -145,7 +145,7 @@ case 2: __get_user_asm(__gu_val,uh,addr,__gu_ret); break; \ case 4: __get_user_asm(__gu_val,uw,addr,__gu_ret); break; \ case 8: __get_user_asm(__gu_val,x,addr,__gu_ret); break; \ default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \ -} data = (type) __gu_val; __gu_ret; }) +} data = (__force type) __gu_val; __gu_ret; }) #define __get_user_nocheck_ret(data,addr,size,type,retval) ({ \ register unsigned long __gu_val __asm__ ("l1"); \ @@ -155,7 +155,7 @@ case 2: __get_user_asm_ret(__gu_val,uh,addr,retval); break; \ case 4: __get_user_asm_ret(__gu_val,uw,addr,retval); break; \ case 8: __get_user_asm_ret(__gu_val,x,addr,retval); break; \ default: if (__get_user_bad()) return retval; \ -} data = (type) __gu_val; }) +} data = (__force type) __gu_val; }) #define __get_user_asm(x,size,addr,ret) \ __asm__ __volatile__( \ From 09a2f7cf6a89ec011bda8c0f0f8d0790a1176973 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 12 Dec 2014 01:56:04 +0200 Subject: [PATCH 116/360] m68k/uaccess: fix sparse errors virtio wants to read bitwise types from userspace using get_user. At the moment this triggers sparse errors, since the value is passed through an integer. Fix that up using __force. Signed-off-by: Michael S. Tsirkin Acked-by: Geert Uytterhoeven --- arch/m68k/include/asm/uaccess_mm.h | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/arch/m68k/include/asm/uaccess_mm.h b/arch/m68k/include/asm/uaccess_mm.h index 15901db435b906..d228601b3afce7 100644 --- a/arch/m68k/include/asm/uaccess_mm.h +++ b/arch/m68k/include/asm/uaccess_mm.h @@ -128,25 +128,25 @@ asm volatile ("\n" \ #define put_user(x, ptr) __put_user(x, ptr) -#define __get_user_asm(res, x, ptr, type, bwl, reg, err) ({ \ - type __gu_val; \ - asm volatile ("\n" \ - "1: "MOVES"."#bwl" %2,%1\n" \ - "2:\n" \ - " .section .fixup,\"ax\"\n" \ - " .even\n" \ - "10: move.l %3,%0\n" \ - " sub.l %1,%1\n" \ - " jra 2b\n" \ - " .previous\n" \ - "\n" \ - " .section __ex_table,\"a\"\n" \ - " .align 4\n" \ - " .long 1b,10b\n" \ - " .previous" \ - : "+d" (res), "=&" #reg (__gu_val) \ - : "m" (*(ptr)), "i" (err)); \ - (x) = (typeof(*(ptr)))(unsigned long)__gu_val; \ +#define __get_user_asm(res, x, ptr, type, bwl, reg, err) ({ \ + type __gu_val; \ + asm volatile ("\n" \ + "1: "MOVES"."#bwl" %2,%1\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "10: move.l %3,%0\n" \ + " sub.l %1,%1\n" \ + " jra 2b\n" \ + " .previous\n" \ + "\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,10b\n" \ + " .previous" \ + : "+d" (res), "=&" #reg (__gu_val) \ + : "m" (*(ptr)), "i" (err)); \ + (x) = (__force typeof(*(ptr)))(__force unsigned long)__gu_val; \ }) #define __get_user(x, ptr) \ @@ -188,7 +188,7 @@ asm volatile ("\n" \ "+a" (__gu_ptr) \ : "i" (-EFAULT) \ : "memory"); \ - (x) = (typeof(*(ptr)))__gu_val; \ + (x) = (__force typeof(*(ptr)))__gu_val; \ break; \ } */ \ default: \ From e8b94dea3867139fe92f03b913e38ca841e390fd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:37:22 +0200 Subject: [PATCH 117/360] arm: fix put_user sparse errors virtio wants to write bitwise types to userspace using put_user. At the moment this triggers sparse errors, since the value is passed through an integer. For example: __le32 __user *p; __le32 x; put_user(x, p); is safe, but currently triggers a sparse warning. Fix that up using __force. Note: this does not suppress any useful sparse checks since caller assigns x to typeof(*p), which in turn forces all the necessary type checks. Signed-off-by: Michael S. Tsirkin --- arch/arm/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index 4767eb9caa78c8..74fcde756fdb73 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -413,14 +413,14 @@ do { \ #ifndef __ARMEB__ #define __put_user_asm_half(x,__pu_addr,err) \ ({ \ - unsigned long __temp = (unsigned long)(x); \ + unsigned long __temp = (__force unsigned long)(x); \ __put_user_asm_byte(__temp, __pu_addr, err); \ __put_user_asm_byte(__temp >> 8, __pu_addr + 1, err); \ }) #else #define __put_user_asm_half(x,__pu_addr,err) \ ({ \ - unsigned long __temp = (unsigned long)(x); \ + unsigned long __temp = (__force unsigned long)(x); \ __put_user_asm_byte(__temp >> 8, __pu_addr, err); \ __put_user_asm_byte(__temp, __pu_addr + 1, err); \ }) From 1734bffc30b80ab2447345369c84175e721ebd65 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:37:22 +0200 Subject: [PATCH 118/360] blackfin: fix put_user sparse errors virtio wants to write bitwise types to userspace using put_user. At the moment this triggers sparse errors, since the value is passed through an integer. For example: __le32 __user *p; __le32 x; put_user(x, p); is safe, but currently triggers a sparse warning. Fix that up using __force. Note: this does not suppress any useful sparse checks since caller assigns x to typeof(*p), which in turn forces all the necessary type checks. Signed-off-by: Michael S. Tsirkin --- arch/blackfin/include/asm/uaccess.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h index 2dcc9303355df0..4bf968c01cf92c 100644 --- a/arch/blackfin/include/asm/uaccess.h +++ b/arch/blackfin/include/asm/uaccess.h @@ -89,10 +89,10 @@ struct exception_table_entry { break; \ case 8: { \ long _xl, _xh; \ - _xl = ((long *)&_x)[0]; \ - _xh = ((long *)&_x)[1]; \ - __put_user_asm(_xl, ((long __user *)_p)+0, ); \ - __put_user_asm(_xh, ((long __user *)_p)+1, ); \ + _xl = ((__force long *)&_x)[0]; \ + _xh = ((__force long *)&_x)[1]; \ + __put_user_asm(_xl, ((__force long __user *)_p)+0, );\ + __put_user_asm(_xh, ((__force long __user *)_p)+1, );\ } break; \ default: \ _err = __put_user_bad(); \ From 9605ce7e5fe058c94fa354415d122462fb419a00 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:37:22 +0200 Subject: [PATCH 119/360] ia64: fix put_user sparse errors virtio wants to write bitwise types to userspace using put_user. At the moment this triggers sparse errors, since the value is passed through an integer. For example: __le32 __user *p; __le32 x; put_user(x, p); is safe, but currently triggers a sparse warning. Fix that up using __force. Note: this does not suppress any useful sparse checks since callers do a cast (__typeof__(*(ptr))) (x) which in turn forces all the necessary type checks. Signed-off-by: Michael S. Tsirkin --- arch/ia64/include/asm/uaccess.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/ia64/include/asm/uaccess.h b/arch/ia64/include/asm/uaccess.h index 967c312a6bafe9..4f3fb6ccbf2139 100644 --- a/arch/ia64/include/asm/uaccess.h +++ b/arch/ia64/include/asm/uaccess.h @@ -169,10 +169,11 @@ do { \ (err) = ia64_getreg(_IA64_REG_R8); \ (val) = ia64_getreg(_IA64_REG_R9); \ } while (0) -# define __put_user_size(val, addr, n, err) \ -do { \ - __st_user("__ex_table", (unsigned long) addr, n, RELOC_TYPE, (unsigned long) (val)); \ - (err) = ia64_getreg(_IA64_REG_R8); \ +# define __put_user_size(val, addr, n, err) \ +do { \ + __st_user("__ex_table", (unsigned long) addr, n, RELOC_TYPE, \ + (__force unsigned long) (val)); \ + (err) = ia64_getreg(_IA64_REG_R8); \ } while (0) #endif /* !ASM_SUPPORTED */ From 9ef8dc161faaa24c58322aa928b3216213621daa Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:37:22 +0200 Subject: [PATCH 120/360] metag: fix put_user sparse errors virtio wants to write bitwise types to userspace using put_user. At the moment this triggers sparse errors, since the value is passed through an integer. For example: __le32 __user *p; __le32 x; put_user(x, p); is safe, but currently triggers a sparse warning. Fix that up using __force. This also fixes warnings due to writing a pointer out to userland. Note: this does not suppress any useful sparse checks since callers do a cast (__typeof__(*(ptr))) (x) which in turn forces all the necessary type checks. Suggested-by: James Hogan Signed-off-by: Michael S. Tsirkin Acked-by: James Hogan --- arch/metag/include/asm/uaccess.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h index fd863fead4af18..8282cbce7e399a 100644 --- a/arch/metag/include/asm/uaccess.h +++ b/arch/metag/include/asm/uaccess.h @@ -107,18 +107,23 @@ extern long __put_user_asm_w(unsigned int x, void __user *addr); extern long __put_user_asm_d(unsigned int x, void __user *addr); extern long __put_user_asm_l(unsigned long long x, void __user *addr); -#define __put_user_size(x, ptr, size, retval) \ -do { \ - retval = 0; \ - switch (size) { \ +#define __put_user_size(x, ptr, size, retval) \ +do { \ + retval = 0; \ + switch (size) { \ case 1: \ - retval = __put_user_asm_b((unsigned int)x, ptr); break; \ + retval = __put_user_asm_b((__force unsigned int)x, ptr);\ + break; \ case 2: \ - retval = __put_user_asm_w((unsigned int)x, ptr); break; \ + retval = __put_user_asm_w((__force unsigned int)x, ptr);\ + break; \ case 4: \ - retval = __put_user_asm_d((unsigned int)x, ptr); break; \ + retval = __put_user_asm_d((__force unsigned int)x, ptr);\ + break; \ case 8: \ - retval = __put_user_asm_l((unsigned long long)x, ptr); break; \ + retval = __put_user_asm_l((__force unsigned long long)x,\ + ptr); \ + break; \ default: \ __put_user_bad(); \ } \ From 66959ed0e4bd673f140f550fd3f7b3a70e8dbd24 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:37:22 +0200 Subject: [PATCH 121/360] sh: fix put_user sparse errors virtio wants to write bitwise types to userspace using put_user. At the moment this triggers sparse errors, since the value is passed through an integer. For example: __le32 __user *p; __le32 x; put_user(x, p); is safe, but currently triggers a sparse warning. Fix that up using __force. Note: this does not suppress any useful sparse checks since caller assigns x to typeof(*p), which in turn forces all the necessary type checks. Signed-off-by: Michael S. Tsirkin --- arch/sh/include/asm/uaccess_64.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/sh/include/asm/uaccess_64.h b/arch/sh/include/asm/uaccess_64.h index 2e07e0f40c6af3..c01376c76b8687 100644 --- a/arch/sh/include/asm/uaccess_64.h +++ b/arch/sh/include/asm/uaccess_64.h @@ -59,19 +59,19 @@ do { \ switch (size) { \ case 1: \ retval = __put_user_asm_b((void *)&x, \ - (long)ptr); \ + (__force long)ptr); \ break; \ case 2: \ retval = __put_user_asm_w((void *)&x, \ - (long)ptr); \ + (__force long)ptr); \ break; \ case 4: \ retval = __put_user_asm_l((void *)&x, \ - (long)ptr); \ + (__force long)ptr); \ break; \ case 8: \ retval = __put_user_asm_q((void *)&x, \ - (long)ptr); \ + (__force long)ptr); \ break; \ default: \ __put_user_unknown(); \ From 99408c840a87e4d1f8f9e1adc95171b9da10459e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:53:58 +0200 Subject: [PATCH 122/360] avr32: whitespace fix Align using tabs to make code prettier. Signed-off-by: Michael S. Tsirkin Acked-by: Hans-Christian Egtvedt --- arch/avr32/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/avr32/include/asm/uaccess.h b/arch/avr32/include/asm/uaccess.h index ccd07c456f9aaa..72607f32eebbab 100644 --- a/arch/avr32/include/asm/uaccess.h +++ b/arch/avr32/include/asm/uaccess.h @@ -278,7 +278,7 @@ extern int __put_user_bad(void); __pu_err); \ break; \ case 8: \ - __put_user_asm("d", __pu_addr, __pu_val, \ + __put_user_asm("d", __pu_addr, __pu_val, \ __pu_err); \ break; \ default: \ From 8ccf7b2599abd08ab3a51d5f2299e301daae0860 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:30:51 +0200 Subject: [PATCH 123/360] sparc32: uaccess_32 macro whitespace fixes Macros within arch/sparc/include/asm/uaccess_32.h are made harder to read because they violate a bunch of coding style rules. Fix it up. Signed-off-by: Michael S. Tsirkin Acked-by: David S. Miller --- arch/sparc/include/asm/uaccess_32.h | 365 +++++++++++++++++----------- 1 file changed, 227 insertions(+), 138 deletions(-) diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index 8b571a0db0f6e6..f96656209a78b8 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -37,7 +37,7 @@ #define get_fs() (current->thread.current_ds) #define set_fs(val) ((current->thread.current_ds) = (val)) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) /* We have there a nice not-mapped page at PAGE_OFFSET - PAGE_SIZE, so that this test * can be fairly lightweight. @@ -46,8 +46,8 @@ */ #define __user_ok(addr, size) ({ (void)(size); (addr) < STACK_TOP; }) #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) -#define __access_ok(addr,size) (__user_ok((addr) & get_fs().seg,(size))) -#define access_ok(type, addr, size) \ +#define __access_ok(addr, size) (__user_ok((addr) & get_fs().seg, (size))) +#define access_ok(type, addr, size) \ ({ (void)(type); __access_ok((unsigned long)(addr), size); }) /* @@ -91,158 +91,247 @@ void __ret_efault(void); * of a performance impact. Thus we have a few rather ugly macros here, * and hide all the ugliness from the user. */ -#define put_user(x,ptr) ({ \ -unsigned long __pu_addr = (unsigned long)(ptr); \ -__chk_user_ptr(ptr); \ -__put_user_check((__typeof__(*(ptr)))(x),__pu_addr,sizeof(*(ptr))); }) - -#define get_user(x,ptr) ({ \ -unsigned long __gu_addr = (unsigned long)(ptr); \ -__chk_user_ptr(ptr); \ -__get_user_check((x),__gu_addr,sizeof(*(ptr)),__typeof__(*(ptr))); }) +#define put_user(x, ptr) ({ \ + unsigned long __pu_addr = (unsigned long)(ptr); \ + __chk_user_ptr(ptr); \ + __put_user_check((__typeof__(*(ptr)))(x), __pu_addr, sizeof(*(ptr))); \ +}) + +#define get_user(x, ptr) ({ \ + unsigned long __gu_addr = (unsigned long)(ptr); \ + __chk_user_ptr(ptr); \ + __get_user_check((x), __gu_addr, sizeof(*(ptr)), __typeof__(*(ptr))); \ +}) /* * The "__xxx" versions do not do address space checking, useful when * doing multiple accesses to the same area (the user has to do the * checks by hand with "access_ok()") */ -#define __put_user(x,ptr) __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) -#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr)),__typeof__(*(ptr))) +#define __put_user(x, ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) +#define __get_user(x, ptr) \ + __get_user_nocheck((x), (ptr), sizeof(*(ptr)), __typeof__(*(ptr))) struct __large_struct { unsigned long buf[100]; }; #define __m(x) ((struct __large_struct __user *)(x)) -#define __put_user_check(x,addr,size) ({ \ -register int __pu_ret; \ -if (__access_ok(addr,size)) { \ -switch (size) { \ -case 1: __put_user_asm(x,b,addr,__pu_ret); break; \ -case 2: __put_user_asm(x,h,addr,__pu_ret); break; \ -case 4: __put_user_asm(x,,addr,__pu_ret); break; \ -case 8: __put_user_asm(x,d,addr,__pu_ret); break; \ -default: __pu_ret = __put_user_bad(); break; \ -} } else { __pu_ret = -EFAULT; } __pu_ret; }) - -#define __put_user_nocheck(x,addr,size) ({ \ -register int __pu_ret; \ -switch (size) { \ -case 1: __put_user_asm(x,b,addr,__pu_ret); break; \ -case 2: __put_user_asm(x,h,addr,__pu_ret); break; \ -case 4: __put_user_asm(x,,addr,__pu_ret); break; \ -case 8: __put_user_asm(x,d,addr,__pu_ret); break; \ -default: __pu_ret = __put_user_bad(); break; \ -} __pu_ret; }) - -#define __put_user_asm(x,size,addr,ret) \ +#define __put_user_check(x, addr, size) ({ \ + register int __pu_ret; \ + if (__access_ok(addr, size)) { \ + switch (size) { \ + case 1: \ + __put_user_asm(x, b, addr, __pu_ret); \ + break; \ + case 2: \ + __put_user_asm(x, h, addr, __pu_ret); \ + break; \ + case 4: \ + __put_user_asm(x, , addr, __pu_ret); \ + break; \ + case 8: \ + __put_user_asm(x, d, addr, __pu_ret); \ + break; \ + default: \ + __pu_ret = __put_user_bad(); \ + break; \ + } \ + } else { \ + __pu_ret = -EFAULT; \ + } \ + __pu_ret; \ +}) + +#define __put_user_nocheck(x, addr, size) ({ \ + register int __pu_ret; \ + switch (size) { \ + case 1: \ + __put_user_asm(x, b, addr, __pu_ret); \ + break; \ + case 2: \ + __put_user_asm(x, h, addr, __pu_ret); \ + break; \ + case 4: \ + __put_user_asm(x, , addr, __pu_ret); \ + break; \ + case 8: \ + __put_user_asm(x, d, addr, __pu_ret); \ + break; \ + default: \ + __pu_ret = __put_user_bad(); \ + break; \ + } \ + __pu_ret; \ +}) + +#define __put_user_asm(x, size, addr, ret) \ __asm__ __volatile__( \ - "/* Put user asm, inline. */\n" \ -"1:\t" "st"#size " %1, %2\n\t" \ - "clr %0\n" \ -"2:\n\n\t" \ - ".section .fixup,#alloc,#execinstr\n\t" \ - ".align 4\n" \ -"3:\n\t" \ - "b 2b\n\t" \ - " mov %3, %0\n\t" \ - ".previous\n\n\t" \ - ".section __ex_table,#alloc\n\t" \ - ".align 4\n\t" \ - ".word 1b, 3b\n\t" \ - ".previous\n\n\t" \ - : "=&r" (ret) : "r" (x), "m" (*__m(addr)), \ - "i" (-EFAULT)) + "/* Put user asm, inline. */\n" \ + "1:\t" "st"#size " %1, %2\n\t" \ + "clr %0\n" \ + "2:\n\n\t" \ + ".section .fixup,#alloc,#execinstr\n\t" \ + ".align 4\n" \ + "3:\n\t" \ + "b 2b\n\t" \ + " mov %3, %0\n\t" \ + ".previous\n\n\t" \ + ".section __ex_table,#alloc\n\t" \ + ".align 4\n\t" \ + ".word 1b, 3b\n\t" \ + ".previous\n\n\t" \ + : "=&r" (ret) : "r" (x), "m" (*__m(addr)), \ + "i" (-EFAULT)) int __put_user_bad(void); -#define __get_user_check(x,addr,size,type) ({ \ -register int __gu_ret; \ -register unsigned long __gu_val; \ -if (__access_ok(addr,size)) { \ -switch (size) { \ -case 1: __get_user_asm(__gu_val,ub,addr,__gu_ret); break; \ -case 2: __get_user_asm(__gu_val,uh,addr,__gu_ret); break; \ -case 4: __get_user_asm(__gu_val,,addr,__gu_ret); break; \ -case 8: __get_user_asm(__gu_val,d,addr,__gu_ret); break; \ -default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \ -} } else { __gu_val = 0; __gu_ret = -EFAULT; } x = (__force type) __gu_val; __gu_ret; }) - -#define __get_user_check_ret(x,addr,size,type,retval) ({ \ -register unsigned long __gu_val __asm__ ("l1"); \ -if (__access_ok(addr,size)) { \ -switch (size) { \ -case 1: __get_user_asm_ret(__gu_val,ub,addr,retval); break; \ -case 2: __get_user_asm_ret(__gu_val,uh,addr,retval); break; \ -case 4: __get_user_asm_ret(__gu_val,,addr,retval); break; \ -case 8: __get_user_asm_ret(__gu_val,d,addr,retval); break; \ -default: if (__get_user_bad()) return retval; \ -} x = (__force type) __gu_val; } else return retval; }) - -#define __get_user_nocheck(x,addr,size,type) ({ \ -register int __gu_ret; \ -register unsigned long __gu_val; \ -switch (size) { \ -case 1: __get_user_asm(__gu_val,ub,addr,__gu_ret); break; \ -case 2: __get_user_asm(__gu_val,uh,addr,__gu_ret); break; \ -case 4: __get_user_asm(__gu_val,,addr,__gu_ret); break; \ -case 8: __get_user_asm(__gu_val,d,addr,__gu_ret); break; \ -default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \ -} x = (__force type) __gu_val; __gu_ret; }) - -#define __get_user_nocheck_ret(x,addr,size,type,retval) ({ \ -register unsigned long __gu_val __asm__ ("l1"); \ -switch (size) { \ -case 1: __get_user_asm_ret(__gu_val,ub,addr,retval); break; \ -case 2: __get_user_asm_ret(__gu_val,uh,addr,retval); break; \ -case 4: __get_user_asm_ret(__gu_val,,addr,retval); break; \ -case 8: __get_user_asm_ret(__gu_val,d,addr,retval); break; \ -default: if (__get_user_bad()) return retval; \ -} x = (__force type) __gu_val; }) - -#define __get_user_asm(x,size,addr,ret) \ +#define __get_user_check(x, addr, size, type) ({ \ + register int __gu_ret; \ + register unsigned long __gu_val; \ + if (__access_ok(addr, size)) { \ + switch (size) { \ + case 1: \ + __get_user_asm(__gu_val, ub, addr, __gu_ret); \ + break; \ + case 2: \ + __get_user_asm(__gu_val, uh, addr, __gu_ret); \ + break; \ + case 4: \ + __get_user_asm(__gu_val, , addr, __gu_ret); \ + break; \ + case 8: \ + __get_user_asm(__gu_val, d, addr, __gu_ret); \ + break; \ + default: \ + __gu_val = 0; \ + __gu_ret = __get_user_bad(); \ + break; \ + } \ + } else { \ + __gu_val = 0; \ + __gu_ret = -EFAULT; \ + } \ + x = (__force type) __gu_val; \ + __gu_ret; \ +}) + +#define __get_user_check_ret(x, addr, size, type, retval) ({ \ + register unsigned long __gu_val __asm__ ("l1"); \ + if (__access_ok(addr, size)) { \ + switch (size) { \ + case 1: \ + __get_user_asm_ret(__gu_val, ub, addr, retval); \ + break; \ + case 2: \ + __get_user_asm_ret(__gu_val, uh, addr, retval); \ + break; \ + case 4: \ + __get_user_asm_ret(__gu_val, , addr, retval); \ + break; \ + case 8: \ + __get_user_asm_ret(__gu_val, d, addr, retval); \ + break; \ + default: \ + if (__get_user_bad()) \ + return retval; \ + } \ + x = (__force type) __gu_val; \ + } else \ + return retval; \ +}) + +#define __get_user_nocheck(x, addr, size, type) ({ \ + register int __gu_ret; \ + register unsigned long __gu_val; \ + switch (size) { \ + case 1: \ + __get_user_asm(__gu_val, ub, addr, __gu_ret); \ + break; \ + case 2: \ + __get_user_asm(__gu_val, uh, addr, __gu_ret); \ + break; \ + case 4: \ + __get_user_asm(__gu_val, , addr, __gu_ret); \ + break; \ + case 8: \ + __get_user_asm(__gu_val, d, addr, __gu_ret); \ + break; \ + default: \ + __gu_val = 0; \ + __gu_ret = __get_user_bad(); \ + break; \ + } \ + x = (__force type) __gu_val; \ + __gu_ret; \ +}) + +#define __get_user_nocheck_ret(x, addr, size, type, retval) ({ \ + register unsigned long __gu_val __asm__ ("l1"); \ + switch (size) { \ + case 1: \ + __get_user_asm_ret(__gu_val, ub, addr, retval); \ + break; \ + case 2: \ + __get_user_asm_ret(__gu_val, uh, addr, retval); \ + break; \ + case 4: \ + __get_user_asm_ret(__gu_val, , addr, retval); \ + break; \ + case 8: \ + __get_user_asm_ret(__gu_val, d, addr, retval); \ + break; \ + default: \ + if (__get_user_bad()) \ + return retval; \ + } \ + x = (__force type) __gu_val; \ +}) + +#define __get_user_asm(x, size, addr, ret) \ __asm__ __volatile__( \ - "/* Get user asm, inline. */\n" \ -"1:\t" "ld"#size " %2, %1\n\t" \ - "clr %0\n" \ -"2:\n\n\t" \ - ".section .fixup,#alloc,#execinstr\n\t" \ - ".align 4\n" \ -"3:\n\t" \ - "clr %1\n\t" \ - "b 2b\n\t" \ - " mov %3, %0\n\n\t" \ - ".previous\n\t" \ - ".section __ex_table,#alloc\n\t" \ - ".align 4\n\t" \ - ".word 1b, 3b\n\n\t" \ - ".previous\n\t" \ - : "=&r" (ret), "=&r" (x) : "m" (*__m(addr)), \ - "i" (-EFAULT)) - -#define __get_user_asm_ret(x,size,addr,retval) \ + "/* Get user asm, inline. */\n" \ + "1:\t" "ld"#size " %2, %1\n\t" \ + "clr %0\n" \ + "2:\n\n\t" \ + ".section .fixup,#alloc,#execinstr\n\t" \ + ".align 4\n" \ + "3:\n\t" \ + "clr %1\n\t" \ + "b 2b\n\t" \ + " mov %3, %0\n\n\t" \ + ".previous\n\t" \ + ".section __ex_table,#alloc\n\t" \ + ".align 4\n\t" \ + ".word 1b, 3b\n\n\t" \ + ".previous\n\t" \ + : "=&r" (ret), "=&r" (x) : "m" (*__m(addr)), \ + "i" (-EFAULT)) + +#define __get_user_asm_ret(x, size, addr, retval) \ if (__builtin_constant_p(retval) && retval == -EFAULT) \ -__asm__ __volatile__( \ - "/* Get user asm ret, inline. */\n" \ -"1:\t" "ld"#size " %1, %0\n\n\t" \ - ".section __ex_table,#alloc\n\t" \ - ".align 4\n\t" \ - ".word 1b,__ret_efault\n\n\t" \ - ".previous\n\t" \ - : "=&r" (x) : "m" (*__m(addr))); \ + __asm__ __volatile__( \ + "/* Get user asm ret, inline. */\n" \ + "1:\t" "ld"#size " %1, %0\n\n\t" \ + ".section __ex_table,#alloc\n\t" \ + ".align 4\n\t" \ + ".word 1b,__ret_efault\n\n\t" \ + ".previous\n\t" \ + : "=&r" (x) : "m" (*__m(addr))); \ else \ -__asm__ __volatile__( \ - "/* Get user asm ret, inline. */\n" \ -"1:\t" "ld"#size " %1, %0\n\n\t" \ - ".section .fixup,#alloc,#execinstr\n\t" \ - ".align 4\n" \ -"3:\n\t" \ - "ret\n\t" \ - " restore %%g0, %2, %%o0\n\n\t" \ - ".previous\n\t" \ - ".section __ex_table,#alloc\n\t" \ - ".align 4\n\t" \ - ".word 1b, 3b\n\n\t" \ - ".previous\n\t" \ - : "=&r" (x) : "m" (*__m(addr)), "i" (retval)) + __asm__ __volatile__( \ + "/* Get user asm ret, inline. */\n" \ + "1:\t" "ld"#size " %1, %0\n\n\t" \ + ".section .fixup,#alloc,#execinstr\n\t" \ + ".align 4\n" \ + "3:\n\t" \ + "ret\n\t" \ + " restore %%g0, %2, %%o0\n\n\t" \ + ".previous\n\t" \ + ".section __ex_table,#alloc\n\t" \ + ".align 4\n\t" \ + ".word 1b, 3b\n\n\t" \ + ".previous\n\t" \ + : "=&r" (x) : "m" (*__m(addr)), "i" (retval)) int __get_user_bad(void); From 7185820a0ab27f88343ff5f75be5e963c8e19113 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 14:32:17 +0200 Subject: [PATCH 124/360] sparc64: uaccess_64 macro whitespace fixes Macros within arch/sparc/include/asm/uaccess_64.h are made harder to read because they violate a bunch of coding style rules. Fix it up. Signed-off-by: Michael S. Tsirkin Acked-by: David S. Miller --- arch/sparc/include/asm/uaccess_64.h | 248 ++++++++++++++++------------ 1 file changed, 144 insertions(+), 104 deletions(-) diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index b80866d9076827..12d95947636b2e 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -41,11 +41,11 @@ #define get_fs() ((mm_segment_t){(current_thread_info()->current_ds)}) #define get_ds() (KERNEL_DS) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define set_fs(val) \ do { \ - current_thread_info()->current_ds =(val).seg; \ + current_thread_info()->current_ds = (val).seg; \ __asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "r" ((val).seg)); \ } while(0) @@ -88,121 +88,161 @@ void __retl_efault(void); * of a performance impact. Thus we have a few rather ugly macros here, * and hide all the ugliness from the user. */ -#define put_user(x,ptr) ({ \ -unsigned long __pu_addr = (unsigned long)(ptr); \ -__chk_user_ptr(ptr); \ -__put_user_nocheck((__typeof__(*(ptr)))(x),__pu_addr,sizeof(*(ptr))); }) +#define put_user(x, ptr) ({ \ + unsigned long __pu_addr = (unsigned long)(ptr); \ + __chk_user_ptr(ptr); \ + __put_user_nocheck((__typeof__(*(ptr)))(x), __pu_addr, sizeof(*(ptr)));\ +}) -#define get_user(x,ptr) ({ \ -unsigned long __gu_addr = (unsigned long)(ptr); \ -__chk_user_ptr(ptr); \ -__get_user_nocheck((x),__gu_addr,sizeof(*(ptr)),__typeof__(*(ptr))); }) +#define get_user(x, ptr) ({ \ + unsigned long __gu_addr = (unsigned long)(ptr); \ + __chk_user_ptr(ptr); \ + __get_user_nocheck((x), __gu_addr, sizeof(*(ptr)), __typeof__(*(ptr)));\ +}) -#define __put_user(x,ptr) put_user(x,ptr) -#define __get_user(x,ptr) get_user(x,ptr) +#define __put_user(x, ptr) put_user(x, ptr) +#define __get_user(x, ptr) get_user(x, ptr) struct __large_struct { unsigned long buf[100]; }; #define __m(x) ((struct __large_struct *)(x)) -#define __put_user_nocheck(data,addr,size) ({ \ -register int __pu_ret; \ -switch (size) { \ -case 1: __put_user_asm(data,b,addr,__pu_ret); break; \ -case 2: __put_user_asm(data,h,addr,__pu_ret); break; \ -case 4: __put_user_asm(data,w,addr,__pu_ret); break; \ -case 8: __put_user_asm(data,x,addr,__pu_ret); break; \ -default: __pu_ret = __put_user_bad(); break; \ -} __pu_ret; }) - -#define __put_user_asm(x,size,addr,ret) \ +#define __put_user_nocheck(data, addr, size) ({ \ + register int __pu_ret; \ + switch (size) { \ + case 1: \ + __put_user_asm(data, b, addr, __pu_ret); \ + break; \ + case 2: \ + __put_user_asm(data, h, addr, __pu_ret); \ + break; \ + case 4: \ + __put_user_asm(data, w, addr, __pu_ret); \ + break; \ + case 8: \ + __put_user_asm(data, x, addr, __pu_ret); \ + break; \ + default: \ + __pu_ret = __put_user_bad(); \ + break; \ + } \ + __pu_ret; \ +}) + +#define __put_user_asm(x, size, addr, ret) \ __asm__ __volatile__( \ - "/* Put user asm, inline. */\n" \ -"1:\t" "st"#size "a %1, [%2] %%asi\n\t" \ - "clr %0\n" \ -"2:\n\n\t" \ - ".section .fixup,#alloc,#execinstr\n\t" \ - ".align 4\n" \ -"3:\n\t" \ - "sethi %%hi(2b), %0\n\t" \ - "jmpl %0 + %%lo(2b), %%g0\n\t" \ - " mov %3, %0\n\n\t" \ - ".previous\n\t" \ - ".section __ex_table,\"a\"\n\t" \ - ".align 4\n\t" \ - ".word 1b, 3b\n\t" \ - ".previous\n\n\t" \ - : "=r" (ret) : "r" (x), "r" (__m(addr)), \ - "i" (-EFAULT)) + "/* Put user asm, inline. */\n" \ + "1:\t" "st"#size "a %1, [%2] %%asi\n\t" \ + "clr %0\n" \ + "2:\n\n\t" \ + ".section .fixup,#alloc,#execinstr\n\t" \ + ".align 4\n" \ + "3:\n\t" \ + "sethi %%hi(2b), %0\n\t" \ + "jmpl %0 + %%lo(2b), %%g0\n\t" \ + " mov %3, %0\n\n\t" \ + ".previous\n\t" \ + ".section __ex_table,\"a\"\n\t" \ + ".align 4\n\t" \ + ".word 1b, 3b\n\t" \ + ".previous\n\n\t" \ + : "=r" (ret) : "r" (x), "r" (__m(addr)), \ + "i" (-EFAULT)) int __put_user_bad(void); -#define __get_user_nocheck(data,addr,size,type) ({ \ -register int __gu_ret; \ -register unsigned long __gu_val; \ -switch (size) { \ -case 1: __get_user_asm(__gu_val,ub,addr,__gu_ret); break; \ -case 2: __get_user_asm(__gu_val,uh,addr,__gu_ret); break; \ -case 4: __get_user_asm(__gu_val,uw,addr,__gu_ret); break; \ -case 8: __get_user_asm(__gu_val,x,addr,__gu_ret); break; \ -default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \ -} data = (__force type) __gu_val; __gu_ret; }) - -#define __get_user_nocheck_ret(data,addr,size,type,retval) ({ \ -register unsigned long __gu_val __asm__ ("l1"); \ -switch (size) { \ -case 1: __get_user_asm_ret(__gu_val,ub,addr,retval); break; \ -case 2: __get_user_asm_ret(__gu_val,uh,addr,retval); break; \ -case 4: __get_user_asm_ret(__gu_val,uw,addr,retval); break; \ -case 8: __get_user_asm_ret(__gu_val,x,addr,retval); break; \ -default: if (__get_user_bad()) return retval; \ -} data = (__force type) __gu_val; }) - -#define __get_user_asm(x,size,addr,ret) \ +#define __get_user_nocheck(data, addr, size, type) ({ \ + register int __gu_ret; \ + register unsigned long __gu_val; \ + switch (size) { \ + case 1: \ + __get_user_asm(__gu_val, ub, addr, __gu_ret); \ + break; \ + case 2: \ + __get_user_asm(__gu_val, uh, addr, __gu_ret); \ + break; \ + case 4: \ + __get_user_asm(__gu_val, uw, addr, __gu_ret); \ + break; \ + case 8: \ + __get_user_asm(__gu_val, x, addr, __gu_ret); \ + break; \ + default: \ + __gu_val = 0; \ + __gu_ret = __get_user_bad(); \ + break; \ + } \ + data = (__force type) __gu_val; \ + __gu_ret; \ +}) + +#define __get_user_nocheck_ret(data, addr, size, type, retval) ({ \ + register unsigned long __gu_val __asm__ ("l1"); \ + switch (size) { \ + case 1: \ + __get_user_asm_ret(__gu_val, ub, addr, retval); \ + break; \ + case 2: \ + __get_user_asm_ret(__gu_val, uh, addr, retval); \ + break; \ + case 4: \ + __get_user_asm_ret(__gu_val, uw, addr, retval); \ + break; \ + case 8: \ + __get_user_asm_ret(__gu_val, x, addr, retval); \ + break; \ + default: \ + if (__get_user_bad()) \ + return retval; \ + } \ + data = (__force type) __gu_val; \ +}) + +#define __get_user_asm(x, size, addr, ret) \ __asm__ __volatile__( \ - "/* Get user asm, inline. */\n" \ -"1:\t" "ld"#size "a [%2] %%asi, %1\n\t" \ - "clr %0\n" \ -"2:\n\n\t" \ - ".section .fixup,#alloc,#execinstr\n\t" \ - ".align 4\n" \ -"3:\n\t" \ - "sethi %%hi(2b), %0\n\t" \ - "clr %1\n\t" \ - "jmpl %0 + %%lo(2b), %%g0\n\t" \ - " mov %3, %0\n\n\t" \ - ".previous\n\t" \ - ".section __ex_table,\"a\"\n\t" \ - ".align 4\n\t" \ - ".word 1b, 3b\n\n\t" \ - ".previous\n\t" \ - : "=r" (ret), "=r" (x) : "r" (__m(addr)), \ - "i" (-EFAULT)) - -#define __get_user_asm_ret(x,size,addr,retval) \ + "/* Get user asm, inline. */\n" \ + "1:\t" "ld"#size "a [%2] %%asi, %1\n\t" \ + "clr %0\n" \ + "2:\n\n\t" \ + ".section .fixup,#alloc,#execinstr\n\t" \ + ".align 4\n" \ + "3:\n\t" \ + "sethi %%hi(2b), %0\n\t" \ + "clr %1\n\t" \ + "jmpl %0 + %%lo(2b), %%g0\n\t" \ + " mov %3, %0\n\n\t" \ + ".previous\n\t" \ + ".section __ex_table,\"a\"\n\t" \ + ".align 4\n\t" \ + ".word 1b, 3b\n\n\t" \ + ".previous\n\t" \ + : "=r" (ret), "=r" (x) : "r" (__m(addr)), \ + "i" (-EFAULT)) + +#define __get_user_asm_ret(x, size, addr, retval) \ if (__builtin_constant_p(retval) && retval == -EFAULT) \ -__asm__ __volatile__( \ - "/* Get user asm ret, inline. */\n" \ -"1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \ - ".section __ex_table,\"a\"\n\t" \ - ".align 4\n\t" \ - ".word 1b,__ret_efault\n\n\t" \ - ".previous\n\t" \ - : "=r" (x) : "r" (__m(addr))); \ + __asm__ __volatile__( \ + "/* Get user asm ret, inline. */\n" \ + "1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \ + ".section __ex_table,\"a\"\n\t" \ + ".align 4\n\t" \ + ".word 1b,__ret_efault\n\n\t" \ + ".previous\n\t" \ + : "=r" (x) : "r" (__m(addr))); \ else \ -__asm__ __volatile__( \ - "/* Get user asm ret, inline. */\n" \ -"1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \ - ".section .fixup,#alloc,#execinstr\n\t" \ - ".align 4\n" \ -"3:\n\t" \ - "ret\n\t" \ - " restore %%g0, %2, %%o0\n\n\t" \ - ".previous\n\t" \ - ".section __ex_table,\"a\"\n\t" \ - ".align 4\n\t" \ - ".word 1b, 3b\n\n\t" \ - ".previous\n\t" \ - : "=r" (x) : "r" (__m(addr)), "i" (retval)) + __asm__ __volatile__( \ + "/* Get user asm ret, inline. */\n" \ + "1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \ + ".section .fixup,#alloc,#execinstr\n\t" \ + ".align 4\n" \ + "3:\n\t" \ + "ret\n\t" \ + " restore %%g0, %2, %%o0\n\n\t" \ + ".previous\n\t" \ + ".section __ex_table,\"a\"\n\t" \ + ".align 4\n\t" \ + ".word 1b, 3b\n\n\t" \ + ".previous\n\t" \ + : "=r" (x) : "r" (__m(addr)), "i" (retval)) int __get_user_bad(void); From 323ae3c5dd941c8257912941eb3da93aaf8d65dd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 125/360] blackfin: macro whitespace fixes While working on arch/blackfin/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/blackfin/include/asm/uaccess.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h index 4bf968c01cf92c..90612a7f2cf32f 100644 --- a/arch/blackfin/include/asm/uaccess.h +++ b/arch/blackfin/include/asm/uaccess.h @@ -27,7 +27,7 @@ static inline void set_fs(mm_segment_t fs) current_thread_info()->addr_limit = fs; } -#define segment_eq(a,b) ((a) == (b)) +#define segment_eq(a, b) ((a) == (b)) #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -68,11 +68,11 @@ struct exception_table_entry { * use the right size if we just have the right pointer type. */ -#define put_user(x,p) \ +#define put_user(x, p) \ ({ \ int _err = 0; \ typeof(*(p)) _x = (x); \ - typeof(*(p)) __user *_p = (p); \ + typeof(*(p)) __user *_p = (p); \ if (!access_ok(VERIFY_WRITE, _p, sizeof(*(_p)))) {\ _err = -EFAULT; \ } \ @@ -102,7 +102,7 @@ struct exception_table_entry { _err; \ }) -#define __put_user(x,p) put_user(x,p) +#define __put_user(x, p) put_user(x, p) static inline int bad_user_access_length(void) { panic("bad_user_access_length"); @@ -121,10 +121,10 @@ static inline int bad_user_access_length(void) #define __ptr(x) ((unsigned long __force *)(x)) -#define __put_user_asm(x,p,bhw) \ +#define __put_user_asm(x, p, bhw) \ __asm__ (#bhw"[%1] = %0;\n\t" \ : /* no outputs */ \ - :"d" (x),"a" (__ptr(p)) : "memory") + :"d" (x), "a" (__ptr(p)) : "memory") #define get_user(x, ptr) \ ({ \ @@ -136,10 +136,10 @@ static inline int bad_user_access_length(void) BUILD_BUG_ON(ptr_size >= 8); \ switch (ptr_size) { \ case 1: \ - __get_user_asm(_val, _p, B,(Z)); \ + __get_user_asm(_val, _p, B, (Z)); \ break; \ case 2: \ - __get_user_asm(_val, _p, W,(Z)); \ + __get_user_asm(_val, _p, W, (Z)); \ break; \ case 4: \ __get_user_asm(_val, _p, , ); \ @@ -151,7 +151,7 @@ static inline int bad_user_access_length(void) _err; \ }) -#define __get_user(x,p) get_user(x,p) +#define __get_user(x, p) get_user(x, p) #define __get_user_bad() (bad_user_access_length(), (-EFAULT)) @@ -168,10 +168,10 @@ static inline int bad_user_access_length(void) #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user -#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n))\ +#define copy_to_user_ret(to, from, n, retval) ({ if (copy_to_user(to, from, n))\ return retval; }) -#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n))\ +#define copy_from_user_ret(to, from, n, retval) ({ if (copy_from_user(to, from, n))\ return retval; }) static inline unsigned long __must_check From 4fb9bccbfbb2b95685671cc65f7ecd64e30384ff Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 126/360] alpha: macro whitespace fixes While working on arch/alpha/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/alpha/include/asm/uaccess.h | 82 ++++++++++++++++---------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/arch/alpha/include/asm/uaccess.h b/arch/alpha/include/asm/uaccess.h index a234de79157a4a..9b0d40093c9a37 100644 --- a/arch/alpha/include/asm/uaccess.h +++ b/arch/alpha/include/asm/uaccess.h @@ -27,7 +27,7 @@ #define get_ds() (KERNEL_DS) #define set_fs(x) (current_thread_info()->addr_limit = (x)) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) /* * Is a address valid? This does a straightforward calculation rather @@ -39,13 +39,13 @@ * - AND "addr+size" doesn't have any high-bits set * - OR we are in kernel mode. */ -#define __access_ok(addr,size,segment) \ +#define __access_ok(addr, size, segment) \ (((segment).seg & (addr | size | (addr+size))) == 0) -#define access_ok(type,addr,size) \ +#define access_ok(type, addr, size) \ ({ \ __chk_user_ptr(addr); \ - __access_ok(((unsigned long)(addr)),(size),get_fs()); \ + __access_ok(((unsigned long)(addr)), (size), get_fs()); \ }) /* @@ -60,20 +60,20 @@ * (a) re-use the arguments for side effects (sizeof/typeof is ok) * (b) require any knowledge of processes at this stage */ -#define put_user(x,ptr) \ - __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)),get_fs()) -#define get_user(x,ptr) \ - __get_user_check((x),(ptr),sizeof(*(ptr)),get_fs()) +#define put_user(x, ptr) \ + __put_user_check((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)), get_fs()) +#define get_user(x, ptr) \ + __get_user_check((x), (ptr), sizeof(*(ptr)), get_fs()) /* * The "__xxx" versions do not do address space checking, useful when * doing multiple accesses to the same area (the programmer has to do the * checks by hand with "access_ok()") */ -#define __put_user(x,ptr) \ - __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) -#define __get_user(x,ptr) \ - __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x, ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) +#define __get_user(x, ptr) \ + __get_user_nocheck((x), (ptr), sizeof(*(ptr))) /* * The "lda %1, 2b-1b(%0)" bits are magic to get the assembler to @@ -84,7 +84,7 @@ extern void __get_user_unknown(void); -#define __get_user_nocheck(x,ptr,size) \ +#define __get_user_nocheck(x, ptr, size) \ ({ \ long __gu_err = 0; \ unsigned long __gu_val; \ @@ -100,12 +100,12 @@ extern void __get_user_unknown(void); __gu_err; \ }) -#define __get_user_check(x,ptr,size,segment) \ +#define __get_user_check(x, ptr, size, segment) \ ({ \ long __gu_err = -EFAULT; \ unsigned long __gu_val = 0; \ const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ - if (__access_ok((unsigned long)__gu_addr,size,segment)) { \ + if (__access_ok((unsigned long)__gu_addr, size, segment)) { \ __gu_err = 0; \ switch (size) { \ case 1: __get_user_8(__gu_addr); break; \ @@ -201,31 +201,31 @@ struct __large_struct { unsigned long buf[100]; }; extern void __put_user_unknown(void); -#define __put_user_nocheck(x,ptr,size) \ +#define __put_user_nocheck(x, ptr, size) \ ({ \ long __pu_err = 0; \ __chk_user_ptr(ptr); \ switch (size) { \ - case 1: __put_user_8(x,ptr); break; \ - case 2: __put_user_16(x,ptr); break; \ - case 4: __put_user_32(x,ptr); break; \ - case 8: __put_user_64(x,ptr); break; \ + case 1: __put_user_8(x, ptr); break; \ + case 2: __put_user_16(x, ptr); break; \ + case 4: __put_user_32(x, ptr); break; \ + case 8: __put_user_64(x, ptr); break; \ default: __put_user_unknown(); break; \ } \ __pu_err; \ }) -#define __put_user_check(x,ptr,size,segment) \ +#define __put_user_check(x, ptr, size, segment) \ ({ \ long __pu_err = -EFAULT; \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ - if (__access_ok((unsigned long)__pu_addr,size,segment)) { \ + if (__access_ok((unsigned long)__pu_addr, size, segment)) { \ __pu_err = 0; \ switch (size) { \ - case 1: __put_user_8(x,__pu_addr); break; \ - case 2: __put_user_16(x,__pu_addr); break; \ - case 4: __put_user_32(x,__pu_addr); break; \ - case 8: __put_user_64(x,__pu_addr); break; \ + case 1: __put_user_8(x, __pu_addr); break; \ + case 2: __put_user_16(x, __pu_addr); break; \ + case 4: __put_user_32(x, __pu_addr); break; \ + case 8: __put_user_64(x, __pu_addr); break; \ default: __put_user_unknown(); break; \ } \ } \ @@ -237,7 +237,7 @@ extern void __put_user_unknown(void); * instead of writing: this is because they do not write to * any memory gcc knows about, so there are no aliasing issues */ -#define __put_user_64(x,addr) \ +#define __put_user_64(x, addr) \ __asm__ __volatile__("1: stq %r2,%1\n" \ "2:\n" \ ".section __ex_table,\"a\"\n" \ @@ -247,7 +247,7 @@ __asm__ __volatile__("1: stq %r2,%1\n" \ : "=r"(__pu_err) \ : "m" (__m(addr)), "rJ" (x), "0"(__pu_err)) -#define __put_user_32(x,addr) \ +#define __put_user_32(x, addr) \ __asm__ __volatile__("1: stl %r2,%1\n" \ "2:\n" \ ".section __ex_table,\"a\"\n" \ @@ -260,7 +260,7 @@ __asm__ __volatile__("1: stl %r2,%1\n" \ #ifdef __alpha_bwx__ /* Those lucky bastards with ev56 and later CPUs can do byte/word moves. */ -#define __put_user_16(x,addr) \ +#define __put_user_16(x, addr) \ __asm__ __volatile__("1: stw %r2,%1\n" \ "2:\n" \ ".section __ex_table,\"a\"\n" \ @@ -270,7 +270,7 @@ __asm__ __volatile__("1: stw %r2,%1\n" \ : "=r"(__pu_err) \ : "m"(__m(addr)), "rJ"(x), "0"(__pu_err)) -#define __put_user_8(x,addr) \ +#define __put_user_8(x, addr) \ __asm__ __volatile__("1: stb %r2,%1\n" \ "2:\n" \ ".section __ex_table,\"a\"\n" \ @@ -283,7 +283,7 @@ __asm__ __volatile__("1: stb %r2,%1\n" \ /* Unfortunately, we can't get an unaligned access trap for the sub-word write, so we have to do a general unaligned operation. */ -#define __put_user_16(x,addr) \ +#define __put_user_16(x, addr) \ { \ long __pu_tmp1, __pu_tmp2, __pu_tmp3, __pu_tmp4; \ __asm__ __volatile__( \ @@ -308,13 +308,13 @@ __asm__ __volatile__("1: stb %r2,%1\n" \ " .long 4b - .\n" \ " lda $31, 5b-4b(%0)\n" \ ".previous" \ - : "=r"(__pu_err), "=&r"(__pu_tmp1), \ - "=&r"(__pu_tmp2), "=&r"(__pu_tmp3), \ + : "=r"(__pu_err), "=&r"(__pu_tmp1), \ + "=&r"(__pu_tmp2), "=&r"(__pu_tmp3), \ "=&r"(__pu_tmp4) \ : "r"(addr), "r"((unsigned long)(x)), "0"(__pu_err)); \ } -#define __put_user_8(x,addr) \ +#define __put_user_8(x, addr) \ { \ long __pu_tmp1, __pu_tmp2; \ __asm__ __volatile__( \ @@ -330,7 +330,7 @@ __asm__ __volatile__("1: stb %r2,%1\n" \ " .long 2b - .\n" \ " lda $31, 3b-2b(%0)\n" \ ".previous" \ - : "=r"(__pu_err), \ + : "=r"(__pu_err), \ "=&r"(__pu_tmp1), "=&r"(__pu_tmp2) \ : "r"((unsigned long)(x)), "r"(addr), "0"(__pu_err)); \ } @@ -366,7 +366,7 @@ __copy_tofrom_user_nocheck(void *to, const void *from, long len) : "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to) : __module_address(__copy_user) "0" (__cu_len), "1" (__cu_from), "2" (__cu_to) - : "$1","$2","$3","$4","$5","$28","memory"); + : "$1", "$2", "$3", "$4", "$5", "$28", "memory"); return __cu_len; } @@ -379,15 +379,15 @@ __copy_tofrom_user(void *to, const void *from, long len, const void __user *vali return len; } -#define __copy_to_user(to,from,n) \ +#define __copy_to_user(to, from, n) \ ({ \ __chk_user_ptr(to); \ - __copy_tofrom_user_nocheck((__force void *)(to),(from),(n)); \ + __copy_tofrom_user_nocheck((__force void *)(to), (from), (n)); \ }) -#define __copy_from_user(to,from,n) \ +#define __copy_from_user(to, from, n) \ ({ \ __chk_user_ptr(from); \ - __copy_tofrom_user_nocheck((to),(__force void *)(from),(n)); \ + __copy_tofrom_user_nocheck((to), (__force void *)(from), (n)); \ }) #define __copy_to_user_inatomic __copy_to_user @@ -418,7 +418,7 @@ __clear_user(void __user *to, long len) : "=r"(__cl_len), "=r"(__cl_to) : __module_address(__do_clear_user) "0"(__cl_len), "1"(__cl_to) - : "$1","$2","$3","$4","$5","$28","memory"); + : "$1", "$2", "$3", "$4", "$5", "$28", "memory"); return __cl_len; } From 295bb01e2666e450be7f6e6e1f5785bfb26fd60e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 127/360] arm: macro whitespace fixes While working on arch/arm/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/arm/include/asm/uaccess.h | 92 +++++++++++++++++----------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index 74fcde756fdb73..ce0786efd26c34 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -73,7 +73,7 @@ static inline void set_fs(mm_segment_t fs) modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER); } -#define segment_eq(a,b) ((a) == (b)) +#define segment_eq(a, b) ((a) == (b)) #define __addr_ok(addr) ({ \ unsigned long flag; \ @@ -84,7 +84,7 @@ static inline void set_fs(mm_segment_t fs) (flag == 0); }) /* We use 33-bit arithmetic here... */ -#define __range_ok(addr,size) ({ \ +#define __range_ok(addr, size) ({ \ unsigned long flag, roksum; \ __chk_user_ptr(addr); \ __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \ @@ -123,7 +123,7 @@ extern int __get_user_64t_4(void *); #define __GUP_CLOBBER_32t_8 "lr", "cc" #define __GUP_CLOBBER_8 "lr", "cc" -#define __get_user_x(__r2,__p,__e,__l,__s) \ +#define __get_user_x(__r2, __p, __e, __l, __s) \ __asm__ __volatile__ ( \ __asmeq("%0", "r0") __asmeq("%1", "r2") \ __asmeq("%3", "r1") \ @@ -134,7 +134,7 @@ extern int __get_user_64t_4(void *); /* narrowing a double-word get into a single 32bit word register: */ #ifdef __ARMEB__ -#define __get_user_x_32t(__r2, __p, __e, __l, __s) \ +#define __get_user_x_32t(__r2, __p, __e, __l, __s) \ __get_user_x(__r2, __p, __e, __l, 32t_8) #else #define __get_user_x_32t __get_user_x @@ -158,7 +158,7 @@ extern int __get_user_64t_4(void *); #endif -#define __get_user_check(x,p) \ +#define __get_user_check(x, p) \ ({ \ unsigned long __limit = current_thread_info()->addr_limit - 1; \ register const typeof(*(p)) __user *__p asm("r0") = (p);\ @@ -196,10 +196,10 @@ extern int __get_user_64t_4(void *); __e; \ }) -#define get_user(x,p) \ +#define get_user(x, p) \ ({ \ might_fault(); \ - __get_user_check(x,p); \ + __get_user_check(x, p); \ }) extern int __put_user_1(void *, unsigned int); @@ -207,7 +207,7 @@ extern int __put_user_2(void *, unsigned int); extern int __put_user_4(void *, unsigned int); extern int __put_user_8(void *, unsigned long long); -#define __put_user_x(__r2,__p,__e,__l,__s) \ +#define __put_user_x(__r2, __p, __e, __l, __s) \ __asm__ __volatile__ ( \ __asmeq("%0", "r0") __asmeq("%2", "r2") \ __asmeq("%3", "r1") \ @@ -216,7 +216,7 @@ extern int __put_user_8(void *, unsigned long long); : "0" (__p), "r" (__r2), "r" (__l) \ : "ip", "lr", "cc") -#define __put_user_check(x,p) \ +#define __put_user_check(x, p) \ ({ \ unsigned long __limit = current_thread_info()->addr_limit - 1; \ const typeof(*(p)) __user *__tmp_p = (p); \ @@ -242,10 +242,10 @@ extern int __put_user_8(void *, unsigned long long); __e; \ }) -#define put_user(x,p) \ +#define put_user(x, p) \ ({ \ might_fault(); \ - __put_user_check(x,p); \ + __put_user_check(x, p); \ }) #else /* CONFIG_MMU */ @@ -255,21 +255,21 @@ extern int __put_user_8(void *, unsigned long long); */ #define USER_DS KERNEL_DS -#define segment_eq(a,b) (1) -#define __addr_ok(addr) ((void)(addr),1) -#define __range_ok(addr,size) ((void)(addr),0) +#define segment_eq(a, b) (1) +#define __addr_ok(addr) ((void)(addr), 1) +#define __range_ok(addr, size) ((void)(addr), 0) #define get_fs() (KERNEL_DS) static inline void set_fs(mm_segment_t fs) { } -#define get_user(x,p) __get_user(x,p) -#define put_user(x,p) __put_user(x,p) +#define get_user(x, p) __get_user(x, p) +#define put_user(x, p) __put_user(x, p) #endif /* CONFIG_MMU */ -#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) +#define access_ok(type, addr, size) (__range_ok(addr, size) == 0) #define user_addr_max() \ (segment_eq(get_fs(), KERNEL_DS) ? ~0UL : get_fs()) @@ -283,35 +283,35 @@ static inline void set_fs(mm_segment_t fs) * error occurs, and leave it unchanged on success. Note that these * versions are void (ie, don't return a value as such). */ -#define __get_user(x,ptr) \ +#define __get_user(x, ptr) \ ({ \ long __gu_err = 0; \ - __get_user_err((x),(ptr),__gu_err); \ + __get_user_err((x), (ptr), __gu_err); \ __gu_err; \ }) -#define __get_user_error(x,ptr,err) \ +#define __get_user_error(x, ptr, err) \ ({ \ - __get_user_err((x),(ptr),err); \ + __get_user_err((x), (ptr), err); \ (void) 0; \ }) -#define __get_user_err(x,ptr,err) \ +#define __get_user_err(x, ptr, err) \ do { \ unsigned long __gu_addr = (unsigned long)(ptr); \ unsigned long __gu_val; \ __chk_user_ptr(ptr); \ might_fault(); \ switch (sizeof(*(ptr))) { \ - case 1: __get_user_asm_byte(__gu_val,__gu_addr,err); break; \ - case 2: __get_user_asm_half(__gu_val,__gu_addr,err); break; \ - case 4: __get_user_asm_word(__gu_val,__gu_addr,err); break; \ + case 1: __get_user_asm_byte(__gu_val, __gu_addr, err); break; \ + case 2: __get_user_asm_half(__gu_val, __gu_addr, err); break; \ + case 4: __get_user_asm_word(__gu_val, __gu_addr, err); break; \ default: (__gu_val) = __get_user_bad(); \ } \ (x) = (__typeof__(*(ptr)))__gu_val; \ } while (0) -#define __get_user_asm_byte(x,addr,err) \ +#define __get_user_asm_byte(x, addr, err) \ __asm__ __volatile__( \ "1: " TUSER(ldrb) " %1,[%2],#0\n" \ "2:\n" \ @@ -330,7 +330,7 @@ do { \ : "cc") #ifndef __ARMEB__ -#define __get_user_asm_half(x,__gu_addr,err) \ +#define __get_user_asm_half(x, __gu_addr, err) \ ({ \ unsigned long __b1, __b2; \ __get_user_asm_byte(__b1, __gu_addr, err); \ @@ -338,7 +338,7 @@ do { \ (x) = __b1 | (__b2 << 8); \ }) #else -#define __get_user_asm_half(x,__gu_addr,err) \ +#define __get_user_asm_half(x, __gu_addr, err) \ ({ \ unsigned long __b1, __b2; \ __get_user_asm_byte(__b1, __gu_addr, err); \ @@ -347,7 +347,7 @@ do { \ }) #endif -#define __get_user_asm_word(x,addr,err) \ +#define __get_user_asm_word(x, addr, err) \ __asm__ __volatile__( \ "1: " TUSER(ldr) " %1,[%2],#0\n" \ "2:\n" \ @@ -365,35 +365,35 @@ do { \ : "r" (addr), "i" (-EFAULT) \ : "cc") -#define __put_user(x,ptr) \ +#define __put_user(x, ptr) \ ({ \ long __pu_err = 0; \ - __put_user_err((x),(ptr),__pu_err); \ + __put_user_err((x), (ptr), __pu_err); \ __pu_err; \ }) -#define __put_user_error(x,ptr,err) \ +#define __put_user_error(x, ptr, err) \ ({ \ - __put_user_err((x),(ptr),err); \ + __put_user_err((x), (ptr), err); \ (void) 0; \ }) -#define __put_user_err(x,ptr,err) \ +#define __put_user_err(x, ptr, err) \ do { \ unsigned long __pu_addr = (unsigned long)(ptr); \ __typeof__(*(ptr)) __pu_val = (x); \ __chk_user_ptr(ptr); \ might_fault(); \ switch (sizeof(*(ptr))) { \ - case 1: __put_user_asm_byte(__pu_val,__pu_addr,err); break; \ - case 2: __put_user_asm_half(__pu_val,__pu_addr,err); break; \ - case 4: __put_user_asm_word(__pu_val,__pu_addr,err); break; \ - case 8: __put_user_asm_dword(__pu_val,__pu_addr,err); break; \ + case 1: __put_user_asm_byte(__pu_val, __pu_addr, err); break; \ + case 2: __put_user_asm_half(__pu_val, __pu_addr, err); break; \ + case 4: __put_user_asm_word(__pu_val, __pu_addr, err); break; \ + case 8: __put_user_asm_dword(__pu_val, __pu_addr, err); break; \ default: __put_user_bad(); \ } \ } while (0) -#define __put_user_asm_byte(x,__pu_addr,err) \ +#define __put_user_asm_byte(x, __pu_addr, err) \ __asm__ __volatile__( \ "1: " TUSER(strb) " %1,[%2],#0\n" \ "2:\n" \ @@ -411,14 +411,14 @@ do { \ : "cc") #ifndef __ARMEB__ -#define __put_user_asm_half(x,__pu_addr,err) \ +#define __put_user_asm_half(x, __pu_addr, err) \ ({ \ unsigned long __temp = (__force unsigned long)(x); \ __put_user_asm_byte(__temp, __pu_addr, err); \ __put_user_asm_byte(__temp >> 8, __pu_addr + 1, err); \ }) #else -#define __put_user_asm_half(x,__pu_addr,err) \ +#define __put_user_asm_half(x, __pu_addr, err) \ ({ \ unsigned long __temp = (__force unsigned long)(x); \ __put_user_asm_byte(__temp >> 8, __pu_addr, err); \ @@ -426,7 +426,7 @@ do { \ }) #endif -#define __put_user_asm_word(x,__pu_addr,err) \ +#define __put_user_asm_word(x, __pu_addr, err) \ __asm__ __volatile__( \ "1: " TUSER(str) " %1,[%2],#0\n" \ "2:\n" \ @@ -451,7 +451,7 @@ do { \ #define __reg_oper1 "%R2" #endif -#define __put_user_asm_dword(x,__pu_addr,err) \ +#define __put_user_asm_dword(x, __pu_addr, err) \ __asm__ __volatile__( \ ARM( "1: " TUSER(str) " " __reg_oper1 ", [%1], #4\n" ) \ ARM( "2: " TUSER(str) " " __reg_oper0 ", [%1]\n" ) \ @@ -480,9 +480,9 @@ extern unsigned long __must_check __copy_to_user_std(void __user *to, const void extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n); extern unsigned long __must_check __clear_user_std(void __user *addr, unsigned long n); #else -#define __copy_from_user(to,from,n) (memcpy(to, (void __force *)from, n), 0) -#define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0) -#define __clear_user(addr,n) (memset((void __force *)addr, 0, n), 0) +#define __copy_from_user(to, from, n) (memcpy(to, (void __force *)from, n), 0) +#define __copy_to_user(to, from, n) (memcpy((void __force *)to, from, n), 0) +#define __clear_user(addr, n) (memset((void __force *)addr, 0, n), 0) #endif static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) From 967f0e5d67518f274e397b4fa703c73cac24fe18 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 128/360] arm64: macro whitespace fixes While working on arch/arm64/include/asm/uaccess.h, I noticed that one macro within this header is made harder to read because it violates a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin Acked-by: Will Deacon --- arch/arm64/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 9a2069bbc00d77..07e1ba449bf1e0 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -63,7 +63,7 @@ static inline void set_fs(mm_segment_t fs) current_thread_info()->addr_limit = fs; } -#define segment_eq(a,b) ((a) == (b)) +#define segment_eq(a, b) ((a) == (b)) /* * Return 1 if addr < current->addr_limit, 0 otherwise. From d13beabea13b528d9d9d9c95a80db6bd7b99402c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 129/360] avr32: macro whitespace fixes While working on arch/avr32/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin Acked-by: Hans-Christian Egtvedt --- arch/avr32/include/asm/uaccess.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/avr32/include/asm/uaccess.h b/arch/avr32/include/asm/uaccess.h index 72607f32eebbab..a46f7cf3e1eab2 100644 --- a/arch/avr32/include/asm/uaccess.h +++ b/arch/avr32/include/asm/uaccess.h @@ -26,7 +26,7 @@ typedef struct { * For historical reasons (Data Segment Register?), these macros are misnamed. */ #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) -#define segment_eq(a,b) ((a).is_user_space == (b).is_user_space) +#define segment_eq(a, b) ((a).is_user_space == (b).is_user_space) #define USER_ADDR_LIMIT 0x80000000 @@ -108,8 +108,8 @@ static inline __kernel_size_t __copy_from_user(void *to, * * Returns zero on success, or -EFAULT on error. */ -#define put_user(x,ptr) \ - __put_user_check((x),(ptr),sizeof(*(ptr))) +#define put_user(x, ptr) \ + __put_user_check((x), (ptr), sizeof(*(ptr))) /* * get_user: - Get a simple variable from user space. @@ -128,8 +128,8 @@ static inline __kernel_size_t __copy_from_user(void *to, * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. */ -#define get_user(x,ptr) \ - __get_user_check((x),(ptr),sizeof(*(ptr))) +#define get_user(x, ptr) \ + __get_user_check((x), (ptr), sizeof(*(ptr))) /* * __put_user: - Write a simple value into user space, with less checking. @@ -150,8 +150,8 @@ static inline __kernel_size_t __copy_from_user(void *to, * * Returns zero on success, or -EFAULT on error. */ -#define __put_user(x,ptr) \ - __put_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x, ptr) \ + __put_user_nocheck((x), (ptr), sizeof(*(ptr))) /* * __get_user: - Get a simple variable from user space, with less checking. @@ -173,8 +173,8 @@ static inline __kernel_size_t __copy_from_user(void *to, * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. */ -#define __get_user(x,ptr) \ - __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __get_user(x, ptr) \ + __get_user_nocheck((x), (ptr), sizeof(*(ptr))) extern int __get_user_bad(void); extern int __put_user_bad(void); From 00eaf4c0c2fdb2121216fe2323fd606b109649ab Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 130/360] cris: macro whitespace fixes While working on arch/cris/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/cris/include/asm/uaccess.h | 113 ++++++++++++++++---------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/arch/cris/include/asm/uaccess.h b/arch/cris/include/asm/uaccess.h index 9cf5a23baed3fb..a21344ab8616ed 100644 --- a/arch/cris/include/asm/uaccess.h +++ b/arch/cris/include/asm/uaccess.h @@ -47,12 +47,13 @@ #define get_fs() (current_thread_info()->addr_limit) #define set_fs(x) (current_thread_info()->addr_limit = (x)) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) -#define __user_ok(addr,size) (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) -#define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size))) -#define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size)) +#define __user_ok(addr, size) \ + (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) +#define __access_ok(addr, size) (__kernel_ok || __user_ok((addr), (size))) +#define access_ok(type, addr, size) __access_ok((unsigned long)(addr), (size)) #include @@ -92,56 +93,56 @@ struct exception_table_entry * CRIS, we can just do these as direct assignments. (Of course, the * exception handling means that it's no longer "just"...) */ -#define get_user(x,ptr) \ - __get_user_check((x),(ptr),sizeof(*(ptr))) -#define put_user(x,ptr) \ - __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define get_user(x, ptr) \ + __get_user_check((x), (ptr), sizeof(*(ptr))) +#define put_user(x, ptr) \ + __put_user_check((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) -#define __get_user(x,ptr) \ - __get_user_nocheck((x),(ptr),sizeof(*(ptr))) -#define __put_user(x,ptr) \ - __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define __get_user(x, ptr) \ + __get_user_nocheck((x), (ptr), sizeof(*(ptr))) +#define __put_user(x, ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) extern long __put_user_bad(void); -#define __put_user_size(x,ptr,size,retval) \ -do { \ - retval = 0; \ - switch (size) { \ - case 1: __put_user_asm(x,ptr,retval,"move.b"); break; \ - case 2: __put_user_asm(x,ptr,retval,"move.w"); break; \ - case 4: __put_user_asm(x,ptr,retval,"move.d"); break; \ - case 8: __put_user_asm_64(x,ptr,retval); break; \ - default: __put_user_bad(); \ - } \ +#define __put_user_size(x, ptr, size, retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __put_user_asm(x, ptr, retval, "move.b"); break; \ + case 2: __put_user_asm(x, ptr, retval, "move.w"); break; \ + case 4: __put_user_asm(x, ptr, retval, "move.d"); break; \ + case 8: __put_user_asm_64(x, ptr, retval); break; \ + default: __put_user_bad(); \ + } \ } while (0) -#define __get_user_size(x,ptr,size,retval) \ -do { \ - retval = 0; \ - switch (size) { \ - case 1: __get_user_asm(x,ptr,retval,"move.b"); break; \ - case 2: __get_user_asm(x,ptr,retval,"move.w"); break; \ - case 4: __get_user_asm(x,ptr,retval,"move.d"); break; \ - case 8: __get_user_asm_64(x,ptr,retval); break; \ - default: (x) = __get_user_bad(); \ - } \ +#define __get_user_size(x, ptr, size, retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __get_user_asm(x, ptr, retval, "move.b"); break; \ + case 2: __get_user_asm(x, ptr, retval, "move.w"); break; \ + case 4: __get_user_asm(x, ptr, retval, "move.d"); break; \ + case 8: __get_user_asm_64(x, ptr, retval); break; \ + default: (x) = __get_user_bad(); \ + } \ } while (0) -#define __put_user_nocheck(x,ptr,size) \ +#define __put_user_nocheck(x, ptr, size) \ ({ \ long __pu_err; \ - __put_user_size((x),(ptr),(size),__pu_err); \ + __put_user_size((x), (ptr), (size), __pu_err); \ __pu_err; \ }) -#define __put_user_check(x,ptr,size) \ -({ \ - long __pu_err = -EFAULT; \ - __typeof__(*(ptr)) *__pu_addr = (ptr); \ - if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ - __put_user_size((x),__pu_addr,(size),__pu_err); \ - __pu_err; \ +#define __put_user_check(x, ptr, size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE, __pu_addr, size)) \ + __put_user_size((x), __pu_addr, (size), __pu_err); \ + __pu_err; \ }) struct __large_struct { unsigned long buf[100]; }; @@ -149,20 +150,20 @@ struct __large_struct { unsigned long buf[100]; }; -#define __get_user_nocheck(x,ptr,size) \ +#define __get_user_nocheck(x, ptr, size) \ ({ \ long __gu_err, __gu_val; \ - __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + __get_user_size(__gu_val, (ptr), (size), __gu_err); \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) -#define __get_user_check(x,ptr,size) \ +#define __get_user_check(x, ptr, size) \ ({ \ long __gu_err = -EFAULT, __gu_val = 0; \ const __typeof__(*(ptr)) *__gu_addr = (ptr); \ - if (access_ok(VERIFY_READ,__gu_addr,size)) \ - __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ + if (access_ok(VERIFY_READ, __gu_addr, size)) \ + __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -180,7 +181,7 @@ static inline unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) - return __copy_user(to,from,n); + return __copy_user(to, from, n); return n; } @@ -188,7 +189,7 @@ static inline unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long n) { if (access_ok(VERIFY_READ, from, n)) - return __copy_user_zeroing(to,from,n); + return __copy_user_zeroing(to, from, n); return n; } @@ -196,7 +197,7 @@ static inline unsigned long __generic_clear_user(void __user *to, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) - return __do_clear_user(to,n); + return __do_clear_user(to, n); return n; } @@ -373,29 +374,31 @@ static inline unsigned long __generic_copy_from_user_nocheck(void *to, const void __user *from, unsigned long n) { - return __copy_user_zeroing(to,from,n); + return __copy_user_zeroing(to, from, n); } static inline unsigned long __generic_copy_to_user_nocheck(void __user *to, const void *from, unsigned long n) { - return __copy_user(to,from,n); + return __copy_user(to, from, n); } static inline unsigned long __generic_clear_user_nocheck(void __user *to, unsigned long n) { - return __do_clear_user(to,n); + return __do_clear_user(to, n); } /* without checking */ -#define __copy_to_user(to,from,n) __generic_copy_to_user_nocheck((to),(from),(n)) -#define __copy_from_user(to,from,n) __generic_copy_from_user_nocheck((to),(from),(n)) +#define __copy_to_user(to, from, n) \ + __generic_copy_to_user_nocheck((to), (from), (n)) +#define __copy_from_user(to, from, n) \ + __generic_copy_from_user_nocheck((to), (from), (n)) #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user -#define __clear_user(to,n) __generic_clear_user_nocheck((to),(n)) +#define __clear_user(to, n) __generic_clear_user_nocheck((to), (n)) #define strlen_user(str) strnlen_user((str), 0x7ffffffe) From fc0ca0ee509f54530a1a90d48519dedb55adbeae Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 131/360] frv: macro whitespace fixes While working on arch/frv/include/asm/uaccess.h, I noticed that one macro within this header is made harder to read because it violates a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/frv/include/asm/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/frv/include/asm/segment.h b/arch/frv/include/asm/segment.h index a2320a4a00424a..4377c89a57f5a6 100644 --- a/arch/frv/include/asm/segment.h +++ b/arch/frv/include/asm/segment.h @@ -31,7 +31,7 @@ typedef struct { #define get_ds() (KERNEL_DS) #define get_fs() (__current_thread_info->addr_limit) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define __kernel_ds_p() segment_eq(get_fs(), KERNEL_DS) #define get_addr_limit() (get_fs().seg) From 0ab066042dea23fe03d2eabc9dc5771dab4e81d4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 132/360] m32r: macro whitespace fixes While working on arch/m32r/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/m32r/include/asm/uaccess.h | 84 ++++++++++++++++----------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/arch/m32r/include/asm/uaccess.h b/arch/m32r/include/asm/uaccess.h index d076942a7b2b2c..71adff209405e1 100644 --- a/arch/m32r/include/asm/uaccess.h +++ b/arch/m32r/include/asm/uaccess.h @@ -54,7 +54,7 @@ static inline void set_fs(mm_segment_t s) #endif /* not CONFIG_MMU */ -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define __addr_ok(addr) \ ((unsigned long)(addr) < (current_thread_info()->addr_limit.seg)) @@ -68,7 +68,7 @@ static inline void set_fs(mm_segment_t s) * * This needs 33-bit arithmetic. We have a carry... */ -#define __range_ok(addr,size) ({ \ +#define __range_ok(addr, size) ({ \ unsigned long flag, roksum; \ __chk_user_ptr(addr); \ asm ( \ @@ -103,7 +103,7 @@ static inline void set_fs(mm_segment_t s) * this function, memory access functions may still return -EFAULT. */ #ifdef CONFIG_MMU -#define access_ok(type,addr,size) (likely(__range_ok(addr,size) == 0)) +#define access_ok(type, addr, size) (likely(__range_ok(addr, size) == 0)) #else static inline int access_ok(int type, const void *addr, unsigned long size) { @@ -167,8 +167,8 @@ extern int fixup_exception(struct pt_regs *regs); * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. */ -#define get_user(x,ptr) \ - __get_user_check((x),(ptr),sizeof(*(ptr))) +#define get_user(x, ptr) \ + __get_user_check((x), (ptr), sizeof(*(ptr))) /** * put_user: - Write a simple value into user space. @@ -186,8 +186,8 @@ extern int fixup_exception(struct pt_regs *regs); * * Returns zero on success, or -EFAULT on error. */ -#define put_user(x,ptr) \ - __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define put_user(x, ptr) \ + __put_user_check((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) /** * __get_user: - Get a simple variable from user space, with less checking. @@ -209,41 +209,41 @@ extern int fixup_exception(struct pt_regs *regs); * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. */ -#define __get_user(x,ptr) \ - __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __get_user(x, ptr) \ + __get_user_nocheck((x), (ptr), sizeof(*(ptr))) -#define __get_user_nocheck(x,ptr,size) \ +#define __get_user_nocheck(x, ptr, size) \ ({ \ long __gu_err = 0; \ unsigned long __gu_val; \ might_fault(); \ - __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + __get_user_size(__gu_val, (ptr), (size), __gu_err); \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) -#define __get_user_check(x,ptr,size) \ +#define __get_user_check(x, ptr, size) \ ({ \ long __gu_err = -EFAULT; \ unsigned long __gu_val = 0; \ const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ might_fault(); \ - if (access_ok(VERIFY_READ,__gu_addr,size)) \ - __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ + if (access_ok(VERIFY_READ, __gu_addr, size)) \ + __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) extern long __get_user_bad(void); -#define __get_user_size(x,ptr,size,retval) \ +#define __get_user_size(x, ptr, size, retval) \ do { \ retval = 0; \ __chk_user_ptr(ptr); \ switch (size) { \ - case 1: __get_user_asm(x,ptr,retval,"ub"); break; \ - case 2: __get_user_asm(x,ptr,retval,"uh"); break; \ - case 4: __get_user_asm(x,ptr,retval,""); break; \ + case 1: __get_user_asm(x, ptr, retval, "ub"); break; \ + case 2: __get_user_asm(x, ptr, retval, "uh"); break; \ + case 4: __get_user_asm(x, ptr, retval, ""); break; \ default: (x) = __get_user_bad(); \ } \ } while (0) @@ -288,26 +288,26 @@ do { \ * * Returns zero on success, or -EFAULT on error. */ -#define __put_user(x,ptr) \ - __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define __put_user(x, ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr))) -#define __put_user_nocheck(x,ptr,size) \ +#define __put_user_nocheck(x, ptr, size) \ ({ \ long __pu_err; \ might_fault(); \ - __put_user_size((x),(ptr),(size),__pu_err); \ + __put_user_size((x), (ptr), (size), __pu_err); \ __pu_err; \ }) -#define __put_user_check(x,ptr,size) \ +#define __put_user_check(x, ptr, size) \ ({ \ long __pu_err = -EFAULT; \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ might_fault(); \ - if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ - __put_user_size((x),__pu_addr,(size),__pu_err); \ + if (access_ok(VERIFY_WRITE, __pu_addr, size)) \ + __put_user_size((x), __pu_addr, (size), __pu_err); \ __pu_err; \ }) @@ -366,15 +366,15 @@ do { \ extern void __put_user_bad(void); -#define __put_user_size(x,ptr,size,retval) \ +#define __put_user_size(x, ptr, size, retval) \ do { \ retval = 0; \ __chk_user_ptr(ptr); \ switch (size) { \ - case 1: __put_user_asm(x,ptr,retval,"b"); break; \ - case 2: __put_user_asm(x,ptr,retval,"h"); break; \ - case 4: __put_user_asm(x,ptr,retval,""); break; \ - case 8: __put_user_u64((__typeof__(*ptr))(x),ptr,retval); break;\ + case 1: __put_user_asm(x, ptr, retval, "b"); break; \ + case 2: __put_user_asm(x, ptr, retval, "h"); break; \ + case 4: __put_user_asm(x, ptr, retval, ""); break; \ + case 8: __put_user_u64((__typeof__(*ptr))(x), ptr, retval); break;\ default: __put_user_bad(); \ } \ } while (0) @@ -421,7 +421,7 @@ struct __large_struct { unsigned long buf[100]; }; /* Generic arbitrary sized copy. */ /* Return the number of bytes NOT copied. */ -#define __copy_user(to,from,size) \ +#define __copy_user(to, from, size) \ do { \ unsigned long __dst, __src, __c; \ __asm__ __volatile__ ( \ @@ -478,7 +478,7 @@ do { \ : "r14", "memory"); \ } while (0) -#define __copy_user_zeroing(to,from,size) \ +#define __copy_user_zeroing(to, from, size) \ do { \ unsigned long __dst, __src, __c; \ __asm__ __volatile__ ( \ @@ -548,14 +548,14 @@ do { \ static inline unsigned long __generic_copy_from_user_nocheck(void *to, const void __user *from, unsigned long n) { - __copy_user_zeroing(to,from,n); + __copy_user_zeroing(to, from, n); return n; } static inline unsigned long __generic_copy_to_user_nocheck(void __user *to, const void *from, unsigned long n) { - __copy_user(to,from,n); + __copy_user(to, from, n); return n; } @@ -576,8 +576,8 @@ unsigned long __generic_copy_from_user(void *, const void __user *, unsigned lon * Returns number of bytes that could not be copied. * On success, this will be zero. */ -#define __copy_to_user(to,from,n) \ - __generic_copy_to_user_nocheck((to),(from),(n)) +#define __copy_to_user(to, from, n) \ + __generic_copy_to_user_nocheck((to), (from), (n)) #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user @@ -595,10 +595,10 @@ unsigned long __generic_copy_from_user(void *, const void __user *, unsigned lon * Returns number of bytes that could not be copied. * On success, this will be zero. */ -#define copy_to_user(to,from,n) \ +#define copy_to_user(to, from, n) \ ({ \ might_fault(); \ - __generic_copy_to_user((to),(from),(n)); \ + __generic_copy_to_user((to), (from), (n)); \ }) /** @@ -617,8 +617,8 @@ unsigned long __generic_copy_from_user(void *, const void __user *, unsigned lon * If some data could not be copied, this function will pad the copied * data to the requested size using zero bytes. */ -#define __copy_from_user(to,from,n) \ - __generic_copy_from_user_nocheck((to),(from),(n)) +#define __copy_from_user(to, from, n) \ + __generic_copy_from_user_nocheck((to), (from), (n)) /** * copy_from_user: - Copy a block of data from user space. @@ -636,10 +636,10 @@ unsigned long __generic_copy_from_user(void *, const void __user *, unsigned lon * If some data could not be copied, this function will pad the copied * data to the requested size using zero bytes. */ -#define copy_from_user(to,from,n) \ +#define copy_from_user(to, from, n) \ ({ \ might_fault(); \ - __generic_copy_from_user((to),(from),(n)); \ + __generic_copy_from_user((to), (from), (n)); \ }) long __must_check strncpy_from_user(char *dst, const char __user *src, From e9e6d91855debc35397f35f6070d6574b47a71f6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 133/360] m68k: macro whitespace fixes While working on arch/m68k/include/asm/uaccess.h, I noticed that one macro within this header is made harder to read because it violates a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin Acked-by: Geert Uytterhoeven --- arch/m68k/include/asm/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/m68k/include/asm/segment.h b/arch/m68k/include/asm/segment.h index 0fa80e97ed2de8..98216b8111f08b 100644 --- a/arch/m68k/include/asm/segment.h +++ b/arch/m68k/include/asm/segment.h @@ -58,7 +58,7 @@ static inline mm_segment_t get_ds(void) #define set_fs(x) (current_thread_info()->addr_limit = (x)) #endif -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #endif /* __ASSEMBLY__ */ From 0e8a2eb02fda7b0c55c3ff5144590a8a79f47e33 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 134/360] parisc: macro whitespace fixes While working on arch/parisc/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/parisc/include/asm/uaccess.h | 116 +++++++++++++++--------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 6c79311acdafe4..0abdd4c607ed9d 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -17,7 +17,7 @@ #define KERNEL_DS ((mm_segment_t){0}) #define USER_DS ((mm_segment_t){1}) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define get_ds() (KERNEL_DS) #define get_fs() (current_thread_info()->addr_limit) @@ -42,14 +42,14 @@ static inline long access_ok(int type, const void __user * addr, #if !defined(CONFIG_64BIT) #define LDD_KERNEL(ptr) BUILD_BUG() #define LDD_USER(ptr) BUILD_BUG() -#define STD_KERNEL(x, ptr) __put_kernel_asm64(x,ptr) -#define STD_USER(x, ptr) __put_user_asm64(x,ptr) +#define STD_KERNEL(x, ptr) __put_kernel_asm64(x, ptr) +#define STD_USER(x, ptr) __put_user_asm64(x, ptr) #define ASM_WORD_INSN ".word\t" #else -#define LDD_KERNEL(ptr) __get_kernel_asm("ldd",ptr) -#define LDD_USER(ptr) __get_user_asm("ldd",ptr) -#define STD_KERNEL(x, ptr) __put_kernel_asm("std",x,ptr) -#define STD_USER(x, ptr) __put_user_asm("std",x,ptr) +#define LDD_KERNEL(ptr) __get_kernel_asm("ldd", ptr) +#define LDD_USER(ptr) __get_user_asm("ldd", ptr) +#define STD_KERNEL(x, ptr) __put_kernel_asm("std", x, ptr) +#define STD_USER(x, ptr) __put_user_asm("std", x, ptr) #define ASM_WORD_INSN ".dword\t" #endif @@ -80,68 +80,68 @@ struct exception_data { unsigned long fault_addr; }; -#define __get_user(x,ptr) \ -({ \ - register long __gu_err __asm__ ("r8") = 0; \ - register long __gu_val __asm__ ("r9") = 0; \ - \ - if (segment_eq(get_fs(),KERNEL_DS)) { \ - switch (sizeof(*(ptr))) { \ - case 1: __get_kernel_asm("ldb",ptr); break; \ - case 2: __get_kernel_asm("ldh",ptr); break; \ - case 4: __get_kernel_asm("ldw",ptr); break; \ - case 8: LDD_KERNEL(ptr); break; \ - default: BUILD_BUG(); break; \ - } \ - } \ - else { \ - switch (sizeof(*(ptr))) { \ - case 1: __get_user_asm("ldb",ptr); break; \ - case 2: __get_user_asm("ldh",ptr); break; \ - case 4: __get_user_asm("ldw",ptr); break; \ - case 8: LDD_USER(ptr); break; \ - default: BUILD_BUG(); break; \ - } \ - } \ - \ - (x) = (__force __typeof__(*(ptr))) __gu_val; \ - __gu_err; \ +#define __get_user(x, ptr) \ +({ \ + register long __gu_err __asm__ ("r8") = 0; \ + register long __gu_val __asm__ ("r9") = 0; \ + \ + if (segment_eq(get_fs(), KERNEL_DS)) { \ + switch (sizeof(*(ptr))) { \ + case 1: __get_kernel_asm("ldb", ptr); break; \ + case 2: __get_kernel_asm("ldh", ptr); break; \ + case 4: __get_kernel_asm("ldw", ptr); break; \ + case 8: LDD_KERNEL(ptr); break; \ + default: BUILD_BUG(); break; \ + } \ + } \ + else { \ + switch (sizeof(*(ptr))) { \ + case 1: __get_user_asm("ldb", ptr); break; \ + case 2: __get_user_asm("ldh", ptr); break; \ + case 4: __get_user_asm("ldw", ptr); break; \ + case 8: LDD_USER(ptr); break; \ + default: BUILD_BUG(); break; \ + } \ + } \ + \ + (x) = (__force __typeof__(*(ptr))) __gu_val; \ + __gu_err; \ }) -#define __get_kernel_asm(ldx,ptr) \ +#define __get_kernel_asm(ldx, ptr) \ __asm__("\n1:\t" ldx "\t0(%2),%0\n\t" \ ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_1)\ : "=r"(__gu_val), "=r"(__gu_err) \ : "r"(ptr), "1"(__gu_err) \ : "r1"); -#define __get_user_asm(ldx,ptr) \ +#define __get_user_asm(ldx, ptr) \ __asm__("\n1:\t" ldx "\t0(%%sr3,%2),%0\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_get_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_1)\ : "=r"(__gu_val), "=r"(__gu_err) \ : "r"(ptr), "1"(__gu_err) \ : "r1"); -#define __put_user(x,ptr) \ +#define __put_user(x, ptr) \ ({ \ register long __pu_err __asm__ ("r8") = 0; \ __typeof__(*(ptr)) __x = (__typeof__(*(ptr)))(x); \ \ - if (segment_eq(get_fs(),KERNEL_DS)) { \ + if (segment_eq(get_fs(), KERNEL_DS)) { \ switch (sizeof(*(ptr))) { \ - case 1: __put_kernel_asm("stb",__x,ptr); break; \ - case 2: __put_kernel_asm("sth",__x,ptr); break; \ - case 4: __put_kernel_asm("stw",__x,ptr); break; \ - case 8: STD_KERNEL(__x,ptr); break; \ + case 1: __put_kernel_asm("stb", __x, ptr); break; \ + case 2: __put_kernel_asm("sth", __x, ptr); break; \ + case 4: __put_kernel_asm("stw", __x, ptr); break; \ + case 8: STD_KERNEL(__x, ptr); break; \ default: BUILD_BUG(); break; \ } \ } \ else { \ switch (sizeof(*(ptr))) { \ - case 1: __put_user_asm("stb",__x,ptr); break; \ - case 2: __put_user_asm("sth",__x,ptr); break; \ - case 4: __put_user_asm("stw",__x,ptr); break; \ - case 8: STD_USER(__x,ptr); break; \ + case 1: __put_user_asm("stb", __x, ptr); break; \ + case 2: __put_user_asm("sth", __x, ptr); break; \ + case 4: __put_user_asm("stw", __x, ptr); break; \ + case 8: STD_USER(__x, ptr); break; \ default: BUILD_BUG(); break; \ } \ } \ @@ -159,18 +159,18 @@ struct exception_data { * r8/r9 are already listed as err/val. */ -#define __put_kernel_asm(stx,x,ptr) \ +#define __put_kernel_asm(stx, x, ptr) \ __asm__ __volatile__ ( \ "\n1:\t" stx "\t%2,0(%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(x), "0"(__pu_err) \ : "r1") -#define __put_user_asm(stx,x,ptr) \ +#define __put_user_asm(stx, x, ptr) \ __asm__ __volatile__ ( \ "\n1:\t" stx "\t%2,0(%%sr3,%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(x), "0"(__pu_err) \ : "r1") @@ -178,23 +178,23 @@ struct exception_data { #if !defined(CONFIG_64BIT) -#define __put_kernel_asm64(__val,ptr) do { \ +#define __put_kernel_asm64(__val, ptr) do { \ __asm__ __volatile__ ( \ "\n1:\tstw %2,0(%1)" \ "\n2:\tstw %R2,4(%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_2)\ - ASM_EXCEPTIONTABLE_ENTRY(2b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_2)\ + ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(__val), "0"(__pu_err) \ : "r1"); \ } while (0) -#define __put_user_asm64(__val,ptr) do { \ +#define __put_user_asm64(__val, ptr) do { \ __asm__ __volatile__ ( \ "\n1:\tstw %2,0(%%sr3,%1)" \ "\n2:\tstw %R2,4(%%sr3,%1)\n\t" \ - ASM_EXCEPTIONTABLE_ENTRY(1b,fixup_put_user_skip_2)\ - ASM_EXCEPTIONTABLE_ENTRY(2b,fixup_put_user_skip_1)\ + ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_2)\ + ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_put_user_skip_1)\ : "=r"(__pu_err) \ : "r"(ptr), "r"(__val), "0"(__pu_err) \ : "r1"); \ @@ -211,8 +211,8 @@ extern unsigned long lcopy_to_user(void __user *, const void *, unsigned long); extern unsigned long lcopy_from_user(void *, const void __user *, unsigned long); extern unsigned long lcopy_in_user(void __user *, const void __user *, unsigned long); extern long strncpy_from_user(char *, const char __user *, long); -extern unsigned lclear_user(void __user *,unsigned long); -extern long lstrnlen_user(const char __user *,long); +extern unsigned lclear_user(void __user *, unsigned long); +extern long lstrnlen_user(const char __user *, long); /* * Complex access routines -- macros */ From 3237f28e6ce3318431d6f66271a35f5eca4e6439 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 135/360] sh: macro whitespace fixes While working on arch/sh/include/asm/uaccess.h, I noticed that one macro within this header is made harder to read because it violates a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin --- arch/sh/include/asm/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/include/asm/segment.h b/arch/sh/include/asm/segment.h index 5e2725f4ac4901..ff795d3a6909f8 100644 --- a/arch/sh/include/asm/segment.h +++ b/arch/sh/include/asm/segment.h @@ -23,7 +23,7 @@ typedef struct { #define USER_DS KERNEL_DS #endif -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define get_ds() (KERNEL_DS) From 33a3dcc2289368c2fc022c3b0b9bc388c8aa5930 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 15:11:13 +0200 Subject: [PATCH 136/360] xtensa: macro whitespace fixes While working on arch/xtensa/include/asm/uaccess.h, I noticed that some macros within this header are made harder to read because they violate a coding style rule: space is missing after comma. Fix it up. Signed-off-by: Michael S. Tsirkin Acked-by: Max Filippov --- arch/xtensa/include/asm/uaccess.h | 90 +++++++++++++++---------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h index 876eb380aa2670..147b26ed9c91f0 100644 --- a/arch/xtensa/include/asm/uaccess.h +++ b/arch/xtensa/include/asm/uaccess.h @@ -182,13 +182,13 @@ #define get_fs() (current->thread.current_ds) #define set_fs(val) (current->thread.current_ds = (val)) -#define segment_eq(a,b) ((a).seg == (b).seg) +#define segment_eq(a, b) ((a).seg == (b).seg) #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) -#define __user_ok(addr,size) \ +#define __user_ok(addr, size) \ (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size))) -#define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size))) -#define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size)) +#define __access_ok(addr, size) (__kernel_ok || __user_ok((addr), (size))) +#define access_ok(type, addr, size) __access_ok((unsigned long)(addr), (size)) /* * These are the main single-value transfer routines. They @@ -204,8 +204,8 @@ * (a) re-use the arguments for side effects (sizeof is ok) * (b) require any knowledge of processes at this stage */ -#define put_user(x,ptr) __put_user_check((x),(ptr),sizeof(*(ptr))) -#define get_user(x,ptr) __get_user_check((x),(ptr),sizeof(*(ptr))) +#define put_user(x, ptr) __put_user_check((x), (ptr), sizeof(*(ptr))) +#define get_user(x, ptr) __get_user_check((x), (ptr), sizeof(*(ptr))) /* * The "__xxx" versions of the user access functions are versions that @@ -213,39 +213,39 @@ * with a separate "access_ok()" call (this is used when we do multiple * accesses to the same area of user memory). */ -#define __put_user(x,ptr) __put_user_nocheck((x),(ptr),sizeof(*(ptr))) -#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x, ptr) __put_user_nocheck((x), (ptr), sizeof(*(ptr))) +#define __get_user(x, ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr))) extern long __put_user_bad(void); -#define __put_user_nocheck(x,ptr,size) \ +#define __put_user_nocheck(x, ptr, size) \ ({ \ long __pu_err; \ - __put_user_size((x),(ptr),(size),__pu_err); \ + __put_user_size((x), (ptr), (size), __pu_err); \ __pu_err; \ }) -#define __put_user_check(x,ptr,size) \ -({ \ - long __pu_err = -EFAULT; \ - __typeof__(*(ptr)) *__pu_addr = (ptr); \ - if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ - __put_user_size((x),__pu_addr,(size),__pu_err); \ - __pu_err; \ +#define __put_user_check(x, ptr, size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE, __pu_addr, size)) \ + __put_user_size((x), __pu_addr, (size), __pu_err); \ + __pu_err; \ }) -#define __put_user_size(x,ptr,size,retval) \ +#define __put_user_size(x, ptr, size, retval) \ do { \ int __cb; \ retval = 0; \ switch (size) { \ - case 1: __put_user_asm(x,ptr,retval,1,"s8i",__cb); break; \ - case 2: __put_user_asm(x,ptr,retval,2,"s16i",__cb); break; \ - case 4: __put_user_asm(x,ptr,retval,4,"s32i",__cb); break; \ + case 1: __put_user_asm(x, ptr, retval, 1, "s8i", __cb); break; \ + case 2: __put_user_asm(x, ptr, retval, 2, "s16i", __cb); break; \ + case 4: __put_user_asm(x, ptr, retval, 4, "s32i", __cb); break; \ case 8: { \ __typeof__(*ptr) __v64 = x; \ - retval = __copy_to_user(ptr,&__v64,8); \ + retval = __copy_to_user(ptr, &__v64, 8); \ break; \ } \ default: __put_user_bad(); \ @@ -316,35 +316,35 @@ __asm__ __volatile__( \ :"=r" (err), "=r" (cb) \ :"r" ((int)(x)), "r" (addr), "i" (-EFAULT), "0" (err)) -#define __get_user_nocheck(x,ptr,size) \ +#define __get_user_nocheck(x, ptr, size) \ ({ \ long __gu_err, __gu_val; \ - __get_user_size(__gu_val,(ptr),(size),__gu_err); \ - (x) = (__force __typeof__(*(ptr)))__gu_val; \ + __get_user_size(__gu_val, (ptr), (size), __gu_err); \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) -#define __get_user_check(x,ptr,size) \ +#define __get_user_check(x, ptr, size) \ ({ \ long __gu_err = -EFAULT, __gu_val = 0; \ const __typeof__(*(ptr)) *__gu_addr = (ptr); \ - if (access_ok(VERIFY_READ,__gu_addr,size)) \ - __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ - (x) = (__force __typeof__(*(ptr)))__gu_val; \ + if (access_ok(VERIFY_READ, __gu_addr, size)) \ + __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ + (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) extern long __get_user_bad(void); -#define __get_user_size(x,ptr,size,retval) \ +#define __get_user_size(x, ptr, size, retval) \ do { \ int __cb; \ retval = 0; \ switch (size) { \ - case 1: __get_user_asm(x,ptr,retval,1,"l8ui",__cb); break; \ - case 2: __get_user_asm(x,ptr,retval,2,"l16ui",__cb); break; \ - case 4: __get_user_asm(x,ptr,retval,4,"l32i",__cb); break; \ - case 8: retval = __copy_from_user(&x,ptr,8); break; \ + case 1: __get_user_asm(x, ptr, retval, 1, "l8ui", __cb); break;\ + case 2: __get_user_asm(x, ptr, retval, 2, "l16ui", __cb); break;\ + case 4: __get_user_asm(x, ptr, retval, 4, "l32i", __cb); break;\ + case 8: retval = __copy_from_user(&x, ptr, 8); break; \ default: (x) = __get_user_bad(); \ } \ } while (0) @@ -390,19 +390,19 @@ __asm__ __volatile__( \ */ extern unsigned __xtensa_copy_user(void *to, const void *from, unsigned n); -#define __copy_user(to,from,size) __xtensa_copy_user(to,from,size) +#define __copy_user(to, from, size) __xtensa_copy_user(to, from, size) static inline unsigned long __generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n) { - return __copy_user(to,from,n); + return __copy_user(to, from, n); } static inline unsigned long __generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n) { - return __copy_user(to,from,n); + return __copy_user(to, from, n); } static inline unsigned long @@ -410,7 +410,7 @@ __generic_copy_to_user(void *to, const void *from, unsigned long n) { prefetch(from); if (access_ok(VERIFY_WRITE, to, n)) - return __copy_user(to,from,n); + return __copy_user(to, from, n); return n; } @@ -419,18 +419,18 @@ __generic_copy_from_user(void *to, const void *from, unsigned long n) { prefetchw(to); if (access_ok(VERIFY_READ, from, n)) - return __copy_user(to,from,n); + return __copy_user(to, from, n); else memset(to, 0, n); return n; } -#define copy_to_user(to,from,n) __generic_copy_to_user((to),(from),(n)) -#define copy_from_user(to,from,n) __generic_copy_from_user((to),(from),(n)) -#define __copy_to_user(to,from,n) \ - __generic_copy_to_user_nocheck((to),(from),(n)) -#define __copy_from_user(to,from,n) \ - __generic_copy_from_user_nocheck((to),(from),(n)) +#define copy_to_user(to, from, n) __generic_copy_to_user((to), (from), (n)) +#define copy_from_user(to, from, n) __generic_copy_from_user((to), (from), (n)) +#define __copy_to_user(to, from, n) \ + __generic_copy_to_user_nocheck((to), (from), (n)) +#define __copy_from_user(to, from, n) \ + __generic_copy_from_user_nocheck((to), (from), (n)) #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user From 4b636ba27008ce11631c89b4e0918800204575e0 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 23:29:43 +0200 Subject: [PATCH 137/360] sparc64: nocheck uaccess coding style tweaks Sam Ravnborg suggested packing single-lines cases in switch statements in nocheck uaccess macros makes for easier to read code. Suggested-by: Sam Ravnborg Signed-off-by: Michael S. Tsirkin Acked-by: Sam Ravnborg --- arch/sparc/include/asm/uaccess_64.h | 100 ++++++++++------------------ 1 file changed, 37 insertions(+), 63 deletions(-) diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index 12d95947636b2e..a35194b7dba010 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -106,26 +106,16 @@ void __retl_efault(void); struct __large_struct { unsigned long buf[100]; }; #define __m(x) ((struct __large_struct *)(x)) -#define __put_user_nocheck(data, addr, size) ({ \ - register int __pu_ret; \ - switch (size) { \ - case 1: \ - __put_user_asm(data, b, addr, __pu_ret); \ - break; \ - case 2: \ - __put_user_asm(data, h, addr, __pu_ret); \ - break; \ - case 4: \ - __put_user_asm(data, w, addr, __pu_ret); \ - break; \ - case 8: \ - __put_user_asm(data, x, addr, __pu_ret); \ - break; \ - default: \ - __pu_ret = __put_user_bad(); \ - break; \ - } \ - __pu_ret; \ +#define __put_user_nocheck(data, addr, size) ({ \ + register int __pu_ret; \ + switch (size) { \ + case 1: __put_user_asm(data, b, addr, __pu_ret); break; \ + case 2: __put_user_asm(data, h, addr, __pu_ret); break; \ + case 4: __put_user_asm(data, w, addr, __pu_ret); break; \ + case 8: __put_user_asm(data, x, addr, __pu_ret); break; \ + default: __pu_ret = __put_user_bad(); break; \ + } \ + __pu_ret; \ }) #define __put_user_asm(x, size, addr, ret) \ @@ -150,51 +140,35 @@ __asm__ __volatile__( \ int __put_user_bad(void); -#define __get_user_nocheck(data, addr, size, type) ({ \ - register int __gu_ret; \ - register unsigned long __gu_val; \ - switch (size) { \ - case 1: \ - __get_user_asm(__gu_val, ub, addr, __gu_ret); \ - break; \ - case 2: \ - __get_user_asm(__gu_val, uh, addr, __gu_ret); \ - break; \ - case 4: \ - __get_user_asm(__gu_val, uw, addr, __gu_ret); \ - break; \ - case 8: \ - __get_user_asm(__gu_val, x, addr, __gu_ret); \ - break; \ - default: \ - __gu_val = 0; \ - __gu_ret = __get_user_bad(); \ - break; \ - } \ - data = (__force type) __gu_val; \ - __gu_ret; \ +#define __get_user_nocheck(data, addr, size, type) ({ \ + register int __gu_ret; \ + register unsigned long __gu_val; \ + switch (size) { \ + case 1: __get_user_asm(__gu_val, ub, addr, __gu_ret); break; \ + case 2: __get_user_asm(__gu_val, uh, addr, __gu_ret); break; \ + case 4: __get_user_asm(__gu_val, uw, addr, __gu_ret); break; \ + case 8: __get_user_asm(__gu_val, x, addr, __gu_ret); break; \ + default: \ + __gu_val = 0; \ + __gu_ret = __get_user_bad(); \ + break; \ + } \ + data = (__force type) __gu_val; \ + __gu_ret; \ }) -#define __get_user_nocheck_ret(data, addr, size, type, retval) ({ \ - register unsigned long __gu_val __asm__ ("l1"); \ - switch (size) { \ - case 1: \ - __get_user_asm_ret(__gu_val, ub, addr, retval); \ - break; \ - case 2: \ - __get_user_asm_ret(__gu_val, uh, addr, retval); \ - break; \ - case 4: \ - __get_user_asm_ret(__gu_val, uw, addr, retval); \ - break; \ - case 8: \ - __get_user_asm_ret(__gu_val, x, addr, retval); \ - break; \ - default: \ - if (__get_user_bad()) \ - return retval; \ - } \ - data = (__force type) __gu_val; \ +#define __get_user_nocheck_ret(data, addr, size, type, retval) ({ \ + register unsigned long __gu_val __asm__ ("l1"); \ + switch (size) { \ + case 1: __get_user_asm_ret(__gu_val, ub, addr, retval); break; \ + case 2: __get_user_asm_ret(__gu_val, uh, addr, retval); break; \ + case 4: __get_user_asm_ret(__gu_val, uw, addr, retval); break; \ + case 8: __get_user_asm_ret(__gu_val, x, addr, retval); break; \ + default: \ + if (__get_user_bad()) \ + return retval; \ + } \ + data = (__force type) __gu_val; \ }) #define __get_user_asm(x, size, addr, ret) \ From 0795cb1b46e7938ed679ccd48f933e75272b30e3 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 6 Jan 2015 23:40:11 +0200 Subject: [PATCH 138/360] sparc32: nocheck uaccess coding style tweaks Sam Ravnborg suggested packing single-lines cases in switch statements in nocheck uaccess macros makes for easier to read code. Suggested-by: Sam Ravnborg Signed-off-by: Michael S. Tsirkin Acked-by: Sam Ravnborg --- arch/sparc/include/asm/uaccess_32.h | 96 +++++++++++------------------ 1 file changed, 35 insertions(+), 61 deletions(-) diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index f96656209a78b8..64ee103dc29da1 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -142,24 +142,14 @@ struct __large_struct { unsigned long buf[100]; }; __pu_ret; \ }) -#define __put_user_nocheck(x, addr, size) ({ \ - register int __pu_ret; \ - switch (size) { \ - case 1: \ - __put_user_asm(x, b, addr, __pu_ret); \ - break; \ - case 2: \ - __put_user_asm(x, h, addr, __pu_ret); \ - break; \ - case 4: \ - __put_user_asm(x, , addr, __pu_ret); \ - break; \ - case 8: \ - __put_user_asm(x, d, addr, __pu_ret); \ - break; \ - default: \ - __pu_ret = __put_user_bad(); \ - break; \ +#define __put_user_nocheck(x, addr, size) ({ \ + register int __pu_ret; \ + switch (size) { \ + case 1: __put_user_asm(x, b, addr, __pu_ret); break; \ + case 2: __put_user_asm(x, h, addr, __pu_ret); break; \ + case 4: __put_user_asm(x, , addr, __pu_ret); break; \ + case 8: __put_user_asm(x, d, addr, __pu_ret); break; \ + default: __pu_ret = __put_user_bad(); break; \ } \ __pu_ret; \ }) @@ -240,51 +230,35 @@ int __put_user_bad(void); return retval; \ }) -#define __get_user_nocheck(x, addr, size, type) ({ \ - register int __gu_ret; \ - register unsigned long __gu_val; \ - switch (size) { \ - case 1: \ - __get_user_asm(__gu_val, ub, addr, __gu_ret); \ - break; \ - case 2: \ - __get_user_asm(__gu_val, uh, addr, __gu_ret); \ - break; \ - case 4: \ - __get_user_asm(__gu_val, , addr, __gu_ret); \ - break; \ - case 8: \ - __get_user_asm(__gu_val, d, addr, __gu_ret); \ - break; \ - default: \ - __gu_val = 0; \ - __gu_ret = __get_user_bad(); \ - break; \ - } \ - x = (__force type) __gu_val; \ - __gu_ret; \ +#define __get_user_nocheck(x, addr, size, type) ({ \ + register int __gu_ret; \ + register unsigned long __gu_val; \ + switch (size) { \ + case 1: __get_user_asm(__gu_val, ub, addr, __gu_ret); break; \ + case 2: __get_user_asm(__gu_val, uh, addr, __gu_ret); break; \ + case 4: __get_user_asm(__gu_val, , addr, __gu_ret); break; \ + case 8: __get_user_asm(__gu_val, d, addr, __gu_ret); break; \ + default: \ + __gu_val = 0; \ + __gu_ret = __get_user_bad(); \ + break; \ + } \ + x = (__force type) __gu_val; \ + __gu_ret; \ }) -#define __get_user_nocheck_ret(x, addr, size, type, retval) ({ \ - register unsigned long __gu_val __asm__ ("l1"); \ - switch (size) { \ - case 1: \ - __get_user_asm_ret(__gu_val, ub, addr, retval); \ - break; \ - case 2: \ - __get_user_asm_ret(__gu_val, uh, addr, retval); \ - break; \ - case 4: \ - __get_user_asm_ret(__gu_val, , addr, retval); \ - break; \ - case 8: \ - __get_user_asm_ret(__gu_val, d, addr, retval); \ - break; \ - default: \ - if (__get_user_bad()) \ - return retval; \ - } \ - x = (__force type) __gu_val; \ +#define __get_user_nocheck_ret(x, addr, size, type, retval) ({ \ + register unsigned long __gu_val __asm__ ("l1"); \ + switch (size) { \ + case 1: __get_user_asm_ret(__gu_val, ub, addr, retval); break; \ + case 2: __get_user_asm_ret(__gu_val, uh, addr, retval); break; \ + case 4: __get_user_asm_ret(__gu_val, , addr, retval); break; \ + case 8: __get_user_asm_ret(__gu_val, d, addr, retval); break; \ + default: \ + if (__get_user_bad()) \ + return retval; \ + } \ + x = (__force type) __gu_val; \ }) #define __get_user_asm(x, size, addr, ret) \ From a1a9becbf35899cd44ac1bea4a04f98e5adc9ffb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 29 Dec 2014 14:01:30 +0100 Subject: [PATCH 139/360] dmaengine: k3: Fix duplicated function name and allmodconfig build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While splitting device control in db08425ebd51 ("dmaengine: k3: Split device_control") new function with the same 'k3_dma_resume' name was added, leading to build error: drivers/dma/k3dma.c:823:12: error: conflicting types for ‘k3_dma_resume’ drivers/dma/k3dma.c:625:12: note: previous definition of ‘k3_dma_resume’ was here Signed-off-by: Krzysztof Kozlowski Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 49be7f687c4cd1..a4ff7e9e68bee0 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -601,7 +601,7 @@ static int k3_dma_terminate_all(struct dma_chan *chan) return 0; } -static int k3_dma_pause(struct dma_chan *chan) +static int k3_dma_transfer_pause(struct dma_chan *chan) { struct k3_dma_chan *c = to_k3_chan(chan); struct k3_dma_dev *d = to_k3_dma(chan->device); @@ -622,7 +622,7 @@ static int k3_dma_pause(struct dma_chan *chan) return 0; } -static int k3_dma_resume(struct dma_chan *chan) +static int k3_dma_transfer_resume(struct dma_chan *chan) { struct k3_dma_chan *c = to_k3_chan(chan); struct k3_dma_dev *d = to_k3_dma(chan->device); @@ -735,8 +735,8 @@ static int k3_dma_probe(struct platform_device *op) d->slave.device_prep_slave_sg = k3_dma_prep_slave_sg; d->slave.device_issue_pending = k3_dma_issue_pending; d->slave.device_config = k3_dma_config; - d->slave.device_pause = k3_dma_pause; - d->slave.device_resume = k3_dma_resume; + d->slave.device_pause = k3_dma_transfer_pause; + d->slave.device_resume = k3_dma_transfer_resume; d->slave.device_terminate_all = k3_dma_terminate_all; d->slave.copy_align = DMA_ALIGN; From 10b3e223174f28eabadab18b5dfa36021d956a2d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Jan 2015 14:23:13 +0100 Subject: [PATCH 140/360] dmaengine: k3: fix duplicate function definition Commit db08425ebd51f ("dmaengine: k3: Split device_control") introduced two new helper functions, which unfortunately have the same names as the existing suspend/resume functions, resulting in a build error when CONFIG_PM_SLEEP is enabled: drivers/dma/k3dma.c:823:12: error: conflicting types for 'k3_dma_resume' static int k3_dma_resume(struct device *dev) ^ drivers/dma/k3dma.c:625:12: note: previous definition of 'k3_dma_resume' was here static int k3_dma_resume(struct dma_chan *chan) ^ Signed-off-by: Arnd Bergmann Fixes: db08425ebd51f ("dmaengine: k3: Split device_control") Reported-by: Mark Brown Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index a4ff7e9e68bee0..6f7f43529ccb17 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -804,7 +804,7 @@ static int k3_dma_remove(struct platform_device *op) } #ifdef CONFIG_PM_SLEEP -static int k3_dma_suspend(struct device *dev) +static int k3_dma_suspend_dev(struct device *dev) { struct k3_dma_dev *d = dev_get_drvdata(dev); u32 stat = 0; @@ -820,7 +820,7 @@ static int k3_dma_suspend(struct device *dev) return 0; } -static int k3_dma_resume(struct device *dev) +static int k3_dma_resume_dev(struct device *dev) { struct k3_dma_dev *d = dev_get_drvdata(dev); int ret = 0; @@ -835,7 +835,7 @@ static int k3_dma_resume(struct device *dev) } #endif -static SIMPLE_DEV_PM_OPS(k3_dma_pmops, k3_dma_suspend, k3_dma_resume); +static SIMPLE_DEV_PM_OPS(k3_dma_pmops, k3_dma_suspend_dev, k3_dma_resume_dev); static struct platform_driver k3_pdma_driver = { .driver = { From 2f56eaff2010326848f13c4a1e468a2dd1527f0b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Jan 2015 14:25:19 +0100 Subject: [PATCH 141/360] dmaengine: mmp-tdma: don't include mach/regs-icu.h The mmp tdma driver does not actually require this header, and we want to enable multiplatform support for MMP, which would make it inaccessible and cause a build error. This patch just removes the old #include. Signed-off-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index a8a79b1763d560..91fb241102f7f4 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include From 3c20ba5fb5b9e3d5e266bfab316ee10b77da88e0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Jan 2015 14:31:46 +0100 Subject: [PATCH 142/360] dmaengine: mmp-tdma: fix terminate_all return code In a recent cleanup, the mmp_tdma_terminate_all function was introduced but does not set a proper return value. Almost no slave driver uses that return value, but if one does, the result will be undefined, which the compiler warns about: dma/mmp_tdma.c: In function 'mmp_tdma_terminate_all': dma/mmp_tdma.c:474:1: warning: no return statement in function returning non-void [-Wreturn-type] This changes the driver to return zero, like most other drivers do. Signed-off-by: Arnd Bergmann Fixes: f43a6fd400ba6 ("dmaengine: mmp-tdma: Split device_control") Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 91fb241102f7f4..70c2fa9963cd4d 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -471,6 +471,8 @@ static int mmp_tdma_terminate_all(struct dma_chan *chan) mmp_tdma_disable_chan(chan); /* disable interrupt */ mmp_tdma_enable_irq(tdmac, false); + + return 0; } static int mmp_tdma_config(struct dma_chan *chan, From 640f204bcdea927f4c2ce4af76a5e92736f20019 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 13 Jan 2015 17:46:48 +0100 Subject: [PATCH 143/360] dmaengine: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index b60d77a22df673..1c4c96b7e2bf84 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1507,7 +1507,6 @@ static struct platform_driver at_xdmac_driver = { .remove = at_xdmac_remove, .driver = { .name = "at_xdmac", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(atmel_xdmac_dt_ids), .pm = &atmel_xdmac_dev_pm_ops, } From 029a40e97d0db269f4a7fc02b0f29f627f628309 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 2 Jan 2015 16:17:24 +0200 Subject: [PATCH 144/360] dmaengine: dw: provide DMA capabilities The new DMAEngine requirement is to provide what the DMA controller can do, such as directions, bus widths, and residue granularity. The patch sets those properties for the DesignWare DMA controller driver. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 4bc307745ef1a6..fcb9a916e6f65c 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -61,6 +61,13 @@ */ #define NR_DESCS_PER_CHANNEL 64 +/* The set of bus widths supported by the DMA controller */ +#define DW_DMA_BUSWIDTHS \ + BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) + /*----------------------------------------------------------------------*/ static struct device *chan2dev(struct dma_chan *chan) @@ -1660,8 +1667,8 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dw->dma.device_free_chan_resources = dwc_free_chan_resources; dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; - dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_config = dwc_config; dw->dma.device_pause = dwc_pause; dw->dma.device_resume = dwc_resume; @@ -1670,6 +1677,13 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dw->dma.device_tx_status = dwc_tx_status; dw->dma.device_issue_pending = dwc_issue_pending; + /* DMA capabilities */ + dw->dma.src_addr_widths = DW_DMA_BUSWIDTHS; + dw->dma.dst_addr_widths = DW_DMA_BUSWIDTHS; + dw->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV) | + BIT(DMA_MEM_TO_MEM); + dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + err = dma_async_device_register(&dw->dma); if (err) goto err_dma_register; From ef9d2a92334a27f4fb183ca1259744303564cc1b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 15:21:19 -0200 Subject: [PATCH 145/360] dmaengine: mxs-dma: Declare slave capabilities for the generic code Since ecc19d17868be9c ("dmaengine: Add a warning for drivers not using the generic slave caps retrieval") the following warning is observed: [ 0.113023] ------------[ cut here ]------------ [ 0.113053] WARNING: CPU: 0 PID: 1 at drivers/dma/dmaengine.c:830 dma_async_device_register+0x2a0/0x4c8() [ 0.113063] this driver doesn't support generic slave capabilities reporting Declare the slave capabilities to avoid such warning. Signed-off-by: Fabio Estevam Acked-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 599ffb5b784836..829ec686dac337 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -848,6 +848,10 @@ static int __init mxs_dma_probe(struct platform_device *pdev) mxs_dma->dma_device.device_pause = mxs_dma_pause_chan; mxs_dma->dma_device.device_resume = mxs_dma_resume_chan; mxs_dma->dma_device.device_terminate_all = mxs_dma_terminate_all; + mxs_dma->dma_device.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + mxs_dma->dma_device.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + mxs_dma->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + mxs_dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; mxs_dma->dma_device.device_issue_pending = mxs_dma_enable_chan; ret = dma_async_device_register(&mxs_dma->dma_device); From 1e4a4f50d2466d18a3e6c64ddd39f2528172b90d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 15:20:51 -0200 Subject: [PATCH 146/360] dmaengine: imx-sdma: Declare slave capabilities for the generic code Since ecc19d17868be9c ("dmaengine: Add a warning for drivers not using the generic slave caps retrieval") the following warning is observed: [ 0.224981] ------------[ cut here ]------------ [ 0.225013] WARNING: CPU: 0 PID: 1 at drivers/dma/dmaengine.c:830 dma_async_device_register+0x2a0/0x4c8() [ 0.225023] this driver doesn't support generic slave capabilities reporting Declare the slave capabilities to avoid such warning. Signed-off-by: Fabio Estevam Acked-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 1748a4bd475f1b..d28c26bf7a2ada 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1595,6 +1595,10 @@ static int sdma_probe(struct platform_device *pdev) sdma->dma_device.device_prep_dma_cyclic = sdma_prep_dma_cyclic; sdma->dma_device.device_config = sdma_config; sdma->dma_device.device_terminate_all = sdma_disable_channel; + sdma->dma_device.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + sdma->dma_device.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + sdma->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + sdma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; sdma->dma_device.device_issue_pending = sdma_issue_pending; sdma->dma_device.dev->dma_parms = &sdma->dma_parms; dma_set_max_seg_size(sdma->dma_device.dev, 65535); From 7f24e0ee00d052f1710b68bbf6221cc674eb7a9b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 15:20:52 -0200 Subject: [PATCH 147/360] dmaengine: imx-sdma: Use devm functions By using devm functions we can make the code shorter and cleaner. Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 62 +++++++++++------------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index d28c26bf7a2ada..125c326c525fcf 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1472,7 +1472,7 @@ static int sdma_probe(struct platform_device *pdev) if (ret) return ret; - sdma = kzalloc(sizeof(*sdma), GFP_KERNEL); + sdma = devm_kzalloc(&pdev->dev, sizeof(*sdma), GFP_KERNEL); if (!sdma) return -ENOMEM; @@ -1481,48 +1481,34 @@ static int sdma_probe(struct platform_device *pdev) sdma->dev = &pdev->dev; sdma->drvdata = drvdata; - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!iores || irq < 0) { - ret = -EINVAL; - goto err_irq; - } + if (irq < 0) + return -EINVAL; - if (!request_mem_region(iores->start, resource_size(iores), pdev->name)) { - ret = -EBUSY; - goto err_request_region; - } + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sdma->regs = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(sdma->regs)) + return PTR_ERR(sdma->regs); sdma->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(sdma->clk_ipg)) { - ret = PTR_ERR(sdma->clk_ipg); - goto err_clk; - } + if (IS_ERR(sdma->clk_ipg)) + return PTR_ERR(sdma->clk_ipg); sdma->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(sdma->clk_ahb)) { - ret = PTR_ERR(sdma->clk_ahb); - goto err_clk; - } + if (IS_ERR(sdma->clk_ahb)) + return PTR_ERR(sdma->clk_ahb); clk_prepare(sdma->clk_ipg); clk_prepare(sdma->clk_ahb); - sdma->regs = ioremap(iores->start, resource_size(iores)); - if (!sdma->regs) { - ret = -ENOMEM; - goto err_ioremap; - } - - ret = request_irq(irq, sdma_int_handler, 0, "sdma", sdma); + ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, "sdma", + sdma); if (ret) - goto err_request_irq; + return ret; sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL); - if (!sdma->script_addrs) { - ret = -ENOMEM; - goto err_alloc; - } + if (!sdma->script_addrs) + return -ENOMEM; /* initially no scripts available */ saddr_arr = (s32 *)sdma->script_addrs; @@ -1627,38 +1613,22 @@ static int sdma_probe(struct platform_device *pdev) dma_async_device_unregister(&sdma->dma_device); err_init: kfree(sdma->script_addrs); -err_alloc: - free_irq(irq, sdma); -err_request_irq: - iounmap(sdma->regs); -err_ioremap: -err_clk: - release_mem_region(iores->start, resource_size(iores)); -err_request_region: -err_irq: - kfree(sdma); return ret; } static int sdma_remove(struct platform_device *pdev) { struct sdma_engine *sdma = platform_get_drvdata(pdev); - struct resource *iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int irq = platform_get_irq(pdev, 0); int i; dma_async_device_unregister(&sdma->dma_device); kfree(sdma->script_addrs); - free_irq(irq, sdma); - iounmap(sdma->regs); - release_mem_region(iores->start, resource_size(iores)); /* Kill the tasklet */ for (i = 0; i < MAX_DMA_CHANNELS; i++) { struct sdma_channel *sdmac = &sdma->channel[i]; tasklet_kill(&sdmac->tasklet); } - kfree(sdma); platform_set_drvdata(pdev, NULL); dev_info(&pdev->dev, "Removed...\n"); From 63c72e028a1dd18515c356834d3f9cfce1cd3f51 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Dec 2014 15:20:53 -0200 Subject: [PATCH 148/360] dmaengine: imx-sdma: Return a proper error code in platform_get_irq() There is no need to return a 'fake' value upon platform_get_irq() failure. Just propagate the real error instead. Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 125c326c525fcf..fc874e53bec400 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1483,7 +1483,7 @@ static int sdma_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -EINVAL; + return irq; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); sdma->regs = devm_ioremap_resource(&pdev->dev, iores); From 7a96337d05e7ca5eee2b4c3851700e21a8bac017 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 Jan 2015 01:38:50 +0000 Subject: [PATCH 149/360] dmaengine: rcar-hpbdma: fixup WARNING of slave caps retrieval ecc19d17868be9c9f8f00ed928791533c420f3e0 (dmaengine: Add a warning for drivers not using the generic slave caps retrieval) added WARN() for DMA_SLAVE. Kernel will shows WARNING without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-hpbdma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/sh/rcar-hpbdma.c b/drivers/dma/sh/rcar-hpbdma.c index 20a6f6f2a01868..6fef1b95c89520 100644 --- a/drivers/dma/sh/rcar-hpbdma.c +++ b/drivers/dma/sh/rcar-hpbdma.c @@ -534,6 +534,8 @@ static int hpb_dmae_chan_probe(struct hpb_dmae_device *hpbdev, int id) static int hpb_dmae_probe(struct platform_device *pdev) { + const enum dma_slave_buswidth widths = DMA_SLAVE_BUSWIDTH_1_BYTE | + DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES; struct hpb_dmae_pdata *pdata = pdev->dev.platform_data; struct hpb_dmae_device *hpbdev; struct dma_device *dma_dev; @@ -595,6 +597,10 @@ static int hpb_dmae_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); + dma_dev->src_addr_widths = widths; + dma_dev->dst_addr_widths = widths; + dma_dev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; hpbdev->shdma_dev.ops = &hpb_dmae_ops; hpbdev->shdma_dev.desc_size = sizeof(struct hpb_desc); From 75dc1775ec83db305a68d153a7ac5eb4e8b634a0 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 8 Jan 2015 18:38:16 +0800 Subject: [PATCH 150/360] dmaengine: fsldma: declare slave capabilities for the generic code Since commit ecc19d17868b ("dmaengine: Add a warning for drivers not using the generic slave caps retrieval"), the dma drivers are required to fill the caps infos in order to support generic slaves caps retrieval. Otherwise we will get a warning like this: WARNING: at drivers/dma/dmaengine.c:830 Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 3.19.0-rc2-next-20150106-dirty #271 task: c0000001f70a0000 ti: c0000001f7044000 task.ti: c0000001f7044000 NIP: c00000000032b238 LR: c00000000032b234 CTR: c00000000001d258 REGS: c0000001f7047330 TRAP: 0700 Tainted: G W (3.19.0-rc2-next-20150106-dirty) MSR: 0000000080029000 CR: 24adbe22 XER: 20000000 SOFTE: 1 GPR00: c00000000032b234 c0000001f70475b0 c0000000009b4848 0000000000000040 GPR04: 0000000000000001 0000000000000001 0000000000000000 000000000000000f GPR08: 0000000000000000 c000000000902988 c000000000902988 00000000000052c8 GPR12: 0000000024adbe22 c00000000fff4000 c000000000002038 0000000000000000 GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 GPR24: 0000000000000000 0000000000000000 c000000000972dc8 c0000000007e6fd0 GPR28: c0000001f76d1d30 c0000001f76d1c10 c0000001f76d1c00 0000000000000000 NIP [c00000000032b238] .dma_async_device_register+0x3f8/0x5b8 LR [c00000000032b234] .dma_async_device_register+0x3f4/0x5b8 Call Trace: [c0000001f70475b0] [c00000000032b234] .dma_async_device_register+0x3f4/0x5b8 (unreliable) [c0000001f70476a0] [c00000000032ca78] .fsldma_of_probe+0x298/0x438 [c0000001f7047750] [c00000000037080c] .platform_drv_probe+0x50/0x9c [c0000001f70477d0] [c00000000036e74c] .really_probe+0xa4/0x29c [c0000001f7047870] [c00000000036eae4] .__driver_attach+0x100/0x104 [c0000001f7047900] [c00000000036c1f0] .bus_for_each_dev+0x84/0xe4 [c0000001f70479a0] [c00000000036e164] .driver_attach+0x24/0x38 [c0000001f7047a10] [c00000000036dcc8] .bus_add_driver+0x1c8/0x2ac [c0000001f7047ab0] [c00000000036f14c] .driver_register+0x8c/0x158 [c0000001f7047b30] [c0000000003707a8] .__platform_driver_register+0x6c/0x80 [c0000001f7047ba0] [c000000000898a3c] .fsldma_init+0x2c/0x40 [c0000001f7047c10] [c000000000001818] .do_one_initcall+0xb8/0x234 [c0000001f7047d00] [c000000000878e2c] .kernel_init_freeable+0x188/0x268 [c0000001f7047db0] [c000000000002054] .kernel_init+0x1c/0xfc8 [c0000001f7047e30] [c000000000000884] .ret_from_kernel_thread+0x58/0xd4 Instruction dump: 7fb9f840 3bffffe0 409effac 7f54d378 48000060 813d0050 2f890000 40befdd0 3c62ffe3 38632450 482f0aa9 60000000 <0fe00000> 4bfffdb8 7f03c378 482ed465 Signed-off-by: Kevin Hao Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 5 +++++ drivers/dma/fsldma.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index b891079a166d6f..6856c8a916abf1 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1372,6 +1372,11 @@ static int fsldma_of_probe(struct platform_device *op) fdev->common.device_terminate_all = fsl_dma_device_terminate_all; fdev->common.dev = &op->dev; + fdev->common.src_addr_widths = FSL_DMA_BUSWIDTHS; + fdev->common.dst_addr_widths = FSL_DMA_BUSWIDTHS; + fdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + fdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + dma_set_mask(&(op->dev), DMA_BIT_MASK(36)); platform_set_drvdata(op, fdev); diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index 239c20c84382ce..31bffccdcc75f9 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -83,6 +83,10 @@ #define FSL_DMA_DGSR_EOSI 0x02 #define FSL_DMA_DGSR_EOLSI 0x01 +#define FSL_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) typedef u64 __bitwise v64; typedef u32 __bitwise v32; From 0fdd244fe820c03b59ab1286db3d0b4a42c66e13 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 8 Jan 2015 18:38:17 +0800 Subject: [PATCH 151/360] dmaengine: fsldma: remove the unused variable Fix the following build warning: drivers/dma/fsldma.c: In function 'fsl_dma_device_terminate_all': drivers/dma/fsldma.c:947:6: warning: unused variable 'size' [-Wunused-variable] Signed-off-by: Kevin Hao Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 6856c8a916abf1..300f821f1890ec 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -944,7 +944,6 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan, static int fsl_dma_device_terminate_all(struct dma_chan *dchan) { struct fsldma_chan *chan; - int size; if (!dchan) return -EINVAL; From 891653ab836d5a8ef9eba0d347318364bd309a00 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Tue, 6 Jan 2015 06:44:56 +0000 Subject: [PATCH 152/360] dmaengine: tegra: add slave capabilities reporting After commit ecc19d17868be9c9f8f00ed928791533c420f3e0 ("dmaengine: Add a warning for drivers not using the generic slave caps retrieval"), the Tegra APB DMA driver causes this warning during boot: WARNING: CPU: 0 PID: 1 at drivers/dma/dmaengine.c:830 dma_async_device_register+0x294/0x538() this driver doesn't support generic slave capabilities reporting Fix by setting the appropriate reporting structure fields that are passed to dma_async_device_register(). Signed-off-by: Paul Walmsley Tested-by: Thierry Reding Acked-by: Thierry Reding Acked-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 5695fb8a7a244e..eaf585e8286b49 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1425,6 +1425,21 @@ static int tegra_dma_probe(struct platform_device *pdev) tegra_dma_free_chan_resources; tdma->dma_dev.device_prep_slave_sg = tegra_dma_prep_slave_sg; tdma->dma_dev.device_prep_dma_cyclic = tegra_dma_prep_dma_cyclic; + tdma->dma_dev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + tdma->dma_dev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + tdma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + /* + * XXX The hardware appears to support + * DMA_RESIDUE_GRANULARITY_BURST-level reporting, but it's + * only used by this driver during tegra_dma_terminate_all() + */ + tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; tdma->dma_dev.device_config = tegra_dma_slave_config; tdma->dma_dev.device_terminate_all = tegra_dma_terminate_all; tdma->dma_dev.device_tx_status = tegra_dma_tx_status; From 816070ede77003e033c76cd1f72127d9bb6c9a03 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Tue, 6 Jan 2015 17:36:26 +0100 Subject: [PATCH 153/360] dmaengine: at_hdmac: declare slave capabilities Declare slave capabilities to suppress "this driver doesn't support generic slave capabilities reporting" warning. Signed-off-by: Ludovic Desroches Acked-by: Maxime Ripard Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 86450b3442f2f2..1e1a4c56754260 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -42,6 +42,11 @@ #define ATC_DEFAULT_CFG (ATC_FIFOCFG_HALFFIFO) #define ATC_DEFAULT_CTRLB (ATC_SIF(AT_DMA_MEM_IF) \ |ATC_DIF(AT_DMA_MEM_IF)) +#define ATC_DMA_BUSWIDTHS\ + (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |\ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) /* * Initial number of descriptors to allocate for each channel. This could @@ -1531,6 +1536,10 @@ static int __init at_dma_probe(struct platform_device *pdev) atdma->dma_common.device_pause = atc_pause; atdma->dma_common.device_resume = atc_resume; atdma->dma_common.device_terminate_all = atc_terminate_all; + atdma->dma_common.src_addr_widths = ATC_DMA_BUSWIDTHS; + atdma->dma_common.dst_addr_widths = ATC_DMA_BUSWIDTHS; + atdma->dma_common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + atdma->dma_common.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; } dma_writel(atdma, EN, AT_DMA_ENABLE); From 45c6a0caeb2bde9e043bcd833af322db57a37498 Mon Sep 17 00:00:00 2001 From: Allen Xu Date: Tue, 13 Jan 2015 04:56:40 +0800 Subject: [PATCH 154/360] mtd: spi-nor: fsl-qspi: support deep sleep mode for imx6 sx chip i.mx6 sx support turn off fastmix and megamix power. qpsi controller can be turned off and all status lost when suspend/resume. add suspend/resume functions and reset qspi controller when resume. Signed-off-by: Allen Xu Signed-off-by: Frank Li Acked-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 39763b94f67d48..9d6080079a4529 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -972,6 +972,22 @@ static int fsl_qspi_remove(struct platform_device *pdev) return 0; } +static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int fsl_qspi_resume(struct platform_device *pdev) +{ + struct fsl_qspi *q = platform_get_drvdata(pdev); + + fsl_qspi_nor_setup(q); + fsl_qspi_set_map_addr(q); + fsl_qspi_nor_setup_last(q); + + return 0; +} + static struct platform_driver fsl_qspi_driver = { .driver = { .name = "fsl-quadspi", @@ -980,6 +996,8 @@ static struct platform_driver fsl_qspi_driver = { }, .probe = fsl_qspi_probe, .remove = fsl_qspi_remove, + .suspend = fsl_qspi_suspend, + .resume = fsl_qspi_resume, }; module_platform_driver(fsl_qspi_driver); From 526789fc532a445975b60885a65d3cea5bfa6391 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 11 Jan 2015 22:07:20 +0900 Subject: [PATCH 155/360] mtd: nand: ams-delta: fix overwritten mtd_info->owner in initialization In initialization routine, mtd_info->owner is overwritten by memset() just after being initialized. This can be fixed by moving memset() calls to just before setting mtd_info->owner. But the memory region is allocated by kmalloc, so we can fix it by using kzalloc instead of kmalloc. Signed-off-by: Akinobu Mita Cc: Jonathan McDowell Cc: David Woodhouse Signed-off-by: Brian Norris --- drivers/mtd/nand/ams-delta.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index f1d555cfb332f1..842f8fe91b56cb 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -183,7 +183,7 @@ static int ams_delta_init(struct platform_device *pdev) return -ENXIO; /* Allocate memory for MTD device structure and private data */ - ams_delta_mtd = kmalloc(sizeof(struct mtd_info) + + ams_delta_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!ams_delta_mtd) { printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n"); @@ -196,10 +196,6 @@ static int ams_delta_init(struct platform_device *pdev) /* Get pointer to private data */ this = (struct nand_chip *) (&ams_delta_mtd[1]); - /* Initialize structures */ - memset(ams_delta_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ ams_delta_mtd->priv = this; From 69a559e7551228352a6987a1bdd86f03504fff83 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 11 Jan 2015 22:07:48 +0900 Subject: [PATCH 156/360] nandsim: remove unused STATE_DATAOUT_STATUS_M and OPT_SMARTMEDIA There is no path to switch to STATE_DATAOUT_STATUS_M state, and OPT_SMARTMEDIA is unused. This is leftover from commit 0be718e5525a73557e76ea1c05b8001dde507049 ("mtd: nand: remove a bunch of unused commands"). Signed-off-by: Akinobu Mita Cc: Artem Bityutskiy Cc: David Woodhouse Cc: Brian Norris Cc: linux-mtd@lists.infradead.org Signed-off-by: Brian Norris --- drivers/mtd/nand/nandsim.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ab5bbf56743944..a8fa8dad9b23ae 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -245,7 +245,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define STATE_DATAOUT 0x00001000 /* waiting for page data output */ #define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ #define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ -#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */ #define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ /* Previous operation is done, ready to accept new requests */ @@ -269,7 +268,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ #define OPT_PAGE512 0x00000002 /* 512-byte page chips */ #define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ -#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ #define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ #define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ @@ -1096,8 +1094,6 @@ static char *get_state_name(uint32_t state) return "STATE_DATAOUT_ID"; case STATE_DATAOUT_STATUS: return "STATE_DATAOUT_STATUS"; - case STATE_DATAOUT_STATUS_M: - return "STATE_DATAOUT_STATUS_M"; case STATE_READY: return "STATE_READY"; case STATE_UNKNOWN: @@ -1865,7 +1861,6 @@ static void switch_state(struct nandsim *ns) break; case STATE_DATAOUT_STATUS: - case STATE_DATAOUT_STATUS_M: ns->regs.count = ns->regs.num = 0; break; @@ -2005,7 +2000,6 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) } if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS - || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M || NS_STATE(ns->state) == STATE_DATAOUT) { int row = ns->regs.row; From 0d5484b1c3db8a3870c6100deeb4678594433b2c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Oct 2014 00:30:58 +0200 Subject: [PATCH 157/360] dmaengine: Move dma_get_slave_caps() implementation to dmaengine.c The function is too big to be a static inline. Signed-off-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 33 +++++++++++++++++++++++++++++++++ include/linux/dmaengine.h | 32 +------------------------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 30211f9791b771..f15712f2fec6c0 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -479,6 +479,39 @@ static void dma_channel_rebalance(void) } } +int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) +{ + struct dma_device *device; + + if (!chan || !caps) + return -EINVAL; + + device = chan->device; + + /* check if the channel supports slave transactions */ + if (!test_bit(DMA_SLAVE, device->cap_mask.bits)) + return -ENXIO; + + /* + * Check whether it reports it uses the generic slave + * capabilities, if not, that means it doesn't support any + * kind of slave capabilities reporting. + */ + if (!device->directions) + return -ENXIO; + + caps->src_addr_widths = device->src_addr_widths; + caps->dst_addr_widths = device->dst_addr_widths; + caps->directions = device->directions; + caps->residue_granularity = device->residue_granularity; + + caps->cmd_pause = !!device->device_pause; + caps->cmd_terminate = !!device->device_terminate_all; + + return 0; +} +EXPORT_SYMBOL_GPL(dma_get_slave_caps); + static struct dma_chan *private_candidate(const dma_cap_mask_t *mask, struct dma_device *dev, dma_filter_fn fn, void *fn_param) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 6d34ce91036ca8..1b4842bb3890f0 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -758,37 +758,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg( src_sg, src_nents, flags); } -static inline int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) -{ - struct dma_device *device; - - if (!chan || !caps) - return -EINVAL; - - device = chan->device; - - /* check if the channel supports slave transactions */ - if (!test_bit(DMA_SLAVE, device->cap_mask.bits)) - return -ENXIO; - - /* - * Check whether it reports it uses the generic slave - * capabilities, if not, that means it doesn't support any - * kind of slave capabilities reporting. - */ - if (!device->directions) - return -ENXIO; - - caps->src_addr_widths = device->src_addr_widths; - caps->dst_addr_widths = device->dst_addr_widths; - caps->directions = device->directions; - caps->residue_granularity = device->residue_granularity; - - caps->cmd_pause = !!device->device_pause; - caps->cmd_terminate = !!device->device_terminate_all; - - return 0; -} +int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); static inline int dmaengine_terminate_all(struct dma_chan *chan) { From a0a51a64f6a3e718e6d461f11d72d9ce41c706d6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 13 Jan 2015 21:16:22 +0100 Subject: [PATCH 158/360] dmaengine: sa11x0: Fix warning and compilation errors The sa11x0_dma_resume conflicts between the dmaengine device_resume callback and the dev_pm_ops resume implementation. Also remove some unused variables at the same time. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/sa11x0-dma.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index e229c62d7bad55..5adf5407a8cb83 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -669,8 +669,8 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic( return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } -static int sa11x0_dma_slave_config(struct dma_chan *chan, - struct dma_slave_config *cfg) +static int sa11x0_dma_device_config(struct dma_chan *chan, + struct dma_slave_config *cfg) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); @@ -706,14 +706,13 @@ static int sa11x0_dma_slave_config(struct dma_chan *chan, return 0; } -static int sa11x0_dma_pause(struct dma_chan *chan) +static int sa11x0_dma_device_pause(struct dma_chan *chan) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; LIST_HEAD(head); unsigned long flags; - int ret; dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc); spin_lock_irqsave(&c->vc.lock, flags); @@ -734,14 +733,13 @@ static int sa11x0_dma_pause(struct dma_chan *chan) return 0; } -static int sa11x0_dma_resume(struct dma_chan *chan) +static int sa11x0_dma_device_resume(struct dma_chan *chan) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; LIST_HEAD(head); unsigned long flags; - int ret; dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc); spin_lock_irqsave(&c->vc.lock, flags); @@ -762,14 +760,13 @@ static int sa11x0_dma_resume(struct dma_chan *chan) return 0; } -static int sa11x0_dma_terminate_all(struct dma_chan *chan) +static int sa11x0_dma_device_terminate_all(struct dma_chan *chan) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_phy *p; LIST_HEAD(head); unsigned long flags; - int ret; dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc); /* Clear the tx descriptor lists */ @@ -840,10 +837,10 @@ static int sa11x0_dma_init_dmadev(struct dma_device *dmadev, dmadev->dev = dev; dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources; dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources; - dmadev->device_config = sa11x0_dma_slave_config; - dmadev->device_pause = sa11x0_dma_pause; - dmadev->device_resume = sa11x0_dma_resume; - dmadev->device_terminate_all = sa11x0_dma_terminate_all; + dmadev->device_config = sa11x0_dma_device_config; + dmadev->device_pause = sa11x0_dma_device_pause; + dmadev->device_resume = sa11x0_dma_device_resume; + dmadev->device_terminate_all = sa11x0_dma_device_terminate_all; dmadev->device_tx_status = sa11x0_dma_tx_status; dmadev->device_issue_pending = sa11x0_dma_issue_pending; From 848e10bb521eca333f4f912cf70efbd5b0f73c32 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 13 Jan 2015 21:16:23 +0100 Subject: [PATCH 159/360] dmaengine: s3c24xx: Fix typo A typo has been introduced in the spin_unlock_irqrestore function. Fix it. Signed-off-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/s3c24xx-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 231f76acebde59..4d5a84815ba7bd 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -406,7 +406,7 @@ static int s3c24xx_dma_set_runtime_config(struct dma_chan *chan, s3cchan->cfg = *config; out: - spin_lock_irqrestore(&s3cchan->vc.lock, flags); + spin_unlock_irqrestore(&s3cchan->vc.lock, flags); return ret; } From 4d76bbed2d8d9f7bf8bca31e64ef977e015a86fa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Jan 2015 22:17:03 +0100 Subject: [PATCH 160/360] dmaengine: coh901318: fix function return types build warnings A recent patch that removed coh901318_control() replaced it with a number of pointers to existing functions, but those unfortunately have the wrong return type and need to be changed to return an 'int' with an error value rather than a 'void' to avoid these build warnings: drivers/dma/coh901318.c:2697:32: warning: assignment from incompatible pointer type base->dma_slave.device_config = coh901318_dma_set_runtimeconfig; ^ drivers/dma/coh901318.c:2698:31: warning: assignment from incompatible pointer type base->dma_slave.device_pause = coh901318_pause; ^ drivers/dma/coh901318.c:2699:32: warning: assignment from incompatible pointer type base->dma_slave.device_resume = coh901318_resume The coh901318_base_init function has the correct return type already, but needs to be marked 'static' to avoid a sparse warning about a missing declaration. Signed-off-by: Arnd Bergmann Fixes: 6782af118b6c ("dmaengine: coh901318: Split device_control") Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/coh901318.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 418e4e4fb7ba77..fd22dd36985f2b 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1690,7 +1690,7 @@ static u32 coh901318_get_bytes_left(struct dma_chan *chan) * Pauses a transfer without losing data. Enables power save. * Use this function in conjunction with coh901318_resume. */ -static void coh901318_pause(struct dma_chan *chan) +static int coh901318_pause(struct dma_chan *chan) { u32 val; unsigned long flags; @@ -1730,12 +1730,13 @@ static void coh901318_pause(struct dma_chan *chan) enable_powersave(cohc); spin_unlock_irqrestore(&cohc->lock, flags); + return 0; } /* Resumes a transfer that has been stopped via 300_dma_stop(..). Power save is handled. */ -static void coh901318_resume(struct dma_chan *chan) +static int coh901318_resume(struct dma_chan *chan) { u32 val; unsigned long flags; @@ -1760,6 +1761,7 @@ static void coh901318_resume(struct dma_chan *chan) } spin_unlock_irqrestore(&cohc->lock, flags); + return 0; } bool coh901318_filter_id(struct dma_chan *chan, void *chan_id) @@ -2512,8 +2514,8 @@ static const struct burst_table burst_sizes[] = { }, }; -static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, - struct dma_slave_config *config) +static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan, + struct dma_slave_config *config) { struct coh901318_chan *cohc = to_coh901318_chan(chan); dma_addr_t addr; @@ -2533,7 +2535,7 @@ static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, maxburst = config->dst_maxburst; } else { dev_err(COHC_2_DEV(cohc), "illegal channel mode\n"); - return; + return -EINVAL; } dev_dbg(COHC_2_DEV(cohc), "configure channel for %d byte transfers\n", @@ -2579,7 +2581,7 @@ static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, default: dev_err(COHC_2_DEV(cohc), "bad runtimeconfig: alien address width\n"); - return; + return -EINVAL; } ctrl |= burst_sizes[i].reg; @@ -2589,10 +2591,12 @@ static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, cohc->addr = addr; cohc->ctrl = ctrl; + + return 0; } -void coh901318_base_init(struct dma_device *dma, const int *pick_chans, - struct coh901318_base *base) +static void coh901318_base_init(struct dma_device *dma, const int *pick_chans, + struct coh901318_base *base) { int chans_i; int i = 0; From fdb8df9933632e177621daf60da74fc693a8c7d1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 Jan 2015 13:54:27 +0200 Subject: [PATCH 161/360] dmaengine: Add dma_get_slave_caps() inline stub when !CONFIG_DMA_ENGINE Commit 0d5484b1c3db8a38 ("dmaengine: Move dma_get_slave_caps() implementation to dmaengine.c") turned the inline dma_get_slave_caps() function into an external function without adding an inline stub for the cases where CONFIG_DMA_ENGINE isn't set. This breaks compilation of drivers using the DMA engine API when CONFIG_DMA_ENGINE isn't set. Add an inline stub to fix compilation. Signed-off-by: Laurent Pinchart Fixes: 0d5484b1c3db ("dmaengine: Move dma_get_slave_caps() implementation to dmaengine.c") Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 1b4842bb3890f0..50745e3a8a3f0b 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -758,8 +758,6 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg( src_sg, src_nents, flags); } -int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); - static inline int dmaengine_terminate_all(struct dma_chan *chan) { if (chan->device->device_terminate_all) @@ -1048,6 +1046,7 @@ struct dma_chan *dma_request_slave_channel_reason(struct device *dev, const char *name); struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); void dma_release_channel(struct dma_chan *chan); +int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); #else static inline struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type) { @@ -1082,6 +1081,11 @@ static inline struct dma_chan *dma_request_slave_channel(struct device *dev, static inline void dma_release_channel(struct dma_chan *chan) { } +static inline int dma_get_slave_caps(struct dma_chan *chan, + struct dma_slave_caps *caps) +{ + return -ENXIO; +} #endif /* --- DMA device --- */ From fcc87a95195236b0935183361a72e4a98bf577d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 16 Dec 2014 22:46:56 +0100 Subject: [PATCH 162/360] mtd: spi-nor: support for (GigaDevice) GD25Q128B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index ea196c18dec980..b6a5a0c269e1d2 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -538,6 +538,7 @@ static const struct spi_device_id spi_nor_ids[] = { /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) }, /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, From cfe4af3aac3bd63e9caf548157805ca46ad7b073 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 13 Jan 2015 20:14:15 -0200 Subject: [PATCH 163/360] mtd: fsl-quadspi: Fix module unbound When removing the fsl-quadspi module and running 'cat /proc/mtd' afterwards, we see garbage data like: $ rmmod fsl-quadspi $ cat /proc/mtd dev: size erasesize name mtd0: 00000000 00000000 "(null)" mtd0: 00000000 00000000 "(null)" mtd0: 00000000 00000000 "(null)" ... mtd0: a22296c6c756e28 00000000 "(null)" mtd0: a22296c6c756e28 3064746d "(null)" If we continue doing multiple module load/unload operations, then it will also lead to a kernel crash. The reason for this is due to the wrong mtd index used in mtd_device_unregister() in the remove function. We need to keep the mtd unregister index aligned with the one used in the probe function, which means we need to take into account the 'has_second_chip' property. By doing so we can guarantee that the mtd index is the same in the registration and unregistration functions. With this patch applied we can load/unload the fsl-quadspi driver several times and it will result in no crash. Signed-off-by: Fabio Estevam Acked-by: Huang Shijie Tested-by: Frank Li Acked-by: Allen Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 9d6080079a4529..a46bea33580f49 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -227,6 +227,7 @@ struct fsl_qspi { u32 nor_num; u32 clk_rate; unsigned int chip_base_addr; /* We may support two chips. */ + bool has_second_chip; }; static inline int is_vybrid_qspi(struct fsl_qspi *q) @@ -783,7 +784,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) struct spi_nor *nor; struct mtd_info *mtd; int ret, i = 0; - bool has_second_chip = false; const struct of_device_id *of_id = of_match_device(fsl_qspi_dt_ids, &pdev->dev); @@ -860,14 +860,14 @@ static int fsl_qspi_probe(struct platform_device *pdev) goto irq_failed; if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) - has_second_chip = true; + q->has_second_chip = true; /* iterate the subnodes. */ for_each_available_child_of_node(dev->of_node, np) { char modalias[40]; /* skip the holes */ - if (!has_second_chip) + if (!q->has_second_chip) i *= 2; nor = &q->nor[i]; @@ -943,9 +943,12 @@ static int fsl_qspi_probe(struct platform_device *pdev) return 0; last_init_failed: - for (i = 0; i < q->nor_num; i++) + for (i = 0; i < q->nor_num; i++) { + /* skip the holes */ + if (!q->has_second_chip) + i *= 2; mtd_device_unregister(&q->mtd[i]); - + } irq_failed: clk_disable_unprepare(q->clk); clk_failed: @@ -960,8 +963,12 @@ static int fsl_qspi_remove(struct platform_device *pdev) struct fsl_qspi *q = platform_get_drvdata(pdev); int i; - for (i = 0; i < q->nor_num; i++) + for (i = 0; i < q->nor_num; i++) { + /* skip the holes */ + if (!q->has_second_chip) + i *= 2; mtd_device_unregister(&q->mtd[i]); + } /* disable the hardware */ writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); From 267d46e635c575e9c8b2932d9617266e6e67ee99 Mon Sep 17 00:00:00 2001 From: "Wu, Josh" Date: Wed, 14 Jan 2015 11:50:46 +0800 Subject: [PATCH 164/360] mtd: atmel_nand: return max bitflips in all sectors in pmecc_correction() atmel_nand_pmecc_read_page() will return the total bitflips in this page. This is incorrect. As one nand page includes multiple ecc sectors, that will cause the returned total bitflips exceed ecc capablity. So this patch will make pmecc_correct() return the max bitflips of all sectors in the page. That also makes atmel_nand_pmecc_read_page() return the max bitflips. Signed-off-by: Josh Wu Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index a345e7b2463a3f..7346d16cf61a62 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -847,7 +847,7 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, struct atmel_nand_host *host = nand_chip->priv; int i, err_nbr; uint8_t *buf_pos; - int total_err = 0; + int max_bitflips = 0; for (i = 0; i < nand_chip->ecc.total; i++) if (ecc[i] != 0xff) @@ -874,13 +874,13 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, pmecc_correct_data(mtd, buf_pos, ecc, i, nand_chip->ecc.bytes, err_nbr); mtd->ecc_stats.corrected += err_nbr; - total_err += err_nbr; + max_bitflips = max_t(int, max_bitflips, err_nbr); } } pmecc_stat >>= 1; } - return total_err; + return max_bitflips; } static void pmecc_enable(struct atmel_nand_host *host, int ecc_op) From 51585778f63adaadbc67399e172fcf11daa9f032 Mon Sep 17 00:00:00 2001 From: "Wu, Josh" Date: Mon, 19 Jan 2015 16:33:06 +0800 Subject: [PATCH 165/360] mtd: atmel_nand: introduce a new compatible string for sama5d4 chip Since in SAMA5D4 chip, the PMECC can correct bit flips in erased page. So we add a DT property to indicate this hardware character. If the PMECC support correct bitflip erased page (all data are 0xff). Then we can use the PMECC correct the page and skip the erased page check. Signed-off-by: Josh Wu Signed-off-by: Brian Norris --- .../devicetree/bindings/mtd/atmel-nand.txt | 2 +- drivers/mtd/nand/atmel_nand.c | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index 1fe6dde9849942..7d4c8eb775a5ff 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -1,7 +1,7 @@ Atmel NAND flash Required properties: -- compatible : "atmel,at91rm9200-nand". +- compatible : should be "atmel,at91rm9200-nand" or "atmel,sama5d4-nand". - reg : should specify localbus address and size used for the chip, and hardware ECC controller if available. If the hardware ECC is PMECC, it should contain address and size for diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 7346d16cf61a62..d93c849b70b5cf 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -63,6 +63,10 @@ module_param(on_flash_bbt, int, 0); #include "atmel_nand_ecc.h" /* Hardware ECC registers */ #include "atmel_nand_nfc.h" /* Nand Flash Controller definition */ +struct atmel_nand_caps { + bool pmecc_correct_erase_page; +}; + /* oob layout for large page size * bad block info is on bytes 0 and 1 * the bytes have to be consecutives to avoid @@ -124,6 +128,7 @@ struct atmel_nand_host { struct atmel_nfc *nfc; + struct atmel_nand_caps *caps; bool has_pmecc; u8 pmecc_corr_cap; u16 pmecc_sector_size; @@ -849,6 +854,10 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, uint8_t *buf_pos; int max_bitflips = 0; + /* If can correct bitfilps from erased page, do the normal check */ + if (host->caps->pmecc_correct_erase_page) + goto normal_check; + for (i = 0; i < nand_chip->ecc.total; i++) if (ecc[i] != 0xff) goto normal_check; @@ -1474,6 +1483,8 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); } +static const struct of_device_id atmel_nand_dt_ids[]; + static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { @@ -1483,6 +1494,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host, struct atmel_nand_data *board = &host->board; enum of_gpio_flags flags = 0; + host->caps = (struct atmel_nand_caps *) + of_match_device(atmel_nand_dt_ids, host->dev)->data; + if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { if (val >= 32) { dev_err(host->dev, "invalid addr-offset %u\n", val); @@ -2288,8 +2302,17 @@ static int atmel_nand_remove(struct platform_device *pdev) return 0; } +static struct atmel_nand_caps at91rm9200_caps = { + .pmecc_correct_erase_page = false, +}; + +static struct atmel_nand_caps sama5d4_caps = { + .pmecc_correct_erase_page = true, +}; + static const struct of_device_id atmel_nand_dt_ids[] = { - { .compatible = "atmel,at91rm9200-nand" }, + { .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps }, + { .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps }, { /* sentinel */ } }; From 7f870c81a068fd1e69fe556f7fe85a5ece144b0c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 28 Dec 2014 12:35:05 +0200 Subject: [PATCH 166/360] virtio_pci: drop virtio_config dependency virtio_pci does not depend on virtio_config: let's not include it, users can pull it in as necessary. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- include/uapi/linux/virtio_pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index 35b552c7f33027..509d630f04f4a5 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -39,7 +39,7 @@ #ifndef _LINUX_VIRTIO_PCI_H #define _LINUX_VIRTIO_PCI_H -#include +#include #ifndef VIRTIO_PCI_NO_LEGACY From 7754f53e944122fa95ef36039d3a5009910ce6fc Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 12 Jan 2015 16:23:37 +0200 Subject: [PATCH 167/360] virtio/9p: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/9p needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- net/9p/trans_virtio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index daa749c8b3fbea..d8e376a5f0f13d 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -524,6 +524,12 @@ static int p9_virtio_probe(struct virtio_device *vdev) int err; struct virtio_chan *chan; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL); if (!chan) { pr_err("Failed to allocate virtio 9P channel\n"); From a4379fd841cc1af60ef495b8e5598b1f7327ec0c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 12 Jan 2015 16:23:37 +0200 Subject: [PATCH 168/360] virtio/blk: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/blk needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index cdfbd21e359751..b72c11a10f409b 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -575,6 +575,12 @@ static int virtblk_probe(struct virtio_device *vdev) u16 min_io_size; u8 physical_block_exp, alignment_offset; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS), GFP_KERNEL); if (err < 0) From 011f0e7a6944855436fb29c7a7e6c3c89c95f8fb Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 12 Jan 2015 16:23:37 +0200 Subject: [PATCH 169/360] virtio/console: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/console needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index de03df9dd7c961..26afb56a807300 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1986,6 +1986,12 @@ static int virtcons_probe(struct virtio_device *vdev) bool multiport; bool early = early_put_chars != NULL; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + /* Ensure to read early_put_chars now */ barrier(); From 6ba422489bcaebd89142cc0aeae9095d03c8301a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 12 Jan 2015 16:23:37 +0200 Subject: [PATCH 170/360] virtio/net: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/net needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/net/virtio_net.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 5ca97713bfb33b..9bc107200709fd 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1713,6 +1713,12 @@ static int virtnet_probe(struct virtio_device *vdev) struct virtnet_info *vi; u16 max_queue_pairs; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + if (!virtnet_validate_features(vdev)) return -EINVAL; From 8cab3cd6ad622f070b18cd1c2e3b97e1d3806629 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 12 Jan 2015 16:23:37 +0200 Subject: [PATCH 171/360] virtio/scsi: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/scsi needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/scsi/virtio_scsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index c52bb5dfaedb1d..f164f24a4a556c 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -950,6 +950,12 @@ static int virtscsi_probe(struct virtio_device *vdev) u32 num_queues; struct scsi_host_template *hostt; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + /* We need to know how many queues before we allocate. */ num_queues = virtscsi_config_get(vdev, num_queues) ? : 1; From 2d9becc1e0c6f86e222e1955e39302d304ad274b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 12 Jan 2015 16:23:37 +0200 Subject: [PATCH 172/360] virtio/balloon: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/balloon needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_balloon.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 50c5f42d7a9f30..3176ea4028a83c 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -466,6 +466,12 @@ static int virtballoon_probe(struct virtio_device *vdev) struct virtio_balloon *vb; int err; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); if (!vb) { err = -ENOMEM; From 54cfe08b5f352d9aa4979e2434e85907c84af07a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 14 Dec 2014 14:54:17 +0200 Subject: [PATCH 173/360] mn10300: drop dead code pci-iomap.c was (apparently, mistakenly) reintroduced as part of commit 83c2dc15ce824450e7044b9f90cd529c25747ae0 MN10300: Handle cacheable PCI regions in pci_iomap() probably as side-effect of forward-porting the patch from an old kernel. It's not really needed: the generic pci_iomap does the right thing here. The new file isn't compiled so it's safe to drop. Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Cc: trivial@kernel.org Cc: David Howells Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- arch/mn10300/unit-asb2305/pci-iomap.c | 35 --------------------------- 1 file changed, 35 deletions(-) delete mode 100644 arch/mn10300/unit-asb2305/pci-iomap.c diff --git a/arch/mn10300/unit-asb2305/pci-iomap.c b/arch/mn10300/unit-asb2305/pci-iomap.c deleted file mode 100644 index bd65dae17f32b6..00000000000000 --- a/arch/mn10300/unit-asb2305/pci-iomap.c +++ /dev/null @@ -1,35 +0,0 @@ -/* ASB2305 PCI I/O mapping handler - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ -#include -#include - -/* - * Create a virtual mapping cookie for a PCI BAR (memory or IO) - */ -void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) -{ - resource_size_t start = pci_resource_start(dev, bar); - resource_size_t len = pci_resource_len(dev, bar); - unsigned long flags = pci_resource_flags(dev, bar); - - if (!len || !start) - return NULL; - - if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) { - if (flags & IORESOURCE_CACHEABLE && !(flags & IORESOURCE_IO)) - return ioremap(start, len); - else - return ioremap_nocache(start, len); - } - - return NULL; -} -EXPORT_SYMBOL(pci_iomap); From eb29d8d2aad70636ea23810b4868693673d630d5 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 29 May 2013 11:52:21 +0930 Subject: [PATCH 174/360] pci: add pci_iomap_range Virtio drivers should map the part of the BAR they need, not necessarily all of it. Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Acked-by: Arnd Bergmann Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- include/asm-generic/pci_iomap.h | 10 ++++++++++ lib/pci_iomap.c | 35 ++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/include/asm-generic/pci_iomap.h b/include/asm-generic/pci_iomap.h index ce37349860fece..7389c87116a090 100644 --- a/include/asm-generic/pci_iomap.h +++ b/include/asm-generic/pci_iomap.h @@ -15,6 +15,9 @@ struct pci_dev; #ifdef CONFIG_PCI /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); +extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, + unsigned long offset, + unsigned long maxlen); /* Create a virtual mapping cookie for a port on a given PCI device. * Do not call this directly, it exists to make it easier for architectures * to override */ @@ -30,6 +33,13 @@ static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned lon { return NULL; } + +static inline void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, + unsigned long offset, + unsigned long maxlen) +{ + return NULL; +} #endif #endif /* __ASM_GENERIC_IO_H */ diff --git a/lib/pci_iomap.c b/lib/pci_iomap.c index 0d83ea8a960542..bcce5f14931013 100644 --- a/lib/pci_iomap.c +++ b/lib/pci_iomap.c @@ -10,10 +10,11 @@ #ifdef CONFIG_PCI /** - * pci_iomap - create a virtual mapping cookie for a PCI BAR + * pci_iomap_range - create a virtual mapping cookie for a PCI BAR * @dev: PCI device that owns the BAR * @bar: BAR number - * @maxlen: length of the memory to map + * @offset: map memory at the given offset in BAR + * @maxlen: max length of the memory to map * * Using this function you will get a __iomem address to your device BAR. * You can access it using ioread*() and iowrite*(). These functions hide @@ -21,16 +22,21 @@ * you expect from them in the correct way. * * @maxlen specifies the maximum length to map. If you want to get access to - * the complete BAR without checking for its length first, pass %0 here. + * the complete BAR from offset to the end, pass %0 here. * */ -void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +void __iomem *pci_iomap_range(struct pci_dev *dev, + int bar, + unsigned long offset, + unsigned long maxlen) { resource_size_t start = pci_resource_start(dev, bar); resource_size_t len = pci_resource_len(dev, bar); unsigned long flags = pci_resource_flags(dev, bar); - if (!len || !start) + if (len <= offset || !start) return NULL; + len -= offset; + start += offset; if (maxlen && len > maxlen) len = maxlen; if (flags & IORESOURCE_IO) @@ -43,6 +49,25 @@ void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) /* What? */ return NULL; } +EXPORT_SYMBOL(pci_iomap_range); +/** + * pci_iomap - create a virtual mapping cookie for a PCI BAR + * @dev: PCI device that owns the BAR + * @bar: BAR number + * @maxlen: length of the memory to map + * + * Using this function you will get a __iomem address to your device BAR. + * You can access it using ioread*() and iowrite*(). These functions hide + * the details if this is a MMIO or PIO address space and will just do what + * you expect from them in the correct way. + * + * @maxlen specifies the maximum length to map. If you want to get access to + * the complete BAR without checking for its length first, pass %0 here. + * */ +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + return pci_iomap_range(dev, bar, 0, maxlen); +} EXPORT_SYMBOL(pci_iomap); #endif /* CONFIG_PCI */ From 8cfc99b58366ea9f391fe0da7d16940ca6a1d9c0 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 29 May 2013 11:52:21 +0930 Subject: [PATCH 175/360] s390: add pci_iomap_range Virtio drivers should map the part of the range they need, not necessarily all of it. To this end, support mapping ranges within BAR on s390. Since multiple ranges can now be mapped within a BAR, we keep track of the number of mappings created, and only clear out the mapping for a BAR when this number reaches 0. Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Tested-by: Sebastian Ott Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- arch/s390/include/asm/pci_io.h | 1 + arch/s390/pci/pci.c | 34 +++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/arch/s390/include/asm/pci_io.h b/arch/s390/include/asm/pci_io.h index f664e96f48c7bd..1a9a98de5bdebc 100644 --- a/arch/s390/include/asm/pci_io.h +++ b/arch/s390/include/asm/pci_io.h @@ -16,6 +16,7 @@ struct zpci_iomap_entry { u32 fh; u8 bar; + u16 count; }; extern struct zpci_iomap_entry *zpci_iomap_start; diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 3290f11ae1d9ca..753a5673195112 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -259,7 +259,10 @@ void __iowrite64_copy(void __iomem *to, const void *from, size_t count) } /* Create a virtual mapping cookie for a PCI BAR */ -void __iomem *pci_iomap(struct pci_dev *pdev, int bar, unsigned long max) +void __iomem *pci_iomap_range(struct pci_dev *pdev, + int bar, + unsigned long offset, + unsigned long max) { struct zpci_dev *zdev = get_zdev(pdev); u64 addr; @@ -270,14 +273,27 @@ void __iomem *pci_iomap(struct pci_dev *pdev, int bar, unsigned long max) idx = zdev->bars[bar].map_idx; spin_lock(&zpci_iomap_lock); - zpci_iomap_start[idx].fh = zdev->fh; - zpci_iomap_start[idx].bar = bar; + if (zpci_iomap_start[idx].count++) { + BUG_ON(zpci_iomap_start[idx].fh != zdev->fh || + zpci_iomap_start[idx].bar != bar); + } else { + zpci_iomap_start[idx].fh = zdev->fh; + zpci_iomap_start[idx].bar = bar; + } + /* Detect overrun */ + BUG_ON(!zpci_iomap_start[idx].count); spin_unlock(&zpci_iomap_lock); addr = ZPCI_IOMAP_ADDR_BASE | ((u64) idx << 48); - return (void __iomem *) addr; + return (void __iomem *) addr + offset; } -EXPORT_SYMBOL_GPL(pci_iomap); +EXPORT_SYMBOL_GPL(pci_iomap_range); + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + return pci_iomap_range(dev, bar, 0, maxlen); +} +EXPORT_SYMBOL(pci_iomap); void pci_iounmap(struct pci_dev *pdev, void __iomem *addr) { @@ -285,8 +301,12 @@ void pci_iounmap(struct pci_dev *pdev, void __iomem *addr) idx = (((__force u64) addr) & ~ZPCI_IOMAP_ADDR_BASE) >> 48; spin_lock(&zpci_iomap_lock); - zpci_iomap_start[idx].fh = 0; - zpci_iomap_start[idx].bar = 0; + /* Detect underrun */ + BUG_ON(!zpci_iomap_start[idx].count); + if (!--zpci_iomap_start[idx].count) { + zpci_iomap_start[idx].fh = 0; + zpci_iomap_start[idx].bar = 0; + } spin_unlock(&zpci_iomap_lock); } EXPORT_SYMBOL_GPL(pci_iounmap); From 2bd56afd44123cea3741c7a46ddd96a46c92b8d9 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 2 Jan 2015 14:47:39 -0500 Subject: [PATCH 176/360] virtio_pci: drop useless del_vqs call Device VQs were getting freed twice: once in every device's removal functions, and then again in virtio_pci_legacy_remove(). The ones in devices are called first, so drop the useless second call. Signed-off-by: Sasha Levin Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci_legacy.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index a5486e65e04bd5..19f93096739a00 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -309,7 +309,6 @@ void virtio_pci_legacy_remove(struct pci_dev *pci_dev) unregister_virtio_device(&vp_dev->vdev); - vp_del_vqs(&vp_dev->vdev); pci_iounmap(pci_dev, vp_dev->ioaddr); pci_release_regions(pci_dev); pci_disable_device(pci_dev); From ff31d2e28549c84d53252b3c36b6f0ba18b78697 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 13 Jan 2015 11:23:32 +0200 Subject: [PATCH 177/360] virtio_pci: move probe/remove code to common Most of initialization is device-independent. Let's move it to common. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci_common.c | 69 ++++++++++++++++++++++++++- drivers/virtio/virtio_pci_common.h | 5 +- drivers/virtio/virtio_pci_legacy.c | 75 ++++-------------------------- 3 files changed, 77 insertions(+), 72 deletions(-) diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 9756f21b809e08..457cbe29c8c45b 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -464,15 +464,80 @@ static const struct pci_device_id virtio_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, virtio_pci_id_table); +static void virtio_pci_release_dev(struct device *_d) +{ + struct virtio_device *vdev = dev_to_virtio(_d); + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + /* As struct device is a kobject, it's not safe to + * free the memory (including the reference counter itself) + * until it's release callback. */ + kfree(vp_dev); +} + static int virtio_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { - return virtio_pci_legacy_probe(pci_dev, id); + struct virtio_pci_device *vp_dev; + int rc; + + /* allocate our structure and fill it out */ + vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL); + if (!vp_dev) + return -ENOMEM; + + pci_set_drvdata(pci_dev, vp_dev); + vp_dev->vdev.dev.parent = &pci_dev->dev; + vp_dev->vdev.dev.release = virtio_pci_release_dev; + vp_dev->pci_dev = pci_dev; + INIT_LIST_HEAD(&vp_dev->virtqueues); + spin_lock_init(&vp_dev->lock); + + /* Disable MSI/MSIX to bring device to a known good state. */ + pci_msi_off(pci_dev); + + /* enable the device */ + rc = pci_enable_device(pci_dev); + if (rc) + goto err_enable_device; + + rc = pci_request_regions(pci_dev, "virtio-pci"); + if (rc) + goto err_request_regions; + + rc = virtio_pci_legacy_probe(vp_dev); + if (rc) + goto err_probe; + + pci_set_master(pci_dev); + + rc = register_virtio_device(&vp_dev->vdev); + if (rc) + goto err_register; + + return 0; + +err_register: + virtio_pci_legacy_remove(vp_dev); +err_probe: + pci_release_regions(pci_dev); +err_request_regions: + pci_disable_device(pci_dev); +err_enable_device: + kfree(vp_dev); + return rc; } static void virtio_pci_remove(struct pci_dev *pci_dev) { - virtio_pci_legacy_remove(pci_dev); + struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); + + unregister_virtio_device(&vp_dev->vdev); + + virtio_pci_legacy_remove(pci_dev); + + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); } static struct pci_driver virtio_pci_driver = { diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 5a497289b7e9c3..2b1e70db44a037 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -127,8 +127,7 @@ const char *vp_bus_name(struct virtio_device *vdev); */ int vp_set_vq_affinity(struct virtqueue *vq, int cpu); -int virtio_pci_legacy_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id); -void virtio_pci_legacy_remove(struct pci_dev *pci_dev); +int virtio_pci_legacy_probe(struct virtio_pci_device *); +void virtio_pci_legacy_remove(struct virtio_pci_device *); #endif diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index 19f93096739a00..256a5278a515de 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -211,23 +211,10 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .set_vq_affinity = vp_set_vq_affinity, }; -static void virtio_pci_release_dev(struct device *_d) -{ - struct virtio_device *vdev = dev_to_virtio(_d); - struct virtio_pci_device *vp_dev = to_vp_device(vdev); - - /* As struct device is a kobject, it's not safe to - * free the memory (including the reference counter itself) - * until it's release callback. */ - kfree(vp_dev); -} - /* the PCI probing function */ -int virtio_pci_legacy_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) +int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev) { - struct virtio_pci_device *vp_dev; - int err; + struct pci_dev *pci_dev = vp_dev->pci_dev; /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f) @@ -239,41 +226,12 @@ int virtio_pci_legacy_probe(struct pci_dev *pci_dev, return -ENODEV; } - /* allocate our structure and fill it out */ - vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL); - if (vp_dev == NULL) - return -ENOMEM; - - vp_dev->vdev.dev.parent = &pci_dev->dev; - vp_dev->vdev.dev.release = virtio_pci_release_dev; - vp_dev->vdev.config = &virtio_pci_config_ops; - vp_dev->pci_dev = pci_dev; - INIT_LIST_HEAD(&vp_dev->virtqueues); - spin_lock_init(&vp_dev->lock); - - /* Disable MSI/MSIX to bring device to a known good state. */ - pci_msi_off(pci_dev); - - /* enable the device */ - err = pci_enable_device(pci_dev); - if (err) - goto out; - - err = pci_request_regions(pci_dev, "virtio-pci"); - if (err) - goto out_enable_device; - vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0); - if (vp_dev->ioaddr == NULL) { - err = -ENOMEM; - goto out_req_regions; - } + if (!vp_dev->ioaddr) + return -ENOMEM; vp_dev->isr = vp_dev->ioaddr + VIRTIO_PCI_ISR; - pci_set_drvdata(pci_dev, vp_dev); - pci_set_master(pci_dev); - /* we use the subsystem vendor/device id as the virtio vendor/device * id. this allows us to use the same PCI vendor/device id for all * virtio devices and to identify the particular virtio driver by @@ -281,35 +239,18 @@ int virtio_pci_legacy_probe(struct pci_dev *pci_dev, vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor; vp_dev->vdev.id.device = pci_dev->subsystem_device; + vp_dev->vdev.config = &virtio_pci_config_ops; + vp_dev->config_vector = vp_config_vector; vp_dev->setup_vq = setup_vq; vp_dev->del_vq = del_vq; - /* finally register the virtio device */ - err = register_virtio_device(&vp_dev->vdev); - if (err) - goto out_set_drvdata; - return 0; - -out_set_drvdata: - pci_iounmap(pci_dev, vp_dev->ioaddr); -out_req_regions: - pci_release_regions(pci_dev); -out_enable_device: - pci_disable_device(pci_dev); -out: - kfree(vp_dev); - return err; } -void virtio_pci_legacy_remove(struct pci_dev *pci_dev) +void virtio_pci_legacy_remove(struct virtio_pci_device *vp_dev) { - struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); - - unregister_virtio_device(&vp_dev->vdev); + struct pci_dev *pci_dev = vp_dev->pci_dev; pci_iounmap(pci_dev, vp_dev->ioaddr); - pci_release_regions(pci_dev); - pci_disable_device(pci_dev); } From 71d70c266c84c4e708bb36b20d0c0a29af42821c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 May 2013 11:52:22 +0930 Subject: [PATCH 178/360] virtio-pci: define layout for virtio 1.0 Based on patches by Michael S. Tsirkin , but I found it hard to follow so changed to use structures which are more self-documenting. Signed-off-by: Rusty Russell Signed-off-by: Michael S. Tsirkin --- include/uapi/linux/virtio_pci.h | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index 509d630f04f4a5..4e054235358f28 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -99,4 +99,66 @@ /* Vector value used to disable MSI for queue */ #define VIRTIO_MSI_NO_VECTOR 0xffff +#ifndef VIRTIO_PCI_NO_MODERN + +/* IDs for different capabilities. Must all exist. */ + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR access */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 +/* Device specific confiuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 + +/* This is the PCI capability header: */ +struct virtio_pci_cap { + __u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + __u8 cap_next; /* Generic PCI field: next ptr. */ + __u8 cap_len; /* Generic PCI field: capability length */ + __u8 type_and_bar; /* Upper 3 bits: bar. + * Lower 3 is VIRTIO_PCI_CAP_*_CFG. */ + __le32 offset; /* Offset within bar. */ + __le32 length; /* Length. */ +}; + +#define VIRTIO_PCI_CAP_BAR_SHIFT 5 +#define VIRTIO_PCI_CAP_BAR_MASK 0x7 +#define VIRTIO_PCI_CAP_TYPE_SHIFT 0 +#define VIRTIO_PCI_CAP_TYPE_MASK 0x7 + +struct virtio_pci_notify_cap { + struct virtio_pci_cap cap; + __le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ +}; + +/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */ +struct virtio_pci_common_cfg { + /* About the whole device. */ + __le32 device_feature_select; /* read-write */ + __le32 device_feature; /* read-only */ + __le32 guest_feature_select; /* read-write */ + __le32 guest_feature; /* read-write */ + __le16 msix_config; /* read-write */ + __le16 num_queues; /* read-only */ + __u8 device_status; /* read-write */ + __u8 config_generation; /* read-only */ + + /* About a specific virtqueue. */ + __le16 queue_select; /* read-write */ + __le16 queue_size; /* read-write, power of 2. */ + __le16 queue_msix_vector; /* read-write */ + __le16 queue_enable; /* read-write */ + __le16 queue_notify_off; /* read-only */ + __le32 queue_desc_lo; /* read-write */ + __le32 queue_desc_hi; /* read-write */ + __le32 queue_avail_lo; /* read-write */ + __le32 queue_avail_hi; /* read-write */ + __le32 queue_used_lo; /* read-write */ + __le32 queue_used_hi; /* read-write */ +}; + +#endif /* VIRTIO_PCI_NO_MODERN */ + #endif From 1fcf0512c9c870e78e1c9898ecb9458593403466 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 11 Dec 2014 13:59:51 +0200 Subject: [PATCH 179/360] virtio_pci: modern driver Lightly tested against qemu. One thing *not* implemented here is separate mappings for descriptor/avail/used rings. That's nice to have, will be done later after we have core support. This also exposes the PCI layout to userspace, and adds macros for PCI layout offsets: QEMU wants it, so why not? Trust, but verify. Signed-off-by: Rusty Russell Signed-off-by: Michael S. Tsirkin --- drivers/virtio/Makefile | 2 +- drivers/virtio/virtio_pci_common.c | 14 +- drivers/virtio/virtio_pci_common.h | 25 +- drivers/virtio/virtio_pci_modern.c | 587 +++++++++++++++++++++++++++++ include/uapi/linux/virtio_pci.h | 7 +- 5 files changed, 625 insertions(+), 10 deletions(-) create mode 100644 drivers/virtio/virtio_pci_modern.c diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index bf5104b5689473..bd230d1c0533cc 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o -virtio_pci-y := virtio_pci_legacy.o virtio_pci_common.o +virtio_pci-y := virtio_pci_modern.o virtio_pci_legacy.o virtio_pci_common.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 457cbe29c8c45b..8ae34a34f3af9a 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -505,7 +505,9 @@ static int virtio_pci_probe(struct pci_dev *pci_dev, if (rc) goto err_request_regions; - rc = virtio_pci_legacy_probe(vp_dev); + rc = virtio_pci_modern_probe(vp_dev); + if (rc == -ENODEV) + rc = virtio_pci_legacy_probe(vp_dev); if (rc) goto err_probe; @@ -518,7 +520,10 @@ static int virtio_pci_probe(struct pci_dev *pci_dev, return 0; err_register: - virtio_pci_legacy_remove(vp_dev); + if (vp_dev->ioaddr) + virtio_pci_legacy_remove(vp_dev); + else + virtio_pci_modern_remove(vp_dev); err_probe: pci_release_regions(pci_dev); err_request_regions: @@ -534,7 +539,10 @@ static void virtio_pci_remove(struct pci_dev *pci_dev) unregister_virtio_device(&vp_dev->vdev); - virtio_pci_legacy_remove(pci_dev); + if (vp_dev->ioaddr) + virtio_pci_legacy_remove(vp_dev); + else + virtio_pci_modern_remove(vp_dev); pci_release_regions(pci_dev); pci_disable_device(pci_dev); diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 2b1e70db44a037..610c43f192306a 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -53,12 +53,29 @@ struct virtio_pci_device { struct virtio_device vdev; struct pci_dev *pci_dev; + /* In legacy mode, these two point to within ->legacy. */ + /* Where to read and clear interrupt */ + u8 __iomem *isr; + + /* Modern only fields */ + /* The IO mapping for the PCI config space (non-legacy mode) */ + struct virtio_pci_common_cfg __iomem *common; + /* Device-specific data (non-legacy mode) */ + void __iomem *device; + + /* So we can sanity-check accesses. */ + size_t device_len; + + /* Capability for when we need to map notifications per-vq. */ + int notify_map_cap; + + /* Multiply queue_notify_off by this value. (non-legacy mode). */ + u32 notify_offset_multiplier; + + /* Legacy only field */ /* the IO mapping for the PCI config space */ void __iomem *ioaddr; - /* the IO mapping for ISR operation */ - void __iomem *isr; - /* a list of queues so we can dispatch IRQs */ spinlock_t lock; struct list_head virtqueues; @@ -129,5 +146,7 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu); int virtio_pci_legacy_probe(struct virtio_pci_device *); void virtio_pci_legacy_remove(struct virtio_pci_device *); +int virtio_pci_modern_probe(struct virtio_pci_device *); +void virtio_pci_modern_remove(struct virtio_pci_device *); #endif diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c new file mode 100644 index 00000000000000..a3d81013e0c2b6 --- /dev/null +++ b/drivers/virtio/virtio_pci_modern.c @@ -0,0 +1,587 @@ +/* + * Virtio PCI driver - modern (virtio 1.0) device support + * + * This module allows virtio devices to be used over a virtual PCI device. + * This can be used with QEMU based VMMs like KVM or Xen. + * + * Copyright IBM Corp. 2007 + * Copyright Red Hat, Inc. 2014 + * + * Authors: + * Anthony Liguori + * Rusty Russell + * Michael S. Tsirkin + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#define VIRTIO_PCI_NO_LEGACY +#include "virtio_pci_common.h" + +static void __iomem *map_capability(struct pci_dev *dev, int off, + size_t minlen, + u32 align, + u32 start, u32 size, + size_t *len) +{ + u8 bar; + u32 offset, length; + void __iomem *p; + + pci_read_config_byte(dev, off + offsetof(struct virtio_pci_cap, + bar), + &bar); + pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, offset), + &offset); + pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, length), + &length); + + if (length <= start) { + dev_err(&dev->dev, + "virtio_pci: bad capability len %u (>%u expected)\n", + length, start); + return NULL; + } + + if (length - start < minlen) { + dev_err(&dev->dev, + "virtio_pci: bad capability len %u (>=%zu expected)\n", + length, minlen); + return NULL; + } + + length -= start; + + if (start + offset < offset) { + dev_err(&dev->dev, + "virtio_pci: map wrap-around %u+%u\n", + start, offset); + return NULL; + } + + offset += start; + + if (offset & (align - 1)) { + dev_err(&dev->dev, + "virtio_pci: offset %u not aligned to %u\n", + offset, align); + return NULL; + } + + if (length > size) + length = size; + + if (len) + *len = length; + + if (minlen + offset < minlen || + minlen + offset > pci_resource_len(dev, bar)) { + dev_err(&dev->dev, + "virtio_pci: map virtio %zu@%u " + "out of range on bar %i length %lu\n", + minlen, offset, + bar, (unsigned long)pci_resource_len(dev, bar)); + return NULL; + } + + p = pci_iomap_range(dev, bar, offset, length); + if (!p) + dev_err(&dev->dev, + "virtio_pci: unable to map virtio %u@%u on bar %i\n", + length, offset, bar); + return p; +} + +static void iowrite64_twopart(u64 val, __le32 __iomem *lo, __le32 __iomem *hi) +{ + iowrite32((u32)val, lo); + iowrite32(val >> 32, hi); +} + +/* virtio config->get_features() implementation */ +static u64 vp_get_features(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + u64 features; + + iowrite32(0, &vp_dev->common->device_feature_select); + features = ioread32(&vp_dev->common->device_feature); + iowrite32(1, &vp_dev->common->device_feature_select); + features |= ((u64)ioread32(&vp_dev->common->device_feature) << 32); + + return features; +} + +/* virtio config->finalize_features() implementation */ +static int vp_finalize_features(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + if (!__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { + dev_err(&vdev->dev, "virtio: device uses modern interface " + "but does not have VIRTIO_F_VERSION_1\n"); + return -EINVAL; + } + + iowrite32(0, &vp_dev->common->guest_feature_select); + iowrite32((u32)vdev->features, &vp_dev->common->guest_feature); + iowrite32(1, &vp_dev->common->guest_feature_select); + iowrite32(vdev->features >> 32, &vp_dev->common->guest_feature); + + return 0; +} + +/* virtio config->get() implementation */ +static void vp_get(struct virtio_device *vdev, unsigned offset, + void *buf, unsigned len) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + u8 b; + __le16 w; + __le32 l; + + BUG_ON(offset + len > vp_dev->device_len); + + switch (len) { + case 1: + b = ioread8(vp_dev->device + offset); + memcpy(buf, &b, sizeof b); + break; + case 2: + w = cpu_to_le16(ioread16(vp_dev->device + offset)); + memcpy(buf, &w, sizeof w); + break; + case 4: + l = cpu_to_le32(ioread32(vp_dev->device + offset)); + memcpy(buf, &l, sizeof l); + break; + case 8: + l = cpu_to_le32(ioread32(vp_dev->device + offset)); + memcpy(buf, &l, sizeof l); + l = cpu_to_le32(ioread32(vp_dev->device + offset + sizeof l)); + memcpy(buf + sizeof l, &l, sizeof l); + break; + default: + BUG(); + } +} + +/* the config->set() implementation. it's symmetric to the config->get() + * implementation */ +static void vp_set(struct virtio_device *vdev, unsigned offset, + const void *buf, unsigned len) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + u8 b; + __le16 w; + __le32 l; + + BUG_ON(offset + len > vp_dev->device_len); + + switch (len) { + case 1: + memcpy(&b, buf, sizeof b); + iowrite8(b, vp_dev->device + offset); + break; + case 2: + memcpy(&w, buf, sizeof w); + iowrite16(le16_to_cpu(w), vp_dev->device + offset); + break; + case 4: + memcpy(&l, buf, sizeof l); + iowrite32(le32_to_cpu(l), vp_dev->device + offset); + break; + case 8: + memcpy(&l, buf, sizeof l); + iowrite32(le32_to_cpu(l), vp_dev->device + offset); + memcpy(&l, buf + sizeof l, sizeof l); + iowrite32(le32_to_cpu(l), vp_dev->device + offset + sizeof l); + break; + default: + BUG(); + } +} + +static u32 vp_generation(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + return ioread8(&vp_dev->common->config_generation); +} + +/* config->{get,set}_status() implementations */ +static u8 vp_get_status(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + return ioread8(&vp_dev->common->device_status); +} + +static void vp_set_status(struct virtio_device *vdev, u8 status) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + /* We should never be setting status to 0. */ + BUG_ON(status == 0); + iowrite8(status, &vp_dev->common->device_status); +} + +static void vp_reset(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + /* 0 status means a reset. */ + iowrite8(0, &vp_dev->common->device_status); + /* Flush out the status write, and flush in device writes, + * including MSI-X interrupts, if any. */ + ioread8(&vp_dev->common->device_status); + /* Flush pending VQ/configuration callbacks. */ + vp_synchronize_vectors(vdev); +} + +static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) +{ + /* Setup the vector used for configuration events */ + iowrite16(vector, &vp_dev->common->msix_config); + /* Verify we had enough resources to assign the vector */ + /* Will also flush the write out to device */ + return ioread16(&vp_dev->common->msix_config); +} + +static size_t vring_pci_size(u16 num) +{ + /* We only need a cacheline separation. */ + return PAGE_ALIGN(vring_size(num, SMP_CACHE_BYTES)); +} + +static void *alloc_virtqueue_pages(int *num) +{ + void *pages; + + /* TODO: allocate each queue chunk individually */ + for (; *num && vring_pci_size(*num) > PAGE_SIZE; *num /= 2) { + pages = alloc_pages_exact(vring_pci_size(*num), + GFP_KERNEL|__GFP_ZERO|__GFP_NOWARN); + if (pages) + return pages; + } + + if (!*num) + return NULL; + + /* Try to get a single page. You are my only hope! */ + return alloc_pages_exact(vring_pci_size(*num), GFP_KERNEL|__GFP_ZERO); +} + +static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, + struct virtio_pci_vq_info *info, + unsigned index, + void (*callback)(struct virtqueue *vq), + const char *name, + u16 msix_vec) +{ + struct virtio_pci_common_cfg __iomem *cfg = vp_dev->common; + struct virtqueue *vq; + u16 num, off; + int err; + + if (index >= ioread16(&cfg->num_queues)) + return ERR_PTR(-ENOENT); + + /* Select the queue we're interested in */ + iowrite16(index, &cfg->queue_select); + + /* Check if queue is either not available or already active. */ + num = ioread16(&cfg->queue_size); + if (!num || ioread8(&cfg->queue_enable)) + return ERR_PTR(-ENOENT); + + if (num & (num - 1)) { + dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", num); + return ERR_PTR(-EINVAL); + } + + /* get offset of notification word for this vq */ + off = ioread16(&cfg->queue_notify_off); + + info->num = num; + info->msix_vector = msix_vec; + + info->queue = alloc_virtqueue_pages(&info->num); + if (info->queue == NULL) + return ERR_PTR(-ENOMEM); + + /* create the vring */ + vq = vring_new_virtqueue(index, info->num, + SMP_CACHE_BYTES, &vp_dev->vdev, + true, info->queue, vp_notify, callback, name); + if (!vq) { + err = -ENOMEM; + goto err_new_queue; + } + + /* activate the queue */ + iowrite16(num, &cfg->queue_size); + iowrite64_twopart(virt_to_phys(info->queue), + &cfg->queue_desc_lo, &cfg->queue_desc_hi); + iowrite64_twopart(virt_to_phys(virtqueue_get_avail(vq)), + &cfg->queue_avail_lo, &cfg->queue_avail_hi); + iowrite64_twopart(virt_to_phys(virtqueue_get_used(vq)), + &cfg->queue_used_lo, &cfg->queue_used_hi); + + vq->priv = (void __force *)map_capability(vp_dev->pci_dev, + vp_dev->notify_map_cap, 2, 2, + off * vp_dev->notify_offset_multiplier, 2, + NULL); + + if (!vq->priv) { + err = -ENOMEM; + goto err_map_notify; + } + + if (msix_vec != VIRTIO_MSI_NO_VECTOR) { + iowrite16(msix_vec, &cfg->queue_msix_vector); + msix_vec = ioread16(&cfg->queue_msix_vector); + if (msix_vec == VIRTIO_MSI_NO_VECTOR) { + err = -EBUSY; + goto err_assign_vector; + } + } + + return vq; + +err_assign_vector: + pci_iounmap(vp_dev->pci_dev, (void __iomem __force *)vq->priv); +err_map_notify: + vring_del_virtqueue(vq); +err_new_queue: + free_pages_exact(info->queue, vring_pci_size(info->num)); + return ERR_PTR(err); +} + +static int vp_modern_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + struct virtqueue *vq; + int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names); + + if (rc) + return rc; + + /* Select and activate all queues. Has to be done last: once we do + * this, there's no way to go back except reset. + */ + list_for_each_entry(vq, &vdev->vqs, list) { + iowrite16(vq->index, &vp_dev->common->queue_select); + iowrite8(1, &vp_dev->common->queue_enable); + } + + return 0; +} + +static void del_vq(struct virtio_pci_vq_info *info) +{ + struct virtqueue *vq = info->vq; + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + + iowrite16(vq->index, &vp_dev->common->queue_select); + + if (vp_dev->msix_enabled) { + iowrite16(VIRTIO_MSI_NO_VECTOR, + &vp_dev->common->queue_msix_vector); + /* Flush the write out to device */ + ioread16(&vp_dev->common->queue_msix_vector); + } + + pci_iounmap(vp_dev->pci_dev, (void __force __iomem *)vq->priv); + + vring_del_virtqueue(vq); + + free_pages_exact(info->queue, vring_pci_size(info->num)); +} + +static const struct virtio_config_ops virtio_pci_config_ops = { + .get = vp_get, + .set = vp_set, + .generation = vp_generation, + .get_status = vp_get_status, + .set_status = vp_set_status, + .reset = vp_reset, + .find_vqs = vp_modern_find_vqs, + .del_vqs = vp_del_vqs, + .get_features = vp_get_features, + .finalize_features = vp_finalize_features, + .bus_name = vp_bus_name, + .set_vq_affinity = vp_set_vq_affinity, +}; + +/** + * virtio_pci_find_capability - walk capabilities to find device info. + * @dev: the pci device + * @cfg_type: the VIRTIO_PCI_CAP_* value we seek + * @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO. + * + * Returns offset of the capability, or 0. + */ +static inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type, + u32 ioresource_types) +{ + int pos; + + for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); + pos > 0; + pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { + u8 type, bar; + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, + cfg_type), + &type); + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, + bar), + &bar); + + /* Ignore structures with reserved BAR values */ + if (bar > 0x5) + continue; + + if (type == cfg_type) { + if (pci_resource_len(dev, bar) && + pci_resource_flags(dev, bar) & ioresource_types) + return pos; + } + } + return 0; +} + +static void virtio_pci_release_dev(struct device *_d) +{ + struct virtio_device *vdev = dev_to_virtio(_d); + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + kfree(vp_dev); +} + +/* TODO: validate the ABI statically. */ +static inline void check_offsets(void) +{ +} + +/* the PCI probing function */ +int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) +{ + struct pci_dev *pci_dev = vp_dev->pci_dev; + int err, common, isr, notify, device; + u32 notify_length; + + check_offsets(); + + /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */ + if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f) + return -ENODEV; + + if (pci_dev->device < 0x1040) { + /* Transitional devices: use the PCI subsystem device id as + * virtio device id, same as legacy driver always did. + */ + vp_dev->vdev.id.device = pci_dev->subsystem_device; + } else { + /* Modern devices: simply use PCI device id, but start from 0x1040. */ + vp_dev->vdev.id.device = pci_dev->device - 0x1040; + } + vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor; + + if (virtio_device_is_legacy_only(vp_dev->vdev.id)) + return -ENODEV; + + /* check for a common config: if not, use legacy mode (bar 0). */ + common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG, + IORESOURCE_IO | IORESOURCE_MEM); + if (!common) { + dev_info(&pci_dev->dev, + "virtio_pci: leaving for legacy driver\n"); + return -ENODEV; + } + + /* If common is there, these should be too... */ + isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG, + IORESOURCE_IO | IORESOURCE_MEM); + notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG, + IORESOURCE_IO | IORESOURCE_MEM); + if (!isr || !notify) { + dev_err(&pci_dev->dev, + "virtio_pci: missing capabilities %i/%i/%i\n", + common, isr, notify); + return -EINVAL; + } + + /* Device capability is only mandatory for devices that have + * device-specific configuration. + */ + device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG, + IORESOURCE_IO | IORESOURCE_MEM); + + err = -EINVAL; + vp_dev->common = map_capability(pci_dev, common, + sizeof(struct virtio_pci_common_cfg), 4, + 0, sizeof(struct virtio_pci_common_cfg), + NULL); + if (!vp_dev->common) + goto err_map_common; + vp_dev->isr = map_capability(pci_dev, isr, sizeof(u8), 1, + 0, 1, + NULL); + if (!vp_dev->isr) + goto err_map_isr; + + /* Read notify_off_multiplier from config space. */ + pci_read_config_dword(pci_dev, + notify + offsetof(struct virtio_pci_notify_cap, + notify_off_multiplier), + &vp_dev->notify_offset_multiplier); + /* Read notify length from config space. */ + pci_read_config_dword(pci_dev, + notify + offsetof(struct virtio_pci_notify_cap, + cap.length), + ¬ify_length); + + vp_dev->notify_map_cap = notify; + + /* Again, we don't know how much we should map, but PAGE_SIZE + * is more than enough for all existing devices. + */ + if (device) { + vp_dev->device = map_capability(pci_dev, device, 0, 4, + 0, PAGE_SIZE, + &vp_dev->device_len); + if (!vp_dev->device) + goto err_map_device; + } + + vp_dev->vdev.config = &virtio_pci_config_ops; + + vp_dev->config_vector = vp_config_vector; + vp_dev->setup_vq = setup_vq; + vp_dev->del_vq = del_vq; + + return 0; + +err_map_device: + pci_iounmap(pci_dev, vp_dev->isr); +err_map_isr: + pci_iounmap(pci_dev, vp_dev->common); +err_map_common: + return err; +} + +void virtio_pci_modern_remove(struct virtio_pci_device *vp_dev) +{ + struct pci_dev *pci_dev = vp_dev->pci_dev; + + if (vp_dev->device) + pci_iounmap(pci_dev, vp_dev->device); + pci_iounmap(pci_dev, vp_dev->isr); + pci_iounmap(pci_dev, vp_dev->common); +} diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index 4e054235358f28..a2b2e1353e3092 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -117,10 +117,11 @@ struct virtio_pci_cap { __u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ __u8 cap_next; /* Generic PCI field: next ptr. */ __u8 cap_len; /* Generic PCI field: capability length */ - __u8 type_and_bar; /* Upper 3 bits: bar. - * Lower 3 is VIRTIO_PCI_CAP_*_CFG. */ + __u8 cfg_type; /* Identifies the structure. */ + __u8 bar; /* Where to find it. */ + __u8 padding[3]; /* Pad to full dword. */ __le32 offset; /* Offset within bar. */ - __le32 length; /* Length. */ + __le32 length; /* Length of the structure, in bytes. */ }; #define VIRTIO_PCI_CAP_BAR_SHIFT 5 From 89461c4a12faa643fd7564037440d626b777b033 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 May 2013 16:29:32 +0930 Subject: [PATCH 180/360] virtio_pci: macros for PCI layout offsets QEMU wants it, so why not? Trust, but verify. Signed-off-by: Rusty Russell Signed-off-by: Michael S. Tsirkin --- drivers/virtio/virtio_pci_modern.c | 60 +++++++++++++++++++++++++++++- include/uapi/linux/virtio_pci.h | 36 +++++++++++++++--- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index a3d81013e0c2b6..b2e707ad81cf36 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -464,9 +464,67 @@ static void virtio_pci_release_dev(struct device *_d) kfree(vp_dev); } -/* TODO: validate the ABI statically. */ +/* This is part of the ABI. Don't screw with it. */ static inline void check_offsets(void) { + /* Note: disk space was harmed in compilation of this function. */ + BUILD_BUG_ON(VIRTIO_PCI_CAP_VNDR != + offsetof(struct virtio_pci_cap, cap_vndr)); + BUILD_BUG_ON(VIRTIO_PCI_CAP_NEXT != + offsetof(struct virtio_pci_cap, cap_next)); + BUILD_BUG_ON(VIRTIO_PCI_CAP_LEN != + offsetof(struct virtio_pci_cap, cap_len)); + BUILD_BUG_ON(VIRTIO_PCI_CAP_CFG_TYPE != + offsetof(struct virtio_pci_cap, cfg_type)); + BUILD_BUG_ON(VIRTIO_PCI_CAP_BAR != + offsetof(struct virtio_pci_cap, bar)); + BUILD_BUG_ON(VIRTIO_PCI_CAP_OFFSET != + offsetof(struct virtio_pci_cap, offset)); + BUILD_BUG_ON(VIRTIO_PCI_CAP_LENGTH != + offsetof(struct virtio_pci_cap, length)); + BUILD_BUG_ON(VIRTIO_PCI_NOTIFY_CAP_MULT != + offsetof(struct virtio_pci_notify_cap, + notify_off_multiplier)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_DFSELECT != + offsetof(struct virtio_pci_common_cfg, + device_feature_select)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_DF != + offsetof(struct virtio_pci_common_cfg, device_feature)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_GFSELECT != + offsetof(struct virtio_pci_common_cfg, + guest_feature_select)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_GF != + offsetof(struct virtio_pci_common_cfg, guest_feature)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_MSIX != + offsetof(struct virtio_pci_common_cfg, msix_config)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_NUMQ != + offsetof(struct virtio_pci_common_cfg, num_queues)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_STATUS != + offsetof(struct virtio_pci_common_cfg, device_status)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_CFGGENERATION != + offsetof(struct virtio_pci_common_cfg, config_generation)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SELECT != + offsetof(struct virtio_pci_common_cfg, queue_select)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SIZE != + offsetof(struct virtio_pci_common_cfg, queue_size)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_MSIX != + offsetof(struct virtio_pci_common_cfg, queue_msix_vector)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_ENABLE != + offsetof(struct virtio_pci_common_cfg, queue_enable)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_NOFF != + offsetof(struct virtio_pci_common_cfg, queue_notify_off)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCLO != + offsetof(struct virtio_pci_common_cfg, queue_desc_lo)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCHI != + offsetof(struct virtio_pci_common_cfg, queue_desc_hi)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILLO != + offsetof(struct virtio_pci_common_cfg, queue_avail_lo)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILHI != + offsetof(struct virtio_pci_common_cfg, queue_avail_hi)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDLO != + offsetof(struct virtio_pci_common_cfg, queue_used_lo)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDHI != + offsetof(struct virtio_pci_common_cfg, queue_used_hi)); } /* the PCI probing function */ diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index a2b2e1353e3092..3b7e4d2765fb52 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -124,11 +124,6 @@ struct virtio_pci_cap { __le32 length; /* Length of the structure, in bytes. */ }; -#define VIRTIO_PCI_CAP_BAR_SHIFT 5 -#define VIRTIO_PCI_CAP_BAR_MASK 0x7 -#define VIRTIO_PCI_CAP_TYPE_SHIFT 0 -#define VIRTIO_PCI_CAP_TYPE_MASK 0x7 - struct virtio_pci_notify_cap { struct virtio_pci_cap cap; __le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ @@ -160,6 +155,37 @@ struct virtio_pci_common_cfg { __le32 queue_used_hi; /* read-write */ }; +/* Macro versions of offsets for the Old Timers! */ +#define VIRTIO_PCI_CAP_VNDR 0 +#define VIRTIO_PCI_CAP_NEXT 1 +#define VIRTIO_PCI_CAP_LEN 2 +#define VIRTIO_PCI_CAP_CFG_TYPE 3 +#define VIRTIO_PCI_CAP_BAR 4 +#define VIRTIO_PCI_CAP_OFFSET 8 +#define VIRTIO_PCI_CAP_LENGTH 12 + +#define VIRTIO_PCI_NOTIFY_CAP_MULT 16 + +#define VIRTIO_PCI_COMMON_DFSELECT 0 +#define VIRTIO_PCI_COMMON_DF 4 +#define VIRTIO_PCI_COMMON_GFSELECT 8 +#define VIRTIO_PCI_COMMON_GF 12 +#define VIRTIO_PCI_COMMON_MSIX 16 +#define VIRTIO_PCI_COMMON_NUMQ 18 +#define VIRTIO_PCI_COMMON_STATUS 20 +#define VIRTIO_PCI_COMMON_CFGGENERATION 21 +#define VIRTIO_PCI_COMMON_Q_SELECT 22 +#define VIRTIO_PCI_COMMON_Q_SIZE 24 +#define VIRTIO_PCI_COMMON_Q_MSIX 26 +#define VIRTIO_PCI_COMMON_Q_ENABLE 28 +#define VIRTIO_PCI_COMMON_Q_NOFF 30 +#define VIRTIO_PCI_COMMON_Q_DESCLO 32 +#define VIRTIO_PCI_COMMON_Q_DESCHI 36 +#define VIRTIO_PCI_COMMON_Q_AVAILLO 40 +#define VIRTIO_PCI_COMMON_Q_AVAILHI 44 +#define VIRTIO_PCI_COMMON_Q_USEDLO 48 +#define VIRTIO_PCI_COMMON_Q_USEDHI 52 + #endif /* VIRTIO_PCI_NO_MODERN */ #endif From 3909213cfd9224cb1827d557fb6eb5ebdb8ddcbe Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 14 Jan 2015 18:50:55 +0200 Subject: [PATCH 181/360] virtio_pci_modern: reduce number of mappings We don't know the # of VQs that drivers are going to use so it's hard to predict how much memory we'll need to map. However, the relevant capability does give us an upper limit. If that's below a page, we can reduce the number of required mappings by mapping it all once ahead of the time. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci_common.h | 3 ++ drivers/virtio/virtio_pci_modern.c | 57 +++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 610c43f192306a..d39180512761e1 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -62,8 +62,11 @@ struct virtio_pci_device { struct virtio_pci_common_cfg __iomem *common; /* Device-specific data (non-legacy mode) */ void __iomem *device; + /* Base of vq notifications (non-legacy mode). */ + void __iomem *notify_base; /* So we can sanity-check accesses. */ + size_t notify_len; size_t device_len; /* Capability for when we need to map notifications per-vq. */ diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index b2e707ad81cf36..0e54cc88fdf51d 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -330,10 +330,26 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, iowrite64_twopart(virt_to_phys(virtqueue_get_used(vq)), &cfg->queue_used_lo, &cfg->queue_used_hi); - vq->priv = (void __force *)map_capability(vp_dev->pci_dev, - vp_dev->notify_map_cap, 2, 2, - off * vp_dev->notify_offset_multiplier, 2, - NULL); + if (vp_dev->notify_base) { + /* offset should not wrap */ + if ((u64)off * vp_dev->notify_offset_multiplier + 2 + > vp_dev->notify_len) { + dev_warn(&vp_dev->pci_dev->dev, + "bad notification offset %u (x %u) " + "for queue %u > %zd", + off, vp_dev->notify_offset_multiplier, + index, vp_dev->notify_len); + err = -EINVAL; + goto err_map_notify; + } + vq->priv = (void __force *)vp_dev->notify_base + + off * vp_dev->notify_offset_multiplier; + } else { + vq->priv = (void __force *)map_capability(vp_dev->pci_dev, + vp_dev->notify_map_cap, 2, 2, + off * vp_dev->notify_offset_multiplier, 2, + NULL); + } if (!vq->priv) { err = -ENOMEM; @@ -352,7 +368,8 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, return vq; err_assign_vector: - pci_iounmap(vp_dev->pci_dev, (void __iomem __force *)vq->priv); + if (!vp_dev->notify_base) + pci_iounmap(vp_dev->pci_dev, (void __iomem __force *)vq->priv); err_map_notify: vring_del_virtqueue(vq); err_new_queue: @@ -397,7 +414,8 @@ static void del_vq(struct virtio_pci_vq_info *info) ioread16(&vp_dev->common->queue_msix_vector); } - pci_iounmap(vp_dev->pci_dev, (void __force __iomem *)vq->priv); + if (!vp_dev->notify_base) + pci_iounmap(vp_dev->pci_dev, (void __force __iomem *)vq->priv); vring_del_virtqueue(vq); @@ -533,6 +551,7 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) struct pci_dev *pci_dev = vp_dev->pci_dev; int err, common, isr, notify, device; u32 notify_length; + u32 notify_offset; check_offsets(); @@ -599,13 +618,30 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) notify + offsetof(struct virtio_pci_notify_cap, notify_off_multiplier), &vp_dev->notify_offset_multiplier); - /* Read notify length from config space. */ + /* Read notify length and offset from config space. */ pci_read_config_dword(pci_dev, notify + offsetof(struct virtio_pci_notify_cap, cap.length), ¬ify_length); - vp_dev->notify_map_cap = notify; + pci_read_config_dword(pci_dev, + notify + offsetof(struct virtio_pci_notify_cap, + cap.length), + ¬ify_offset); + + /* We don't know how many VQs we'll map, ahead of the time. + * If notify length is small, map it all now. + * Otherwise, map each VQ individually later. + */ + if ((u64)notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) { + vp_dev->notify_base = map_capability(pci_dev, notify, 2, 2, + 0, notify_length, + &vp_dev->notify_len); + if (!vp_dev->notify_base) + goto err_map_notify; + } else { + vp_dev->notify_map_cap = notify; + } /* Again, we don't know how much we should map, but PAGE_SIZE * is more than enough for all existing devices. @@ -627,6 +663,9 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) return 0; err_map_device: + if (vp_dev->notify_base) + pci_iounmap(pci_dev, vp_dev->notify_base); +err_map_notify: pci_iounmap(pci_dev, vp_dev->isr); err_map_isr: pci_iounmap(pci_dev, vp_dev->common); @@ -640,6 +679,8 @@ void virtio_pci_modern_remove(struct virtio_pci_device *vp_dev) if (vp_dev->device) pci_iounmap(pci_dev, vp_dev->device); + if (vp_dev->notify_base) + pci_iounmap(pci_dev, vp_dev->notify_base); pci_iounmap(pci_dev, vp_dev->isr); pci_iounmap(pci_dev, vp_dev->common); } From d3f5f065603705cd4275d57324c49e391f786b5e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 13 Jan 2015 16:34:58 +0200 Subject: [PATCH 182/360] virtio_pci_modern: support devices with no config Virtio 1.0 spec lists device config as optional. Set get/set callbacks to NULL. Drivers can check that and fail gracefully. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci_modern.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 0e54cc88fdf51d..68ebc204e3ea64 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -422,6 +422,21 @@ static void del_vq(struct virtio_pci_vq_info *info) free_pages_exact(info->queue, vring_pci_size(info->num)); } +static const struct virtio_config_ops virtio_pci_config_nodev_ops = { + .get = NULL, + .set = NULL, + .generation = vp_generation, + .get_status = vp_get_status, + .set_status = vp_set_status, + .reset = vp_reset, + .find_vqs = vp_modern_find_vqs, + .del_vqs = vp_del_vqs, + .get_features = vp_get_features, + .finalize_features = vp_finalize_features, + .bus_name = vp_bus_name, + .set_vq_affinity = vp_set_vq_affinity, +}; + static const struct virtio_config_ops virtio_pci_config_ops = { .get = vp_get, .set = vp_set, @@ -652,9 +667,11 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) &vp_dev->device_len); if (!vp_dev->device) goto err_map_device; - } - vp_dev->vdev.config = &virtio_pci_config_ops; + vp_dev->vdev.config = &virtio_pci_config_ops; + } else { + vp_dev->vdev.config = &virtio_pci_config_nodev_ops; + } vp_dev->config_vector = vp_config_vector; vp_dev->setup_vq = setup_vq; From 25e65e4efca4116a9fc7a892ede2cf98f138de45 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 13:33:31 +0200 Subject: [PATCH 183/360] virtio_balloon: coding style fixes Most of our code has struct foo { } Fix two instances where balloon is inconsistent. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_balloon.c | 3 +-- include/uapi/linux/virtio_balloon.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 3176ea4028a83c..0413157f3b49c2 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -44,8 +44,7 @@ static int oom_pages = OOM_VBALLOON_DEFAULT_PAGES; module_param(oom_pages, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(oom_pages, "pages to free on OOM"); -struct virtio_balloon -{ +struct virtio_balloon { struct virtio_device *vdev; struct virtqueue *inflate_vq, *deflate_vq, *stats_vq; diff --git a/include/uapi/linux/virtio_balloon.h b/include/uapi/linux/virtio_balloon.h index be40f7059e939d..4b0488f20b2ef5 100644 --- a/include/uapi/linux/virtio_balloon.h +++ b/include/uapi/linux/virtio_balloon.h @@ -36,8 +36,7 @@ /* Size of a PFN in the balloon interface. */ #define VIRTIO_BALLOON_PFN_SHIFT 12 -struct virtio_balloon_config -{ +struct virtio_balloon_config { /* Number of pages host wants Guest to give up. */ __le32 num_pages; /* Number of pages we've actually got in balloon. */ From bb6ec57600f0a57554652d21e55d57a658420921 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 13:33:31 +0200 Subject: [PATCH 184/360] virtio_blk: coding style fixes Most of our code has struct foo { } Fix two instances where blk is inconsistent. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index b72c11a10f409b..655e570b9b3170 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -28,8 +28,7 @@ struct virtio_blk_vq { char name[VQ_NAME_LEN]; } ____cacheline_aligned_in_smp; -struct virtio_blk -{ +struct virtio_blk { struct virtio_device *vdev; /* The disk structure for the kernel. */ @@ -52,8 +51,7 @@ struct virtio_blk struct virtio_blk_vq *vqs; }; -struct virtblk_req -{ +struct virtblk_req { struct request *req; struct virtio_blk_outhdr out_hdr; struct virtio_scsi_inhdr in_hdr; From 43b4f721ce6d497648a8d4a21c1d53483090bcf9 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 13:33:31 +0200 Subject: [PATCH 185/360] virtio_ring: coding style fix Most of our code has struct foo { } Fix one instances where ring is inconsistent. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 00ec6b3f96b2bf..95b9661554d14b 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -54,8 +54,7 @@ #define END_USE(vq) #endif -struct vring_virtqueue -{ +struct vring_virtqueue { struct virtqueue vq; /* Actual memory layout for this queue */ From 3c7322405d1efac27cc547fa4d6947cf23871dac Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 13:50:06 +0200 Subject: [PATCH 186/360] virtio_rng: drop extra empty line makes code look a bit prettier. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/char/hw_random/virtio-rng.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 72295ea2fd1c9b..3fa2f8a009b378 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -39,7 +39,6 @@ struct virtrng_info { bool hwrng_removed; }; - static void random_recv_done(struct virtqueue *vq) { struct virtrng_info *vi = vq->vdev->priv; From b2a6d51ddf7b238cc4c7b32c7fb53b6de06977d8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 14:15:51 +0200 Subject: [PATCH 187/360] virtio_pci: Kconfig grammar fix This drivers -> this driver. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 00b22863827432..8286b63eaf8052 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -12,7 +12,7 @@ config VIRTIO_PCI depends on PCI select VIRTIO ---help--- - This drivers provides support for virtio based paravirtual device + This driver provides support for virtio based paravirtual device drivers over PCI. This requires that your VMM has appropriate PCI virtio backends. Most QEMU based VMMs should support these devices (like KVM or Xen). From 0327642337fee1dba50fa4d9a45d3a8b8fb2d1c3 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 14:16:24 +0200 Subject: [PATCH 188/360] virtio_pci: drop Kconfig warnings The ABI *is* stable, and has been for a while now. Drop Kconfig warning saying that it's not guaranteed to work. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 8286b63eaf8052..083fb45950f5b5 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -17,9 +17,6 @@ config VIRTIO_PCI virtio backends. Most QEMU based VMMs should support these devices (like KVM or Xen). - Currently, the ABI is not considered stable so there is no guarantee - that this version of the driver will work with your VMM. - If unsure, say M. config VIRTIO_BALLOON From 46506da5f365efe7fe3e4c9da73ab679c0382fac Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 16:06:26 +0200 Subject: [PATCH 189/360] virtio_pci: add an option to disable legacy driver Useful for testing device virtio 1 compatibility. Based on patch by Rusty - couldn't resist putting that flying car joke in there! Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/Kconfig | 19 +++++++++++++++++++ drivers/virtio/Makefile | 3 ++- drivers/virtio/virtio_pci_common.h | 10 ++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 083fb45950f5b5..b546da5d8ea323 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -19,6 +19,25 @@ config VIRTIO_PCI If unsure, say M. +config VIRTIO_PCI_LEGACY + bool "Support for legacy virtio draft 0.9.X and older devices" + default y + depends on VIRTIO_PCI + ---help--- + Virtio PCI Card 0.9.X Draft (circa 2014) and older device support. + + This option enables building a transitional driver, supporting + both devices conforming to Virtio 1 specification, and legacy devices. + If disabled, you get a slightly smaller, non-transitional driver, + with no legacy compatibility. + + So look out into your driveway. Do you have a flying car? If + so, you can happily disable this option and virtio will not + break. Otherwise, leave it set. Unless you're testing what + life will be like in The Future. + + If unsure, say Y. + config VIRTIO_BALLOON tristate "Virtio balloon driver" depends on VIRTIO diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index bd230d1c0533cc..d85565b8ea46dd 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o -virtio_pci-y := virtio_pci_modern.o virtio_pci_legacy.o virtio_pci_common.o +virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o +virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index d39180512761e1..28ee4e56badf15 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -147,8 +147,18 @@ const char *vp_bus_name(struct virtio_device *vdev); */ int vp_set_vq_affinity(struct virtqueue *vq, int cpu); +#if IS_ENABLED(CONFIG_VIRTIO_PCI_LEGACY) int virtio_pci_legacy_probe(struct virtio_pci_device *); void virtio_pci_legacy_remove(struct virtio_pci_device *); +#else +static inline int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev) +{ + return -ENODEV; +} +static inline void virtio_pci_legacy_remove(struct virtio_pci_device *vp_dev) +{ +} +#endif int virtio_pci_modern_probe(struct virtio_pci_device *); void virtio_pci_modern_remove(struct virtio_pci_device *); From ac399d8f39a860655961660efa5c67e7f3c47912 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 15 Jan 2015 17:54:13 +0200 Subject: [PATCH 190/360] virtio_pci: add module param to force legacy mode If set, try legacy interface first, modern one if that fails. Useful to work around device/driver bugs, and for compatibility testing. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci_common.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 8ae34a34f3af9a..e894eb278d8336 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -19,6 +19,14 @@ #include "virtio_pci_common.h" +static bool force_legacy = false; + +#if IS_ENABLED(CONFIG_VIRTIO_PCI_LEGACY) +module_param(force_legacy, bool, 0444); +MODULE_PARM_DESC(force_legacy, + "Force legacy mode for transitional virtio 1 devices"); +#endif + /* wait for pending irq handlers */ void vp_synchronize_vectors(struct virtio_device *vdev) { @@ -505,11 +513,20 @@ static int virtio_pci_probe(struct pci_dev *pci_dev, if (rc) goto err_request_regions; - rc = virtio_pci_modern_probe(vp_dev); - if (rc == -ENODEV) + if (force_legacy) { rc = virtio_pci_legacy_probe(vp_dev); - if (rc) - goto err_probe; + /* Also try modern mode if we can't map BAR0 (no IO space). */ + if (rc == -ENODEV || rc == -ENOMEM) + rc = virtio_pci_modern_probe(vp_dev); + if (rc) + goto err_probe; + } else { + rc = virtio_pci_modern_probe(vp_dev); + if (rc == -ENODEV) + rc = virtio_pci_legacy_probe(vp_dev); + if (rc) + goto err_probe; + } pci_set_master(pci_dev); From 76545f066d2a85464a9f81de2e159b199cc2942b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 20 Jan 2015 14:30:52 +0200 Subject: [PATCH 191/360] virtio_pci_modern: drop an unused function release function in modern driver is unused: it's a left-over from when each driver had to have its own release. Signed-off-by: Michael S. Tsirkin --- drivers/virtio/virtio_pci_modern.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 68ebc204e3ea64..f16e462cb4ef35 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -489,14 +489,6 @@ static inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type, return 0; } -static void virtio_pci_release_dev(struct device *_d) -{ - struct virtio_device *vdev = dev_to_virtio(_d); - struct virtio_pci_device *vp_dev = to_vp_device(vdev); - - kfree(vp_dev); -} - /* This is part of the ABI. Don't screw with it. */ static inline void check_offsets(void) { From 240181fd0ffa69cac08d6b06c94e843707370f5f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 12 Jan 2015 12:51:29 -0800 Subject: [PATCH 192/360] mtd: nand: default bitflip-reporting threshold to 75% of correction strength The MTD API reports -EUCLEAN only if the maximum number of bitflips found in any ECC block exceeds a certain threshold. This is done to avoid excessive -EUCLEAN reports to MTD users, which may induce additional scrubbing of data, even when the ECC algorithm in use is perfectly capable of handling the bitflips. This threshold can be controlled by user-space (via sysfs), to allow users to determine what they are willing to tolerate in their application. But it still helps to have sane defaults. In recent discussion [1], it was pointed out that our default threshold is equal to the correction strength. That means that we won't actually report any -EUCLEAN (i.e., "bitflips were corrected") errors until there are almost too many to handle. It was determined that 3/4 of the correction strength is probably a better default. [1] http://lists.infradead.org/pipermail/linux-mtd/2015-January/057259.html Signed-off-by: Brian Norris Acked-by: Huang Shijie --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 816b5c1fd41653..3f24b587304f03 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4171,7 +4171,7 @@ int nand_scan_tail(struct mtd_info *mtd) * properly set. */ if (!mtd->bitflip_threshold) - mtd->bitflip_threshold = mtd->ecc_strength; + mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) From b625a825955ede39047e821f4b69f36cf042b952 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Fri, 2 Jan 2015 22:02:43 +0100 Subject: [PATCH 193/360] firewire: ohci: Remove unused function Remove the function ar_prev_buffer_index() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index aff9018d06588d..f51d376d10ba64 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -718,11 +718,6 @@ static inline unsigned int ar_next_buffer_index(unsigned int index) return (index + 1) % AR_BUFFERS; } -static inline unsigned int ar_prev_buffer_index(unsigned int index) -{ - return (index - 1 + AR_BUFFERS) % AR_BUFFERS; -} - static inline unsigned int ar_first_buffer_index(struct ar_context *ctx) { return ar_next_buffer_index(ctx->last_buffer_index); From 0b271258544b3f89f9a14fa3b53e20ec9ce75392 Mon Sep 17 00:00:00 2001 From: Beomho Seo Date: Tue, 9 Dec 2014 21:03:51 +0900 Subject: [PATCH 194/360] mfd: rt5033: Add Richtek RT5033 driver core. This patch adds a new driver for Richtek RT5033 driver. RT5033 is a Multifunction device which includes battery charger, fuel gauge, flash LED current source, LDO and synchronous Buck converter. It is interfaced to host controller using I2C interface. Signed-off-by: Beomho Seo Acked-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 12 ++ drivers/mfd/Makefile | 1 + drivers/mfd/rt5033.c | 142 ++++++++++++++++ include/linux/mfd/rt5033-private.h | 260 +++++++++++++++++++++++++++++ include/linux/mfd/rt5033.h | 62 +++++++ 5 files changed, 477 insertions(+) create mode 100644 drivers/mfd/rt5033.c create mode 100644 include/linux/mfd/rt5033-private.h create mode 100644 include/linux/mfd/rt5033.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2e6b7311fabc89..9cd2af660cc535 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -623,6 +623,18 @@ config MFD_RTSX_PCI types of memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard. +config MFD_RT5033 + tristate "Richtek RT5033 Power Management IC" + depends on I2C=y + select MFD_CORE + select REGMAP_I2C + help + This driver provides for the Richtek RT5033 Power Management IC, + which includes the I2C driver and the Core APIs. This driver provides + common support for accessing the device. The device supports multiple + sub-devices like charger, fuel gauge, flash LED, current source, + LDO and Buck. + config MFD_RTSX_USB tristate "Realtek USB card reader" depends on USB diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 53467e21138118..4059c247c59ab8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -176,6 +176,7 @@ obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o obj-$(CONFIG_MFD_DLN2) += dln2.o +obj-$(CONFIG_MFD_RT5033) += rt5033.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c new file mode 100644 index 00000000000000..db395a6c52bc79 --- /dev/null +++ b/drivers/mfd/rt5033.c @@ -0,0 +1,142 @@ +/* + * MFD core driver for the Richtek RT5033. + * + * RT5033 comprises multiple sub-devices switcing charger, fuel gauge, + * flash LED, current source, LDO and BUCK regulators. + * + * Copyright (C) 2014 Samsung Electronics, Co., Ltd. + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published bythe Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct regmap_irq rt5033_irqs[] = { + { .mask = RT5033_PMIC_IRQ_BUCKOCP, }, + { .mask = RT5033_PMIC_IRQ_BUCKLV, }, + { .mask = RT5033_PMIC_IRQ_SAFELDOLV, }, + { .mask = RT5033_PMIC_IRQ_LDOLV, }, + { .mask = RT5033_PMIC_IRQ_OT, }, + { .mask = RT5033_PMIC_IRQ_VDDA_UV, }, +}; + +static const struct regmap_irq_chip rt5033_irq_chip = { + .name = "rt5033", + .status_base = RT5033_REG_PMIC_IRQ_STAT, + .mask_base = RT5033_REG_PMIC_IRQ_CTRL, + .mask_invert = true, + .num_regs = 1, + .irqs = rt5033_irqs, + .num_irqs = ARRAY_SIZE(rt5033_irqs), +}; + +static const struct mfd_cell rt5033_devs[] = { + { .name = "rt5033-regulator", }, + { + .name = "rt5033-charger", + .of_compatible = "richtek,rt5033-charger", + }, { + .name = "rt5033-battery", + .of_compatible = "richtek,rt5033-battery", + }, +}; + +static const struct regmap_config rt5033_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT5033_REG_END, +}; + +static int rt5033_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5033_dev *rt5033; + unsigned int dev_id; + int ret; + + rt5033 = devm_kzalloc(&i2c->dev, sizeof(*rt5033), GFP_KERNEL); + if (!rt5033) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5033); + rt5033->dev = &i2c->dev; + rt5033->irq = i2c->irq; + rt5033->wakeup = true; + + rt5033->regmap = devm_regmap_init_i2c(i2c, &rt5033_regmap_config); + if (IS_ERR(rt5033->regmap)) { + dev_err(&i2c->dev, "Failed to allocate register map.\n"); + return PTR_ERR(rt5033->regmap); + } + + ret = regmap_read(rt5033->regmap, RT5033_REG_DEVICE_ID, &dev_id); + if (ret) { + dev_err(&i2c->dev, "Device not found\n"); + return -ENODEV; + } + dev_info(&i2c->dev, "Device found Device ID: %04x\n", dev_id); + + ret = regmap_add_irq_chip(rt5033->regmap, rt5033->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + 0, &rt5033_irq_chip, &rt5033->irq_data); + if (ret) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + rt5033->irq, ret); + return ret; + } + + ret = mfd_add_devices(rt5033->dev, -1, rt5033_devs, + ARRAY_SIZE(rt5033_devs), NULL, 0, + regmap_irq_get_domain(rt5033->irq_data)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to add RT5033 child devices.\n"); + return ret; + } + + device_init_wakeup(rt5033->dev, rt5033->wakeup); + + return 0; +} + +static int rt5033_i2c_remove(struct i2c_client *i2c) +{ + mfd_remove_devices(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id rt5033_i2c_id[] = { + { "rt5033", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5033_i2c_id); + +static const struct of_device_id rt5033_dt_match[] = { + { .compatible = "richtek,rt5033", }, + { } +}; + +static struct i2c_driver rt5033_driver = { + .driver = { + .name = "rt5033", + .of_match_table = of_match_ptr(rt5033_dt_match), + }, + .probe = rt5033_i2c_probe, + .remove = rt5033_i2c_remove, + .id_table = rt5033_i2c_id, +}; +module_i2c_driver(rt5033_driver); + +MODULE_ALIAS("i2c:rt5033"); +MODULE_DESCRIPTION("Richtek RT5033 multi-function core driver"); +MODULE_AUTHOR("Beomho Seo "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/rt5033-private.h b/include/linux/mfd/rt5033-private.h new file mode 100644 index 00000000000000..1b63fc2f42d1c0 --- /dev/null +++ b/include/linux/mfd/rt5033-private.h @@ -0,0 +1,260 @@ +/* + * MFD core driver for Richtek RT5033 + * + * Copyright (C) 2014 Samsung Electronics, Co., Ltd. + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published bythe Free Software Foundation. + */ + +#ifndef __RT5033_PRIVATE_H__ +#define __RT5033_PRIVATE_H__ + +enum rt5033_reg { + RT5033_REG_CHG_STAT = 0x00, + RT5033_REG_CHG_CTRL1 = 0x01, + RT5033_REG_CHG_CTRL2 = 0x02, + RT5033_REG_DEVICE_ID = 0x03, + RT5033_REG_CHG_CTRL3 = 0x04, + RT5033_REG_CHG_CTRL4 = 0x05, + RT5033_REG_CHG_CTRL5 = 0x06, + RT5033_REG_RT_CTRL0 = 0x07, + RT5033_REG_CHG_RESET = 0x08, + /* Reserved 0x09~0x18 */ + RT5033_REG_RT_CTRL1 = 0x19, + /* Reserved 0x1A~0x20 */ + RT5033_REG_FLED_FUNCTION1 = 0x21, + RT5033_REG_FLED_FUNCTION2 = 0x22, + RT5033_REG_FLED_STROBE_CTRL1 = 0x23, + RT5033_REG_FLED_STROBE_CTRL2 = 0x24, + RT5033_REG_FLED_CTRL1 = 0x25, + RT5033_REG_FLED_CTRL2 = 0x26, + RT5033_REG_FLED_CTRL3 = 0x27, + RT5033_REG_FLED_CTRL4 = 0x28, + RT5033_REG_FLED_CTRL5 = 0x29, + /* Reserved 0x2A~0x40 */ + RT5033_REG_CTRL = 0x41, + RT5033_REG_BUCK_CTRL = 0x42, + RT5033_REG_LDO_CTRL = 0x43, + /* Reserved 0x44~0x46 */ + RT5033_REG_MANUAL_RESET_CTRL = 0x47, + /* Reserved 0x48~0x5F */ + RT5033_REG_CHG_IRQ1 = 0x60, + RT5033_REG_CHG_IRQ2 = 0x61, + RT5033_REG_CHG_IRQ3 = 0x62, + RT5033_REG_CHG_IRQ1_CTRL = 0x63, + RT5033_REG_CHG_IRQ2_CTRL = 0x64, + RT5033_REG_CHG_IRQ3_CTRL = 0x65, + RT5033_REG_LED_IRQ_STAT = 0x66, + RT5033_REG_LED_IRQ_CTRL = 0x67, + RT5033_REG_PMIC_IRQ_STAT = 0x68, + RT5033_REG_PMIC_IRQ_CTRL = 0x69, + RT5033_REG_SHDN_CTRL = 0x6A, + RT5033_REG_OFF_EVENT = 0x6B, + + RT5033_REG_END, +}; + +/* RT5033 Charger state register */ +#define RT5033_CHG_STAT_MASK 0x20 +#define RT5033_CHG_STAT_DISCHARGING 0x00 +#define RT5033_CHG_STAT_FULL 0x10 +#define RT5033_CHG_STAT_CHARGING 0x20 +#define RT5033_CHG_STAT_NOT_CHARGING 0x30 +#define RT5033_CHG_STAT_TYPE_MASK 0x60 +#define RT5033_CHG_STAT_TYPE_PRE 0x20 +#define RT5033_CHG_STAT_TYPE_FAST 0x60 + +/* RT5033 CHGCTRL1 register */ +#define RT5033_CHGCTRL1_IAICR_MASK 0xe0 +#define RT5033_CHGCTRL1_MODE_MASK 0x01 + +/* RT5033 CHGCTRL2 register */ +#define RT5033_CHGCTRL2_CV_MASK 0xfc + +/* RT5033 CHGCTRL3 register */ +#define RT5033_CHGCTRL3_CFO_EN_MASK 0x40 +#define RT5033_CHGCTRL3_TIMER_MASK 0x38 +#define RT5033_CHGCTRL3_TIMER_EN_MASK 0x01 + +/* RT5033 CHGCTRL4 register */ +#define RT5033_CHGCTRL4_EOC_MASK 0x07 +#define RT5033_CHGCTRL4_IPREC_MASK 0x18 + +/* RT5033 CHGCTRL5 register */ +#define RT5033_CHGCTRL5_VPREC_MASK 0x0f +#define RT5033_CHGCTRL5_ICHG_MASK 0xf0 +#define RT5033_CHGCTRL5_ICHG_SHIFT 0x04 +#define RT5033_CHG_MAX_CURRENT 0x0d + +/* RT5033 RT CTRL1 register */ +#define RT5033_RT_CTRL1_UUG_MASK 0x02 +#define RT5033_RT_HZ_MASK 0x01 + +/* RT5033 control register */ +#define RT5033_CTRL_FCCM_BUCK_MASK 0x00 +#define RT5033_CTRL_BUCKOMS_MASK 0x01 +#define RT5033_CTRL_LDOOMS_MASK 0x02 +#define RT5033_CTRL_SLDOOMS_MASK 0x03 +#define RT5033_CTRL_EN_BUCK_MASK 0x04 +#define RT5033_CTRL_EN_LDO_MASK 0x05 +#define RT5033_CTRL_EN_SAFE_LDO_MASK 0x06 +#define RT5033_CTRL_LDO_SLEEP_MASK 0x07 + +/* RT5033 BUCK control register */ +#define RT5033_BUCK_CTRL_MASK 0x1f + +/* RT5033 LDO control register */ +#define RT5033_LDO_CTRL_MASK 0x1f + +/* RT5033 charger property - model, manufacturer */ + +#define RT5033_CHARGER_MODEL "RT5033WSC Charger" +#define RT5033_MANUFACTURER "Richtek Technology Corporation" + +/* + * RT5033 charger fast-charge current lmits (as in CHGCTRL1 register), + * AICR mode limits the input current for example, + * the AIRC 100 mode limits the input current to 100 mA. + */ +#define RT5033_AICR_100_MODE 0x20 +#define RT5033_AICR_500_MODE 0x40 +#define RT5033_AICR_700_MODE 0x60 +#define RT5033_AICR_900_MODE 0x80 +#define RT5033_AICR_1500_MODE 0xc0 +#define RT5033_AICR_2000_MODE 0xe0 +#define RT5033_AICR_MODE_MASK 0xe0 + +/* RT5033 use internal timer need to set time */ +#define RT5033_FAST_CHARGE_TIMER4 0x00 +#define RT5033_FAST_CHARGE_TIMER6 0x01 +#define RT5033_FAST_CHARGE_TIMER8 0x02 +#define RT5033_FAST_CHARGE_TIMER9 0x03 +#define RT5033_FAST_CHARGE_TIMER12 0x04 +#define RT5033_FAST_CHARGE_TIMER14 0x05 +#define RT5033_FAST_CHARGE_TIMER16 0x06 + +#define RT5033_INT_TIMER_ENABLE 0x01 + +/* RT5033 charger termination enable mask */ +#define RT5033_TE_ENABLE_MASK 0x08 + +/* + * RT5033 charger opa mode. RT50300 have two opa mode charger mode + * and boost mode for OTG + */ + +#define RT5033_CHARGER_MODE 0x00 +#define RT5033_BOOST_MODE 0x01 + +/* RT5033 charger termination enable */ +#define RT5033_TE_ENABLE 0x08 + +/* RT5033 charger CFO enable */ +#define RT5033_CFO_ENABLE 0x40 + +/* RT5033 charger constant charge voltage (as in CHGCTRL2 register), uV */ +#define RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MIN 3650000U +#define RT5033_CHARGER_CONST_VOLTAGE_STEP_NUM 25000U +#define RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MAX 4400000U + +/* RT5033 charger pre-charge current limits (as in CHGCTRL4 register), uA */ +#define RT5033_CHARGER_PRE_CURRENT_LIMIT_MIN 350000U +#define RT5033_CHARGER_PRE_CURRENT_STEP_NUM 100000U +#define RT5033_CHARGER_PRE_CURRENT_LIMIT_MAX 650000U + +/* RT5033 charger fast-charge current (as in CHGCTRL5 register), uA */ +#define RT5033_CHARGER_FAST_CURRENT_MIN 700000U +#define RT5033_CHARGER_FAST_CURRENT_STEP_NUM 100000U +#define RT5033_CHARGER_FAST_CURRENT_MAX 2000000U + +/* + * RT5033 charger const-charge end of charger current ( + * as in CHGCTRL4 register), uA + */ +#define RT5033_CHARGER_EOC_MIN 150000U +#define RT5033_CHARGER_EOC_REF 300000U +#define RT5033_CHARGER_EOC_STEP_NUM1 50000U +#define RT5033_CHARGER_EOC_STEP_NUM2 100000U +#define RT5033_CHARGER_EOC_MAX 600000U + +/* + * RT5033 charger pre-charge threshold volt limits + * (as in CHGCTRL5 register), uV + */ + +#define RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MIN 2300000U +#define RT5033_CHARGER_PRE_THRESHOLD_STEP_NUM 100000U +#define RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MAX 3800000U + +/* + * RT5033 charger enable UUG, If UUG enable MOS auto control by H/W charger + * circuit. + */ +#define RT5033_CHARGER_UUG_ENABLE 0x02 + +/* RT5033 charger High impedance mode */ +#define RT5033_CHARGER_HZ_DISABLE 0x00 +#define RT5033_CHARGER_HZ_ENABLE 0x01 + +/* RT5033 regulator BUCK output voltage uV */ +#define RT5033_REGULATOR_BUCK_VOLTAGE_MIN 1000000U +#define RT5033_REGULATOR_BUCK_VOLTAGE_MAX 3000000U +#define RT5033_REGULATOR_BUCK_VOLTAGE_STEP 100000U +#define RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM 32 + +/* RT5033 regulator LDO output voltage uV */ +#define RT5033_REGULATOR_LDO_VOLTAGE_MIN 1200000U +#define RT5033_REGULATOR_LDO_VOLTAGE_MAX 3000000U +#define RT5033_REGULATOR_LDO_VOLTAGE_STEP 100000U +#define RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM 32 + +/* RT5033 regulator SAFE LDO output voltage uV */ +#define RT5033_REGULATOR_SAFE_LDO_VOLTAGE 4900000U + +enum rt5033_fuel_reg { + RT5033_FUEL_REG_OCV_H = 0x00, + RT5033_FUEL_REG_OCV_L = 0x01, + RT5033_FUEL_REG_VBAT_H = 0x02, + RT5033_FUEL_REG_VBAT_L = 0x03, + RT5033_FUEL_REG_SOC_H = 0x04, + RT5033_FUEL_REG_SOC_L = 0x05, + RT5033_FUEL_REG_CTRL_H = 0x06, + RT5033_FUEL_REG_CTRL_L = 0x07, + RT5033_FUEL_REG_CRATE = 0x08, + RT5033_FUEL_REG_DEVICE_ID = 0x09, + RT5033_FUEL_REG_AVG_VOLT_H = 0x0A, + RT5033_FUEL_REG_AVG_VOLT_L = 0x0B, + RT5033_FUEL_REG_CONFIG_H = 0x0C, + RT5033_FUEL_REG_CONFIG_L = 0x0D, + /* Reserved 0x0E~0x0F */ + RT5033_FUEL_REG_IRQ_CTRL = 0x10, + RT5033_FUEL_REG_IRQ_FLAG = 0x11, + RT5033_FUEL_VMIN = 0x12, + RT5033_FUEL_SMIN = 0x13, + /* Reserved 0x14~0x1F */ + RT5033_FUEL_VGCOMP1 = 0x20, + RT5033_FUEL_VGCOMP2 = 0x21, + RT5033_FUEL_VGCOMP3 = 0x22, + RT5033_FUEL_VGCOMP4 = 0x23, + /* Reserved 0x24~0xFD */ + RT5033_FUEL_MFA_H = 0xFE, + RT5033_FUEL_MFA_L = 0xFF, + + RT5033_FUEL_REG_END, +}; + +/* RT5033 fuel gauge battery present property */ +#define RT5033_FUEL_BAT_PRESENT 0x02 + +/* RT5033 PMIC interrupts */ +#define RT5033_PMIC_IRQ_BUCKOCP 2 +#define RT5033_PMIC_IRQ_BUCKLV 3 +#define RT5033_PMIC_IRQ_SAFELDOLV 4 +#define RT5033_PMIC_IRQ_LDOLV 5 +#define RT5033_PMIC_IRQ_OT 6 +#define RT5033_PMIC_IRQ_VDDA_UV 7 + +#endif /* __RT5033_PRIVATE_H__ */ diff --git a/include/linux/mfd/rt5033.h b/include/linux/mfd/rt5033.h new file mode 100644 index 00000000000000..010cff49a98eba --- /dev/null +++ b/include/linux/mfd/rt5033.h @@ -0,0 +1,62 @@ +/* + * MFD core driver for the RT5033 + * + * Copyright (C) 2014 Samsung Electronics + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published bythe Free Software Foundation. + */ + +#ifndef __RT5033_H__ +#define __RT5033_H__ + +#include +#include +#include +#include + +/* RT5033 regulator IDs */ +enum rt5033_regulators { + RT5033_BUCK = 0, + RT5033_LDO, + RT5033_SAFE_LDO, + + RT5033_REGULATOR_NUM, +}; + +struct rt5033_dev { + struct device *dev; + + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + int irq; + bool wakeup; +}; + +struct rt5033_battery { + struct i2c_client *client; + struct rt5033_dev *rt5033; + struct regmap *regmap; + struct power_supply psy; +}; + +/* RT5033 charger platform data */ +struct rt5033_charger_data { + unsigned int pre_uamp; + unsigned int pre_uvolt; + unsigned int const_uvolt; + unsigned int eoc_uamp; + unsigned int fast_uamp; +}; + +struct rt5033_charger { + struct device *dev; + struct rt5033_dev *rt5033; + struct power_supply psy; + + struct rt5033_charger_data *chg; +}; + +#endif /* __RT5033_H__ */ From 6f262ecfdcda004804d1f444285b99c7e92ddbd9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 17 Dec 2014 18:18:18 +0100 Subject: [PATCH 195/360] mfd: sun6i-prcm: Add support for the ir-clk Add support for the ir-clk which is part of the sun6i SoC prcm module. Signed-off-by: Hans de Goede Signed-off-by: Lee Jones --- drivers/mfd/sun6i-prcm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c index 2f2e9f06257116..191173166d6532 100644 --- a/drivers/mfd/sun6i-prcm.c +++ b/drivers/mfd/sun6i-prcm.c @@ -41,6 +41,14 @@ static const struct resource sun6i_a31_apb0_gates_clk_res[] = { }, }; +static const struct resource sun6i_a31_ir_clk_res[] = { + { + .start = 0x54, + .end = 0x57, + .flags = IORESOURCE_MEM, + }, +}; + static const struct resource sun6i_a31_apb0_rstc_res[] = { { .start = 0xb0, @@ -68,6 +76,12 @@ static const struct mfd_cell sun6i_a31_prcm_subdevs[] = { .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), .resources = sun6i_a31_apb0_gates_clk_res, }, + { + .name = "sun6i-a31-ir-clk", + .of_compatible = "allwinner,sun4i-a10-mod0-clk", + .num_resources = ARRAY_SIZE(sun6i_a31_ir_clk_res), + .resources = sun6i_a31_ir_clk_res, + }, { .name = "sun6i-a31-apb0-clock-reset", .of_compatible = "allwinner,sun6i-a31-clock-reset", From 1ae68f95de1e18751ff2830c361c173e8ae677d8 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 18:49:53 +0100 Subject: [PATCH 196/360] mfd: db8500-prcmu: Remove unused function Remove the function prcmu_get_boot_status() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Acked-by: Linus Walleij Acked-by: Arnd Bergmann Signed-off-by: Lee Jones --- drivers/mfd/db8500-prcmu.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 16162bf43656a0..cc1a404328c294 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -674,15 +674,6 @@ bool prcmu_has_arm_maxopp(void) PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK; } -/** - * prcmu_get_boot_status - PRCMU boot status checking - * Returns: the current PRCMU boot status - */ -int prcmu_get_boot_status(void) -{ - return readb(tcdm_base + PRCM_BOOT_STATUS); -} - /** * prcmu_set_rc_a2p - This function is used to run few power state sequences * @val: Value to be set, i.e. transition requested From b8fce55c09d3327014191247957d875c9fe5b7ce Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Mon, 22 Dec 2014 16:51:06 +0000 Subject: [PATCH 197/360] mfd: Add support for DA9150 combined charger & fuel-gauge device DA9150 is a combined Charger and Fuel-Gauge IC, with additional GPIO and GPADC functionality. Signed-off-by: Adam Thomson Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 12 + drivers/mfd/Makefile | 2 +- drivers/mfd/da9150-core.c | 413 +++++++++ include/linux/mfd/da9150/core.h | 68 ++ include/linux/mfd/da9150/registers.h | 1155 ++++++++++++++++++++++++++ 5 files changed, 1649 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/da9150-core.c create mode 100644 include/linux/mfd/da9150/core.h create mode 100644 include/linux/mfd/da9150/registers.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9cd2af660cc535..c47b2da7986e9a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -195,6 +195,18 @@ config MFD_DA9063 Additional drivers must be enabled in order to use the functionality of the device. +config MFD_DA9150 + tristate "Dialog Semiconductor DA9150 Charger Fuel-Gauge chip" + depends on I2C=y + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + This adds support for the DA9150 integrated charger and fuel-gauge + chip. This driver provides common support for accessing the device. + Additional drivers must be enabled in order to use the specific + features of the device. + config MFD_DLN2 tristate "Diolan DLN2 support" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4059c247c59ab8..02c2f2c69b949f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -113,7 +113,7 @@ obj-$(CONFIG_MFD_DA9055) += da9055.o da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o obj-$(CONFIG_MFD_DA9063) += da9063.o - +obj-$(CONFIG_MFD_DA9150) += da9150-core.o obj-$(CONFIG_MFD_MAX14577) += max14577.o obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c new file mode 100644 index 00000000000000..4d757b97ef9a07 --- /dev/null +++ b/drivers/mfd/da9150-core.c @@ -0,0 +1,413 @@ +/* + * DA9150 Core MFD Driver + * + * Copyright (c) 2014 Dialog Semiconductor + * + * Author: Adam Thomson + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool da9150_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA9150_PAGE_CON: + case DA9150_STATUS_A: + case DA9150_STATUS_B: + case DA9150_STATUS_C: + case DA9150_STATUS_D: + case DA9150_STATUS_E: + case DA9150_STATUS_F: + case DA9150_STATUS_G: + case DA9150_STATUS_H: + case DA9150_STATUS_I: + case DA9150_STATUS_J: + case DA9150_STATUS_K: + case DA9150_STATUS_L: + case DA9150_STATUS_N: + case DA9150_FAULT_LOG_A: + case DA9150_FAULT_LOG_B: + case DA9150_EVENT_E: + case DA9150_EVENT_F: + case DA9150_EVENT_G: + case DA9150_EVENT_H: + case DA9150_CONTROL_B: + case DA9150_CONTROL_C: + case DA9150_GPADC_MAN: + case DA9150_GPADC_RES_A: + case DA9150_GPADC_RES_B: + case DA9150_ADETVB_CFG_C: + case DA9150_ADETD_STAT: + case DA9150_ADET_CMPSTAT: + case DA9150_ADET_CTRL_A: + case DA9150_PPR_TCTR_B: + case DA9150_COREBTLD_STAT_A: + case DA9150_CORE_DATA_A: + case DA9150_CORE_DATA_B: + case DA9150_CORE_DATA_C: + case DA9150_CORE_DATA_D: + case DA9150_CORE2WIRE_STAT_A: + case DA9150_FW_CTRL_C: + case DA9150_FG_CTRL_B: + case DA9150_FW_CTRL_B: + case DA9150_GPADC_CMAN: + case DA9150_GPADC_CRES_A: + case DA9150_GPADC_CRES_B: + case DA9150_CC_ICHG_RES_A: + case DA9150_CC_ICHG_RES_B: + case DA9150_CC_IAVG_RES_A: + case DA9150_CC_IAVG_RES_B: + case DA9150_TAUX_CTRL_A: + case DA9150_TAUX_VALUE_H: + case DA9150_TAUX_VALUE_L: + case DA9150_TBAT_RES_A: + case DA9150_TBAT_RES_B: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg da9150_range_cfg[] = { + { + .range_min = DA9150_PAGE_CON, + .range_max = DA9150_TBAT_RES_B, + .selector_reg = DA9150_PAGE_CON, + .selector_mask = DA9150_I2C_PAGE_MASK, + .selector_shift = DA9150_I2C_PAGE_SHIFT, + .window_start = 0, + .window_len = 256, + }, +}; + +static struct regmap_config da9150_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .ranges = da9150_range_cfg, + .num_ranges = ARRAY_SIZE(da9150_range_cfg), + .max_register = DA9150_TBAT_RES_B, + + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = da9150_volatile_reg, +}; + +u8 da9150_reg_read(struct da9150 *da9150, u16 reg) +{ + int val, ret; + + ret = regmap_read(da9150->regmap, reg, &val); + if (ret) + dev_err(da9150->dev, "Failed to read from reg 0x%x: %d\n", + reg, ret); + + return (u8) val; +} +EXPORT_SYMBOL_GPL(da9150_reg_read); + +void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val) +{ + int ret; + + ret = regmap_write(da9150->regmap, reg, val); + if (ret) + dev_err(da9150->dev, "Failed to write to reg 0x%x: %d\n", + reg, ret); +} +EXPORT_SYMBOL_GPL(da9150_reg_write); + +void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val) +{ + int ret; + + ret = regmap_update_bits(da9150->regmap, reg, mask, val); + if (ret) + dev_err(da9150->dev, "Failed to set bits in reg 0x%x: %d\n", + reg, ret); +} +EXPORT_SYMBOL_GPL(da9150_set_bits); + +void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf) +{ + int ret; + + ret = regmap_bulk_read(da9150->regmap, reg, buf, count); + if (ret) + dev_err(da9150->dev, "Failed to bulk read from reg 0x%x: %d\n", + reg, ret); +} +EXPORT_SYMBOL_GPL(da9150_bulk_read); + +void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf) +{ + int ret; + + ret = regmap_raw_write(da9150->regmap, reg, buf, count); + if (ret) + dev_err(da9150->dev, "Failed to bulk write to reg 0x%x %d\n", + reg, ret); +} +EXPORT_SYMBOL_GPL(da9150_bulk_write); + +static struct regmap_irq da9150_irqs[] = { + [DA9150_IRQ_VBUS] = { + .reg_offset = 0, + .mask = DA9150_E_VBUS_MASK, + }, + [DA9150_IRQ_CHG] = { + .reg_offset = 0, + .mask = DA9150_E_CHG_MASK, + }, + [DA9150_IRQ_TCLASS] = { + .reg_offset = 0, + .mask = DA9150_E_TCLASS_MASK, + }, + [DA9150_IRQ_TJUNC] = { + .reg_offset = 0, + .mask = DA9150_E_TJUNC_MASK, + }, + [DA9150_IRQ_VFAULT] = { + .reg_offset = 0, + .mask = DA9150_E_VFAULT_MASK, + }, + [DA9150_IRQ_CONF] = { + .reg_offset = 1, + .mask = DA9150_E_CONF_MASK, + }, + [DA9150_IRQ_DAT] = { + .reg_offset = 1, + .mask = DA9150_E_DAT_MASK, + }, + [DA9150_IRQ_DTYPE] = { + .reg_offset = 1, + .mask = DA9150_E_DTYPE_MASK, + }, + [DA9150_IRQ_ID] = { + .reg_offset = 1, + .mask = DA9150_E_ID_MASK, + }, + [DA9150_IRQ_ADP] = { + .reg_offset = 1, + .mask = DA9150_E_ADP_MASK, + }, + [DA9150_IRQ_SESS_END] = { + .reg_offset = 1, + .mask = DA9150_E_SESS_END_MASK, + }, + [DA9150_IRQ_SESS_VLD] = { + .reg_offset = 1, + .mask = DA9150_E_SESS_VLD_MASK, + }, + [DA9150_IRQ_FG] = { + .reg_offset = 2, + .mask = DA9150_E_FG_MASK, + }, + [DA9150_IRQ_GP] = { + .reg_offset = 2, + .mask = DA9150_E_GP_MASK, + }, + [DA9150_IRQ_TBAT] = { + .reg_offset = 2, + .mask = DA9150_E_TBAT_MASK, + }, + [DA9150_IRQ_GPIOA] = { + .reg_offset = 2, + .mask = DA9150_E_GPIOA_MASK, + }, + [DA9150_IRQ_GPIOB] = { + .reg_offset = 2, + .mask = DA9150_E_GPIOB_MASK, + }, + [DA9150_IRQ_GPIOC] = { + .reg_offset = 2, + .mask = DA9150_E_GPIOC_MASK, + }, + [DA9150_IRQ_GPIOD] = { + .reg_offset = 2, + .mask = DA9150_E_GPIOD_MASK, + }, + [DA9150_IRQ_GPADC] = { + .reg_offset = 2, + .mask = DA9150_E_GPADC_MASK, + }, + [DA9150_IRQ_WKUP] = { + .reg_offset = 3, + .mask = DA9150_E_WKUP_MASK, + }, +}; + +static struct regmap_irq_chip da9150_regmap_irq_chip = { + .name = "da9150_irq", + .status_base = DA9150_EVENT_E, + .mask_base = DA9150_IRQ_MASK_E, + .ack_base = DA9150_EVENT_E, + .num_regs = DA9150_NUM_IRQ_REGS, + .irqs = da9150_irqs, + .num_irqs = ARRAY_SIZE(da9150_irqs), +}; + +static struct resource da9150_gpadc_resources[] = { + { + .name = "GPADC", + .start = DA9150_IRQ_GPADC, + .end = DA9150_IRQ_GPADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource da9150_charger_resources[] = { + { + .name = "CHG_STATUS", + .start = DA9150_IRQ_CHG, + .end = DA9150_IRQ_CHG, + .flags = IORESOURCE_IRQ, + }, + { + .name = "CHG_TJUNC", + .start = DA9150_IRQ_TJUNC, + .end = DA9150_IRQ_TJUNC, + .flags = IORESOURCE_IRQ, + }, + { + .name = "CHG_VFAULT", + .start = DA9150_IRQ_VFAULT, + .end = DA9150_IRQ_VFAULT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "CHG_VBUS", + .start = DA9150_IRQ_VBUS, + .end = DA9150_IRQ_VBUS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell da9150_devs[] = { + { + .name = "da9150-gpadc", + .of_compatible = "dlg,da9150-gpadc", + .resources = da9150_gpadc_resources, + .num_resources = ARRAY_SIZE(da9150_gpadc_resources), + }, + { + .name = "da9150-charger", + .of_compatible = "dlg,da9150-charger", + .resources = da9150_charger_resources, + .num_resources = ARRAY_SIZE(da9150_charger_resources), + }, +}; + +static int da9150_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct da9150 *da9150; + struct da9150_pdata *pdata = dev_get_platdata(&client->dev); + int ret; + + da9150 = devm_kzalloc(&client->dev, sizeof(*da9150), GFP_KERNEL); + if (!da9150) + return -ENOMEM; + + da9150->dev = &client->dev; + da9150->irq = client->irq; + i2c_set_clientdata(client, da9150); + + da9150->regmap = devm_regmap_init_i2c(client, &da9150_regmap_config); + if (IS_ERR(da9150->regmap)) { + ret = PTR_ERR(da9150->regmap); + dev_err(da9150->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + da9150->irq_base = pdata ? pdata->irq_base : -1; + + ret = regmap_add_irq_chip(da9150->regmap, da9150->irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + da9150->irq_base, &da9150_regmap_irq_chip, + &da9150->regmap_irq_data); + if (ret) + return ret; + + da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data); + enable_irq_wake(da9150->irq); + + ret = mfd_add_devices(da9150->dev, -1, da9150_devs, + ARRAY_SIZE(da9150_devs), NULL, + da9150->irq_base, NULL); + if (ret) { + dev_err(da9150->dev, "Failed to add child devices: %d\n", ret); + regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data); + return ret; + } + + return 0; +} + +static int da9150_remove(struct i2c_client *client) +{ + struct da9150 *da9150 = i2c_get_clientdata(client); + + regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data); + mfd_remove_devices(da9150->dev); + + return 0; +} + +static void da9150_shutdown(struct i2c_client *client) +{ + struct da9150 *da9150 = i2c_get_clientdata(client); + + /* Make sure we have a wakup source for the device */ + da9150_set_bits(da9150, DA9150_CONFIG_D, + DA9150_WKUP_PM_EN_MASK, + DA9150_WKUP_PM_EN_MASK); + + /* Set device to DISABLED mode */ + da9150_set_bits(da9150, DA9150_CONTROL_C, + DA9150_DISABLE_MASK, DA9150_DISABLE_MASK); +} + +static const struct i2c_device_id da9150_i2c_id[] = { + { "da9150", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da9150_i2c_id); + +static const struct of_device_id da9150_of_match[] = { + { .compatible = "dlg,da9150", }, + { } +}; +MODULE_DEVICE_TABLE(of, da9150_of_match); + +static struct i2c_driver da9150_driver = { + .driver = { + .name = "da9150", + .of_match_table = of_match_ptr(da9150_of_match), + }, + .probe = da9150_probe, + .remove = da9150_remove, + .shutdown = da9150_shutdown, + .id_table = da9150_i2c_id, +}; + +module_i2c_driver(da9150_driver); + +MODULE_DESCRIPTION("MFD Core Driver for DA9150"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/da9150/core.h b/include/linux/mfd/da9150/core.h new file mode 100644 index 00000000000000..76e668933a7746 --- /dev/null +++ b/include/linux/mfd/da9150/core.h @@ -0,0 +1,68 @@ +/* + * DA9150 MFD Driver - Core Data + * + * Copyright (c) 2014 Dialog Semiconductor + * + * Author: Adam Thomson + * + * 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 __DA9150_CORE_H +#define __DA9150_CORE_H + +#include +#include +#include + +/* I2C address paging */ +#define DA9150_REG_PAGE_SHIFT 8 +#define DA9150_REG_PAGE_MASK 0xFF + +/* IRQs */ +#define DA9150_NUM_IRQ_REGS 4 +#define DA9150_IRQ_VBUS 0 +#define DA9150_IRQ_CHG 1 +#define DA9150_IRQ_TCLASS 2 +#define DA9150_IRQ_TJUNC 3 +#define DA9150_IRQ_VFAULT 4 +#define DA9150_IRQ_CONF 5 +#define DA9150_IRQ_DAT 6 +#define DA9150_IRQ_DTYPE 7 +#define DA9150_IRQ_ID 8 +#define DA9150_IRQ_ADP 9 +#define DA9150_IRQ_SESS_END 10 +#define DA9150_IRQ_SESS_VLD 11 +#define DA9150_IRQ_FG 12 +#define DA9150_IRQ_GP 13 +#define DA9150_IRQ_TBAT 14 +#define DA9150_IRQ_GPIOA 15 +#define DA9150_IRQ_GPIOB 16 +#define DA9150_IRQ_GPIOC 17 +#define DA9150_IRQ_GPIOD 18 +#define DA9150_IRQ_GPADC 19 +#define DA9150_IRQ_WKUP 20 + +struct da9150_pdata { + int irq_base; +}; + +struct da9150 { + struct device *dev; + struct regmap *regmap; + struct regmap_irq_chip_data *regmap_irq_data; + int irq; + int irq_base; +}; + +/* Device I/O */ +u8 da9150_reg_read(struct da9150 *da9150, u16 reg); +void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val); +void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val); + +void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf); +void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf); +#endif /* __DA9150_CORE_H */ diff --git a/include/linux/mfd/da9150/registers.h b/include/linux/mfd/da9150/registers.h new file mode 100644 index 00000000000000..27ca6ee4d84000 --- /dev/null +++ b/include/linux/mfd/da9150/registers.h @@ -0,0 +1,1155 @@ +/* + * DA9150 MFD Driver - Registers + * + * Copyright (c) 2014 Dialog Semiconductor + * + * Author: Adam Thomson + * + * 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 __DA9150_REGISTERS_H +#define __DA9150_REGISTERS_H + +#include + +/* Registers */ +#define DA9150_PAGE_CON 0x000 +#define DA9150_STATUS_A 0x068 +#define DA9150_STATUS_B 0x069 +#define DA9150_STATUS_C 0x06A +#define DA9150_STATUS_D 0x06B +#define DA9150_STATUS_E 0x06C +#define DA9150_STATUS_F 0x06D +#define DA9150_STATUS_G 0x06E +#define DA9150_STATUS_H 0x06F +#define DA9150_STATUS_I 0x070 +#define DA9150_STATUS_J 0x071 +#define DA9150_STATUS_K 0x072 +#define DA9150_STATUS_L 0x073 +#define DA9150_STATUS_N 0x074 +#define DA9150_FAULT_LOG_A 0x076 +#define DA9150_FAULT_LOG_B 0x077 +#define DA9150_EVENT_E 0x078 +#define DA9150_EVENT_F 0x079 +#define DA9150_EVENT_G 0x07A +#define DA9150_EVENT_H 0x07B +#define DA9150_IRQ_MASK_E 0x07C +#define DA9150_IRQ_MASK_F 0x07D +#define DA9150_IRQ_MASK_G 0x07E +#define DA9150_IRQ_MASK_H 0x07F +#define DA9150_PAGE_CON_1 0x080 +#define DA9150_CONFIG_A 0x0E0 +#define DA9150_CONFIG_B 0x0E1 +#define DA9150_CONFIG_C 0x0E2 +#define DA9150_CONFIG_D 0x0E3 +#define DA9150_CONFIG_E 0x0E4 +#define DA9150_CONTROL_A 0x0E5 +#define DA9150_CONTROL_B 0x0E6 +#define DA9150_CONTROL_C 0x0E7 +#define DA9150_GPIO_A_B 0x0E8 +#define DA9150_GPIO_C_D 0x0E9 +#define DA9150_GPIO_MODE_CONT 0x0EA +#define DA9150_GPIO_CTRL_B 0x0EB +#define DA9150_GPIO_CTRL_A 0x0EC +#define DA9150_GPIO_CTRL_C 0x0ED +#define DA9150_GPIO_CFG_A 0x0EE +#define DA9150_GPIO_CFG_B 0x0EF +#define DA9150_GPIO_CFG_C 0x0F0 +#define DA9150_GPADC_MAN 0x0F2 +#define DA9150_GPADC_RES_A 0x0F4 +#define DA9150_GPADC_RES_B 0x0F5 +#define DA9150_PAGE_CON_2 0x100 +#define DA9150_OTP_CONT_SHARED 0x101 +#define DA9150_INTERFACE_SHARED 0x105 +#define DA9150_CONFIG_A_SHARED 0x106 +#define DA9150_CONFIG_D_SHARED 0x109 +#define DA9150_ADETVB_CFG_C 0x150 +#define DA9150_ADETD_STAT 0x151 +#define DA9150_ADET_CMPSTAT 0x152 +#define DA9150_ADET_CTRL_A 0x153 +#define DA9150_ADETVB_CFG_B 0x154 +#define DA9150_ADETVB_CFG_A 0x155 +#define DA9150_ADETAC_CFG_A 0x156 +#define DA9150_ADDETAC_CFG_B 0x157 +#define DA9150_ADETAC_CFG_C 0x158 +#define DA9150_ADETAC_CFG_D 0x159 +#define DA9150_ADETVB_CFG_D 0x15A +#define DA9150_ADETID_CFG_A 0x15B +#define DA9150_ADET_RID_PT_CHG_H 0x15C +#define DA9150_ADET_RID_PT_CHG_L 0x15D +#define DA9150_PPR_TCTR_B 0x160 +#define DA9150_PPR_BKCTRL_A 0x163 +#define DA9150_PPR_BKCFG_A 0x164 +#define DA9150_PPR_BKCFG_B 0x165 +#define DA9150_PPR_CHGCTRL_A 0x166 +#define DA9150_PPR_CHGCTRL_B 0x167 +#define DA9150_PPR_CHGCTRL_C 0x168 +#define DA9150_PPR_TCTR_A 0x169 +#define DA9150_PPR_CHGCTRL_D 0x16A +#define DA9150_PPR_CHGCTRL_E 0x16B +#define DA9150_PPR_CHGCTRL_F 0x16C +#define DA9150_PPR_CHGCTRL_G 0x16D +#define DA9150_PPR_CHGCTRL_H 0x16E +#define DA9150_PPR_CHGCTRL_I 0x16F +#define DA9150_PPR_CHGCTRL_J 0x170 +#define DA9150_PPR_CHGCTRL_K 0x171 +#define DA9150_PPR_CHGCTRL_L 0x172 +#define DA9150_PPR_CHGCTRL_M 0x173 +#define DA9150_PPR_THYST_A 0x174 +#define DA9150_PPR_THYST_B 0x175 +#define DA9150_PPR_THYST_C 0x176 +#define DA9150_PPR_THYST_D 0x177 +#define DA9150_PPR_THYST_E 0x178 +#define DA9150_PPR_THYST_F 0x179 +#define DA9150_PPR_THYST_G 0x17A +#define DA9150_PAGE_CON_3 0x180 +#define DA9150_PAGE_CON_4 0x200 +#define DA9150_PAGE_CON_5 0x280 +#define DA9150_PAGE_CON_6 0x300 +#define DA9150_COREBTLD_STAT_A 0x302 +#define DA9150_COREBTLD_CTRL_A 0x303 +#define DA9150_CORE_CONFIG_A 0x304 +#define DA9150_CORE_CONFIG_C 0x305 +#define DA9150_CORE_CONFIG_B 0x306 +#define DA9150_CORE_CFG_DATA_A 0x307 +#define DA9150_CORE_CFG_DATA_B 0x308 +#define DA9150_CORE_CMD_A 0x309 +#define DA9150_CORE_DATA_A 0x30A +#define DA9150_CORE_DATA_B 0x30B +#define DA9150_CORE_DATA_C 0x30C +#define DA9150_CORE_DATA_D 0x30D +#define DA9150_CORE2WIRE_STAT_A 0x310 +#define DA9150_CORE2WIRE_CTRL_A 0x311 +#define DA9150_FW_CTRL_A 0x312 +#define DA9150_FW_CTRL_C 0x313 +#define DA9150_FW_CTRL_D 0x314 +#define DA9150_FG_CTRL_A 0x315 +#define DA9150_FG_CTRL_B 0x316 +#define DA9150_FW_CTRL_E 0x317 +#define DA9150_FW_CTRL_B 0x318 +#define DA9150_GPADC_CMAN 0x320 +#define DA9150_GPADC_CRES_A 0x322 +#define DA9150_GPADC_CRES_B 0x323 +#define DA9150_CC_CFG_A 0x328 +#define DA9150_CC_CFG_B 0x329 +#define DA9150_CC_ICHG_RES_A 0x32A +#define DA9150_CC_ICHG_RES_B 0x32B +#define DA9150_CC_IAVG_RES_A 0x32C +#define DA9150_CC_IAVG_RES_B 0x32D +#define DA9150_TAUX_CTRL_A 0x330 +#define DA9150_TAUX_RELOAD_H 0x332 +#define DA9150_TAUX_RELOAD_L 0x333 +#define DA9150_TAUX_VALUE_H 0x334 +#define DA9150_TAUX_VALUE_L 0x335 +#define DA9150_AUX_DATA_0 0x338 +#define DA9150_AUX_DATA_1 0x339 +#define DA9150_AUX_DATA_2 0x33A +#define DA9150_AUX_DATA_3 0x33B +#define DA9150_BIF_CTRL 0x340 +#define DA9150_TBAT_CTRL_A 0x342 +#define DA9150_TBAT_CTRL_B 0x343 +#define DA9150_TBAT_RES_A 0x344 +#define DA9150_TBAT_RES_B 0x345 + +/* DA9150_PAGE_CON = 0x000 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_I2C_PAGE_SHIFT 1 +#define DA9150_I2C_PAGE_MASK (0x1f << 1) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_STATUS_A = 0x068 */ +#define DA9150_WKUP_STAT_SHIFT 2 +#define DA9150_WKUP_STAT_MASK (0x0f << 2) +#define DA9150_SLEEP_STAT_SHIFT 6 +#define DA9150_SLEEP_STAT_MASK (0x03 << 6) + +/* DA9150_STATUS_B = 0x069 */ +#define DA9150_VFAULT_STAT_SHIFT 0 +#define DA9150_VFAULT_STAT_MASK BIT(0) +#define DA9150_TFAULT_STAT_SHIFT 1 +#define DA9150_TFAULT_STAT_MASK BIT(1) + +/* DA9150_STATUS_C = 0x06A */ +#define DA9150_VDD33_STAT_SHIFT 0 +#define DA9150_VDD33_STAT_MASK BIT(0) +#define DA9150_VDD33_SLEEP_SHIFT 1 +#define DA9150_VDD33_SLEEP_MASK BIT(1) +#define DA9150_LFOSC_STAT_SHIFT 7 +#define DA9150_LFOSC_STAT_MASK BIT(7) + +/* DA9150_STATUS_D = 0x06B */ +#define DA9150_GPIOA_STAT_SHIFT 0 +#define DA9150_GPIOA_STAT_MASK BIT(0) +#define DA9150_GPIOB_STAT_SHIFT 1 +#define DA9150_GPIOB_STAT_MASK BIT(1) +#define DA9150_GPIOC_STAT_SHIFT 2 +#define DA9150_GPIOC_STAT_MASK BIT(2) +#define DA9150_GPIOD_STAT_SHIFT 3 +#define DA9150_GPIOD_STAT_MASK BIT(3) + +/* DA9150_STATUS_E = 0x06C */ +#define DA9150_DTYPE_SHIFT 0 +#define DA9150_DTYPE_MASK (0x1f << 0) +#define DA9150_DTYPE_DT_NIL (0x00 << 0) +#define DA9150_DTYPE_DT_USB_OTG BIT(0) +#define DA9150_DTYPE_DT_USB_STD (0x02 << 0) +#define DA9150_DTYPE_DT_USB_CHG (0x03 << 0) +#define DA9150_DTYPE_DT_ACA_CHG (0x04 << 0) +#define DA9150_DTYPE_DT_ACA_OTG (0x05 << 0) +#define DA9150_DTYPE_DT_ACA_DOC (0x06 << 0) +#define DA9150_DTYPE_DT_DED_CHG (0x07 << 0) +#define DA9150_DTYPE_DT_CR5_CHG (0x08 << 0) +#define DA9150_DTYPE_DT_CR4_CHG (0x0c << 0) +#define DA9150_DTYPE_DT_PT_CHG (0x11 << 0) +#define DA9150_DTYPE_DT_NN_ACC (0x16 << 0) +#define DA9150_DTYPE_DT_NN_CHG (0x17 << 0) + +/* DA9150_STATUS_F = 0x06D */ +#define DA9150_SESS_VLD_SHIFT 0 +#define DA9150_SESS_VLD_MASK BIT(0) +#define DA9150_ID_ERR_SHIFT 1 +#define DA9150_ID_ERR_MASK BIT(1) +#define DA9150_PT_CHG_SHIFT 2 +#define DA9150_PT_CHG_MASK BIT(2) + +/* DA9150_STATUS_G = 0x06E */ +#define DA9150_RID_SHIFT 0 +#define DA9150_RID_MASK (0xff << 0) + +/* DA9150_STATUS_H = 0x06F */ +#define DA9150_VBUS_STAT_SHIFT 0 +#define DA9150_VBUS_STAT_MASK (0x07 << 0) +#define DA9150_VBUS_STAT_OFF (0x00 << 0) +#define DA9150_VBUS_STAT_WAIT BIT(0) +#define DA9150_VBUS_STAT_CHG (0x02 << 0) +#define DA9150_VBUS_TRED_SHIFT 3 +#define DA9150_VBUS_TRED_MASK BIT(3) +#define DA9150_VBUS_DROP_STAT_SHIFT 4 +#define DA9150_VBUS_DROP_STAT_MASK (0x0f << 4) + +/* DA9150_STATUS_I = 0x070 */ +#define DA9150_VBUS_ISET_STAT_SHIFT 0 +#define DA9150_VBUS_ISET_STAT_MASK (0x1f << 0) +#define DA9150_VBUS_OT_SHIFT 7 +#define DA9150_VBUS_OT_MASK BIT(7) + +/* DA9150_STATUS_J = 0x071 */ +#define DA9150_CHG_STAT_SHIFT 0 +#define DA9150_CHG_STAT_MASK (0x0f << 0) +#define DA9150_CHG_STAT_OFF (0x00 << 0) +#define DA9150_CHG_STAT_SUSP BIT(0) +#define DA9150_CHG_STAT_ACT (0x02 << 0) +#define DA9150_CHG_STAT_PRE (0x03 << 0) +#define DA9150_CHG_STAT_CC (0x04 << 0) +#define DA9150_CHG_STAT_CV (0x05 << 0) +#define DA9150_CHG_STAT_FULL (0x06 << 0) +#define DA9150_CHG_STAT_TEMP (0x07 << 0) +#define DA9150_CHG_STAT_TIME (0x08 << 0) +#define DA9150_CHG_STAT_BAT (0x09 << 0) +#define DA9150_CHG_TEMP_SHIFT 4 +#define DA9150_CHG_TEMP_MASK (0x07 << 4) +#define DA9150_CHG_TEMP_UNDER (0x06 << 4) +#define DA9150_CHG_TEMP_OVER (0x07 << 4) +#define DA9150_CHG_IEND_STAT_SHIFT 7 +#define DA9150_CHG_IEND_STAT_MASK BIT(7) + +/* DA9150_STATUS_K = 0x072 */ +#define DA9150_CHG_IAV_H_SHIFT 0 +#define DA9150_CHG_IAV_H_MASK (0xff << 0) + +/* DA9150_STATUS_L = 0x073 */ +#define DA9150_CHG_IAV_L_SHIFT 5 +#define DA9150_CHG_IAV_L_MASK (0x07 << 5) + +/* DA9150_STATUS_N = 0x074 */ +#define DA9150_CHG_TIME_SHIFT 1 +#define DA9150_CHG_TIME_MASK BIT(1) +#define DA9150_CHG_TRED_SHIFT 2 +#define DA9150_CHG_TRED_MASK BIT(2) +#define DA9150_CHG_TJUNC_CLASS_SHIFT 3 +#define DA9150_CHG_TJUNC_CLASS_MASK (0x07 << 3) +#define DA9150_CHG_TJUNC_CLASS_6 (0x06 << 3) +#define DA9150_EBS_STAT_SHIFT 6 +#define DA9150_EBS_STAT_MASK BIT(6) +#define DA9150_CHG_BAT_REMOVED_SHIFT 7 +#define DA9150_CHG_BAT_REMOVED_MASK BIT(7) + +/* DA9150_FAULT_LOG_A = 0x076 */ +#define DA9150_TEMP_FAULT_SHIFT 0 +#define DA9150_TEMP_FAULT_MASK BIT(0) +#define DA9150_VSYS_FAULT_SHIFT 1 +#define DA9150_VSYS_FAULT_MASK BIT(1) +#define DA9150_START_FAULT_SHIFT 2 +#define DA9150_START_FAULT_MASK BIT(2) +#define DA9150_EXT_FAULT_SHIFT 3 +#define DA9150_EXT_FAULT_MASK BIT(3) +#define DA9150_POR_FAULT_SHIFT 4 +#define DA9150_POR_FAULT_MASK BIT(4) + +/* DA9150_FAULT_LOG_B = 0x077 */ +#define DA9150_VBUS_FAULT_SHIFT 0 +#define DA9150_VBUS_FAULT_MASK BIT(0) +#define DA9150_OTG_FAULT_SHIFT 1 +#define DA9150_OTG_FAULT_MASK BIT(1) + +/* DA9150_EVENT_E = 0x078 */ +#define DA9150_E_VBUS_SHIFT 0 +#define DA9150_E_VBUS_MASK BIT(0) +#define DA9150_E_CHG_SHIFT 1 +#define DA9150_E_CHG_MASK BIT(1) +#define DA9150_E_TCLASS_SHIFT 2 +#define DA9150_E_TCLASS_MASK BIT(2) +#define DA9150_E_TJUNC_SHIFT 3 +#define DA9150_E_TJUNC_MASK BIT(3) +#define DA9150_E_VFAULT_SHIFT 4 +#define DA9150_E_VFAULT_MASK BIT(4) +#define DA9150_EVENTS_H_SHIFT 5 +#define DA9150_EVENTS_H_MASK BIT(5) +#define DA9150_EVENTS_G_SHIFT 6 +#define DA9150_EVENTS_G_MASK BIT(6) +#define DA9150_EVENTS_F_SHIFT 7 +#define DA9150_EVENTS_F_MASK BIT(7) + +/* DA9150_EVENT_F = 0x079 */ +#define DA9150_E_CONF_SHIFT 0 +#define DA9150_E_CONF_MASK BIT(0) +#define DA9150_E_DAT_SHIFT 1 +#define DA9150_E_DAT_MASK BIT(1) +#define DA9150_E_DTYPE_SHIFT 3 +#define DA9150_E_DTYPE_MASK BIT(3) +#define DA9150_E_ID_SHIFT 4 +#define DA9150_E_ID_MASK BIT(4) +#define DA9150_E_ADP_SHIFT 5 +#define DA9150_E_ADP_MASK BIT(5) +#define DA9150_E_SESS_END_SHIFT 6 +#define DA9150_E_SESS_END_MASK BIT(6) +#define DA9150_E_SESS_VLD_SHIFT 7 +#define DA9150_E_SESS_VLD_MASK BIT(7) + +/* DA9150_EVENT_G = 0x07A */ +#define DA9150_E_FG_SHIFT 0 +#define DA9150_E_FG_MASK BIT(0) +#define DA9150_E_GP_SHIFT 1 +#define DA9150_E_GP_MASK BIT(1) +#define DA9150_E_TBAT_SHIFT 2 +#define DA9150_E_TBAT_MASK BIT(2) +#define DA9150_E_GPIOA_SHIFT 3 +#define DA9150_E_GPIOA_MASK BIT(3) +#define DA9150_E_GPIOB_SHIFT 4 +#define DA9150_E_GPIOB_MASK BIT(4) +#define DA9150_E_GPIOC_SHIFT 5 +#define DA9150_E_GPIOC_MASK BIT(5) +#define DA9150_E_GPIOD_SHIFT 6 +#define DA9150_E_GPIOD_MASK BIT(6) +#define DA9150_E_GPADC_SHIFT 7 +#define DA9150_E_GPADC_MASK BIT(7) + +/* DA9150_EVENT_H = 0x07B */ +#define DA9150_E_WKUP_SHIFT 0 +#define DA9150_E_WKUP_MASK BIT(0) + +/* DA9150_IRQ_MASK_E = 0x07C */ +#define DA9150_M_VBUS_SHIFT 0 +#define DA9150_M_VBUS_MASK BIT(0) +#define DA9150_M_CHG_SHIFT 1 +#define DA9150_M_CHG_MASK BIT(1) +#define DA9150_M_TJUNC_SHIFT 3 +#define DA9150_M_TJUNC_MASK BIT(3) +#define DA9150_M_VFAULT_SHIFT 4 +#define DA9150_M_VFAULT_MASK BIT(4) + +/* DA9150_IRQ_MASK_F = 0x07D */ +#define DA9150_M_CONF_SHIFT 0 +#define DA9150_M_CONF_MASK BIT(0) +#define DA9150_M_DAT_SHIFT 1 +#define DA9150_M_DAT_MASK BIT(1) +#define DA9150_M_DTYPE_SHIFT 3 +#define DA9150_M_DTYPE_MASK BIT(3) +#define DA9150_M_ID_SHIFT 4 +#define DA9150_M_ID_MASK BIT(4) +#define DA9150_M_ADP_SHIFT 5 +#define DA9150_M_ADP_MASK BIT(5) +#define DA9150_M_SESS_END_SHIFT 6 +#define DA9150_M_SESS_END_MASK BIT(6) +#define DA9150_M_SESS_VLD_SHIFT 7 +#define DA9150_M_SESS_VLD_MASK BIT(7) + +/* DA9150_IRQ_MASK_G = 0x07E */ +#define DA9150_M_FG_SHIFT 0 +#define DA9150_M_FG_MASK BIT(0) +#define DA9150_M_GP_SHIFT 1 +#define DA9150_M_GP_MASK BIT(1) +#define DA9150_M_TBAT_SHIFT 2 +#define DA9150_M_TBAT_MASK BIT(2) +#define DA9150_M_GPIOA_SHIFT 3 +#define DA9150_M_GPIOA_MASK BIT(3) +#define DA9150_M_GPIOB_SHIFT 4 +#define DA9150_M_GPIOB_MASK BIT(4) +#define DA9150_M_GPIOC_SHIFT 5 +#define DA9150_M_GPIOC_MASK BIT(5) +#define DA9150_M_GPIOD_SHIFT 6 +#define DA9150_M_GPIOD_MASK BIT(6) +#define DA9150_M_GPADC_SHIFT 7 +#define DA9150_M_GPADC_MASK BIT(7) + +/* DA9150_IRQ_MASK_H = 0x07F */ +#define DA9150_M_WKUP_SHIFT 0 +#define DA9150_M_WKUP_MASK BIT(0) + +/* DA9150_PAGE_CON_1 = 0x080 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_CONFIG_A = 0x0E0 */ +#define DA9150_RESET_DUR_SHIFT 0 +#define DA9150_RESET_DUR_MASK (0x03 << 0) +#define DA9150_RESET_EXT_SHIFT 2 +#define DA9150_RESET_EXT_MASK (0x03 << 2) +#define DA9150_START_MAX_SHIFT 4 +#define DA9150_START_MAX_MASK (0x03 << 4) +#define DA9150_PS_WAIT_EN_SHIFT 6 +#define DA9150_PS_WAIT_EN_MASK BIT(6) +#define DA9150_PS_DISABLE_DIRECT_SHIFT 7 +#define DA9150_PS_DISABLE_DIRECT_MASK BIT(7) + +/* DA9150_CONFIG_B = 0x0E1 */ +#define DA9150_VFAULT_ADJ_SHIFT 0 +#define DA9150_VFAULT_ADJ_MASK (0x0f << 0) +#define DA9150_VFAULT_HYST_SHIFT 4 +#define DA9150_VFAULT_HYST_MASK (0x07 << 4) +#define DA9150_VFAULT_EN_SHIFT 7 +#define DA9150_VFAULT_EN_MASK BIT(7) + +/* DA9150_CONFIG_C = 0x0E2 */ +#define DA9150_VSYS_MIN_SHIFT 3 +#define DA9150_VSYS_MIN_MASK (0x1f << 3) + +/* DA9150_CONFIG_D = 0x0E3 */ +#define DA9150_LFOSC_EXT_SHIFT 0 +#define DA9150_LFOSC_EXT_MASK BIT(0) +#define DA9150_VDD33_DWN_SHIFT 1 +#define DA9150_VDD33_DWN_MASK BIT(1) +#define DA9150_WKUP_PM_EN_SHIFT 2 +#define DA9150_WKUP_PM_EN_MASK BIT(2) +#define DA9150_WKUP_CE_SEL_SHIFT 3 +#define DA9150_WKUP_CE_SEL_MASK (0x03 << 3) +#define DA9150_WKUP_CLK32K_EN_SHIFT 5 +#define DA9150_WKUP_CLK32K_EN_MASK BIT(5) +#define DA9150_DISABLE_DEL_SHIFT 7 +#define DA9150_DISABLE_DEL_MASK BIT(7) + +/* DA9150_CONFIG_E = 0x0E4 */ +#define DA9150_PM_SPKSUP_DIS_SHIFT 0 +#define DA9150_PM_SPKSUP_DIS_MASK BIT(0) +#define DA9150_PM_MERGE_SHIFT 1 +#define DA9150_PM_MERGE_MASK BIT(1) +#define DA9150_PM_SR_OFF_SHIFT 2 +#define DA9150_PM_SR_OFF_MASK BIT(2) +#define DA9150_PM_TIMEOUT_EN_SHIFT 3 +#define DA9150_PM_TIMEOUT_EN_MASK BIT(3) +#define DA9150_PM_DLY_SEL_SHIFT 4 +#define DA9150_PM_DLY_SEL_MASK (0x07 << 4) +#define DA9150_PM_OUT_DLY_SEL_SHIFT 7 +#define DA9150_PM_OUT_DLY_SEL_MASK BIT(7) + +/* DA9150_CONTROL_A = 0x0E5 */ +#define DA9150_VDD33_SL_SHIFT 0 +#define DA9150_VDD33_SL_MASK BIT(0) +#define DA9150_VDD33_LPM_SHIFT 1 +#define DA9150_VDD33_LPM_MASK (0x03 << 1) +#define DA9150_VDD33_EN_SHIFT 3 +#define DA9150_VDD33_EN_MASK BIT(3) +#define DA9150_GPI_LPM_SHIFT 6 +#define DA9150_GPI_LPM_MASK BIT(6) +#define DA9150_PM_IF_LPM_SHIFT 7 +#define DA9150_PM_IF_LPM_MASK BIT(7) + +/* DA9150_CONTROL_B = 0x0E6 */ +#define DA9150_LPM_SHIFT 0 +#define DA9150_LPM_MASK BIT(0) +#define DA9150_RESET_SHIFT 1 +#define DA9150_RESET_MASK BIT(1) +#define DA9150_RESET_USRCONF_EN_SHIFT 2 +#define DA9150_RESET_USRCONF_EN_MASK BIT(2) + +/* DA9150_CONTROL_C = 0x0E7 */ +#define DA9150_DISABLE_SHIFT 0 +#define DA9150_DISABLE_MASK BIT(0) + +/* DA9150_GPIO_A_B = 0x0E8 */ +#define DA9150_GPIOA_PIN_SHIFT 0 +#define DA9150_GPIOA_PIN_MASK (0x07 << 0) +#define DA9150_GPIOA_PIN_GPI (0x00 << 0) +#define DA9150_GPIOA_PIN_GPO_OD BIT(0) +#define DA9150_GPIOA_TYPE_SHIFT 3 +#define DA9150_GPIOA_TYPE_MASK BIT(3) +#define DA9150_GPIOB_PIN_SHIFT 4 +#define DA9150_GPIOB_PIN_MASK (0x07 << 4) +#define DA9150_GPIOB_PIN_GPI (0x00 << 4) +#define DA9150_GPIOB_PIN_GPO_OD BIT(4) +#define DA9150_GPIOB_TYPE_SHIFT 7 +#define DA9150_GPIOB_TYPE_MASK BIT(7) + +/* DA9150_GPIO_C_D = 0x0E9 */ +#define DA9150_GPIOC_PIN_SHIFT 0 +#define DA9150_GPIOC_PIN_MASK (0x07 << 0) +#define DA9150_GPIOC_PIN_GPI (0x00 << 0) +#define DA9150_GPIOC_PIN_GPO_OD BIT(0) +#define DA9150_GPIOC_TYPE_SHIFT 3 +#define DA9150_GPIOC_TYPE_MASK BIT(3) +#define DA9150_GPIOD_PIN_SHIFT 4 +#define DA9150_GPIOD_PIN_MASK (0x07 << 4) +#define DA9150_GPIOD_PIN_GPI (0x00 << 4) +#define DA9150_GPIOD_PIN_GPO_OD BIT(4) +#define DA9150_GPIOD_TYPE_SHIFT 7 +#define DA9150_GPIOD_TYPE_MASK BIT(7) + +/* DA9150_GPIO_MODE_CONT = 0x0EA */ +#define DA9150_GPIOA_MODE_SHIFT 0 +#define DA9150_GPIOA_MODE_MASK BIT(0) +#define DA9150_GPIOB_MODE_SHIFT 1 +#define DA9150_GPIOB_MODE_MASK BIT(1) +#define DA9150_GPIOC_MODE_SHIFT 2 +#define DA9150_GPIOC_MODE_MASK BIT(2) +#define DA9150_GPIOD_MODE_SHIFT 3 +#define DA9150_GPIOD_MODE_MASK BIT(3) +#define DA9150_GPIOA_CONT_SHIFT 4 +#define DA9150_GPIOA_CONT_MASK BIT(4) +#define DA9150_GPIOB_CONT_SHIFT 5 +#define DA9150_GPIOB_CONT_MASK BIT(5) +#define DA9150_GPIOC_CONT_SHIFT 6 +#define DA9150_GPIOC_CONT_MASK BIT(6) +#define DA9150_GPIOD_CONT_SHIFT 7 +#define DA9150_GPIOD_CONT_MASK BIT(7) + +/* DA9150_GPIO_CTRL_B = 0x0EB */ +#define DA9150_WAKE_PIN_SHIFT 0 +#define DA9150_WAKE_PIN_MASK (0x03 << 0) +#define DA9150_WAKE_MODE_SHIFT 2 +#define DA9150_WAKE_MODE_MASK BIT(2) +#define DA9150_WAKE_CONT_SHIFT 3 +#define DA9150_WAKE_CONT_MASK BIT(3) +#define DA9150_WAKE_DLY_SHIFT 4 +#define DA9150_WAKE_DLY_MASK BIT(4) + +/* DA9150_GPIO_CTRL_A = 0x0EC */ +#define DA9150_GPIOA_ANAEN_SHIFT 0 +#define DA9150_GPIOA_ANAEN_MASK BIT(0) +#define DA9150_GPIOB_ANAEN_SHIFT 1 +#define DA9150_GPIOB_ANAEN_MASK BIT(1) +#define DA9150_GPIOC_ANAEN_SHIFT 2 +#define DA9150_GPIOC_ANAEN_MASK BIT(2) +#define DA9150_GPIOD_ANAEN_SHIFT 3 +#define DA9150_GPIOD_ANAEN_MASK BIT(3) +#define DA9150_GPIO_ANAEN 0x01 +#define DA9150_GPIO_ANAEN_MASK 0x0F +#define DA9150_CHGLED_PIN_SHIFT 5 +#define DA9150_CHGLED_PIN_MASK (0x07 << 5) + +/* DA9150_GPIO_CTRL_C = 0x0ED */ +#define DA9150_CHGBL_DUR_SHIFT 0 +#define DA9150_CHGBL_DUR_MASK (0x03 << 0) +#define DA9150_CHGBL_DBL_SHIFT 2 +#define DA9150_CHGBL_DBL_MASK BIT(2) +#define DA9150_CHGBL_FRQ_SHIFT 3 +#define DA9150_CHGBL_FRQ_MASK (0x03 << 3) +#define DA9150_CHGBL_FLKR_SHIFT 5 +#define DA9150_CHGBL_FLKR_MASK BIT(5) + +/* DA9150_GPIO_CFG_A = 0x0EE */ +#define DA9150_CE_LPM_DEB_SHIFT 0 +#define DA9150_CE_LPM_DEB_MASK (0x07 << 0) + +/* DA9150_GPIO_CFG_B = 0x0EF */ +#define DA9150_GPIOA_PUPD_SHIFT 0 +#define DA9150_GPIOA_PUPD_MASK BIT(0) +#define DA9150_GPIOB_PUPD_SHIFT 1 +#define DA9150_GPIOB_PUPD_MASK BIT(1) +#define DA9150_GPIOC_PUPD_SHIFT 2 +#define DA9150_GPIOC_PUPD_MASK BIT(2) +#define DA9150_GPIOD_PUPD_SHIFT 3 +#define DA9150_GPIOD_PUPD_MASK BIT(3) +#define DA9150_GPIO_PUPD_MASK (0xF << 0) +#define DA9150_GPI_DEB_SHIFT 4 +#define DA9150_GPI_DEB_MASK (0x07 << 4) +#define DA9150_LPM_EN_SHIFT 7 +#define DA9150_LPM_EN_MASK BIT(7) + +/* DA9150_GPIO_CFG_C = 0x0F0 */ +#define DA9150_GPI_V_SHIFT 0 +#define DA9150_GPI_V_MASK BIT(0) +#define DA9150_VDDIO_INT_SHIFT 1 +#define DA9150_VDDIO_INT_MASK BIT(1) +#define DA9150_FAULT_PIN_SHIFT 3 +#define DA9150_FAULT_PIN_MASK (0x07 << 3) +#define DA9150_FAULT_TYPE_SHIFT 6 +#define DA9150_FAULT_TYPE_MASK BIT(6) +#define DA9150_NIRQ_PUPD_SHIFT 7 +#define DA9150_NIRQ_PUPD_MASK BIT(7) + +/* DA9150_GPADC_MAN = 0x0F2 */ +#define DA9150_GPADC_EN_SHIFT 0 +#define DA9150_GPADC_EN_MASK BIT(0) +#define DA9150_GPADC_MUX_SHIFT 1 +#define DA9150_GPADC_MUX_MASK (0x1f << 1) + +/* DA9150_GPADC_RES_A = 0x0F4 */ +#define DA9150_GPADC_RES_H_SHIFT 0 +#define DA9150_GPADC_RES_H_MASK (0xff << 0) + +/* DA9150_GPADC_RES_B = 0x0F5 */ +#define DA9150_GPADC_RUN_SHIFT 0 +#define DA9150_GPADC_RUN_MASK BIT(0) +#define DA9150_GPADC_RES_L_SHIFT 6 +#define DA9150_GPADC_RES_L_MASK (0x03 << 6) +#define DA9150_GPADC_RES_L_BITS 2 + +/* DA9150_PAGE_CON_2 = 0x100 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_OTP_CONT_SHARED = 0x101 */ +#define DA9150_PC_DONE_SHIFT 3 +#define DA9150_PC_DONE_MASK BIT(3) + +/* DA9150_INTERFACE_SHARED = 0x105 */ +#define DA9150_IF_BASE_ADDR_SHIFT 4 +#define DA9150_IF_BASE_ADDR_MASK (0x0f << 4) + +/* DA9150_CONFIG_A_SHARED = 0x106 */ +#define DA9150_NIRQ_VDD_SHIFT 1 +#define DA9150_NIRQ_VDD_MASK BIT(1) +#define DA9150_NIRQ_PIN_SHIFT 2 +#define DA9150_NIRQ_PIN_MASK BIT(2) +#define DA9150_NIRQ_TYPE_SHIFT 3 +#define DA9150_NIRQ_TYPE_MASK BIT(3) +#define DA9150_PM_IF_V_SHIFT 4 +#define DA9150_PM_IF_V_MASK BIT(4) +#define DA9150_PM_IF_FMP_SHIFT 5 +#define DA9150_PM_IF_FMP_MASK BIT(5) +#define DA9150_PM_IF_HSM_SHIFT 6 +#define DA9150_PM_IF_HSM_MASK BIT(6) + +/* DA9150_CONFIG_D_SHARED = 0x109 */ +#define DA9150_NIRQ_MODE_SHIFT 1 +#define DA9150_NIRQ_MODE_MASK BIT(1) + +/* DA9150_ADETVB_CFG_C = 0x150 */ +#define DA9150_TADP_RISE_SHIFT 0 +#define DA9150_TADP_RISE_MASK (0xff << 0) + +/* DA9150_ADETD_STAT = 0x151 */ +#define DA9150_DCD_STAT_SHIFT 0 +#define DA9150_DCD_STAT_MASK BIT(0) +#define DA9150_PCD_STAT_SHIFT 1 +#define DA9150_PCD_STAT_MASK (0x03 << 1) +#define DA9150_SCD_STAT_SHIFT 3 +#define DA9150_SCD_STAT_MASK (0x03 << 3) +#define DA9150_DP_STAT_SHIFT 5 +#define DA9150_DP_STAT_MASK BIT(5) +#define DA9150_DM_STAT_SHIFT 6 +#define DA9150_DM_STAT_MASK BIT(6) + +/* DA9150_ADET_CMPSTAT = 0x152 */ +#define DA9150_DP_COMP_SHIFT 1 +#define DA9150_DP_COMP_MASK BIT(1) +#define DA9150_DM_COMP_SHIFT 2 +#define DA9150_DM_COMP_MASK BIT(2) +#define DA9150_ADP_SNS_COMP_SHIFT 3 +#define DA9150_ADP_SNS_COMP_MASK BIT(3) +#define DA9150_ADP_PRB_COMP_SHIFT 4 +#define DA9150_ADP_PRB_COMP_MASK BIT(4) +#define DA9150_ID_COMP_SHIFT 5 +#define DA9150_ID_COMP_MASK BIT(5) + +/* DA9150_ADET_CTRL_A = 0x153 */ +#define DA9150_AID_DAT_SHIFT 0 +#define DA9150_AID_DAT_MASK BIT(0) +#define DA9150_AID_ID_SHIFT 1 +#define DA9150_AID_ID_MASK BIT(1) +#define DA9150_AID_TRIG_SHIFT 2 +#define DA9150_AID_TRIG_MASK BIT(2) + +/* DA9150_ADETVB_CFG_B = 0x154 */ +#define DA9150_VB_MODE_SHIFT 0 +#define DA9150_VB_MODE_MASK (0x03 << 0) +#define DA9150_VB_MODE_VB_SESS BIT(0) + +#define DA9150_TADP_PRB_SHIFT 2 +#define DA9150_TADP_PRB_MASK BIT(2) +#define DA9150_DAT_RPD_EXT_SHIFT 5 +#define DA9150_DAT_RPD_EXT_MASK BIT(5) +#define DA9150_CONF_RPD_SHIFT 6 +#define DA9150_CONF_RPD_MASK BIT(6) +#define DA9150_CONF_SRP_SHIFT 7 +#define DA9150_CONF_SRP_MASK BIT(7) + +/* DA9150_ADETVB_CFG_A = 0x155 */ +#define DA9150_AID_MODE_SHIFT 0 +#define DA9150_AID_MODE_MASK (0x03 << 0) +#define DA9150_AID_EXT_POL_SHIFT 2 +#define DA9150_AID_EXT_POL_MASK BIT(2) + +/* DA9150_ADETAC_CFG_A = 0x156 */ +#define DA9150_ISET_CDP_SHIFT 0 +#define DA9150_ISET_CDP_MASK (0x1f << 0) +#define DA9150_CONF_DBP_SHIFT 5 +#define DA9150_CONF_DBP_MASK BIT(5) + +/* DA9150_ADDETAC_CFG_B = 0x157 */ +#define DA9150_ISET_DCHG_SHIFT 0 +#define DA9150_ISET_DCHG_MASK (0x1f << 0) +#define DA9150_CONF_GPIOA_SHIFT 5 +#define DA9150_CONF_GPIOA_MASK BIT(5) +#define DA9150_CONF_GPIOB_SHIFT 6 +#define DA9150_CONF_GPIOB_MASK BIT(6) +#define DA9150_AID_VB_SHIFT 7 +#define DA9150_AID_VB_MASK BIT(7) + +/* DA9150_ADETAC_CFG_C = 0x158 */ +#define DA9150_ISET_DEF_SHIFT 0 +#define DA9150_ISET_DEF_MASK (0x1f << 0) +#define DA9150_CONF_MODE_SHIFT 5 +#define DA9150_CONF_MODE_MASK (0x03 << 5) +#define DA9150_AID_CR_DIS_SHIFT 7 +#define DA9150_AID_CR_DIS_MASK BIT(7) + +/* DA9150_ADETAC_CFG_D = 0x159 */ +#define DA9150_ISET_UNIT_SHIFT 0 +#define DA9150_ISET_UNIT_MASK (0x1f << 0) +#define DA9150_AID_UNCLAMP_SHIFT 5 +#define DA9150_AID_UNCLAMP_MASK BIT(5) + +/* DA9150_ADETVB_CFG_D = 0x15A */ +#define DA9150_ID_MODE_SHIFT 0 +#define DA9150_ID_MODE_MASK (0x03 << 0) +#define DA9150_DAT_MODE_SHIFT 2 +#define DA9150_DAT_MODE_MASK (0x0f << 2) +#define DA9150_DAT_SWP_SHIFT 6 +#define DA9150_DAT_SWP_MASK BIT(6) +#define DA9150_DAT_CLAMP_EXT_SHIFT 7 +#define DA9150_DAT_CLAMP_EXT_MASK BIT(7) + +/* DA9150_ADETID_CFG_A = 0x15B */ +#define DA9150_TID_POLL_SHIFT 0 +#define DA9150_TID_POLL_MASK (0x07 << 0) +#define DA9150_RID_CONV_SHIFT 3 +#define DA9150_RID_CONV_MASK BIT(3) + +/* DA9150_ADET_RID_PT_CHG_H = 0x15C */ +#define DA9150_RID_PT_CHG_H_SHIFT 0 +#define DA9150_RID_PT_CHG_H_MASK (0xff << 0) + +/* DA9150_ADET_RID_PT_CHG_L = 0x15D */ +#define DA9150_RID_PT_CHG_L_SHIFT 6 +#define DA9150_RID_PT_CHG_L_MASK (0x03 << 6) + +/* DA9150_PPR_TCTR_B = 0x160 */ +#define DA9150_CHG_TCTR_VAL_SHIFT 0 +#define DA9150_CHG_TCTR_VAL_MASK (0xff << 0) + +/* DA9150_PPR_BKCTRL_A = 0x163 */ +#define DA9150_VBUS_MODE_SHIFT 0 +#define DA9150_VBUS_MODE_MASK (0x03 << 0) +#define DA9150_VBUS_MODE_CHG BIT(0) +#define DA9150_VBUS_MODE_OTG (0x02 << 0) +#define DA9150_VBUS_LPM_SHIFT 2 +#define DA9150_VBUS_LPM_MASK (0x03 << 2) +#define DA9150_VBUS_SUSP_SHIFT 4 +#define DA9150_VBUS_SUSP_MASK BIT(4) +#define DA9150_VBUS_PWM_SHIFT 5 +#define DA9150_VBUS_PWM_MASK BIT(5) +#define DA9150_VBUS_ISO_SHIFT 6 +#define DA9150_VBUS_ISO_MASK BIT(6) +#define DA9150_VBUS_LDO_SHIFT 7 +#define DA9150_VBUS_LDO_MASK BIT(7) + +/* DA9150_PPR_BKCFG_A = 0x164 */ +#define DA9150_VBUS_ISET_SHIFT 0 +#define DA9150_VBUS_ISET_MASK (0x1f << 0) +#define DA9150_VBUS_IMAX_SHIFT 5 +#define DA9150_VBUS_IMAX_MASK BIT(5) +#define DA9150_VBUS_IOTG_SHIFT 6 +#define DA9150_VBUS_IOTG_MASK (0x03 << 6) + +/* DA9150_PPR_BKCFG_B = 0x165 */ +#define DA9150_VBUS_DROP_SHIFT 0 +#define DA9150_VBUS_DROP_MASK (0x0f << 0) +#define DA9150_VBUS_FAULT_DIS_SHIFT 6 +#define DA9150_VBUS_FAULT_DIS_MASK BIT(6) +#define DA9150_OTG_FAULT_DIS_SHIFT 7 +#define DA9150_OTG_FAULT_DIS_MASK BIT(7) + +/* DA9150_PPR_CHGCTRL_A = 0x166 */ +#define DA9150_CHG_EN_SHIFT 0 +#define DA9150_CHG_EN_MASK BIT(0) + +/* DA9150_PPR_CHGCTRL_B = 0x167 */ +#define DA9150_CHG_VBAT_SHIFT 0 +#define DA9150_CHG_VBAT_MASK (0x1f << 0) +#define DA9150_CHG_VDROP_SHIFT 6 +#define DA9150_CHG_VDROP_MASK (0x03 << 6) + +/* DA9150_PPR_CHGCTRL_C = 0x168 */ +#define DA9150_CHG_VFAULT_SHIFT 0 +#define DA9150_CHG_VFAULT_MASK (0x0f << 0) +#define DA9150_CHG_IPRE_SHIFT 4 +#define DA9150_CHG_IPRE_MASK (0x03 << 4) + +/* DA9150_PPR_TCTR_A = 0x169 */ +#define DA9150_CHG_TCTR_SHIFT 0 +#define DA9150_CHG_TCTR_MASK (0x07 << 0) +#define DA9150_CHG_TCTR_MODE_SHIFT 4 +#define DA9150_CHG_TCTR_MODE_MASK BIT(4) + +/* DA9150_PPR_CHGCTRL_D = 0x16A */ +#define DA9150_CHG_IBAT_SHIFT 0 +#define DA9150_CHG_IBAT_MASK (0xff << 0) + +/* DA9150_PPR_CHGCTRL_E = 0x16B */ +#define DA9150_CHG_IEND_SHIFT 0 +#define DA9150_CHG_IEND_MASK (0xff << 0) + +/* DA9150_PPR_CHGCTRL_F = 0x16C */ +#define DA9150_CHG_VCOLD_SHIFT 0 +#define DA9150_CHG_VCOLD_MASK (0x1f << 0) +#define DA9150_TBAT_TQA_EN_SHIFT 6 +#define DA9150_TBAT_TQA_EN_MASK BIT(6) +#define DA9150_TBAT_TDP_EN_SHIFT 7 +#define DA9150_TBAT_TDP_EN_MASK BIT(7) + +/* DA9150_PPR_CHGCTRL_G = 0x16D */ +#define DA9150_CHG_VWARM_SHIFT 0 +#define DA9150_CHG_VWARM_MASK (0x1f << 0) + +/* DA9150_PPR_CHGCTRL_H = 0x16E */ +#define DA9150_CHG_VHOT_SHIFT 0 +#define DA9150_CHG_VHOT_MASK (0x1f << 0) + +/* DA9150_PPR_CHGCTRL_I = 0x16F */ +#define DA9150_CHG_ICOLD_SHIFT 0 +#define DA9150_CHG_ICOLD_MASK (0xff << 0) + +/* DA9150_PPR_CHGCTRL_J = 0x170 */ +#define DA9150_CHG_IWARM_SHIFT 0 +#define DA9150_CHG_IWARM_MASK (0xff << 0) + +/* DA9150_PPR_CHGCTRL_K = 0x171 */ +#define DA9150_CHG_IHOT_SHIFT 0 +#define DA9150_CHG_IHOT_MASK (0xff << 0) + +/* DA9150_PPR_CHGCTRL_L = 0x172 */ +#define DA9150_CHG_IBAT_TRED_SHIFT 0 +#define DA9150_CHG_IBAT_TRED_MASK (0xff << 0) + +/* DA9150_PPR_CHGCTRL_M = 0x173 */ +#define DA9150_CHG_VFLOAT_SHIFT 0 +#define DA9150_CHG_VFLOAT_MASK (0x0f << 0) +#define DA9150_CHG_LPM_SHIFT 5 +#define DA9150_CHG_LPM_MASK BIT(5) +#define DA9150_CHG_NBLO_SHIFT 6 +#define DA9150_CHG_NBLO_MASK BIT(6) +#define DA9150_EBS_EN_SHIFT 7 +#define DA9150_EBS_EN_MASK BIT(7) + +/* DA9150_PPR_THYST_A = 0x174 */ +#define DA9150_TBAT_T1_SHIFT 0 +#define DA9150_TBAT_T1_MASK (0xff << 0) + +/* DA9150_PPR_THYST_B = 0x175 */ +#define DA9150_TBAT_T2_SHIFT 0 +#define DA9150_TBAT_T2_MASK (0xff << 0) + +/* DA9150_PPR_THYST_C = 0x176 */ +#define DA9150_TBAT_T3_SHIFT 0 +#define DA9150_TBAT_T3_MASK (0xff << 0) + +/* DA9150_PPR_THYST_D = 0x177 */ +#define DA9150_TBAT_T4_SHIFT 0 +#define DA9150_TBAT_T4_MASK (0xff << 0) + +/* DA9150_PPR_THYST_E = 0x178 */ +#define DA9150_TBAT_T5_SHIFT 0 +#define DA9150_TBAT_T5_MASK (0xff << 0) + +/* DA9150_PPR_THYST_F = 0x179 */ +#define DA9150_TBAT_H1_SHIFT 0 +#define DA9150_TBAT_H1_MASK (0xff << 0) + +/* DA9150_PPR_THYST_G = 0x17A */ +#define DA9150_TBAT_H5_SHIFT 0 +#define DA9150_TBAT_H5_MASK (0xff << 0) + +/* DA9150_PAGE_CON_3 = 0x180 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_PAGE_CON_4 = 0x200 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_PAGE_CON_5 = 0x280 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_PAGE_CON_6 = 0x300 */ +#define DA9150_PAGE_SHIFT 0 +#define DA9150_PAGE_MASK (0x3f << 0) +#define DA9150_WRITE_MODE_SHIFT 6 +#define DA9150_WRITE_MODE_MASK BIT(6) +#define DA9150_REVERT_SHIFT 7 +#define DA9150_REVERT_MASK BIT(7) + +/* DA9150_COREBTLD_STAT_A = 0x302 */ +#define DA9150_BOOTLD_STAT_SHIFT 0 +#define DA9150_BOOTLD_STAT_MASK (0x03 << 0) +#define DA9150_CORE_LOCKUP_SHIFT 2 +#define DA9150_CORE_LOCKUP_MASK BIT(2) + +/* DA9150_COREBTLD_CTRL_A = 0x303 */ +#define DA9150_CORE_RESET_SHIFT 0 +#define DA9150_CORE_RESET_MASK BIT(0) +#define DA9150_CORE_STOP_SHIFT 1 +#define DA9150_CORE_STOP_MASK BIT(1) + +/* DA9150_CORE_CONFIG_A = 0x304 */ +#define DA9150_CORE_MEMMUX_SHIFT 0 +#define DA9150_CORE_MEMMUX_MASK (0x03 << 0) +#define DA9150_WDT_AUTO_START_SHIFT 2 +#define DA9150_WDT_AUTO_START_MASK BIT(2) +#define DA9150_WDT_AUTO_LOCK_SHIFT 3 +#define DA9150_WDT_AUTO_LOCK_MASK BIT(3) +#define DA9150_WDT_HLT_NO_CLK_SHIFT 4 +#define DA9150_WDT_HLT_NO_CLK_MASK BIT(4) + +/* DA9150_CORE_CONFIG_C = 0x305 */ +#define DA9150_CORE_SW_SIZE_SHIFT 0 +#define DA9150_CORE_SW_SIZE_MASK (0xff << 0) + +/* DA9150_CORE_CONFIG_B = 0x306 */ +#define DA9150_BOOTLD_EN_SHIFT 0 +#define DA9150_BOOTLD_EN_MASK BIT(0) +#define DA9150_CORE_EN_SHIFT 2 +#define DA9150_CORE_EN_MASK BIT(2) +#define DA9150_CORE_SW_SRC_SHIFT 3 +#define DA9150_CORE_SW_SRC_MASK (0x07 << 3) +#define DA9150_DEEP_SLEEP_EN_SHIFT 7 +#define DA9150_DEEP_SLEEP_EN_MASK BIT(7) + +/* DA9150_CORE_CFG_DATA_A = 0x307 */ +#define DA9150_CORE_CFG_DT_A_SHIFT 0 +#define DA9150_CORE_CFG_DT_A_MASK (0xff << 0) + +/* DA9150_CORE_CFG_DATA_B = 0x308 */ +#define DA9150_CORE_CFG_DT_B_SHIFT 0 +#define DA9150_CORE_CFG_DT_B_MASK (0xff << 0) + +/* DA9150_CORE_CMD_A = 0x309 */ +#define DA9150_CORE_CMD_SHIFT 0 +#define DA9150_CORE_CMD_MASK (0xff << 0) + +/* DA9150_CORE_DATA_A = 0x30A */ +#define DA9150_CORE_DATA_0_SHIFT 0 +#define DA9150_CORE_DATA_0_MASK (0xff << 0) + +/* DA9150_CORE_DATA_B = 0x30B */ +#define DA9150_CORE_DATA_1_SHIFT 0 +#define DA9150_CORE_DATA_1_MASK (0xff << 0) + +/* DA9150_CORE_DATA_C = 0x30C */ +#define DA9150_CORE_DATA_2_SHIFT 0 +#define DA9150_CORE_DATA_2_MASK (0xff << 0) + +/* DA9150_CORE_DATA_D = 0x30D */ +#define DA9150_CORE_DATA_3_SHIFT 0 +#define DA9150_CORE_DATA_3_MASK (0xff << 0) + +/* DA9150_CORE2WIRE_STAT_A = 0x310 */ +#define DA9150_FW_FWDL_ERR_SHIFT 7 +#define DA9150_FW_FWDL_ERR_MASK BIT(7) + +/* DA9150_CORE2WIRE_CTRL_A = 0x311 */ +#define DA9150_FW_FWDL_EN_SHIFT 0 +#define DA9150_FW_FWDL_EN_MASK BIT(0) +#define DA9150_FG_QIF_EN_SHIFT 1 +#define DA9150_FG_QIF_EN_MASK BIT(1) +#define DA9150_CORE_BASE_ADDR_SHIFT 4 +#define DA9150_CORE_BASE_ADDR_MASK (0x0f << 4) + +/* DA9150_FW_CTRL_A = 0x312 */ +#define DA9150_FW_SEAL_SHIFT 0 +#define DA9150_FW_SEAL_MASK (0xff << 0) + +/* DA9150_FW_CTRL_C = 0x313 */ +#define DA9150_FW_FWDL_CRC_SHIFT 0 +#define DA9150_FW_FWDL_CRC_MASK (0xff << 0) + +/* DA9150_FW_CTRL_D = 0x314 */ +#define DA9150_FW_FWDL_BASE_SHIFT 0 +#define DA9150_FW_FWDL_BASE_MASK (0x0f << 0) + +/* DA9150_FG_CTRL_A = 0x315 */ +#define DA9150_FG_QIF_CODE_SHIFT 0 +#define DA9150_FG_QIF_CODE_MASK (0xff << 0) + +/* DA9150_FG_CTRL_B = 0x316 */ +#define DA9150_FG_QIF_VALUE_SHIFT 0 +#define DA9150_FG_QIF_VALUE_MASK (0xff << 0) + +/* DA9150_FW_CTRL_E = 0x317 */ +#define DA9150_FW_FWDL_SEG_SHIFT 0 +#define DA9150_FW_FWDL_SEG_MASK (0xff << 0) + +/* DA9150_FW_CTRL_B = 0x318 */ +#define DA9150_FW_FWDL_VALUE_SHIFT 0 +#define DA9150_FW_FWDL_VALUE_MASK (0xff << 0) + +/* DA9150_GPADC_CMAN = 0x320 */ +#define DA9150_GPADC_CEN_SHIFT 0 +#define DA9150_GPADC_CEN_MASK BIT(0) +#define DA9150_GPADC_CMUX_SHIFT 1 +#define DA9150_GPADC_CMUX_MASK (0x1f << 1) + +/* DA9150_GPADC_CRES_A = 0x322 */ +#define DA9150_GPADC_CRES_H_SHIFT 0 +#define DA9150_GPADC_CRES_H_MASK (0xff << 0) + +/* DA9150_GPADC_CRES_B = 0x323 */ +#define DA9150_GPADC_CRUN_SHIFT 0 +#define DA9150_GPADC_CRUN_MASK BIT(0) +#define DA9150_GPADC_CRES_L_SHIFT 6 +#define DA9150_GPADC_CRES_L_MASK (0x03 << 6) + +/* DA9150_CC_CFG_A = 0x328 */ +#define DA9150_CC_EN_SHIFT 0 +#define DA9150_CC_EN_MASK BIT(0) +#define DA9150_CC_TIMEBASE_SHIFT 1 +#define DA9150_CC_TIMEBASE_MASK (0x03 << 1) +#define DA9150_CC_CFG_SHIFT 5 +#define DA9150_CC_CFG_MASK (0x03 << 5) +#define DA9150_CC_ENDLESS_MODE_SHIFT 7 +#define DA9150_CC_ENDLESS_MODE_MASK BIT(7) + +/* DA9150_CC_CFG_B = 0x329 */ +#define DA9150_CC_OPT_SHIFT 0 +#define DA9150_CC_OPT_MASK (0x03 << 0) +#define DA9150_CC_PREAMP_SHIFT 2 +#define DA9150_CC_PREAMP_MASK (0x03 << 2) + +/* DA9150_CC_ICHG_RES_A = 0x32A */ +#define DA9150_CC_ICHG_RES_H_SHIFT 0 +#define DA9150_CC_ICHG_RES_H_MASK (0xff << 0) + +/* DA9150_CC_ICHG_RES_B = 0x32B */ +#define DA9150_CC_ICHG_RES_L_SHIFT 3 +#define DA9150_CC_ICHG_RES_L_MASK (0x1f << 3) + +/* DA9150_CC_IAVG_RES_A = 0x32C */ +#define DA9150_CC_IAVG_RES_H_SHIFT 0 +#define DA9150_CC_IAVG_RES_H_MASK (0xff << 0) + +/* DA9150_CC_IAVG_RES_B = 0x32D */ +#define DA9150_CC_IAVG_RES_L_SHIFT 0 +#define DA9150_CC_IAVG_RES_L_MASK (0xff << 0) + +/* DA9150_TAUX_CTRL_A = 0x330 */ +#define DA9150_TAUX_EN_SHIFT 0 +#define DA9150_TAUX_EN_MASK BIT(0) +#define DA9150_TAUX_MOD_SHIFT 1 +#define DA9150_TAUX_MOD_MASK BIT(1) +#define DA9150_TAUX_UPDATE_SHIFT 2 +#define DA9150_TAUX_UPDATE_MASK BIT(2) + +/* DA9150_TAUX_RELOAD_H = 0x332 */ +#define DA9150_TAUX_RLD_H_SHIFT 0 +#define DA9150_TAUX_RLD_H_MASK (0xff << 0) + +/* DA9150_TAUX_RELOAD_L = 0x333 */ +#define DA9150_TAUX_RLD_L_SHIFT 3 +#define DA9150_TAUX_RLD_L_MASK (0x1f << 3) + +/* DA9150_TAUX_VALUE_H = 0x334 */ +#define DA9150_TAUX_VAL_H_SHIFT 0 +#define DA9150_TAUX_VAL_H_MASK (0xff << 0) + +/* DA9150_TAUX_VALUE_L = 0x335 */ +#define DA9150_TAUX_VAL_L_SHIFT 3 +#define DA9150_TAUX_VAL_L_MASK (0x1f << 3) + +/* DA9150_AUX_DATA_0 = 0x338 */ +#define DA9150_AUX_DAT_0_SHIFT 0 +#define DA9150_AUX_DAT_0_MASK (0xff << 0) + +/* DA9150_AUX_DATA_1 = 0x339 */ +#define DA9150_AUX_DAT_1_SHIFT 0 +#define DA9150_AUX_DAT_1_MASK (0xff << 0) + +/* DA9150_AUX_DATA_2 = 0x33A */ +#define DA9150_AUX_DAT_2_SHIFT 0 +#define DA9150_AUX_DAT_2_MASK (0xff << 0) + +/* DA9150_AUX_DATA_3 = 0x33B */ +#define DA9150_AUX_DAT_3_SHIFT 0 +#define DA9150_AUX_DAT_3_MASK (0xff << 0) + +/* DA9150_BIF_CTRL = 0x340 */ +#define DA9150_BIF_ISRC_EN_SHIFT 0 +#define DA9150_BIF_ISRC_EN_MASK BIT(0) + +/* DA9150_TBAT_CTRL_A = 0x342 */ +#define DA9150_TBAT_EN_SHIFT 0 +#define DA9150_TBAT_EN_MASK BIT(0) +#define DA9150_TBAT_SW1_SHIFT 1 +#define DA9150_TBAT_SW1_MASK BIT(1) +#define DA9150_TBAT_SW2_SHIFT 2 +#define DA9150_TBAT_SW2_MASK BIT(2) + +/* DA9150_TBAT_CTRL_B = 0x343 */ +#define DA9150_TBAT_SW_FRC_SHIFT 0 +#define DA9150_TBAT_SW_FRC_MASK BIT(0) +#define DA9150_TBAT_STAT_SW1_SHIFT 1 +#define DA9150_TBAT_STAT_SW1_MASK BIT(1) +#define DA9150_TBAT_STAT_SW2_SHIFT 2 +#define DA9150_TBAT_STAT_SW2_MASK BIT(2) +#define DA9150_TBAT_HIGH_CURR_SHIFT 3 +#define DA9150_TBAT_HIGH_CURR_MASK BIT(3) + +/* DA9150_TBAT_RES_A = 0x344 */ +#define DA9150_TBAT_RES_H_SHIFT 0 +#define DA9150_TBAT_RES_H_MASK (0xff << 0) + +/* DA9150_TBAT_RES_B = 0x345 */ +#define DA9150_TBAT_RES_DIS_SHIFT 0 +#define DA9150_TBAT_RES_DIS_MASK BIT(0) +#define DA9150_TBAT_RES_L_SHIFT 6 +#define DA9150_TBAT_RES_L_MASK (0x03 << 6) + +#endif /* __DA9150_REGISTERS_H */ From c68a8658a47615f8c07981782ec6bfd90d0d175c Mon Sep 17 00:00:00 2001 From: Ong Boon Leong Date: Wed, 14 Jan 2015 16:20:20 +0800 Subject: [PATCH 198/360] mfd: lpc_sch: Enable WDT for Intel Quark X1000 Quark X1000 uses ie6xx_wdt driver for WDT. To enable WDT, we declare WDT IO resource size for Quark X1000. Signed-off-by: Ong Boon Leong Signed-off-by: Lee Jones --- drivers/mfd/lpc_sch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 5c38df35a84d0e..a56e4ba5227b0a 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -75,6 +75,7 @@ static struct lpc_sch_info sch_chipset_info[] = { [LPC_QUARK_X1000] = { .io_size_gpio = GPIO_IO_SIZE, .irq_gpio = GPIO_IRQ_QUARK_X1000, + .io_size_wdt = WDT_IO_SIZE, }, }; From ee231aeed9dc43f3755a3d654fb3bafcb11d4e88 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 19 Jan 2015 13:51:35 +0200 Subject: [PATCH 199/360] mfd: dln2: Add start/stop RX URBs helpers This is in preparation for adding suspend / resume support. Signed-off-by: Octavian Purdila Reviewed-by: Johan Hovold Signed-off-by: Lee Jones --- drivers/mfd/dln2.c | 51 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index 6d49685d4ee4f4..8311820cde4594 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -587,12 +587,19 @@ static void dln2_free_rx_urbs(struct dln2_dev *dln2) int i; for (i = 0; i < DLN2_MAX_URBS; i++) { - usb_kill_urb(dln2->rx_urb[i]); usb_free_urb(dln2->rx_urb[i]); kfree(dln2->rx_buf[i]); } } +static void dln2_stop_rx_urbs(struct dln2_dev *dln2) +{ + int i; + + for (i = 0; i < DLN2_MAX_URBS; i++) + usb_kill_urb(dln2->rx_urb[i]); +} + static void dln2_free(struct dln2_dev *dln2) { dln2_free_rx_urbs(dln2); @@ -604,9 +611,7 @@ static int dln2_setup_rx_urbs(struct dln2_dev *dln2, struct usb_host_interface *hostif) { int i; - int ret; const int rx_max_size = DLN2_RX_BUF_SIZE; - struct device *dev = &dln2->interface->dev; for (i = 0; i < DLN2_MAX_URBS; i++) { dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); @@ -620,8 +625,19 @@ static int dln2_setup_rx_urbs(struct dln2_dev *dln2, usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev, usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in), dln2->rx_buf[i], rx_max_size, dln2_rx, dln2); + } + + return 0; +} - ret = usb_submit_urb(dln2->rx_urb[i], GFP_KERNEL); +static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp) +{ + struct device *dev = &dln2->interface->dev; + int ret; + int i; + + for (i = 0; i < DLN2_MAX_URBS; i++) { + ret = usb_submit_urb(dln2->rx_urb[i], gfp); if (ret < 0) { dev_err(dev, "failed to submit RX URB: %d\n", ret); return ret; @@ -665,9 +681,8 @@ static const struct mfd_cell dln2_devs[] = { }, }; -static void dln2_disconnect(struct usb_interface *interface) +static void dln2_stop(struct dln2_dev *dln2) { - struct dln2_dev *dln2 = usb_get_intfdata(interface); int i, j; /* don't allow starting new transfers */ @@ -696,6 +711,15 @@ static void dln2_disconnect(struct usb_interface *interface) /* wait for transfers to end */ wait_event(dln2->disconnect_wq, !dln2->active_transfers); + dln2_stop_rx_urbs(dln2); +} + +static void dln2_disconnect(struct usb_interface *interface) +{ + struct dln2_dev *dln2 = usb_get_intfdata(interface); + + dln2_stop(dln2); + mfd_remove_devices(&interface->dev); dln2_free(dln2); @@ -738,23 +762,30 @@ static int dln2_probe(struct usb_interface *interface, ret = dln2_setup_rx_urbs(dln2, hostif); if (ret) - goto out_cleanup; + goto out_free; + + ret = dln2_start_rx_urbs(dln2, GFP_KERNEL); + if (ret) + goto out_stop_rx; ret = dln2_hw_init(dln2); if (ret < 0) { dev_err(dev, "failed to initialize hardware\n"); - goto out_cleanup; + goto out_stop_rx; } ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs)); if (ret != 0) { dev_err(dev, "failed to add mfd devices to core\n"); - goto out_cleanup; + goto out_stop_rx; } return 0; -out_cleanup: +out_stop_rx: + dln2_stop_rx_urbs(dln2); + +out_free: dln2_free(dln2); return ret; From 3daa122d6b710762aff5fa6aae534ed7cc45c2d6 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 19 Jan 2015 13:51:36 +0200 Subject: [PATCH 200/360] mfd: dln2: Add suspend/resume functionality Without suspend/resume functionality in the USB driver the USB core will disconnect and reconnect the DLN2 port and because the GPIO framework does not yet support removal of an in-use controller a suspend/resume operation will result in a crash. This patch provides suspend and resume functions for the DLN2 driver so that the above scenario is avoided, if the host controller does not drop VBUS during suspend, since in this case the device state is preserved. We chose not implemented reset_resume so that if the host controller does drop VBUS the resume path will go through above the disconnect/reconnect process since it is probably better to fix the GPIO framework disconnect issue then to save and restore the device state for every driver. Signed-off-by: Octavian Purdila Reviewed-by: Johan Hovold Signed-off-by: Lee Jones --- drivers/mfd/dln2.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index 8311820cde4594..1be9bd1c046d1e 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -791,6 +791,24 @@ static int dln2_probe(struct usb_interface *interface, return ret; } +static int dln2_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct dln2_dev *dln2 = usb_get_intfdata(iface); + + dln2_stop(dln2); + + return 0; +} + +static int dln2_resume(struct usb_interface *iface) +{ + struct dln2_dev *dln2 = usb_get_intfdata(iface); + + dln2->disconnect = false; + + return dln2_start_rx_urbs(dln2, GFP_NOIO); +} + static const struct usb_device_id dln2_table[] = { { USB_DEVICE(0xa257, 0x2013) }, { } @@ -803,6 +821,8 @@ static struct usb_driver dln2_driver = { .probe = dln2_probe, .disconnect = dln2_disconnect, .id_table = dln2_table, + .suspend = dln2_suspend, + .resume = dln2_resume, }; module_usb_driver(dln2_driver); From fef22cb414387a1b8f57cbbca310fdb3895ab4da Mon Sep 17 00:00:00 2001 From: Inha Song Date: Thu, 8 Jan 2015 10:04:33 +0900 Subject: [PATCH 201/360] mfd: wm8994: Set mfd id-base for regulator devs creation to avoid conflicts After commit: 6e3f62f0793e ("mfd: core: Fix platform-device id generation") We must set the id base when register a duplicate name of mfd_cell. If not, duplicate filename error was reported. - sysfs: cannot create duplicate filename '/devices/.../wm8994-ldo' Signed-off-by: Inha Song Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm8994-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 6ca9d25cc3f0f6..53ae5af5d6e4f9 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -36,12 +36,12 @@ static const struct mfd_cell wm8994_regulator_devs[] = { { .name = "wm8994-ldo", - .id = 1, + .id = 0, .pm_runtime_no_callbacks = true, }, { .name = "wm8994-ldo", - .id = 2, + .id = 1, .pm_runtime_no_callbacks = true, }, }; @@ -344,7 +344,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) dev_set_drvdata(wm8994->dev, wm8994); /* Add the on-chip regulators first for bootstrapping */ - ret = mfd_add_devices(wm8994->dev, -1, + ret = mfd_add_devices(wm8994->dev, 0, wm8994_regulator_devs, ARRAY_SIZE(wm8994_regulator_devs), NULL, 0, NULL); From 774e0b41d485e18e655247441b8ca8b8dbe217c4 Mon Sep 17 00:00:00 2001 From: Todd E Brandt Date: Wed, 7 Jan 2015 13:25:52 -0800 Subject: [PATCH 202/360] mfd: axp20x: Add support for fuel gauge cell driver mfd/axp20x: add support for fuel gauge cell Register definitions and platform data structure for fuel gauge cell devices. Signed-off-by: Todd Brandt Acked-by: Jacob Pan Signed-off-by: Lee Jones --- include/linux/mfd/axp20x.h | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 81589d176ae873..dfabd6db7ddf7c 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -124,10 +124,27 @@ enum { #define AXP288_PMIC_ADC_H 0x56 #define AXP288_PMIC_ADC_L 0x57 #define AXP288_ADC_TS_PIN_CTRL 0x84 - #define AXP288_PMIC_ADC_EN 0x84 -#define AXP288_FG_TUNE5 0xed +/* Fuel Gauge */ +#define AXP288_FG_RDC1_REG 0xba +#define AXP288_FG_RDC0_REG 0xbb +#define AXP288_FG_OCVH_REG 0xbc +#define AXP288_FG_OCVL_REG 0xbd +#define AXP288_FG_OCV_CURVE_REG 0xc0 +#define AXP288_FG_DES_CAP1_REG 0xe0 +#define AXP288_FG_DES_CAP0_REG 0xe1 +#define AXP288_FG_CC_MTR1_REG 0xe2 +#define AXP288_FG_CC_MTR0_REG 0xe3 +#define AXP288_FG_OCV_CAP_REG 0xe4 +#define AXP288_FG_CC_CAP_REG 0xe5 +#define AXP288_FG_LOW_CAP_REG 0xe6 +#define AXP288_FG_TUNE0 0xe8 +#define AXP288_FG_TUNE1 0xe9 +#define AXP288_FG_TUNE2 0xea +#define AXP288_FG_TUNE3 0xeb +#define AXP288_FG_TUNE4 0xec +#define AXP288_FG_TUNE5 0xed /* Regulators IDs */ enum { @@ -236,4 +253,26 @@ struct axp20x_dev { const struct regmap_irq_chip *regmap_irq_chip; }; +#define BATTID_LEN 64 +#define OCV_CURVE_SIZE 32 +#define MAX_THERM_CURVE_SIZE 25 +#define PD_DEF_MIN_TEMP 0 +#define PD_DEF_MAX_TEMP 55 + +struct axp20x_fg_pdata { + char battid[BATTID_LEN + 1]; + int design_cap; + int min_volt; + int max_volt; + int max_temp; + int min_temp; + int cap1; + int cap0; + int rdc1; + int rdc0; + int ocv_curve[OCV_CURVE_SIZE]; + int tcsz; + int thermistor_curve[MAX_THERM_CURVE_SIZE][2]; +}; + #endif /* __LINUX_MFD_AXP20X_H */ From 8da90cc825fc0c0894393b19de5d57671c799a6b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:20 +0100 Subject: [PATCH 203/360] mfd: 88pm860x-core: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/88pm860x-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 3a260458016457..d2a85cde68da50 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -1111,7 +1111,7 @@ static int verify_addr(struct i2c_client *i2c) return 0; } -static struct regmap_config pm860x_regmap_config = { +static const struct regmap_config pm860x_regmap_config = { .reg_bits = 8, .val_bits = 8, }; From 7f9e3feb7c0f71f5aa797f455abf0d503a520139 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:21 +0100 Subject: [PATCH 204/360] mfd: hi6421-pmic: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/hi6421-pmic-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c index 321a2656fd0044..7210ae28bf816c 100644 --- a/drivers/mfd/hi6421-pmic-core.c +++ b/drivers/mfd/hi6421-pmic-core.c @@ -35,7 +35,7 @@ static const struct mfd_cell hi6421_devs[] = { { .name = "hi6421-regulator", }, }; -static struct regmap_config hi6421_regmap_config = { +static const struct regmap_config hi6421_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 8, From 172cb30177cbe7a0d0eb38c1f3d9534d63ce822f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:22 +0100 Subject: [PATCH 205/360] mfd: intel_soc_pmic: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_core.h | 2 +- drivers/mfd/intel_soc_pmic_crc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/intel_soc_pmic_core.h b/drivers/mfd/intel_soc_pmic_core.h index 33aacd9baddc87..9498d671984776 100644 --- a/drivers/mfd/intel_soc_pmic_core.h +++ b/drivers/mfd/intel_soc_pmic_core.h @@ -23,7 +23,7 @@ struct intel_soc_pmic_config { unsigned long irq_flags; struct mfd_cell *cell_dev; int n_cell_devs; - struct regmap_config *regmap_config; + const struct regmap_config *regmap_config; struct regmap_irq_chip *irq_chip; }; diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index c85e2ecb868ab7..4cc1b324e97173 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -111,7 +111,7 @@ static struct mfd_cell crystal_cove_dev[] = { }, }; -static struct regmap_config crystal_cove_regmap_config = { +static const struct regmap_config crystal_cove_regmap_config = { .reg_bits = 8, .val_bits = 8, From 68be231ccf95ed8b70ad1541bd4e95121704ba54 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:25 +0100 Subject: [PATCH 206/360] mfd: max77686: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 929795eae9fcbf..2b2f2ccda523ae 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -111,17 +111,17 @@ static bool max77802_is_volatile_reg(struct device *dev, unsigned int reg) max77802_rtc_is_volatile_reg(dev, reg)); } -static struct regmap_config max77686_regmap_config = { +static const struct regmap_config max77686_regmap_config = { .reg_bits = 8, .val_bits = 8, }; -static struct regmap_config max77686_rtc_regmap_config = { +static const struct regmap_config max77686_rtc_regmap_config = { .reg_bits = 8, .val_bits = 8, }; -static struct regmap_config max77802_regmap_config = { +static const struct regmap_config max77802_regmap_config = { .reg_bits = 8, .val_bits = 8, .writeable_reg = max77802_is_accessible_reg, From dd635161e086747ca6302a46f0aa28ff3e8394db Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:24 +0100 Subject: [PATCH 207/360] mfd: lm3533: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/lm3533-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index 8c29f7b27324f4..d42fbb667d8cf4 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -583,7 +583,7 @@ static bool lm3533_precious_register(struct device *dev, unsigned int reg) } } -static struct regmap_config regmap_config = { +static const struct regmap_config regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = LM3533_REG_MAX, From 1b33d5e2824152e3c09bc05a3c86ae2792864507 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:28 +0100 Subject: [PATCH 208/360] mfd: retu: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/retu-mfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index 663f8a37aa6b27..2d64430c719bca 100644 --- a/drivers/mfd/retu-mfd.c +++ b/drivers/mfd/retu-mfd.c @@ -222,7 +222,7 @@ static struct regmap_bus retu_bus = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; -static struct regmap_config retu_config = { +static const struct regmap_config retu_config = { .reg_bits = 8, .val_bits = 16, }; From 1590d4a1788d6b569b90f27c4b68ab081d6fb9ea Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:27 +0100 Subject: [PATCH 209/360] mfd: pcf50633: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/pcf50633-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 43664eb69c93f5..6155d123a84e76 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -183,7 +183,7 @@ static int pcf50633_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); -static struct regmap_config pcf50633_regmap_config = { +static const struct regmap_config pcf50633_regmap_config = { .reg_bits = 8, .val_bits = 8, }; From b8d12eac2838f3fe31bb9d2deaa42c044c8d4d23 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:23 +0100 Subject: [PATCH 210/360] mfd: davinci_voicecodec: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/davinci_voicecodec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index c835e85539b2ae..9bbc642a7b9db5 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -33,7 +33,7 @@ #include -static struct regmap_config davinci_vc_regmap = { +static const struct regmap_config davinci_vc_regmap = { .reg_bits = 32, .val_bits = 32, }; From 486212f53fe1ccad156804318ca83e77484b7de3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:29 +0100 Subject: [PATCH 211/360] mfd: smsc-ece1099: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/smsc-ece1099.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c index 90112d4cc90591..03246880d48406 100644 --- a/drivers/mfd/smsc-ece1099.c +++ b/drivers/mfd/smsc-ece1099.c @@ -24,7 +24,7 @@ #include #include -static struct regmap_config smsc_regmap_config = { +static const struct regmap_config smsc_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = SMSC_VEN_ID_H, From 18bb399f6de0314aede17fc05fbb42c9b1c1b12d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:31 +0100 Subject: [PATCH 212/360] mfd: tps65218: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/tps65218.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index d6b764349f9d30..7af11a8b975327 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -135,7 +135,7 @@ static const struct regmap_access_table tps65218_volatile_table = { .n_yes_ranges = ARRAY_SIZE(tps65218_yes_ranges), }; -static struct regmap_config tps65218_regmap_config = { +static const struct regmap_config tps65218_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_RBTREE, From 18dd21ab107325a260836be824f7bb58b3e0ff7c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:26 +0100 Subject: [PATCH 213/360] mfd: mc13xxx: i2c/spi: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/mc13xxx-i2c.c | 2 +- drivers/mfd/mc13xxx-spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index ae3addb153a2e8..68b84481156674 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -46,7 +46,7 @@ static const struct of_device_id mc13xxx_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); -static struct regmap_config mc13xxx_regmap_i2c_config = { +static const struct regmap_config mc13xxx_regmap_i2c_config = { .reg_bits = 8, .val_bits = 24, diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 702925e242c905..58a170e45d88dd 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -48,7 +48,7 @@ static const struct of_device_id mc13xxx_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); -static struct regmap_config mc13xxx_regmap_spi_config = { +static const struct regmap_config mc13xxx_regmap_spi_config = { .reg_bits = 7, .pad_bits = 1, .val_bits = 24, From af0a837de60c548032f9b364d75fadf56e3a1b09 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:30 +0100 Subject: [PATCH 214/360] mfd: tps65217: Constify struct regmap_config The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Signed-off-by: Krzysztof Kozlowski Acked-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/tps65217.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 80a919a8ca975a..7d1cfc1d3ce003 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -145,7 +145,7 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, } EXPORT_SYMBOL_GPL(tps65217_clear_bits); -static struct regmap_config tps65217_regmap_config = { +static const struct regmap_config tps65217_regmap_config = { .reg_bits = 8, .val_bits = 8, From d842b61ba9e63b77830f24f1bfdcc102f5628167 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:32 +0100 Subject: [PATCH 215/360] mfd: twl-core: Constify struct regmap_config and reg_default array The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make array of struct reg_default const as well. Signed-off-by: Krzysztof Kozlowski Acked-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/twl-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index db11b4f4061161..489674a2497e04 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -207,7 +207,7 @@ static struct twl_mapping twl4030_map[] = { { 2, TWL5031_BASEADD_INTERRUPTS }, }; -static struct reg_default twl4030_49_defaults[] = { +static const struct reg_default twl4030_49_defaults[] = { /* Audio Registers */ { 0x01, 0x00}, /* CODEC_MODE */ { 0x02, 0x00}, /* OPTION */ @@ -306,7 +306,7 @@ static const struct regmap_access_table twl4030_49_volatile_table = { .n_yes_ranges = ARRAY_SIZE(twl4030_49_volatile_ranges), }; -static struct regmap_config twl4030_regmap_config[4] = { +static const struct regmap_config twl4030_regmap_config[4] = { { /* Address 0x48 */ .reg_bits = 8, @@ -369,7 +369,7 @@ static struct twl_mapping twl6030_map[] = { { 1, TWL6030_BASEADD_GASGAUGE }, }; -static struct regmap_config twl6030_regmap_config[3] = { +static const struct regmap_config twl6030_regmap_config[3] = { { /* Address 0x48 */ .reg_bits = 8, @@ -1087,7 +1087,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) struct twl4030_platform_data *pdata = dev_get_platdata(&client->dev); struct device_node *node = client->dev.of_node; struct platform_device *pdev; - struct regmap_config *twl_regmap_config; + const struct regmap_config *twl_regmap_config; int irq_base = 0; int status; unsigned i, num_slaves; From de1e23f83441d1f44f12e56b1e972b728a821f32 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:01:33 +0100 Subject: [PATCH 216/360] mfd: twl6040: Constify struct regmap_config and reg_default array The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make array of struct reg_default const as well. Signed-off-by: Krzysztof Kozlowski Acked-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/twl6040.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index 9687645162aef9..f71ee3dbc2a24a 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -44,7 +44,7 @@ #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) #define TWL6040_NUM_SUPPLIES (2) -static struct reg_default twl6040_defaults[] = { +static const struct reg_default twl6040_defaults[] = { { 0x01, 0x4B }, /* REG_ASICID (ro) */ { 0x02, 0x00 }, /* REG_ASICREV (ro) */ { 0x03, 0x00 }, /* REG_INTID */ @@ -580,7 +580,7 @@ static bool twl6040_writeable_reg(struct device *dev, unsigned int reg) } } -static struct regmap_config twl6040_regmap_config = { +static const struct regmap_config twl6040_regmap_config = { .reg_bits = 8, .val_bits = 8, From 5dd6eeb2c0c3f5e1364b51a11a7e47629655b6ff Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 4 Jan 2015 01:11:21 +0100 Subject: [PATCH 217/360] mfd: omap-usb-host: Remove some unused functions Removes some functions that are not used anywhere: usbhs_readb() usbhs_writeb() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-host.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 04cd54dd507c2c..1d924d1533c027 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -129,16 +129,6 @@ static inline u32 usbhs_read(void __iomem *base, u32 reg) return readl_relaxed(base + reg); } -static inline void usbhs_writeb(void __iomem *base, u8 reg, u8 val) -{ - writeb_relaxed(val, base + reg); -} - -static inline u8 usbhs_readb(void __iomem *base, u8 reg) -{ - return readb_relaxed(base + reg); -} - /*-------------------------------------------------------------------------*/ /** From 2b50635ea376c89082ef1f3204bcd7791a6e37d7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 29 Dec 2014 10:09:16 +0100 Subject: [PATCH 218/360] mfd: max77686/802: Remove support for board files The driver is used only on Exynos based boards with DTS support. After removal of board file support from max77686 and max77802 regulator drivers, the MFD driver can be converted to DTS-only version. This simplifies a little the code: 1. No dead (unused) entries in platform_data structure. 2. More code removed. 3. Regulator driver does not depend on allocated memory from MFD driver. 4. It makes also easier extending the regulator driver. Add to the max77686 MFD driver dependency on CONFIG_OF because without DTS the regulator drivers (max77686 and max77802) won't bind. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + drivers/mfd/max77686.c | 23 ----------------------- include/linux/mfd/max77686-private.h | 1 - include/linux/mfd/max77686.h | 28 ---------------------------- 4 files changed, 1 insertion(+), 52 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c47b2da7986e9a..be5b684eefcf8e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -429,6 +429,7 @@ config MFD_MAX14577 config MFD_MAX77686 bool "Maxim Semiconductor MAX77686/802 PMIC Support" depends on I2C=y + depends on OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 2b2f2ccda523ae..760d08d7923d42 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -205,24 +205,10 @@ static const struct of_device_id max77686_pmic_dt_match[] = { { }, }; -static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device - *dev) -{ - struct max77686_platform_data *pd; - - pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return NULL; - - dev->platform_data = pd; - return pd; -} - static int max77686_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max77686_dev *max77686 = NULL; - struct max77686_platform_data *pdata = dev_get_platdata(&i2c->dev); const struct of_device_id *match; unsigned int data; int ret = 0; @@ -233,14 +219,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, const struct mfd_cell *cells; int n_devs; - if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node && !pdata) - pdata = max77686_i2c_parse_dt_pdata(&i2c->dev); - - if (!pdata) { - dev_err(&i2c->dev, "No platform data found.\n"); - return -EINVAL; - } - max77686 = devm_kzalloc(&i2c->dev, sizeof(struct max77686_dev), GFP_KERNEL); if (!max77686) @@ -259,7 +237,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, max77686->dev = &i2c->dev; max77686->i2c = i2c; - max77686->wakeup = pdata->wakeup; max77686->irq = i2c->irq; if (max77686->type == TYPE_MAX77686) { diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index 960b92ad450d5a..f5043490d67c95 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -447,7 +447,6 @@ struct max77686_dev { struct regmap_irq_chip_data *rtc_irq_data; int irq; - bool wakeup; struct mutex irqlock; int irq_masks_cur[MAX77686_IRQ_GROUP_NR]; int irq_masks_cache[MAX77686_IRQ_GROUP_NR]; diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h index 553f7d09258acc..bb995ab9a57564 100644 --- a/include/linux/mfd/max77686.h +++ b/include/linux/mfd/max77686.h @@ -119,12 +119,6 @@ enum max77802_regulators { MAX77802_REG_MAX, }; -struct max77686_regulator_data { - int id; - struct regulator_init_data *initdata; - struct device_node *of_node; -}; - enum max77686_opmode { MAX77686_OPMODE_NORMAL, MAX77686_OPMODE_LP, @@ -136,26 +130,4 @@ struct max77686_opmode_data { int mode; }; -struct max77686_platform_data { - int ono; - int wakeup; - - /* ---- PMIC ---- */ - struct max77686_regulator_data *regulators; - int num_regulators; - - struct max77686_opmode_data *opmode_data; - - /* - * GPIO-DVS feature is not enabled with the current version of - * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default - * voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW. - */ - int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */ - int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */ - unsigned int buck2_voltage[8]; /* buckx_voltage in uV */ - unsigned int buck3_voltage[8]; - unsigned int buck4_voltage[8]; -}; - #endif /* __LINUX_MFD_MAX77686_H */ From aa0c4b815045420ea54d5ae5362f5a0190609d46 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 26 Nov 2014 13:50:59 -0800 Subject: [PATCH 219/360] mfd: devicetree: bindings: Add Qualcomm RPM DT binding Add binding for the Qualcomm Resource Power Manager (RPM) found in 8660, 8960 and 8064 based devices. Signed-off-by: Bjorn Andersson Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/qcom-rpm.txt | 70 ++++++++ include/dt-bindings/mfd/qcom-rpm.h | 154 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/qcom-rpm.txt create mode 100644 include/dt-bindings/mfd/qcom-rpm.h diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt new file mode 100644 index 00000000000000..85e31980017aa9 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt @@ -0,0 +1,70 @@ +Qualcomm Resource Power Manager (RPM) + +This driver is used to interface with the Resource Power Manager (RPM) found in +various Qualcomm platforms. The RPM allows each component in the system to vote +for state of the system resources, such as clocks, regulators and bus +frequencies. + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,rpm-apq8064" + "qcom,rpm-msm8660" + "qcom,rpm-msm8960" + +- reg: + Usage: required + Value type: + Definition: base address and size of the RPM's message ram + +- interrupts: + Usage: required + Value type: + Definition: three entries specifying the RPM's: + 1. acknowledgement interrupt + 2. error interrupt + 3. wakeup interrupt + +- interrupt-names: + Usage: required + Value type: + Definition: must be the three strings "ack", "err" and "wakeup", in order + +- #address-cells: + Usage: required + Value type: + Definition: must be 1 + +- #size-cells: + Usage: required + Value type: + Definition: must be 0 + +- qcom,ipc: + Usage: required + Value type: + + Definition: three entries specifying the outgoing ipc bit used for + signaling the RPM: + - phandle to a syscon node representing the apcs registers + - u32 representing offset to the register within the syscon + - u32 representing the ipc bit within the register + + += EXAMPLE + + #include + + rpm@108000 { + compatible = "qcom,rpm-msm8960"; + reg = <0x108000 0x1000>; + qcom,ipc = <&apcs 0x8 2>; + + interrupts = <0 19 0>, <0 21 0>, <0 22 0>; + interrupt-names = "ack", "err", "wakeup"; + + #address-cells = <1>; + #size-cells = <0>; + }; + diff --git a/include/dt-bindings/mfd/qcom-rpm.h b/include/dt-bindings/mfd/qcom-rpm.h new file mode 100644 index 00000000000000..388a6f3d6165e8 --- /dev/null +++ b/include/dt-bindings/mfd/qcom-rpm.h @@ -0,0 +1,154 @@ +/* + * This header provides constants for the Qualcomm RPM bindings. + */ + +#ifndef _DT_BINDINGS_MFD_QCOM_RPM_H +#define _DT_BINDINGS_MFD_QCOM_RPM_H + +/* + * Constants use to identify individual resources in the RPM. + */ +#define QCOM_RPM_APPS_FABRIC_ARB 1 +#define QCOM_RPM_APPS_FABRIC_CLK 2 +#define QCOM_RPM_APPS_FABRIC_HALT 3 +#define QCOM_RPM_APPS_FABRIC_IOCTL 4 +#define QCOM_RPM_APPS_FABRIC_MODE 5 +#define QCOM_RPM_APPS_L2_CACHE_CTL 6 +#define QCOM_RPM_CFPB_CLK 7 +#define QCOM_RPM_CXO_BUFFERS 8 +#define QCOM_RPM_CXO_CLK 9 +#define QCOM_RPM_DAYTONA_FABRIC_CLK 10 +#define QCOM_RPM_DDR_DMM 11 +#define QCOM_RPM_EBI1_CLK 12 +#define QCOM_RPM_HDMI_SWITCH 13 +#define QCOM_RPM_MMFPB_CLK 14 +#define QCOM_RPM_MM_FABRIC_ARB 15 +#define QCOM_RPM_MM_FABRIC_CLK 16 +#define QCOM_RPM_MM_FABRIC_HALT 17 +#define QCOM_RPM_MM_FABRIC_IOCTL 18 +#define QCOM_RPM_MM_FABRIC_MODE 19 +#define QCOM_RPM_PLL_4 20 +#define QCOM_RPM_PM8058_LDO0 21 +#define QCOM_RPM_PM8058_LDO1 22 +#define QCOM_RPM_PM8058_LDO2 23 +#define QCOM_RPM_PM8058_LDO3 24 +#define QCOM_RPM_PM8058_LDO4 25 +#define QCOM_RPM_PM8058_LDO5 26 +#define QCOM_RPM_PM8058_LDO6 27 +#define QCOM_RPM_PM8058_LDO7 28 +#define QCOM_RPM_PM8058_LDO8 29 +#define QCOM_RPM_PM8058_LDO9 30 +#define QCOM_RPM_PM8058_LDO10 31 +#define QCOM_RPM_PM8058_LDO11 32 +#define QCOM_RPM_PM8058_LDO12 33 +#define QCOM_RPM_PM8058_LDO13 34 +#define QCOM_RPM_PM8058_LDO14 35 +#define QCOM_RPM_PM8058_LDO15 36 +#define QCOM_RPM_PM8058_LDO16 37 +#define QCOM_RPM_PM8058_LDO17 38 +#define QCOM_RPM_PM8058_LDO18 39 +#define QCOM_RPM_PM8058_LDO19 40 +#define QCOM_RPM_PM8058_LDO20 41 +#define QCOM_RPM_PM8058_LDO21 42 +#define QCOM_RPM_PM8058_LDO22 43 +#define QCOM_RPM_PM8058_LDO23 44 +#define QCOM_RPM_PM8058_LDO24 45 +#define QCOM_RPM_PM8058_LDO25 46 +#define QCOM_RPM_PM8058_LVS0 47 +#define QCOM_RPM_PM8058_LVS1 48 +#define QCOM_RPM_PM8058_NCP 49 +#define QCOM_RPM_PM8058_SMPS0 50 +#define QCOM_RPM_PM8058_SMPS1 51 +#define QCOM_RPM_PM8058_SMPS2 52 +#define QCOM_RPM_PM8058_SMPS3 53 +#define QCOM_RPM_PM8058_SMPS4 54 +#define QCOM_RPM_PM8821_LDO1 55 +#define QCOM_RPM_PM8821_SMPS1 56 +#define QCOM_RPM_PM8821_SMPS2 57 +#define QCOM_RPM_PM8901_LDO0 58 +#define QCOM_RPM_PM8901_LDO1 59 +#define QCOM_RPM_PM8901_LDO2 60 +#define QCOM_RPM_PM8901_LDO3 61 +#define QCOM_RPM_PM8901_LDO4 62 +#define QCOM_RPM_PM8901_LDO5 63 +#define QCOM_RPM_PM8901_LDO6 64 +#define QCOM_RPM_PM8901_LVS0 65 +#define QCOM_RPM_PM8901_LVS1 66 +#define QCOM_RPM_PM8901_LVS2 67 +#define QCOM_RPM_PM8901_LVS3 68 +#define QCOM_RPM_PM8901_MVS 69 +#define QCOM_RPM_PM8901_SMPS0 70 +#define QCOM_RPM_PM8901_SMPS1 71 +#define QCOM_RPM_PM8901_SMPS2 72 +#define QCOM_RPM_PM8901_SMPS3 73 +#define QCOM_RPM_PM8901_SMPS4 74 +#define QCOM_RPM_PM8921_CLK1 75 +#define QCOM_RPM_PM8921_CLK2 76 +#define QCOM_RPM_PM8921_LDO1 77 +#define QCOM_RPM_PM8921_LDO2 78 +#define QCOM_RPM_PM8921_LDO3 79 +#define QCOM_RPM_PM8921_LDO4 80 +#define QCOM_RPM_PM8921_LDO5 81 +#define QCOM_RPM_PM8921_LDO6 82 +#define QCOM_RPM_PM8921_LDO7 83 +#define QCOM_RPM_PM8921_LDO8 84 +#define QCOM_RPM_PM8921_LDO9 85 +#define QCOM_RPM_PM8921_LDO10 86 +#define QCOM_RPM_PM8921_LDO11 87 +#define QCOM_RPM_PM8921_LDO12 88 +#define QCOM_RPM_PM8921_LDO13 89 +#define QCOM_RPM_PM8921_LDO14 90 +#define QCOM_RPM_PM8921_LDO15 91 +#define QCOM_RPM_PM8921_LDO16 92 +#define QCOM_RPM_PM8921_LDO17 93 +#define QCOM_RPM_PM8921_LDO18 94 +#define QCOM_RPM_PM8921_LDO19 95 +#define QCOM_RPM_PM8921_LDO20 96 +#define QCOM_RPM_PM8921_LDO21 97 +#define QCOM_RPM_PM8921_LDO22 98 +#define QCOM_RPM_PM8921_LDO23 99 +#define QCOM_RPM_PM8921_LDO24 100 +#define QCOM_RPM_PM8921_LDO25 101 +#define QCOM_RPM_PM8921_LDO26 102 +#define QCOM_RPM_PM8921_LDO27 103 +#define QCOM_RPM_PM8921_LDO28 104 +#define QCOM_RPM_PM8921_LDO29 105 +#define QCOM_RPM_PM8921_LVS1 106 +#define QCOM_RPM_PM8921_LVS2 107 +#define QCOM_RPM_PM8921_LVS3 108 +#define QCOM_RPM_PM8921_LVS4 109 +#define QCOM_RPM_PM8921_LVS5 110 +#define QCOM_RPM_PM8921_LVS6 111 +#define QCOM_RPM_PM8921_LVS7 112 +#define QCOM_RPM_PM8921_MVS 113 +#define QCOM_RPM_PM8921_NCP 114 +#define QCOM_RPM_PM8921_SMPS1 115 +#define QCOM_RPM_PM8921_SMPS2 116 +#define QCOM_RPM_PM8921_SMPS3 117 +#define QCOM_RPM_PM8921_SMPS4 118 +#define QCOM_RPM_PM8921_SMPS5 119 +#define QCOM_RPM_PM8921_SMPS6 120 +#define QCOM_RPM_PM8921_SMPS7 121 +#define QCOM_RPM_PM8921_SMPS8 122 +#define QCOM_RPM_PXO_CLK 123 +#define QCOM_RPM_QDSS_CLK 124 +#define QCOM_RPM_SFPB_CLK 125 +#define QCOM_RPM_SMI_CLK 126 +#define QCOM_RPM_SYS_FABRIC_ARB 127 +#define QCOM_RPM_SYS_FABRIC_CLK 128 +#define QCOM_RPM_SYS_FABRIC_HALT 129 +#define QCOM_RPM_SYS_FABRIC_IOCTL 130 +#define QCOM_RPM_SYS_FABRIC_MODE 131 +#define QCOM_RPM_USB_OTG_SWITCH 132 +#define QCOM_RPM_VDDMIN_GPIO 133 + +/* + * Constants used to select force mode for regulators. + */ +#define QCOM_RPM_FORCE_MODE_NONE 0 +#define QCOM_RPM_FORCE_MODE_LPM 1 +#define QCOM_RPM_FORCE_MODE_HPM 2 +#define QCOM_RPM_FORCE_MODE_AUTO 3 +#define QCOM_RPM_FORCE_MODE_BYPASS 4 + +#endif From 58e214382bdd1eb48c5a3519182bddcb26edabad Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 26 Nov 2014 13:51:00 -0800 Subject: [PATCH 220/360] mfd: qcom-rpm: Driver for the Qualcomm RPM Driver for the Resource Power Manager (RPM) found in Qualcomm 8660, 8960 and 8064 based devices. The driver exposes resources that child drivers can operate on; to implementing regulator, clock and bus frequency drivers. Signed-off-by: Bjorn Andersson Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 14 + drivers/mfd/Makefile | 1 + drivers/mfd/qcom_rpm.c | 581 +++++++++++++++++++++++++++++++++++ include/linux/mfd/qcom_rpm.h | 13 + 4 files changed, 609 insertions(+) create mode 100644 drivers/mfd/qcom_rpm.c create mode 100644 include/linux/mfd/qcom_rpm.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index be5b684eefcf8e..38356e39adba0b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -602,6 +602,20 @@ config MFD_PM8921_CORE Say M here if you want to include support for PM8921 chip as a module. This will build a module called "pm8921-core". +config MFD_QCOM_RPM + tristate "Qualcomm Resource Power Manager (RPM)" + depends on ARCH_QCOM && OF + help + If you say yes to this option, support will be included for the + Resource Power Manager system found in the Qualcomm 8660, 8960 and + 8064 based devices. + + This is required to access many regulators, clocks and bus + frequencies controlled by the RPM on these devices. + + Say M here if you want to include support for the Qualcomm RPM as a + module. This will build a module called "qcom_rpm". + config MFD_SPMI_PMIC tristate "Qualcomm SPMI PMICs" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 02c2f2c69b949f..19f3d744e3bdad 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -153,6 +153,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o +obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c new file mode 100644 index 00000000000000..f696328c2933ff --- /dev/null +++ b/drivers/mfd/qcom_rpm.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2014, Sony Mobile Communications AB. + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Author: Bjorn Andersson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct qcom_rpm_resource { + unsigned target_id; + unsigned status_id; + unsigned select_id; + unsigned size; +}; + +struct qcom_rpm_data { + u32 version; + const struct qcom_rpm_resource *resource_table; + unsigned n_resources; +}; + +struct qcom_rpm { + struct device *dev; + struct regmap *ipc_regmap; + unsigned ipc_offset; + unsigned ipc_bit; + + struct completion ack; + struct mutex lock; + + void __iomem *status_regs; + void __iomem *ctrl_regs; + void __iomem *req_regs; + + u32 ack_status; + + const struct qcom_rpm_data *data; +}; + +#define RPM_STATUS_REG(rpm, i) ((rpm)->status_regs + (i) * 4) +#define RPM_CTRL_REG(rpm, i) ((rpm)->ctrl_regs + (i) * 4) +#define RPM_REQ_REG(rpm, i) ((rpm)->req_regs + (i) * 4) + +#define RPM_REQUEST_TIMEOUT (5 * HZ) + +#define RPM_REQUEST_CONTEXT 3 +#define RPM_REQ_SELECT 11 +#define RPM_ACK_CONTEXT 15 +#define RPM_ACK_SELECTOR 23 +#define RPM_SELECT_SIZE 7 + +#define RPM_NOTIFICATION BIT(30) +#define RPM_REJECTED BIT(31) + +#define RPM_SIGNAL BIT(2) + +static const struct qcom_rpm_resource apq8064_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, + [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, + [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 1 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 1 }, + [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 21 }, + [QCOM_RPM_PM8921_SMPS1] = { 116, 31, 30, 2 }, + [QCOM_RPM_PM8921_SMPS2] = { 118, 33, 31, 2 }, + [QCOM_RPM_PM8921_SMPS3] = { 120, 35, 32, 2 }, + [QCOM_RPM_PM8921_SMPS4] = { 122, 37, 33, 2 }, + [QCOM_RPM_PM8921_SMPS5] = { 124, 39, 34, 2 }, + [QCOM_RPM_PM8921_SMPS6] = { 126, 41, 35, 2 }, + [QCOM_RPM_PM8921_SMPS7] = { 128, 43, 36, 2 }, + [QCOM_RPM_PM8921_SMPS8] = { 130, 45, 37, 2 }, + [QCOM_RPM_PM8921_LDO1] = { 132, 47, 38, 2 }, + [QCOM_RPM_PM8921_LDO2] = { 134, 49, 39, 2 }, + [QCOM_RPM_PM8921_LDO3] = { 136, 51, 40, 2 }, + [QCOM_RPM_PM8921_LDO4] = { 138, 53, 41, 2 }, + [QCOM_RPM_PM8921_LDO5] = { 140, 55, 42, 2 }, + [QCOM_RPM_PM8921_LDO6] = { 142, 57, 43, 2 }, + [QCOM_RPM_PM8921_LDO7] = { 144, 59, 44, 2 }, + [QCOM_RPM_PM8921_LDO8] = { 146, 61, 45, 2 }, + [QCOM_RPM_PM8921_LDO9] = { 148, 63, 46, 2 }, + [QCOM_RPM_PM8921_LDO10] = { 150, 65, 47, 2 }, + [QCOM_RPM_PM8921_LDO11] = { 152, 67, 48, 2 }, + [QCOM_RPM_PM8921_LDO12] = { 154, 69, 49, 2 }, + [QCOM_RPM_PM8921_LDO13] = { 156, 71, 50, 2 }, + [QCOM_RPM_PM8921_LDO14] = { 158, 73, 51, 2 }, + [QCOM_RPM_PM8921_LDO15] = { 160, 75, 52, 2 }, + [QCOM_RPM_PM8921_LDO16] = { 162, 77, 53, 2 }, + [QCOM_RPM_PM8921_LDO17] = { 164, 79, 54, 2 }, + [QCOM_RPM_PM8921_LDO18] = { 166, 81, 55, 2 }, + [QCOM_RPM_PM8921_LDO19] = { 168, 83, 56, 2 }, + [QCOM_RPM_PM8921_LDO20] = { 170, 85, 57, 2 }, + [QCOM_RPM_PM8921_LDO21] = { 172, 87, 58, 2 }, + [QCOM_RPM_PM8921_LDO22] = { 174, 89, 59, 2 }, + [QCOM_RPM_PM8921_LDO23] = { 176, 91, 60, 2 }, + [QCOM_RPM_PM8921_LDO24] = { 178, 93, 61, 2 }, + [QCOM_RPM_PM8921_LDO25] = { 180, 95, 62, 2 }, + [QCOM_RPM_PM8921_LDO26] = { 182, 97, 63, 2 }, + [QCOM_RPM_PM8921_LDO27] = { 184, 99, 64, 2 }, + [QCOM_RPM_PM8921_LDO28] = { 186, 101, 65, 2 }, + [QCOM_RPM_PM8921_LDO29] = { 188, 103, 66, 2 }, + [QCOM_RPM_PM8921_CLK1] = { 190, 105, 67, 2 }, + [QCOM_RPM_PM8921_CLK2] = { 192, 107, 68, 2 }, + [QCOM_RPM_PM8921_LVS1] = { 194, 109, 69, 1 }, + [QCOM_RPM_PM8921_LVS2] = { 195, 110, 70, 1 }, + [QCOM_RPM_PM8921_LVS3] = { 196, 111, 71, 1 }, + [QCOM_RPM_PM8921_LVS4] = { 197, 112, 72, 1 }, + [QCOM_RPM_PM8921_LVS5] = { 198, 113, 73, 1 }, + [QCOM_RPM_PM8921_LVS6] = { 199, 114, 74, 1 }, + [QCOM_RPM_PM8921_LVS7] = { 200, 115, 75, 1 }, + [QCOM_RPM_PM8821_SMPS1] = { 201, 116, 76, 2 }, + [QCOM_RPM_PM8821_SMPS2] = { 203, 118, 77, 2 }, + [QCOM_RPM_PM8821_LDO1] = { 205, 120, 78, 2 }, + [QCOM_RPM_PM8921_NCP] = { 207, 122, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 209, 124, 81, 1 }, + [QCOM_RPM_USB_OTG_SWITCH] = { 210, 125, 82, 1 }, + [QCOM_RPM_HDMI_SWITCH] = { 211, 126, 83, 1 }, + [QCOM_RPM_DDR_DMM] = { 212, 127, 84, 2 }, + [QCOM_RPM_VDDMIN_GPIO] = { 215, 131, 89, 1 }, +}; + +static const struct qcom_rpm_data apq8064_template = { + .version = 3, + .resource_table = apq8064_rpm_resource_table, + .n_resources = ARRAY_SIZE(apq8064_rpm_resource_table), +}; + +static const struct qcom_rpm_resource msm8660_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 32, 12, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 33, 13, 6, 1 }, + [QCOM_RPM_PLL_4] = { 34, 14, 7, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 35, 15, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 36, 16, 9, 1 }, + [QCOM_RPM_MM_FABRIC_CLK] = { 37, 17, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 38, 18, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 39, 19, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 40, 20, 13, 1 }, + [QCOM_RPM_MMFPB_CLK] = { 41, 21, 14, 1 }, + [QCOM_RPM_SMI_CLK] = { 42, 22, 15, 1 }, + [QCOM_RPM_EBI1_CLK] = { 43, 23, 16, 1 }, + [QCOM_RPM_APPS_L2_CACHE_CTL] = { 44, 24, 17, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 45, 25, 18, 2 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 47, 26, 19, 3 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 51, 28, 21, 6 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 63, 29, 22, 2 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 65, 30, 23, 3 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 69, 32, 25, 22 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 105, 33, 26, 2 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 107, 34, 27, 3 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 111, 36, 29, 23 }, + [QCOM_RPM_PM8901_SMPS0] = { 134, 37, 30, 2 }, + [QCOM_RPM_PM8901_SMPS1] = { 136, 39, 31, 2 }, + [QCOM_RPM_PM8901_SMPS2] = { 138, 41, 32, 2 }, + [QCOM_RPM_PM8901_SMPS3] = { 140, 43, 33, 2 }, + [QCOM_RPM_PM8901_SMPS4] = { 142, 45, 34, 2 }, + [QCOM_RPM_PM8901_LDO0] = { 144, 47, 35, 2 }, + [QCOM_RPM_PM8901_LDO1] = { 146, 49, 36, 2 }, + [QCOM_RPM_PM8901_LDO2] = { 148, 51, 37, 2 }, + [QCOM_RPM_PM8901_LDO3] = { 150, 53, 38, 2 }, + [QCOM_RPM_PM8901_LDO4] = { 152, 55, 39, 2 }, + [QCOM_RPM_PM8901_LDO5] = { 154, 57, 40, 2 }, + [QCOM_RPM_PM8901_LDO6] = { 156, 59, 41, 2 }, + [QCOM_RPM_PM8901_LVS0] = { 158, 61, 42, 1 }, + [QCOM_RPM_PM8901_LVS1] = { 159, 62, 43, 1 }, + [QCOM_RPM_PM8901_LVS2] = { 160, 63, 44, 1 }, + [QCOM_RPM_PM8901_LVS3] = { 161, 64, 45, 1 }, + [QCOM_RPM_PM8901_MVS] = { 162, 65, 46, 1 }, + [QCOM_RPM_PM8058_SMPS0] = { 163, 66, 47, 2 }, + [QCOM_RPM_PM8058_SMPS1] = { 165, 68, 48, 2 }, + [QCOM_RPM_PM8058_SMPS2] = { 167, 70, 49, 2 }, + [QCOM_RPM_PM8058_SMPS3] = { 169, 72, 50, 2 }, + [QCOM_RPM_PM8058_SMPS4] = { 171, 74, 51, 2 }, + [QCOM_RPM_PM8058_LDO0] = { 173, 76, 52, 2 }, + [QCOM_RPM_PM8058_LDO1] = { 175, 78, 53, 2 }, + [QCOM_RPM_PM8058_LDO2] = { 177, 80, 54, 2 }, + [QCOM_RPM_PM8058_LDO3] = { 179, 82, 55, 2 }, + [QCOM_RPM_PM8058_LDO4] = { 181, 84, 56, 2 }, + [QCOM_RPM_PM8058_LDO5] = { 183, 86, 57, 2 }, + [QCOM_RPM_PM8058_LDO6] = { 185, 88, 58, 2 }, + [QCOM_RPM_PM8058_LDO7] = { 187, 90, 59, 2 }, + [QCOM_RPM_PM8058_LDO8] = { 189, 92, 60, 2 }, + [QCOM_RPM_PM8058_LDO9] = { 191, 94, 61, 2 }, + [QCOM_RPM_PM8058_LDO10] = { 193, 96, 62, 2 }, + [QCOM_RPM_PM8058_LDO11] = { 195, 98, 63, 2 }, + [QCOM_RPM_PM8058_LDO12] = { 197, 100, 64, 2 }, + [QCOM_RPM_PM8058_LDO13] = { 199, 102, 65, 2 }, + [QCOM_RPM_PM8058_LDO14] = { 201, 104, 66, 2 }, + [QCOM_RPM_PM8058_LDO15] = { 203, 106, 67, 2 }, + [QCOM_RPM_PM8058_LDO16] = { 205, 108, 68, 2 }, + [QCOM_RPM_PM8058_LDO17] = { 207, 110, 69, 2 }, + [QCOM_RPM_PM8058_LDO18] = { 209, 112, 70, 2 }, + [QCOM_RPM_PM8058_LDO19] = { 211, 114, 71, 2 }, + [QCOM_RPM_PM8058_LDO20] = { 213, 116, 72, 2 }, + [QCOM_RPM_PM8058_LDO21] = { 215, 118, 73, 2 }, + [QCOM_RPM_PM8058_LDO22] = { 217, 120, 74, 2 }, + [QCOM_RPM_PM8058_LDO23] = { 219, 122, 75, 2 }, + [QCOM_RPM_PM8058_LDO24] = { 221, 124, 76, 2 }, + [QCOM_RPM_PM8058_LDO25] = { 223, 126, 77, 2 }, + [QCOM_RPM_PM8058_LVS0] = { 225, 128, 78, 1 }, + [QCOM_RPM_PM8058_LVS1] = { 226, 129, 79, 1 }, + [QCOM_RPM_PM8058_NCP] = { 227, 130, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 229, 132, 81, 1 }, +}; + +static const struct qcom_rpm_data msm8660_template = { + .version = 2, + .resource_table = msm8660_rpm_resource_table, + .n_resources = ARRAY_SIZE(msm8660_rpm_resource_table), +}; + +static const struct qcom_rpm_resource msm8960_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, + [QCOM_RPM_MM_FABRIC_CLK] = { 29, 13, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, + [QCOM_RPM_MMFPB_CLK] = { 33, 17, 14, 1 }, + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 1 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 1 }, + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 1 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 1 }, + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 29 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 88, 27, 26, 1 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 90, 28, 27, 1 }, + [QCOM_RPM_MM_FABRIC_IOCTL] = { 93, 29, 28, 1 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 94, 30, 29, 23 }, + [QCOM_RPM_PM8921_SMPS1] = { 117, 31, 30, 2 }, + [QCOM_RPM_PM8921_SMPS2] = { 119, 33, 31, 2 }, + [QCOM_RPM_PM8921_SMPS3] = { 121, 35, 32, 2 }, + [QCOM_RPM_PM8921_SMPS4] = { 123, 37, 33, 2 }, + [QCOM_RPM_PM8921_SMPS5] = { 125, 39, 34, 2 }, + [QCOM_RPM_PM8921_SMPS6] = { 127, 41, 35, 2 }, + [QCOM_RPM_PM8921_SMPS7] = { 129, 43, 36, 2 }, + [QCOM_RPM_PM8921_SMPS8] = { 131, 45, 37, 2 }, + [QCOM_RPM_PM8921_LDO1] = { 133, 47, 38, 2 }, + [QCOM_RPM_PM8921_LDO2] = { 135, 49, 39, 2 }, + [QCOM_RPM_PM8921_LDO3] = { 137, 51, 40, 2 }, + [QCOM_RPM_PM8921_LDO4] = { 139, 53, 41, 2 }, + [QCOM_RPM_PM8921_LDO5] = { 141, 55, 42, 2 }, + [QCOM_RPM_PM8921_LDO6] = { 143, 57, 43, 2 }, + [QCOM_RPM_PM8921_LDO7] = { 145, 59, 44, 2 }, + [QCOM_RPM_PM8921_LDO8] = { 147, 61, 45, 2 }, + [QCOM_RPM_PM8921_LDO9] = { 149, 63, 46, 2 }, + [QCOM_RPM_PM8921_LDO10] = { 151, 65, 47, 2 }, + [QCOM_RPM_PM8921_LDO11] = { 153, 67, 48, 2 }, + [QCOM_RPM_PM8921_LDO12] = { 155, 69, 49, 2 }, + [QCOM_RPM_PM8921_LDO13] = { 157, 71, 50, 2 }, + [QCOM_RPM_PM8921_LDO14] = { 159, 73, 51, 2 }, + [QCOM_RPM_PM8921_LDO15] = { 161, 75, 52, 2 }, + [QCOM_RPM_PM8921_LDO16] = { 163, 77, 53, 2 }, + [QCOM_RPM_PM8921_LDO17] = { 165, 79, 54, 2 }, + [QCOM_RPM_PM8921_LDO18] = { 167, 81, 55, 2 }, + [QCOM_RPM_PM8921_LDO19] = { 169, 83, 56, 2 }, + [QCOM_RPM_PM8921_LDO20] = { 171, 85, 57, 2 }, + [QCOM_RPM_PM8921_LDO21] = { 173, 87, 58, 2 }, + [QCOM_RPM_PM8921_LDO22] = { 175, 89, 59, 2 }, + [QCOM_RPM_PM8921_LDO23] = { 177, 91, 60, 2 }, + [QCOM_RPM_PM8921_LDO24] = { 179, 93, 61, 2 }, + [QCOM_RPM_PM8921_LDO25] = { 181, 95, 62, 2 }, + [QCOM_RPM_PM8921_LDO26] = { 183, 97, 63, 2 }, + [QCOM_RPM_PM8921_LDO27] = { 185, 99, 64, 2 }, + [QCOM_RPM_PM8921_LDO28] = { 187, 101, 65, 2 }, + [QCOM_RPM_PM8921_LDO29] = { 189, 103, 66, 2 }, + [QCOM_RPM_PM8921_CLK1] = { 191, 105, 67, 2 }, + [QCOM_RPM_PM8921_CLK2] = { 193, 107, 68, 2 }, + [QCOM_RPM_PM8921_LVS1] = { 195, 109, 69, 1 }, + [QCOM_RPM_PM8921_LVS2] = { 196, 110, 70, 1 }, + [QCOM_RPM_PM8921_LVS3] = { 197, 111, 71, 1 }, + [QCOM_RPM_PM8921_LVS4] = { 198, 112, 72, 1 }, + [QCOM_RPM_PM8921_LVS5] = { 199, 113, 73, 1 }, + [QCOM_RPM_PM8921_LVS6] = { 200, 114, 74, 1 }, + [QCOM_RPM_PM8921_LVS7] = { 201, 115, 75, 1 }, + [QCOM_RPM_PM8921_NCP] = { 202, 116, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 204, 118, 81, 1 }, + [QCOM_RPM_USB_OTG_SWITCH] = { 205, 119, 82, 1 }, + [QCOM_RPM_HDMI_SWITCH] = { 206, 120, 83, 1 }, + [QCOM_RPM_DDR_DMM] = { 207, 121, 84, 2 }, +}; + +static const struct qcom_rpm_data msm8960_template = { + .version = 3, + .resource_table = msm8960_rpm_resource_table, + .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table), +}; + +static const struct of_device_id qcom_rpm_of_match[] = { + { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, + { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, + { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); + +int qcom_rpm_write(struct qcom_rpm *rpm, + int state, + int resource, + u32 *buf, size_t count) +{ + const struct qcom_rpm_resource *res; + const struct qcom_rpm_data *data = rpm->data; + u32 sel_mask[RPM_SELECT_SIZE] = { 0 }; + int left; + int ret = 0; + int i; + + if (WARN_ON(resource < 0 || resource >= data->n_resources)) + return -EINVAL; + + res = &data->resource_table[resource]; + if (WARN_ON(res->size != count)) + return -EINVAL; + + mutex_lock(&rpm->lock); + + for (i = 0; i < res->size; i++) + writel_relaxed(buf[i], RPM_REQ_REG(rpm, res->target_id + i)); + + bitmap_set((unsigned long *)sel_mask, res->select_id, 1); + for (i = 0; i < ARRAY_SIZE(sel_mask); i++) { + writel_relaxed(sel_mask[i], + RPM_CTRL_REG(rpm, RPM_REQ_SELECT + i)); + } + + writel_relaxed(BIT(state), RPM_CTRL_REG(rpm, RPM_REQUEST_CONTEXT)); + + reinit_completion(&rpm->ack); + regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit)); + + left = wait_for_completion_timeout(&rpm->ack, RPM_REQUEST_TIMEOUT); + if (!left) + ret = -ETIMEDOUT; + else if (rpm->ack_status & RPM_REJECTED) + ret = -EIO; + + mutex_unlock(&rpm->lock); + + return ret; +} +EXPORT_SYMBOL(qcom_rpm_write); + +static irqreturn_t qcom_rpm_ack_interrupt(int irq, void *dev) +{ + struct qcom_rpm *rpm = dev; + u32 ack; + int i; + + ack = readl_relaxed(RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); + for (i = 0; i < RPM_SELECT_SIZE; i++) + writel_relaxed(0, RPM_CTRL_REG(rpm, RPM_ACK_SELECTOR + i)); + writel(0, RPM_CTRL_REG(rpm, RPM_ACK_CONTEXT)); + + if (ack & RPM_NOTIFICATION) { + dev_warn(rpm->dev, "ignoring notification!\n"); + } else { + rpm->ack_status = ack; + complete(&rpm->ack); + } + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_rpm_err_interrupt(int irq, void *dev) +{ + struct qcom_rpm *rpm = dev; + + regmap_write(rpm->ipc_regmap, rpm->ipc_offset, BIT(rpm->ipc_bit)); + dev_err(rpm->dev, "RPM triggered fatal error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +static int qcom_rpm_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *syscon_np; + struct resource *res; + struct qcom_rpm *rpm; + u32 fw_version[3]; + int irq_wakeup; + int irq_ack; + int irq_err; + int ret; + + rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL); + if (!rpm) + return -ENOMEM; + + rpm->dev = &pdev->dev; + mutex_init(&rpm->lock); + init_completion(&rpm->ack); + + irq_ack = platform_get_irq_byname(pdev, "ack"); + if (irq_ack < 0) { + dev_err(&pdev->dev, "required ack interrupt missing\n"); + return irq_ack; + } + + irq_err = platform_get_irq_byname(pdev, "err"); + if (irq_err < 0) { + dev_err(&pdev->dev, "required err interrupt missing\n"); + return irq_err; + } + + irq_wakeup = platform_get_irq_byname(pdev, "wakeup"); + if (irq_wakeup < 0) { + dev_err(&pdev->dev, "required wakeup interrupt missing\n"); + return irq_wakeup; + } + + match = of_match_device(qcom_rpm_of_match, &pdev->dev); + rpm->data = match->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rpm->status_regs)) + return PTR_ERR(rpm->status_regs); + rpm->ctrl_regs = rpm->status_regs + 0x400; + rpm->req_regs = rpm->status_regs + 0x600; + + syscon_np = of_parse_phandle(pdev->dev.of_node, "qcom,ipc", 0); + if (!syscon_np) { + dev_err(&pdev->dev, "no qcom,ipc node\n"); + return -ENODEV; + } + + rpm->ipc_regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(rpm->ipc_regmap)) + return PTR_ERR(rpm->ipc_regmap); + + ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 1, + &rpm->ipc_offset); + if (ret < 0) { + dev_err(&pdev->dev, "no offset in qcom,ipc\n"); + return -EINVAL; + } + + ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,ipc", 2, + &rpm->ipc_bit); + if (ret < 0) { + dev_err(&pdev->dev, "no bit in qcom,ipc\n"); + return -EINVAL; + } + + dev_set_drvdata(&pdev->dev, rpm); + + fw_version[0] = readl(RPM_STATUS_REG(rpm, 0)); + fw_version[1] = readl(RPM_STATUS_REG(rpm, 1)); + fw_version[2] = readl(RPM_STATUS_REG(rpm, 2)); + if (fw_version[0] != rpm->data->version) { + dev_err(&pdev->dev, + "RPM version %u.%u.%u incompatible with driver version %u", + fw_version[0], + fw_version[1], + fw_version[2], + rpm->data->version); + return -EFAULT; + } + + dev_info(&pdev->dev, "RPM firmware %u.%u.%u\n", fw_version[0], + fw_version[1], + fw_version[2]); + + ret = devm_request_irq(&pdev->dev, + irq_ack, + qcom_rpm_ack_interrupt, + IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, + "qcom_rpm_ack", + rpm); + if (ret) { + dev_err(&pdev->dev, "failed to request ack interrupt\n"); + return ret; + } + + ret = irq_set_irq_wake(irq_ack, 1); + if (ret) + dev_warn(&pdev->dev, "failed to mark ack irq as wakeup\n"); + + ret = devm_request_irq(&pdev->dev, + irq_err, + qcom_rpm_err_interrupt, + IRQF_TRIGGER_RISING, + "qcom_rpm_err", + rpm); + if (ret) { + dev_err(&pdev->dev, "failed to request err interrupt\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, + irq_wakeup, + qcom_rpm_wakeup_interrupt, + IRQF_TRIGGER_RISING, + "qcom_rpm_wakeup", + rpm); + if (ret) { + dev_err(&pdev->dev, "failed to request wakeup interrupt\n"); + return ret; + } + + ret = irq_set_irq_wake(irq_wakeup, 1); + if (ret) + dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n"); + + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); +} + +static int qcom_rpm_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); + return 0; +} + +static struct platform_driver qcom_rpm_driver = { + .probe = qcom_rpm_probe, + .remove = qcom_rpm_remove, + .driver = { + .name = "qcom_rpm", + .of_match_table = qcom_rpm_of_match, + }, +}; + +static int __init qcom_rpm_init(void) +{ + return platform_driver_register(&qcom_rpm_driver); +} +arch_initcall(qcom_rpm_init); + +static void __exit qcom_rpm_exit(void) +{ + platform_driver_unregister(&qcom_rpm_driver); +} +module_exit(qcom_rpm_exit) + +MODULE_DESCRIPTION("Qualcomm Resource Power Manager driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Bjorn Andersson "); diff --git a/include/linux/mfd/qcom_rpm.h b/include/linux/mfd/qcom_rpm.h new file mode 100644 index 00000000000000..742ebf1b76ca2e --- /dev/null +++ b/include/linux/mfd/qcom_rpm.h @@ -0,0 +1,13 @@ +#ifndef __QCOM_RPM_H__ +#define __QCOM_RPM_H__ + +#include + +struct qcom_rpm; + +#define QCOM_RPM_ACTIVE_STATE 0 +#define QCOM_RPM_SLEEP_STATE 1 + +int qcom_rpm_write(struct qcom_rpm *rpm, int state, int resource, u32 *buf, size_t count); + +#endif From 803926825fa4db007437f76654e3e63bafb9b906 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 26 Nov 2014 13:51:01 -0800 Subject: [PATCH 221/360] regulator: qcom-rpm: Add missing state flag in call to RPM This adds the missing state parameter to the call down to the RPM. This is currently hard coded to the active state, as that's all we're supporting at this moment. Signed-off-by: Bjorn Andersson Signed-off-by: Lee Jones --- drivers/regulator/qcom_rpm-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c index 8364ff331a8183..edd0a177693630 100644 --- a/drivers/regulator/qcom_rpm-regulator.c +++ b/drivers/regulator/qcom_rpm-regulator.c @@ -205,6 +205,7 @@ static int rpm_reg_write(struct qcom_rpm_reg *vreg, vreg->val[req->word] |= value << req->shift; return qcom_rpm_write(vreg->rpm, + QCOM_RPM_ACTIVE_STATE, vreg->resource, vreg->val, vreg->parts->request_len); From 71e03de46d73b87aab5f80fa55449a9e40c55d17 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Tue, 20 Jan 2015 13:54:25 +0000 Subject: [PATCH 222/360] mfd: da9063: Add device tree support Add device tree support for DA9063 regulators; Real-Time Clock and Watchdog. Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- drivers/mfd/da9063-core.c | 2 ++ drivers/mfd/da9063-i2c.c | 9 +++++++++ include/linux/mfd/da9063/core.h | 1 + 3 files changed, 12 insertions(+) diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index f38bc98a3c57a6..facd3610ac77f3 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -86,6 +86,7 @@ static const struct mfd_cell da9063_devs[] = { }, { .name = DA9063_DRVNAME_WATCHDOG, + .of_compatible = "dlg,da9063-watchdog", }, { .name = DA9063_DRVNAME_HWMON, @@ -101,6 +102,7 @@ static const struct mfd_cell da9063_devs[] = { .name = DA9063_DRVNAME_RTC, .num_resources = ARRAY_SIZE(da9063_rtc_resources), .resources = da9063_rtc_resources, + .of_compatible = "dlg,da9063-rtc", }, { .name = DA9063_DRVNAME_VIBRATION, diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 21fd8d9a217b01..6f3a7c0001f9f2 100644 --- a/drivers/mfd/da9063-i2c.c +++ b/drivers/mfd/da9063-i2c.c @@ -25,6 +25,9 @@ #include #include +#include +#include + static const struct regmap_range da9063_ad_readable_ranges[] = { { .range_min = DA9063_REG_PAGE_CON, @@ -203,6 +206,11 @@ static struct regmap_config da9063_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const struct of_device_id da9063_dt_ids[] = { + { .compatible = "dlg,da9063", }, + { } +}; +MODULE_DEVICE_TABLE(of, da9063_dt_ids); static int da9063_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -257,6 +265,7 @@ static struct i2c_driver da9063_i2c_driver = { .driver = { .name = "da9063", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(da9063_dt_ids), }, .probe = da9063_i2c_probe, .remove = da9063_i2c_remove, diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h index b92a3262f8f6cd..79f4d822ba133a 100644 --- a/include/linux/mfd/da9063/core.h +++ b/include/linux/mfd/da9063/core.h @@ -36,6 +36,7 @@ enum da9063_models { enum da9063_variant_codes { PMIC_DA9063_AD = 0x3, PMIC_DA9063_BB = 0x5, + PMIC_DA9063_CA = 0x6, }; /* Interrupts */ From c7f15d43a5c59c6cedb438e02032060558303589 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Tue, 20 Jan 2015 13:54:25 +0000 Subject: [PATCH 223/360] mfd: devicetree: Add bindings for DA9063 Add device tree bindings for DA9063 regulators; Real-Time Clock and Watchdog. Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/da9063.txt | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/da9063.txt diff --git a/Documentation/devicetree/bindings/mfd/da9063.txt b/Documentation/devicetree/bindings/mfd/da9063.txt new file mode 100644 index 00000000000000..42c6fa6f1c9aa9 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/da9063.txt @@ -0,0 +1,93 @@ +* Dialog DA9063 Power Management Integrated Circuit (PMIC) + +DA9093 consists of a large and varied group of sub-devices (I2C Only): + +Device Supply Names Description +------ ------------ ----------- +da9063-regulator : : LDOs & BUCKs +da9063-rtc : : Real-Time Clock +da9063-watchdog : : Watchdog + +====== + +Required properties: + +- compatible : Should be "dlg,da9063" +- reg : Specifies the I2C slave address (this defaults to 0x58 but it can be + modified to match the chip's OTP settings). +- interrupt-parent : Specifies the reference to the interrupt controller for + the DA9063. +- interrupts : IRQ line information. +- interrupt-controller + +Sub-nodes: + +- regulators : This node defines the settings for the LDOs and BUCKs. The + DA9063 regulators are bound using their names listed below: + + bcore1 : BUCK CORE1 + bcore2 : BUCK CORE2 + bpro : BUCK PRO + bmem : BUCK MEM + bio : BUCK IO + bperi : BUCK PERI + ldo1 : LDO_1 + ldo2 : LDO_2 + ldo3 : LDO_3 + ldo4 : LDO_4 + ldo5 : LDO_5 + ldo6 : LDO_6 + ldo7 : LDO_7 + ldo8 : LDO_8 + ldo9 : LDO_9 + ldo10 : LDO_10 + ldo11 : LDO_11 + + The component follows the standard regulator framework and the bindings + details of individual regulator device can be found in: + Documentation/devicetree/bindings/regulator/regulator.txt + +- rtc : This node defines settings for the Real-Time Clock associated with + the DA9063. There are currently no entries in this binding, however + compatible = "dlg,da9063-rtc" should be added if a node is created. + +- watchdog : This node defines settings for the Watchdog timer associated + with the DA9063. There are currently no entries in this binding, however + compatible = "dlg,da9063-watchdog" should be added if a node is created. + + +Example: + + pmic0: da9063@58 { + compatible = "dlg,da9063" + reg = <0x58>; + interrupt-parent = <&gpio6>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + + rtc { + compatible = "dlg,da9063-rtc"; + }; + + wdt { + compatible = "dlg,da9063-watchdog"; + }; + + regulators { + DA9063_BCORE1: bcore1 { + regulator-name = "BCORE1"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1570000>; + regulator-min-microamp = <500000>; + regulator-max-microamp = <2000000>; + regulator-boot-on; + }; + DA9063_LDO11: ldo11 { + regulator-name = "LDO_11"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <3600000>; + regulator-boot-on; + }; + }; + }; + From bb400d2120bd253d73661c452b18485d2d5b42dd Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Wed, 21 Jan 2015 18:09:21 +0800 Subject: [PATCH 224/360] mfd: rtsx_usb: Defer autosuspend while card exists A card insertion happens after the lastest polling before reader is suspended may never have a chance to be detected. Under current 1-HZ polling interval setting in mmc_core, the worst case of such undetectablility is about 1 second. To further reduce the undetectability, detect card slot again in suspend method and defer the autosuspend if the slot is loaded. The default 2 second autosuspend delay of USB subsystem should let the next polling detects the card. Signed-off-by: Roger Tseng Signed-off-by: Lee Jones --- drivers/mfd/rtsx_usb.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index 210d1f85679e50..ede50244f265b1 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -681,9 +681,27 @@ static void rtsx_usb_disconnect(struct usb_interface *intf) #ifdef CONFIG_PM static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) { + struct rtsx_ucr *ucr = + (struct rtsx_ucr *)usb_get_intfdata(intf); + u16 val = 0; + dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n", __func__, message.event); + if (PMSG_IS_AUTO(message)) { + if (mutex_trylock(&ucr->dev_mutex)) { + rtsx_usb_get_card_status(ucr, &val); + mutex_unlock(&ucr->dev_mutex); + + /* Defer the autosuspend if card exists */ + if (val & (SD_CD | MS_CD)) + return -EAGAIN; + } else { + /* There is an ongoing operation*/ + return -EAGAIN; + } + } + return 0; } From 1862ee22ce2e28087299aebb6556a5cdc122d0ef Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Fri, 23 Jan 2015 14:45:55 +1030 Subject: [PATCH 225/360] virtio-mmio: Update the device to OASIS spec version This patch add a support for second version of the virtio-mmio device, which follows OASIS "Virtual I/O Device (VIRTIO) Version 1.0" specification. Main changes: 1. The control register symbolic names use the new device/driver nomenclature rather than the old guest/host one. 2. The driver detect the device version (version 1 is the pre-OASIS spec, version 2 is compatible with fist revision of the OASIS spec) and drives the device accordingly. 3. New version uses direct addressing (64 bit address split into two low/high register) instead of the guest page size based one, and addresses each part of the queue (descriptors, available, used) separately. 4. The device activity is now explicitly triggered by writing to the "queue ready" register. 5. Whole 64 bit features are properly handled now (both ways). Signed-off-by: Pawel Moll Acked-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_mmio.c | 131 ++++++++++++++++++++++------------- include/linux/virtio_mmio.h | 44 ++++++++++-- 2 files changed, 118 insertions(+), 57 deletions(-) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 00d115b22bd8b3..cad569890908de 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -1,7 +1,7 @@ /* * Virtio memory mapped device driver * - * Copyright 2011, ARM Ltd. + * Copyright 2011-2014, ARM Ltd. * * This module allows virtio devices to be used over a virtual, memory mapped * platform device. @@ -50,36 +50,6 @@ * * * - * Registers layout (all 32-bit wide): - * - * offset d. name description - * ------ -- ---------------- ----------------- - * - * 0x000 R MagicValue Magic value "virt" - * 0x004 R Version Device version (current max. 1) - * 0x008 R DeviceID Virtio device ID - * 0x00c R VendorID Virtio vendor ID - * - * 0x010 R HostFeatures Features supported by the host - * 0x014 W HostFeaturesSel Set of host features to access via HostFeatures - * - * 0x020 W GuestFeatures Features activated by the guest - * 0x024 W GuestFeaturesSel Set of activated features to set via GuestFeatures - * 0x028 W GuestPageSize Size of guest's memory page in bytes - * - * 0x030 W QueueSel Queue selector - * 0x034 R QueueNumMax Maximum size of the currently selected queue - * 0x038 W QueueNum Queue size for the currently selected queue - * 0x03c W QueueAlign Used Ring alignment for the current queue - * 0x040 RW QueuePFN PFN for the currently selected queue - * - * 0x050 W QueueNotify Queue notifier - * 0x060 R InterruptStatus Interrupt status register - * 0x064 W InterruptACK Interrupt acknowledge register - * 0x070 RW Status Device status register - * - * 0x100+ RW Device-specific configuration space - * * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007 * * This work is licensed under the terms of the GNU GPL, version 2 or later. @@ -145,11 +115,16 @@ struct virtio_mmio_vq_info { static u64 vm_get_features(struct virtio_device *vdev) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); + u64 features; + + writel(1, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); + features = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); + features <<= 32; - /* TODO: Features > 32 bits */ - writel(0, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL); + writel(0, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); + features |= readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); - return readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES); + return features; } static int vm_finalize_features(struct virtio_device *vdev) @@ -159,11 +134,20 @@ static int vm_finalize_features(struct virtio_device *vdev) /* Give virtio_ring a chance to accept features. */ vring_transport_features(vdev); - /* Make sure we don't have any features > 32 bits! */ - BUG_ON((u32)vdev->features != vdev->features); + /* Make sure there is are no mixed devices */ + if (vm_dev->version == 2 && + !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { + dev_err(&vdev->dev, "New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n"); + return -EINVAL; + } + + writel(1, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); + writel((u32)(vdev->features >> 32), + vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); - writel(0, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SEL); - writel(vdev->features, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES); + writel(0, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); + writel((u32)vdev->features, + vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); return 0; } @@ -275,7 +259,12 @@ static void vm_del_vq(struct virtqueue *vq) /* Select and deactivate the queue */ writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); - writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + if (vm_dev->version == 1) { + writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + } else { + writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); + } size = PAGE_ALIGN(vring_size(info->num, VIRTIO_MMIO_VRING_ALIGN)); free_pages_exact(info->queue, size); @@ -312,7 +301,8 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); /* Queue shouldn't already be set up. */ - if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN)) { + if (readl(vm_dev->base + (vm_dev->version == 1 ? + VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) { err = -ENOENT; goto error_available; } @@ -356,13 +346,6 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, info->num /= 2; } - /* Activate the queue */ - writel(info->num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); - writel(VIRTIO_MMIO_VRING_ALIGN, - vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); - writel(virt_to_phys(info->queue) >> PAGE_SHIFT, - vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); - /* Create the vring */ vq = vring_new_virtqueue(index, info->num, VIRTIO_MMIO_VRING_ALIGN, vdev, true, info->queue, vm_notify, callback, name); @@ -371,6 +354,33 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, goto error_new_virtqueue; } + /* Activate the queue */ + writel(info->num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); + if (vm_dev->version == 1) { + writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); + writel(virt_to_phys(info->queue) >> PAGE_SHIFT, + vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + } else { + u64 addr; + + addr = virt_to_phys(info->queue); + writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); + writel((u32)(addr >> 32), + vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); + + addr = virt_to_phys(virtqueue_get_avail(vq)); + writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); + writel((u32)(addr >> 32), + vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); + + addr = virt_to_phys(virtqueue_get_used(vq)); + writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); + writel((u32)(addr >> 32), + vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); + + writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + } + vq->priv = info; info->vq = vq; @@ -381,7 +391,12 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, return vq; error_new_virtqueue: - writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + if (vm_dev->version == 1) { + writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + } else { + writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); + } free_pages_exact(info->queue, size); error_alloc_pages: kfree(info); @@ -476,16 +491,32 @@ static int virtio_mmio_probe(struct platform_device *pdev) /* Check device version */ vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION); - if (vm_dev->version != 1) { + if (vm_dev->version < 1 || vm_dev->version > 2) { dev_err(&pdev->dev, "Version %ld not supported!\n", vm_dev->version); return -ENXIO; } vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); + if (vm_dev->vdev.id.device == 0) { + /* + * virtio-mmio device with an ID 0 is a (dummy) placeholder + * with no function. End probing now with no error reported. + */ + return -ENODEV; + } vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); - writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); + /* Reject legacy-only IDs for version 2 devices */ + if (vm_dev->version == 2 && + virtio_device_is_legacy_only(vm_dev->vdev.id)) { + dev_err(&pdev->dev, "Version 2 not supported for devices %u!\n", + vm_dev->vdev.id.device); + return -ENODEV; + } + + if (vm_dev->version == 1) + writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); platform_set_drvdata(pdev, vm_dev); diff --git a/include/linux/virtio_mmio.h b/include/linux/virtio_mmio.h index 5c7b6f0daef8f1..c4b09689ab6447 100644 --- a/include/linux/virtio_mmio.h +++ b/include/linux/virtio_mmio.h @@ -51,23 +51,29 @@ /* Virtio vendor ID - Read Only */ #define VIRTIO_MMIO_VENDOR_ID 0x00c -/* Bitmask of the features supported by the host +/* Bitmask of the features supported by the device (host) * (32 bits per set) - Read Only */ -#define VIRTIO_MMIO_HOST_FEATURES 0x010 +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 -/* Host features set selector - Write Only */ -#define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014 +/* Device (host) features set selector - Write Only */ +#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 -/* Bitmask of features activated by the guest +/* Bitmask of features activated by the driver (guest) * (32 bits per set) - Write Only */ -#define VIRTIO_MMIO_GUEST_FEATURES 0x020 +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* Activated features set selector - Write Only */ -#define VIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 +#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 + + +#ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */ /* Guest's memory page size in bytes - Write Only */ #define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#endif + + /* Queue selector - Write Only */ #define VIRTIO_MMIO_QUEUE_SEL 0x030 @@ -77,12 +83,21 @@ /* Queue size for the currently selected queue - Write Only */ #define VIRTIO_MMIO_QUEUE_NUM 0x038 + +#ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */ + /* Used Ring alignment for the currently selected queue - Write Only */ #define VIRTIO_MMIO_QUEUE_ALIGN 0x03c /* Guest's PFN for the currently selected queue - Read Write */ #define VIRTIO_MMIO_QUEUE_PFN 0x040 +#endif + + +/* Ready bit for the currently selected queue - Read Write */ +#define VIRTIO_MMIO_QUEUE_READY 0x044 + /* Queue notifier - Write Only */ #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 @@ -95,6 +110,21 @@ /* Device status register - Read Write */ #define VIRTIO_MMIO_STATUS 0x070 +/* Selected queue's Descriptor Table address, 64 bits in two halves */ +#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 + +/* Selected queue's Available Ring address, 64 bits in two halves */ +#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 +#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 + +/* Selected queue's Used Ring address, 64 bits in two halves */ +#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 +#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 + +/* Configuration atomicity value */ +#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc + /* The config space is defined by each driver as * the per-driver configuration space - Read Write */ #define VIRTIO_MMIO_CONFIG 0x100 From 2b5fbb824f99c5a31e9ff175e96257c1d49e2e15 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Jan 2015 04:42:22 +0000 Subject: [PATCH 226/360] dmaengine: rcar-hpbdma: tidyup residue_granularity The driver doesn't support residue reporting at all. residue_granularity should be set to DMA_RESIDUE_GRANULARITY_DESCRIPTOR. Special thanks to Laurent Reported-by: Laurent Pinchart Signed-off-by: Kuninori Morimoto Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-hpbdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/sh/rcar-hpbdma.c b/drivers/dma/sh/rcar-hpbdma.c index 6fef1b95c89520..749f26ecd3b32b 100644 --- a/drivers/dma/sh/rcar-hpbdma.c +++ b/drivers/dma/sh/rcar-hpbdma.c @@ -600,7 +600,7 @@ static int hpb_dmae_probe(struct platform_device *pdev) dma_dev->src_addr_widths = widths; dma_dev->dst_addr_widths = widths; dma_dev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); - dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; hpbdev->shdma_dev.ops = &hpb_dmae_ops; hpbdev->shdma_dev.desc_size = sizeof(struct hpb_desc); From 5cf5aec5b38a5143883fc5b689bf5c1c8ee48aa3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Jan 2015 02:32:51 +0000 Subject: [PATCH 227/360] dmaengine: shdmac: fixup WARNING of slave caps retrieval ecc19d17868be9c9f8f00ed928791533c420f3e0 (dmaengine: Add a warning for drivers not using the generic slave caps retrieval) added WARN() for DMA_SLAVE. Kernel will shows WARNING without this patch. Signed-off-by: Kuninori Morimoto Acked-by: Laurent Pinchart Tested-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- drivers/dma/sh/shdmac.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index aec8a84784a469..ce4cd6be07cf8d 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -684,6 +684,10 @@ MODULE_DEVICE_TABLE(of, sh_dmae_of_match); static int sh_dmae_probe(struct platform_device *pdev) { + const enum dma_slave_buswidth widths = + DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES | + DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES | + DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES; const struct sh_dmae_pdata *pdata; unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {}; int chan_irq[SH_DMAE_MAX_CHANNELS]; @@ -746,6 +750,11 @@ static int sh_dmae_probe(struct platform_device *pdev) return PTR_ERR(shdev->dmars); } + dma_dev->src_addr_widths = widths; + dma_dev->dst_addr_widths = widths; + dma_dev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + if (!pdata->slave_only) dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); if (pdata->slave && pdata->slave_num) From c914570f28552eb4ed6f016ec7b1db292a7c924b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 14 Jan 2015 15:16:28 +0100 Subject: [PATCH 228/360] dmaengine: of: bail out early if "dmas" property is not present And don't print an error: not configured is not an error. Reported-by: Philipp Zabel Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Acked-by: Philipp Zabel Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index d5fbeaa1e7ba76..ca31f1b45366d2 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -159,6 +159,10 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, return ERR_PTR(-ENODEV); } + /* Silently fail if there is not even the "dmas" property */ + if (!of_find_property(np, "dmas", NULL)) + return ERR_PTR(-ENODEV); + count = of_property_count_strings(np, "dma-names"); if (count < 0) { pr_err("%s: dma-names property of node '%s' missing or empty\n", From d2048c49152a414efcf8b2696896bc8803ef2f1e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 18 Dec 2014 10:09:42 +0100 Subject: [PATCH 229/360] pwm: atmel-hlcdc: Depend on HAVE_CLK The include/linux/clk.h header defines dummy implementations for the various clk_*() functions if HAVE_CLK is not selected to improve build coverage in randconfig builds. The dummy implementation of clk_get_rate() returns 0, which causes the Atmel HLCDC PWM driver's atmel_hlcdc_pwm_config() implementation to end up calling: do_div(clk_period_ns, 0) On x86, do_div(n, base) will end up evaluating to this: n >>= ilog2(base) with base = 0, the implementation of ilog2() will call ____ilog2_NaN(), which is purposely undefined and results in a linker failure: ERROR: "____ilog2_NaN" [drivers/pwm/pwm-atmel-hlcdc.ko] undefined! The implementation of do_div() checks that base is a power of 2 before calling ilog2(). The compiler doesn't optimize this away, presumably because is_power_of_2() is an inline function and the compiler doesn't or can't inspect it closely enough. ilog2() being a macro it still ends up generating the ____ilog2_NaN() because of the constant 0. The root of the problem is that the driver really should be checking before possibly dividing by zero. That should eventually be fixed, but for now just assume that the clock runs at a sensible frequency when available. Reported-by: Jim Davis Acked-by: Boris Brezillon Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a3ecf58096348c..468af1b4ca302e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -53,6 +53,7 @@ config PWM_ATMEL config PWM_ATMEL_HLCDC_PWM tristate "Atmel HLCDC PWM support" depends on MFD_ATMEL_HLCDC + depends on HAVE_CLK help Generic PWM framework driver for the PWM output of the HLCDC (Atmel High-end LCD Controller). This PWM output is mainly used From df6922adec987b1c7f07870a00f899fbc9a7d4cc Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 18 Dec 2014 21:05:30 +0100 Subject: [PATCH 230/360] pwm: atmel-hlcdc: Prevent division by zero The slow and system clock should never return a rate of zero, but this might happen if the clocks property defined in the DT is referencing the wrong clocks. Prevent any division by zero from happening by testing the clk_freq value before calling do_div(). Signed-off-by: Boris Brezillon Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel-hlcdc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index e7a785fadcdf97..522f7075bb1a42 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -64,6 +64,9 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c, if (!chip->errata || !chip->errata->slow_clk_erratum) { clk_freq = clk_get_rate(new_clk); + if (!clk_freq) + return -EINVAL; + clk_period_ns = (u64)NSEC_PER_SEC * 256; do_div(clk_period_ns, clk_freq); } @@ -73,6 +76,9 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c, clk_period_ns > period_ns) { new_clk = hlcdc->sys_clk; clk_freq = clk_get_rate(new_clk); + if (!clk_freq) + return -EINVAL; + clk_period_ns = (u64)NSEC_PER_SEC * 256; do_div(clk_period_ns, clk_freq); } From 09853ce7bc1003a490c7ee74a5705d7a7cf16b7d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 17 Dec 2014 22:15:39 +0100 Subject: [PATCH 231/360] pwm: Add Allwinner SoC support This adds a generic PWM framework driver for the PWM controller found on Allwinner SoCs. Signed-off-by: Alexandre Belloni Acked-by: Maxime Ripard Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 10 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sun4i.c | 366 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 drivers/pwm/pwm-sun4i.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 468af1b4ca302e..1ba937fb749606 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -284,6 +284,16 @@ config PWM_STI To compile this driver as a module, choose M here: the module will be called pwm-sti. +config PWM_SUN4I + tristate "Allwinner PWM support" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && COMMON_CLK + help + Generic PWM framework driver for Allwinner SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-sun4i. + config PWM_TEGRA tristate "NVIDIA Tegra PWM support" depends on ARCH_TEGRA diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 65259ac1e8de8c..260be018713f61 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_STI) += pwm-sti.o +obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c new file mode 100644 index 00000000000000..cd9dde5630181d --- /dev/null +++ b/drivers/pwm/pwm-sun4i.c @@ -0,0 +1,366 @@ +/* + * Driver for Allwinner sun4i Pulse Width Modulation Controller + * + * Copyright (C) 2014 Alexandre Belloni + * + * Licensed under GPLv2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PWM_CTRL_REG 0x0 + +#define PWM_CH_PRD_BASE 0x4 +#define PWM_CH_PRD_OFFSET 0x4 +#define PWM_CH_PRD(ch) (PWM_CH_PRD_BASE + PWM_CH_PRD_OFFSET * (ch)) + +#define PWMCH_OFFSET 15 +#define PWM_PRESCAL_MASK GENMASK(3, 0) +#define PWM_PRESCAL_OFF 0 +#define PWM_EN BIT(4) +#define PWM_ACT_STATE BIT(5) +#define PWM_CLK_GATING BIT(6) +#define PWM_MODE BIT(7) +#define PWM_PULSE BIT(8) +#define PWM_BYPASS BIT(9) + +#define PWM_RDY_BASE 28 +#define PWM_RDY_OFFSET 1 +#define PWM_RDY(ch) BIT(PWM_RDY_BASE + PWM_RDY_OFFSET * (ch)) + +#define PWM_PRD(prd) (((prd) - 1) << 16) +#define PWM_PRD_MASK GENMASK(15, 0) + +#define PWM_DTY_MASK GENMASK(15, 0) + +#define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET)) + +static const u32 prescaler_table[] = { + 120, + 180, + 240, + 360, + 480, + 0, + 0, + 0, + 12000, + 24000, + 36000, + 48000, + 72000, + 0, + 0, + 0, /* Actually 1 but tested separately */ +}; + +struct sun4i_pwm_data { + bool has_prescaler_bypass; + bool has_rdy; +}; + +struct sun4i_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + void __iomem *base; + spinlock_t ctrl_lock; + const struct sun4i_pwm_data *data; +}; + +static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct sun4i_pwm_chip, chip); +} + +static inline u32 sun4i_pwm_readl(struct sun4i_pwm_chip *chip, + unsigned long offset) +{ + return readl(chip->base + offset); +} + +static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, + u32 val, unsigned long offset) +{ + writel(val, chip->base + offset); +} + +static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + u32 prd, dty, val, clk_gate; + u64 clk_rate, div = 0; + unsigned int prescaler = 0; + int err; + + clk_rate = clk_get_rate(sun4i_pwm->clk); + + if (sun4i_pwm->data->has_prescaler_bypass) { + /* First, test without any prescaler when available */ + prescaler = PWM_PRESCAL_MASK; + /* + * When not using any prescaler, the clock period in nanoseconds + * is not an integer so round it half up instead of + * truncating to get less surprising values. + */ + div = clk_rate * period_ns + NSEC_PER_SEC/2; + do_div(div, NSEC_PER_SEC); + if (div - 1 > PWM_PRD_MASK) + prescaler = 0; + } + + if (prescaler == 0) { + /* Go up from the first divider */ + for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) { + if (!prescaler_table[prescaler]) + continue; + div = clk_rate; + do_div(div, prescaler_table[prescaler]); + div = div * period_ns; + do_div(div, NSEC_PER_SEC); + if (div - 1 <= PWM_PRD_MASK) + break; + } + + if (div - 1 > PWM_PRD_MASK) { + dev_err(chip->dev, "period exceeds the maximum value\n"); + return -EINVAL; + } + } + + prd = div; + div *= duty_ns; + do_div(div, period_ns); + dty = div; + + err = clk_prepare_enable(sun4i_pwm->clk); + if (err) { + dev_err(chip->dev, "failed to enable PWM clock\n"); + return err; + } + + spin_lock(&sun4i_pwm->ctrl_lock); + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + + if (sun4i_pwm->data->has_rdy && (val & PWM_RDY(pwm->hwpwm))) { + spin_unlock(&sun4i_pwm->ctrl_lock); + clk_disable_unprepare(sun4i_pwm->clk); + return -EBUSY; + } + + clk_gate = val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + if (clk_gate) { + val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + } + + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + val &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); + val |= BIT_CH(prescaler, pwm->hwpwm); + sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + + val = (dty & PWM_DTY_MASK) | PWM_PRD(prd); + sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm)); + + if (clk_gate) { + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + val |= clk_gate; + sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + } + + spin_unlock(&sun4i_pwm->ctrl_lock); + clk_disable_unprepare(sun4i_pwm->clk); + + return 0; +} + +static int sun4i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + u32 val; + int ret; + + ret = clk_prepare_enable(sun4i_pwm->clk); + if (ret) { + dev_err(chip->dev, "failed to enable PWM clock\n"); + return ret; + } + + spin_lock(&sun4i_pwm->ctrl_lock); + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + + if (polarity != PWM_POLARITY_NORMAL) + val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); + else + val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm); + + sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + + spin_unlock(&sun4i_pwm->ctrl_lock); + clk_disable_unprepare(sun4i_pwm->clk); + + return 0; +} + +static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + u32 val; + int ret; + + ret = clk_prepare_enable(sun4i_pwm->clk); + if (ret) { + dev_err(chip->dev, "failed to enable PWM clock\n"); + return ret; + } + + spin_lock(&sun4i_pwm->ctrl_lock); + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + val |= BIT_CH(PWM_EN, pwm->hwpwm); + val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + spin_unlock(&sun4i_pwm->ctrl_lock); + + return 0; +} + +static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + u32 val; + + spin_lock(&sun4i_pwm->ctrl_lock); + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + val &= ~BIT_CH(PWM_EN, pwm->hwpwm); + val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + spin_unlock(&sun4i_pwm->ctrl_lock); + + clk_disable_unprepare(sun4i_pwm->clk); +} + +static const struct pwm_ops sun4i_pwm_ops = { + .config = sun4i_pwm_config, + .set_polarity = sun4i_pwm_set_polarity, + .enable = sun4i_pwm_enable, + .disable = sun4i_pwm_disable, + .owner = THIS_MODULE, +}; + +static const struct sun4i_pwm_data sun4i_pwm_data_a10 = { + .has_prescaler_bypass = false, + .has_rdy = false, +}; + +static const struct sun4i_pwm_data sun4i_pwm_data_a20 = { + .has_prescaler_bypass = true, + .has_rdy = true, +}; + +static const struct of_device_id sun4i_pwm_dt_ids[] = { + { + .compatible = "allwinner,sun4i-a10-pwm", + .data = &sun4i_pwm_data_a10, + }, { + .compatible = "allwinner,sun7i-a20-pwm", + .data = &sun4i_pwm_data_a20, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, sun4i_pwm_dt_ids); + +static int sun4i_pwm_probe(struct platform_device *pdev) +{ + struct sun4i_pwm_chip *pwm; + struct resource *res; + u32 val; + int i, ret; + const struct of_device_id *match; + + match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev); + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pwm->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pwm->base)) + return PTR_ERR(pwm->base); + + pwm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &sun4i_pwm_ops; + pwm->chip.base = -1; + pwm->chip.npwm = 2; + pwm->chip.can_sleep = true; + pwm->chip.of_xlate = of_pwm_xlate_with_flags; + pwm->chip.of_pwm_n_cells = 3; + pwm->data = match->data; + + spin_lock_init(&pwm->ctrl_lock); + + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, pwm); + + ret = clk_prepare_enable(pwm->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable PWM clock\n"); + goto clk_error; + } + + val = sun4i_pwm_readl(pwm, PWM_CTRL_REG); + for (i = 0; i < pwm->chip.npwm; i++) + if (!(val & BIT_CH(PWM_ACT_STATE, i))) + pwm->chip.pwms[i].polarity = PWM_POLARITY_INVERSED; + clk_disable_unprepare(pwm->clk); + + return 0; + +clk_error: + pwmchip_remove(&pwm->chip); + return ret; +} + +static int sun4i_pwm_remove(struct platform_device *pdev) +{ + struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev); + + return pwmchip_remove(&pwm->chip); +} + +static struct platform_driver sun4i_pwm_driver = { + .driver = { + .name = "sun4i-pwm", + .of_match_table = sun4i_pwm_dt_ids, + }, + .probe = sun4i_pwm_probe, + .remove = sun4i_pwm_remove, +}; +module_platform_driver(sun4i_pwm_driver); + +MODULE_ALIAS("platform:sun4i-pwm"); +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Allwinner sun4i PWM driver"); +MODULE_LICENSE("GPL v2"); From ed73598531337bce238915280396c8e0dcee93bc Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 17 Dec 2014 22:15:40 +0100 Subject: [PATCH 232/360] pwm: sunxi: document OF bindings This is the documentation for the Allwinner SoCs PWM bindings. Signed-off-by: Alexandre Belloni Acked-by: Maxime Ripard Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-sun4i.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sun4i.txt diff --git a/Documentation/devicetree/bindings/pwm/pwm-sun4i.txt b/Documentation/devicetree/bindings/pwm/pwm-sun4i.txt new file mode 100644 index 00000000000000..ae0273e1950628 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-sun4i.txt @@ -0,0 +1,20 @@ +Allwinner sun4i and sun7i SoC PWM controller + +Required properties: + - compatible: should be one of: + - "allwinner,sun4i-a10-pwm" + - "allwinner,sun7i-a20-pwm" + - reg: physical base address and length of the controller's registers + - #pwm-cells: should be 3. See pwm.txt in this directory for a description of + the cells format. + - clocks: From common clock binding, handle to the parent clock. + +Example: + + pwm: pwm@01c20e00 { + compatible = "allwinner,sun7i-a20-pwm"; + reg = <0x01c20e00 0xc>; + clocks = <&osc24M>; + #pwm-cells = <3>; + status = "disabled"; + }; From cd264b6a6559caa74579b5afca11379ddde92dca Mon Sep 17 00:00:00 2001 From: Ajit Pal Singh Date: Thu, 29 Jan 2015 14:34:43 +0530 Subject: [PATCH 233/360] pwm: sti: Maintain a bitmap of configured devices This patch introduces a bitmap which is used to keep track of the pwm channels which have been configured in a pwm chip. The method used earlier to find the number of configured channels, was to count the pwmdevices with PWMF_REQUESTED field set and period value configured. This was not correct and failed when of_pwm_get()/pwm_get() and then pwm_config() was used. Signed-off-by: Ajit Pal Singh Signed-off-by: Thierry Reding --- drivers/pwm/pwm-sti.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index b95115cdaea7ba..92abbd56b9f718 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -57,6 +57,7 @@ struct sti_pwm_chip { struct regmap_field *pwm_int_en; struct pwm_chip chip; struct pwm_device *cur; + unsigned long configured; unsigned int en_count; struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ void __iomem *mmio; @@ -102,24 +103,6 @@ static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, return 0; } -/* Calculate the number of PWM devices configured with a period. */ -static unsigned int sti_pwm_count_configured(struct pwm_chip *chip) -{ - struct pwm_device *pwm; - unsigned int ncfg = 0; - unsigned int i; - - for (i = 0; i < chip->npwm; i++) { - pwm = &chip->pwms[i]; - if (test_bit(PWMF_REQUESTED, &pwm->flags)) { - if (pwm_get_period(pwm)) - ncfg++; - } - } - - return ncfg; -} - /* * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. * The only way to change the period (apart from changing the PWM input clock) @@ -141,7 +124,7 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned int ncfg; bool period_same = false; - ncfg = sti_pwm_count_configured(chip); + ncfg = hweight_long(pc->configured); if (ncfg) period_same = (period_ns == pwm_get_period(cur)); @@ -197,6 +180,7 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = regmap_field_write(pc->pwm_int_en, 0); + set_bit(pwm->hwpwm, &pc->configured); pc->cur = pwm; dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n", @@ -254,10 +238,18 @@ static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) mutex_unlock(&pc->sti_pwm_lock); } +static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct sti_pwm_chip *pc = to_sti_pwmchip(chip); + + clear_bit(pwm->hwpwm, &pc->configured); +} + static const struct pwm_ops sti_pwm_ops = { .config = sti_pwm_config, .enable = sti_pwm_enable, .disable = sti_pwm_disable, + .free = sti_pwm_free, .owner = THIS_MODULE, }; From 277bb6a29e0037c3e7d9e70945eb22003006935d Mon Sep 17 00:00:00 2001 From: Naidu Tellapati Date: Fri, 9 Jan 2015 14:54:47 -0300 Subject: [PATCH 234/360] pwm: Imagination Technologies PWM DAC driver The Pistachio SOC from Imagination Technologies includes a Pulse Width Modulation DAC which produces 1 to 4 digital bit-outputs which represent digital waveforms. These PWM outputs are primarily in charge of controlling backlight LED devices. Reviewed-by: Andrew Bresticker Signed-off-by: Naidu Tellapati Signed-off-by: Sai Masarapu Signed-off-by: Ezequiel Garcia Reviewed-by: Vladimir Zapolskiy [thierry.reding: fixup license header as discussed on list] Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 13 +++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-img.c | 249 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 drivers/pwm/pwm-img.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 1ba937fb749606..b1541f40fd8d19 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -131,6 +131,19 @@ config PWM_FSL_FTM To compile this driver as a module, choose M here: the module will be called pwm-fsl-ftm. +config PWM_IMG + tristate "Imagination Technologies PWM driver" + depends on HAS_IOMEM + depends on MFD_SYSCON + depends on COMMON_CLK + depends on MIPS || COMPILE_TEST + help + Generic PWM framework driver for Imagination Technologies + PWM block which supports 4 channels. + + To compile this driver as a module, choose M here: the module + will be called pwm-img + config PWM_IMX tristate "i.MX PWM support" depends on ARCH_MXC diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 260be018713f61..ec50eb5b5a8fd7 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o +obj-$(CONFIG_PWM_IMG) += pwm-img.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c new file mode 100644 index 00000000000000..476171a768d61d --- /dev/null +++ b/drivers/pwm/pwm-img.c @@ -0,0 +1,249 @@ +/* + * Imagination Technologies Pulse Width Modulator driver + * + * Copyright (c) 2014-2015, Imagination Technologies + * + * Based on drivers/pwm/pwm-tegra.c, Copyright (c) 2010, NVIDIA Corporation + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PWM registers */ +#define PWM_CTRL_CFG 0x0000 +#define PWM_CTRL_CFG_NO_SUB_DIV 0 +#define PWM_CTRL_CFG_SUB_DIV0 1 +#define PWM_CTRL_CFG_SUB_DIV1 2 +#define PWM_CTRL_CFG_SUB_DIV0_DIV1 3 +#define PWM_CTRL_CFG_DIV_SHIFT(ch) ((ch) * 2 + 4) +#define PWM_CTRL_CFG_DIV_MASK 0x3 + +#define PWM_CH_CFG(ch) (0x4 + (ch) * 4) +#define PWM_CH_CFG_TMBASE_SHIFT 0 +#define PWM_CH_CFG_DUTY_SHIFT 16 + +#define PERIP_PWM_PDM_CONTROL 0x0140 +#define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1 +#define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4) + +#define MAX_TMBASE_STEPS 65536 + +struct img_pwm_chip { + struct device *dev; + struct pwm_chip chip; + struct clk *pwm_clk; + struct clk *sys_clk; + void __iomem *base; + struct regmap *periph_regs; +}; + +static inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct img_pwm_chip, chip); +} + +static inline void img_pwm_writel(struct img_pwm_chip *chip, + u32 reg, u32 val) +{ + writel(val, chip->base + reg); +} + +static inline u32 img_pwm_readl(struct img_pwm_chip *chip, + u32 reg) +{ + return readl(chip->base + reg); +} + +static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + u32 val, div, duty, timebase; + unsigned long mul, output_clk_hz, input_clk_hz; + struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); + + input_clk_hz = clk_get_rate(pwm_chip->pwm_clk); + output_clk_hz = DIV_ROUND_UP(NSEC_PER_SEC, period_ns); + + mul = DIV_ROUND_UP(input_clk_hz, output_clk_hz); + if (mul <= MAX_TMBASE_STEPS) { + div = PWM_CTRL_CFG_NO_SUB_DIV; + timebase = DIV_ROUND_UP(mul, 1); + } else if (mul <= MAX_TMBASE_STEPS * 8) { + div = PWM_CTRL_CFG_SUB_DIV0; + timebase = DIV_ROUND_UP(mul, 8); + } else if (mul <= MAX_TMBASE_STEPS * 64) { + div = PWM_CTRL_CFG_SUB_DIV1; + timebase = DIV_ROUND_UP(mul, 64); + } else if (mul <= MAX_TMBASE_STEPS * 512) { + div = PWM_CTRL_CFG_SUB_DIV0_DIV1; + timebase = DIV_ROUND_UP(mul, 512); + } else if (mul > MAX_TMBASE_STEPS * 512) { + dev_err(chip->dev, + "failed to configure timebase steps/divider value\n"); + return -EINVAL; + } + + duty = DIV_ROUND_UP(timebase * duty_ns, period_ns); + + val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm)); + val |= (div & PWM_CTRL_CFG_DIV_MASK) << + PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm); + img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); + + val = (duty << PWM_CH_CFG_DUTY_SHIFT) | + (timebase << PWM_CH_CFG_TMBASE_SHIFT); + img_pwm_writel(pwm_chip, PWM_CH_CFG(pwm->hwpwm), val); + + return 0; +} + +static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 val; + struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); + + val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + val |= BIT(pwm->hwpwm); + img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); + + regmap_update_bits(pwm_chip->periph_regs, PERIP_PWM_PDM_CONTROL, + PERIP_PWM_PDM_CONTROL_CH_MASK << + PERIP_PWM_PDM_CONTROL_CH_SHIFT(pwm->hwpwm), 0); + + return 0; +} + +static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 val; + struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); + + val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + val &= ~BIT(pwm->hwpwm); + img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); +} + +static const struct pwm_ops img_pwm_ops = { + .config = img_pwm_config, + .enable = img_pwm_enable, + .disable = img_pwm_disable, + .owner = THIS_MODULE, +}; + +static int img_pwm_probe(struct platform_device *pdev) +{ + int ret; + struct resource *res; + struct img_pwm_chip *pwm; + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + pwm->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pwm->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pwm->base)) + return PTR_ERR(pwm->base); + + pwm->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "img,cr-periph"); + if (IS_ERR(pwm->periph_regs)) + return PTR_ERR(pwm->periph_regs); + + pwm->sys_clk = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(pwm->sys_clk)) { + dev_err(&pdev->dev, "failed to get system clock\n"); + return PTR_ERR(pwm->sys_clk); + } + + pwm->pwm_clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(pwm->pwm_clk)) { + dev_err(&pdev->dev, "failed to get pwm clock\n"); + return PTR_ERR(pwm->pwm_clk); + } + + ret = clk_prepare_enable(pwm->sys_clk); + if (ret < 0) { + dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); + return ret; + } + + ret = clk_prepare_enable(pwm->pwm_clk); + if (ret < 0) { + dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); + goto disable_sysclk; + } + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &img_pwm_ops; + pwm->chip.base = -1; + pwm->chip.npwm = 4; + + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); + goto disable_pwmclk; + } + + platform_set_drvdata(pdev, pwm); + return 0; + +disable_pwmclk: + clk_disable_unprepare(pwm->pwm_clk); +disable_sysclk: + clk_disable_unprepare(pwm->sys_clk); + return ret; +} + +static int img_pwm_remove(struct platform_device *pdev) +{ + struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); + u32 val; + unsigned int i; + + for (i = 0; i < pwm_chip->chip.npwm; i++) { + val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + val &= ~BIT(i); + img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); + } + + clk_disable_unprepare(pwm_chip->pwm_clk); + clk_disable_unprepare(pwm_chip->sys_clk); + + return pwmchip_remove(&pwm_chip->chip); +} + +static const struct of_device_id img_pwm_of_match[] = { + { .compatible = "img,pistachio-pwm", }, + { } +}; +MODULE_DEVICE_TABLE(of, img_pwm_of_match); + +static struct platform_driver img_pwm_driver = { + .driver = { + .name = "img-pwm", + .of_match_table = img_pwm_of_match, + }, + .probe = img_pwm_probe, + .remove = img_pwm_remove, +}; +module_platform_driver(img_pwm_driver); + +MODULE_AUTHOR("Sai Masarapu "); +MODULE_DESCRIPTION("Imagination Technologies PWM DAC driver"); +MODULE_LICENSE("GPL v2"); From 9c959bfe00396bc2e6143dc9a8b27feaffb78c74 Mon Sep 17 00:00:00 2001 From: Naidu Tellapati Date: Fri, 9 Jan 2015 14:54:48 -0300 Subject: [PATCH 235/360] pwm: Add device tree binding document for IMG PWM DAC Add binding document for IMG Pulse Width Modulator (PWM) DAC present on the Pistachio SOC. The PWM DAC has four channels. Signed-off-by: Naidu Tellapati Signed-off-by: Sai Masarapu Signed-off-by: Ezequiel Garcia Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/img-pwm.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/img-pwm.txt diff --git a/Documentation/devicetree/bindings/pwm/img-pwm.txt b/Documentation/devicetree/bindings/pwm/img-pwm.txt new file mode 100644 index 00000000000000..fade5f26fcacc5 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/img-pwm.txt @@ -0,0 +1,24 @@ +*Imagination Technologies PWM DAC driver + +Required properties: + - compatible: Should be "img,pistachio-pwm" + - reg: Should contain physical base address and length of pwm registers. + - clocks: Must contain an entry for each entry in clock-names. + See ../clock/clock-bindings.txt for details. + - clock-names: Must include the following entries. + - pwm: PWM operating clock. + - sys: PWM system interface clock. + - #pwm-cells: Should be 2. See pwm.txt in this directory for the + description of the cells format. + - img,cr-periph: Must contain a phandle to the peripheral control + syscon node which contains PWM control registers. + +Example: + pwm: pwm@18101300 { + compatible = "img,pistachio-pwm"; + reg = <0x18101300 0x100>; + clocks = <&pwm_clk>, <&system_clk>; + clock-names = "pwm", "sys"; + #pwm-cells = <2>; + img,cr-periph = <&cr_periph>; + }; From 1f95f8c9fddb14ec0c5b2d49f691b9c2aee7b9b9 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 23 Jan 2015 15:05:13 +0100 Subject: [PATCH 236/360] firewire: sbp2: remove redundant check for bidi command [Bart van Asche:] SCSI core never sets cmd->sc_data_direction to DMA_BIDIRECTIONAL; scsi_bidi_cmnd(cmd) should be used instead to test for a bidirectional command. [Christoph Hellwig:] Bidirectional commands won't ever be queued anyway, unless a LLD or transport driver sets QUEUE_FLAG_BIDI. So, simply remove the respective queuecommand check in the SBP-2 transport driver. Signed-off-by: Stefan Richter --- drivers/firewire/sbp2.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 64ac8f8f509887..c22606fe3d44bf 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -1463,17 +1463,6 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost, struct sbp2_command_orb *orb; int generation, retval = SCSI_MLQUEUE_HOST_BUSY; - /* - * Bidirectional commands are not yet implemented, and unknown - * transfer direction not handled. - */ - if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) { - dev_err(lu_dev(lu), "cannot handle bidirectional command\n"); - cmd->result = DID_ERROR << 16; - cmd->scsi_done(cmd); - return 0; - } - orb = kzalloc(sizeof(*orb), GFP_ATOMIC); if (orb == NULL) return SCSI_MLQUEUE_HOST_BUSY; From e0377cdebaf3913bff693c9eea17ff6eb4d7abc8 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Wed, 14 Jan 2015 17:41:31 -0600 Subject: [PATCH 237/360] mtd: nand: Request strength instead of bytes for soft BCH Previously, we requested that drivers pass ecc.size and ecc.bytes when using NAND_ECC_SOFT_BCH. However, a driver is likely to only know the ECC strength required for its NAND, so each driver would need to perform a strength-to-bytes calculation. Avoid duplicating this calculation in each driver by asking drivers to pass ecc.size and ecc.strength so that the strength-to-bytes calculation need only be implemented once. This reverts/generalizes this commit: mtd: nand: Base BCH ECC bytes on required strength Signed-off-by: Aaron Sierra Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 14 ++++++++------ drivers/mtd/nand/nandsim.c | 1 + drivers/mtd/nand/sunxi_nand.c | 2 -- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3f24b587304f03..f6af96926f0059 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4037,22 +4037,24 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->read_oob = nand_read_oob_std; ecc->write_oob = nand_write_oob_std; /* - * Board driver should supply ecc.size and ecc.bytes values to - * select how many bits are correctable; see nand_bch_init() - * for details. Otherwise, default to 4 bits for large page - * devices. + * Board driver should supply ecc.size and ecc.strength values + * to select how many bits are correctable. Otherwise, default + * to 4 bits for large page devices. */ if (!ecc->size && (mtd->oobsize >= 64)) { ecc->size = 512; - ecc->bytes = DIV_ROUND_UP(13 * ecc->strength, 8); + ecc->strength = 4; } + + /* See nand_bch_init() for details. */ + ecc->bytes = DIV_ROUND_UP( + ecc->strength * fls(8 * ecc->size), 8); ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes, &ecc->layout); if (!ecc->priv) { pr_warn("BCH ECC initialization failed!\n"); BUG(); } - ecc->strength = ecc->bytes * 8 / fls(8 * ecc->size); break; case NAND_ECC_NONE: diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index a8fa8dad9b23ae..f2324271b94e9d 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -2337,6 +2337,7 @@ static int __init ns_init_module(void) } chip->ecc.mode = NAND_ECC_SOFT_BCH; chip->ecc.size = 512; + chip->ecc.strength = bch; chip->ecc.bytes = eccbytes; NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); } diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index ccaa8e28338855..6f93b2990d250e 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1110,8 +1110,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, switch (ecc->mode) { case NAND_ECC_SOFT_BCH: - ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * ecc->size), - 8); break; case NAND_ECC_HW: ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); From cd145af998886bf3c596cb7b6ddc55a287b76e76 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 2 Dec 2014 20:48:26 +0100 Subject: [PATCH 238/360] mtd: nand: jz4740: Convert to GPIO descriptor API Use the GPIO descriptor API instead of the deprecated legacy GPIO API to manage the busy GPIO. The patch updates both the jz4740 nand driver and the only user of the driver the qi-lb60 board driver. Signed-off-by: Lars-Peter Clausen Acked-by: Ralf Baechle Signed-off-by: Brian Norris --- .../include/asm/mach-jz4740/jz4740_nand.h | 2 -- arch/mips/jz4740/board-qi_lb60.c | 11 ++++++- drivers/mtd/nand/jz4740_nand.c | 29 +++++++------------ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h index 986982db7c38c9..79cff26d8b36f1 100644 --- a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h +++ b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h @@ -27,8 +27,6 @@ struct jz_nand_platform_data { struct nand_ecclayout *ecc_layout; - unsigned int busy_gpio; - unsigned char banks[JZ_NAND_NUM_BANKS]; void (*ident_callback)(struct platform_device *, struct nand_chip *, diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index c454525e769570..9dd051edb411f2 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -140,10 +140,18 @@ static void qi_lb60_nand_ident(struct platform_device *pdev, static struct jz_nand_platform_data qi_lb60_nand_pdata = { .ident_callback = qi_lb60_nand_ident, - .busy_gpio = 94, .banks = { 1 }, }; +static struct gpiod_lookup_table qi_lb60_nand_gpio_table = { + .dev_id = "jz4740-nand.0", + .table = { + GPIO_LOOKUP("Bank C", 30, "busy", 0), + { }, + }, +}; + + /* Keyboard*/ #define KEY_QI_QI KEY_F13 @@ -472,6 +480,7 @@ static int __init qi_lb60_init_platform_devices(void) jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata; gpiod_add_lookup_table(&qi_lb60_audio_gpio_table); + gpiod_add_lookup_table(&qi_lb60_nand_gpio_table); jz4740_serial_device_register(); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 1633ec9c51082f..ebf2cce04cba14 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -69,7 +69,7 @@ struct jz_nand { int selected_bank; - struct jz_nand_platform_data *pdata; + struct gpio_desc *busy_gpio; bool is_reading; }; @@ -131,7 +131,7 @@ static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) static int jz_nand_dev_ready(struct mtd_info *mtd) { struct jz_nand *nand = mtd_to_jz_nand(mtd); - return gpio_get_value_cansleep(nand->pdata->busy_gpio); + return gpiod_get_value_cansleep(nand->busy_gpio); } static void jz_nand_hwctl(struct mtd_info *mtd, int mode) @@ -423,14 +423,12 @@ static int jz_nand_probe(struct platform_device *pdev) if (ret) goto err_free; - if (pdata && gpio_is_valid(pdata->busy_gpio)) { - ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); - if (ret) { - dev_err(&pdev->dev, - "Failed to request busy gpio %d: %d\n", - pdata->busy_gpio, ret); - goto err_iounmap_mmio; - } + nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN); + if (IS_ERR(nand->busy_gpio)) { + ret = PTR_ERR(nand->busy_gpio); + dev_err(&pdev->dev, "Failed to request busy gpio %d\n", + ret); + goto err_iounmap_mmio; } mtd = &nand->mtd; @@ -454,10 +452,9 @@ static int jz_nand_probe(struct platform_device *pdev) chip->cmd_ctrl = jz_nand_cmd_ctrl; chip->select_chip = jz_nand_select_chip; - if (pdata && gpio_is_valid(pdata->busy_gpio)) + if (nand->busy_gpio) chip->dev_ready = jz_nand_dev_ready; - nand->pdata = pdata; platform_set_drvdata(pdev, nand); /* We are going to autodetect NAND chips in the banks specified in the @@ -496,7 +493,7 @@ static int jz_nand_probe(struct platform_device *pdev) } if (chipnr == 0) { dev_err(&pdev->dev, "No NAND chips found\n"); - goto err_gpio_busy; + goto err_iounmap_mmio; } if (pdata && pdata->ident_callback) { @@ -533,9 +530,6 @@ static int jz_nand_probe(struct platform_device *pdev) nand->bank_base[bank - 1]); } writel(0, nand->base + JZ_REG_NAND_CTRL); -err_gpio_busy: - if (pdata && gpio_is_valid(pdata->busy_gpio)) - gpio_free(pdata->busy_gpio); err_iounmap_mmio: jz_nand_iounmap_resource(nand->mem, nand->base); err_free: @@ -546,7 +540,6 @@ static int jz_nand_probe(struct platform_device *pdev) static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); - struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t i; nand_release(&nand->mtd); @@ -562,8 +555,6 @@ static int jz_nand_remove(struct platform_device *pdev) gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); } } - if (pdata && gpio_is_valid(pdata->busy_gpio)) - gpio_free(pdata->busy_gpio); jz_nand_iounmap_resource(nand->mem, nand->base); From d71e6a11737f4b3d857425a1d6f893231cbd1296 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 28 Jan 2015 21:04:48 +0100 Subject: [PATCH 239/360] firewire: core: use correct vendor/model IDs The kernel was using the vendor ID 0xd00d1e, which was inherited from the old ieee1394 driver stack. However, this ID was not registered, and invalid. Instead, use the vendor/model IDs that are now officially assigned to the kernel: https://ieee1394.wiki.kernel.org/index.php/IEEE_OUI_Assignments [stefanr: - The vendor ID 001f11 is Openmoko, Inc.'s identifier, registered at IEEE Registration Authority. - The range of model IDs 023900...0239ff are the Linux kernel 1394 subsystem's identifiers, registered at Openmoko. - Model ID 023901 is picked by the subsystem developers as firewire-core's model ID.] Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index eb6935c8ad9449..d6a09b9cd8ccae 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1246,14 +1246,14 @@ static const u32 model_textual_descriptor[] = { static struct fw_descriptor vendor_id_descriptor = { .length = ARRAY_SIZE(vendor_textual_descriptor), - .immediate = 0x03d00d1e, + .immediate = 0x03001f11, .key = 0x81000000, .data = vendor_textual_descriptor, }; static struct fw_descriptor model_id_descriptor = { .length = ARRAY_SIZE(model_textual_descriptor), - .immediate = 0x17000001, + .immediate = 0x17023901, .key = 0x81000000, .data = model_textual_descriptor, }; From 8d6cc0738540f97edb021d3b76a4367519f1e5f1 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 3 Feb 2015 11:54:28 +0100 Subject: [PATCH 240/360] pwm: Remove unnecessary check before of_node_put() The of_node_put() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 966497d10c6ef2..810aef3f4c3e84 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -192,7 +192,7 @@ static void of_pwmchip_add(struct pwm_chip *chip) static void of_pwmchip_remove(struct pwm_chip *chip) { - if (chip->dev && chip->dev->of_node) + if (chip->dev) of_node_put(chip->dev->of_node); } From 1fb200d6d42c67678bfa98a7d122dfa61f8cbb8d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 Jan 2015 18:23:06 +0200 Subject: [PATCH 241/360] dmaengine: dw: update MAINTAINERS file This is a follow up to the previously done changes in the layout of the driver files. We now have an additional file include/linux/dma/dw.h which is missed in the MAINTAINERS data base. Fixes: 3d588f83e4d6 (dmaengine: dw: split dma-dw.h to platform and private parts) Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8d32b3ed..59c34631511092 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8324,6 +8324,7 @@ SYNOPSYS DESIGNWARE DMAC DRIVER M: Viresh Kumar M: Andy Shevchenko S: Maintained +F: include/linux/dma/dw.h F: include/linux/platform_data/dma-dw.h F: drivers/dma/dw/ From 09aa8ac0f9a76b7d650cf3a27a2cfcb4d3d612fb Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 3 Feb 2015 17:07:35 -0600 Subject: [PATCH 242/360] dma: mmp_tdma: Fix build for ARM64 sram_get_gpool is only used for legacy, non-DT MMP/PXA platforms. Provide an empty version in order to build on ARM64. Signed-off-by: Rob Herring Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-mmp_tdma.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/platform_data/dma-mmp_tdma.h b/include/linux/platform_data/dma-mmp_tdma.h index 66574ea39f97dc..0c72886030ef0f 100644 --- a/include/linux/platform_data/dma-mmp_tdma.h +++ b/include/linux/platform_data/dma-mmp_tdma.h @@ -28,6 +28,13 @@ struct sram_platdata { int granularity; }; +#ifdef CONFIG_ARM extern struct gen_pool *sram_get_gpool(char *pool_name); +#else +static inline struct gen_pool *sram_get_gpool(char *pool_name) +{ + return NULL; +} +#endif #endif /* __DMA_MMP_TDMA_H */ From 68a8cc9e9e57abcf2b4f40fb54fb8fb3c7f0462e Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 30 Jan 2015 15:06:01 -0700 Subject: [PATCH 243/360] ioatdma: Adding support for BDX-DE ioatdma. Adding PCI device IDs and hooks in workarounds for Broadwell DE ioatdma. Signed-off-by: Dave Jiang Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma_v3.c | 5 +++++ drivers/dma/ioat/hw.h | 5 +++++ drivers/dma/ioat/pci.c | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index be307182231ea3..61510abfabb7cd 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -214,6 +214,11 @@ static bool is_bwd_ioat(struct pci_dev *pdev) case PCI_DEVICE_ID_INTEL_IOAT_BWD1: case PCI_DEVICE_ID_INTEL_IOAT_BWD2: case PCI_DEVICE_ID_INTEL_IOAT_BWD3: + /* even though not Atom, BDX-DE has same DMA silicon */ + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE0: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE1: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE2: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE3: return true; default: return false; diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index 62f83e983d8d0d..02177ecf09f89b 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -57,6 +57,11 @@ #define PCI_DEVICE_ID_INTEL_IOAT_BWD2 0x0C52 #define PCI_DEVICE_ID_INTEL_IOAT_BWD3 0x0C53 +#define PCI_DEVICE_ID_INTEL_IOAT_BDXDE0 0x6f50 +#define PCI_DEVICE_ID_INTEL_IOAT_BDXDE1 0x6f51 +#define PCI_DEVICE_ID_INTEL_IOAT_BDXDE2 0x6f52 +#define PCI_DEVICE_ID_INTEL_IOAT_BDXDE3 0x6f53 + #define IOAT_VER_1_2 0x12 /* Version 1.2 */ #define IOAT_VER_2_0 0x20 /* Version 2.0 */ #define IOAT_VER_3_0 0x30 /* Version 3.0 */ diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index 1d051cd045dbc4..5501eb072d6981 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -111,6 +111,11 @@ static struct pci_device_id ioat_pci_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD2) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD3) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE0) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE1) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE2) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BDXDE3) }, + { 0, } }; MODULE_DEVICE_TABLE(pci, ioat_pci_tbl); From 3028718fd06bb931868a1a8068eab7396a7a622a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 30 Jan 2015 11:29:33 +0300 Subject: [PATCH 244/360] dmaengine: s3c24xx: missing unlock on an error path We should unlock here before returning -EINVAL. Fixes: 39ad46009654 ('dmaengine: s3c24xx: Split device_control') Signed-off-by: Dan Carpenter Acked-by: Maxime Ripard Signed-off-by: Vinod Koul --- drivers/dma/s3c24xx-dma.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 4d5a84815ba7bd..2f91da3db8361c 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -718,13 +718,15 @@ static int s3c24xx_dma_terminate_all(struct dma_chan *chan) struct s3c24xx_dma_chan *s3cchan = to_s3c24xx_dma_chan(chan); struct s3c24xx_dma_engine *s3cdma = s3cchan->host; unsigned long flags; + int ret = 0; spin_lock_irqsave(&s3cchan->vc.lock, flags); if (!s3cchan->phy && !s3cchan->at) { dev_err(&s3cdma->pdev->dev, "trying to terminate already stopped channel %d\n", s3cchan->id); - return -EINVAL; + ret = -EINVAL; + goto unlock; } s3cchan->state = S3C24XX_DMA_CHAN_IDLE; @@ -741,10 +743,10 @@ static int s3c24xx_dma_terminate_all(struct dma_chan *chan) /* Dequeue jobs not yet fired as well */ s3c24xx_dma_free_txd_list(s3cdma, s3cchan); - +unlock: spin_unlock_irqrestore(&s3cchan->vc.lock, flags); - return 0; + return ret; } static int s3c24xx_dma_alloc_chan_resources(struct dma_chan *chan) From 91d457dd50ea2ea35fe5b6e069169491ad45bffb Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Thu, 11 Dec 2014 14:59:16 -0800 Subject: [PATCH 245/360] dmaengine: Add binding document for IMG MDC Add a binding document for the IMG Multi-threaded DMA Controller (MDC) present on the MIPS-based Pistachio and other IMG SoCs. Signed-off-by: Andrew Bresticker Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/img-mdc-dma.txt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/img-mdc-dma.txt diff --git a/Documentation/devicetree/bindings/dma/img-mdc-dma.txt b/Documentation/devicetree/bindings/dma/img-mdc-dma.txt new file mode 100644 index 00000000000000..28c1341db3461c --- /dev/null +++ b/Documentation/devicetree/bindings/dma/img-mdc-dma.txt @@ -0,0 +1,57 @@ +* IMG Multi-threaded DMA Controller (MDC) + +Required properties: +- compatible: Must be "img,pistachio-mdc-dma". +- reg: Must contain the base address and length of the MDC registers. +- interrupts: Must contain all the per-channel DMA interrupts. +- clocks: Must contain an entry for each entry in clock-names. + See ../clock/clock-bindings.txt for details. +- clock-names: Must include the following entries: + - sys: MDC system interface clock. +- img,cr-periph: Must contain a phandle to the peripheral control syscon + node which contains the DMA request to channel mapping registers. +- img,max-burst-multiplier: Must be the maximum supported burst size multiplier. + The maximum burst size is this value multiplied by the hardware-reported bus + width. +- #dma-cells: Must be 3: + - The first cell is the peripheral's DMA request line. + - The second cell is a bitmap specifying to which channels the DMA request + line may be mapped (i.e. bit N set indicates channel N is usable). + - The third cell is the thread ID to be used by the channel. + +Optional properties: +- dma-channels: Number of supported DMA channels, up to 32. If not specified + the number reported by the hardware is used. + +Example: + +mdc: dma-controller@18143000 { + compatible = "img,pistachio-mdc-dma"; + reg = <0x18143000 0x1000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + clocks = <&system_clk>; + clock-names = "sys"; + + img,max-burst-multiplier = <16>; + img,cr-periph = <&cr_periph>; + + #dma-cells = <3>; +}; + +spi@18100f00 { + ... + dmas = <&mdc 9 0xffffffff 0>, <&mdc 10 0xffffffff 0>; + dma-names = "tx", "rx"; + ... +}; From 5689ba7fd9f1118bc6b9e4020c116e0cfebc4654 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Thu, 11 Dec 2014 14:59:17 -0800 Subject: [PATCH 246/360] dmaengine: Add driver for IMG MDC Add support for the IMG Multi-threaded DMA Controller (MDC) found on certain IMG SoCs. Currently this driver supports the variant present on the MIPS-based Pistachio SoC. Signed-off-by: Andrew Bresticker Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/img-mdc-dma.c | 1011 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1021 insertions(+) create mode 100644 drivers/dma/img-mdc-dma.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index f2b2c4e87aef40..8990d483988bb7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -416,6 +416,15 @@ config NBPFAXI_DMA help Support for "Type-AXI" NBPF DMA IPs from Renesas +config IMG_MDC_DMA + tristate "IMG MDC support" + depends on MIPS || COMPILE_TEST + depends on MFD_SYSCON + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Enable support for the IMG multi-threaded DMA controller (MDC). + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index b290e6a611d0ce..f915f61ec57471 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -50,3 +50,4 @@ obj-y += xilinx/ obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o +obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c new file mode 100644 index 00000000000000..ed045a9ad634a4 --- /dev/null +++ b/drivers/dma/img-mdc-dma.c @@ -0,0 +1,1011 @@ +/* + * IMG Multi-threaded DMA Controller (MDC) + * + * Copyright (C) 2009,2012,2013 Imagination Technologies Ltd. + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" +#include "virt-dma.h" + +#define MDC_MAX_DMA_CHANNELS 32 + +#define MDC_GENERAL_CONFIG 0x000 +#define MDC_GENERAL_CONFIG_LIST_IEN BIT(31) +#define MDC_GENERAL_CONFIG_IEN BIT(29) +#define MDC_GENERAL_CONFIG_LEVEL_INT BIT(28) +#define MDC_GENERAL_CONFIG_INC_W BIT(12) +#define MDC_GENERAL_CONFIG_INC_R BIT(8) +#define MDC_GENERAL_CONFIG_PHYSICAL_W BIT(7) +#define MDC_GENERAL_CONFIG_WIDTH_W_SHIFT 4 +#define MDC_GENERAL_CONFIG_WIDTH_W_MASK 0x7 +#define MDC_GENERAL_CONFIG_PHYSICAL_R BIT(3) +#define MDC_GENERAL_CONFIG_WIDTH_R_SHIFT 0 +#define MDC_GENERAL_CONFIG_WIDTH_R_MASK 0x7 + +#define MDC_READ_PORT_CONFIG 0x004 +#define MDC_READ_PORT_CONFIG_STHREAD_SHIFT 28 +#define MDC_READ_PORT_CONFIG_STHREAD_MASK 0xf +#define MDC_READ_PORT_CONFIG_RTHREAD_SHIFT 24 +#define MDC_READ_PORT_CONFIG_RTHREAD_MASK 0xf +#define MDC_READ_PORT_CONFIG_WTHREAD_SHIFT 16 +#define MDC_READ_PORT_CONFIG_WTHREAD_MASK 0xf +#define MDC_READ_PORT_CONFIG_BURST_SIZE_SHIFT 4 +#define MDC_READ_PORT_CONFIG_BURST_SIZE_MASK 0xff +#define MDC_READ_PORT_CONFIG_DREQ_ENABLE BIT(1) + +#define MDC_READ_ADDRESS 0x008 + +#define MDC_WRITE_ADDRESS 0x00c + +#define MDC_TRANSFER_SIZE 0x010 +#define MDC_TRANSFER_SIZE_MASK 0xffffff + +#define MDC_LIST_NODE_ADDRESS 0x014 + +#define MDC_CMDS_PROCESSED 0x018 +#define MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT 16 +#define MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK 0x3f +#define MDC_CMDS_PROCESSED_INT_ACTIVE BIT(8) +#define MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT 0 +#define MDC_CMDS_PROCESSED_CMDS_DONE_MASK 0x3f + +#define MDC_CONTROL_AND_STATUS 0x01c +#define MDC_CONTROL_AND_STATUS_CANCEL BIT(20) +#define MDC_CONTROL_AND_STATUS_LIST_EN BIT(4) +#define MDC_CONTROL_AND_STATUS_EN BIT(0) + +#define MDC_ACTIVE_TRANSFER_SIZE 0x030 + +#define MDC_GLOBAL_CONFIG_A 0x900 +#define MDC_GLOBAL_CONFIG_A_THREAD_ID_WIDTH_SHIFT 16 +#define MDC_GLOBAL_CONFIG_A_THREAD_ID_WIDTH_MASK 0xff +#define MDC_GLOBAL_CONFIG_A_DMA_CONTEXTS_SHIFT 8 +#define MDC_GLOBAL_CONFIG_A_DMA_CONTEXTS_MASK 0xff +#define MDC_GLOBAL_CONFIG_A_SYS_DAT_WIDTH_SHIFT 0 +#define MDC_GLOBAL_CONFIG_A_SYS_DAT_WIDTH_MASK 0xff + +struct mdc_hw_list_desc { + u32 gen_conf; + u32 readport_conf; + u32 read_addr; + u32 write_addr; + u32 xfer_size; + u32 node_addr; + u32 cmds_done; + u32 ctrl_status; + /* + * Not part of the list descriptor, but instead used by the CPU to + * traverse the list. + */ + struct mdc_hw_list_desc *next_desc; +}; + +struct mdc_tx_desc { + struct mdc_chan *chan; + struct virt_dma_desc vd; + dma_addr_t list_phys; + struct mdc_hw_list_desc *list; + bool cyclic; + bool cmd_loaded; + unsigned int list_len; + unsigned int list_period_len; + size_t list_xfer_size; + unsigned int list_cmds_done; +}; + +struct mdc_chan { + struct mdc_dma *mdma; + struct virt_dma_chan vc; + struct dma_slave_config config; + struct mdc_tx_desc *desc; + int irq; + unsigned int periph; + unsigned int thread; + unsigned int chan_nr; +}; + +struct mdc_dma_soc_data { + void (*enable_chan)(struct mdc_chan *mchan); + void (*disable_chan)(struct mdc_chan *mchan); +}; + +struct mdc_dma { + struct dma_device dma_dev; + void __iomem *regs; + struct clk *clk; + struct dma_pool *desc_pool; + struct regmap *periph_regs; + spinlock_t lock; + unsigned int nr_threads; + unsigned int nr_channels; + unsigned int bus_width; + unsigned int max_burst_mult; + unsigned int max_xfer_size; + const struct mdc_dma_soc_data *soc; + struct mdc_chan channels[MDC_MAX_DMA_CHANNELS]; +}; + +static inline u32 mdc_readl(struct mdc_dma *mdma, u32 reg) +{ + return readl(mdma->regs + reg); +} + +static inline void mdc_writel(struct mdc_dma *mdma, u32 val, u32 reg) +{ + writel(val, mdma->regs + reg); +} + +static inline u32 mdc_chan_readl(struct mdc_chan *mchan, u32 reg) +{ + return mdc_readl(mchan->mdma, mchan->chan_nr * 0x040 + reg); +} + +static inline void mdc_chan_writel(struct mdc_chan *mchan, u32 val, u32 reg) +{ + mdc_writel(mchan->mdma, val, mchan->chan_nr * 0x040 + reg); +} + +static inline struct mdc_chan *to_mdc_chan(struct dma_chan *c) +{ + return container_of(to_virt_chan(c), struct mdc_chan, vc); +} + +static inline struct mdc_tx_desc *to_mdc_desc(struct dma_async_tx_descriptor *t) +{ + struct virt_dma_desc *vdesc = container_of(t, struct virt_dma_desc, tx); + + return container_of(vdesc, struct mdc_tx_desc, vd); +} + +static inline struct device *mdma2dev(struct mdc_dma *mdma) +{ + return mdma->dma_dev.dev; +} + +static inline unsigned int to_mdc_width(unsigned int bytes) +{ + return ffs(bytes) - 1; +} + +static inline void mdc_set_read_width(struct mdc_hw_list_desc *ldesc, + unsigned int bytes) +{ + ldesc->gen_conf |= to_mdc_width(bytes) << + MDC_GENERAL_CONFIG_WIDTH_R_SHIFT; +} + +static inline void mdc_set_write_width(struct mdc_hw_list_desc *ldesc, + unsigned int bytes) +{ + ldesc->gen_conf |= to_mdc_width(bytes) << + MDC_GENERAL_CONFIG_WIDTH_W_SHIFT; +} + +static void mdc_list_desc_config(struct mdc_chan *mchan, + struct mdc_hw_list_desc *ldesc, + enum dma_transfer_direction dir, + dma_addr_t src, dma_addr_t dst, size_t len) +{ + struct mdc_dma *mdma = mchan->mdma; + unsigned int max_burst, burst_size; + + ldesc->gen_conf = MDC_GENERAL_CONFIG_IEN | MDC_GENERAL_CONFIG_LIST_IEN | + MDC_GENERAL_CONFIG_LEVEL_INT | MDC_GENERAL_CONFIG_PHYSICAL_W | + MDC_GENERAL_CONFIG_PHYSICAL_R; + ldesc->readport_conf = + (mchan->thread << MDC_READ_PORT_CONFIG_STHREAD_SHIFT) | + (mchan->thread << MDC_READ_PORT_CONFIG_RTHREAD_SHIFT) | + (mchan->thread << MDC_READ_PORT_CONFIG_WTHREAD_SHIFT); + ldesc->read_addr = src; + ldesc->write_addr = dst; + ldesc->xfer_size = len - 1; + ldesc->node_addr = 0; + ldesc->cmds_done = 0; + ldesc->ctrl_status = MDC_CONTROL_AND_STATUS_LIST_EN | + MDC_CONTROL_AND_STATUS_EN; + ldesc->next_desc = NULL; + + if (IS_ALIGNED(dst, mdma->bus_width) && + IS_ALIGNED(src, mdma->bus_width)) + max_burst = mdma->bus_width * mdma->max_burst_mult; + else + max_burst = mdma->bus_width * (mdma->max_burst_mult - 1); + + if (dir == DMA_MEM_TO_DEV) { + ldesc->gen_conf |= MDC_GENERAL_CONFIG_INC_R; + ldesc->readport_conf |= MDC_READ_PORT_CONFIG_DREQ_ENABLE; + mdc_set_read_width(ldesc, mdma->bus_width); + mdc_set_write_width(ldesc, mchan->config.dst_addr_width); + burst_size = min(max_burst, mchan->config.dst_maxburst * + mchan->config.dst_addr_width); + } else if (dir == DMA_DEV_TO_MEM) { + ldesc->gen_conf |= MDC_GENERAL_CONFIG_INC_W; + ldesc->readport_conf |= MDC_READ_PORT_CONFIG_DREQ_ENABLE; + mdc_set_read_width(ldesc, mchan->config.src_addr_width); + mdc_set_write_width(ldesc, mdma->bus_width); + burst_size = min(max_burst, mchan->config.src_maxburst * + mchan->config.src_addr_width); + } else { + ldesc->gen_conf |= MDC_GENERAL_CONFIG_INC_R | + MDC_GENERAL_CONFIG_INC_W; + mdc_set_read_width(ldesc, mdma->bus_width); + mdc_set_write_width(ldesc, mdma->bus_width); + burst_size = max_burst; + } + ldesc->readport_conf |= (burst_size - 1) << + MDC_READ_PORT_CONFIG_BURST_SIZE_SHIFT; +} + +static void mdc_list_desc_free(struct mdc_tx_desc *mdesc) +{ + struct mdc_dma *mdma = mdesc->chan->mdma; + struct mdc_hw_list_desc *curr, *next; + dma_addr_t curr_phys, next_phys; + + curr = mdesc->list; + curr_phys = mdesc->list_phys; + while (curr) { + next = curr->next_desc; + next_phys = curr->node_addr; + dma_pool_free(mdma->desc_pool, curr, curr_phys); + curr = next; + curr_phys = next_phys; + } +} + +static void mdc_desc_free(struct virt_dma_desc *vd) +{ + struct mdc_tx_desc *mdesc = to_mdc_desc(&vd->tx); + + mdc_list_desc_free(mdesc); + kfree(mdesc); +} + +static struct dma_async_tx_descriptor *mdc_prep_dma_memcpy( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, + unsigned long flags) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + struct mdc_dma *mdma = mchan->mdma; + struct mdc_tx_desc *mdesc; + struct mdc_hw_list_desc *curr, *prev = NULL; + dma_addr_t curr_phys, prev_phys; + + if (!len) + return NULL; + + mdesc = kzalloc(sizeof(*mdesc), GFP_NOWAIT); + if (!mdesc) + return NULL; + mdesc->chan = mchan; + mdesc->list_xfer_size = len; + + while (len > 0) { + size_t xfer_size; + + curr = dma_pool_alloc(mdma->desc_pool, GFP_NOWAIT, &curr_phys); + if (!curr) + goto free_desc; + + if (prev) { + prev->node_addr = curr_phys; + prev->next_desc = curr; + } else { + mdesc->list_phys = curr_phys; + mdesc->list = curr; + } + + xfer_size = min_t(size_t, mdma->max_xfer_size, len); + + mdc_list_desc_config(mchan, curr, DMA_MEM_TO_MEM, src, dest, + xfer_size); + + prev = curr; + prev_phys = curr_phys; + + mdesc->list_len++; + src += xfer_size; + dest += xfer_size; + len -= xfer_size; + } + + return vchan_tx_prep(&mchan->vc, &mdesc->vd, flags); + +free_desc: + mdc_desc_free(&mdesc->vd); + + return NULL; +} + +static int mdc_check_slave_width(struct mdc_chan *mchan, + enum dma_transfer_direction dir) +{ + enum dma_slave_buswidth width; + + if (dir == DMA_MEM_TO_DEV) + width = mchan->config.dst_addr_width; + else + width = mchan->config.src_addr_width; + + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + case DMA_SLAVE_BUSWIDTH_2_BYTES: + case DMA_SLAVE_BUSWIDTH_4_BYTES: + case DMA_SLAVE_BUSWIDTH_8_BYTES: + break; + default: + return -EINVAL; + } + + if (width > mchan->mdma->bus_width) + return -EINVAL; + + return 0; +} + +static struct dma_async_tx_descriptor *mdc_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction dir, + unsigned long flags) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + struct mdc_dma *mdma = mchan->mdma; + struct mdc_tx_desc *mdesc; + struct mdc_hw_list_desc *curr, *prev = NULL; + dma_addr_t curr_phys, prev_phys; + + if (!buf_len && !period_len) + return NULL; + + if (!is_slave_direction(dir)) + return NULL; + + if (mdc_check_slave_width(mchan, dir) < 0) + return NULL; + + mdesc = kzalloc(sizeof(*mdesc), GFP_NOWAIT); + if (!mdesc) + return NULL; + mdesc->chan = mchan; + mdesc->cyclic = true; + mdesc->list_xfer_size = buf_len; + mdesc->list_period_len = DIV_ROUND_UP(period_len, + mdma->max_xfer_size); + + while (buf_len > 0) { + size_t remainder = min(period_len, buf_len); + + while (remainder > 0) { + size_t xfer_size; + + curr = dma_pool_alloc(mdma->desc_pool, GFP_NOWAIT, + &curr_phys); + if (!curr) + goto free_desc; + + if (!prev) { + mdesc->list_phys = curr_phys; + mdesc->list = curr; + } else { + prev->node_addr = curr_phys; + prev->next_desc = curr; + } + + xfer_size = min_t(size_t, mdma->max_xfer_size, + remainder); + + if (dir == DMA_MEM_TO_DEV) { + mdc_list_desc_config(mchan, curr, dir, + buf_addr, + mchan->config.dst_addr, + xfer_size); + } else { + mdc_list_desc_config(mchan, curr, dir, + mchan->config.src_addr, + buf_addr, + xfer_size); + } + + prev = curr; + prev_phys = curr_phys; + + mdesc->list_len++; + buf_addr += xfer_size; + buf_len -= xfer_size; + remainder -= xfer_size; + } + } + prev->node_addr = mdesc->list_phys; + + return vchan_tx_prep(&mchan->vc, &mdesc->vd, flags); + +free_desc: + mdc_desc_free(&mdesc->vd); + + return NULL; +} + +static struct dma_async_tx_descriptor *mdc_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags, void *context) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + struct mdc_dma *mdma = mchan->mdma; + struct mdc_tx_desc *mdesc; + struct scatterlist *sg; + struct mdc_hw_list_desc *curr, *prev = NULL; + dma_addr_t curr_phys, prev_phys; + unsigned int i; + + if (!sgl) + return NULL; + + if (!is_slave_direction(dir)) + return NULL; + + if (mdc_check_slave_width(mchan, dir) < 0) + return NULL; + + mdesc = kzalloc(sizeof(*mdesc), GFP_NOWAIT); + if (!mdesc) + return NULL; + mdesc->chan = mchan; + + for_each_sg(sgl, sg, sg_len, i) { + dma_addr_t buf = sg_dma_address(sg); + size_t buf_len = sg_dma_len(sg); + + while (buf_len > 0) { + size_t xfer_size; + + curr = dma_pool_alloc(mdma->desc_pool, GFP_NOWAIT, + &curr_phys); + if (!curr) + goto free_desc; + + if (!prev) { + mdesc->list_phys = curr_phys; + mdesc->list = curr; + } else { + prev->node_addr = curr_phys; + prev->next_desc = curr; + } + + xfer_size = min_t(size_t, mdma->max_xfer_size, + buf_len); + + if (dir == DMA_MEM_TO_DEV) { + mdc_list_desc_config(mchan, curr, dir, buf, + mchan->config.dst_addr, + xfer_size); + } else { + mdc_list_desc_config(mchan, curr, dir, + mchan->config.src_addr, + buf, xfer_size); + } + + prev = curr; + prev_phys = curr_phys; + + mdesc->list_len++; + mdesc->list_xfer_size += xfer_size; + buf += xfer_size; + buf_len -= xfer_size; + } + } + + return vchan_tx_prep(&mchan->vc, &mdesc->vd, flags); + +free_desc: + mdc_desc_free(&mdesc->vd); + + return NULL; +} + +static void mdc_issue_desc(struct mdc_chan *mchan) +{ + struct mdc_dma *mdma = mchan->mdma; + struct virt_dma_desc *vd; + struct mdc_tx_desc *mdesc; + u32 val; + + vd = vchan_next_desc(&mchan->vc); + if (!vd) + return; + + list_del(&vd->node); + + mdesc = to_mdc_desc(&vd->tx); + mchan->desc = mdesc; + + dev_dbg(mdma2dev(mdma), "Issuing descriptor on channel %d\n", + mchan->chan_nr); + + mdma->soc->enable_chan(mchan); + + val = mdc_chan_readl(mchan, MDC_GENERAL_CONFIG); + val |= MDC_GENERAL_CONFIG_LIST_IEN | MDC_GENERAL_CONFIG_IEN | + MDC_GENERAL_CONFIG_LEVEL_INT | MDC_GENERAL_CONFIG_PHYSICAL_W | + MDC_GENERAL_CONFIG_PHYSICAL_R; + mdc_chan_writel(mchan, val, MDC_GENERAL_CONFIG); + val = (mchan->thread << MDC_READ_PORT_CONFIG_STHREAD_SHIFT) | + (mchan->thread << MDC_READ_PORT_CONFIG_RTHREAD_SHIFT) | + (mchan->thread << MDC_READ_PORT_CONFIG_WTHREAD_SHIFT); + mdc_chan_writel(mchan, val, MDC_READ_PORT_CONFIG); + mdc_chan_writel(mchan, mdesc->list_phys, MDC_LIST_NODE_ADDRESS); + val = mdc_chan_readl(mchan, MDC_CONTROL_AND_STATUS); + val |= MDC_CONTROL_AND_STATUS_LIST_EN; + mdc_chan_writel(mchan, val, MDC_CONTROL_AND_STATUS); +} + +static void mdc_issue_pending(struct dma_chan *chan) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&mchan->vc.lock, flags); + if (vchan_issue_pending(&mchan->vc) && !mchan->desc) + mdc_issue_desc(mchan); + spin_unlock_irqrestore(&mchan->vc.lock, flags); +} + +static enum dma_status mdc_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + struct mdc_tx_desc *mdesc; + struct virt_dma_desc *vd; + unsigned long flags; + size_t bytes = 0; + int ret; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + + if (!txstate) + return ret; + + spin_lock_irqsave(&mchan->vc.lock, flags); + vd = vchan_find_desc(&mchan->vc, cookie); + if (vd) { + mdesc = to_mdc_desc(&vd->tx); + bytes = mdesc->list_xfer_size; + } else if (mchan->desc && mchan->desc->vd.tx.cookie == cookie) { + struct mdc_hw_list_desc *ldesc; + u32 val1, val2, done, processed, residue; + int i, cmds; + + mdesc = mchan->desc; + + /* + * Determine the number of commands that haven't been + * processed (handled by the IRQ handler) yet. + */ + do { + val1 = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED) & + ~MDC_CMDS_PROCESSED_INT_ACTIVE; + residue = mdc_chan_readl(mchan, + MDC_ACTIVE_TRANSFER_SIZE); + val2 = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED) & + ~MDC_CMDS_PROCESSED_INT_ACTIVE; + } while (val1 != val2); + + done = (val1 >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) & + MDC_CMDS_PROCESSED_CMDS_DONE_MASK; + processed = (val1 >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) & + MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK; + cmds = (done - processed) % + (MDC_CMDS_PROCESSED_CMDS_DONE_MASK + 1); + + /* + * If the command loaded event hasn't been processed yet, then + * the difference above includes an extra command. + */ + if (!mdesc->cmd_loaded) + cmds--; + else + cmds += mdesc->list_cmds_done; + + bytes = mdesc->list_xfer_size; + ldesc = mdesc->list; + for (i = 0; i < cmds; i++) { + bytes -= ldesc->xfer_size + 1; + ldesc = ldesc->next_desc; + } + if (ldesc) { + if (residue != MDC_TRANSFER_SIZE_MASK) + bytes -= ldesc->xfer_size - residue; + else + bytes -= ldesc->xfer_size + 1; + } + } + spin_unlock_irqrestore(&mchan->vc.lock, flags); + + dma_set_residue(txstate, bytes); + + return ret; +} + +static int mdc_terminate_all(struct dma_chan *chan) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + struct mdc_tx_desc *mdesc; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&mchan->vc.lock, flags); + + mdc_chan_writel(mchan, MDC_CONTROL_AND_STATUS_CANCEL, + MDC_CONTROL_AND_STATUS); + + mdesc = mchan->desc; + mchan->desc = NULL; + vchan_get_all_descriptors(&mchan->vc, &head); + + spin_unlock_irqrestore(&mchan->vc.lock, flags); + + if (mdesc) + mdc_desc_free(&mdesc->vd); + vchan_dma_desc_free_list(&mchan->vc, &head); + + return 0; +} + +static int mdc_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&mchan->vc.lock, flags); + mchan->config = *config; + spin_unlock_irqrestore(&mchan->vc.lock, flags); + + return 0; +} + +static int mdc_alloc_chan_resources(struct dma_chan *chan) +{ + return 0; +} + +static void mdc_free_chan_resources(struct dma_chan *chan) +{ + struct mdc_chan *mchan = to_mdc_chan(chan); + struct mdc_dma *mdma = mchan->mdma; + + mdc_terminate_all(chan); + + mdma->soc->disable_chan(mchan); +} + +static irqreturn_t mdc_chan_irq(int irq, void *dev_id) +{ + struct mdc_chan *mchan = (struct mdc_chan *)dev_id; + struct mdc_tx_desc *mdesc; + u32 val, processed, done1, done2; + unsigned int i; + + spin_lock(&mchan->vc.lock); + + val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED); + processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) & + MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK; + /* + * CMDS_DONE may have incremented between reading CMDS_PROCESSED + * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we + * didn't miss a command completion. + */ + do { + val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED); + done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) & + MDC_CMDS_PROCESSED_CMDS_DONE_MASK; + val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK << + MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) | + MDC_CMDS_PROCESSED_INT_ACTIVE); + val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT; + mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED); + val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED); + done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) & + MDC_CMDS_PROCESSED_CMDS_DONE_MASK; + } while (done1 != done2); + + dev_dbg(mdma2dev(mchan->mdma), "IRQ on channel %d\n", mchan->chan_nr); + + mdesc = mchan->desc; + if (!mdesc) { + dev_warn(mdma2dev(mchan->mdma), + "IRQ with no active descriptor on channel %d\n", + mchan->chan_nr); + goto out; + } + + for (i = processed; i != done1; + i = (i + 1) % (MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1)) { + /* + * The first interrupt in a transfer indicates that the + * command list has been loaded, not that a command has + * been completed. + */ + if (!mdesc->cmd_loaded) { + mdesc->cmd_loaded = true; + continue; + } + + mdesc->list_cmds_done++; + if (mdesc->cyclic) { + mdesc->list_cmds_done %= mdesc->list_len; + if (mdesc->list_cmds_done % mdesc->list_period_len == 0) + vchan_cyclic_callback(&mdesc->vd); + } else if (mdesc->list_cmds_done == mdesc->list_len) { + mchan->desc = NULL; + vchan_cookie_complete(&mdesc->vd); + mdc_issue_desc(mchan); + break; + } + } +out: + spin_unlock(&mchan->vc.lock); + + return IRQ_HANDLED; +} + +static struct dma_chan *mdc_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct mdc_dma *mdma = ofdma->of_dma_data; + struct dma_chan *chan; + + if (dma_spec->args_count != 3) + return NULL; + + list_for_each_entry(chan, &mdma->dma_dev.channels, device_node) { + struct mdc_chan *mchan = to_mdc_chan(chan); + + if (!(dma_spec->args[1] & BIT(mchan->chan_nr))) + continue; + if (dma_get_slave_channel(chan)) { + mchan->periph = dma_spec->args[0]; + mchan->thread = dma_spec->args[2]; + return chan; + } + } + + return NULL; +} + +#define PISTACHIO_CR_PERIPH_DMA_ROUTE(ch) (0x120 + 0x4 * ((ch) / 4)) +#define PISTACHIO_CR_PERIPH_DMA_ROUTE_SHIFT(ch) (8 * ((ch) % 4)) +#define PISTACHIO_CR_PERIPH_DMA_ROUTE_MASK 0x3f + +static void pistachio_mdc_enable_chan(struct mdc_chan *mchan) +{ + struct mdc_dma *mdma = mchan->mdma; + + regmap_update_bits(mdma->periph_regs, + PISTACHIO_CR_PERIPH_DMA_ROUTE(mchan->chan_nr), + PISTACHIO_CR_PERIPH_DMA_ROUTE_MASK << + PISTACHIO_CR_PERIPH_DMA_ROUTE_SHIFT(mchan->chan_nr), + mchan->periph << + PISTACHIO_CR_PERIPH_DMA_ROUTE_SHIFT(mchan->chan_nr)); +} + +static void pistachio_mdc_disable_chan(struct mdc_chan *mchan) +{ + struct mdc_dma *mdma = mchan->mdma; + + regmap_update_bits(mdma->periph_regs, + PISTACHIO_CR_PERIPH_DMA_ROUTE(mchan->chan_nr), + PISTACHIO_CR_PERIPH_DMA_ROUTE_MASK << + PISTACHIO_CR_PERIPH_DMA_ROUTE_SHIFT(mchan->chan_nr), + 0); +} + +static const struct mdc_dma_soc_data pistachio_mdc_data = { + .enable_chan = pistachio_mdc_enable_chan, + .disable_chan = pistachio_mdc_disable_chan, +}; + +static const struct of_device_id mdc_dma_of_match[] = { + { .compatible = "img,pistachio-mdc-dma", .data = &pistachio_mdc_data, }, + { }, +}; +MODULE_DEVICE_TABLE(of, mdc_dma_of_match); + +static int mdc_dma_probe(struct platform_device *pdev) +{ + struct mdc_dma *mdma; + struct resource *res; + const struct of_device_id *match; + unsigned int i; + u32 val; + int ret; + + mdma = devm_kzalloc(&pdev->dev, sizeof(*mdma), GFP_KERNEL); + if (!mdma) + return -ENOMEM; + platform_set_drvdata(pdev, mdma); + + match = of_match_device(mdc_dma_of_match, &pdev->dev); + mdma->soc = match->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdma->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdma->regs)) + return PTR_ERR(mdma->regs); + + mdma->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "img,cr-periph"); + if (IS_ERR(mdma->periph_regs)) + return PTR_ERR(mdma->periph_regs); + + mdma->clk = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(mdma->clk)) + return PTR_ERR(mdma->clk); + + ret = clk_prepare_enable(mdma->clk); + if (ret) + return ret; + + dma_cap_zero(mdma->dma_dev.cap_mask); + dma_cap_set(DMA_SLAVE, mdma->dma_dev.cap_mask); + dma_cap_set(DMA_PRIVATE, mdma->dma_dev.cap_mask); + dma_cap_set(DMA_CYCLIC, mdma->dma_dev.cap_mask); + dma_cap_set(DMA_MEMCPY, mdma->dma_dev.cap_mask); + + val = mdc_readl(mdma, MDC_GLOBAL_CONFIG_A); + mdma->nr_channels = (val >> MDC_GLOBAL_CONFIG_A_DMA_CONTEXTS_SHIFT) & + MDC_GLOBAL_CONFIG_A_DMA_CONTEXTS_MASK; + mdma->nr_threads = + 1 << ((val >> MDC_GLOBAL_CONFIG_A_THREAD_ID_WIDTH_SHIFT) & + MDC_GLOBAL_CONFIG_A_THREAD_ID_WIDTH_MASK); + mdma->bus_width = + (1 << ((val >> MDC_GLOBAL_CONFIG_A_SYS_DAT_WIDTH_SHIFT) & + MDC_GLOBAL_CONFIG_A_SYS_DAT_WIDTH_MASK)) / 8; + /* + * Although transfer sizes of up to MDC_TRANSFER_SIZE_MASK + 1 bytes + * are supported, this makes it possible for the value reported in + * MDC_ACTIVE_TRANSFER_SIZE to be ambiguous - an active transfer size + * of MDC_TRANSFER_SIZE_MASK may indicate either that 0 bytes or + * MDC_TRANSFER_SIZE_MASK + 1 bytes are remaining. To eliminate this + * ambiguity, restrict transfer sizes to one bus-width less than the + * actual maximum. + */ + mdma->max_xfer_size = MDC_TRANSFER_SIZE_MASK + 1 - mdma->bus_width; + + of_property_read_u32(pdev->dev.of_node, "dma-channels", + &mdma->nr_channels); + ret = of_property_read_u32(pdev->dev.of_node, + "img,max-burst-multiplier", + &mdma->max_burst_mult); + if (ret) + goto disable_clk; + + mdma->dma_dev.dev = &pdev->dev; + mdma->dma_dev.device_prep_slave_sg = mdc_prep_slave_sg; + mdma->dma_dev.device_prep_dma_cyclic = mdc_prep_dma_cyclic; + mdma->dma_dev.device_prep_dma_memcpy = mdc_prep_dma_memcpy; + mdma->dma_dev.device_alloc_chan_resources = mdc_alloc_chan_resources; + mdma->dma_dev.device_free_chan_resources = mdc_free_chan_resources; + mdma->dma_dev.device_tx_status = mdc_tx_status; + mdma->dma_dev.device_issue_pending = mdc_issue_pending; + mdma->dma_dev.device_terminate_all = mdc_terminate_all; + mdma->dma_dev.device_config = mdc_slave_config; + + mdma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + mdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + for (i = 1; i <= mdma->bus_width; i <<= 1) { + mdma->dma_dev.src_addr_widths |= BIT(i); + mdma->dma_dev.dst_addr_widths |= BIT(i); + } + + INIT_LIST_HEAD(&mdma->dma_dev.channels); + for (i = 0; i < mdma->nr_channels; i++) { + struct mdc_chan *mchan = &mdma->channels[i]; + + mchan->mdma = mdma; + mchan->chan_nr = i; + mchan->irq = platform_get_irq(pdev, i); + if (mchan->irq < 0) { + ret = mchan->irq; + goto disable_clk; + } + ret = devm_request_irq(&pdev->dev, mchan->irq, mdc_chan_irq, + IRQ_TYPE_LEVEL_HIGH, + dev_name(&pdev->dev), mchan); + if (ret < 0) + goto disable_clk; + + mchan->vc.desc_free = mdc_desc_free; + vchan_init(&mchan->vc, &mdma->dma_dev); + } + + mdma->desc_pool = dmam_pool_create(dev_name(&pdev->dev), &pdev->dev, + sizeof(struct mdc_hw_list_desc), + 4, 0); + if (!mdma->desc_pool) { + ret = -ENOMEM; + goto disable_clk; + } + + ret = dma_async_device_register(&mdma->dma_dev); + if (ret) + goto disable_clk; + + ret = of_dma_controller_register(pdev->dev.of_node, mdc_of_xlate, mdma); + if (ret) + goto unregister; + + dev_info(&pdev->dev, "MDC with %u channels and %u threads\n", + mdma->nr_channels, mdma->nr_threads); + + return 0; + +unregister: + dma_async_device_unregister(&mdma->dma_dev); +disable_clk: + clk_disable_unprepare(mdma->clk); + return ret; +} + +static int mdc_dma_remove(struct platform_device *pdev) +{ + struct mdc_dma *mdma = platform_get_drvdata(pdev); + struct mdc_chan *mchan, *next; + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&mdma->dma_dev); + + list_for_each_entry_safe(mchan, next, &mdma->dma_dev.channels, + vc.chan.device_node) { + list_del(&mchan->vc.chan.device_node); + + synchronize_irq(mchan->irq); + devm_free_irq(&pdev->dev, mchan->irq, mchan); + + tasklet_kill(&mchan->vc.task); + } + + clk_disable_unprepare(mdma->clk); + + return 0; +} + +static struct platform_driver mdc_dma_driver = { + .driver = { + .name = "img-mdc-dma", + .of_match_table = of_match_ptr(mdc_dma_of_match), + }, + .probe = mdc_dma_probe, + .remove = mdc_dma_remove, +}; +module_platform_driver(mdc_dma_driver); + +MODULE_DESCRIPTION("IMG Multi-threaded DMA Controller (MDC) driver"); +MODULE_AUTHOR("Andrew Bresticker "); +MODULE_LICENSE("GPL v2"); From a835bb8550f73c5e9aaf71ae829da86d34dcbe2b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Oct 2014 16:16:42 +0300 Subject: [PATCH 247/360] dmatest: fix indentation Simple fixes an indentation in few places across the code. There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index a8d7809e2f4c63..87e5d9a75f1616 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -349,14 +349,14 @@ static void dbg_result(const char *err, unsigned int n, unsigned int src_off, unsigned long data) { pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n", - current->comm, n, err, src_off, dst_off, len, data); + current->comm, n, err, src_off, dst_off, len, data); } -#define verbose_result(err, n, src_off, dst_off, len, data) ({ \ - if (verbose) \ - result(err, n, src_off, dst_off, len, data); \ - else \ - dbg_result(err, n, src_off, dst_off, len, data); \ +#define verbose_result(err, n, src_off, dst_off, len, data) ({ \ + if (verbose) \ + result(err, n, src_off, dst_off, len, data); \ + else \ + dbg_result(err, n, src_off, dst_off, len, data);\ }) static unsigned long long dmatest_persec(s64 runtime, unsigned int val) From ede23a586886bbd0d5246b2fa89cf78b81265aa3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Oct 2014 16:16:43 +0300 Subject: [PATCH 248/360] dmatest: move src_off, dst_off, len inside loop The scope of those varsiables is in while-loop. This patch moves them there and removes duplicate code. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 87e5d9a75f1616..220ee49633e49e 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -405,7 +405,6 @@ static int dmatest_func(void *data) struct dmatest_params *params; struct dma_chan *chan; struct dma_device *dev; - unsigned int src_off, dst_off, len; unsigned int error_count; unsigned int failed_tests = 0; unsigned int total_tests = 0; @@ -484,6 +483,7 @@ static int dmatest_func(void *data) struct dmaengine_unmap_data *um; dma_addr_t srcs[src_cnt]; dma_addr_t *dsts; + unsigned int src_off, dst_off, len; u8 align = 0; total_tests++; @@ -502,15 +502,21 @@ static int dmatest_func(void *data) break; } - if (params->noverify) { + if (params->noverify) len = params->buf_size; + else + len = dmatest_random() % params->buf_size + 1; + + len = (len >> align) << align; + if (!len) + len = 1 << align; + + total_len += len; + + if (params->noverify) { src_off = 0; dst_off = 0; } else { - len = dmatest_random() % params->buf_size + 1; - len = (len >> align) << align; - if (!len) - len = 1 << align; src_off = dmatest_random() % (params->buf_size - len + 1); dst_off = dmatest_random() % (params->buf_size - len + 1); @@ -523,11 +529,6 @@ static int dmatest_func(void *data) params->buf_size); } - len = (len >> align) << align; - if (!len) - len = 1 << align; - total_len += len; - um = dmaengine_get_unmap_data(dev->dev, src_cnt+dst_cnt, GFP_KERNEL); if (!um) { From cfd8fef322305bbe9955817464f0d2054ce545c0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Jan 2015 19:08:13 +0200 Subject: [PATCH 249/360] dmaengine: dw: amend description of dma_dev field The dma_dev field is widely used in filter functions to mach with a proper DMA controller device. Thus it's not deprecated. The patch fixes the description of that field. There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-dw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index d8155c005242e7..359127d290e74d 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -16,7 +16,7 @@ /** * struct dw_dma_slave - Controller-specific information about a slave * - * @dma_dev: required DMA master device. Depricated. + * @dma_dev: required DMA master device * @src_id: src request line * @dst_id: dst request line * @src_master: src master for transfers on allocated channel. From d8ded50f8b26a224df48f9f93e49440c6a39b77f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Jan 2015 19:08:14 +0200 Subject: [PATCH 250/360] dmaengine: dw: define DW_DMA_MAX_NR_MASTERS Instead of using magic number in the code the patch provides DW_DMA_MAX_NR_MASTERS constant. While here, restrict the reading of data width array by amount of the actual number of AHB masters. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps-dma.txt | 2 +- arch/arc/boot/dts/abilis_tb10x.dtsi | 2 +- arch/arm/boot/dts/spear13xx.dtsi | 4 ++-- arch/avr32/mach-at32ap/at32ap700x.c | 2 +- drivers/dma/dw/core.c | 3 ++- drivers/dma/dw/platform.c | 4 ++-- drivers/dma/dw/regs.h | 2 +- include/linux/platform_data/dma-dw.h | 4 +++- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index d58675ea1abf2e..c261598164a761 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -38,7 +38,7 @@ Example: chan_allocation_order = <1>; chan_priority = <1>; block_size = <0xfff>; - data_width = <3 3 0 0>; + data_width = <3 3>; }; DMA clients connected to the Designware DMA controller must use the format diff --git a/arch/arc/boot/dts/abilis_tb10x.dtsi b/arch/arc/boot/dts/abilis_tb10x.dtsi index a098d7c05e9674..cfb5052239a1cf 100644 --- a/arch/arc/boot/dts/abilis_tb10x.dtsi +++ b/arch/arc/boot/dts/abilis_tb10x.dtsi @@ -112,7 +112,7 @@ chan_allocation_order = <0>; chan_priority = <1>; block_size = <0x7ff>; - data_width = <2 0 0 0>; + data_width = <2>; clocks = <&ahb_clk>; clock-names = "hclk"; }; diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index a6eb5436d26d45..40accc87e3a252 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -117,7 +117,7 @@ chan_priority = <1>; block_size = <0xfff>; dma-masters = <2>; - data_width = <3 3 0 0>; + data_width = <3 3>; }; dma@eb000000 { @@ -133,7 +133,7 @@ chan_allocation_order = <1>; chan_priority = <1>; block_size = <0xfff>; - data_width = <3 3 0 0>; + data_width = <3 3>; }; fsmc: flash@b0000000 { diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index cc92cdb9994c88..1d8b147282cf78 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -607,7 +607,7 @@ static struct dw_dma_platform_data dw_dmac0_data = { .nr_channels = 3, .block_size = 4095U, .nr_masters = 2, - .data_width = { 2, 2, 0, 0 }, + .data_width = { 2, 2 }, }; static struct resource dw_dmac0_resource[] = { diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index fcb9a916e6f65c..0469d8eda25369 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1562,7 +1562,8 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) } } else { dw->nr_masters = pdata->nr_masters; - memcpy(dw->data_width, pdata->data_width, 4); + for (i = 0; i < dw->nr_masters; i++) + dw->data_width[i] = pdata->data_width[i]; } /* Calculate all channel mask before DMA setup */ diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index a630161473a4fa..aaff37f53523c2 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -99,7 +99,7 @@ dw_dma_parse_dt(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct dw_dma_platform_data *pdata; - u32 tmp, arr[4]; + u32 tmp, arr[DW_DMA_MAX_NR_MASTERS]; if (!np) { dev_err(&pdev->dev, "Missing DT data\n"); @@ -126,7 +126,7 @@ dw_dma_parse_dt(struct platform_device *pdev) pdata->block_size = tmp; if (!of_property_read_u32(np, "dma-masters", &tmp)) { - if (tmp > 4) + if (tmp > DW_DMA_MAX_NR_MASTERS) return NULL; pdata->nr_masters = tmp; diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 254a1db0368079..241ff2b1402bf9 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -285,7 +285,7 @@ struct dw_dma { /* hardware configuration */ unsigned char nr_masters; - unsigned char data_width[4]; + unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; }; static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 359127d290e74d..87ac14c584f2cd 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -13,6 +13,8 @@ #include +#define DW_DMA_MAX_NR_MASTERS 4 + /** * struct dw_dma_slave - Controller-specific information about a slave * @@ -53,7 +55,7 @@ struct dw_dma_platform_data { unsigned char chan_priority; unsigned short block_size; unsigned char nr_masters; - unsigned char data_width[4]; + unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; }; #endif /* _PLATFORM_DATA_DMA_DW_H */ From 12385f458a27fe46da31483ed9f9ec5e9da575e7 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Thu, 8 Jan 2015 15:16:35 +0100 Subject: [PATCH 251/360] ioat: fail self-test if wait_for_completion times out wait_for_completion_timeout reaching timeout was being ignored, fail the self-test if timeout condition occurs. v2: fixup of coding style issues. Signed-off-by: Nicholas Mc Guire Acked-by: Dave Jiang Reviewed-by: Prarit Bhargava Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma_v3.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 61510abfabb7cd..77a6dcf25b98b8 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1316,7 +1316,8 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { + if (tmo == 0 || + dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { dev_err(dev, "Self-test xor timed out\n"); err = -ENODEV; goto dma_unmap; @@ -1382,7 +1383,8 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { + if (tmo == 0 || + dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { dev_err(dev, "Self-test validate timed out\n"); err = -ENODEV; goto dma_unmap; @@ -1434,7 +1436,8 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { + if (tmo == 0 || + dma->device_tx_status(dma_chan, cookie, NULL) != DMA_COMPLETE) { dev_err(dev, "Self-test 2nd validate timed out\n"); err = -ENODEV; goto dma_unmap; From cbb85e672690ad3d02e97aeba33a1e1f722bbacc Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 27 Jan 2015 16:30:29 +0100 Subject: [PATCH 252/360] dmaengine: at_xdmac: wait for in-progress transaction to complete after pausing a channel Signed-off-by: Cyrille Pitchen Signed-off-by: Ludovic Desroches Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index c39000b9980acc..ed8a576cee7bd8 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1136,9 +1136,14 @@ static int at_xdmac_device_pause(struct dma_chan *chan) dev_dbg(chan2dev(chan), "%s\n", __func__); + if (test_and_set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status)) + return 0; + spin_lock_bh(&atchan->lock); at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask); - set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); + while (at_xdmac_chan_read(atchan, AT_XDMAC_CC) + & (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP)) + cpu_relax(); spin_unlock_bh(&atchan->lock); return 0; From 734bb9a7b3e198ba3b7d12565dce31d1568ab018 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Tue, 27 Jan 2015 16:30:30 +0100 Subject: [PATCH 253/360] dmaengine: at_xdmac: introduce save_cc field When suspending the device, read the channel configuration directly from the register instead of relying on a software snapshot, it will be safer. Signed-off-by: Ludovic Desroches Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index ed8a576cee7bd8..8aebf183328455 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -200,6 +200,7 @@ struct at_xdmac_chan { u8 memif; /* Memory Interface */ u32 per_src_addr; u32 per_dst_addr; + u32 save_cc; u32 save_cim; u32 save_cnda; u32 save_cndc; @@ -1276,6 +1277,7 @@ static int atmel_xdmac_suspend(struct device *dev) list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) { struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); + atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC); if (at_xdmac_chan_is_cyclic(atchan)) { if (!at_xdmac_chan_is_paused(atchan)) at_xdmac_device_pause(chan); @@ -1298,7 +1300,6 @@ static int atmel_xdmac_resume(struct device *dev) struct at_xdmac_chan *atchan; struct dma_chan *chan, *_chan; int i; - u32 cfg; clk_prepare_enable(atxdmac->clk); @@ -1313,8 +1314,7 @@ static int atmel_xdmac_resume(struct device *dev) at_xdmac_write(atxdmac, AT_XDMAC_GE, atxdmac->save_gs); list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) { atchan = to_at_xdmac_chan(chan); - cfg = atchan->cfg[AT_XDMAC_CUR_CFG]; - at_xdmac_chan_write(atchan, AT_XDMAC_CC, cfg); + at_xdmac_chan_write(atchan, AT_XDMAC_CC, atchan->save_cc); if (at_xdmac_chan_is_cyclic(atchan)) { at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda); at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, atchan->save_cndc); From be835074829b13c5f635ef78ed911b13b9c15fa9 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Tue, 27 Jan 2015 16:30:31 +0100 Subject: [PATCH 254/360] dmaengine: at_xdmac: simplify channel configuration stuff This patch simplifies the channel configuration register management. Relying on a "software snapshot" of the configuration is not safe and too complex. Multiple dwidths will be introduced for slave transfers. In this case, it becomes quite difficult to have an accurate snapshot of the channel configuration register in the way it is done. Using the channel configuration available in the lli descriptor simplifies this stuff. Signed-off-by: Ludovic Desroches Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 8aebf183328455..eba66a22a265df 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -191,10 +191,9 @@ struct at_xdmac_chan { struct dma_chan chan; void __iomem *ch_regs; u32 mask; /* Channel Mask */ - u32 cfg[3]; /* Channel Configuration Register */ - #define AT_XDMAC_CUR_CFG 0 /* Current channel conf */ - #define AT_XDMAC_DEV_TO_MEM_CFG 1 /* Predifined dev to mem channel conf */ - #define AT_XDMAC_MEM_TO_DEV_CFG 2 /* Predifined mem to dev channel conf */ + u32 cfg[2]; /* Channel Configuration Register */ + #define AT_XDMAC_DEV_TO_MEM_CFG 0 /* Predifined dev to mem channel conf */ + #define AT_XDMAC_MEM_TO_DEV_CFG 1 /* Predifined mem to dev channel conf */ u8 perid; /* Peripheral ID */ u8 perif; /* Peripheral Interface */ u8 memif; /* Memory Interface */ @@ -358,14 +357,7 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan, */ if (is_slave_direction(first->direction)) { reg = AT_XDMAC_CNDC_NDVIEW_NDV1; - if (first->direction == DMA_MEM_TO_DEV) - atchan->cfg[AT_XDMAC_CUR_CFG] = - atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG]; - else - atchan->cfg[AT_XDMAC_CUR_CFG] = - atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG]; - at_xdmac_chan_write(atchan, AT_XDMAC_CC, - atchan->cfg[AT_XDMAC_CUR_CFG]); + at_xdmac_chan_write(atchan, AT_XDMAC_CC, first->lld.mbr_cfg); } else { /* * No need to write AT_XDMAC_CC reg, it will be done when the @@ -569,7 +561,6 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct at_xdmac_desc *first = NULL, *prev = NULL; struct scatterlist *sg; int i; - u32 cfg; unsigned int xfer_size = 0; if (!sgl) @@ -616,17 +607,17 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (direction == DMA_DEV_TO_MEM) { desc->lld.mbr_sa = atchan->per_src_addr; desc->lld.mbr_da = mem; - cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG]; + desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG]; } else { desc->lld.mbr_sa = mem; desc->lld.mbr_da = atchan->per_dst_addr; - cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG]; + desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG]; } - desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1 /* next descriptor view */ - | AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ - | AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ - | (i == sg_len - 1 ? 0 : AT_XDMAC_MBR_UBC_NDE) /* descriptor fetch */ - | len / (1 << at_xdmac_get_dwidth(cfg)); /* microblock length */ + desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1 /* next descriptor view */ + | AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ + | AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ + | (i == sg_len - 1 ? 0 : AT_XDMAC_MBR_UBC_NDE) /* descriptor fetch */ + | len / (1 << at_xdmac_get_dwidth(desc->lld.mbr_cfg)); /* microblock length */ dev_dbg(chan2dev(chan), "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc); @@ -890,7 +881,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, enum dma_status ret; int residue; u32 cur_nda, mask, value; - u8 dwidth = at_xdmac_get_dwidth(atchan->cfg[AT_XDMAC_CUR_CFG]); + u8 dwidth = 0; ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_COMPLETE) @@ -920,7 +911,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, */ mask = AT_XDMAC_CC_TYPE | AT_XDMAC_CC_DSYNC; value = AT_XDMAC_CC_TYPE_PER_TRAN | AT_XDMAC_CC_DSYNC_PER2MEM; - if ((atchan->cfg[AT_XDMAC_CUR_CFG] & mask) == value) { + if ((desc->lld.mbr_cfg & mask) == value) { at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask); while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS)) cpu_relax(); @@ -934,6 +925,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, */ descs_list = &desc->descs_list; list_for_each_entry_safe(desc, _desc, descs_list, desc_node) { + dwidth = at_xdmac_get_dwidth(desc->lld.mbr_cfg); residue -= (desc->lld.mbr_ubc & 0xffffff) << dwidth; if ((desc->lld.mbr_nda & 0xfffffffc) == cur_nda) break; From 6d3a7d9e3ada345948f72564ce638c412ccd8c4a Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Tue, 27 Jan 2015 16:30:32 +0100 Subject: [PATCH 255/360] dmaengine: at_xdmac: allow muliple dwidths when doing slave transfers When using FIFO, we need to support differents data width in a single transfer. For example, serial device which usually uses 1-byte data width will use 4-bytes data width when using the FIFO. If the transfer size is not aligned on 4-bytes then the end of the transfer will be performed with 1-byte data-width. For that reason, at_xdmac_prep_slave_sg() now builds linked list descriptors using view 2 instead of view 1 so each of them can update the DWIDTH field into the Channel Configuration Register. Signed-off-by: Cyrille Pitchen Signed-off-by: Ludovic Desroches Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index eba66a22a265df..09e2825a547a20 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -351,11 +352,11 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan, at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, reg); /* - * When doing memory to memory transfer we need to use the next + * When doing non cyclic transfer we need to use the next * descriptor view 2 since some fields of the configuration register * depend on transfer size and src/dest addresses. */ - if (is_slave_direction(first->direction)) { + if (at_xdmac_chan_is_cyclic(atchan)) { reg = AT_XDMAC_CNDC_NDVIEW_NDV1; at_xdmac_chan_write(atchan, AT_XDMAC_CC, first->lld.mbr_cfg); } else { @@ -582,7 +583,7 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, /* Prepare descriptors. */ for_each_sg(sgl, sg, sg_len, i) { struct at_xdmac_desc *desc = NULL; - u32 len, mem; + u32 len, mem, dwidth, fixed_dwidth; len = sg_dma_len(sg); mem = sg_dma_address(sg); @@ -613,11 +614,15 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->lld.mbr_da = atchan->per_dst_addr; desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG]; } - desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1 /* next descriptor view */ + dwidth = at_xdmac_get_dwidth(desc->lld.mbr_cfg); + fixed_dwidth = IS_ALIGNED(len, 1 << dwidth) + ? at_xdmac_get_dwidth(desc->lld.mbr_cfg) + : AT_XDMAC_CC_DWIDTH_BYTE; + desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV2 /* next descriptor view */ | AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ | AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ | (i == sg_len - 1 ? 0 : AT_XDMAC_MBR_UBC_NDE) /* descriptor fetch */ - | len / (1 << at_xdmac_get_dwidth(desc->lld.mbr_cfg)); /* microblock length */ + | (len >> fixed_dwidth); /* microblock length */ dev_dbg(chan2dev(chan), "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc); From 0760e818646d801a13eed57f5fb75c43c7c2794a Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Wed, 4 Feb 2015 12:24:06 -0500 Subject: [PATCH 256/360] mtd: nand: omap: drop condition with no effect The if and the else branch code are identical - so the condition has no effect on the effective code. This patch removes the condition and the duplicated code and updates the documentation as suggested by Roger Quadros . Acked-by: Roger Quadros Signed-off-by: Nicholas Mc Guire Reviewed-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 63f858e6bf3987..60fa89939c2486 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1048,10 +1048,9 @@ static int omap_dev_ready(struct mtd_info *mtd) * @mtd: MTD device structure * @mode: Read/Write mode * - * When using BCH, sector size is hardcoded to 512 bytes. - * Using wrapping mode 6 both for reading and writing if ELM module not uses - * for error correction. - * On writing, + * When using BCH with SW correction (i.e. no ELM), sector size is set + * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode + * for both reading and writing with: * eccsize0 = 0 (no additional protected byte in spare area) * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) */ @@ -1071,15 +1070,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: bch_type = 0; nsectors = 1; - if (mode == NAND_ECC_READ) { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } else { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; break; case OMAP_ECC_BCH4_CODE_HW: bch_type = 0; @@ -1097,15 +1090,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: bch_type = 1; nsectors = 1; - if (mode == NAND_ECC_READ) { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } else { - wr_mode = BCH_WRAPMODE_6; - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - } + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; break; case OMAP_ECC_BCH8_CODE_HW: bch_type = 1; From e4ca6840837be86b89e77c8402c1e6ab8a36f274 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 22 Jan 2015 22:43:05 -0200 Subject: [PATCH 257/360] mtd: fsl-quadspi: Fix the error paths Jumping to 'map_failed' label is not correct at these points, as it misses to disable the clocks that were previously enabled. Jump to 'irq_failed' label instead that will correctly disable the clocks. Signed-off-by: Fabio Estevam Acked-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index a46bea33580f49..a733bd2eb39304 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -890,24 +890,24 @@ static int fsl_qspi_probe(struct platform_device *pdev) ret = of_modalias_node(np, modalias, sizeof(modalias)); if (ret < 0) - goto map_failed; + goto irq_failed; ret = of_property_read_u32(np, "spi-max-frequency", &q->clk_rate); if (ret < 0) - goto map_failed; + goto irq_failed; /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); if (ret) - goto map_failed; + goto irq_failed; ppdata.of_node = np; ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (ret) - goto map_failed; + goto irq_failed; /* Set the correct NOR size now. */ if (q->nor_size == 0) { From 50131b73de14894d556baa5510f4e49db0454d93 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 22 Jan 2015 22:43:06 -0200 Subject: [PATCH 258/360] mtd: fsl-quadspi: Remove unneeded success/error messages When the driver successfully probe we already have messages like: [ 1.140989] fsl-quadspi 21e4000.qspi: s25fl128s (16384 Kbytes) [ 1.150902] fsl-quadspi 21e4000.qspi: s25fl128s (16384 Kbytes) Or in case of error: [ 1.175920] fsl-quadspi: probe of 21e4000.qspi failed with error -12 , so remove the unneeded success/error messages. Signed-off-by: Fabio Estevam Acked-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index a733bd2eb39304..1c0572a0a57618 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -939,7 +939,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) clk_disable(q->clk); clk_disable(q->clk_en); - dev_info(dev, "QuadSPI SPI NOR flash driver\n"); return 0; last_init_failed: @@ -954,7 +953,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) clk_failed: clk_disable_unprepare(q->clk_en); map_failed: - dev_err(dev, "Freescale QuadSPI probe failed\n"); return ret; } From b1ab474fdaab6da2b9a0695b2c72ee5896dd6796 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 22 Jan 2015 22:43:07 -0200 Subject: [PATCH 259/360] mtd: fsl-quadspi: Remove unnecessary 'map_failed' label There is no need to keep the 'map_failed' label. We can simply return the error code directly and let the code shorter and cleaner. Signed-off-by: Fabio Estevam Acked-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 1c0572a0a57618..b1cc182c46a185 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -798,37 +798,30 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* find the resources */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); q->iobase = devm_ioremap_resource(dev, res); - if (IS_ERR(q->iobase)) { - ret = PTR_ERR(q->iobase); - goto map_failed; - } + if (IS_ERR(q->iobase)) + return PTR_ERR(q->iobase); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); q->ahb_base = devm_ioremap_resource(dev, res); - if (IS_ERR(q->ahb_base)) { - ret = PTR_ERR(q->ahb_base); - goto map_failed; - } + if (IS_ERR(q->ahb_base)) + return PTR_ERR(q->ahb_base); + q->memmap_phy = res->start; /* find the clocks */ q->clk_en = devm_clk_get(dev, "qspi_en"); - if (IS_ERR(q->clk_en)) { - ret = PTR_ERR(q->clk_en); - goto map_failed; - } + if (IS_ERR(q->clk_en)) + return PTR_ERR(q->clk_en); q->clk = devm_clk_get(dev, "qspi"); - if (IS_ERR(q->clk)) { - ret = PTR_ERR(q->clk); - goto map_failed; - } + if (IS_ERR(q->clk)) + return PTR_ERR(q->clk); ret = clk_prepare_enable(q->clk_en); if (ret) { dev_err(dev, "can not enable the qspi_en clock\n"); - goto map_failed; + return ret; } ret = clk_prepare_enable(q->clk); @@ -952,7 +945,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) clk_disable_unprepare(q->clk); clk_failed: clk_disable_unprepare(q->clk_en); -map_failed: return ret; } From 4e898ce7d9221db090e6c4e7f289760930dbda6e Mon Sep 17 00:00:00 2001 From: Allen Xu Date: Wed, 14 Jan 2015 00:28:56 +0800 Subject: [PATCH 260/360] mtd: fsl-quadspi: improve read performance by increase AHB transfer size Set AHB transfer size to 1K which improved the read performance. Add ahb_buf_size field in fsl_qspi_devtype_data to denote the size for different SoC. Before: root@imx6qdlsolo:~# dd if=/dev/mtd1 of=/dev/null bs=1M count=16 16+0 records in 16+0 records out 16777216 bytes (17 MB) copied, 0.472183 s, 25.1 MB/s After: root@imx6qdlsolo:~# dd if=/dev/mtd1 of=/dev/null bs=1M count=16 16+0 records in 16+0 records out 16777216 bytes (17 MB) copied, 0.369439 s, 29.5 MB/s Signed-off-by: Allen Xu Signed-off-by: Huang Shijie Signed-off-by: Frank Li Acked-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index b1cc182c46a185..1c7308c2c77d9b 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -57,7 +57,9 @@ #define QUADSPI_BUF3CR 0x1c #define QUADSPI_BUF3CR_ALLMST_SHIFT 31 -#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) +#define QUADSPI_BUF3CR_ALLMST_MASK (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) +#define QUADSPI_BUF3CR_ADATSZ_SHIFT 8 +#define QUADSPI_BUF3CR_ADATSZ_MASK (0xFF << QUADSPI_BUF3CR_ADATSZ_SHIFT) #define QUADSPI_BFGENCR 0x20 #define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 @@ -198,18 +200,21 @@ struct fsl_qspi_devtype_data { enum fsl_qspi_devtype devtype; int rxfifo; int txfifo; + int ahb_buf_size; }; static struct fsl_qspi_devtype_data vybrid_data = { .devtype = FSL_QUADSPI_VYBRID, .rxfifo = 128, - .txfifo = 64 + .txfifo = 64, + .ahb_buf_size = 1024 }; static struct fsl_qspi_devtype_data imx6sx_data = { .devtype = FSL_QUADSPI_IMX6SX, .rxfifo = 128, - .txfifo = 512 + .txfifo = 512, + .ahb_buf_size = 1024 }; #define FSL_QSPI_MAX_CHIP 4 @@ -584,7 +589,12 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); - writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); + /* + * Set ADATSZ with the maximum AHB buffer size to improve the + * read performance. + */ + writel(QUADSPI_BUF3CR_ALLMST_MASK | ((q->devtype_data->ahb_buf_size / 8) + << QUADSPI_BUF3CR_ADATSZ_SHIFT), base + QUADSPI_BUF3CR); /* We only use the buffer3 */ writel(0, base + QUADSPI_BUF0IND); From 4d8e2cef25c3c8c8fcb9a4b7b5cf5bba771a43a7 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Fri, 16 Jan 2015 03:29:17 +0800 Subject: [PATCH 261/360] MAINTAINERS: add maintainer entry for FREESCALE QUAD SPI driver Add a maintainer entry for FREESCALE QUAD SPI driver and add myself as a maintainer. Signed-off-by: Han Xu Acked-by: Huang Shijie Signed-off-by: Brian Norris --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8d32b3ed..cabe127fba76c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4028,6 +4028,12 @@ S: Maintained F: include/linux/platform_data/video-imxfb.h F: drivers/video/fbdev/imxfb.c +FREESCALE QUAD SPI DRIVER +M: Han Xu +L: linux-mtd@lists.infradead.org +S: Maintained +F: drivers/mtd/spi-nor/fsl-quadspi.c + FREESCALE SOC FS_ENET DRIVER M: Pantelis Antoniou M: Vitaly Bordug From 2ea69d217a376b4d549fc155675f948eaf7afa86 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 22 Jan 2015 15:23:05 +0200 Subject: [PATCH 262/360] mtd: nand: remove redundant local variable Signed-off-by: Baruch Siach Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index f6af96926f0059..df7eb4ff07d156 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1750,11 +1750,10 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size; - uint8_t *bufpoi = buf; + uint8_t *bufpoi = chip->oob_poi; int i, toread, sndrnd = 0, pos; chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); From d79ee72b8ac37f30a2218fec8c2ba9618c306302 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 13 Jan 2015 13:29:17 +0900 Subject: [PATCH 263/360] mtd: denali: remove unnecessary stubs This driver uses NAND_ECC_HW_SYNDROME mode. The nand_scan_tail() function would not complain about missing ecc->calculate, ecc->correct, ecc->hwctl handlers. Signed-off-by: Masahiro Yamada Signed-off-by: Brian Norris --- drivers/mtd/nand/denali.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 5e397fba419baf..f44c6061536a83 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1328,35 +1328,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, break; } } - -/* stubs for ECC functions not used by the NAND core */ -static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, - uint8_t *ecc_code) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - dev_err(denali->dev, "denali_ecc_calculate called unexpectedly\n"); - BUG(); - return -EIO; -} - -static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - dev_err(denali->dev, "denali_ecc_correct called unexpectedly\n"); - BUG(); - return -EIO; -} - -static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - dev_err(denali->dev, "denali_ecc_hwctl called unexpectedly\n"); - BUG(); -} /* end NAND core entry points */ /* Initialization code to bring the device up to a known good state */ @@ -1609,15 +1580,6 @@ int denali_init(struct denali_nand_info *denali) denali->totalblks = denali->mtd.size >> denali->nand.phys_erase_shift; denali->blksperchip = denali->totalblks / denali->nand.numchips; - /* - * These functions are required by the NAND core framework, otherwise, - * the NAND core will assert. However, we don't need them, so we'll stub - * them out. - */ - denali->nand.ecc.calculate = denali_ecc_calculate; - denali->nand.ecc.correct = denali_ecc_correct; - denali->nand.ecc.hwctl = denali_ecc_hwctl; - /* override the default read operations */ denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum; denali->nand.ecc.read_page = denali_read_page; From be802bf955a1aa9b92b71395405899ce076eca6e Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sat, 31 Jan 2015 21:47:42 +0800 Subject: [PATCH 264/360] mtd: kconfig: replace PPC_OF with PPC The PPC_OF is a ppc specific option which is used to mean that the firmware device tree access functions are available. Since all the ppc platforms have a device tree, it is aways set to 'y' for ppc. So it makes no sense to keep a such option in the current kernel. Replace it with PPC. Signed-off-by: Kevin Hao Signed-off-by: Brian Norris --- drivers/mtd/nand/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7d0150d2043201..ceade04480250c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -421,7 +421,7 @@ config MTD_NAND_ORION config MTD_NAND_FSL_ELBC tristate "NAND support for Freescale eLBC controllers" - depends on PPC_OF + depends on PPC select FSL_LBC help Various Freescale chips, including the 8313, include a NAND Flash From d164ea32674ec82e80f480769ffcd2144f901380 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 20 Jan 2015 10:35:32 +0100 Subject: [PATCH 265/360] mtd: concat: set the return lengths properly In concat_read_oob both retlen and oobretlen should be updated. concat_write_oob previously only (improperly) updated retlen. Signed-off-by: Niklas Cassel Signed-off-by: Brian Norris --- drivers/mtd/mtdconcat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index b9000563b9f431..d85c9468d30c93 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -311,7 +311,8 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) devops.len = subdev->size - to; err = mtd_write_oob(subdev, to, &devops); - ops->retlen += devops.oobretlen; + ops->retlen += devops.retlen; + ops->oobretlen += devops.oobretlen; if (err) return err; From e1dd8641c8c36ff4aacf24c7e2575770e30afbe5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Sun, 1 Feb 2015 02:08:50 +0100 Subject: [PATCH 266/360] mtd: avoid registering reboot notifier twice Calling mtd_device_parse_register with the same mtd_info (e.g. registering several partitions on a single device) would add the same reboot notifier twice, causing an infinte loop in notifier_chain_register during boot up. Signed-off-by: Niklas Cassel [Brian: add FIXME comments] Signed-off-by: Brian Norris Signed-off-by: Brian Norris --- drivers/mtd/mtdcore.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index cbc0fc46d2d6eb..52eea932c312b7 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -577,7 +577,15 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, err = -ENODEV; } - if (mtd->_reboot) { + /* + * FIXME: some drivers unfortunately call this function more than once. + * So we have to check if we've already assigned the reboot notifier. + * + * Generally, we can make multiple calls work for most cases, but it + * does cause problems with parse_mtd_partitions() above (e.g., + * cmdlineparts will register partitions more than once). + */ + if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) { mtd->reboot_notifier.notifier_call = mtd_reboot_notifier; register_reboot_notifier(&mtd->reboot_notifier); } From 54f531f6e332875bd8a604871532f7f1174adc0e Mon Sep 17 00:00:00 2001 From: Zhou Wang Date: Sun, 25 Jan 2015 18:53:13 +0800 Subject: [PATCH 267/360] mtd: hisilicon: add a new NAND controller driver for hisilicon hip04 Soc This patch adds the support for hisilicon 504 NAND controller which is now used by Hisilicon Soc Hip04. Signed-off-by: Zhou Wang Signed-off-by: Brian Norris --- drivers/mtd/nand/Kconfig | 5 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/hisi504_nand.c | 891 ++++++++++++++++++++++++++++++++ 3 files changed, 897 insertions(+) create mode 100644 drivers/mtd/nand/hisi504_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ceade04480250c..5b76a173cd95d6 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -524,4 +524,9 @@ config MTD_NAND_SUNXI help Enables support for NAND Flash chips on Allwinner SoCs. +config MTD_NAND_HISI504 + tristate "Support for NAND controller on Hisilicon SoC Hip04" + help + Enables support for NAND controller on Hisilicon SoC Hip04. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bd38f21d2e28e8..582bbd05aff7ab 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -51,5 +51,6 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o +obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c new file mode 100644 index 00000000000000..484e1db4d601d6 --- /dev/null +++ b/drivers/mtd/nand/hisi504_nand.c @@ -0,0 +1,891 @@ +/* + * Hisilicon NAND Flash controller driver + * + * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * + * Author: Zhou Wang + * The initial developer of the original code is Zhiyong Cai + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HINFC504_MAX_CHIP (4) +#define HINFC504_W_LATCH (5) +#define HINFC504_R_LATCH (7) +#define HINFC504_RW_LATCH (3) + +#define HINFC504_NFC_TIMEOUT (2 * HZ) +#define HINFC504_NFC_PM_TIMEOUT (1 * HZ) +#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ) +#define HINFC504_CHIP_DELAY (25) + +#define HINFC504_REG_BASE_ADDRESS_LEN (0x100) +#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128) + +#define HINFC504_ADDR_CYCLE_MASK 0x4 + +#define HINFC504_CON 0x00 +#define HINFC504_CON_OP_MODE_NORMAL BIT(0) +#define HINFC504_CON_PAGEISZE_SHIFT (1) +#define HINFC504_CON_PAGESIZE_MASK (0x07) +#define HINFC504_CON_BUS_WIDTH BIT(4) +#define HINFC504_CON_READY_BUSY_SEL BIT(8) +#define HINFC504_CON_ECCTYPE_SHIFT (9) +#define HINFC504_CON_ECCTYPE_MASK (0x07) + +#define HINFC504_PWIDTH 0x04 +#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ + ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) + +#define HINFC504_CMD 0x0C +#define HINFC504_ADDRL 0x10 +#define HINFC504_ADDRH 0x14 +#define HINFC504_DATA_NUM 0x18 + +#define HINFC504_OP 0x1C +#define HINFC504_OP_READ_DATA_EN BIT(1) +#define HINFC504_OP_WAIT_READY_EN BIT(2) +#define HINFC504_OP_CMD2_EN BIT(3) +#define HINFC504_OP_WRITE_DATA_EN BIT(4) +#define HINFC504_OP_ADDR_EN BIT(5) +#define HINFC504_OP_CMD1_EN BIT(6) +#define HINFC504_OP_NF_CS_SHIFT (7) +#define HINFC504_OP_NF_CS_MASK (3) +#define HINFC504_OP_ADDR_CYCLE_SHIFT (9) +#define HINFC504_OP_ADDR_CYCLE_MASK (7) + +#define HINFC504_STATUS 0x20 +#define HINFC504_READY BIT(0) + +#define HINFC504_INTEN 0x24 +#define HINFC504_INTEN_DMA BIT(9) +#define HINFC504_INTEN_UE BIT(6) +#define HINFC504_INTEN_CE BIT(5) + +#define HINFC504_INTS 0x28 +#define HINFC504_INTS_DMA BIT(9) +#define HINFC504_INTS_UE BIT(6) +#define HINFC504_INTS_CE BIT(5) + +#define HINFC504_INTCLR 0x2C +#define HINFC504_INTCLR_DMA BIT(9) +#define HINFC504_INTCLR_UE BIT(6) +#define HINFC504_INTCLR_CE BIT(5) + +#define HINFC504_ECC_STATUS 0x5C +#define HINFC504_ECC_16_BIT_SHIFT 12 + +#define HINFC504_DMA_CTRL 0x60 +#define HINFC504_DMA_CTRL_DMA_START BIT(0) +#define HINFC504_DMA_CTRL_WE BIT(1) +#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2) +#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3) +#define HINFC504_DMA_CTRL_BURST4_EN BIT(4) +#define HINFC504_DMA_CTRL_BURST8_EN BIT(5) +#define HINFC504_DMA_CTRL_BURST16_EN BIT(6) +#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7) +#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1) +#define HINFC504_DMA_CTRL_CS_SHIFT (8) +#define HINFC504_DMA_CTRL_CS_MASK (0x03) + +#define HINFC504_DMA_ADDR_DATA 0x64 +#define HINFC504_DMA_ADDR_OOB 0x68 + +#define HINFC504_DMA_LEN 0x6C +#define HINFC504_DMA_LEN_OOB_SHIFT (16) +#define HINFC504_DMA_LEN_OOB_MASK (0xFFF) + +#define HINFC504_DMA_PARA 0x70 +#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0) +#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1) +#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2) +#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3) +#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4) +#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5) + +#define HINFC_VERSION 0x74 +#define HINFC504_LOG_READ_ADDR 0x7C +#define HINFC504_LOG_READ_LEN 0x80 + +#define HINFC504_NANDINFO_LEN 0x10 + +struct hinfc_host { + struct nand_chip chip; + struct mtd_info mtd; + struct device *dev; + void __iomem *iobase; + void __iomem *mmio; + struct completion cmd_complete; + unsigned int offset; + unsigned int command; + int chipselect; + unsigned int addr_cycle; + u32 addr_value[2]; + u32 cache_addr_value[2]; + char *buffer; + dma_addr_t dma_buffer; + dma_addr_t dma_oob; + int version; + unsigned int irq_status; /* interrupt status */ +}; + +static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg) +{ + return readl(host->iobase + reg); +} + +static inline void hinfc_write(struct hinfc_host *host, unsigned int value, + unsigned int reg) +{ + writel(value, host->iobase + reg); +} + +static void wait_controller_finished(struct hinfc_host *host) +{ + unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT; + int val; + + while (time_before(jiffies, timeout)) { + val = hinfc_read(host, HINFC504_STATUS); + if (host->command == NAND_CMD_ERASE2) { + /* nfc is ready */ + while (!(val & HINFC504_READY)) { + usleep_range(500, 1000); + val = hinfc_read(host, HINFC504_STATUS); + } + return; + } + + if (val & HINFC504_READY) + return; + } + + /* wait cmd timeout */ + dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n"); +} + +static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = mtd->priv; + unsigned long val; + int ret; + + hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); + hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); + + if (chip->ecc.mode == NAND_ECC_NONE) { + hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) + << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); + + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA); + } else { + if (host->command == NAND_CMD_READOOB) + hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN + | HINFC504_DMA_PARA_OOB_EDC_EN + | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); + else + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN + | HINFC504_DMA_PARA_DATA_EDC_EN + | HINFC504_DMA_PARA_OOB_EDC_EN + | HINFC504_DMA_PARA_DATA_ECC_EN + | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA); + + } + + val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN + | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN + | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN + | ((host->addr_cycle == 4 ? 1 : 0) + << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT) + | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK) + << HINFC504_DMA_CTRL_CS_SHIFT)); + + if (todev) + val |= HINFC504_DMA_CTRL_WE; + + init_completion(&host->cmd_complete); + + hinfc_write(host, val, HINFC504_DMA_CTRL); + ret = wait_for_completion_timeout(&host->cmd_complete, + HINFC504_NFC_DMA_TIMEOUT); + + if (!ret) { + dev_err(host->dev, "DMA operation(irq) timeout!\n"); + /* sanity check */ + val = hinfc_read(host, HINFC504_DMA_CTRL); + if (!(val & HINFC504_DMA_CTRL_DMA_START)) + dev_err(host->dev, "DMA is already done but without irq ACK!\n"); + else + dev_err(host->dev, "DMA is really timeout!\n"); + } +} + +static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host) +{ + host->addr_value[0] &= 0xffff0000; + + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); + hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN, + HINFC504_CMD); + + hisi_nfc_dma_transfer(host, 1); + + return 0; +} + +static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host) +{ + struct mtd_info *mtd = &host->mtd; + + if ((host->addr_value[0] == host->cache_addr_value[0]) && + (host->addr_value[1] == host->cache_addr_value[1])) + return 0; + + host->addr_value[0] &= 0xffff0000; + + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, host->addr_value[1], HINFC504_ADDRH); + hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0, + HINFC504_CMD); + + hinfc_write(host, 0, HINFC504_LOG_READ_ADDR); + hinfc_write(host, mtd->writesize + mtd->oobsize, + HINFC504_LOG_READ_LEN); + + hisi_nfc_dma_transfer(host, 0); + + host->cache_addr_value[0] = host->addr_value[0]; + host->cache_addr_value[1] = host->addr_value[1]; + + return 0; +} + +static int hisi_nfc_send_cmd_erase(struct hinfc_host *host) +{ + hinfc_write(host, host->addr_value[0], HINFC504_ADDRL); + hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1, + HINFC504_CMD); + + hinfc_write(host, HINFC504_OP_WAIT_READY_EN + | HINFC504_OP_CMD2_EN + | HINFC504_OP_CMD1_EN + | HINFC504_OP_ADDR_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK) + << HINFC504_OP_ADDR_CYCLE_SHIFT), + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_readid(struct hinfc_host *host) +{ + hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); + hinfc_write(host, NAND_CMD_READID, HINFC504_CMD); + hinfc_write(host, 0, HINFC504_ADDRL); + + hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN + | HINFC504_OP_READ_DATA_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_status(struct hinfc_host *host) +{ + hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM); + hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD); + hinfc_write(host, HINFC504_OP_CMD1_EN + | HINFC504_OP_READ_DATA_EN + | ((host->chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT), + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect) +{ + hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD); + + hinfc_write(host, HINFC504_OP_CMD1_EN + | ((chipselect & HINFC504_OP_NF_CS_MASK) + << HINFC504_OP_NF_CS_SHIFT) + | HINFC504_OP_WAIT_READY_EN, + HINFC504_OP); + + wait_controller_finished(host); + + return 0; +} + +static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + if (chipselect < 0) + return; + + host->chipselect = chipselect; +} + +static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + if (host->command == NAND_CMD_STATUS) + return *(uint8_t *)(host->mmio); + + host->offset++; + + if (host->command == NAND_CMD_READID) + return *(uint8_t *)(host->mmio + host->offset - 1); + + return *(uint8_t *)(host->buffer + host->offset - 1); +} + +static u16 hisi_nfc_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + host->offset += 2; + return *(u16 *)(host->buffer + host->offset - 2); +} + +static void +hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + memcpy(host->buffer + host->offset, buf, len); + host->offset += len; +} + +static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + + memcpy(buf, host->buffer + host->offset, len); + host->offset += len; +} + +static void set_addr(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + unsigned int command = host->command; + + host->addr_cycle = 0; + host->addr_value[0] = 0; + host->addr_value[1] = 0; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + + host->addr_value[0] = column & 0xffff; + host->addr_cycle = 2; + } + if (page_addr != -1) { + host->addr_value[0] |= (page_addr & 0xffff) + << (host->addr_cycle * 8); + host->addr_cycle += 2; + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) { + host->addr_cycle += 1; + if (host->command == NAND_CMD_ERASE1) + host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16; + else + host->addr_value[1] |= ((page_addr >> 16) & 0xff); + } + } +} + +static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct hinfc_host *host = chip->priv; + int is_cache_invalid = 1; + unsigned int flag = 0; + + host->command = command; + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + if (command == NAND_CMD_READ0) + host->offset = column; + else + host->offset = column + mtd->writesize; + + is_cache_invalid = 0; + set_addr(mtd, column, page_addr); + hisi_nfc_send_cmd_readstart(host); + break; + + case NAND_CMD_SEQIN: + host->offset = column; + set_addr(mtd, column, page_addr); + break; + + case NAND_CMD_ERASE1: + set_addr(mtd, column, page_addr); + break; + + case NAND_CMD_PAGEPROG: + hisi_nfc_send_cmd_pageprog(host); + break; + + case NAND_CMD_ERASE2: + hisi_nfc_send_cmd_erase(host); + break; + + case NAND_CMD_READID: + host->offset = column; + memset(host->mmio, 0, 0x10); + hisi_nfc_send_cmd_readid(host); + break; + + case NAND_CMD_STATUS: + flag = hinfc_read(host, HINFC504_CON); + if (chip->ecc.mode == NAND_ECC_HW) + hinfc_write(host, + flag && ~(HINFC504_CON_ECCTYPE_MASK << + HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); + + host->offset = 0; + memset(host->mmio, 0, 0x10); + hisi_nfc_send_cmd_status(host); + hinfc_write(host, flag, HINFC504_CON); + break; + + case NAND_CMD_RESET: + hisi_nfc_send_cmd_reset(host, host->chipselect); + break; + + default: + dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n", + command, column, page_addr); + } + + if (is_cache_invalid) { + host->cache_addr_value[0] = ~0; + host->cache_addr_value[1] = ~0; + } +} + +static irqreturn_t hinfc_irq_handle(int irq, void *devid) +{ + struct hinfc_host *host = devid; + unsigned int flag; + + flag = hinfc_read(host, HINFC504_INTS); + /* store interrupts state */ + host->irq_status |= flag; + + if (flag & HINFC504_INTS_DMA) { + hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR); + complete(&host->cmd_complete); + } else if (flag & HINFC504_INTS_CE) { + hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR); + } else if (flag & HINFC504_INTS_UE) { + hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR); + } + + return IRQ_HANDLED; +} + +static int hisi_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct hinfc_host *host = chip->priv; + int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc; + int stat_1, stat_2; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* errors which can not be corrected by ECC */ + if (host->irq_status & HINFC504_INTS_UE) { + mtd->ecc_stats.failed++; + } else if (host->irq_status & HINFC504_INTS_CE) { + /* TODO: need add other ECC modes! */ + switch (chip->ecc.strength) { + case 16: + status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >> + HINFC504_ECC_16_BIT_SHIFT & 0x0fff; + stat_2 = status_ecc & 0x3f; + stat_1 = status_ecc >> 6 & 0x3f; + stat = stat_1 + stat_2; + stat_max = max_t(int, stat_1, stat_2); + } + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(int, max_bitflips, stat_max); + } + host->irq_status = 0; + + return max_bitflips; +} + +static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct hinfc_host *host = chip->priv; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (host->irq_status & HINFC504_INTS_UE) { + host->irq_status = 0; + return -EBADMSG; + } + + host->irq_status = 0; + return 0; +} + +static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + chip->write_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void hisi_nfc_host_init(struct hinfc_host *host) +{ + struct nand_chip *chip = &host->chip; + unsigned int flag = 0; + + host->version = hinfc_read(host, HINFC_VERSION); + host->addr_cycle = 0; + host->addr_value[0] = 0; + host->addr_value[1] = 0; + host->cache_addr_value[0] = ~0; + host->cache_addr_value[1] = ~0; + host->chipselect = 0; + + /* default page size: 2K, ecc_none. need modify */ + flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL + | ((0x001 & HINFC504_CON_PAGESIZE_MASK) + << HINFC504_CON_PAGEISZE_SHIFT) + | ((0x0 & HINFC504_CON_ECCTYPE_MASK) + << HINFC504_CON_ECCTYPE_SHIFT) + | ((chip->options & NAND_BUSWIDTH_16) ? + HINFC504_CON_BUS_WIDTH : 0); + hinfc_write(host, flag, HINFC504_CON); + + memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN); + + hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, + HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); + + /* enable DMA irq */ + hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN); +} + +static struct nand_ecclayout nand_ecc_2K_16bits = { + .oobavail = 6, + .oobfree = { {2, 6} }, +}; + +static int hisi_nfc_ecc_probe(struct hinfc_host *host) +{ + unsigned int flag; + int size, strength, ecc_bits; + struct device *dev = host->dev; + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = &host->mtd; + struct device_node *np = host->dev->of_node; + + size = of_get_nand_ecc_step_size(np); + strength = of_get_nand_ecc_strength(np); + if (size != 1024) { + dev_err(dev, "error ecc size: %d\n", size); + return -EINVAL; + } + + if ((size == 1024) && ((strength != 8) && (strength != 16) && + (strength != 24) && (strength != 40))) { + dev_err(dev, "ecc size and strength do not match\n"); + return -EINVAL; + } + + chip->ecc.size = size; + chip->ecc.strength = strength; + + chip->ecc.read_page = hisi_nand_read_page_hwecc; + chip->ecc.read_oob = hisi_nand_read_oob; + chip->ecc.write_page = hisi_nand_write_page_hwecc; + + switch (chip->ecc.strength) { + case 16: + ecc_bits = 6; + if (mtd->writesize == 2048) + chip->ecc.layout = &nand_ecc_2K_16bits; + + /* TODO: add more page size support */ + break; + + /* TODO: add more ecc strength support */ + default: + dev_err(dev, "not support strength: %d\n", chip->ecc.strength); + return -EINVAL; + } + + flag = hinfc_read(host, HINFC504_CON); + /* add ecc type configure */ + flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK) + << HINFC504_CON_ECCTYPE_SHIFT); + hinfc_write(host, flag, HINFC504_CON); + + /* enable ecc irq */ + flag = hinfc_read(host, HINFC504_INTEN) & 0xfff; + hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE, + HINFC504_INTEN); + + return 0; +} + +static int hisi_nfc_probe(struct platform_device *pdev) +{ + int ret = 0, irq, buswidth, flag, max_chips = HINFC504_MAX_CHIP; + struct device *dev = &pdev->dev; + struct hinfc_host *host; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + struct device_node *np = dev->of_node; + struct mtd_part_parser_data ppdata; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->dev = dev; + + platform_set_drvdata(pdev, host); + chip = &host->chip; + mtd = &host->mtd; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no IRQ resource defined\n"); + ret = -ENXIO; + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(host->iobase)) { + ret = PTR_ERR(host->iobase); + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + host->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(host->mmio)) { + ret = PTR_ERR(host->mmio); + dev_err(dev, "devm_ioremap_resource[1] fail\n"); + goto err_res; + } + + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->name = "hisi_nand"; + mtd->dev.parent = &pdev->dev; + + chip->priv = host; + chip->cmdfunc = hisi_nfc_cmdfunc; + chip->select_chip = hisi_nfc_select_chip; + chip->read_byte = hisi_nfc_read_byte; + chip->read_word = hisi_nfc_read_word; + chip->write_buf = hisi_nfc_write_buf; + chip->read_buf = hisi_nfc_read_buf; + chip->chip_delay = HINFC504_CHIP_DELAY; + + chip->ecc.mode = of_get_nand_ecc_mode(np); + + buswidth = of_get_nand_bus_width(np); + if (buswidth == 16) + chip->options |= NAND_BUSWIDTH_16; + + hisi_nfc_host_init(host); + + ret = devm_request_irq(dev, irq, hinfc_irq_handle, IRQF_DISABLED, + "nandc", host); + if (ret) { + dev_err(dev, "failed to request IRQ\n"); + goto err_res; + } + + ret = nand_scan_ident(mtd, max_chips, NULL); + if (ret) { + ret = -ENODEV; + goto err_res; + } + + host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, + &host->dma_buffer, GFP_KERNEL); + if (!host->buffer) { + ret = -ENOMEM; + goto err_res; + } + + host->dma_oob = host->dma_buffer + mtd->writesize; + memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); + + flag = hinfc_read(host, HINFC504_CON); + flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); + switch (mtd->writesize) { + case 2048: + flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break; + /* + * TODO: add more pagesize support, + * default pagesize has been set in hisi_nfc_host_init + */ + default: + dev_err(dev, "NON-2KB page size nand flash\n"); + ret = -EINVAL; + goto err_res; + } + hinfc_write(host, flag, HINFC504_CON); + + if (chip->ecc.mode == NAND_ECC_HW) + hisi_nfc_ecc_probe(host); + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + goto err_res; + } + + ppdata.of_node = np; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) { + dev_err(dev, "Err MTD partition=%d\n", ret); + goto err_mtd; + } + + return 0; + +err_mtd: + nand_release(mtd); +err_res: + return ret; +} + +static int hisi_nfc_remove(struct platform_device *pdev) +{ + struct hinfc_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int hisi_nfc_suspend(struct device *dev) +{ + struct hinfc_host *host = dev_get_drvdata(dev); + unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT; + + while (time_before(jiffies, timeout)) { + if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) && + (hinfc_read(host, HINFC504_DMA_CTRL) & + HINFC504_DMA_CTRL_DMA_START)) { + cond_resched(); + return 0; + } + } + + dev_err(host->dev, "nand controller suspend timeout.\n"); + + return -EAGAIN; +} + +static int hisi_nfc_resume(struct device *dev) +{ + int cs; + struct hinfc_host *host = dev_get_drvdata(dev); + struct nand_chip *chip = &host->chip; + + for (cs = 0; cs < chip->numchips; cs++) + hisi_nfc_send_cmd_reset(host, cs); + hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH, + HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume); + +static const struct of_device_id nfc_id_table[] = { + { .compatible = "hisilicon,504-nfc" }, + {} +}; +MODULE_DEVICE_TABLE(of, nfc_id_table); + +static struct platform_driver hisi_nfc_driver = { + .driver = { + .name = "hisi_nand", + .of_match_table = nfc_id_table, + .pm = &hisi_nfc_pm_ops, + }, + .probe = hisi_nfc_probe, + .remove = hisi_nfc_remove, +}; + +module_platform_driver(hisi_nfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhou Wang"); +MODULE_AUTHOR("Zhiyong Cai"); +MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver"); From 72f55d74bd12e66151d6db79d179cae678bcd290 Mon Sep 17 00:00:00 2001 From: Zhou Wang Date: Sun, 25 Jan 2015 18:53:14 +0800 Subject: [PATCH 268/360] mtd: hisilicon: add device tree binding documentation This patch adds the related dts binding document for Hisilicon 504 NAND controller. Signed-off-by: Zhou Wang Signed-off-by: Brian Norris --- .../devicetree/bindings/mtd/hisi504-nand.txt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/hisi504-nand.txt diff --git a/Documentation/devicetree/bindings/mtd/hisi504-nand.txt b/Documentation/devicetree/bindings/mtd/hisi504-nand.txt new file mode 100644 index 00000000000000..2e35f0662912aa --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/hisi504-nand.txt @@ -0,0 +1,47 @@ +Hisilicon Hip04 Soc NAND controller DT binding + +Required properties: + +- compatible: Should be "hisilicon,504-nfc". +- reg: The first contains base physical address and size of + NAND controller's registers. The second contains base + physical address and size of NAND controller's buffer. +- interrupts: Interrupt number for nfc. +- nand-bus-width: See nand.txt. +- nand-ecc-mode: Support none and hw ecc mode. +- #address-cells: Partition address, should be set 1. +- #size-cells: Partition size, should be set 1. + +Optional properties: + +- nand-ecc-strength: Number of bits to correct per ECC step. +- nand-ecc-step-size: Number of data bytes covered by a single ECC step. + +The following ECC strength and step size are currently supported: + + - nand-ecc-strength = <16>, nand-ecc-step-size = <1024> + +Flash chip may optionally contain additional sub-nodes describing partitions of +the address space. See partition.txt for more detail. + +Example: + + nand: nand@4020000 { + compatible = "hisilicon,504-nfc"; + reg = <0x4020000 0x10000>, <0x5000000 0x1000>; + interrupts = <0 379 4>; + nand-bus-width = <8>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <16>; + nand-ecc-step-size = <1024>; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "nand_text"; + reg = <0x00000000 0x00400000>; + }; + + ... + + }; From 0091b9d6c1ef2caab6cb3b6c0aa75f9948307856 Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Mon, 8 Dec 2014 19:28:20 +0800 Subject: [PATCH 269/360] dmaengine: pl330: fix bug that cause start the same descs in cyclic This bug will cause NULL pointer after commit dfac17, and cause wrong package in I2S DMA transfer before commit dfac17. Tested on RK3288-pinky2 board. Detail: I2S DMA transfer(sound/core/pcm_dmaengine.c): dmaengine_pcm_prepare_and_submit --> dmaengine_prep_dma_cyclic --> pl330_prep_dma_cyclic --> the case: 1. pl330_submit_req(desc0): thrd->req[0].desc = desc0, thrd->lstenq = 0 2. pl330_submit_req(desc1): thrd->req[1].desc = desc1, thrd->lstenq = 1 3. _start(desc0) by submit_req: thrd->req_running = 0 because: idx = 1 - thrd->lstenq = 0 4. pl330_update(desc0 OK): thrd->req[0].desc = NULL, desc0 to req_done list because: idx = active = thrd->req_running = 0 5. _start(desc1) by pl330_update: thrd->req_running = 1 because: idx = 1 - thrd->lstenq = 0, but thrd->req[0].desc == NULL, so: idx = thrd->lstenq = 1 6. pl330_submit_req(desc2): thrd->req[0].desc = desc2, thrd->lstenq = 0 7. _start(desc1) by submit_req: thrd->req_running = 1 because: idx = 1 - thrd->lstenq = 1 Note: _start started the same descs _start should start desc2 here, NOT desc1 8. pl330_update(desc1 OK): thrd->req[1].desc = NULL, desc1 to req_done list because: idx = active = thrd->req_running = 1 9. _start(desc2) by pl330_update : thrd->req_running = 0 because: idx = 1 - thrd->lstenq = 0 10.pl330_update(desc1 OK, NOT desc2): thrd->req[0].desc = NULL, desc2 to req_done list because: idx = active = thrd->req_running = 0 11.pl330_submit_req(desc3): thrd->req[0].desc = desc3, thrd->lstenq = 0 12.pl330_submit_req(desc4): thrd->req[1].desc = desc4, thrd->lstenq = 1 13._start(desc3) by submit_req: thrd->req_running = 0 because: idx = 1 - thrd->lstenq = 0 14.pl330_update(desc2 OK NOT desc3): thrd->req[0].desc = NULL desc3 to req_done list because: idx = active = thrd->req_running = 0 15._start(desc4) by pl330_update: thrd->req_running = 1 because: idx = 1 - thrd->lstenq = 0, but thrd->req[0].desc == NULL, so: idx = thrd->lstenq = 1 16.pl330_submit_req(desc5): thrd->req[0].desc = desc5, thrd->lstenq = 0 17._start(desc4) by submit_req: thrd->req_running = 1 because: idx = 1 - thrd->lstenq = 1 18.pl330_update(desc3 OK NOT desc4): thrd->req[1].desc = NULL desc4 to req_done list because: idx = active = thrd->req_running = 1 19._start(desc4) by pl330_update: thrd->req_running = 0 because: idx = 1 - thrd->lstenq = 1, but thrd->req[1].desc == NULL, so: idx = thrd->lstenq = 0 20.pl330_update(desc4 OK): thrd->req[0].desc = NULL, desc5 to req_done list because: idx = active = thrd->req_running = 0 21.pl330_update(desc4 OK): 1) before commit dfac17(set req_running -1 in pl330_update/mark_free()): because: active = -1, abort result: desc0-desc5's callback are all called, but step 10 and step 18 go wrong. 2) before commit dfac17: idx = active = thrd->req_runnig = 0 --> descdone = thrd->req[0] = NULL --> list_add_tail(&descdone->rqd, &pl330->req_done); --> got NULL pointer!!! Signed-off-by: Addy Ke Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 027f1d7ea4fcc8..2dbc93011c0efa 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1048,6 +1048,10 @@ static bool _trigger(struct pl330_thread *thrd) if (!req) return true; + /* Return if req is running */ + if (idx == thrd->req_running) + return true; + desc = req->desc; ns = desc->rqcfg.nonsecure ? 1 : 0; @@ -1587,6 +1591,8 @@ static int pl330_update(struct pl330_dmac *pl330) descdone = thrd->req[active].desc; thrd->req[active].desc = NULL; + thrd->req_running = -1; + /* Get going again ASAP */ _start(thrd); From 5e05bf5833eb3dd97b6b6a52301d81e033714cb3 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 11 Feb 2015 15:01:13 +1030 Subject: [PATCH 270/360] virtio: Avoid possible kernel panic if DEBUG is enabled. The virtqueue_add() calls START_USE() upon entry. The virtqueue_kick() is called if vq->num_added == (1 << 16) - 1 before calling END_USE(). The virtqueue_kick_prepare() called via virtqueue_kick() calls START_USE() upon entry, and will call panic() if DEBUG is enabled. Move this virtqueue_kick() call to after END_USE() call. Signed-off-by: Tetsuo Handa Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 95b9661554d14b..096b857e7b75ab 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -244,14 +244,14 @@ static inline int virtqueue_add(struct virtqueue *_vq, vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx) + 1); vq->num_added++; + pr_debug("Added buffer head %i to %p\n", head, vq); + END_USE(vq); + /* This is very unlikely, but theoretically possible. Kick * just in case. */ if (unlikely(vq->num_added == (1 << 16) - 1)) virtqueue_kick(_vq); - pr_debug("Added buffer head %i to %p\n", head, vq); - END_USE(vq); - return 0; } From e6a02746e0a9cdda5114db912fe2aadfed289aae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:01:14 +1030 Subject: [PATCH 271/360] virtio: define VIRTIO_PCI_CAP_PCI_CFG in header. This provides backdoor access to the device MMIOs, and every device should have one. From the virtio 1.0 spec (CS03): 4.1.4.7.1 Device Requirements: PCI configuration access capability The device MUST present at least one VIRTIO_PCI_CAP_PCI_CFG capability. Signed-off-by: Rusty Russell Acked-by: Michael S. Tsirkin --- include/uapi/linux/virtio_pci.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index 3b7e4d2765fb52..75301468359f0c 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -109,8 +109,10 @@ #define VIRTIO_PCI_CAP_NOTIFY_CFG 2 /* ISR access */ #define VIRTIO_PCI_CAP_ISR_CFG 3 -/* Device specific confiuration */ +/* Device specific configuration */ #define VIRTIO_PCI_CAP_DEVICE_CFG 4 +/* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 /* This is the PCI capability header: */ struct virtio_pci_cap { From 527100a4ee744bbfc90f1609ee4a0144883b3e4a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:01:14 +1030 Subject: [PATCH 272/360] virtio: Don't expose legacy block features when VIRTIO_BLK_NO_LEGACY defined. This allows modern implementations to ensure they don't use legacy feature bits or SCSI commands (which are not used in v1.0 non-legacy). Signed-off-by: Rusty Russell Acked-by: Michael S. Tsirkin --- include/uapi/linux/virtio_blk.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h index 247c8ba8544a33..3c53eec4ae2269 100644 --- a/include/uapi/linux/virtio_blk.h +++ b/include/uapi/linux/virtio_blk.h @@ -31,22 +31,25 @@ #include /* Feature bits */ -#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ #define VIRTIO_BLK_F_GEOMETRY 4 /* Legacy geometry available */ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ -#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ -#define VIRTIO_BLK_F_WCE 9 /* Writeback mode enabled after reset */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ -#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ #define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ +/* Legacy feature bits */ +#ifndef VIRTIO_BLK_NO_LEGACY +#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ +#define VIRTIO_BLK_F_WCE 9 /* Writeback mode enabled after reset */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ #ifndef __KERNEL__ /* Old (deprecated) name for VIRTIO_BLK_F_WCE. */ #define VIRTIO_BLK_F_FLUSH VIRTIO_BLK_F_WCE #endif +#endif /* !VIRTIO_BLK_NO_LEGACY */ #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ @@ -100,8 +103,10 @@ struct virtio_blk_config { #define VIRTIO_BLK_T_IN 0 #define VIRTIO_BLK_T_OUT 1 +#ifndef VIRTIO_BLK_NO_LEGACY /* This bit says it's a scsi command, not an actual read or write. */ #define VIRTIO_BLK_T_SCSI_CMD 2 +#endif /* VIRTIO_BLK_NO_LEGACY */ /* Cache flush command */ #define VIRTIO_BLK_T_FLUSH 4 @@ -109,8 +114,10 @@ struct virtio_blk_config { /* Get device ID command */ #define VIRTIO_BLK_T_GET_ID 8 +#ifndef VIRTIO_BLK_NO_LEGACY /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000 +#endif /* !VIRTIO_BLK_NO_LEGACY */ /* This is the first element of the read scatter-gather list. */ struct virtio_blk_outhdr { @@ -122,12 +129,14 @@ struct virtio_blk_outhdr { __virtio64 sector; }; +#ifndef VIRTIO_BLK_NO_LEGACY struct virtio_scsi_inhdr { __virtio32 errors; __virtio32 data_len; __virtio32 sense_len; __virtio32 residual; }; +#endif /* !VIRTIO_BLK_NO_LEGACY */ /* And this is the final byte of the write scatter-gather list. */ #define VIRTIO_BLK_S_OK 0 From 6d96ee98b1d08bcf0f90a6bf2c6766dda6b3a010 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:01:14 +1030 Subject: [PATCH 273/360] virtio: Don't expose legacy config features when VIRTIO_CONFIG_NO_LEGACY defined. The VIRTIO_F_ANY_LAYOUT and VIRTIO_F_NOTIFY_ON_EMPTY features are pre-1.0 only. Signed-off-by: Rusty Russell Acked-by: Michael S. Tsirkin --- include/uapi/linux/virtio_config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/virtio_config.h b/include/uapi/linux/virtio_config.h index a6d0cdeaacd4dc..c18264df9504c1 100644 --- a/include/uapi/linux/virtio_config.h +++ b/include/uapi/linux/virtio_config.h @@ -49,12 +49,14 @@ #define VIRTIO_TRANSPORT_F_START 28 #define VIRTIO_TRANSPORT_F_END 33 +#ifndef VIRTIO_CONFIG_NO_LEGACY /* Do we get callbacks when the ring is completely used, even if we've * suppressed them? */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 /* Can the device handle any descriptor layout? */ #define VIRTIO_F_ANY_LAYOUT 27 +#endif /* VIRTIO_CONFIG_NO_LEGACY */ /* v1.0 compliant. */ #define VIRTIO_F_VERSION_1 32 From 7abb568dbb32d055ec6a5633d26fb39fbcd525e3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:01:14 +1030 Subject: [PATCH 274/360] virtio_pci: use 16-bit accessor for queue_enable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since PCI is little endian, 8-bit access might work, but the spec section is very clear on this: 4.1.3.1 Driver Requirements: PCI Device Layout The driver MUST access each field using the “natural” access method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for 16-bit fields and 8-bit accesses for 8-bit fields. Signed-off-by: Rusty Russell Acked-by: Michael S. Tsirkin --- drivers/virtio/virtio_pci_modern.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index f16e462cb4ef35..2aa38e59db2e08 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -294,7 +294,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, /* Check if queue is either not available or already active. */ num = ioread16(&cfg->queue_size); - if (!num || ioread8(&cfg->queue_enable)) + if (!num || ioread16(&cfg->queue_enable)) return ERR_PTR(-ENOENT); if (num & (num - 1)) { @@ -394,7 +394,7 @@ static int vp_modern_find_vqs(struct virtio_device *vdev, unsigned nvqs, */ list_for_each_entry(vq, &vdev->vqs, list) { iowrite16(vq->index, &vp_dev->common->queue_select); - iowrite8(1, &vp_dev->common->queue_enable); + iowrite16(1, &vp_dev->common->queue_enable); } return 0; From be8ff5952a8d943660d3c01f1abf4e71eb565fdb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:01:14 +1030 Subject: [PATCH 275/360] virtio: don't require a config space on the console device. Strictly, it's only needed when we have features (size or multiport). Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 26afb56a807300..fae2dbbf57459f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1986,7 +1986,10 @@ static int virtcons_probe(struct virtio_device *vdev) bool multiport; bool early = early_put_chars != NULL; - if (!vdev->config->get) { + /* We only need a config space if features are offered */ + if (!vdev->config->get && + (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE) + || virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT))) { dev_err(&vdev->dev, "%s failure: config access disabled\n", __func__); return -EINVAL; From a454bb36cabdac163fcd80ddb37662893ea603f1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:09 +1030 Subject: [PATCH 276/360] lguest: have --rng read from /dev/urandom not /dev/random. Theoretical debates aside, now it boots. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 32cf2ce15d69bc..3f7f2326cd9a26 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1733,9 +1733,9 @@ static void setup_block_file(const char *filename) } /*L:211 - * Our random number generator device reads from /dev/random into the Guest's + * Our random number generator device reads from /dev/urandom into the Guest's * input buffers. The usual case is that the Guest doesn't want random numbers - * and so has no buffers although /dev/random is still readable, whereas + * and so has no buffers although /dev/urandom is still readable, whereas * console is the reverse. * * The same logic applies, however. @@ -1763,7 +1763,7 @@ static void rng_input(struct virtqueue *vq) while (!iov_empty(iov, in_num)) { len = readv(rng_info->rfd, iov, in_num); if (len <= 0) - err(1, "Read from /dev/random gave %i", len); + err(1, "Read from /dev/urandom gave %i", len); iov_consume(iov, in_num, NULL, len); totlen += len; } @@ -1780,8 +1780,8 @@ static void setup_rng(void) struct device *dev; struct rng_info *rng_info = malloc(sizeof(*rng_info)); - /* Our device's privat info simply contains the /dev/random fd. */ - rng_info->rfd = open_or_die("/dev/random", O_RDONLY); + /* Our device's private info simply contains the /dev/urandom fd. */ + rng_info->rfd = open_or_die("/dev/urandom", O_RDONLY); /* Create the new device. */ dev = new_device("rng", VIRTIO_ID_RNG); From 18c137371b2ea86d263b75665a4904a0b8872990 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:09 +1030 Subject: [PATCH 277/360] lguest: add operations to get/set a register from the Launcher. We use the ptrace API struct, and we currently don't let them set anything but the normal registers (we'd have to filter the others). Signed-off-by: Rusty Russell --- drivers/lguest/core.c | 8 ++++++ drivers/lguest/lg.h | 3 ++ drivers/lguest/lguest_user.c | 49 +++++++++++++++++++++++++++++++++ drivers/lguest/x86/core.c | 46 +++++++++++++++++++++++++++++++ include/linux/lguest_launcher.h | 2 ++ 5 files changed, 108 insertions(+) diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 6590558d1d31c6..cdb2f9aa58605d 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -208,6 +208,14 @@ void __lgwrite(struct lg_cpu *cpu, unsigned long addr, const void *b, */ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) { + /* If the launcher asked for a register with LHREQ_GETREG */ + if (cpu->reg_read) { + if (put_user(*cpu->reg_read, user)) + return -EFAULT; + cpu->reg_read = NULL; + return sizeof(*cpu->reg_read); + } + /* We stop running once the Guest is dead. */ while (!cpu->lg->dead) { unsigned int irq; diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 2eef40be4c0410..1c98bf74fd68b6 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -52,6 +52,8 @@ struct lg_cpu { unsigned long pending_notify; /* pfn from LHCALL_NOTIFY */ + unsigned long *reg_read; /* register from LHREQ_GETREG */ + /* At end of a page shared mapped over lguest_pages in guest. */ unsigned long regs_page; struct lguest_regs *regs; @@ -210,6 +212,7 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu); int lguest_arch_init_hypercalls(struct lg_cpu *cpu); int lguest_arch_do_hcall(struct lg_cpu *cpu, struct hcall_args *args); void lguest_arch_setup_regs(struct lg_cpu *cpu, unsigned long start); +unsigned long *lguest_arch_regptr(struct lg_cpu *cpu, size_t reg_off, bool any); /* /switcher.S: */ extern char start_switcher_text[], end_switcher_text[], switch_to_guest[]; diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 4263f4cc8c55c0..7f14c152dd23b3 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -173,6 +173,51 @@ static int attach_eventfd(struct lguest *lg, const unsigned long __user *input) return err; } +/* The Launcher can get the registers, and also set some of them. */ +static int getreg_setup(struct lg_cpu *cpu, const unsigned long __user *input) +{ + unsigned long which; + + /* We re-use the ptrace structure to specify which register to read. */ + if (get_user(which, input) != 0) + return -EFAULT; + + /* + * We set up the cpu register pointer, and their next read will + * actually get the value (instead of running the guest). + * + * The last argument 'true' says we can access any register. + */ + cpu->reg_read = lguest_arch_regptr(cpu, which, true); + if (!cpu->reg_read) + return -ENOENT; + + /* And because this is a write() call, we return the length used. */ + return sizeof(unsigned long) * 2; +} + +static int setreg(struct lg_cpu *cpu, const unsigned long __user *input) +{ + unsigned long which, value, *reg; + + /* We re-use the ptrace structure to specify which register to read. */ + if (get_user(which, input) != 0) + return -EFAULT; + input++; + if (get_user(value, input) != 0) + return -EFAULT; + + /* The last argument 'false' means we can't access all registers. */ + reg = lguest_arch_regptr(cpu, which, false); + if (!reg) + return -ENOENT; + + *reg = value; + + /* And because this is a write() call, we return the length used. */ + return sizeof(unsigned long) * 3; +} + /*L:050 * Sending an interrupt is done by writing LHREQ_IRQ and an interrupt * number to /dev/lguest. @@ -434,6 +479,10 @@ static ssize_t write(struct file *file, const char __user *in, return user_send_irq(cpu, input); case LHREQ_EVENTFD: return attach_eventfd(lg, input); + case LHREQ_GETREG: + return getreg_setup(cpu, input); + case LHREQ_SETREG: + return setreg(cpu, input); default: return -EINVAL; } diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 922a1acbf652b9..f7a16b4ea456ec 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -181,6 +181,52 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages) } /*:*/ +unsigned long *lguest_arch_regptr(struct lg_cpu *cpu, size_t reg_off, bool any) +{ + switch (reg_off) { + case offsetof(struct pt_regs, bx): + return &cpu->regs->ebx; + case offsetof(struct pt_regs, cx): + return &cpu->regs->ecx; + case offsetof(struct pt_regs, dx): + return &cpu->regs->edx; + case offsetof(struct pt_regs, si): + return &cpu->regs->esi; + case offsetof(struct pt_regs, di): + return &cpu->regs->edi; + case offsetof(struct pt_regs, bp): + return &cpu->regs->ebp; + case offsetof(struct pt_regs, ax): + return &cpu->regs->eax; + case offsetof(struct pt_regs, ip): + return &cpu->regs->eip; + case offsetof(struct pt_regs, sp): + return &cpu->regs->esp; + } + + /* Launcher can read these, but we don't allow any setting. */ + if (any) { + switch (reg_off) { + case offsetof(struct pt_regs, ds): + return &cpu->regs->ds; + case offsetof(struct pt_regs, es): + return &cpu->regs->es; + case offsetof(struct pt_regs, fs): + return &cpu->regs->fs; + case offsetof(struct pt_regs, gs): + return &cpu->regs->gs; + case offsetof(struct pt_regs, cs): + return &cpu->regs->cs; + case offsetof(struct pt_regs, flags): + return &cpu->regs->eflags; + case offsetof(struct pt_regs, ss): + return &cpu->regs->ss; + } + } + + return NULL; +} + /*M:002 * There are hooks in the scheduler which we can register to tell when we * get kicked off the CPU (preempt_notifier_register()). This would allow us diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index 495203ff221c3e..f27cae27b0c199 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -63,6 +63,8 @@ enum lguest_req LHREQ_IRQ, /* + irq */ LHREQ_BREAK, /* No longer used */ LHREQ_EVENTFD, /* + address, fd. */ + LHREQ_GETREG, /* + offset within struct pt_regs (then read value). */ + LHREQ_SETREG, /* + offset within struct pt_regs, value. */ }; /* From 69a09dc1742ffbb3b02f3a1e03da4801e96452e9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:09 +1030 Subject: [PATCH 278/360] lguest: write more information to userspace about pending traps. This is preparation for userspace handling MMIO and ioport accesses. Signed-off-by: Rusty Russell --- drivers/lguest/core.c | 7 ++++--- drivers/lguest/hypercalls.c | 7 ++++--- drivers/lguest/lg.h | 3 ++- drivers/lguest/lguest_user.c | 14 +++++++++----- include/linux/lguest_launcher.h | 13 +++++++++++++ tools/lguest/lguest.c | 16 ++++++++++------ 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index cdb2f9aa58605d..9159dbc583f67e 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -229,16 +229,17 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) * It's possible the Guest did a NOTIFY hypercall to the * Launcher. */ - if (cpu->pending_notify) { + if (cpu->pending.trap) { /* * Does it just needs to write to a registered * eventfd (ie. the appropriate virtqueue thread)? */ if (!send_notify_to_eventfd(cpu)) { /* OK, we tell the main Launcher. */ - if (put_user(cpu->pending_notify, user)) + if (copy_to_user(user, &cpu->pending, + sizeof(cpu->pending))) return -EFAULT; - return sizeof(cpu->pending_notify); + return sizeof(cpu->pending); } } diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index 83511eb0923d2f..5dd1fb8a6610a4 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c @@ -118,7 +118,8 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args) cpu->halted = 1; break; case LHCALL_NOTIFY: - cpu->pending_notify = args->arg1; + cpu->pending.trap = LGUEST_TRAP_ENTRY; + cpu->pending.addr = args->arg1; break; default: /* It should be an architecture-specific hypercall. */ @@ -189,7 +190,7 @@ static void do_async_hcalls(struct lg_cpu *cpu) * Stop doing hypercalls if they want to notify the Launcher: * it needs to service this first. */ - if (cpu->pending_notify) + if (cpu->pending.trap) break; } } @@ -280,7 +281,7 @@ void do_hypercalls(struct lg_cpu *cpu) * NOTIFY to the Launcher, we want to return now. Otherwise we do * the hypercall. */ - if (!cpu->pending_notify) { + if (!cpu->pending.trap) { do_hcall(cpu, cpu->hcall); /* * Tricky point: we reset the hcall pointer to mark the diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 1c98bf74fd68b6..020fec5bb07203 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -50,7 +50,8 @@ struct lg_cpu { /* Bitmap of what has changed: see CHANGED_* above. */ int changed; - unsigned long pending_notify; /* pfn from LHCALL_NOTIFY */ + /* Pending operation. */ + struct lguest_pending pending; unsigned long *reg_read; /* register from LHREQ_GETREG */ diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 7f14c152dd23b3..dcf9efd94cf409 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -29,6 +29,10 @@ bool send_notify_to_eventfd(struct lg_cpu *cpu) unsigned int i; struct lg_eventfd_map *map; + /* We only connect LHCALL_NOTIFY to event fds, not other traps. */ + if (cpu->pending.trap != LGUEST_TRAP_ENTRY) + return false; + /* * This "rcu_read_lock()" helps track when someone is still looking at * the (RCU-using) eventfds array. It's not actually a lock at all; @@ -52,9 +56,9 @@ bool send_notify_to_eventfd(struct lg_cpu *cpu) * we'll continue to use the old array and just won't see the new one. */ for (i = 0; i < map->num; i++) { - if (map->map[i].addr == cpu->pending_notify) { + if (map->map[i].addr == cpu->pending.addr) { eventfd_signal(map->map[i].event, 1); - cpu->pending_notify = 0; + cpu->pending.trap = 0; break; } } @@ -62,7 +66,7 @@ bool send_notify_to_eventfd(struct lg_cpu *cpu) rcu_read_unlock(); /* If we cleared the notification, it's because we found a match. */ - return cpu->pending_notify == 0; + return cpu->pending.trap == 0; } /*L:055 @@ -282,8 +286,8 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) * If we returned from read() last time because the Guest sent I/O, * clear the flag. */ - if (cpu->pending_notify) - cpu->pending_notify = 0; + if (cpu->pending.trap) + cpu->pending.trap = 0; /* Run the Guest until something interesting happens. */ return run_guest(cpu, (unsigned long __user *)user); diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index f27cae27b0c199..c4451ebece4713 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -67,6 +67,19 @@ enum lguest_req LHREQ_SETREG, /* + offset within struct pt_regs, value. */ }; +/* + * This is what read() of the lguest fd populates. trap == + * LGUEST_TRAP_ENTRY for an LHCALL_NOTIFY (addr is the + * argument), 14 for a page fault in the MMIO region (addr is + * the trap address, insn is the instruction), or 13 for a GPF + * (insn is the instruction). + */ +struct lguest_pending { + __u8 trap; + __u8 insn[7]; + __u32 addr; +}; + /* * The alignment to use between consumer and producer parts of vring. * x86 pagesize for historical reasons. diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 3f7f2326cd9a26..0e754d04876d06 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1820,17 +1820,21 @@ static void __attribute__((noreturn)) restart_guest(void) static void __attribute__((noreturn)) run_guest(void) { for (;;) { - unsigned long notify_addr; + struct lguest_pending notify; int readval; /* We read from the /dev/lguest device to run the Guest. */ - readval = pread(lguest_fd, ¬ify_addr, - sizeof(notify_addr), cpu_id); + readval = pread(lguest_fd, ¬ify, sizeof(notify), cpu_id); /* One unsigned long means the Guest did HCALL_NOTIFY */ - if (readval == sizeof(notify_addr)) { - verbose("Notify on address %#lx\n", notify_addr); - handle_output(notify_addr); + if (readval == sizeof(notify)) { + if (notify.trap == 0x1F) { + verbose("Notify on address %#08x\n", + notify.addr); + handle_output(notify.addr); + } else + errx(1, "Unknown trap %i addr %#08x\n", + notify.trap, notify.addr); /* ENOENT means the Guest died. Reading tells us why. */ } else if (errno == ENOENT) { char reason[1024] = { 0 }; From 8ed313001a892f240269dea05d4b925cbd150492 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:09 +1030 Subject: [PATCH 279/360] lguest: add infrastructure for userspace to deliver a trap to the guest. This is required for instruction emulation to move to userspace. Signed-off-by: Rusty Russell --- drivers/lguest/lguest_user.c | 19 +++++++++++++++++++ include/linux/lguest_launcher.h | 1 + 2 files changed, 20 insertions(+) diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index dcf9efd94cf409..be996d17361518 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -243,6 +243,23 @@ static int user_send_irq(struct lg_cpu *cpu, const unsigned long __user *input) return 0; } +/*L:053 + * Deliver a trap: this is used by the Launcher if it can't emulate + * an instruction. + */ +static int trap(struct lg_cpu *cpu, const unsigned long __user *input) +{ + unsigned long trapnum; + + if (get_user(trapnum, input) != 0) + return -EFAULT; + + if (!deliver_trap(cpu, trapnum)) + return -EINVAL; + + return 0; +} + /*L:040 * Once our Guest is initialized, the Launcher makes it run by reading * from /dev/lguest. @@ -487,6 +504,8 @@ static ssize_t write(struct file *file, const char __user *in, return getreg_setup(cpu, input); case LHREQ_SETREG: return setreg(cpu, input); + case LHREQ_TRAP: + return trap(cpu, input); default: return -EINVAL; } diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index c4451ebece4713..3c402b843e03c2 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -65,6 +65,7 @@ enum lguest_req LHREQ_EVENTFD, /* + address, fd. */ LHREQ_GETREG, /* + offset within struct pt_regs (then read value). */ LHREQ_SETREG, /* + offset within struct pt_regs, value. */ + LHREQ_TRAP, /* + trap number to deliver to guest. */ }; /* From c9e433e4b852b70ea267388cf9b5d8096b04c44c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:09 +1030 Subject: [PATCH 280/360] lguest: add infrastructure to check mappings. We normally abort the guest unconditionally when it gives us a bad address, but in the next patch we want to copy some bytes which may not be mapped. Signed-off-by: Rusty Russell --- drivers/lguest/lg.h | 1 + drivers/lguest/page_tables.c | 42 +++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 020fec5bb07203..9da4f351e07794 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -202,6 +202,7 @@ void guest_set_pte(struct lg_cpu *cpu, unsigned long gpgdir, void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages); bool demand_page(struct lg_cpu *cpu, unsigned long cr2, int errcode); void pin_page(struct lg_cpu *cpu, unsigned long vaddr); +bool __guest_pa(struct lg_cpu *cpu, unsigned long vaddr, unsigned long *paddr); unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr); void page_table_guest_data_init(struct lg_cpu *cpu); diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index e8b55c3a617042..69c35caa955aab 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -647,7 +647,7 @@ void guest_pagetable_flush_user(struct lg_cpu *cpu) /*:*/ /* We walk down the guest page tables to get a guest-physical address */ -unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr) +bool __guest_pa(struct lg_cpu *cpu, unsigned long vaddr, unsigned long *paddr) { pgd_t gpgd; pte_t gpte; @@ -656,31 +656,47 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr) #endif /* Still not set up? Just map 1:1. */ - if (unlikely(cpu->linear_pages)) - return vaddr; + if (unlikely(cpu->linear_pages)) { + *paddr = vaddr; + return true; + } /* First step: get the top-level Guest page table entry. */ gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t); /* Toplevel not present? We can't map it in. */ - if (!(pgd_flags(gpgd) & _PAGE_PRESENT)) { - kill_guest(cpu, "Bad address %#lx", vaddr); - return -1UL; - } + if (!(pgd_flags(gpgd) & _PAGE_PRESENT)) + goto fail; #ifdef CONFIG_X86_PAE gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t); - if (!(pmd_flags(gpmd) & _PAGE_PRESENT)) { - kill_guest(cpu, "Bad address %#lx", vaddr); - return -1UL; - } + if (!(pmd_flags(gpmd) & _PAGE_PRESENT)) + goto fail; gpte = lgread(cpu, gpte_addr(cpu, gpmd, vaddr), pte_t); #else gpte = lgread(cpu, gpte_addr(cpu, gpgd, vaddr), pte_t); #endif if (!(pte_flags(gpte) & _PAGE_PRESENT)) - kill_guest(cpu, "Bad address %#lx", vaddr); + goto fail; + + *paddr = pte_pfn(gpte) * PAGE_SIZE | (vaddr & ~PAGE_MASK); + return true; + +fail: + *paddr = -1UL; + return false; +} - return pte_pfn(gpte) * PAGE_SIZE | (vaddr & ~PAGE_MASK); +/* + * This is the version we normally use: kills the Guest if it uses a + * bad address + */ +unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr) +{ + unsigned long paddr; + + if (!__guest_pa(cpu, vaddr, &paddr)) + kill_guest(cpu, "Bad address %#lx", vaddr); + return paddr; } /* From c565650b1028bc551e5d16dd0ec8f7078da7cace Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:10 +1030 Subject: [PATCH 281/360] lguest: send trap 13 through to userspace. We copy 7 bytes at eip for userspace's instruction decode; we have to carefully handle the case where eip is at the end of a page. We can't leave this to userspace since kernel has all the page table decode logic. The decode logic moves to userspace, basically unchanged. Signed-off-by: Rusty Russell --- drivers/lguest/x86/core.c | 133 +++++++++++----------------------- tools/lguest/lguest.c | 149 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 90 deletions(-) diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index f7a16b4ea456ec..42e87bf14113e8 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -314,95 +314,52 @@ void lguest_arch_run_guest(struct lg_cpu *cpu) * usually attached to a PC. * * When the Guest uses one of these instructions, we get a trap (General - * Protection Fault) and come here. We see if it's one of those troublesome - * instructions and skip over it. We return true if we did. + * Protection Fault) and come here. We queue this to be sent out to the + * Launcher to handle. */ -static int emulate_insn(struct lg_cpu *cpu) -{ - u8 insn; - unsigned int insnlen = 0, in = 0, small_operand = 0; - /* - * The eip contains the *virtual* address of the Guest's instruction: - * walk the Guest's page tables to find the "physical" address. - */ - unsigned long physaddr = guest_pa(cpu, cpu->regs->eip); - - /* - * This must be the Guest kernel trying to do something, not userspace! - * The bottom two bits of the CS segment register are the privilege - * level. - */ - if ((cpu->regs->cs & 3) != GUEST_PL) - return 0; - /* Decoding x86 instructions is icky. */ - insn = lgread(cpu, physaddr, u8); - - /* - * Around 2.6.33, the kernel started using an emulation for the - * cmpxchg8b instruction in early boot on many configurations. This - * code isn't paravirtualized, and it tries to disable interrupts. - * Ignore it, which will Mostly Work. - */ - if (insn == 0xfa) { - /* "cli", or Clear Interrupt Enable instruction. Skip it. */ - cpu->regs->eip++; - return 1; +/* + * The eip contains the *virtual* address of the Guest's instruction: + * we copy the instruction here so the Launcher doesn't have to walk + * the page tables to decode it. We handle the case (eg. in a kernel + * module) where the instruction is over two pages, and the pages are + * virtually but not physically contiguous. + * + * The longest possible x86 instruction is 15 bytes, but we don't handle + * anything that strange. + */ +static void copy_from_guest(struct lg_cpu *cpu, + void *dst, unsigned long vaddr, size_t len) +{ + size_t to_page_end = PAGE_SIZE - (vaddr % PAGE_SIZE); + unsigned long paddr; + + BUG_ON(len > PAGE_SIZE); + + /* If it goes over a page, copy in two parts. */ + if (len > to_page_end) { + /* But make sure the next page is mapped! */ + if (__guest_pa(cpu, vaddr + to_page_end, &paddr)) + copy_from_guest(cpu, dst + to_page_end, + vaddr + to_page_end, + len - to_page_end); + else + /* Otherwise fill with zeroes. */ + memset(dst + to_page_end, 0, len - to_page_end); + len = to_page_end; } - /* - * 0x66 is an "operand prefix". It means a 16, not 32 bit in/out. - */ - if (insn == 0x66) { - small_operand = 1; - /* The instruction is 1 byte so far, read the next byte. */ - insnlen = 1; - insn = lgread(cpu, physaddr + insnlen, u8); - } + /* This will kill the guest if it isn't mapped, but that + * shouldn't happen. */ + __lgread(cpu, dst, guest_pa(cpu, vaddr), len); +} - /* - * We can ignore the lower bit for the moment and decode the 4 opcodes - * we need to emulate. - */ - switch (insn & 0xFE) { - case 0xE4: /* in ,%al */ - insnlen += 2; - in = 1; - break; - case 0xEC: /* in (%dx),%al */ - insnlen += 1; - in = 1; - break; - case 0xE6: /* out %al, */ - insnlen += 2; - break; - case 0xEE: /* out %al,(%dx) */ - insnlen += 1; - break; - default: - /* OK, we don't know what this is, can't emulate. */ - return 0; - } - /* - * If it was an "IN" instruction, they expect the result to be read - * into %eax, so we change %eax. We always return all-ones, which - * traditionally means "there's nothing there". - */ - if (in) { - /* Lower bit tells means it's a 32/16 bit access */ - if (insn & 0x1) { - if (small_operand) - cpu->regs->eax |= 0xFFFF; - else - cpu->regs->eax = 0xFFFFFFFF; - } else - cpu->regs->eax |= 0xFF; - } - /* Finally, we've "done" the instruction, so move past it. */ - cpu->regs->eip += insnlen; - /* Success! */ - return 1; +static void setup_emulate_insn(struct lg_cpu *cpu) +{ + cpu->pending.trap = 13; + copy_from_guest(cpu, cpu->pending.insn, cpu->regs->eip, + sizeof(cpu->pending.insn)); } /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ @@ -410,14 +367,10 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) { switch (cpu->regs->trapnum) { case 13: /* We've intercepted a General Protection Fault. */ - /* - * Check if this was one of those annoying IN or OUT - * instructions which we need to emulate. If so, we just go - * back into the Guest after we've done it. - */ + /* Hand to Launcher to emulate those pesky IN and OUT insns */ if (cpu->regs->errcode == 0) { - if (emulate_insn(cpu)) - return; + setup_emulate_insn(cpu); + return; } break; case 14: /* We've intercepted a Page Fault. */ diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 0e754d04876d06..b2217657f62cf3 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -41,6 +41,7 @@ #include #include #include +#include #ifndef VIRTIO_F_ANY_LAYOUT #define VIRTIO_F_ANY_LAYOUT 27 @@ -1143,6 +1144,150 @@ static void handle_output(unsigned long addr) strnlen(from_guest_phys(addr), guest_limit - addr)); } +/*L:216 + * This is where we emulate a handful of Guest instructions. It's ugly + * and we used to do it in the kernel but it grew over time. + */ + +/* + * We use the ptrace syscall's pt_regs struct to talk about registers + * to lguest: these macros convert the names to the offsets. + */ +#define getreg(name) getreg_off(offsetof(struct user_regs_struct, name)) +#define setreg(name, val) \ + setreg_off(offsetof(struct user_regs_struct, name), (val)) + +static u32 getreg_off(size_t offset) +{ + u32 r; + unsigned long args[] = { LHREQ_GETREG, offset }; + + if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0) + err(1, "Getting register %u", offset); + if (pread(lguest_fd, &r, sizeof(r), cpu_id) != sizeof(r)) + err(1, "Reading register %u", offset); + + return r; +} + +static void setreg_off(size_t offset, u32 val) +{ + unsigned long args[] = { LHREQ_SETREG, offset, val }; + + if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0) + err(1, "Setting register %u", offset); +} + +static void emulate_insn(const u8 insn[]) +{ + unsigned long args[] = { LHREQ_TRAP, 13 }; + unsigned int insnlen = 0, in = 0, small_operand = 0, byte_access; + unsigned int eax, port, mask; + /* + * We always return all-ones on IO port reads, which traditionally + * means "there's nothing there". + */ + u32 val = 0xFFFFFFFF; + + /* + * This must be the Guest kernel trying to do something, not userspace! + * The bottom two bits of the CS segment register are the privilege + * level. + */ + if ((getreg(xcs) & 3) != 0x1) + goto no_emulate; + + /* Decoding x86 instructions is icky. */ + + /* + * Around 2.6.33, the kernel started using an emulation for the + * cmpxchg8b instruction in early boot on many configurations. This + * code isn't paravirtualized, and it tries to disable interrupts. + * Ignore it, which will Mostly Work. + */ + if (insn[insnlen] == 0xfa) { + /* "cli", or Clear Interrupt Enable instruction. Skip it. */ + insnlen = 1; + goto skip_insn; + } + + /* + * 0x66 is an "operand prefix". It means a 16, not 32 bit in/out. + */ + if (insn[insnlen] == 0x66) { + small_operand = 1; + /* The instruction is 1 byte so far, read the next byte. */ + insnlen = 1; + } + + /* If the lower bit isn't set, it's a single byte access */ + byte_access = !(insn[insnlen] & 1); + + /* + * Now we can ignore the lower bit and decode the 4 opcodes + * we need to emulate. + */ + switch (insn[insnlen] & 0xFE) { + case 0xE4: /* in ,%al */ + port = insn[insnlen+1]; + insnlen += 2; + in = 1; + break; + case 0xEC: /* in (%dx),%al */ + port = getreg(edx) & 0xFFFF; + insnlen += 1; + in = 1; + break; + case 0xE6: /* out %al, */ + port = insn[insnlen+1]; + insnlen += 2; + break; + case 0xEE: /* out %al,(%dx) */ + port = getreg(edx) & 0xFFFF; + insnlen += 1; + break; + default: + /* OK, we don't know what this is, can't emulate. */ + goto no_emulate; + } + + /* Set a mask of the 1, 2 or 4 bytes, depending on size of IO */ + if (byte_access) + mask = 0xFF; + else if (small_operand) + mask = 0xFFFF; + else + mask = 0xFFFFFFFF; + + /* + * If it was an "IN" instruction, they expect the result to be read + * into %eax, so we change %eax. + */ + eax = getreg(eax); + + if (in) { + /* Clear the bits we're about to read */ + eax &= ~mask; + /* Copy bits in from val. */ + eax |= val & mask; + /* Now update the register. */ + setreg(eax, eax); + } + + verbose("IO %s of %x to %u: %#08x\n", + in ? "IN" : "OUT", mask, port, eax); +skip_insn: + /* Finally, we've "done" the instruction, so move past it. */ + setreg(eip, getreg(eip) + insnlen); + return; + +no_emulate: + /* Inject trap into Guest. */ + if (write(lguest_fd, args, sizeof(args)) < 0) + err(1, "Reinjecting trap 13 for fault at %#x", getreg(eip)); +} + + /*L:190 * Device Setup * @@ -1832,6 +1977,10 @@ static void __attribute__((noreturn)) run_guest(void) verbose("Notify on address %#08x\n", notify.addr); handle_output(notify.addr); + } else if (notify.trap == 13) { + verbose("Emulating instruction at %#x\n", + getreg(eip)); + emulate_insn(notify.insn); } else errx(1, "Unknown trap %i addr %#08x\n", notify.trap, notify.addr); From 48fd6b71d60ef66ef2d791045d750168c0d09201 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:10 +1030 Subject: [PATCH 282/360] lguest: suppress PS/2 keyboard polling. While hacking on getting I/O out to the lguest launcher, I noticed that returning 0xFF for the PS/2 keyboard status made it spin for a while thinking there was a key pending. Fix this by returning 1 instead of 0xFF. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index b2217657f62cf3..485fe13db12e58 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1259,6 +1259,10 @@ static void emulate_insn(const u8 insn[]) else mask = 0xFFFFFFFF; + /* This is the PS/2 keyboard status; 1 means ready for output */ + if (port == 0x64) + val = 1; + /* * If it was an "IN" instruction, they expect the result to be read * into %eax, so we change %eax. From d1c29465b8a52d8fc5a59aac92c6b206b69fe631 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:10 +1030 Subject: [PATCH 283/360] lguest: don't disable iospace. This no longer speeds up boot (IDE got better, I guess), but it does stop us probing for a PCI bus. Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index c1c1544b84859e..47ec7f201d27af 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -1399,14 +1399,6 @@ __init void lguest_init(void) /* Hook in our special panic hypercall code. */ atomic_notifier_chain_register(&panic_notifier_list, &paniced); - /* - * The IDE code spends about 3 seconds probing for disks: if we reserve - * all the I/O ports up front it can't get them and so doesn't probe. - * Other device drivers are similar (but less severe). This cuts the - * kernel boot time on my machine from 4.1 seconds to 0.45 seconds. - */ - paravirt_disable_iospace(); - /* * This is messy CPU setup stuff which the native boot code does before * start_kernel, so we have to do, too: From 7313d5217e6b9817897172d6a6ff477bdc415ed6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:10 +1030 Subject: [PATCH 284/360] lguest: add iomem region, where guest page faults get sent to userspace. This lets us implement PCI. Signed-off-by: Rusty Russell --- drivers/lguest/lg.h | 7 ++++++- drivers/lguest/lguest_user.c | 3 ++- drivers/lguest/page_tables.c | 33 ++++++++++++++++++++++++++++++--- drivers/lguest/x86/core.c | 19 ++++++++++++++++++- tools/lguest/lguest.c | 3 ++- 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index 9da4f351e07794..eb81abc05995ac 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -97,8 +97,12 @@ struct lguest { struct lg_cpu cpus[NR_CPUS]; unsigned int nr_cpus; + /* Valid guest memory pages must be < this. */ u32 pfn_limit; + /* Device memory is >= pfn_limit and < device_limit. */ + u32 device_limit; + /* * This provides the offset to the base of guest-physical memory in the * Launcher. @@ -200,7 +204,8 @@ void guest_pagetable_flush_user(struct lg_cpu *cpu); void guest_set_pte(struct lg_cpu *cpu, unsigned long gpgdir, unsigned long vaddr, pte_t val); void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages); -bool demand_page(struct lg_cpu *cpu, unsigned long cr2, int errcode); +bool demand_page(struct lg_cpu *cpu, unsigned long cr2, int errcode, + unsigned long *iomem); void pin_page(struct lg_cpu *cpu, unsigned long vaddr); bool __guest_pa(struct lg_cpu *cpu, unsigned long vaddr, unsigned long *paddr); unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr); diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index be996d17361518..c8b0e8575b44fa 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -385,7 +385,7 @@ static int initialize(struct file *file, const unsigned long __user *input) /* "struct lguest" contains all we (the Host) know about a Guest. */ struct lguest *lg; int err; - unsigned long args[3]; + unsigned long args[4]; /* * We grab the Big Lguest lock, which protects against multiple @@ -419,6 +419,7 @@ static int initialize(struct file *file, const unsigned long __user *input) /* Populate the easy fields of our "struct lguest" */ lg->mem_base = (void __user *)args[0]; lg->pfn_limit = args[1]; + lg->device_limit = args[3]; /* This is the first cpu (cpu 0) and it will start booting at args[2] */ err = lg_cpu_start(&lg->cpus[0], 0, args[2]); diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 69c35caa955aab..e3abebc912c00a 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -250,6 +250,16 @@ static void release_pte(pte_t pte) } /*:*/ +static bool gpte_in_iomem(struct lg_cpu *cpu, pte_t gpte) +{ + /* We don't handle large pages. */ + if (pte_flags(gpte) & _PAGE_PSE) + return false; + + return (pte_pfn(gpte) >= cpu->lg->pfn_limit + && pte_pfn(gpte) < cpu->lg->device_limit); +} + static bool check_gpte(struct lg_cpu *cpu, pte_t gpte) { if ((pte_flags(gpte) & _PAGE_PSE) || @@ -374,8 +384,14 @@ static pte_t *find_spte(struct lg_cpu *cpu, unsigned long vaddr, bool allocate, * * If we fixed up the fault (ie. we mapped the address), this routine returns * true. Otherwise, it was a real fault and we need to tell the Guest. + * + * There's a corner case: they're trying to access memory between + * pfn_limit and device_limit, which is I/O memory. In this case, we + * return false and set @iomem to the physical address, so the the + * Launcher can handle the instruction manually. */ -bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) +bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode, + unsigned long *iomem) { unsigned long gpte_ptr; pte_t gpte; @@ -383,6 +399,8 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) pmd_t gpmd; pgd_t gpgd; + *iomem = 0; + /* We never demand page the Switcher, so trying is a mistake. */ if (vaddr >= switcher_addr) return false; @@ -459,6 +477,12 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) if ((errcode & 4) && !(pte_flags(gpte) & _PAGE_USER)) return false; + /* If they're accessing io memory, we expect a fault. */ + if (gpte_in_iomem(cpu, gpte)) { + *iomem = (pte_pfn(gpte) << PAGE_SHIFT) | (vaddr & ~PAGE_MASK); + return false; + } + /* * Check that the Guest PTE flags are OK, and the page number is below * the pfn_limit (ie. not mapping the Launcher binary). @@ -553,7 +577,9 @@ static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr) */ void pin_page(struct lg_cpu *cpu, unsigned long vaddr) { - if (!page_writable(cpu, vaddr) && !demand_page(cpu, vaddr, 2)) + unsigned long iomem; + + if (!page_writable(cpu, vaddr) && !demand_page(cpu, vaddr, 2, &iomem)) kill_guest(cpu, "bad stack page %#lx", vaddr); } /*:*/ @@ -928,7 +954,8 @@ static void __guest_set_pte(struct lg_cpu *cpu, int idx, * now. This shaves 10% off a copy-on-write * micro-benchmark. */ - if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) { + if ((pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) + && !gpte_in_iomem(cpu, gpte)) { if (!check_gpte(cpu, gpte)) return; set_pte(spte, diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 42e87bf14113e8..18d841e738bc24 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -362,9 +362,19 @@ static void setup_emulate_insn(struct lg_cpu *cpu) sizeof(cpu->pending.insn)); } +static void setup_iomem_insn(struct lg_cpu *cpu, unsigned long iomem_addr) +{ + cpu->pending.trap = 14; + cpu->pending.addr = iomem_addr; + copy_from_guest(cpu, cpu->pending.insn, cpu->regs->eip, + sizeof(cpu->pending.insn)); +} + /*H:050 Once we've re-enabled interrupts, we look at why the Guest exited. */ void lguest_arch_handle_trap(struct lg_cpu *cpu) { + unsigned long iomem_addr; + switch (cpu->regs->trapnum) { case 13: /* We've intercepted a General Protection Fault. */ /* Hand to Launcher to emulate those pesky IN and OUT insns */ @@ -385,8 +395,15 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) * whether kernel or userspace code. */ if (demand_page(cpu, cpu->arch.last_pagefault, - cpu->regs->errcode)) + cpu->regs->errcode, &iomem_addr)) + return; + + /* Was this an access to memory mapped IO? */ + if (iomem_addr) { + /* Tell Launcher, let it handle it. */ + setup_iomem_insn(cpu, iomem_addr); return; + } /* * OK, it's really not there (or not OK): the Guest needs to diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 485fe13db12e58..02f353989e6c61 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -548,7 +548,8 @@ static void tell_kernel(unsigned long start) { unsigned long args[] = { LHREQ_INITIALIZE, (unsigned long)guest_base, - guest_limit / getpagesize(), start }; + guest_limit / getpagesize(), start, + guest_limit / getpagesize() }; verbose("Guest: %p - %p (%#lx)\n", guest_base, guest_base + guest_limit, guest_limit); lguest_fd = open_or_die("/dev/lguest", O_RDWR); From ee72576c143d8e9081ae1fe8644122454dd323c5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:10 +1030 Subject: [PATCH 285/360] lguest: disable ACPI explicitly. Once we add PCI, it starts trying to manage our interrupts. Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 47ec7f201d27af..aa6e3b4ce29c88 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -1428,6 +1429,9 @@ __init void lguest_init(void) /* Register our very early console. */ virtio_cons_early_init(early_put_chars); + /* Don't let ACPI try to control our PCI interrupts. */ + disable_acpi(); + /* * Last of all, we set the power management poweroff hook to point to * the Guest routine to power off, and the reboot hook to our restart From e1b83e27881cf3153ce420aea853797fed29a9ea Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:10 +1030 Subject: [PATCH 286/360] lguest: Override pcibios_enable_irq/pcibios_disable_irq to our stupid PIC This lets us deliver interrupts for our emulated PCI devices using our dumb PIC, and not emulate an 8259 and PCI irq mapping tables or whatever. Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index aa6e3b4ce29c88..2943ab931671d9 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,7 @@ #include #include /* for struct machine_ops */ #include +#include /*G:010 * Welcome to the Guest! @@ -832,6 +834,24 @@ static struct irq_chip lguest_irq_controller = { .irq_unmask = enable_lguest_irq, }; +static int lguest_enable_irq(struct pci_dev *dev) +{ + u8 line = 0; + + /* We literally use the PCI interrupt line as the irq number. */ + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line); + irq_set_chip_and_handler_name(line, &lguest_irq_controller, + handle_level_irq, "level"); + dev->irq = line; + return 0; +} + +/* We don't do hotplug PCI, so this shouldn't be called. */ +static void lguest_disable_irq(struct pci_dev *dev) +{ + WARN_ON(1); +} + /* * This sets up the Interrupt Descriptor Table (IDT) entry for each hardware * interrupt (except 128, which is used for system calls), and then tells the @@ -1432,6 +1452,10 @@ __init void lguest_init(void) /* Don't let ACPI try to control our PCI interrupts. */ disable_acpi(); + /* We control them ourselves, by overriding these two hooks. */ + pcibios_enable_irq = lguest_enable_irq; + pcibios_disable_irq = lguest_disable_irq; + /* * Last of all, we set the power management poweroff hook to point to * the Guest routine to power off, and the reboot hook to our restart From 0a6bcc183f5377eca07cbf0cf6f4b6cb00e4c1ec Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:11 +1030 Subject: [PATCH 287/360] lguest: add MMIO region allocator in example launcher. This is where we point our PCI BARs, so that we can intercept MMIO accesses. We tell the kernel about it so any faults in this area are directed to us. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 02f353989e6c61..35d7aa90aa24dc 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -92,7 +92,7 @@ static bool verbose; /* The pointer to the start of guest memory. */ static void *guest_base; /* The maximum guest physical address allowed, and maximum possible. */ -static unsigned long guest_limit, guest_max; +static unsigned long guest_limit, guest_max, guest_mmio; /* The /dev/lguest file descriptor. */ static int lguest_fd; @@ -321,6 +321,23 @@ static void *get_pages(unsigned int num) return addr; } +/* Get some bytes which won't be mapped into the guest. */ +static unsigned long get_mmio_region(size_t size) +{ + unsigned long addr = guest_mmio; + size_t i; + + if (!size) + return addr; + + /* Size has to be a power of 2 (and multiple of 16) */ + for (i = 1; i < size; i <<= 1); + + guest_mmio += i; + + return addr; +} + /* * This routine is used to load the kernel or initrd. It tries mmap, but if * that fails (Plan 9's kernel file isn't nicely aligned on page boundaries), @@ -549,9 +566,10 @@ static void tell_kernel(unsigned long start) unsigned long args[] = { LHREQ_INITIALIZE, (unsigned long)guest_base, guest_limit / getpagesize(), start, - guest_limit / getpagesize() }; - verbose("Guest: %p - %p (%#lx)\n", - guest_base, guest_base + guest_limit, guest_limit); + (guest_mmio+getpagesize()-1) / getpagesize() }; + verbose("Guest: %p - %p (%#lx, MMIO %#lx)\n", + guest_base, guest_base + guest_limit, + guest_limit, guest_mmio); lguest_fd = open_or_die("/dev/lguest", O_RDWR); if (write(lguest_fd, args, sizeof(args)) < 0) err(1, "Writing to /dev/lguest"); @@ -2079,7 +2097,7 @@ int main(int argc, char *argv[]) guest_base = map_zeroed_pages(mem / getpagesize() + DEVICE_PAGES); guest_limit = mem; - guest_max = mem + DEVICE_PAGES*getpagesize(); + guest_max = guest_mmio = mem + DEVICE_PAGES*getpagesize(); devices.descpage = get_pages(1); break; } From 6a54f9ab0d65a2095de50160b8ca7ce6469aaac0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:11 +1030 Subject: [PATCH 288/360] lguest: decode mmio accesses for PCI in example launcher. We don't do anything with them yet (emulate_mmio_write and emulate_mmio_read are stubs), but we decode the instructions and search for the device they're hitting. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 167 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 35d7aa90aa24dc..e52a3571076ac1 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -99,6 +99,9 @@ static int lguest_fd; /* a per-cpu variable indicating whose vcpu is currently running */ static unsigned int __thread cpu_id; +/* 5 bit device number in the PCI_CONFIG_ADDR => 32 only */ +#define MAX_PCI_DEVICES 32 + /* This is our list of devices. */ struct device_list { /* Counter to assign interrupt numbers. */ @@ -114,6 +117,9 @@ struct device_list { struct device *dev; /* And a pointer to the last device for easy append. */ struct device *lastdev; + + /* PCI devices. */ + struct device *pci[MAX_PCI_DEVICES]; }; /* The list of Guest devices, based on command line arguments. */ @@ -140,6 +146,10 @@ struct device { /* Is it operational */ bool running; + /* PCI MMIO resources (all in BAR0) */ + size_t mmio_size; + u32 mmio_addr; + /* Device-specific data. */ void *priv; }; @@ -1197,6 +1207,77 @@ static void setreg_off(size_t offset, u32 val) err(1, "Setting register %u", offset); } +/* Get register by instruction encoding */ +static u32 getreg_num(unsigned regnum, u32 mask) +{ + /* 8 bit ops use regnums 4-7 for high parts of word */ + if (mask == 0xFF && (regnum & 0x4)) + return getreg_num(regnum & 0x3, 0xFFFF) >> 8; + + switch (regnum) { + case 0: return getreg(eax) & mask; + case 1: return getreg(ecx) & mask; + case 2: return getreg(edx) & mask; + case 3: return getreg(ebx) & mask; + case 4: return getreg(esp) & mask; + case 5: return getreg(ebp) & mask; + case 6: return getreg(esi) & mask; + case 7: return getreg(edi) & mask; + } + abort(); +} + +/* Set register by instruction encoding */ +static void setreg_num(unsigned regnum, u32 val, u32 mask) +{ + /* Don't try to set bits out of range */ + assert(~(val & ~mask)); + + /* 8 bit ops use regnums 4-7 for high parts of word */ + if (mask == 0xFF && (regnum & 0x4)) { + /* Construct the 16 bits we want. */ + val = (val << 8) | getreg_num(regnum & 0x3, 0xFF); + setreg_num(regnum & 0x3, val, 0xFFFF); + return; + } + + switch (regnum) { + case 0: setreg(eax, val | (getreg(eax) & ~mask)); return; + case 1: setreg(ecx, val | (getreg(ecx) & ~mask)); return; + case 2: setreg(edx, val | (getreg(edx) & ~mask)); return; + case 3: setreg(ebx, val | (getreg(ebx) & ~mask)); return; + case 4: setreg(esp, val | (getreg(esp) & ~mask)); return; + case 5: setreg(ebp, val | (getreg(ebp) & ~mask)); return; + case 6: setreg(esi, val | (getreg(esi) & ~mask)); return; + case 7: setreg(edi, val | (getreg(edi) & ~mask)); return; + } + abort(); +} + +/* Get bytes of displacement appended to instruction, from r/m encoding */ +static u32 insn_displacement_len(u8 mod_reg_rm) +{ + /* Switch on the mod bits */ + switch (mod_reg_rm >> 6) { + case 0: + /* If mod == 0, and r/m == 101, 16-bit displacement follows */ + if ((mod_reg_rm & 0x7) == 0x5) + return 2; + /* Normally, mod == 0 means no literal displacement */ + return 0; + case 1: + /* One byte displacement */ + return 1; + case 2: + /* Four byte displacement */ + return 4; + case 3: + /* Register mode */ + return 0; + } + abort(); +} + static void emulate_insn(const u8 insn[]) { unsigned long args[] = { LHREQ_TRAP, 13 }; @@ -1310,6 +1391,88 @@ static void emulate_insn(const u8 insn[]) err(1, "Reinjecting trap 13 for fault at %#x", getreg(eip)); } +static struct device *find_mmio_region(unsigned long paddr, u32 *off) +{ + unsigned int i; + + for (i = 1; i < MAX_PCI_DEVICES; i++) { + struct device *d = devices.pci[i]; + + if (!d) + continue; + if (paddr < d->mmio_addr) + continue; + if (paddr >= d->mmio_addr + d->mmio_size) + continue; + *off = paddr - d->mmio_addr; + return d; + } + return NULL; +} + +static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) +{ +} + +static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) +{ + return 0xFFFFFFFF; +} + +static void emulate_mmio(unsigned long paddr, const u8 *insn) +{ + u32 val, off, mask = 0xFFFFFFFF, insnlen = 0; + struct device *d = find_mmio_region(paddr, &off); + unsigned long args[] = { LHREQ_TRAP, 14 }; + + if (!d) { + warnx("MMIO touching %#08lx (not a device)", paddr); + goto reinject; + } + + /* Prefix makes it a 16 bit op */ + if (insn[0] == 0x66) { + mask = 0xFFFF; + insnlen++; + } + + /* iowrite */ + if (insn[insnlen] == 0x89) { + /* Next byte is r/m byte: bits 3-5 are register. */ + val = getreg_num((insn[insnlen+1] >> 3) & 0x7, mask); + emulate_mmio_write(d, off, val, mask); + insnlen += 2 + insn_displacement_len(insn[insnlen+1]); + } else if (insn[insnlen] == 0x8b) { /* ioread */ + /* Next byte is r/m byte: bits 3-5 are register. */ + val = emulate_mmio_read(d, off, mask); + setreg_num((insn[insnlen+1] >> 3) & 0x7, val, mask); + insnlen += 2 + insn_displacement_len(insn[insnlen+1]); + } else if (insn[0] == 0x88) { /* 8-bit iowrite */ + mask = 0xff; + /* Next byte is r/m byte: bits 3-5 are register. */ + val = getreg_num((insn[1] >> 3) & 0x7, mask); + emulate_mmio_write(d, off, val, mask); + insnlen = 2 + insn_displacement_len(insn[1]); + } else if (insn[0] == 0x8a) { /* 8-bit ioread */ + mask = 0xff; + val = emulate_mmio_read(d, off, mask); + setreg_num((insn[1] >> 3) & 0x7, val, mask); + insnlen = 2 + insn_displacement_len(insn[1]); + } else { + warnx("Unknown MMIO instruction touching %#08lx:" + " %02x %02x %02x %02x at %u", + paddr, insn[0], insn[1], insn[2], insn[3], getreg(eip)); + reinject: + /* Inject trap into Guest. */ + if (write(lguest_fd, args, sizeof(args)) < 0) + err(1, "Reinjecting trap 14 for fault at %#x", + getreg(eip)); + return; + } + + /* Finally, we've "done" the instruction, so move past it. */ + setreg(eip, getreg(eip) + insnlen); +} /*L:190 * Device Setup @@ -2004,6 +2167,10 @@ static void __attribute__((noreturn)) run_guest(void) verbose("Emulating instruction at %#x\n", getreg(eip)); emulate_insn(notify.insn); + } else if (notify.trap == 14) { + verbose("Emulating MMIO at %#x\n", + getreg(eip)); + emulate_mmio(notify.addr, notify.insn); } else errx(1, "Unknown trap %i addr %#08x\n", notify.trap, notify.addr); From d7fbf6e95e2c5e7ef97c463a97499d7a2341fb09 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:11 +1030 Subject: [PATCH 289/360] lguest: add PCI config space emulation to example launcher. This handles ioport 0xCF8 and 0xCFC accesses, which are used to read/write PCI device config space. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 216 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 5 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index e52a3571076ac1..0f29657fc0655d 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -42,6 +42,7 @@ #include #include #include +#include #ifndef VIRTIO_F_ANY_LAYOUT #define VIRTIO_F_ANY_LAYOUT 27 @@ -125,6 +126,21 @@ struct device_list { /* The list of Guest devices, based on command line arguments. */ static struct device_list devices; +/* This is the layout (little-endian) of the PCI config space. */ +struct pci_config { + u16 vendor_id, device_id; + u16 command, status; + u8 revid, prog_if, subclass, class; + u8 cacheline_size, lat_timer, header_type, bist; + u32 bar[6]; + u32 cardbus_cis_ptr; + u16 subsystem_vendor_id, subsystem_device_id; + u32 expansion_rom_addr; + u8 capabilities, reserved1[3]; + u32 reserved2; + u8 irq_line, irq_pin, min_grant, max_latency; +}; + /* The device structure describes a single device. */ struct device { /* The linked-list pointer. */ @@ -146,6 +162,15 @@ struct device { /* Is it operational */ bool running; + /* PCI configuration */ + union { + struct pci_config config; + u32 config_words[sizeof(struct pci_config) / sizeof(u32)]; + }; + + /* Device-specific config hangs off the end of this. */ + struct virtio_pci_mmio *mmio; + /* PCI MMIO resources (all in BAR0) */ size_t mmio_size; u32 mmio_addr; @@ -1173,6 +1198,169 @@ static void handle_output(unsigned long addr) strnlen(from_guest_phys(addr), guest_limit - addr)); } +/*L:217 + * We do PCI. This is mainly done to let us test the kernel virtio PCI + * code. + */ + +/* The IO ports used to read the PCI config space. */ +#define PCI_CONFIG_ADDR 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +/* + * Not really portable, but does help readability: this is what the Guest + * writes to the PCI_CONFIG_ADDR IO port. + */ +union pci_config_addr { + struct { + unsigned mbz: 2; + unsigned offset: 6; + unsigned funcnum: 3; + unsigned devnum: 5; + unsigned busnum: 8; + unsigned reserved: 7; + unsigned enabled : 1; + } bits; + u32 val; +}; + +/* + * We cache what they wrote to the address port, so we know what they're + * talking about when they access the data port. + */ +static union pci_config_addr pci_config_addr; + +static struct device *find_pci_device(unsigned int index) +{ + return devices.pci[index]; +} + +/* PCI can do 1, 2 and 4 byte reads; we handle that here. */ +static void ioread(u16 off, u32 v, u32 mask, u32 *val) +{ + assert(off < 4); + assert(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF); + *val = (v >> (off * 8)) & mask; +} + +/* PCI can do 1, 2 and 4 byte writes; we handle that here. */ +static void iowrite(u16 off, u32 v, u32 mask, u32 *dst) +{ + assert(off < 4); + assert(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF); + *dst &= ~(mask << (off * 8)); + *dst |= (v & mask) << (off * 8); +} + +/* + * Where PCI_CONFIG_DATA accesses depends on the previous write to + * PCI_CONFIG_ADDR. + */ +static struct device *dev_and_reg(u32 *reg) +{ + if (!pci_config_addr.bits.enabled) + return NULL; + + if (pci_config_addr.bits.funcnum != 0) + return NULL; + + if (pci_config_addr.bits.busnum != 0) + return NULL; + + if (pci_config_addr.bits.offset * 4 >= sizeof(struct pci_config)) + return NULL; + + *reg = pci_config_addr.bits.offset; + return find_pci_device(pci_config_addr.bits.devnum); +} + +/* Is this accessing the PCI config address port?. */ +static bool is_pci_addr_port(u16 port) +{ + return port >= PCI_CONFIG_ADDR && port < PCI_CONFIG_ADDR + 4; +} + +static bool pci_addr_iowrite(u16 port, u32 mask, u32 val) +{ + iowrite(port - PCI_CONFIG_ADDR, val, mask, + &pci_config_addr.val); + verbose("PCI%s: %#x/%x: bus %u dev %u func %u reg %u\n", + pci_config_addr.bits.enabled ? "" : " DISABLED", + val, mask, + pci_config_addr.bits.busnum, + pci_config_addr.bits.devnum, + pci_config_addr.bits.funcnum, + pci_config_addr.bits.offset); + return true; +} + +static void pci_addr_ioread(u16 port, u32 mask, u32 *val) +{ + ioread(port - PCI_CONFIG_ADDR, pci_config_addr.val, mask, val); +} + +/* Is this accessing the PCI config data port?. */ +static bool is_pci_data_port(u16 port) +{ + return port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + 4; +} + +static bool pci_data_iowrite(u16 port, u32 mask, u32 val) +{ + u32 reg, portoff; + struct device *d = dev_and_reg(®); + + /* Complain if they don't belong to a device. */ + if (!d) + return false; + + /* They can do 1 byte writes, etc. */ + portoff = port - PCI_CONFIG_DATA; + + /* + * PCI uses a weird way to determine the BAR size: the OS + * writes all 1's, and sees which ones stick. + */ + if (&d->config_words[reg] == &d->config.bar[0]) { + int i; + + iowrite(portoff, val, mask, &d->config.bar[0]); + for (i = 0; (1 << i) < d->mmio_size; i++) + d->config.bar[0] &= ~(1 << i); + return true; + } else if ((&d->config_words[reg] > &d->config.bar[0] + && &d->config_words[reg] <= &d->config.bar[6]) + || &d->config_words[reg] == &d->config.expansion_rom_addr) { + /* Allow writing to any other BAR, or expansion ROM */ + iowrite(portoff, val, mask, &d->config_words[reg]); + return true; + /* We let them overide latency timer and cacheline size */ + } else if (&d->config_words[reg] == (void *)&d->config.cacheline_size) { + /* Only let them change the first two fields. */ + if (mask == 0xFFFFFFFF) + mask = 0xFFFF; + iowrite(portoff, val, mask, &d->config_words[reg]); + return true; + } else if (&d->config_words[reg] == (void *)&d->config.command + && mask == 0xFFFF) { + /* Ignore command writes. */ + return true; + } + + /* Complain about other writes. */ + return false; +} + +static void pci_data_ioread(u16 port, u32 mask, u32 *val) +{ + u32 reg; + struct device *d = dev_and_reg(®); + + if (!d) + return; + ioread(port - PCI_CONFIG_DATA, d->config_words[reg], mask, val); +} + /*L:216 * This is where we emulate a handful of Guest instructions. It's ugly * and we used to do it in the kernel but it grew over time. @@ -1284,7 +1472,7 @@ static void emulate_insn(const u8 insn[]) unsigned int insnlen = 0, in = 0, small_operand = 0, byte_access; unsigned int eax, port, mask; /* - * We always return all-ones on IO port reads, which traditionally + * Default is to return all-ones on IO port reads, which traditionally * means "there's nothing there". */ u32 val = 0xFFFFFFFF; @@ -1359,10 +1547,6 @@ static void emulate_insn(const u8 insn[]) else mask = 0xFFFFFFFF; - /* This is the PS/2 keyboard status; 1 means ready for output */ - if (port == 0x64) - val = 1; - /* * If it was an "IN" instruction, they expect the result to be read * into %eax, so we change %eax. @@ -1370,12 +1554,30 @@ static void emulate_insn(const u8 insn[]) eax = getreg(eax); if (in) { + /* This is the PS/2 keyboard status; 1 means ready for output */ + if (port == 0x64) + val = 1; + else if (is_pci_addr_port(port)) + pci_addr_ioread(port, mask, &val); + else if (is_pci_data_port(port)) + pci_data_ioread(port, mask, &val); + /* Clear the bits we're about to read */ eax &= ~mask; /* Copy bits in from val. */ eax |= val & mask; /* Now update the register. */ setreg(eax, eax); + } else { + if (is_pci_addr_port(port)) { + if (!pci_addr_iowrite(port, mask, eax)) + goto bad_io; + } else if (is_pci_data_port(port)) { + if (!pci_data_iowrite(port, mask, eax)) + goto bad_io; + } + /* There are many other ports, eg. CMOS clock, serial + * and parallel ports, so we ignore them all. */ } verbose("IO %s of %x to %u: %#08x\n", @@ -1385,6 +1587,10 @@ static void emulate_insn(const u8 insn[]) setreg(eip, getreg(eip) + insnlen); return; +bad_io: + warnx("Attempt to %s port %u (%#x mask)", + in ? "read from" : "write to", port, mask); + no_emulate: /* Inject trap into Guest. */ if (write(lguest_fd, args, sizeof(args)) < 0) From 93153077107ecfbf35a3412f6220521e8d8c14ba Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:11 +1030 Subject: [PATCH 290/360] lguest: implement virtio-PCI MMIO accesses. For each device, We need to include the vendor capabilities to demark where virtio common, notification and ISR regions are (we put them all in BAR0). We need to handle the switching of the virtqueues using the accessors. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 492 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 490 insertions(+), 2 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 0f29657fc0655d..eafdaf2a14c442 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -63,12 +63,16 @@ typedef uint16_t u16; typedef uint8_t u8; /*:*/ -#include +#define VIRTIO_PCI_NO_LEGACY + +/* Use in-kernel ones, which defines VIRTIO_F_VERSION_1 */ +#include "../../include/uapi/linux/virtio_config.h" #include #include #include #include #include +#include "../../include/uapi/linux/virtio_pci.h" #include #include "../../include/linux/lguest_launcher.h" @@ -126,6 +130,19 @@ struct device_list { /* The list of Guest devices, based on command line arguments. */ static struct device_list devices; +struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; + u32 window; /* Data for BAR access. */ +}; + +struct virtio_pci_mmio { + struct virtio_pci_common_cfg cfg; + u16 notify; + u8 isr; + u8 padding; + /* Device-specific configuration follows this. */ +}; + /* This is the layout (little-endian) of the PCI config space. */ struct pci_config { u16 vendor_id, device_id; @@ -139,6 +156,14 @@ struct pci_config { u8 capabilities, reserved1[3]; u32 reserved2; u8 irq_line, irq_pin, min_grant, max_latency; + + /* Now, this is the linked capability list. */ + struct virtio_pci_cap common; + struct virtio_pci_notify_cap notify; + struct virtio_pci_cap isr; + struct virtio_pci_cap device; + /* FIXME: Implement this! */ + struct virtio_pci_cfg_cap cfg_access; }; /* The device structure describes a single device. */ @@ -168,6 +193,9 @@ struct device { u32 config_words[sizeof(struct pci_config) / sizeof(u32)]; }; + /* Features we offer, and those accepted. */ + u64 features, features_accepted; + /* Device-specific config hangs off the end of this. */ struct virtio_pci_mmio *mmio; @@ -192,6 +220,9 @@ struct virtqueue { /* The actual ring of buffers. */ struct vring vring; + /* The information about this virtqueue (we only use queue_size on) */ + struct virtio_pci_common_cfg pci_config; + /* Last available index we saw. */ u16 last_avail_idx; @@ -680,6 +711,10 @@ static void trigger_irq(struct virtqueue *vq) return; } + /* For a PCI device, set isr to 1 (queue interrupt pending) */ + if (vq->dev->mmio) + vq->dev->mmio->isr = 0x1; + /* Send the Guest an interrupt tell them we used something up. */ if (write(lguest_fd, buf, sizeof(buf)) != 0) err(1, "Triggering irq %i", vq->config.irq); @@ -1616,13 +1651,264 @@ static struct device *find_mmio_region(unsigned long paddr, u32 *off) return NULL; } +/* FIXME: Use vq array. */ +static struct virtqueue *vq_by_num(struct device *d, u32 num) +{ + struct virtqueue *vq = d->vq; + + while (num-- && vq) + vq = vq->next; + + return vq; +} + +static void save_vq_config(const struct virtio_pci_common_cfg *cfg, + struct virtqueue *vq) +{ + vq->pci_config = *cfg; +} + +static void restore_vq_config(struct virtio_pci_common_cfg *cfg, + struct virtqueue *vq) +{ + /* Only restore the per-vq part */ + size_t off = offsetof(struct virtio_pci_common_cfg, queue_size); + + memcpy((void *)cfg + off, (void *)&vq->pci_config + off, + sizeof(*cfg) - off); +} + +/* + * When they enable the virtqueue, we check that their setup is valid. + */ +static void enable_virtqueue(struct device *d, struct virtqueue *vq) +{ + /* + * Create stack for thread. Since the stack grows upwards, we point + * the stack pointer to the end of this region. + */ + char *stack = malloc(32768); + + /* Because lguest is 32 bit, all the descriptor high bits must be 0 */ + if (vq->pci_config.queue_desc_hi + || vq->pci_config.queue_avail_hi + || vq->pci_config.queue_used_hi) + errx(1, "%s: invalid 64-bit queue address", d->name); + + /* Initialize the virtqueue and check they're all in range. */ + vq->vring.num = vq->pci_config.queue_size; + vq->vring.desc = check_pointer(vq->pci_config.queue_desc_lo, + sizeof(*vq->vring.desc) * vq->vring.num); + vq->vring.avail = check_pointer(vq->pci_config.queue_avail_lo, + sizeof(*vq->vring.avail) + + (sizeof(vq->vring.avail->ring[0]) + * vq->vring.num)); + vq->vring.used = check_pointer(vq->pci_config.queue_used_lo, + sizeof(*vq->vring.used) + + (sizeof(vq->vring.used->ring[0]) + * vq->vring.num)); + + + /* Create a zero-initialized eventfd. */ + vq->eventfd = eventfd(0, 0); + if (vq->eventfd < 0) + err(1, "Creating eventfd"); + + /* + * CLONE_VM: because it has to access the Guest memory, and SIGCHLD so + * we get a signal if it dies. + */ + vq->thread = clone(do_thread, stack + 32768, CLONE_VM | SIGCHLD, vq); + if (vq->thread == (pid_t)-1) + err(1, "Creating clone"); +} + +static void reset_pci_device(struct device *dev) +{ + /* FIXME */ +} + static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) { + struct virtqueue *vq; + + switch (off) { + case offsetof(struct virtio_pci_mmio, cfg.device_feature_select): + if (val == 0) + d->mmio->cfg.device_feature = d->features; + else if (val == 1) + d->mmio->cfg.device_feature = (d->features >> 32); + else + d->mmio->cfg.device_feature = 0; + goto write_through32; + case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): + if (val > 1) + errx(1, "%s: Unexpected driver select %u", + d->name, val); + goto write_through32; + case offsetof(struct virtio_pci_mmio, cfg.guest_feature): + if (d->mmio->cfg.guest_feature_select == 0) { + d->features_accepted &= ~((u64)0xFFFFFFFF); + d->features_accepted |= val; + } else { + assert(d->mmio->cfg.guest_feature_select == 1); + d->features_accepted &= ((u64)0xFFFFFFFF << 32); + d->features_accepted |= ((u64)val) << 32; + } + if (d->features_accepted & ~d->features) + errx(1, "%s: over-accepted features %#llx of %#llx", + d->name, d->features_accepted, d->features); + goto write_through32; + case offsetof(struct virtio_pci_mmio, cfg.device_status): + verbose("%s: device status -> %#x\n", d->name, val); + if (val == 0) + reset_pci_device(d); + goto write_through8; + case offsetof(struct virtio_pci_mmio, cfg.queue_select): + vq = vq_by_num(d, val); + /* Out of range? Return size 0 */ + if (!vq) { + d->mmio->cfg.queue_size = 0; + goto write_through16; + } + /* Save registers for old vq, if it was a valid vq */ + if (d->mmio->cfg.queue_size) + save_vq_config(&d->mmio->cfg, + vq_by_num(d, d->mmio->cfg.queue_select)); + /* Restore the registers for the queue they asked for */ + restore_vq_config(&d->mmio->cfg, vq); + goto write_through16; + case offsetof(struct virtio_pci_mmio, cfg.queue_size): + if (val & (val-1)) + errx(1, "%s: invalid queue size %u\n", d->name, val); + if (d->mmio->cfg.queue_enable) + errx(1, "%s: changing queue size on live device", + d->name); + goto write_through16; + case offsetof(struct virtio_pci_mmio, cfg.queue_msix_vector): + errx(1, "%s: attempt to set MSIX vector to %u", + d->name, val); + case offsetof(struct virtio_pci_mmio, cfg.queue_enable): + if (val != 1) + errx(1, "%s: setting queue_enable to %u", d->name, val); + d->mmio->cfg.queue_enable = val; + save_vq_config(&d->mmio->cfg, + vq_by_num(d, d->mmio->cfg.queue_select)); + enable_virtqueue(d, vq_by_num(d, d->mmio->cfg.queue_select)); + goto write_through16; + case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off): + errx(1, "%s: attempt to write to queue_notify_off", d->name); + case offsetof(struct virtio_pci_mmio, cfg.queue_desc_lo): + case offsetof(struct virtio_pci_mmio, cfg.queue_desc_hi): + case offsetof(struct virtio_pci_mmio, cfg.queue_avail_lo): + case offsetof(struct virtio_pci_mmio, cfg.queue_avail_hi): + case offsetof(struct virtio_pci_mmio, cfg.queue_used_lo): + case offsetof(struct virtio_pci_mmio, cfg.queue_used_hi): + if (d->mmio->cfg.queue_enable) + errx(1, "%s: changing queue on live device", + d->name); + goto write_through32; + case offsetof(struct virtio_pci_mmio, notify): + vq = vq_by_num(d, val); + if (!vq) + errx(1, "Invalid vq notification on %u", val); + /* Notify the process handling this vq by adding 1 to eventfd */ + write(vq->eventfd, "\1\0\0\0\0\0\0\0", 8); + goto write_through16; + case offsetof(struct virtio_pci_mmio, isr): + errx(1, "%s: Unexpected write to isr", d->name); + default: + errx(1, "%s: Unexpected write to offset %u", d->name, off); + } + +write_through32: + if (mask != 0xFFFFFFFF) { + errx(1, "%s: non-32-bit write to offset %u (%#x)", + d->name, off, getreg(eip)); + return; + } + memcpy((char *)d->mmio + off, &val, 4); + return; + +write_through16: + if (mask != 0xFFFF) + errx(1, "%s: non-16-bit (%#x) write to offset %u (%#x)", + d->name, mask, off, getreg(eip)); + memcpy((char *)d->mmio + off, &val, 2); + return; + +write_through8: + if (mask != 0xFF) + errx(1, "%s: non-8-bit write to offset %u (%#x)", + d->name, off, getreg(eip)); + memcpy((char *)d->mmio + off, &val, 1); + return; } static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) { - return 0xFFFFFFFF; + u8 isr; + u32 val = 0; + + switch (off) { + case offsetof(struct virtio_pci_mmio, cfg.device_feature_select): + case offsetof(struct virtio_pci_mmio, cfg.device_feature): + case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): + case offsetof(struct virtio_pci_mmio, cfg.guest_feature): + goto read_through32; + case offsetof(struct virtio_pci_mmio, cfg.msix_config): + errx(1, "%s: read of msix_config", d->name); + case offsetof(struct virtio_pci_mmio, cfg.num_queues): + goto read_through16; + case offsetof(struct virtio_pci_mmio, cfg.device_status): + case offsetof(struct virtio_pci_mmio, cfg.config_generation): + goto read_through8; + case offsetof(struct virtio_pci_mmio, notify): + goto read_through16; + case offsetof(struct virtio_pci_mmio, isr): + if (mask != 0xFF) + errx(1, "%s: non-8-bit read from offset %u (%#x)", + d->name, off, getreg(eip)); + /* Read resets the isr */ + isr = d->mmio->isr; + d->mmio->isr = 0; + return isr; + case offsetof(struct virtio_pci_mmio, padding): + errx(1, "%s: read from padding (%#x)", + d->name, getreg(eip)); + default: + /* Read from device config space, beware unaligned overflow */ + if (off > d->mmio_size - 4) + errx(1, "%s: read past end (%#x)", + d->name, getreg(eip)); + if (mask == 0xFFFFFFFF) + goto read_through32; + else if (mask == 0xFFFF) + goto read_through16; + else + goto read_through8; + } + +read_through32: + if (mask != 0xFFFFFFFF) + errx(1, "%s: non-32-bit read to offset %u (%#x)", + d->name, off, getreg(eip)); + memcpy(&val, (char *)d->mmio + off, 4); + return val; + +read_through16: + if (mask != 0xFFFF) + errx(1, "%s: non-16-bit read to offset %u (%#x)", + d->name, off, getreg(eip)); + memcpy(&val, (char *)d->mmio + off, 2); + return val; + +read_through8: + if (mask != 0xFF) + errx(1, "%s: non-8-bit read to offset %u (%#x)", + d->name, off, getreg(eip)); + memcpy(&val, (char *)d->mmio + off, 1); + return val; } static void emulate_mmio(unsigned long paddr, const u8 *insn) @@ -1783,6 +2069,42 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs, *i = vq; } +static void add_pci_virtqueue(struct device *dev, + void (*service)(struct virtqueue *)) +{ + struct virtqueue **i, *vq = malloc(sizeof(*vq)); + + /* Initialize the virtqueue */ + vq->next = NULL; + vq->last_avail_idx = 0; + vq->dev = dev; + + /* + * This is the routine the service thread will run, and its Process ID + * once it's running. + */ + vq->service = service; + vq->thread = (pid_t)-1; + + /* Initialize the configuration. */ + vq->pci_config.queue_size = VIRTQUEUE_NUM; + vq->pci_config.queue_enable = 0; + vq->pci_config.queue_notify_off = 0; + + /* Add one to the number of queues */ + vq->dev->mmio->cfg.num_queues++; + + /* FIXME: Do irq per virtqueue, not per device. */ + vq->config.irq = vq->dev->config.irq_line; + + /* + * Add to tail of list, so dev->vq is first vq, dev->vq->next is + * second. + */ + for (i = &dev->vq; *i; i = &(*i)->next); + *i = vq; +} + /* * The first half of the feature bitmask is for us to advertise features. The * second half is for the Guest to accept features. @@ -1800,6 +2122,11 @@ static void add_feature(struct device *dev, unsigned bit) features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT)); } +static void add_pci_feature(struct device *dev, unsigned bit) +{ + dev->features |= (1ULL << bit); +} + /* * This routine sets the configuration fields for an existing device's * descriptor. It only works for the last device, but that's OK because that's @@ -1819,6 +2146,139 @@ static void set_config(struct device *dev, unsigned len, const void *conf) assert(dev->desc->config_len == len); } +/* For devices with no config. */ +static void no_device_config(struct device *dev) +{ + dev->mmio_addr = get_mmio_region(dev->mmio_size); + + dev->config.bar[0] = dev->mmio_addr; + /* Bottom 4 bits must be zero */ + assert(~(dev->config.bar[0] & 0xF)); +} + +/* This puts the device config into BAR0 */ +static void set_device_config(struct device *dev, const void *conf, size_t len) +{ + /* Set up BAR 0 */ + dev->mmio_size += len; + dev->mmio = realloc(dev->mmio, dev->mmio_size); + memcpy(dev->mmio + 1, conf, len); + + /* Hook up device cfg */ + dev->config.cfg_access.cap.cap_next + = offsetof(struct pci_config, device); + + /* Fix up device cfg field length. */ + dev->config.device.length = len; + + /* The rest is the same as the no-config case */ + no_device_config(dev); +} + +static void init_cap(struct virtio_pci_cap *cap, size_t caplen, int type, + size_t bar_offset, size_t bar_bytes, u8 next) +{ + cap->cap_vndr = PCI_CAP_ID_VNDR; + cap->cap_next = next; + cap->cap_len = caplen; + cap->cfg_type = type; + cap->bar = 0; + memset(cap->padding, 0, sizeof(cap->padding)); + cap->offset = bar_offset; + cap->length = bar_bytes; +} + +/* + * This sets up the pci_config structure, as defined in the virtio 1.0 + * standard (and PCI standard). + */ +static void init_pci_config(struct pci_config *pci, u16 type, + u8 class, u8 subclass) +{ + size_t bar_offset, bar_len; + + /* Save typing: most thing are happy being zero. */ + memset(pci, 0, sizeof(*pci)); + + /* 4.1.2.1: Devices MUST have the PCI Vendor ID 0x1AF4 */ + pci->vendor_id = 0x1AF4; + /* 4.1.2.1: ... PCI Device ID calculated by adding 0x1040 ... */ + pci->device_id = 0x1040 + type; + + /* + * PCI have specific codes for different types of devices. + * Linux doesn't care, but it's a good clue for people looking + * at the device. + * + * eg : + * VIRTIO_ID_CONSOLE: class = 0x07, subclass = 0x00 + * VIRTIO_ID_NET: class = 0x02, subclass = 0x00 + * VIRTIO_ID_BLOCK: class = 0x01, subclass = 0x80 + * VIRTIO_ID_RNG: class = 0xff, subclass = 0 + */ + pci->class = class; + pci->subclass = subclass; + + /* + * 4.1.2.1 Non-transitional devices SHOULD have a PCI Revision + * ID of 1 or higher + */ + pci->revid = 1; + + /* + * 4.1.2.1 Non-transitional devices SHOULD have a PCI + * Subsystem Device ID of 0x40 or higher. + */ + pci->subsystem_device_id = 0x40; + + /* We use our dummy interrupt controller, and irq_line is the irq */ + pci->irq_line = devices.next_irq++; + pci->irq_pin = 0; + + /* Support for extended capabilities. */ + pci->status = (1 << 4); + + /* Link them in. */ + pci->capabilities = offsetof(struct pci_config, common); + + bar_offset = offsetof(struct virtio_pci_mmio, cfg); + bar_len = sizeof(((struct virtio_pci_mmio *)0)->cfg); + init_cap(&pci->common, sizeof(pci->common), VIRTIO_PCI_CAP_COMMON_CFG, + bar_offset, bar_len, + offsetof(struct pci_config, notify)); + + bar_offset += bar_len; + bar_len = sizeof(((struct virtio_pci_mmio *)0)->notify); + /* FIXME: Use a non-zero notify_off, for per-queue notification? */ + init_cap(&pci->notify.cap, sizeof(pci->notify), + VIRTIO_PCI_CAP_NOTIFY_CFG, + bar_offset, bar_len, + offsetof(struct pci_config, isr)); + + bar_offset += bar_len; + bar_len = sizeof(((struct virtio_pci_mmio *)0)->isr); + init_cap(&pci->isr, sizeof(pci->isr), + VIRTIO_PCI_CAP_ISR_CFG, + bar_offset, bar_len, + offsetof(struct pci_config, cfg_access)); + + /* This doesn't have any presence in the BAR */ + init_cap(&pci->cfg_access.cap, sizeof(pci->cfg_access), + VIRTIO_PCI_CAP_PCI_CFG, + 0, 0, 0); + + bar_offset += bar_len + sizeof(((struct virtio_pci_mmio *)0)->padding); + assert(bar_offset == sizeof(struct virtio_pci_mmio)); + + /* + * This gets sewn in and length set in set_device_config(). + * Some devices don't have a device configuration interface, so + * we never expose this if we don't call set_device_config(). + */ + init_cap(&pci->device, sizeof(pci->device), VIRTIO_PCI_CAP_DEVICE_CFG, + bar_offset, 0, 0); +} + /* * This routine does all the creation and setup of a new device, including * calling new_dev_desc() to allocate the descriptor and device memory. We @@ -1854,6 +2314,34 @@ static struct device *new_device(const char *name, u16 type) return dev; } +static struct device *new_pci_device(const char *name, u16 type, + u8 class, u8 subclass) +{ + struct device *dev = malloc(sizeof(*dev)); + + /* Now we populate the fields one at a time. */ + dev->desc = NULL; + dev->name = name; + dev->vq = NULL; + dev->feature_len = 0; + dev->num_vq = 0; + dev->running = false; + dev->next = NULL; + dev->mmio_size = sizeof(struct virtio_pci_mmio); + dev->mmio = calloc(1, dev->mmio_size); + dev->features = (u64)1 << VIRTIO_F_VERSION_1; + dev->features_accepted = 0; + + if (devices.device_num + 1 >= 32) + errx(1, "Can only handle 31 PCI devices"); + + init_pci_config(&dev->config, type, class, subclass); + assert(!devices.pci[devices.device_num+1]); + devices.pci[++devices.device_num] = dev; + + return dev; +} + /* * Our first setup routine is the console. It's a fairly simple device, but * UNIX tty handling makes it uglier than it could be. From 3e0e5f2640d3b8f8f958e72f1577f1e323e11da6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:11 +1030 Subject: [PATCH 291/360] lguest: fix failure to find linux/virtio_types.h We want to use the local kernel headers, but -I../../include/uapi leads us into a world of hurt. Instead we create a dummy include/ dir with symlinks. If we just use #include "../../include/uapi/linux/virtio_blk.h" we get: ../../include/uapi/linux/virtio_blk.h:31:32: fatal error: linux/virtio_types.h: No such file or directory #include Signed-off-by: Rusty Russell --- tools/lguest/Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/lguest/Makefile b/tools/lguest/Makefile index 97bca4871ea34d..a107b5e4da1341 100644 --- a/tools/lguest/Makefile +++ b/tools/lguest/Makefile @@ -1,7 +1,13 @@ # This creates the demonstration utility "lguest" which runs a Linux guest. -CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE +CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE -Iinclude all: lguest +include/linux/virtio_types.h: ../../include/uapi/linux/virtio_types.h + mkdir -p include/linux 2>&1 || true + ln -sf ../../../../include/uapi/linux/virtio_types.h $@ + +lguest: include/linux/virtio_types.h + clean: rm -f lguest From 8e70946943961cf5bb9be3a0cf12bd0da7a7cb0d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:12 +1030 Subject: [PATCH 292/360] lguest: add a dummy PCI host bridge. Otherwise Linux fails to find the bus. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index eafdaf2a14c442..c8930bc5ce996c 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1238,6 +1238,17 @@ static void handle_output(unsigned long addr) * code. */ +/* Linux expects a PCI host bridge: ours is a dummy, and first on the bus. */ +static struct device pci_host_bridge; + +static void init_pci_host_bridge(void) +{ + pci_host_bridge.name = "PCI Host Bridge"; + pci_host_bridge.config.class = 0x06; /* bridge */ + pci_host_bridge.config.subclass = 0; /* host bridge */ + devices.pci[0] = &pci_host_bridge; +} + /* The IO ports used to read the PCI config space. */ #define PCI_CONFIG_ADDR 0xCF8 #define PCI_CONFIG_DATA 0xCFC @@ -3007,6 +3018,9 @@ int main(int argc, char *argv[]) /* We always have a console device */ setup_console(); + /* Initialize the (fake) PCI host bridge device. */ + init_pci_host_bridge(); + /* Now we load the kernel */ start = load_kernel(open_or_die(argv[optind+1], O_RDONLY)); From 5051654764d55a101747b5b2a695bcecae75fa4c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:15:12 +1030 Subject: [PATCH 293/360] lguest: Convert block device to virtio 1.0 PCI. We remove SCSI support (which was removed for 1.0) and VIRTIO_BLK_F_FLUSH feature flag (removed too, since it's compulsory for 1.0). The rest is mainly mechanical. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index c8930bc5ce996c..d4a79f6ddfbd5d 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -64,11 +64,12 @@ typedef uint8_t u8; /*:*/ #define VIRTIO_PCI_NO_LEGACY +#define VIRTIO_BLK_NO_LEGACY /* Use in-kernel ones, which defines VIRTIO_F_VERSION_1 */ #include "../../include/uapi/linux/virtio_config.h" #include -#include +#include "../../include/uapi/linux/virtio_blk.h" #include #include #include @@ -2224,7 +2225,6 @@ static void init_pci_config(struct pci_config *pci, u16 type, * eg : * VIRTIO_ID_CONSOLE: class = 0x07, subclass = 0x00 * VIRTIO_ID_NET: class = 0x02, subclass = 0x00 - * VIRTIO_ID_BLOCK: class = 0x01, subclass = 0x80 * VIRTIO_ID_RNG: class = 0xff, subclass = 0 */ pci->class = class; @@ -2663,15 +2663,7 @@ static void blk_request(struct virtqueue *vq) */ off = out.sector * 512; - /* - * In general the virtio block driver is allowed to try SCSI commands. - * It'd be nice if we supported eject, for example, but we don't. - */ - if (out.type & VIRTIO_BLK_T_SCSI_CMD) { - fprintf(stderr, "Scsi commands unsupported\n"); - *in = VIRTIO_BLK_S_UNSUPP; - wlen = sizeof(*in); - } else if (out.type & VIRTIO_BLK_T_OUT) { + if (out.type & VIRTIO_BLK_T_OUT) { /* * Write * @@ -2735,11 +2727,11 @@ static void setup_block_file(const char *filename) struct vblk_info *vblk; struct virtio_blk_config conf; - /* Creat the device. */ - dev = new_device("block", VIRTIO_ID_BLOCK); + /* Create the device. */ + dev = new_pci_device("block", VIRTIO_ID_BLOCK, 0x01, 0x80); /* The device has one virtqueue, where the Guest places requests. */ - add_virtqueue(dev, VIRTQUEUE_NUM, blk_request); + add_pci_virtqueue(dev, blk_request); /* Allocate the room for our own bookkeeping */ vblk = dev->priv = malloc(sizeof(*vblk)); @@ -2748,9 +2740,6 @@ static void setup_block_file(const char *filename) vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE); vblk->len = lseek64(vblk->fd, 0, SEEK_END); - /* We support FLUSH. */ - add_feature(dev, VIRTIO_BLK_F_FLUSH); - /* Tell Guest how many sectors this device has. */ conf.capacity = cpu_to_le64(vblk->len / 512); @@ -2758,14 +2747,13 @@ static void setup_block_file(const char *filename) * Tell Guest not to put in too many descriptors at once: two are used * for the in and out elements. */ - add_feature(dev, VIRTIO_BLK_F_SEG_MAX); + add_pci_feature(dev, VIRTIO_BLK_F_SEG_MAX); conf.seg_max = cpu_to_le32(VIRTQUEUE_NUM - 2); - /* Don't try to put whole struct: we have 8 bit limit. */ - set_config(dev, offsetof(struct virtio_blk_config, geometry), &conf); + set_device_config(dev, &conf, sizeof(struct virtio_blk_config)); verbose("device %u: virtblock %llu sectors\n", - ++devices.device_num, le64_to_cpu(conf.capacity)); + devices.device_num, le64_to_cpu(conf.capacity)); } /*L:211 From bf6d40344d7006f29da1a2782f45188cdbbb0904 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:16:01 +1030 Subject: [PATCH 294/360] lguest: Convert net device to virtio 1.0 PCI. The only real change here (other than using the PCI bus) is that we didn't negotiate VIRTIO_NET_F_MRG_RXBUF before, so the format of the packet header changed with virtio 1.0; we need TUNSETVNETHDRSZ on the tun fd to tell it about the extra two bytes. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 48 +++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index d4a79f6ddfbd5d..b6c88a10a4c949 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -68,7 +68,7 @@ typedef uint8_t u8; /* Use in-kernel ones, which defines VIRTIO_F_VERSION_1 */ #include "../../include/uapi/linux/virtio_config.h" -#include +#include "../../include/uapi/linux/virtio_net.h" #include "../../include/uapi/linux/virtio_blk.h" #include #include @@ -2224,7 +2224,6 @@ static void init_pci_config(struct pci_config *pci, u16 type, * * eg : * VIRTIO_ID_CONSOLE: class = 0x07, subclass = 0x00 - * VIRTIO_ID_NET: class = 0x02, subclass = 0x00 * VIRTIO_ID_RNG: class = 0xff, subclass = 0 */ pci->class = class; @@ -2485,6 +2484,7 @@ static void configure_device(int fd, const char *tapif, u32 ipaddr) static int get_tun_device(char tapif[IFNAMSIZ]) { struct ifreq ifr; + int vnet_hdr_sz; int netfd; /* Start with this zeroed. Messy but sure. */ @@ -2512,6 +2512,18 @@ static int get_tun_device(char tapif[IFNAMSIZ]) */ ioctl(netfd, TUNSETNOCSUM, 1); + /* + * In virtio before 1.0 (aka legacy virtio), we added a 16-bit + * field at the end of the network header iff + * VIRTIO_NET_F_MRG_RXBUF was negotiated. For virtio 1.0, + * that became the norm, but we need to tell the tun device + * about our expanded header (which is called + * virtio_net_hdr_mrg_rxbuf in the legacy system). + */ + vnet_hdr_sz = sizeof(struct virtio_net_hdr_mrg_rxbuf); + if (ioctl(netfd, TUNSETVNETHDRSZ, &vnet_hdr_sz) != 0) + err(1, "Setting tun header size to %u", vnet_hdr_sz); + memcpy(tapif, ifr.ifr_name, IFNAMSIZ); return netfd; } @@ -2535,12 +2547,12 @@ static void setup_tun_net(char *arg) net_info->tunfd = get_tun_device(tapif); /* First we create a new network device. */ - dev = new_device("net", VIRTIO_ID_NET); + dev = new_pci_device("net", VIRTIO_ID_NET, 0x02, 0x00); dev->priv = net_info; /* Network devices need a recv and a send queue, just like console. */ - add_virtqueue(dev, VIRTQUEUE_NUM, net_input); - add_virtqueue(dev, VIRTQUEUE_NUM, net_output); + add_pci_virtqueue(dev, net_input); + add_pci_virtqueue(dev, net_output); /* * We need a socket to perform the magic network ioctls to bring up the @@ -2560,7 +2572,7 @@ static void setup_tun_net(char *arg) p = strchr(arg, ':'); if (p) { str2mac(p+1, conf.mac); - add_feature(dev, VIRTIO_NET_F_MAC); + add_pci_feature(dev, VIRTIO_NET_F_MAC); *p = '\0'; } @@ -2574,25 +2586,21 @@ static void setup_tun_net(char *arg) configure_device(ipfd, tapif, ip); /* Expect Guest to handle everything except UFO */ - add_feature(dev, VIRTIO_NET_F_CSUM); - add_feature(dev, VIRTIO_NET_F_GUEST_CSUM); - add_feature(dev, VIRTIO_NET_F_GUEST_TSO4); - add_feature(dev, VIRTIO_NET_F_GUEST_TSO6); - add_feature(dev, VIRTIO_NET_F_GUEST_ECN); - add_feature(dev, VIRTIO_NET_F_HOST_TSO4); - add_feature(dev, VIRTIO_NET_F_HOST_TSO6); - add_feature(dev, VIRTIO_NET_F_HOST_ECN); + add_pci_feature(dev, VIRTIO_NET_F_CSUM); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_CSUM); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_TSO4); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_TSO6); + add_pci_feature(dev, VIRTIO_NET_F_GUEST_ECN); + add_pci_feature(dev, VIRTIO_NET_F_HOST_TSO4); + add_pci_feature(dev, VIRTIO_NET_F_HOST_TSO6); + add_pci_feature(dev, VIRTIO_NET_F_HOST_ECN); /* We handle indirect ring entries */ - add_feature(dev, VIRTIO_RING_F_INDIRECT_DESC); - /* We're compliant with the damn spec. */ - add_feature(dev, VIRTIO_F_ANY_LAYOUT); - set_config(dev, sizeof(conf), &conf); + add_pci_feature(dev, VIRTIO_RING_F_INDIRECT_DESC); + set_device_config(dev, &conf, sizeof(conf)); /* We don't need the socket any more; setup is done. */ close(ipfd); - devices.device_num++; - if (bridging) verbose("device %u: tun %s attached to bridge: %s\n", devices.device_num, tapif, arg); From 0d5b5d399f8cecfeebefdd010048b2d608eab463 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:17:01 +1030 Subject: [PATCH 295/360] lguest: Convert entropy device to virtio 1.0 PCI. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index b6c88a10a4c949..842c82bd76fffc 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -71,7 +71,7 @@ typedef uint8_t u8; #include "../../include/uapi/linux/virtio_net.h" #include "../../include/uapi/linux/virtio_blk.h" #include -#include +#include "../../include/uapi/linux/virtio_rng.h" #include #include "../../include/uapi/linux/virtio_pci.h" #include @@ -2224,7 +2224,6 @@ static void init_pci_config(struct pci_config *pci, u16 type, * * eg : * VIRTIO_ID_CONSOLE: class = 0x07, subclass = 0x00 - * VIRTIO_ID_RNG: class = 0xff, subclass = 0 */ pci->class = class; pci->subclass = subclass; @@ -2816,13 +2815,16 @@ static void setup_rng(void) rng_info->rfd = open_or_die("/dev/urandom", O_RDONLY); /* Create the new device. */ - dev = new_device("rng", VIRTIO_ID_RNG); + dev = new_pci_device("rng", VIRTIO_ID_RNG, 0xff, 0); dev->priv = rng_info; /* The device has one virtqueue, where the Guest places inbufs. */ - add_virtqueue(dev, VIRTQUEUE_NUM, rng_input); + add_pci_virtqueue(dev, rng_input); - verbose("device %u: rng\n", devices.device_num++); + /* We don't have any configuration space */ + no_device_config(dev); + + verbose("device %u: rng\n", devices.device_num); } /* That's the end of device setup. */ From ebff01137acd21534fffaffcf35cd4a3681b95ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:18:01 +1030 Subject: [PATCH 296/360] lguest: Convert console device to virtio 1.0 PCI. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 842c82bd76fffc..fadd5723ba57a2 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -2221,9 +2221,6 @@ static void init_pci_config(struct pci_config *pci, u16 type, * PCI have specific codes for different types of devices. * Linux doesn't care, but it's a good clue for people looking * at the device. - * - * eg : - * VIRTIO_ID_CONSOLE: class = 0x07, subclass = 0x00 */ pci->class = class; pci->subclass = subclass; @@ -2370,7 +2367,7 @@ static void setup_console(void) tcsetattr(STDIN_FILENO, TCSANOW, &term); } - dev = new_device("console", VIRTIO_ID_CONSOLE); + dev = new_pci_device("console", VIRTIO_ID_CONSOLE, 0x07, 0x00); /* We store the console state in dev->priv, and initialize it. */ dev->priv = malloc(sizeof(struct console_abort)); @@ -2382,10 +2379,13 @@ static void setup_console(void) * stdin. When they put something in the output queue, we write it to * stdout. */ - add_virtqueue(dev, VIRTQUEUE_NUM, console_input); - add_virtqueue(dev, VIRTQUEUE_NUM, console_output); + add_pci_virtqueue(dev, console_input); + add_pci_virtqueue(dev, console_output); + + /* There's no configuration area for this device. */ + no_device_config(dev); - verbose("device %u: console\n", ++devices.device_num); + verbose("device %u: console\n", devices.device_num); } /*:*/ From eb39f83372b45bebc8af59b34af5d35bb0defe53 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:19:01 +1030 Subject: [PATCH 297/360] lguest: define VIRTIO_CONFIG_NO_LEGACY in example launcher. We only support virtio 1.0 now Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index fadd5723ba57a2..663166aff1f508 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -63,6 +63,7 @@ typedef uint16_t u16; typedef uint8_t u8; /*:*/ +#define VIRTIO_CONFIG_NO_LEGACY #define VIRTIO_PCI_NO_LEGACY #define VIRTIO_BLK_NO_LEGACY From e68ccd1f9d3d0fe8085b4e18c2cc2245f384c420 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:20:01 +1030 Subject: [PATCH 298/360] lguest: remove support for lguest bus. The demonstration launcher now uses PCI entirely. Signed-off-by: Rusty Russell --- drivers/lguest/Makefile | 3 - drivers/lguest/lguest_device.c | 540 --------------------------------- 2 files changed, 543 deletions(-) delete mode 100644 drivers/lguest/lguest_device.c diff --git a/drivers/lguest/Makefile b/drivers/lguest/Makefile index c4197503900ed3..16f52ee739942b 100644 --- a/drivers/lguest/Makefile +++ b/drivers/lguest/Makefile @@ -1,6 +1,3 @@ -# Guest requires the device configuration and probing code. -obj-$(CONFIG_LGUEST_GUEST) += lguest_device.o - # Host requires the other files, which can be a module. obj-$(CONFIG_LGUEST) += lg.o lg-y = core.o hypercalls.o page_tables.o interrupts_and_traps.o \ diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c deleted file mode 100644 index 89088d6538fd03..00000000000000 --- a/drivers/lguest/lguest_device.c +++ /dev/null @@ -1,540 +0,0 @@ -/*P:050 - * Lguest guests use a very simple method to describe devices. It's a - * series of device descriptors contained just above the top of normal Guest - * memory. - * - * We use the standard "virtio" device infrastructure, which provides us with a - * console, a network and a block driver. Each one expects some configuration - * information and a "virtqueue" or two to send and receive data. -:*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* The pointer to our (page) of device descriptions. */ -static void *lguest_devices; - -/* - * For Guests, device memory can be used as normal memory, so we cast away the - * __iomem to quieten sparse. - */ -static inline void *lguest_map(unsigned long phys_addr, unsigned long pages) -{ - return (__force void *)ioremap_cache(phys_addr, PAGE_SIZE*pages); -} - -static inline void lguest_unmap(void *addr) -{ - iounmap((__force void __iomem *)addr); -} - -/*D:100 - * Each lguest device is just a virtio device plus a pointer to its entry - * in the lguest_devices page. - */ -struct lguest_device { - struct virtio_device vdev; - - /* The entry in the lguest_devices page for this device. */ - struct lguest_device_desc *desc; -}; - -/* - * Since the virtio infrastructure hands us a pointer to the virtio_device all - * the time, it helps to have a curt macro to get a pointer to the struct - * lguest_device it's enclosed in. - */ -#define to_lgdev(vd) container_of(vd, struct lguest_device, vdev) - -/*D:130 - * Device configurations - * - * The configuration information for a device consists of one or more - * virtqueues, a feature bitmap, and some configuration bytes. The - * configuration bytes don't really matter to us: the Launcher sets them up, and - * the driver will look at them during setup. - * - * A convenient routine to return the device's virtqueue config array: - * immediately after the descriptor. - */ -static struct lguest_vqconfig *lg_vq(const struct lguest_device_desc *desc) -{ - return (void *)(desc + 1); -} - -/* The features come immediately after the virtqueues. */ -static u8 *lg_features(const struct lguest_device_desc *desc) -{ - return (void *)(lg_vq(desc) + desc->num_vq); -} - -/* The config space comes after the two feature bitmasks. */ -static u8 *lg_config(const struct lguest_device_desc *desc) -{ - return lg_features(desc) + desc->feature_len * 2; -} - -/* The total size of the config page used by this device (incl. desc) */ -static unsigned desc_size(const struct lguest_device_desc *desc) -{ - return sizeof(*desc) - + desc->num_vq * sizeof(struct lguest_vqconfig) - + desc->feature_len * 2 - + desc->config_len; -} - -/* This gets the device's feature bits. */ -static u64 lg_get_features(struct virtio_device *vdev) -{ - unsigned int i; - u32 features = 0; - struct lguest_device_desc *desc = to_lgdev(vdev)->desc; - u8 *in_features = lg_features(desc); - - /* We do this the slow but generic way. */ - for (i = 0; i < min(desc->feature_len * 8, 32); i++) - if (in_features[i / 8] & (1 << (i % 8))) - features |= (1 << i); - - return features; -} - -/* - * To notify on reset or feature finalization, we (ab)use the NOTIFY - * hypercall, with the descriptor address of the device. - */ -static void status_notify(struct virtio_device *vdev) -{ - unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; - - hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0); -} - -/* - * The virtio core takes the features the Host offers, and copies the ones - * supported by the driver into the vdev->features array. Once that's all - * sorted out, this routine is called so we can tell the Host which features we - * understand and accept. - */ -static int lg_finalize_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - struct lguest_device_desc *desc = to_lgdev(vdev)->desc; - /* Second half of bitmap is features we accept. */ - u8 *out_features = lg_features(desc) + desc->feature_len; - - /* Give virtio_ring a chance to accept features. */ - vring_transport_features(vdev); - - /* Make sure we don't have any features > 32 bits! */ - BUG_ON((u32)vdev->features != vdev->features); - - /* - * Since lguest is currently x86-only, we're little-endian. That - * means we could just memcpy. But it's not time critical, and in - * case someone copies this code, we do it the slow, obvious way. - */ - memset(out_features, 0, desc->feature_len); - bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) { - if (__virtio_test_bit(vdev, i)) - out_features[i / 8] |= (1 << (i % 8)); - } - - /* Tell Host we've finished with this device's feature negotiation */ - status_notify(vdev); - - return 0; -} - -/* Once they've found a field, getting a copy of it is easy. */ -static void lg_get(struct virtio_device *vdev, unsigned int offset, - void *buf, unsigned len) -{ - struct lguest_device_desc *desc = to_lgdev(vdev)->desc; - - /* Check they didn't ask for more than the length of the config! */ - BUG_ON(offset + len > desc->config_len); - memcpy(buf, lg_config(desc) + offset, len); -} - -/* Setting the contents is also trivial. */ -static void lg_set(struct virtio_device *vdev, unsigned int offset, - const void *buf, unsigned len) -{ - struct lguest_device_desc *desc = to_lgdev(vdev)->desc; - - /* Check they didn't ask for more than the length of the config! */ - BUG_ON(offset + len > desc->config_len); - memcpy(lg_config(desc) + offset, buf, len); -} - -/* - * The operations to get and set the status word just access the status field - * of the device descriptor. - */ -static u8 lg_get_status(struct virtio_device *vdev) -{ - return to_lgdev(vdev)->desc->status; -} - -static void lg_set_status(struct virtio_device *vdev, u8 status) -{ - BUG_ON(!status); - to_lgdev(vdev)->desc->status = status; - - /* Tell Host immediately if we failed. */ - if (status & VIRTIO_CONFIG_S_FAILED) - status_notify(vdev); -} - -static void lg_reset(struct virtio_device *vdev) -{ - /* 0 status means "reset" */ - to_lgdev(vdev)->desc->status = 0; - status_notify(vdev); -} - -/* - * Virtqueues - * - * The other piece of infrastructure virtio needs is a "virtqueue": a way of - * the Guest device registering buffers for the other side to read from or - * write into (ie. send and receive buffers). Each device can have multiple - * virtqueues: for example the console driver uses one queue for sending and - * another for receiving. - * - * Fortunately for us, a very fast shared-memory-plus-descriptors virtqueue - * already exists in virtio_ring.c. We just need to connect it up. - * - * We start with the information we need to keep about each virtqueue. - */ - -/*D:140 This is the information we remember about each virtqueue. */ -struct lguest_vq_info { - /* A copy of the information contained in the device config. */ - struct lguest_vqconfig config; - - /* The address where we mapped the virtio ring, so we can unmap it. */ - void *pages; -}; - -/* - * When the virtio_ring code wants to prod the Host, it calls us here and we - * make a hypercall. We hand the physical address of the virtqueue so the Host - * knows which virtqueue we're talking about. - */ -static bool lg_notify(struct virtqueue *vq) -{ - /* - * We store our virtqueue information in the "priv" pointer of the - * virtqueue structure. - */ - struct lguest_vq_info *lvq = vq->priv; - - hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0, 0); - return true; -} - -/* An extern declaration inside a C file is bad form. Don't do it. */ -extern int lguest_setup_irq(unsigned int irq); - -/* - * This routine finds the Nth virtqueue described in the configuration of - * this device and sets it up. - * - * This is kind of an ugly duckling. It'd be nicer to have a standard - * representation of a virtqueue in the configuration space, but it seems that - * everyone wants to do it differently. The KVM coders want the Guest to - * allocate its own pages and tell the Host where they are, but for lguest it's - * simpler for the Host to simply tell us where the pages are. - */ -static struct virtqueue *lg_find_vq(struct virtio_device *vdev, - unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name) -{ - struct lguest_device *ldev = to_lgdev(vdev); - struct lguest_vq_info *lvq; - struct virtqueue *vq; - int err; - - if (!name) - return NULL; - - /* We must have this many virtqueues. */ - if (index >= ldev->desc->num_vq) - return ERR_PTR(-ENOENT); - - lvq = kmalloc(sizeof(*lvq), GFP_KERNEL); - if (!lvq) - return ERR_PTR(-ENOMEM); - - /* - * Make a copy of the "struct lguest_vqconfig" entry, which sits after - * the descriptor. We need a copy because the config space might not - * be aligned correctly. - */ - memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config)); - - printk("Mapping virtqueue %i addr %lx\n", index, - (unsigned long)lvq->config.pfn << PAGE_SHIFT); - /* Figure out how many pages the ring will take, and map that memory */ - lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT, - DIV_ROUND_UP(vring_size(lvq->config.num, - LGUEST_VRING_ALIGN), - PAGE_SIZE)); - if (!lvq->pages) { - err = -ENOMEM; - goto free_lvq; - } - - /* - * OK, tell virtio_ring.c to set up a virtqueue now we know its size - * and we've got a pointer to its pages. Note that we set weak_barriers - * to 'true': the host just a(nother) SMP CPU, so we only need inter-cpu - * barriers. - */ - vq = vring_new_virtqueue(index, lvq->config.num, LGUEST_VRING_ALIGN, vdev, - true, lvq->pages, lg_notify, callback, name); - if (!vq) { - err = -ENOMEM; - goto unmap; - } - - /* Make sure the interrupt is allocated. */ - err = lguest_setup_irq(lvq->config.irq); - if (err) - goto destroy_vring; - - /* - * Tell the interrupt for this virtqueue to go to the virtio_ring - * interrupt handler. - * - * FIXME: We used to have a flag for the Host to tell us we could use - * the interrupt as a source of randomness: it'd be nice to have that - * back. - */ - err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED, - dev_name(&vdev->dev), vq); - if (err) - goto free_desc; - - /* - * Last of all we hook up our 'struct lguest_vq_info" to the - * virtqueue's priv pointer. - */ - vq->priv = lvq; - return vq; - -free_desc: - irq_free_desc(lvq->config.irq); -destroy_vring: - vring_del_virtqueue(vq); -unmap: - lguest_unmap(lvq->pages); -free_lvq: - kfree(lvq); - return ERR_PTR(err); -} -/*:*/ - -/* Cleaning up a virtqueue is easy */ -static void lg_del_vq(struct virtqueue *vq) -{ - struct lguest_vq_info *lvq = vq->priv; - - /* Release the interrupt */ - free_irq(lvq->config.irq, vq); - /* Tell virtio_ring.c to free the virtqueue. */ - vring_del_virtqueue(vq); - /* Unmap the pages containing the ring. */ - lguest_unmap(lvq->pages); - /* Free our own queue information. */ - kfree(lvq); -} - -static void lg_del_vqs(struct virtio_device *vdev) -{ - struct virtqueue *vq, *n; - - list_for_each_entry_safe(vq, n, &vdev->vqs, list) - lg_del_vq(vq); -} - -static int lg_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char *names[]) -{ - struct lguest_device *ldev = to_lgdev(vdev); - int i; - - /* We must have this many virtqueues. */ - if (nvqs > ldev->desc->num_vq) - return -ENOENT; - - for (i = 0; i < nvqs; ++i) { - vqs[i] = lg_find_vq(vdev, i, callbacks[i], names[i]); - if (IS_ERR(vqs[i])) - goto error; - } - return 0; - -error: - lg_del_vqs(vdev); - return PTR_ERR(vqs[i]); -} - -static const char *lg_bus_name(struct virtio_device *vdev) -{ - return ""; -} - -/* The ops structure which hooks everything together. */ -static const struct virtio_config_ops lguest_config_ops = { - .get_features = lg_get_features, - .finalize_features = lg_finalize_features, - .get = lg_get, - .set = lg_set, - .get_status = lg_get_status, - .set_status = lg_set_status, - .reset = lg_reset, - .find_vqs = lg_find_vqs, - .del_vqs = lg_del_vqs, - .bus_name = lg_bus_name, -}; - -/* - * The root device for the lguest virtio devices. This makes them appear as - * /sys/devices/lguest/0,1,2 not /sys/devices/0,1,2. - */ -static struct device *lguest_root; - -/*D:120 - * This is the core of the lguest bus: actually adding a new device. - * It's a separate function because it's neater that way, and because an - * earlier version of the code supported hotplug and unplug. They were removed - * early on because they were never used. - * - * As Andrew Tridgell says, "Untested code is buggy code". - * - * It's worth reading this carefully: we start with a pointer to the new device - * descriptor in the "lguest_devices" page, and the offset into the device - * descriptor page so we can uniquely identify it if things go badly wrong. - */ -static void add_lguest_device(struct lguest_device_desc *d, - unsigned int offset) -{ - struct lguest_device *ldev; - - /* Start with zeroed memory; Linux's device layer counts on it. */ - ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); - if (!ldev) { - printk(KERN_EMERG "Cannot allocate lguest dev %u type %u\n", - offset, d->type); - return; - } - - /* This devices' parent is the lguest/ dir. */ - ldev->vdev.dev.parent = lguest_root; - /* - * The device type comes straight from the descriptor. There's also a - * device vendor field in the virtio_device struct, which we leave as - * 0. - */ - ldev->vdev.id.device = d->type; - /* - * We have a simple set of routines for querying the device's - * configuration information and setting its status. - */ - ldev->vdev.config = &lguest_config_ops; - /* And we remember the device's descriptor for lguest_config_ops. */ - ldev->desc = d; - - /* - * register_virtio_device() sets up the generic fields for the struct - * virtio_device and calls device_register(). This makes the bus - * infrastructure look for a matching driver. - */ - if (register_virtio_device(&ldev->vdev) != 0) { - printk(KERN_ERR "Failed to register lguest dev %u type %u\n", - offset, d->type); - kfree(ldev); - } -} - -/*D:110 - * scan_devices() simply iterates through the device page. The type 0 is - * reserved to mean "end of devices". - */ -static void scan_devices(void) -{ - unsigned int i; - struct lguest_device_desc *d; - - /* We start at the page beginning, and skip over each entry. */ - for (i = 0; i < PAGE_SIZE; i += desc_size(d)) { - d = lguest_devices + i; - - /* Once we hit a zero, stop. */ - if (d->type == 0) - break; - - printk("Device at %i has size %u\n", i, desc_size(d)); - add_lguest_device(d, i); - } -} - -/*D:105 - * Fairly early in boot, lguest_devices_init() is called to set up the - * lguest device infrastructure. We check that we are a Guest by checking - * pv_info.name: there are other ways of checking, but this seems most - * obvious to me. - * - * So we can access the "struct lguest_device_desc"s easily, we map that memory - * and store the pointer in the global "lguest_devices". Then we register a - * root device from which all our devices will hang (this seems to be the - * correct sysfs incantation). - * - * Finally we call scan_devices() which adds all the devices found in the - * lguest_devices page. - */ -static int __init lguest_devices_init(void) -{ - if (strcmp(pv_info.name, "lguest") != 0) - return 0; - - lguest_root = root_device_register("lguest"); - if (IS_ERR(lguest_root)) - panic("Could not register lguest root"); - - /* Devices are in a single page above top of "normal" mem */ - lguest_devices = lguest_map(max_pfn< Date: Wed, 11 Feb 2015 15:21:01 +1030 Subject: [PATCH 299/360] lguest: remove support for lguest bus in demonstration launcher. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 355 +++--------------------------------------- 1 file changed, 22 insertions(+), 333 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 663166aff1f508..b5ac73525f6dee 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -117,14 +117,6 @@ struct device_list { /* Counter to print out convenient device numbers. */ unsigned int device_num; - /* The descriptor page for the devices. */ - u8 *descpage; - - /* A single linked list of devices. */ - struct device *dev; - /* And a pointer to the last device for easy append. */ - struct device *lastdev; - /* PCI devices. */ struct device *pci[MAX_PCI_DEVICES]; }; @@ -170,16 +162,6 @@ struct pci_config { /* The device structure describes a single device. */ struct device { - /* The linked-list pointer. */ - struct device *next; - - /* The device's descriptor, as mapped into the Guest. */ - struct lguest_device_desc *desc; - - /* We can't trust desc values once Guest has booted: we use these. */ - unsigned int feature_len; - unsigned int num_vq; - /* The name of this device, for --verbose. */ const char *name; @@ -216,9 +198,6 @@ struct virtqueue { /* Which device owns me. */ struct device *dev; - /* The configuration for this queue. */ - struct lguest_vqconfig config; - /* The actual ring of buffers. */ struct vring vring; @@ -301,13 +280,6 @@ static void iov_consume(struct iovec iov[], unsigned num_iov, errx(1, "iovec too short!"); } -/* The device virtqueue descriptors are followed by feature bitmasks. */ -static u8 *get_feature_bits(struct device *dev) -{ - return (u8 *)(dev->desc + 1) - + dev->num_vq * sizeof(struct lguest_vqconfig); -} - /*L:100 * The Launcher code itself takes us out into userspace, that scary place where * pointers run wild and free! Unfortunately, like most userspace programs, @@ -378,17 +350,6 @@ static void *map_zeroed_pages(unsigned int num) return addr + getpagesize(); } -/* Get some more pages for a device. */ -static void *get_pages(unsigned int num) -{ - void *addr = from_guest_phys(guest_limit); - - guest_limit += num * getpagesize(); - if (guest_limit > guest_max) - errx(1, "Not enough memory for devices"); - return addr; -} - /* Get some bytes which won't be mapped into the guest. */ static unsigned long get_mmio_region(size_t size) { @@ -701,7 +662,7 @@ static unsigned next_desc(struct vring_desc *desc, */ static void trigger_irq(struct virtqueue *vq) { - unsigned long buf[] = { LHREQ_IRQ, vq->config.irq }; + unsigned long buf[] = { LHREQ_IRQ, vq->dev->config.irq_line }; /* Don't inform them if nothing used. */ if (!vq->pending_used) @@ -713,13 +674,12 @@ static void trigger_irq(struct virtqueue *vq) return; } - /* For a PCI device, set isr to 1 (queue interrupt pending) */ - if (vq->dev->mmio) - vq->dev->mmio->isr = 0x1; + /* Set isr to 1 (queue interrupt pending) */ + vq->dev->mmio->isr = 0x1; /* Send the Guest an interrupt tell them we used something up. */ if (write(lguest_fd, buf, sizeof(buf)) != 0) - err(1, "Triggering irq %i", vq->config.irq); + err(1, "Triggering irq %i", vq->dev->config.irq_line); } /* @@ -1085,21 +1045,18 @@ static void reset_device(struct device *dev) verbose("Resetting device %s\n", dev->name); /* Clear any features they've acked. */ - memset(get_feature_bits(dev) + dev->feature_len, 0, dev->feature_len); + dev->features_accepted = 0; /* We're going to be explicitly killing threads, so ignore them. */ signal(SIGCHLD, SIG_IGN); - /* Zero out the virtqueues, get rid of their threads */ + /* Get rid of the virtqueue threads */ for (vq = dev->vq; vq; vq = vq->next) { if (vq->thread != (pid_t)-1) { kill(vq->thread, SIGTERM); waitpid(vq->thread, NULL, 0); vq->thread = (pid_t)-1; } - memset(vq->vring.desc, 0, - vring_size(vq->config.num, LGUEST_VRING_ALIGN)); - lg_last_avail(vq) = 0; } dev->running = false; @@ -1107,122 +1064,27 @@ static void reset_device(struct device *dev) signal(SIGCHLD, (void *)kill_launcher); } -/*L:216 - * This actually creates the thread which services the virtqueue for a device. - */ -static void create_thread(struct virtqueue *vq) -{ - /* - * Create stack for thread. Since the stack grows upwards, we point - * the stack pointer to the end of this region. - */ - char *stack = malloc(32768); - unsigned long args[] = { LHREQ_EVENTFD, - vq->config.pfn*getpagesize(), 0 }; - - /* Create a zero-initialized eventfd. */ - vq->eventfd = eventfd(0, 0); - if (vq->eventfd < 0) - err(1, "Creating eventfd"); - args[2] = vq->eventfd; - - /* - * Attach an eventfd to this virtqueue: it will go off when the Guest - * does an LHCALL_NOTIFY for this vq. - */ - if (write(lguest_fd, &args, sizeof(args)) != 0) - err(1, "Attaching eventfd"); - - /* - * CLONE_VM: because it has to access the Guest memory, and SIGCHLD so - * we get a signal if it dies. - */ - vq->thread = clone(do_thread, stack + 32768, CLONE_VM | SIGCHLD, vq); - if (vq->thread == (pid_t)-1) - err(1, "Creating clone"); - - /* We close our local copy now the child has it. */ - close(vq->eventfd); -} - -static void start_device(struct device *dev) +static void cleanup_devices(void) { unsigned int i; - struct virtqueue *vq; - - verbose("Device %s OK: offered", dev->name); - for (i = 0; i < dev->feature_len; i++) - verbose(" %02x", get_feature_bits(dev)[i]); - verbose(", accepted"); - for (i = 0; i < dev->feature_len; i++) - verbose(" %02x", get_feature_bits(dev) - [dev->feature_len+i]); - for (vq = dev->vq; vq; vq = vq->next) { - if (vq->service) - create_thread(vq); + for (i = 1; i < MAX_PCI_DEVICES; i++) { + struct device *d = devices.pci[i]; + if (!d) + continue; + reset_device(d); } - dev->running = true; -} - -static void cleanup_devices(void) -{ - struct device *dev; - - for (dev = devices.dev; dev; dev = dev->next) - reset_device(dev); /* If we saved off the original terminal settings, restore them now. */ if (orig_term.c_lflag & (ISIG|ICANON|ECHO)) tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); } -/* When the Guest tells us they updated the status field, we handle it. */ -static void update_device_status(struct device *dev) -{ - /* A zero status is a reset, otherwise it's a set of flags. */ - if (dev->desc->status == 0) - reset_device(dev); - else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) { - warnx("Device %s configuration FAILED", dev->name); - if (dev->running) - reset_device(dev); - } else { - if (dev->running) - err(1, "Device %s features finalized twice", dev->name); - start_device(dev); - } -} - /*L:215 - * This is the generic routine we call when the Guest uses LHCALL_NOTIFY. In - * particular, it's used to notify us of device status changes during boot. + * This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */ static void handle_output(unsigned long addr) { - struct device *i; - - /* Check each device. */ - for (i = devices.dev; i; i = i->next) { - struct virtqueue *vq; - - /* - * Notifications to device descriptors mean they updated the - * device status. - */ - if (from_guest_phys(addr) == i->desc) { - update_device_status(i); - return; - } - - /* Devices should not be used before features are finalized. */ - for (vq = i->vq; vq; vq = vq->next) { - if (addr != vq->config.pfn*getpagesize()) - continue; - errx(1, "Notification on %s before setup!", i->name); - } - } - /* * Early console write is done using notify on a nul-terminated string * in Guest memory. It's also great for hacking debugging messages @@ -1736,11 +1598,6 @@ static void enable_virtqueue(struct device *d, struct virtqueue *vq) err(1, "Creating clone"); } -static void reset_pci_device(struct device *dev) -{ - /* FIXME */ -} - static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) { struct virtqueue *vq; @@ -1775,7 +1632,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) case offsetof(struct virtio_pci_mmio, cfg.device_status): verbose("%s: device status -> %#x\n", d->name, val); if (val == 0) - reset_pci_device(d); + reset_device(d); goto write_through8; case offsetof(struct virtio_pci_mmio, cfg.queue_select): vq = vq_by_num(d, val); @@ -1986,102 +1843,6 @@ static void emulate_mmio(unsigned long paddr, const u8 *insn) * device" so the Launcher can keep track of it. We have common helper * routines to allocate and manage them. */ - -/* - * The layout of the device page is a "struct lguest_device_desc" followed by a - * number of virtqueue descriptors, then two sets of feature bits, then an - * array of configuration bytes. This routine returns the configuration - * pointer. - */ -static u8 *device_config(const struct device *dev) -{ - return (void *)(dev->desc + 1) - + dev->num_vq * sizeof(struct lguest_vqconfig) - + dev->feature_len * 2; -} - -/* - * This routine allocates a new "struct lguest_device_desc" from descriptor - * table page just above the Guest's normal memory. It returns a pointer to - * that descriptor. - */ -static struct lguest_device_desc *new_dev_desc(u16 type) -{ - struct lguest_device_desc d = { .type = type }; - void *p; - - /* Figure out where the next device config is, based on the last one. */ - if (devices.lastdev) - p = device_config(devices.lastdev) - + devices.lastdev->desc->config_len; - else - p = devices.descpage; - - /* We only have one page for all the descriptors. */ - if (p + sizeof(d) > (void *)devices.descpage + getpagesize()) - errx(1, "Too many devices"); - - /* p might not be aligned, so we memcpy in. */ - return memcpy(p, &d, sizeof(d)); -} - -/* - * Each device descriptor is followed by the description of its virtqueues. We - * specify how many descriptors the virtqueue is to have. - */ -static void add_virtqueue(struct device *dev, unsigned int num_descs, - void (*service)(struct virtqueue *)) -{ - unsigned int pages; - struct virtqueue **i, *vq = malloc(sizeof(*vq)); - void *p; - - /* First we need some memory for this virtqueue. */ - pages = (vring_size(num_descs, LGUEST_VRING_ALIGN) + getpagesize() - 1) - / getpagesize(); - p = get_pages(pages); - - /* Initialize the virtqueue */ - vq->next = NULL; - vq->last_avail_idx = 0; - vq->dev = dev; - - /* - * This is the routine the service thread will run, and its Process ID - * once it's running. - */ - vq->service = service; - vq->thread = (pid_t)-1; - - /* Initialize the configuration. */ - vq->config.num = num_descs; - vq->config.irq = devices.next_irq++; - vq->config.pfn = to_guest_phys(p) / getpagesize(); - - /* Initialize the vring. */ - vring_init(&vq->vring, num_descs, p, LGUEST_VRING_ALIGN); - - /* - * Append virtqueue to this device's descriptor. We use - * device_config() to get the end of the device's current virtqueues; - * we check that we haven't added any config or feature information - * yet, otherwise we'd be overwriting them. - */ - assert(dev->desc->config_len == 0 && dev->desc->feature_len == 0); - memcpy(device_config(dev), &vq->config, sizeof(vq->config)); - dev->num_vq++; - dev->desc->num_vq++; - - verbose("Virtqueue page %#lx\n", to_guest_phys(p)); - - /* - * Add to tail of list, so dev->vq is first vq, dev->vq->next is - * second. - */ - for (i = &dev->vq; *i; i = &(*i)->next); - *i = vq; -} - static void add_pci_virtqueue(struct device *dev, void (*service)(struct virtqueue *)) { @@ -2107,9 +1868,6 @@ static void add_pci_virtqueue(struct device *dev, /* Add one to the number of queues */ vq->dev->mmio->cfg.num_queues++; - /* FIXME: Do irq per virtqueue, not per device. */ - vq->config.irq = vq->dev->config.irq_line; - /* * Add to tail of list, so dev->vq is first vq, dev->vq->next is * second. @@ -2118,47 +1876,12 @@ static void add_pci_virtqueue(struct device *dev, *i = vq; } -/* - * The first half of the feature bitmask is for us to advertise features. The - * second half is for the Guest to accept features. - */ -static void add_feature(struct device *dev, unsigned bit) -{ - u8 *features = get_feature_bits(dev); - - /* We can't extend the feature bits once we've added config bytes */ - if (dev->desc->feature_len <= bit / CHAR_BIT) { - assert(dev->desc->config_len == 0); - dev->feature_len = dev->desc->feature_len = (bit/CHAR_BIT) + 1; - } - - features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT)); -} - +/* The Guest accesses the feature bits via the PCI common config MMIO region */ static void add_pci_feature(struct device *dev, unsigned bit) { dev->features |= (1ULL << bit); } -/* - * This routine sets the configuration fields for an existing device's - * descriptor. It only works for the last device, but that's OK because that's - * how we use it. - */ -static void set_config(struct device *dev, unsigned len, const void *conf) -{ - /* Check we haven't overflowed our single page. */ - if (device_config(dev) + len > devices.descpage + getpagesize()) - errx(1, "Too many devices"); - - /* Copy in the config information, and store the length. */ - memcpy(device_config(dev), conf, len); - dev->desc->config_len = len; - - /* Size must fit in config_len field (8 bits)! */ - assert(dev->desc->config_len == len); -} - /* For devices with no config. */ static void no_device_config(struct device *dev) { @@ -2287,59 +2010,28 @@ static void init_pci_config(struct pci_config *pci, u16 type, } /* - * This routine does all the creation and setup of a new device, including - * calling new_dev_desc() to allocate the descriptor and device memory. We - * don't actually start the service threads until later. + * This routine does all the creation and setup of a new device, but we don't + * actually place the MMIO region until we know the size (if any) of the + * device-specific config. And we don't actually start the service threads + * until later. * * See what I mean about userspace being boring? */ -static struct device *new_device(const char *name, u16 type) -{ - struct device *dev = malloc(sizeof(*dev)); - - /* Now we populate the fields one at a time. */ - dev->desc = new_dev_desc(type); - dev->name = name; - dev->vq = NULL; - dev->feature_len = 0; - dev->num_vq = 0; - dev->running = false; - dev->next = NULL; - - /* - * Append to device list. Prepending to a single-linked list is - * easier, but the user expects the devices to be arranged on the bus - * in command-line order. The first network device on the command line - * is eth0, the first block device /dev/vda, etc. - */ - if (devices.lastdev) - devices.lastdev->next = dev; - else - devices.dev = dev; - devices.lastdev = dev; - - return dev; -} - static struct device *new_pci_device(const char *name, u16 type, u8 class, u8 subclass) { struct device *dev = malloc(sizeof(*dev)); /* Now we populate the fields one at a time. */ - dev->desc = NULL; dev->name = name; dev->vq = NULL; - dev->feature_len = 0; - dev->num_vq = 0; dev->running = false; - dev->next = NULL; dev->mmio_size = sizeof(struct virtio_pci_mmio); dev->mmio = calloc(1, dev->mmio_size); dev->features = (u64)1 << VIRTIO_F_VERSION_1; dev->features_accepted = 0; - if (devices.device_num + 1 >= 32) + if (devices.device_num + 1 >= MAX_PCI_DEVICES) errx(1, "Can only handle 31 PCI devices"); init_pci_config(&dev->config, type, class, subclass); @@ -2940,11 +2632,9 @@ int main(int argc, char *argv[]) main_args = argv; /* - * First we initialize the device list. We keep a pointer to the last - * device, and the next interrupt number to use for devices (1: - * remember that 0 is used by the timer). + * First we initialize the device list. We remember next interrupt + * number to use for devices (1: remember that 0 is used by the timer). */ - devices.lastdev = NULL; devices.next_irq = 1; /* We're CPU 0. In fact, that's the only CPU possible right now. */ @@ -2969,7 +2659,6 @@ int main(int argc, char *argv[]) + DEVICE_PAGES); guest_limit = mem; guest_max = guest_mmio = mem + DEVICE_PAGES*getpagesize(); - devices.descpage = get_pages(1); break; } } From b3e28b65de254570140832cf7c95255ab4d501bb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:22:01 +1030 Subject: [PATCH 300/360] lguest: remove lguest bus definitions from header. Signed-off-by: Rusty Russell --- include/linux/lguest_launcher.h | 49 ++------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index 3c402b843e03c2..677cde735d4bb0 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -8,52 +8,13 @@ * * The Guest needs devices to do anything useful. Since we don't let it touch * real devices (think of the damage it could do!) we provide virtual devices. - * We could emulate a PCI bus with various devices on it, but that is a fairly - * complex burden for the Host and suboptimal for the Guest, so we have our own - * simple lguest bus and we use "virtio" drivers. These drivers need a set of - * routines from us which will actually do the virtual I/O, but they handle all - * the net/block/console stuff themselves. This means that if we want to add - * a new device, we simply need to write a new virtio driver and create support - * for it in the Launcher: this code won't need to change. + * We emulate a PCI bus with virtio devices on it; we used to have our own + * lguest bus which was far simpler, but this tests the virtio 1.0 standard. * * Virtio devices are also used by kvm, so we can simply reuse their optimized * device drivers. And one day when everyone uses virtio, my plan will be * complete. Bwahahahah! - * - * Devices are described by a simplified ID, a status byte, and some "config" - * bytes which describe this device's configuration. This is placed by the - * Launcher just above the top of physical memory: - */ -struct lguest_device_desc { - /* The device type: console, network, disk etc. Type 0 terminates. */ - __u8 type; - /* The number of virtqueues (first in config array) */ - __u8 num_vq; - /* - * The number of bytes of feature bits. Multiply by 2: one for host - * features and one for Guest acknowledgements. - */ - __u8 feature_len; - /* The number of bytes of the config array after virtqueues. */ - __u8 config_len; - /* A status byte, written by the Guest. */ - __u8 status; - __u8 config[0]; -}; - -/*D:135 - * This is how we expect the device configuration field for a virtqueue - * to be laid out in config space. */ -struct lguest_vqconfig { - /* The number of entries in the virtio_ring */ - __u16 num; - /* The interrupt we get when something happens. */ - __u16 irq; - /* The page number of the virtio ring for this device. */ - __u32 pfn; -}; -/*:*/ /* Write command first word is a request. */ enum lguest_req @@ -80,10 +41,4 @@ struct lguest_pending { __u8 insn[7]; __u32 addr; }; - -/* - * The alignment to use between consumer and producer parts of vring. - * x86 pagesize for historical reasons. - */ -#define LGUEST_VRING_ALIGN 4096 #endif /* _LINUX_LGUEST_LAUNCHER */ From e8330d9bc1f7af7737500aebd3fc1f488e3dbb71 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:23:01 +1030 Subject: [PATCH 301/360] lguest: support emerg_wr in console device in example launcher. This is a magic register which causes a character to be outputted: it can be used even before the device is configured. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index b5ac73525f6dee..8959ac246668b8 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -71,7 +71,7 @@ typedef uint8_t u8; #include "../../include/uapi/linux/virtio_config.h" #include "../../include/uapi/linux/virtio_net.h" #include "../../include/uapi/linux/virtio_blk.h" -#include +#include "../../include/uapi/linux/virtio_console.h" #include "../../include/uapi/linux/virtio_rng.h" #include #include "../../include/uapi/linux/virtio_pci.h" @@ -1687,6 +1687,15 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) goto write_through16; case offsetof(struct virtio_pci_mmio, isr): errx(1, "%s: Unexpected write to isr", d->name); + /* Weird corner case: write to emerg_wr of console */ + case sizeof(struct virtio_pci_mmio) + + offsetof(struct virtio_console_config, emerg_wr): + if (strcmp(d->name, "console") == 0) { + char c = val; + write(STDOUT_FILENO, &c, 1); + goto write_through32; + } + /* Fall through... */ default: errx(1, "%s: Unexpected write to offset %u", d->name, off); } @@ -2048,6 +2057,7 @@ static struct device *new_pci_device(const char *name, u16 type, static void setup_console(void) { struct device *dev; + struct virtio_console_config conf; /* If we can save the initial standard input settings... */ if (tcgetattr(STDIN_FILENO, &orig_term) == 0) { @@ -2075,8 +2085,9 @@ static void setup_console(void) add_pci_virtqueue(dev, console_input); add_pci_virtqueue(dev, console_output); - /* There's no configuration area for this device. */ - no_device_config(dev); + /* We need a configuration area for the emerg_wr early writes. */ + add_pci_feature(dev, VIRTIO_CONSOLE_F_EMERG_WRITE); + set_device_config(dev, &conf, sizeof(conf)); verbose("device %u: console\n", devices.device_num); } From 59eba788db298c3597728774dc3d0f16bdc8a1a4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:24:01 +1030 Subject: [PATCH 302/360] lguest: support backdoor window. The VIRTIO_PCI_CAP_PCI_CFG in the PCI virtio 1.0 spec allows access to the BAR registers without mapping them. This is a compulsory feature, and we implement it here. There are some subtleties involving access widths which we should note: 4.1.4.7.1 Device Requirements: PCI configuration access capability ... Upon detecting driver write access to pci_cfg_data, the device MUST execute a write access at offset cap.offset at BAR selected by cap.bar using the first cap.length bytes from pci_cfg_data. Upon detecting driver read access to pci_cfg_data, the device MUST execute a read access of length cap.length at offset cap.offset at BAR selected by cap.bar and store the first cap.length bytes in pci_cfg_data. So, for a write, we copy into the pci_cfg_data window, then write from there out to the BAR. This works correctly if cap.length != width of write. Similarly, for a read, we read into window from the BAR then read the value from there. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 101 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 8959ac246668b8..e3c4d3d7dc2aa4 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -156,7 +156,6 @@ struct pci_config { struct virtio_pci_notify_cap notify; struct virtio_pci_cap isr; struct virtio_pci_cap device; - /* FIXME: Implement this! */ struct virtio_pci_cfg_cap cfg_access; }; @@ -1184,6 +1183,36 @@ static struct device *dev_and_reg(u32 *reg) return find_pci_device(pci_config_addr.bits.devnum); } +/* + * We can get invalid combinations of values while they're writing, so we + * only fault if they try to write with some invalid bar/offset/length. + */ +static bool valid_bar_access(struct device *d, + struct virtio_pci_cfg_cap *cfg_access) +{ + /* We only have 1 bar (BAR0) */ + if (cfg_access->cap.bar != 0) + return false; + + /* Check it's within BAR0. */ + if (cfg_access->cap.offset >= d->mmio_size + || cfg_access->cap.offset + cfg_access->cap.length > d->mmio_size) + return false; + + /* Check length is 1, 2 or 4. */ + if (cfg_access->cap.length != 1 + && cfg_access->cap.length != 2 + && cfg_access->cap.length != 4) + return false; + + /* Offset must be multiple of length */ + if (cfg_access->cap.offset % cfg_access->cap.length != 0) + return false; + + /* Return pointer into word in BAR0. */ + return true; +} + /* Is this accessing the PCI config address port?. */ static bool is_pci_addr_port(u16 port) { @@ -1215,6 +1244,8 @@ static bool is_pci_data_port(u16 port) return port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + 4; } +static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask); + static bool pci_data_iowrite(u16 port, u32 mask, u32 val) { u32 reg, portoff; @@ -1255,12 +1286,53 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) && mask == 0xFFFF) { /* Ignore command writes. */ return true; + } else if (&d->config_words[reg] + == (void *)&d->config.cfg_access.cap.bar + || &d->config_words[reg] + == &d->config.cfg_access.cap.length + || &d->config_words[reg] + == &d->config.cfg_access.cap.offset) { + + /* + * The VIRTIO_PCI_CAP_PCI_CFG capability + * provides a backdoor to access the MMIO + * regions without mapping them. Weird, but + * useful. + */ + iowrite(portoff, val, mask, &d->config_words[reg]); + return true; + } else if (&d->config_words[reg] == &d->config.cfg_access.window) { + u32 write_mask; + + /* Must be bar 0 */ + if (!valid_bar_access(d, &d->config.cfg_access)) + return false; + + /* First copy what they wrote into the window */ + iowrite(portoff, val, mask, &d->config.cfg_access.window); + + /* + * Now emulate a write. The mask we use is set by + * len, *not* this write! + */ + write_mask = (1ULL<<(8*d->config.cfg_access.cap.length)) - 1; + verbose("Window writing %#x/%#x to bar %u, offset %u len %u\n", + d->config.cfg_access.window, write_mask, + d->config.cfg_access.cap.bar, + d->config.cfg_access.cap.offset, + d->config.cfg_access.cap.length); + + emulate_mmio_write(d, d->config.cfg_access.cap.offset, + d->config.cfg_access.window, write_mask); + return true; } /* Complain about other writes. */ return false; } +static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask); + static void pci_data_ioread(u16 port, u32 mask, u32 *val) { u32 reg; @@ -1268,6 +1340,33 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val) if (!d) return; + + /* Read through the PCI MMIO access window is special */ + if (&d->config_words[reg] == &d->config.cfg_access.window) { + u32 read_mask; + + /* Must be bar 0 */ + if (!valid_bar_access(d, &d->config.cfg_access)) + errx(1, "Invalid cfg_access to bar%u, offset %u len %u", + d->config.cfg_access.cap.bar, + d->config.cfg_access.cap.offset, + d->config.cfg_access.cap.length); + + /* + * Read into the window. The mask we use is set by + * len, *not* this read! + */ + read_mask = (1ULL<<(8*d->config.cfg_access.cap.length))-1; + d->config.cfg_access.window + = emulate_mmio_read(d, + d->config.cfg_access.cap.offset, + read_mask); + verbose("Window read %#x/%#x from bar %u, offset %u len %u\n", + d->config.cfg_access.window, read_mask, + d->config.cfg_access.cap.bar, + d->config.cfg_access.cap.offset, + d->config.cfg_access.cap.length); + } ioread(port - PCI_CONFIG_DATA, d->config_words[reg], mask, val); } From 713e3f72244cb67fe1ad5c82a061c0b1be2f2fc5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:25:01 +1030 Subject: [PATCH 303/360] lguest: always put console in PCI slot #1. This simplifies the early probe. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index e3c4d3d7dc2aa4..7cc1fed1094d22 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -2773,6 +2773,9 @@ int main(int argc, char *argv[]) } } + /* We always have a console device, and it's always device 1. */ + setup_console(); + /* The options are fairly straight-forward */ while ((c = getopt_long(argc, argv, "v", opts, NULL)) != EOF) { switch (c) { @@ -2813,9 +2816,6 @@ int main(int argc, char *argv[]) verbose("Guest base is at %p\n", guest_base); - /* We always have a console device */ - setup_console(); - /* Initialize the (fake) PCI host bridge device. */ init_pci_host_bridge(); From a561adfaecc9eb6fb66941b450458801f3f60ca0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:26:01 +1030 Subject: [PATCH 304/360] lguest: use the PCI console device's emerg_wr for early boot messages. This involves manually checking the console device (which is always in slot 1 of bus 0) and using the window in VIRTIO_PCI_CAP_PCI_CFG to program it (as we can't map the BAR yet). We could in fact do this much earlier, but we wait for the first write from the virtio_cons_early_init() facility. Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 146 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 134 insertions(+), 12 deletions(-) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 2943ab931671d9..531b844cb48db0 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ #include /* for struct machine_ops */ #include #include +#include /*G:010 * Welcome to the Guest! @@ -1202,25 +1204,145 @@ static __init char *lguest_memory_setup(void) return "LGUEST"; } +/* Offset within PCI config space of BAR access capability. */ +static int console_cfg_offset = 0; +static int console_access_cap; + +/* Set up so that we access off in bar0 (on bus 0, device 1, function 0) */ +static void set_cfg_window(u32 cfg_offset, u32 off) +{ + write_pci_config_byte(0, 1, 0, + cfg_offset + offsetof(struct virtio_pci_cap, bar), + 0); + write_pci_config(0, 1, 0, + cfg_offset + offsetof(struct virtio_pci_cap, length), + 4); + write_pci_config(0, 1, 0, + cfg_offset + offsetof(struct virtio_pci_cap, offset), + off); +} + +static u32 read_bar_via_cfg(u32 cfg_offset, u32 off) +{ + set_cfg_window(cfg_offset, off); + return read_pci_config(0, 1, 0, + cfg_offset + sizeof(struct virtio_pci_cap)); +} + +static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) +{ + set_cfg_window(cfg_offset, off); + write_pci_config(0, 1, 0, + cfg_offset + sizeof(struct virtio_pci_cap), val); +} + +static void probe_pci_console(void) +{ + u8 cap, common_cap = 0, device_cap = 0; + /* Offsets within BAR0 */ + u32 common_offset, device_offset; + + /* Avoid recursive printk into here. */ + console_cfg_offset = -1; + + if (!early_pci_allowed()) { + printk(KERN_ERR "lguest: early PCI access not allowed!\n"); + return; + } + + /* We expect a console PCI device at BUS0, slot 1. */ + if (read_pci_config(0, 1, 0, 0) != 0x10431AF4) { + printk(KERN_ERR "lguest: PCI device is %#x!\n", + read_pci_config(0, 1, 0, 0)); + return; + } + + /* Find the capabilities we need (must be in bar0) */ + cap = read_pci_config_byte(0, 1, 0, PCI_CAPABILITY_LIST); + while (cap) { + u8 vndr = read_pci_config_byte(0, 1, 0, cap); + if (vndr == PCI_CAP_ID_VNDR) { + u8 type, bar; + u32 offset; + + type = read_pci_config_byte(0, 1, 0, + cap + offsetof(struct virtio_pci_cap, cfg_type)); + bar = read_pci_config_byte(0, 1, 0, + cap + offsetof(struct virtio_pci_cap, bar)); + offset = read_pci_config(0, 1, 0, + cap + offsetof(struct virtio_pci_cap, offset)); + + switch (type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + if (bar == 0) { + common_cap = cap; + common_offset = offset; + } + break; + case VIRTIO_PCI_CAP_DEVICE_CFG: + if (bar == 0) { + device_cap = cap; + device_offset = offset; + } + break; + case VIRTIO_PCI_CAP_PCI_CFG: + console_access_cap = cap; + break; + } + } + cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT); + } + if (!common_cap || !device_cap || !console_access_cap) { + printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n", + common_cap, device_cap, console_access_cap); + return; + } + + +#define write_common_config(reg, val) \ + write_bar_via_cfg(console_access_cap, \ + common_offset+offsetof(struct virtio_pci_common_cfg,reg),\ + val) + +#define read_common_config(reg) \ + read_bar_via_cfg(console_access_cap, \ + common_offset+offsetof(struct virtio_pci_common_cfg,reg)) + + /* Check features: they must offer EMERG_WRITE */ + write_common_config(device_feature_select, 0); + + if (!(read_common_config(device_feature) + & (1 << VIRTIO_CONSOLE_F_EMERG_WRITE))) { + printk(KERN_ERR "lguest: console missing EMERG_WRITE\n"); + return; + } + + console_cfg_offset = device_offset; +} + /* * We will eventually use the virtio console device to produce console output, - * but before that is set up we use LHCALL_NOTIFY on normal memory to produce - * console output. + * but before that is set up we use the virtio PCI console's backdoor mmio + * access and the "emergency" write facility (which is legal even before the + * device is configured). */ static __init int early_put_chars(u32 vtermno, const char *buf, int count) { - char scratch[17]; - unsigned int len = count; + /* If we couldn't find PCI console, forget it. */ + if (console_cfg_offset < 0) + return count; - /* We use a nul-terminated string, so we make a copy. Icky, huh? */ - if (len > sizeof(scratch) - 1) - len = sizeof(scratch) - 1; - scratch[len] = '\0'; - memcpy(scratch, buf, len); - hcall(LHCALL_NOTIFY, __pa(scratch), 0, 0, 0); + if (unlikely(!console_cfg_offset)) { + probe_pci_console(); + if (console_cfg_offset < 0) + return count; + } - /* This routine returns the number of bytes actually written. */ - return len; + write_bar_via_cfg(console_access_cap, + console_cfg_offset + + offsetof(struct virtio_console_config, emerg_wr), + buf[0]); + return 1; } /* From 00f8d546512a7661d43600625f87a42a98cae26a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:27:01 +1030 Subject: [PATCH 305/360] lguest: remove NOTIFY facility from demonstration launcher. This was only used for early console, now we can get rid of it altogether. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 7cc1fed1094d22..5d104321f70f73 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1079,23 +1079,6 @@ static void cleanup_devices(void) tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); } -/*L:215 - * This is the generic routine we call when the Guest uses LHCALL_NOTIFY. - */ -static void handle_output(unsigned long addr) -{ - /* - * Early console write is done using notify on a nul-terminated string - * in Guest memory. It's also great for hacking debugging messages - * into a Guest. - */ - if (addr >= guest_limit) - errx(1, "Bad NOTIFY %#lx", addr); - - write(STDOUT_FILENO, from_guest_phys(addr), - strnlen(from_guest_phys(addr), guest_limit - addr)); -} - /*L:217 * We do PCI. This is mainly done to let us test the kernel virtio PCI * code. @@ -2662,14 +2645,8 @@ static void __attribute__((noreturn)) run_guest(void) /* We read from the /dev/lguest device to run the Guest. */ readval = pread(lguest_fd, ¬ify, sizeof(notify), cpu_id); - - /* One unsigned long means the Guest did HCALL_NOTIFY */ if (readval == sizeof(notify)) { - if (notify.trap == 0x1F) { - verbose("Notify on address %#08x\n", - notify.addr); - handle_output(notify.addr); - } else if (notify.trap == 13) { + if (notify.trap == 13) { verbose("Emulating instruction at %#x\n", getreg(eip)); emulate_insn(notify.insn); From d9bab50aa46ce46dd4537d455eb13b200cdac516 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Feb 2015 15:28:01 +1030 Subject: [PATCH 306/360] lguest: remove NOTIFY call and eventfd facility. Disappointing, as this was kind of neat (especially getting to use RCU to manage the address -> eventfd mapping). But now the devices are PCI handled in userspace, we get rid of both the NOTIFY hypercall and the interface to connect an eventfd. Signed-off-by: Rusty Russell --- arch/x86/include/asm/lguest_hcall.h | 1 - drivers/lguest/core.c | 20 +-- drivers/lguest/hypercalls.c | 4 - drivers/lguest/lg.h | 12 -- drivers/lguest/lguest_user.c | 186 +--------------------------- include/linux/lguest_launcher.h | 2 +- 6 files changed, 10 insertions(+), 215 deletions(-) diff --git a/arch/x86/include/asm/lguest_hcall.h b/arch/x86/include/asm/lguest_hcall.h index 879fd7d3387751..ef01fef3eebc65 100644 --- a/arch/x86/include/asm/lguest_hcall.h +++ b/arch/x86/include/asm/lguest_hcall.h @@ -16,7 +16,6 @@ #define LHCALL_SET_PTE 14 #define LHCALL_SET_PGD 15 #define LHCALL_LOAD_TLS 16 -#define LHCALL_NOTIFY 17 #define LHCALL_LOAD_GDT_ENTRY 18 #define LHCALL_SEND_INTERRUPTS 19 diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 9159dbc583f67e..7dc93aa004c86c 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -225,22 +225,12 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) if (cpu->hcall) do_hypercalls(cpu); - /* - * It's possible the Guest did a NOTIFY hypercall to the - * Launcher. - */ + /* Do we have to tell the Launcher about a trap? */ if (cpu->pending.trap) { - /* - * Does it just needs to write to a registered - * eventfd (ie. the appropriate virtqueue thread)? - */ - if (!send_notify_to_eventfd(cpu)) { - /* OK, we tell the main Launcher. */ - if (copy_to_user(user, &cpu->pending, - sizeof(cpu->pending))) - return -EFAULT; - return sizeof(cpu->pending); - } + if (copy_to_user(user, &cpu->pending, + sizeof(cpu->pending))) + return -EFAULT; + return sizeof(cpu->pending); } /* diff --git a/drivers/lguest/hypercalls.c b/drivers/lguest/hypercalls.c index 5dd1fb8a6610a4..1219af493c0f18 100644 --- a/drivers/lguest/hypercalls.c +++ b/drivers/lguest/hypercalls.c @@ -117,10 +117,6 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args) /* Similarly, this sets the halted flag for run_guest(). */ cpu->halted = 1; break; - case LHCALL_NOTIFY: - cpu->pending.trap = LGUEST_TRAP_ENTRY; - cpu->pending.addr = args->arg1; - break; default: /* It should be an architecture-specific hypercall. */ if (lguest_arch_do_hcall(cpu, args)) diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index eb81abc05995ac..307e8b39e7d1dd 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -81,16 +81,6 @@ struct lg_cpu { struct lg_cpu_arch arch; }; -struct lg_eventfd { - unsigned long addr; - struct eventfd_ctx *event; -}; - -struct lg_eventfd_map { - unsigned int num; - struct lg_eventfd map[]; -}; - /* The private info the thread maintains about the guest. */ struct lguest { struct lguest_data __user *lguest_data; @@ -117,8 +107,6 @@ struct lguest { unsigned int stack_pages; u32 tsc_khz; - struct lg_eventfd_map *eventfds; - /* Dead? */ const char *dead; }; diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index c8b0e8575b44fa..c4c6113eb9a617 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -2,182 +2,20 @@ * launcher controls and communicates with the Guest. For example, * the first write will tell us the Guest's memory layout and entry * point. A read will run the Guest until something happens, such as - * a signal or the Guest doing a NOTIFY out to the Launcher. There is - * also a way for the Launcher to attach eventfds to particular NOTIFY - * values instead of returning from the read() call. + * a signal or the Guest accessing a device. :*/ #include #include #include #include -#include #include #include #include #include "lg.h" -/*L:056 - * Before we move on, let's jump ahead and look at what the kernel does when - * it needs to look up the eventfds. That will complete our picture of how we - * use RCU. - * - * The notification value is in cpu->pending_notify: we return true if it went - * to an eventfd. - */ -bool send_notify_to_eventfd(struct lg_cpu *cpu) -{ - unsigned int i; - struct lg_eventfd_map *map; - - /* We only connect LHCALL_NOTIFY to event fds, not other traps. */ - if (cpu->pending.trap != LGUEST_TRAP_ENTRY) - return false; - - /* - * This "rcu_read_lock()" helps track when someone is still looking at - * the (RCU-using) eventfds array. It's not actually a lock at all; - * indeed it's a noop in many configurations. (You didn't expect me to - * explain all the RCU secrets here, did you?) - */ - rcu_read_lock(); - /* - * rcu_dereference is the counter-side of rcu_assign_pointer(); it - * makes sure we don't access the memory pointed to by - * cpu->lg->eventfds before cpu->lg->eventfds is set. Sounds crazy, - * but Alpha allows this! Paul McKenney points out that a really - * aggressive compiler could have the same effect: - * http://lists.ozlabs.org/pipermail/lguest/2009-July/001560.html - * - * So play safe, use rcu_dereference to get the rcu-protected pointer: - */ - map = rcu_dereference(cpu->lg->eventfds); - /* - * Simple array search: even if they add an eventfd while we do this, - * we'll continue to use the old array and just won't see the new one. - */ - for (i = 0; i < map->num; i++) { - if (map->map[i].addr == cpu->pending.addr) { - eventfd_signal(map->map[i].event, 1); - cpu->pending.trap = 0; - break; - } - } - /* We're done with the rcu-protected variable cpu->lg->eventfds. */ - rcu_read_unlock(); - - /* If we cleared the notification, it's because we found a match. */ - return cpu->pending.trap == 0; -} - -/*L:055 - * One of the more tricksy tricks in the Linux Kernel is a technique called - * Read Copy Update. Since one point of lguest is to teach lguest journeyers - * about kernel coding, I use it here. (In case you're curious, other purposes - * include learning about virtualization and instilling a deep appreciation for - * simplicity and puppies). - * - * We keep a simple array which maps LHCALL_NOTIFY values to eventfds, but we - * add new eventfds without ever blocking readers from accessing the array. - * The current Launcher only does this during boot, so that never happens. But - * Read Copy Update is cool, and adding a lock risks damaging even more puppies - * than this code does. - * - * We allocate a brand new one-larger array, copy the old one and add our new - * element. Then we make the lg eventfd pointer point to the new array. - * That's the easy part: now we need to free the old one, but we need to make - * sure no slow CPU somewhere is still looking at it. That's what - * synchronize_rcu does for us: waits until every CPU has indicated that it has - * moved on to know it's no longer using the old one. - * - * If that's unclear, see http://en.wikipedia.org/wiki/Read-copy-update. - */ -static int add_eventfd(struct lguest *lg, unsigned long addr, int fd) -{ - struct lg_eventfd_map *new, *old = lg->eventfds; - - /* - * We don't allow notifications on value 0 anyway (pending_notify of - * 0 means "nothing pending"). - */ - if (!addr) - return -EINVAL; - - /* - * Replace the old array with the new one, carefully: others can - * be accessing it at the same time. - */ - new = kmalloc(sizeof(*new) + sizeof(new->map[0]) * (old->num + 1), - GFP_KERNEL); - if (!new) - return -ENOMEM; - - /* First make identical copy. */ - memcpy(new->map, old->map, sizeof(old->map[0]) * old->num); - new->num = old->num; - - /* Now append new entry. */ - new->map[new->num].addr = addr; - new->map[new->num].event = eventfd_ctx_fdget(fd); - if (IS_ERR(new->map[new->num].event)) { - int err = PTR_ERR(new->map[new->num].event); - kfree(new); - return err; - } - new->num++; - - /* - * Now put new one in place: rcu_assign_pointer() is a fancy way of - * doing "lg->eventfds = new", but it uses memory barriers to make - * absolutely sure that the contents of "new" written above is nailed - * down before we actually do the assignment. - * - * We have to think about these kinds of things when we're operating on - * live data without locks. - */ - rcu_assign_pointer(lg->eventfds, new); - - /* - * We're not in a big hurry. Wait until no one's looking at old - * version, then free it. - */ - synchronize_rcu(); - kfree(old); - - return 0; -} - /*L:052 - * Receiving notifications from the Guest is usually done by attaching a - * particular LHCALL_NOTIFY value to an event filedescriptor. The eventfd will - * become readable when the Guest does an LHCALL_NOTIFY with that value. - * - * This is really convenient for processing each virtqueue in a separate - * thread. - */ -static int attach_eventfd(struct lguest *lg, const unsigned long __user *input) -{ - unsigned long addr, fd; - int err; - - if (get_user(addr, input) != 0) - return -EFAULT; - input++; - if (get_user(fd, input) != 0) - return -EFAULT; - - /* - * Just make sure two callers don't add eventfds at once. We really - * only need to lock against callers adding to the same Guest, so using - * the Big Lguest Lock is overkill. But this is setup, not a fast path. - */ - mutex_lock(&lguest_lock); - err = add_eventfd(lg, addr, fd); - mutex_unlock(&lguest_lock); - - return err; -} - -/* The Launcher can get the registers, and also set some of them. */ + The Launcher can get the registers, and also set some of them. +*/ static int getreg_setup(struct lg_cpu *cpu, const unsigned long __user *input) { unsigned long which; @@ -409,13 +247,6 @@ static int initialize(struct file *file, const unsigned long __user *input) goto unlock; } - lg->eventfds = kmalloc(sizeof(*lg->eventfds), GFP_KERNEL); - if (!lg->eventfds) { - err = -ENOMEM; - goto free_lg; - } - lg->eventfds->num = 0; - /* Populate the easy fields of our "struct lguest" */ lg->mem_base = (void __user *)args[0]; lg->pfn_limit = args[1]; @@ -424,7 +255,7 @@ static int initialize(struct file *file, const unsigned long __user *input) /* This is the first cpu (cpu 0) and it will start booting at args[2] */ err = lg_cpu_start(&lg->cpus[0], 0, args[2]); if (err) - goto free_eventfds; + goto free_lg; /* * Initialize the Guest's shadow page tables. This allocates @@ -445,8 +276,6 @@ static int initialize(struct file *file, const unsigned long __user *input) free_regs: /* FIXME: This should be in free_vcpu */ free_page(lg->cpus[0].regs_page); -free_eventfds: - kfree(lg->eventfds); free_lg: kfree(lg); unlock: @@ -499,8 +328,6 @@ static ssize_t write(struct file *file, const char __user *in, return initialize(file, input); case LHREQ_IRQ: return user_send_irq(cpu, input); - case LHREQ_EVENTFD: - return attach_eventfd(lg, input); case LHREQ_GETREG: return getreg_setup(cpu, input); case LHREQ_SETREG: @@ -551,11 +378,6 @@ static int close(struct inode *inode, struct file *file) mmput(lg->cpus[i].mm); } - /* Release any eventfds they registered. */ - for (i = 0; i < lg->eventfds->num; i++) - eventfd_ctx_put(lg->eventfds->map[i].event); - kfree(lg->eventfds); - /* * If lg->dead doesn't contain an error code it will be NULL or a * kmalloc()ed string, either of which is ok to hand to kfree(). diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h index 677cde735d4bb0..acd5b12565cc27 100644 --- a/include/linux/lguest_launcher.h +++ b/include/linux/lguest_launcher.h @@ -23,7 +23,7 @@ enum lguest_req LHREQ_GETDMA, /* No longer used */ LHREQ_IRQ, /* + irq */ LHREQ_BREAK, /* No longer used */ - LHREQ_EVENTFD, /* + address, fd. */ + LHREQ_EVENTFD, /* No longer used. */ LHREQ_GETREG, /* + offset within struct pt_regs (then read value). */ LHREQ_SETREG, /* + offset within struct pt_regs, value. */ LHREQ_TRAP, /* + trap number to deliver to guest. */ From feb28979c137ba3f649ad36fc27c85c64c111f78 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 11 Feb 2015 04:58:35 +0000 Subject: [PATCH 307/360] of/pci: Remove duplicate kfree in of_pci_get_host_bridge_resources() Commit d2be00c0fb5a ("of/pci: Free resources on failure in of_pci_get_host_bridge_resources()") fixed the error path so it frees everything on the "resources" list. That list includes the bus_range, so we should not free it again. Remove the superfluous free of bus_range. [bhelgaas: changelog] Fixes: d2be00c0fb5a ("of/pci: Free resources on failure in of_pci_get_host_bridge_resources()") Reported-by: Jiang Liu Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas CC: Rafael J. Wysocki --- drivers/of/of_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 110fece2ff5372..62426d81a4d656 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -229,7 +229,6 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, resource_list_for_each_entry(window, resources) kfree(window->res); pci_free_resource_list(resources); - kfree(bus_range); return err; } EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); From 82bf90c62834842249c4f94535079f5cbec9014c Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 21 Jan 2015 00:09:46 +0300 Subject: [PATCH 308/360] dmaengine: shdmac: use SET_RUNTIME_PM_OPS() Use SET_RUNTIME_PM_OPS() to initialize the runtime PM method pointers in the 'struct dev_pm_ops'; since that macro doesn't do anything if CONFIG_PM is not defined, we have to move #ifdef up to also cover the runtime PM methods in order to avoid compilation warnings. Based on orignal patch by Mikhail Ulyanov . Signed-off-by: Sergei Shtylyov Signed-off-by: Vinod Koul --- drivers/dma/sh/shdmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index ce4cd6be07cf8d..023344f7f78ee6 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -588,6 +588,7 @@ static void sh_dmae_shutdown(struct platform_device *pdev) sh_dmae_ctl_stop(shdev); } +#ifdef CONFIG_PM static int sh_dmae_runtime_suspend(struct device *dev) { return 0; @@ -600,7 +601,6 @@ static int sh_dmae_runtime_resume(struct device *dev) return sh_dmae_rst(shdev); } -#ifdef CONFIG_PM static int sh_dmae_suspend(struct device *dev) { return 0; @@ -640,8 +640,8 @@ static int sh_dmae_resume(struct device *dev) static const struct dev_pm_ops sh_dmae_pm = { .suspend = sh_dmae_suspend, .resume = sh_dmae_resume, - .runtime_suspend = sh_dmae_runtime_suspend, - .runtime_resume = sh_dmae_runtime_resume, + SET_RUNTIME_PM_OPS(sh_dmae_runtime_suspend, sh_dmae_runtime_resume, + NULL) }; static dma_addr_t sh_dmae_slave_addr(struct shdma_chan *schan) From bf44a4175e566c72ae2d01929f76a04a9e861e0d Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 21 Jan 2015 00:13:21 +0300 Subject: [PATCH 309/360] dmaengine: shdmac: extend PM methods In order to make it possible to restore from hibernation not only in Linux but also in e.g. U-Boot, we have to use sh_dmae_{suspend|resume}() for the {freeze| thaw|restore}() PM methods. It's handy to achieve this with SIMPLE_DEV_PM_OPS() macro; since that macro doesn't do anything when CONFIG_PM_SLEEP is undefined, we don't need to #define sh_dmae_{suspend|resume} NULL anymore but we'll have to enclose sh_dmae_{suspend|resume}() into the new #ifdef... Based on original patch by Mikhail Ulyanov . Signed-off-by: Sergei Shtylyov Signed-off-by: Vinod Koul --- drivers/dma/sh/shdmac.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 023344f7f78ee6..b2431aa3003312 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -600,7 +600,9 @@ static int sh_dmae_runtime_resume(struct device *dev) return sh_dmae_rst(shdev); } +#endif +#ifdef CONFIG_PM_SLEEP static int sh_dmae_suspend(struct device *dev) { return 0; @@ -632,14 +634,10 @@ static int sh_dmae_resume(struct device *dev) return 0; } -#else -#define sh_dmae_suspend NULL -#define sh_dmae_resume NULL #endif static const struct dev_pm_ops sh_dmae_pm = { - .suspend = sh_dmae_suspend, - .resume = sh_dmae_resume, + SET_SYSTEM_SLEEP_PM_OPS(sh_dmae_suspend, sh_dmae_resume) SET_RUNTIME_PM_OPS(sh_dmae_runtime_suspend, sh_dmae_runtime_resume, NULL) }; From a55e07c8a5aaf5442d10b0b392ce8ce41a96921d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Jan 2015 18:29:25 +0200 Subject: [PATCH 310/360] dmaengine: rcar-dmac: Fix uninitialized variable usage The desc variable is used uninitialized in the rcar_dmac_desc_get() and rcar_dmac_xfer_chunk_get() functions if descriptors need to be allocated. Fix it. Reported-by: Dan Carpenter Signed-off-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 69 +++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 29dd09ad41ff29..8367578bac638d 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -549,26 +549,22 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan) spin_lock_irq(&chan->lock); - do { - if (list_empty(&chan->desc.free)) { - /* - * No free descriptors, allocate a page worth of them - * and try again, as someone else could race us to get - * the newly allocated descriptors. If the allocation - * fails return an error. - */ - spin_unlock_irq(&chan->lock); - ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT); - if (ret < 0) - return NULL; - spin_lock_irq(&chan->lock); - continue; - } + while (list_empty(&chan->desc.free)) { + /* + * No free descriptors, allocate a page worth of them and try + * again, as someone else could race us to get the newly + * allocated descriptors. If the allocation fails return an + * error. + */ + spin_unlock_irq(&chan->lock); + ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT); + if (ret < 0) + return NULL; + spin_lock_irq(&chan->lock); + } - desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, - node); - list_del(&desc->node); - } while (!desc); + desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node); + list_del(&desc->node); spin_unlock_irq(&chan->lock); @@ -621,26 +617,23 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan) spin_lock_irq(&chan->lock); - do { - if (list_empty(&chan->desc.chunks_free)) { - /* - * No free descriptors, allocate a page worth of them - * and try again, as someone else could race us to get - * the newly allocated descriptors. If the allocation - * fails return an error. - */ - spin_unlock_irq(&chan->lock); - ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT); - if (ret < 0) - return NULL; - spin_lock_irq(&chan->lock); - continue; - } + while (list_empty(&chan->desc.chunks_free)) { + /* + * No free descriptors, allocate a page worth of them and try + * again, as someone else could race us to get the newly + * allocated descriptors. If the allocation fails return an + * error. + */ + spin_unlock_irq(&chan->lock); + ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT); + if (ret < 0) + return NULL; + spin_lock_irq(&chan->lock); + } - chunk = list_first_entry(&chan->desc.chunks_free, - struct rcar_dmac_xfer_chunk, node); - list_del(&chunk->node); - } while (!chunk); + chunk = list_first_entry(&chan->desc.chunks_free, + struct rcar_dmac_xfer_chunk, node); + list_del(&chunk->node); spin_unlock_irq(&chan->lock); From a2e199915725e666772dd077dbffbef154e58096 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 13 Feb 2015 17:13:40 +1030 Subject: [PATCH 311/360] virtual: Documentation: simplify and generalize paravirt_ops.txt The general documentation we have for pv_ops is currenty present on the IA64 docs, but since this documentation covers IA64 xen enablement and IA64 Xen support got ripped out a while ago through commit d52eefb47 present since v3.14-rc1 lets just simplify, generalize and move the pv_ops documentation to a shared place. Cc: Isaku Yamahata Cc: Jeremy Fitzhardinge Cc: Chris Wright Cc: Alok Kataria Cc: Rusty Russell Cc: virtualization@lists.linux-foundation.org Cc: Tony Luck Cc: Fenghua Yu Cc: Boris Ostrovsky Cc: xen-devel@lists.xenproject.org Cc: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: Rusty Russell --- Documentation/ia64/paravirt_ops.txt | 137 ------------------------- Documentation/virtual/00-INDEX | 3 + Documentation/virtual/paravirt_ops.txt | 32 ++++++ MAINTAINERS | 2 +- 4 files changed, 36 insertions(+), 138 deletions(-) delete mode 100644 Documentation/ia64/paravirt_ops.txt create mode 100644 Documentation/virtual/paravirt_ops.txt diff --git a/Documentation/ia64/paravirt_ops.txt b/Documentation/ia64/paravirt_ops.txt deleted file mode 100644 index 39ded02ec33fc8..00000000000000 --- a/Documentation/ia64/paravirt_ops.txt +++ /dev/null @@ -1,137 +0,0 @@ -Paravirt_ops on IA64 -==================== - 21 May 2008, Isaku Yamahata - - -Introduction ------------- -The aim of this documentation is to help with maintainability and/or to -encourage people to use paravirt_ops/IA64. - -paravirt_ops (pv_ops in short) is a way for virtualization support of -Linux kernel on x86. Several ways for virtualization support were -proposed, paravirt_ops is the winner. -On the other hand, now there are also several IA64 virtualization -technologies like kvm/IA64, xen/IA64 and many other academic IA64 -hypervisors so that it is good to add generic virtualization -infrastructure on Linux/IA64. - - -What is paravirt_ops? ---------------------- -It has been developed on x86 as virtualization support via API, not ABI. -It allows each hypervisor to override operations which are important for -hypervisors at API level. And it allows a single kernel binary to run on -all supported execution environments including native machine. -Essentially paravirt_ops is a set of function pointers which represent -operations corresponding to low level sensitive instructions and high -level functionalities in various area. But one significant difference -from usual function pointer table is that it allows optimization with -binary patch. It is because some of these operations are very -performance sensitive and indirect call overhead is not negligible. -With binary patch, indirect C function call can be transformed into -direct C function call or in-place execution to eliminate the overhead. - -Thus, operations of paravirt_ops are classified into three categories. -- simple indirect call - These operations correspond to high level functionality so that the - overhead of indirect call isn't very important. - -- indirect call which allows optimization with binary patch - Usually these operations correspond to low level instructions. They - are called frequently and performance critical. So the overhead is - very important. - -- a set of macros for hand written assembly code - Hand written assembly codes (.S files) also need paravirtualization - because they include sensitive instructions or some of code paths in - them are very performance critical. - - -The relation to the IA64 machine vector ---------------------------------------- -Linux/IA64 has the IA64 machine vector functionality which allows the -kernel to switch implementations (e.g. initialization, ipi, dma api...) -depending on executing platform. -We can replace some implementations very easily defining a new machine -vector. Thus another approach for virtualization support would be -enhancing the machine vector functionality. -But paravirt_ops approach was taken because -- virtualization support needs wider support than machine vector does. - e.g. low level instruction paravirtualization. It must be - initialized very early before platform detection. - -- virtualization support needs more functionality like binary patch. - Probably the calling overhead might not be very large compared to the - emulation overhead of virtualization. However in the native case, the - overhead should be eliminated completely. - A single kernel binary should run on each environment including native, - and the overhead of paravirt_ops on native environment should be as - small as possible. - -- for full virtualization technology, e.g. KVM/IA64 or - Xen/IA64 HVM domain, the result would be - (the emulated platform machine vector. probably dig) + (pv_ops). - This means that the virtualization support layer should be under - the machine vector layer. - -Possibly it might be better to move some function pointers from -paravirt_ops to machine vector. In fact, Xen domU case utilizes both -pv_ops and machine vector. - - -IA64 paravirt_ops ------------------ -In this section, the concrete paravirt_ops will be discussed. -Because of the architecture difference between ia64 and x86, the -resulting set of functions is very different from x86 pv_ops. - -- C function pointer tables -They are not very performance critical so that simple C indirect -function call is acceptable. The following structures are defined at -this moment. For details see linux/include/asm-ia64/paravirt.h - - struct pv_info - This structure describes the execution environment. - - struct pv_init_ops - This structure describes the various initialization hooks. - - struct pv_iosapic_ops - This structure describes hooks to iosapic operations. - - struct pv_irq_ops - This structure describes hooks to irq related operations - - struct pv_time_op - This structure describes hooks to steal time accounting. - -- a set of indirect calls which need optimization -Currently this class of functions correspond to a subset of IA64 -intrinsics. At this moment the optimization with binary patch isn't -implemented yet. -struct pv_cpu_op is defined. For details see -linux/include/asm-ia64/paravirt_privop.h -Mostly they correspond to ia64 intrinsics 1-to-1. -Caveat: Now they are defined as C indirect function pointers, but in -order to support binary patch optimization, they will be changed -using GCC extended inline assembly code. - -- a set of macros for hand written assembly code (.S files) -For maintenance purpose, the taken approach for .S files is single -source code and compile multiple times with different macros definitions. -Each pv_ops instance must define those macros to compile. -The important thing here is that sensitive, but non-privileged -instructions must be paravirtualized and that some privileged -instructions also need paravirtualization for reasonable performance. -Developers who modify .S files must be aware of that. At this moment -an easy checker is implemented to detect paravirtualization breakage. -But it doesn't cover all the cases. - -Sometimes this set of macros is called pv_cpu_asm_op. But there is no -corresponding structure in the source code. -Those macros mostly 1:1 correspond to a subset of privileged -instructions. See linux/include/asm-ia64/native/inst.h. -And some functions written in assembly also need to be overrided so -that each pv_ops instance have to define some macros. Again see -linux/include/asm-ia64/native/inst.h. - - -Those structures must be initialized very early before start_kernel. -Probably initialized in head.S using multi entry point or some other trick. -For native case implementation see linux/arch/ia64/kernel/paravirt.c. diff --git a/Documentation/virtual/00-INDEX b/Documentation/virtual/00-INDEX index e952d30bbf0f8f..af0d23968ee710 100644 --- a/Documentation/virtual/00-INDEX +++ b/Documentation/virtual/00-INDEX @@ -2,6 +2,9 @@ Virtualization support in the Linux kernel. 00-INDEX - this file. + +paravirt_ops.txt + - Describes the Linux kernel pv_ops to support different hypervisors kvm/ - Kernel Virtual Machine. See also http://linux-kvm.org uml/ diff --git a/Documentation/virtual/paravirt_ops.txt b/Documentation/virtual/paravirt_ops.txt new file mode 100644 index 00000000000000..d4881c00e3396a --- /dev/null +++ b/Documentation/virtual/paravirt_ops.txt @@ -0,0 +1,32 @@ +Paravirt_ops +============ + +Linux provides support for different hypervisor virtualization technologies. +Historically different binary kernels would be required in order to support +different hypervisors, this restriction was removed with pv_ops. +Linux pv_ops is a virtualization API which enables support for different +hypervisors. It allows each hypervisor to override critical operations and +allows a single kernel binary to run on all supported execution environments +including native machine -- without any hypervisors. + +pv_ops provides a set of function pointers which represent operations +corresponding to low level critical instructions and high level +functionalities in various areas. pv-ops allows for optimizations at run +time by enabling binary patching of the low-ops critical operations +at boot time. + +pv_ops operations are classified into three categories: + +- simple indirect call + These operations correspond to high level functionality where it is + known that the overhead of indirect call isn't very important. + +- indirect call which allows optimization with binary patch + Usually these operations correspond to low level critical instructions. They + are called frequently and are performance critical. The overhead is + very important. + +- a set of macros for hand written assembly code + Hand written assembly codes (.S files) also need paravirtualization + because they include sensitive instructions or some of code paths in + them are very performance critical. diff --git a/MAINTAINERS b/MAINTAINERS index 93409ade65a5b1..9af1c6e5ee6576 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7171,7 +7171,7 @@ M: Alok Kataria M: Rusty Russell L: virtualization@lists.linux-foundation.org S: Supported -F: Documentation/ia64/paravirt_ops.txt +F: Documentation/virtual/paravirt_ops.txt F: arch/*/kernel/paravirt* F: arch/*/include/asm/paravirt.h From d2dbdac336e8ea1296fd08c4eb8a28daacec1817 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:40 +1030 Subject: [PATCH 312/360] tools/lguest: handle device reset correctly in example launcher. The example launcher doesn't reset the queue_enable like the spec says we have to. Plus, we should reset the size in case they negotiated a different (smaller) one. This is easy to test by unloading and reloading a virtio module. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 5d104321f70f73..60cabafdf615e0 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1037,6 +1037,12 @@ static void kill_launcher(int signal) kill(0, SIGTERM); } +static void reset_vq_pci_config(struct virtqueue *vq) +{ + vq->pci_config.queue_size = VIRTQUEUE_NUM; + vq->pci_config.queue_enable = 0; +} + static void reset_device(struct device *dev) { struct virtqueue *vq; @@ -1049,8 +1055,19 @@ static void reset_device(struct device *dev) /* We're going to be explicitly killing threads, so ignore them. */ signal(SIGCHLD, SIG_IGN); + /* + * 4.1.4.3.1: + * + * The device MUST present a 0 in queue_enable on reset. + * + * This means we set it here, and reset the saved ones in every vq. + */ + dev->mmio->cfg.queue_enable = 0; + /* Get rid of the virtqueue threads */ for (vq = dev->vq; vq; vq = vq->next) { + vq->last_avail_idx = 0; + reset_vq_pci_config(vq); if (vq->thread != (pid_t)-1) { kill(vq->thread, SIGTERM); waitpid(vq->thread, NULL, 0); @@ -1952,8 +1969,7 @@ static void add_pci_virtqueue(struct device *dev, vq->thread = (pid_t)-1; /* Initialize the configuration. */ - vq->pci_config.queue_size = VIRTQUEUE_NUM; - vq->pci_config.queue_enable = 0; + reset_vq_pci_config(vq); vq->pci_config.queue_notify_off = 0; /* Add one to the number of queues */ From 53aceb49f9b7e1d42064ffff4f4df7e9882b182d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:41 +1030 Subject: [PATCH 313/360] tools/lguest: fix features_accepted logic in example launcher. We were clearing the lower bits when setting the upper bits. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 60cabafdf615e0..b3e73f258910a6 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1721,7 +1721,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) d->features_accepted |= val; } else { assert(d->mmio->cfg.guest_feature_select == 1); - d->features_accepted &= ((u64)0xFFFFFFFF << 32); + d->features_accepted &= 0xFFFFFFFF; d->features_accepted |= ((u64)val) << 32; } if (d->features_accepted & ~d->features) From b2ce1ea4427f0c752f8718a411435cc9527faa3d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:41 +1030 Subject: [PATCH 314/360] tools/lguest: rename virtio_pci_cfg_cap field to match spec. The next patch will insert many quotes from the virtio 1.0 spec; they make most sense if we copy the spec. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index b3e73f258910a6..b00263f5febbbf 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -126,7 +126,7 @@ static struct device_list devices; struct virtio_pci_cfg_cap { struct virtio_pci_cap cap; - u32 window; /* Data for BAR access. */ + u32 pci_cfg_data; /* Data for BAR access. */ }; struct virtio_pci_mmio { @@ -1301,7 +1301,7 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) */ iowrite(portoff, val, mask, &d->config_words[reg]); return true; - } else if (&d->config_words[reg] == &d->config.cfg_access.window) { + } else if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) { u32 write_mask; /* Must be bar 0 */ @@ -1309,7 +1309,7 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) return false; /* First copy what they wrote into the window */ - iowrite(portoff, val, mask, &d->config.cfg_access.window); + iowrite(portoff, val, mask, &d->config.cfg_access.pci_cfg_data); /* * Now emulate a write. The mask we use is set by @@ -1317,13 +1317,14 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) */ write_mask = (1ULL<<(8*d->config.cfg_access.cap.length)) - 1; verbose("Window writing %#x/%#x to bar %u, offset %u len %u\n", - d->config.cfg_access.window, write_mask, + d->config.cfg_access.pci_cfg_data, write_mask, d->config.cfg_access.cap.bar, d->config.cfg_access.cap.offset, d->config.cfg_access.cap.length); emulate_mmio_write(d, d->config.cfg_access.cap.offset, - d->config.cfg_access.window, write_mask); + d->config.cfg_access.pci_cfg_data, + write_mask); return true; } @@ -1342,7 +1343,7 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val) return; /* Read through the PCI MMIO access window is special */ - if (&d->config_words[reg] == &d->config.cfg_access.window) { + if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) { u32 read_mask; /* Must be bar 0 */ @@ -1357,12 +1358,12 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val) * len, *not* this read! */ read_mask = (1ULL<<(8*d->config.cfg_access.cap.length))-1; - d->config.cfg_access.window + d->config.cfg_access.pci_cfg_data = emulate_mmio_read(d, d->config.cfg_access.cap.offset, read_mask); verbose("Window read %#x/%#x from bar %u, offset %u len %u\n", - d->config.cfg_access.window, read_mask, + d->config.cfg_access.pci_cfg_data, read_mask, d->config.cfg_access.cap.bar, d->config.cfg_access.cap.offset, d->config.cfg_access.cap.length); From 8dc425ffdd20b3462cfb43eb4f94a7ed8296dd63 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:41 +1030 Subject: [PATCH 315/360] tools/lguest: insert device references from the 1.0 spec (4.1 Virtio Over PCI) There are some (optional) parts we don't implement, but this quotes all the device requirements from the spec (csd 03, but it should be the same across all released versions). Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 140 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 9 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index b00263f5febbbf..10a72b81012727 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -673,7 +673,13 @@ static void trigger_irq(struct virtqueue *vq) return; } - /* Set isr to 1 (queue interrupt pending) */ + /* + * 4.1.4.5.1: + * + * If MSI-X capability is disabled, the device MUST set the Queue + * Interrupt bit in ISR status before sending a virtqueue notification + * to the driver. + */ vq->dev->mmio->isr = 0x1; /* Send the Guest an interrupt tell them we used something up. */ @@ -1304,11 +1310,19 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) } else if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) { u32 write_mask; + /* + * 4.1.4.7.1: + * + * Upon detecting driver write access to pci_cfg_data, the + * device MUST execute a write access at offset cap.offset at + * BAR selected by cap.bar using the first cap.length bytes + * from pci_cfg_data. + */ + /* Must be bar 0 */ if (!valid_bar_access(d, &d->config.cfg_access)) return false; - /* First copy what they wrote into the window */ iowrite(portoff, val, mask, &d->config.cfg_access.pci_cfg_data); /* @@ -1346,6 +1360,14 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val) if (&d->config_words[reg] == &d->config.cfg_access.pci_cfg_data) { u32 read_mask; + /* + * 4.1.4.7.1: + * + * Upon detecting driver read access to pci_cfg_data, the + * device MUST execute a read access of length cap.length at + * offset cap.offset at BAR selected by cap.bar and store the + * first cap.length bytes in pci_cfg_data. + */ /* Must be bar 0 */ if (!valid_bar_access(d, &d->config.cfg_access)) errx(1, "Invalid cfg_access to bar%u, offset %u len %u", @@ -1704,6 +1726,13 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) switch (off) { case offsetof(struct virtio_pci_mmio, cfg.device_feature_select): + /* + * 4.1.4.3.1: + * + * The device MUST present the feature bits it is offering in + * device_feature, starting at bit device_feature_select ∗ 32 + * for any device_feature_select written by the driver + */ if (val == 0) d->mmio->cfg.device_feature = d->features; else if (val == 1) @@ -1731,12 +1760,23 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) goto write_through32; case offsetof(struct virtio_pci_mmio, cfg.device_status): verbose("%s: device status -> %#x\n", d->name, val); + /* + * 4.1.4.3.1: + * + * The device MUST reset when 0 is written to device_status, + * and present a 0 in device_status once that is done. + */ if (val == 0) reset_device(d); goto write_through8; case offsetof(struct virtio_pci_mmio, cfg.queue_select): vq = vq_by_num(d, val); - /* Out of range? Return size 0 */ + /* + * 4.1.4.3.1: + * + * The device MUST present a 0 in queue_size if the virtqueue + * corresponding to the current queue_select is unavailable. + */ if (!vq) { d->mmio->cfg.queue_size = 0; goto write_through16; @@ -1841,6 +1881,17 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) goto read_through16; case offsetof(struct virtio_pci_mmio, cfg.device_status): case offsetof(struct virtio_pci_mmio, cfg.config_generation): + /* + * 4.1.4.3.1: + * + * The device MUST present a changed config_generation after + * the driver has read a device-specific configuration value + * which has changed since any part of the device-specific + * configuration was last read. + * + * This is simple: none of our devices change config, so this + * is always 0. + */ goto read_through8; case offsetof(struct virtio_pci_mmio, notify): goto read_through16; @@ -1848,8 +1899,12 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) if (mask != 0xFF) errx(1, "%s: non-8-bit read from offset %u (%#x)", d->name, off, getreg(eip)); - /* Read resets the isr */ isr = d->mmio->isr; + /* + * 4.1.4.5.1: + * + * The device MUST reset ISR status to 0 on driver read. + */ d->mmio->isr = 0; return isr; case offsetof(struct virtio_pci_mmio, padding): @@ -2008,10 +2063,25 @@ static void set_device_config(struct device *dev, const void *conf, size_t len) dev->mmio = realloc(dev->mmio, dev->mmio_size); memcpy(dev->mmio + 1, conf, len); + /* + * 4.1.4.6: + * + * The device MUST present at least one VIRTIO_PCI_CAP_DEVICE_CFG + * capability for any device type which has a device-specific + * configuration. + */ /* Hook up device cfg */ dev->config.cfg_access.cap.cap_next = offsetof(struct pci_config, device); + /* + * 4.1.4.6.1: + * + * The offset for the device-specific configuration MUST be 4-byte + * aligned. + */ + assert(dev->config.cfg_access.cap.cap_next % 4 == 0); + /* Fix up device cfg field length. */ dev->config.device.length = len; @@ -2041,7 +2111,12 @@ static void init_pci_config(struct pci_config *pci, u16 type, { size_t bar_offset, bar_len; - /* Save typing: most thing are happy being zero. */ + /* + * 4.1.4.4.1: + * + * The device MUST either present notify_off_multiplier as an even + * power of 2, or present notify_off_multiplier as 0. + */ memset(pci, 0, sizeof(*pci)); /* 4.1.2.1: Devices MUST have the PCI Vendor ID 0x1AF4 */ @@ -2058,14 +2133,18 @@ static void init_pci_config(struct pci_config *pci, u16 type, pci->subclass = subclass; /* - * 4.1.2.1 Non-transitional devices SHOULD have a PCI Revision - * ID of 1 or higher + * 4.1.2.1: + * + * Non-transitional devices SHOULD have a PCI Revision ID of 1 or + * higher */ pci->revid = 1; /* - * 4.1.2.1 Non-transitional devices SHOULD have a PCI - * Subsystem Device ID of 0x40 or higher. + * 4.1.2.1: + * + * Non-transitional devices SHOULD have a PCI Subsystem Device ID of + * 0x40 or higher. */ pci->subsystem_device_id = 0x40; @@ -2077,17 +2156,48 @@ static void init_pci_config(struct pci_config *pci, u16 type, pci->status = (1 << 4); /* Link them in. */ + /* + * 4.1.4.3.1: + * + * The device MUST present at least one common configuration + * capability. + */ pci->capabilities = offsetof(struct pci_config, common); + /* 4.1.4.3.1 ... offset MUST be 4-byte aligned. */ + assert(pci->capabilities % 4 == 0); + bar_offset = offsetof(struct virtio_pci_mmio, cfg); bar_len = sizeof(((struct virtio_pci_mmio *)0)->cfg); init_cap(&pci->common, sizeof(pci->common), VIRTIO_PCI_CAP_COMMON_CFG, bar_offset, bar_len, offsetof(struct pci_config, notify)); + /* + * 4.1.4.4.1: + * + * The device MUST present at least one notification capability. + */ bar_offset += bar_len; bar_len = sizeof(((struct virtio_pci_mmio *)0)->notify); + + /* + * 4.1.4.4.1: + * + * The cap.offset MUST be 2-byte aligned. + */ + assert(pci->common.cap_next % 2 == 0); + /* FIXME: Use a non-zero notify_off, for per-queue notification? */ + /* + * 4.1.4.4.1: + * + * The value cap.length presented by the device MUST be at least 2 and + * MUST be large enough to support queue notification offsets for all + * supported queues in all possible configurations. + */ + assert(bar_len >= 2); + init_cap(&pci->notify.cap, sizeof(pci->notify), VIRTIO_PCI_CAP_NOTIFY_CFG, bar_offset, bar_len, @@ -2095,11 +2205,23 @@ static void init_pci_config(struct pci_config *pci, u16 type, bar_offset += bar_len; bar_len = sizeof(((struct virtio_pci_mmio *)0)->isr); + /* + * 4.1.4.5.1: + * + * The device MUST present at least one VIRTIO_PCI_CAP_ISR_CFG + * capability. + */ init_cap(&pci->isr, sizeof(pci->isr), VIRTIO_PCI_CAP_ISR_CFG, bar_offset, bar_len, offsetof(struct pci_config, cfg_access)); + /* + * 4.1.4.7.1: + * + * The device MUST present at least one VIRTIO_PCI_CAP_PCI_CFG + * capability. + */ /* This doesn't have any presence in the BAR */ init_cap(&pci->cfg_access.cap, sizeof(pci->cfg_access), VIRTIO_PCI_CAP_PCI_CFG, From c97eb679ef70dbb4482e66b9d192fc9e5bc6e0d6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:42 +1030 Subject: [PATCH 316/360] tools/lguest: insert driver references from the 1.0 spec (4.1 Virtio Over PCI) As a demonstration, the lguest launcher is pretty strict, trying to catch badly behaved drivers. Document this precisely. A good implementation would *NOT* crash the guest when these happened! Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 59 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 10a72b81012727..80dc6346030e9a 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1211,7 +1211,12 @@ static bool valid_bar_access(struct device *d, && cfg_access->cap.length != 4) return false; - /* Offset must be multiple of length */ + /* + * 4.1.4.7.2: + * + * The driver MUST NOT write a cap.offset which is not a multiple of + * cap.length (ie. all accesses MUST be aligned). + */ if (cfg_access->cap.offset % cfg_access->cap.length != 0) return false; @@ -1342,7 +1347,13 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) return true; } - /* Complain about other writes. */ + /* + * 4.1.4.1: + * + * The driver MUST NOT write into any field of the capability + * structure, with the exception of those with cap_type + * VIRTIO_PCI_CAP_PCI_CFG... + */ return false; } @@ -1789,6 +1800,12 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) restore_vq_config(&d->mmio->cfg, vq); goto write_through16; case offsetof(struct virtio_pci_mmio, cfg.queue_size): + /* + * 4.1.4.3.2: + * + * The driver MUST NOT write a value which is not a power of 2 + * to queue_size. + */ if (val & (val-1)) errx(1, "%s: invalid queue size %u\n", d->name, val); if (d->mmio->cfg.queue_enable) @@ -1799,11 +1816,22 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) errx(1, "%s: attempt to set MSIX vector to %u", d->name, val); case offsetof(struct virtio_pci_mmio, cfg.queue_enable): + /* + * 4.1.4.3.2: + * + * The driver MUST NOT write a 0 to queue_enable. + */ if (val != 1) errx(1, "%s: setting queue_enable to %u", d->name, val); d->mmio->cfg.queue_enable = val; save_vq_config(&d->mmio->cfg, vq_by_num(d, d->mmio->cfg.queue_select)); + /* + * 4.1.4.3.2: + * + * The driver MUST configure the other virtqueue fields before + * enabling the virtqueue with queue_enable. + */ enable_virtqueue(d, vq_by_num(d, d->mmio->cfg.queue_select)); goto write_through16; case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off): @@ -1814,6 +1842,12 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) case offsetof(struct virtio_pci_mmio, cfg.queue_avail_hi): case offsetof(struct virtio_pci_mmio, cfg.queue_used_lo): case offsetof(struct virtio_pci_mmio, cfg.queue_used_hi): + /* + * 4.1.4.3.2: + * + * The driver MUST configure the other virtqueue fields before + * enabling the virtqueue with queue_enable. + */ if (d->mmio->cfg.queue_enable) errx(1, "%s: changing queue on live device", d->name); @@ -1837,9 +1871,23 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) } /* Fall through... */ default: + /* + * 4.1.4.3.2: + * + * The driver MUST NOT write to device_feature, num_queues, + * config_generation or queue_notify_off. + */ errx(1, "%s: Unexpected write to offset %u", d->name, off); } + + /* + * 4.1.3.1: + * + * The driver MUST access each field using the “natural” access + * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for + * 16-bit fields and 8-bit accesses for 8-bit fields. + */ write_through32: if (mask != 0xFFFFFFFF) { errx(1, "%s: non-32-bit write to offset %u (%#x)", @@ -1923,6 +1971,13 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) goto read_through8; } + /* + * 4.1.3.1: + * + * The driver MUST access each field using the “natural” access + * method, i.e. 32-bit accesses for 32-bit fields, 16-bit accesses for + * 16-bit fields and 8-bit accesses for 8-bit fields. + */ read_through32: if (mask != 0xFFFFFFFF) errx(1, "%s: non-32-bit read to offset %u (%#x)", From 3afe3e0f8db10a41a5923e1d7498318877473f33 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:42 +1030 Subject: [PATCH 317/360] tools/lguest: handle indirect partway through chain. Linux doesn't generate these, but it's perfectly valid according to a close reading of the spec. I opened virtio spec bug VIRTIO-134 to make this clearer there, too. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 80dc6346030e9a..990671e61f87ad 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -769,20 +769,21 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, * that: no rmb() required. */ - /* - * If this is an indirect entry, then this buffer contains a descriptor - * table which we handle as if it's any normal descriptor chain. - */ - if (desc[i].flags & VRING_DESC_F_INDIRECT) { - if (desc[i].len % sizeof(struct vring_desc)) - errx(1, "Invalid size for indirect buffer table"); + do { + /* + * If this is an indirect entry, then this buffer contains a + * descriptor table which we handle as if it's any normal + * descriptor chain. + */ + if (desc[i].flags & VRING_DESC_F_INDIRECT) { + if (desc[i].len % sizeof(struct vring_desc)) + errx(1, "Invalid size for indirect buffer table"); - max = desc[i].len / sizeof(struct vring_desc); - desc = check_pointer(desc[i].addr, desc[i].len); - i = 0; - } + max = desc[i].len / sizeof(struct vring_desc); + desc = check_pointer(desc[i].addr, desc[i].len); + i = 0; + } - do { /* Grab the first descriptor, and check it's OK. */ iov[*out_num + *in_num].iov_len = desc[i].len; iov[*out_num + *in_num].iov_base From d761b0329108c73020a7c95b6fa0d7e82e35fe8b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:42 +1030 Subject: [PATCH 318/360] tools/lguest: don't start devices until DRIVER_OK status set. We were activating them with the virtqueues, and that's not allowed. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 44 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 990671e61f87ad..4c7c2aa66c8934 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1688,16 +1688,15 @@ static void restore_vq_config(struct virtio_pci_common_cfg *cfg, } /* + * 4.1.4.3.2: + * + * The driver MUST configure the other virtqueue fields before + * enabling the virtqueue with queue_enable. + * * When they enable the virtqueue, we check that their setup is valid. */ -static void enable_virtqueue(struct device *d, struct virtqueue *vq) +static void check_virtqueue(struct device *d, struct virtqueue *vq) { - /* - * Create stack for thread. Since the stack grows upwards, we point - * the stack pointer to the end of this region. - */ - char *stack = malloc(32768); - /* Because lguest is 32 bit, all the descriptor high bits must be 0 */ if (vq->pci_config.queue_desc_hi || vq->pci_config.queue_avail_hi @@ -1716,7 +1715,15 @@ static void enable_virtqueue(struct device *d, struct virtqueue *vq) sizeof(*vq->vring.used) + (sizeof(vq->vring.used->ring[0]) * vq->vring.num)); +} +static void start_virtqueue(struct virtqueue *vq) +{ + /* + * Create stack for thread. Since the stack grows upwards, we point + * the stack pointer to the end of this region. + */ + char *stack = malloc(32768); /* Create a zero-initialized eventfd. */ vq->eventfd = eventfd(0, 0); @@ -1732,6 +1739,16 @@ static void enable_virtqueue(struct device *d, struct virtqueue *vq) err(1, "Creating clone"); } +static void start_virtqueues(struct device *d) +{ + struct virtqueue *vq; + + for (vq = d->vq; vq; vq = vq->next) { + if (vq->pci_config.queue_enable) + start_virtqueue(vq); + } +} + static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) { struct virtqueue *vq; @@ -1780,6 +1797,17 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) */ if (val == 0) reset_device(d); + + /* + * 2.1.2: + * + * The device MUST NOT consume buffers or notify the driver + * before DRIVER_OK. + */ + if (val & VIRTIO_CONFIG_S_DRIVER_OK + && !(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)) + start_virtqueues(d); + goto write_through8; case offsetof(struct virtio_pci_mmio, cfg.queue_select): vq = vq_by_num(d, val); @@ -1833,7 +1861,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * The driver MUST configure the other virtqueue fields before * enabling the virtqueue with queue_enable. */ - enable_virtqueue(d, vq_by_num(d, d->mmio->cfg.queue_select)); + check_virtqueue(d, vq_by_num(d, d->mmio->cfg.queue_select)); goto write_through16; case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off): errx(1, "%s: attempt to write to queue_notify_off", d->name); From 55c2d7884e9a97c2f2d46d5818f783bf3dcc5314 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:43 +1030 Subject: [PATCH 319/360] lguest: don't look in console features to find emerg_wr. The 1.0 spec clearly states that you must set the ACKNOWLEDGE and DRIVER status bits before accessing the feature bits. This is a problem for the early console code, which doesn't really want to acknowledge the device (the spec specifically excepts writing to the console's emerg_wr from the usual ordering constrains). Instead, we check that the *size* of the device configuration is sufficient to hold emerg_wr: at worst (if the device doesn't support the VIRTIO_CONSOLE_F_EMERG_WRITE feature), it will ignore the writes. Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 57 ++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 531b844cb48db0..ac4453d8520efd 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -1222,15 +1222,13 @@ static void set_cfg_window(u32 cfg_offset, u32 off) off); } -static u32 read_bar_via_cfg(u32 cfg_offset, u32 off) -{ - set_cfg_window(cfg_offset, off); - return read_pci_config(0, 1, 0, - cfg_offset + sizeof(struct virtio_pci_cap)); -} - static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) { + /* + * We could set this up once, then leave it; nothing else in the * + * kernel should touch these registers. But if it went wrong, that + * would be a horrible bug to find. + */ set_cfg_window(cfg_offset, off); write_pci_config(0, 1, 0, cfg_offset + sizeof(struct virtio_pci_cap), val); @@ -1239,8 +1237,9 @@ static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) static void probe_pci_console(void) { u8 cap, common_cap = 0, device_cap = 0; - /* Offsets within BAR0 */ - u32 common_offset, device_offset; + /* Offset within BAR0 */ + u32 device_offset; + u32 device_len; /* Avoid recursive printk into here. */ console_cfg_offset = -1; @@ -1263,7 +1262,7 @@ static void probe_pci_console(void) u8 vndr = read_pci_config_byte(0, 1, 0, cap); if (vndr == PCI_CAP_ID_VNDR) { u8 type, bar; - u32 offset; + u32 offset, length; type = read_pci_config_byte(0, 1, 0, cap + offsetof(struct virtio_pci_cap, cfg_type)); @@ -1271,18 +1270,15 @@ static void probe_pci_console(void) cap + offsetof(struct virtio_pci_cap, bar)); offset = read_pci_config(0, 1, 0, cap + offsetof(struct virtio_pci_cap, offset)); + length = read_pci_config(0, 1, 0, + cap + offsetof(struct virtio_pci_cap, length)); switch (type) { - case VIRTIO_PCI_CAP_COMMON_CFG: - if (bar == 0) { - common_cap = cap; - common_offset = offset; - } - break; case VIRTIO_PCI_CAP_DEVICE_CFG: if (bar == 0) { device_cap = cap; device_offset = offset; + device_len = length; } break; case VIRTIO_PCI_CAP_PCI_CFG: @@ -1292,32 +1288,27 @@ static void probe_pci_console(void) } cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT); } - if (!common_cap || !device_cap || !console_access_cap) { + if (!device_cap || !console_access_cap) { printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n", common_cap, device_cap, console_access_cap); return; } - -#define write_common_config(reg, val) \ - write_bar_via_cfg(console_access_cap, \ - common_offset+offsetof(struct virtio_pci_common_cfg,reg),\ - val) - -#define read_common_config(reg) \ - read_bar_via_cfg(console_access_cap, \ - common_offset+offsetof(struct virtio_pci_common_cfg,reg)) - - /* Check features: they must offer EMERG_WRITE */ - write_common_config(device_feature_select, 0); - - if (!(read_common_config(device_feature) - & (1 << VIRTIO_CONSOLE_F_EMERG_WRITE))) { - printk(KERN_ERR "lguest: console missing EMERG_WRITE\n"); + /* + * Note that we can't check features, until we've set the DRIVER + * status bit. We don't want to do that until we have a real driver, + * so we just check that the device-specific config has room for + * emerg_wr. If it doesn't support VIRTIO_CONSOLE_F_EMERG_WRITE + * it should ignore the access. + */ + if (device_len < (offsetof(struct virtio_console_config, emerg_wr) + + sizeof(u32))) { + printk(KERN_ERR "lguest: console missing emerg_wr field\n"); return; } console_cfg_offset = device_offset; + printk(KERN_INFO "lguest: Console via virtio-pci emerg_wr\n"); } /* From d39a6785f40af658224bc3ff3d4c4a5a2f7c9eda Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:43 +1030 Subject: [PATCH 320/360] tools/lguest: more documentation and checking of virtio 1.0 compliance. This is from all the non-PCI parts of the spec. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 307 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 293 insertions(+), 14 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 4c7c2aa66c8934..bc444aff2333dd 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -170,6 +170,9 @@ struct device { /* Is it operational */ bool running; + /* Has it written FEATURES_OK but not re-checked it? */ + bool wrote_features_ok; + /* PCI configuration */ union { struct pci_config config; @@ -668,7 +671,26 @@ static void trigger_irq(struct virtqueue *vq) return; vq->pending_used = 0; - /* If they don't want an interrupt, don't send one... */ + /* + * 2.4.7.1: + * + * If the VIRTIO_F_EVENT_IDX feature bit is not negotiated: + * The driver MUST set flags to 0 or 1. + */ + if (vq->vring.avail->flags > 1) + errx(1, "%s: avail->flags = %u\n", + vq->dev->name, vq->vring.avail->flags); + + /* + * 2.4.7.2: + * + * If the VIRTIO_F_EVENT_IDX feature bit is not negotiated: + * + * - The device MUST ignore the used_event value. + * - After the device writes a descriptor index into the used ring: + * - If flags is 1, the device SHOULD NOT send an interrupt. + * - If flags is 0, the device MUST send an interrupt. + */ if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) { return; } @@ -703,6 +725,14 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, struct vring_desc *desc; u16 last_avail = lg_last_avail(vq); + /* + * 2.4.7.1: + * + * The driver MUST handle spurious interrupts from the device. + * + * That's why this is a while loop. + */ + /* There's nothing available? */ while (last_avail == vq->vring.avail->idx) { u64 event; @@ -776,12 +806,62 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, * descriptor chain. */ if (desc[i].flags & VRING_DESC_F_INDIRECT) { + /* 2.4.5.3.1: + * + * The driver MUST NOT set the VIRTQ_DESC_F_INDIRECT + * flag unless the VIRTIO_F_INDIRECT_DESC feature was + * negotiated. + */ + if (!(vq->dev->features_accepted & + (1<dev->name); + + /* + * 2.4.5.3.1: + * + * The driver MUST NOT set the VIRTQ_DESC_F_INDIRECT + * flag within an indirect descriptor (ie. only one + * table per descriptor). + */ + if (desc != vq->vring.desc) + errx(1, "%s: Indirect within indirect", + vq->dev->name); + + /* + * Proposed update VIRTIO-134 spells this out: + * + * A driver MUST NOT set both VIRTQ_DESC_F_INDIRECT + * and VIRTQ_DESC_F_NEXT in flags. + */ + if (desc[i].flags & VRING_DESC_F_NEXT) + errx(1, "%s: indirect and next together", + vq->dev->name); + if (desc[i].len % sizeof(struct vring_desc)) errx(1, "Invalid size for indirect buffer table"); + /* + * 2.4.5.3.2: + * + * The device MUST ignore the write-only flag + * (flags&VIRTQ_DESC_F_WRITE) in the descriptor that + * refers to an indirect table. + * + * We ignore it here: :) + */ max = desc[i].len / sizeof(struct vring_desc); desc = check_pointer(desc[i].addr, desc[i].len); i = 0; + + /* 2.4.5.3.1: + * + * A driver MUST NOT create a descriptor chain longer + * than the Queue Size of the device. + */ + if (max > vq->pci_config.queue_size) + errx(1, "%s: indirect has too many entries", + vq->dev->name); } /* Grab the first descriptor, and check it's OK. */ @@ -1082,6 +1162,7 @@ static void reset_device(struct device *dev) } } dev->running = false; + dev->wrote_features_ok = false; /* Now we care if threads die. */ signal(SIGCHLD, (void *)kill_launcher); @@ -1703,6 +1784,18 @@ static void check_virtqueue(struct device *d, struct virtqueue *vq) || vq->pci_config.queue_used_hi) errx(1, "%s: invalid 64-bit queue address", d->name); + /* + * 2.4.1: + * + * The driver MUST ensure that the physical address of the first byte + * of each virtqueue part is a multiple of the specified alignment + * value in the above table. + */ + if (vq->pci_config.queue_desc_lo % 16 + || vq->pci_config.queue_avail_lo % 2 + || vq->pci_config.queue_used_lo % 4) + errx(1, "%s: invalid alignment in queue addresses", d->name); + /* Initialize the virtqueue and check they're all in range. */ vq->vring.num = vq->pci_config.queue_size; vq->vring.desc = check_pointer(vq->pci_config.queue_desc_lo, @@ -1715,6 +1808,16 @@ static void check_virtqueue(struct device *d, struct virtqueue *vq) sizeof(*vq->vring.used) + (sizeof(vq->vring.used->ring[0]) * vq->vring.num)); + + /* + * 2.4.9.1: + * + * The driver MUST initialize flags in the used ring to 0 + * when allocating the used ring. + */ + if (vq->vring.used->flags != 0) + errx(1, "%s: invalid initial used.flags %#x", + d->name, vq->vring.used->flags); } static void start_virtqueue(struct virtqueue *vq) @@ -1768,12 +1871,12 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) d->mmio->cfg.device_feature = (d->features >> 32); else d->mmio->cfg.device_feature = 0; - goto write_through32; + goto feature_write_through32; case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): if (val > 1) errx(1, "%s: Unexpected driver select %u", d->name, val); - goto write_through32; + goto feature_write_through32; case offsetof(struct virtio_pci_mmio, cfg.guest_feature): if (d->mmio->cfg.guest_feature_select == 0) { d->features_accepted &= ~((u64)0xFFFFFFFF); @@ -1783,11 +1886,19 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) d->features_accepted &= 0xFFFFFFFF; d->features_accepted |= ((u64)val) << 32; } + /* + * 2.2.1: + * + * The driver MUST NOT accept a feature which the device did + * not offer + */ if (d->features_accepted & ~d->features) errx(1, "%s: over-accepted features %#llx of %#llx", d->name, d->features_accepted, d->features); - goto write_through32; - case offsetof(struct virtio_pci_mmio, cfg.device_status): + goto feature_write_through32; + case offsetof(struct virtio_pci_mmio, cfg.device_status): { + u8 prev; + verbose("%s: device status -> %#x\n", d->name, val); /* * 4.1.4.3.1: @@ -1795,8 +1906,15 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * The device MUST reset when 0 is written to device_status, * and present a 0 in device_status once that is done. */ - if (val == 0) + if (val == 0) { reset_device(d); + goto write_through8; + } + + /* 2.1.1: The driver MUST NOT clear a device status bit. */ + if (d->mmio->cfg.device_status & ~val) + errx(1, "%s: unset of device status bit %#x -> %#x", + d->name, d->mmio->cfg.device_status, val); /* * 2.1.2: @@ -1808,7 +1926,67 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) && !(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)) start_virtqueues(d); + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + * - Reset the device. + * - Set the ACKNOWLEDGE status bit: the guest OS has + * notice the device. + * - Set the DRIVER status bit: the guest OS knows how + * to drive the device. + * - Read device feature bits, and write the subset + * of feature bits understood by the OS and driver + * to the device. During this step the driver MAY + * read (but MUST NOT write) the device-specific + * configuration fields to check that it can + * support the device before accepting it. + * - Set the FEATURES_OK status bit. The driver + * MUST not accept new feature bits after this + * step. + * - Re-read device status to ensure the FEATURES_OK + * bit is still set: otherwise, the device does + * not support our subset of features and the + * device is unusable. + * - Perform device-specific setup, including + * discovery of virtqueues for the device, + * optional per-bus setup, reading and possibly + * writing the device’s virtio configuration + * space, and population of virtqueues. + * - Set the DRIVER_OK status bit. At this point the + * device is “live”. + */ + prev = 0; + switch (val & ~d->mmio->cfg.device_status) { + case VIRTIO_CONFIG_S_DRIVER_OK: + prev |= VIRTIO_CONFIG_S_FEATURES_OK; /* fall thru */ + case VIRTIO_CONFIG_S_FEATURES_OK: + prev |= VIRTIO_CONFIG_S_DRIVER; /* fall thru */ + case VIRTIO_CONFIG_S_DRIVER: + prev |= VIRTIO_CONFIG_S_ACKNOWLEDGE; /* fall thru */ + case VIRTIO_CONFIG_S_ACKNOWLEDGE: + break; + default: + errx(1, "%s: unknown device status bit %#x -> %#x", + d->name, d->mmio->cfg.device_status, val); + } + if (d->mmio->cfg.device_status != prev) + errx(1, "%s: unexpected status transition %#x -> %#x", + d->name, d->mmio->cfg.device_status, val); + + /* If they just wrote FEATURES_OK, we make sure they read */ + switch (val & ~d->mmio->cfg.device_status) { + case VIRTIO_CONFIG_S_FEATURES_OK: + d->wrote_features_ok = true; + break; + case VIRTIO_CONFIG_S_DRIVER_OK: + if (d->wrote_features_ok) + errx(1, "%s: did not re-read FEATURES_OK", + d->name); + break; + } goto write_through8; + } case offsetof(struct virtio_pci_mmio, cfg.queue_select): vq = vq_by_num(d, val); /* @@ -1844,7 +2022,9 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) case offsetof(struct virtio_pci_mmio, cfg.queue_msix_vector): errx(1, "%s: attempt to set MSIX vector to %u", d->name, val); - case offsetof(struct virtio_pci_mmio, cfg.queue_enable): + case offsetof(struct virtio_pci_mmio, cfg.queue_enable): { + struct virtqueue *vq = vq_by_num(d, d->mmio->cfg.queue_select); + /* * 4.1.4.3.2: * @@ -1852,17 +2032,27 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) */ if (val != 1) errx(1, "%s: setting queue_enable to %u", d->name, val); - d->mmio->cfg.queue_enable = val; - save_vq_config(&d->mmio->cfg, - vq_by_num(d, d->mmio->cfg.queue_select)); + /* - * 4.1.4.3.2: + * 3.1.1: * - * The driver MUST configure the other virtqueue fields before - * enabling the virtqueue with queue_enable. + * 7. Perform device-specific setup, including discovery of + * virtqueues for the device, optional per-bus setup, + * reading and possibly writing the device’s virtio + * configuration space, and population of virtqueues. + * 8. Set the DRIVER_OK status bit. + * + * All our devices require all virtqueues to be enabled, so + * they should have done that before setting DRIVER_OK. */ - check_virtqueue(d, vq_by_num(d, d->mmio->cfg.queue_select)); + if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK) + errx(1, "%s: enabling vs after DRIVER_OK", d->name); + + d->mmio->cfg.queue_enable = val; + save_vq_config(&d->mmio->cfg, vq); + check_virtqueue(d, vq); goto write_through16; + } case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off): errx(1, "%s: attempt to write to queue_notify_off", d->name); case offsetof(struct virtio_pci_mmio, cfg.queue_desc_lo): @@ -1880,6 +2070,26 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) if (d->mmio->cfg.queue_enable) errx(1, "%s: changing queue on live device", d->name); + + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + *... + * 5. Set the FEATURES_OK status bit. The driver MUST not + * accept new feature bits after this step. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK)) + errx(1, "%s: enabling vs before FEATURES_OK", d->name); + + /* + * 6. Re-read device status to ensure the FEATURES_OK bit is + * still set... + */ + if (d->wrote_features_ok) + errx(1, "%s: didn't re-read FEATURES_OK before setup", + d->name); + goto write_through32; case offsetof(struct virtio_pci_mmio, notify): vq = vq_by_num(d, val); @@ -1909,6 +2119,27 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) errx(1, "%s: Unexpected write to offset %u", d->name, off); } +feature_write_through32: + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + *... + * - Set the DRIVER status bit: the guest OS knows how + * to drive the device. + * - Read device feature bits, and write the subset + * of feature bits understood by the OS and driver + * to the device. + *... + * - Set the FEATURES_OK status bit. The driver MUST not + * accept new feature bits after this step. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) + errx(1, "%s: feature write before VIRTIO_CONFIG_S_DRIVER", + d->name); + if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK) + errx(1, "%s: feature write after VIRTIO_CONFIG_S_FEATURES_OK", + d->name); /* * 4.1.3.1: @@ -1951,12 +2182,29 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) case offsetof(struct virtio_pci_mmio, cfg.device_feature): case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): case offsetof(struct virtio_pci_mmio, cfg.guest_feature): + /* + * 3.1.1: + * + * The driver MUST follow this sequence to initialize a device: + *... + * - Set the DRIVER status bit: the guest OS knows how + * to drive the device. + * - Read device feature bits, and write the subset + * of feature bits understood by the OS and driver + * to the device. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) + errx(1, "%s: feature read before VIRTIO_CONFIG_S_DRIVER", + d->name); goto read_through32; case offsetof(struct virtio_pci_mmio, cfg.msix_config): errx(1, "%s: read of msix_config", d->name); case offsetof(struct virtio_pci_mmio, cfg.num_queues): goto read_through16; case offsetof(struct virtio_pci_mmio, cfg.device_status): + /* As they did read, any write of FEATURES_OK is now fine. */ + d->wrote_features_ok = false; + goto read_through8; case offsetof(struct virtio_pci_mmio, cfg.config_generation): /* * 4.1.4.3.1: @@ -1971,6 +2219,15 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) */ goto read_through8; case offsetof(struct virtio_pci_mmio, notify): + /* + * 3.1.1: + * + * The driver MUST NOT notify the device before setting + * DRIVER_OK. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)) + errx(1, "%s: notify before VIRTIO_CONFIG_S_DRIVER_OK", + d->name); goto read_through16; case offsetof(struct virtio_pci_mmio, isr): if (mask != 0xFF) @@ -1992,6 +2249,23 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) if (off > d->mmio_size - 4) errx(1, "%s: read past end (%#x)", d->name, getreg(eip)); + + /* + * 3.1.1: + * The driver MUST follow this sequence to initialize a device: + *... + * 3. Set the DRIVER status bit: the guest OS knows how to + * drive the device. + * 4. Read device feature bits, and write the subset of + * feature bits understood by the OS and driver to the + * device. During this step the driver MAY read (but MUST NOT + * write) the device-specific configuration fields to check + * that it can support the device before accepting it. + */ + if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) + errx(1, "%s: config read before VIRTIO_CONFIG_S_DRIVER", + d->name); + if (mask == 0xFFFFFFFF) goto read_through32; else if (mask == 0xFFFF) @@ -2200,6 +2474,10 @@ static void init_pci_config(struct pci_config *pci, u16 type, * * The device MUST either present notify_off_multiplier as an even * power of 2, or present notify_off_multiplier as 0. + * + * 2.1.2: + * + * The device MUST initialize device status to 0 upon reset. */ memset(pci, 0, sizeof(*pci)); @@ -2340,6 +2618,7 @@ static struct device *new_pci_device(const char *name, u16 type, dev->name = name; dev->vq = NULL; dev->running = false; + dev->wrote_features_ok = false; dev->mmio_size = sizeof(struct virtio_pci_mmio); dev->mmio = calloc(1, dev->mmio_size); dev->features = (u64)1 << VIRTIO_F_VERSION_1; From 17c56d6de8e809ac57bf4c93d504f5336eb03dd1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:43 +1030 Subject: [PATCH 321/360] tools/lguest: give virtqueues names for better error messages Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index bc444aff2333dd..70ee62a0eb9aea 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -200,6 +200,9 @@ struct virtqueue { /* Which device owns me. */ struct device *dev; + /* Name for printing errors. */ + const char *name; + /* The actual ring of buffers. */ struct vring vring; @@ -2366,7 +2369,8 @@ static void emulate_mmio(unsigned long paddr, const u8 *insn) * routines to allocate and manage them. */ static void add_pci_virtqueue(struct device *dev, - void (*service)(struct virtqueue *)) + void (*service)(struct virtqueue *), + const char *name) { struct virtqueue **i, *vq = malloc(sizeof(*vq)); @@ -2374,6 +2378,7 @@ static void add_pci_virtqueue(struct device *dev, vq->next = NULL; vq->last_avail_idx = 0; vq->dev = dev; + vq->name = name; /* * This is the routine the service thread will run, and its Process ID @@ -2666,8 +2671,8 @@ static void setup_console(void) * stdin. When they put something in the output queue, we write it to * stdout. */ - add_pci_virtqueue(dev, console_input); - add_pci_virtqueue(dev, console_output); + add_pci_virtqueue(dev, console_input, "input"); + add_pci_virtqueue(dev, console_output, "output"); /* We need a configuration area for the emerg_wr early writes. */ add_pci_feature(dev, VIRTIO_CONSOLE_F_EMERG_WRITE); @@ -2838,8 +2843,8 @@ static void setup_tun_net(char *arg) dev->priv = net_info; /* Network devices need a recv and a send queue, just like console. */ - add_pci_virtqueue(dev, net_input); - add_pci_virtqueue(dev, net_output); + add_pci_virtqueue(dev, net_input, "rx"); + add_pci_virtqueue(dev, net_output, "tx"); /* * We need a socket to perform the magic network ioctls to bring up the @@ -3026,7 +3031,7 @@ static void setup_block_file(const char *filename) dev = new_pci_device("block", VIRTIO_ID_BLOCK, 0x01, 0x80); /* The device has one virtqueue, where the Guest places requests. */ - add_pci_virtqueue(dev, blk_request); + add_pci_virtqueue(dev, blk_request, "request"); /* Allocate the room for our own bookkeeping */ vblk = dev->priv = malloc(sizeof(*vblk)); @@ -3107,7 +3112,7 @@ static void setup_rng(void) dev->priv = rng_info; /* The device has one virtqueue, where the Guest places inbufs. */ - add_pci_virtqueue(dev, rng_input); + add_pci_virtqueue(dev, rng_input, "input"); /* We don't have any configuration space */ no_device_config(dev); From 1e1c17a7a2e5c585926eefffbea8a61d7a03a247 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:44 +1030 Subject: [PATCH 322/360] tools/lguest: use common error macros in the example launcher. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 206 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 101 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 70ee62a0eb9aea..eebe94b84e8c2c 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -252,6 +252,16 @@ static struct termios orig_term; #define le32_to_cpu(v32) (v32) #define le64_to_cpu(v64) (v64) +/* + * A real device would ignore weird/non-compliant driver behaviour. We + * stop and flag it, to help debugging Linux problems. + */ +#define bad_driver(d, fmt, ...) \ + errx(1, "%s: bad driver: " fmt, (d)->name, ## __VA_ARGS__) +#define bad_driver_vq(vq, fmt, ...) \ + errx(1, "%s vq %s: bad driver: " fmt, (vq)->dev->name, \ + vq->name, ## __VA_ARGS__) + /* Is this iovec empty? */ static bool iov_empty(const struct iovec iov[], unsigned int num_iov) { @@ -264,7 +274,8 @@ static bool iov_empty(const struct iovec iov[], unsigned int num_iov) } /* Take len bytes from the front of this iovec. */ -static void iov_consume(struct iovec iov[], unsigned num_iov, +static void iov_consume(struct device *d, + struct iovec iov[], unsigned num_iov, void *dest, unsigned len) { unsigned int i; @@ -282,7 +293,7 @@ static void iov_consume(struct iovec iov[], unsigned num_iov, len -= used; } if (len != 0) - errx(1, "iovec too short!"); + bad_driver(d, "iovec too short!"); } /*L:100 @@ -618,7 +629,8 @@ static void tell_kernel(unsigned long start) * we have a convenient routine which checks it and exits with an error message * if something funny is going on: */ -static void *_check_pointer(unsigned long addr, unsigned int size, +static void *_check_pointer(struct device *d, + unsigned long addr, unsigned int size, unsigned int line) { /* @@ -626,7 +638,8 @@ static void *_check_pointer(unsigned long addr, unsigned int size, * or addr + size wraps around. */ if ((addr + size) > guest_limit || (addr + size) < addr) - errx(1, "%s:%i: Invalid address %#lx", __FILE__, line, addr); + bad_driver(d, "%s:%i: Invalid address %#lx", + __FILE__, line, addr); /* * We return a pointer for the caller's convenience, now we know it's * safe to use. @@ -634,14 +647,14 @@ static void *_check_pointer(unsigned long addr, unsigned int size, return from_guest_phys(addr); } /* A macro which transparently hands the line number to the real function. */ -#define check_pointer(addr,size) _check_pointer(addr, size, __LINE__) +#define check_pointer(d,addr,size) _check_pointer(d, addr, size, __LINE__) /* * Each buffer in the virtqueues is actually a chain of descriptors. This * function returns the next descriptor in the chain, or vq->vring.num if we're * at the end. */ -static unsigned next_desc(struct vring_desc *desc, +static unsigned next_desc(struct device *d, struct vring_desc *desc, unsigned int i, unsigned int max) { unsigned int next; @@ -656,7 +669,7 @@ static unsigned next_desc(struct vring_desc *desc, wmb(); if (next >= max) - errx(1, "Desc next is %u", next); + bad_driver(d, "Desc next is %u", next); return next; } @@ -681,8 +694,7 @@ static void trigger_irq(struct virtqueue *vq) * The driver MUST set flags to 0 or 1. */ if (vq->vring.avail->flags > 1) - errx(1, "%s: avail->flags = %u\n", - vq->dev->name, vq->vring.avail->flags); + bad_driver_vq(vq, "avail->flags = %u\n", vq->vring.avail->flags); /* * 2.4.7.2: @@ -769,8 +781,8 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, /* Check it isn't doing very strange things with descriptor numbers. */ if ((u16)(vq->vring.avail->idx - last_avail) > vq->vring.num) - errx(1, "Guest moved used index from %u to %u", - last_avail, vq->vring.avail->idx); + bad_driver_vq(vq, "Guest moved used index from %u to %u", + last_avail, vq->vring.avail->idx); /* * Make sure we read the descriptor number *after* we read the ring @@ -787,7 +799,7 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, /* If their number is silly, that's a fatal mistake. */ if (head >= vq->vring.num) - errx(1, "Guest says index %u is available", head); + bad_driver_vq(vq, "Guest says index %u is available", head); /* When we start there are none of either input nor output. */ *out_num = *in_num = 0; @@ -817,8 +829,7 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, */ if (!(vq->dev->features_accepted & (1<dev->name); + bad_driver_vq(vq, "vq indirect not negotiated"); /* * 2.4.5.3.1: @@ -828,8 +839,7 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, * table per descriptor). */ if (desc != vq->vring.desc) - errx(1, "%s: Indirect within indirect", - vq->dev->name); + bad_driver_vq(vq, "Indirect within indirect"); /* * Proposed update VIRTIO-134 spells this out: @@ -838,11 +848,11 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, * and VIRTQ_DESC_F_NEXT in flags. */ if (desc[i].flags & VRING_DESC_F_NEXT) - errx(1, "%s: indirect and next together", - vq->dev->name); + bad_driver_vq(vq, "indirect and next together"); if (desc[i].len % sizeof(struct vring_desc)) - errx(1, "Invalid size for indirect buffer table"); + bad_driver_vq(vq, + "Invalid size for indirect table"); /* * 2.4.5.3.2: * @@ -854,7 +864,7 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, */ max = desc[i].len / sizeof(struct vring_desc); - desc = check_pointer(desc[i].addr, desc[i].len); + desc = check_pointer(vq->dev, desc[i].addr, desc[i].len); i = 0; /* 2.4.5.3.1: @@ -863,14 +873,14 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, * than the Queue Size of the device. */ if (max > vq->pci_config.queue_size) - errx(1, "%s: indirect has too many entries", - vq->dev->name); + bad_driver_vq(vq, + "indirect has too many entries"); } /* Grab the first descriptor, and check it's OK. */ iov[*out_num + *in_num].iov_len = desc[i].len; iov[*out_num + *in_num].iov_base - = check_pointer(desc[i].addr, desc[i].len); + = check_pointer(vq->dev, desc[i].addr, desc[i].len); /* If this is an input descriptor, increment that count. */ if (desc[i].flags & VRING_DESC_F_WRITE) (*in_num)++; @@ -880,14 +890,15 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, * to come before any input descriptors. */ if (*in_num) - errx(1, "Descriptor has out after in"); + bad_driver_vq(vq, + "Descriptor has out after in"); (*out_num)++; } /* If we've got too many, that implies a descriptor loop. */ if (*out_num + *in_num > max) - errx(1, "Looped descriptor"); - } while ((i = next_desc(desc, i, max)) != max); + bad_driver_vq(vq, "Looped descriptor"); + } while ((i = next_desc(vq->dev, desc, i, max)) != max); return head; } @@ -944,7 +955,7 @@ static void console_input(struct virtqueue *vq) /* Make sure there's a descriptor available. */ head = wait_for_vq_desc(vq, iov, &out_num, &in_num); if (out_num) - errx(1, "Output buffers in console in queue?"); + bad_driver_vq(vq, "Output buffers in console in queue?"); /* Read into it. This is where we usually wait. */ len = readv(STDIN_FILENO, iov, in_num); @@ -997,7 +1008,7 @@ static void console_output(struct virtqueue *vq) /* We usually wait in here, for the Guest to give us something. */ head = wait_for_vq_desc(vq, iov, &out, &in); if (in) - errx(1, "Input buffers in console output queue?"); + bad_driver_vq(vq, "Input buffers in console output queue?"); /* writev can return a partial write, so we loop here. */ while (!iov_empty(iov, out)) { @@ -1006,7 +1017,7 @@ static void console_output(struct virtqueue *vq) warn("Write to stdout gave %i (%d)", len, errno); break; } - iov_consume(iov, out, NULL, len); + iov_consume(vq->dev, iov, out, NULL, len); } /* @@ -1035,7 +1046,7 @@ static void net_output(struct virtqueue *vq) /* We usually wait in here for the Guest to give us a packet. */ head = wait_for_vq_desc(vq, iov, &out, &in); if (in) - errx(1, "Input buffers in net output queue?"); + bad_driver_vq(vq, "Input buffers in net output queue?"); /* * Send the whole thing through to /dev/net/tun. It expects the exact * same format: what a coincidence! @@ -1083,7 +1094,7 @@ static void net_input(struct virtqueue *vq) */ head = wait_for_vq_desc(vq, iov, &out, &in); if (out) - errx(1, "Output buffers in net input queue?"); + bad_driver_vq(vq, "Output buffers in net input queue?"); /* * If it looks like we'll block reading from the tun device, send them @@ -1466,7 +1477,8 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val) */ /* Must be bar 0 */ if (!valid_bar_access(d, &d->config.cfg_access)) - errx(1, "Invalid cfg_access to bar%u, offset %u len %u", + bad_driver(d, + "Invalid cfg_access to bar%u, offset %u len %u", d->config.cfg_access.cap.bar, d->config.cfg_access.cap.offset, d->config.cfg_access.cap.length); @@ -1785,7 +1797,7 @@ static void check_virtqueue(struct device *d, struct virtqueue *vq) if (vq->pci_config.queue_desc_hi || vq->pci_config.queue_avail_hi || vq->pci_config.queue_used_hi) - errx(1, "%s: invalid 64-bit queue address", d->name); + bad_driver_vq(vq, "invalid 64-bit queue address"); /* * 2.4.1: @@ -1797,17 +1809,20 @@ static void check_virtqueue(struct device *d, struct virtqueue *vq) if (vq->pci_config.queue_desc_lo % 16 || vq->pci_config.queue_avail_lo % 2 || vq->pci_config.queue_used_lo % 4) - errx(1, "%s: invalid alignment in queue addresses", d->name); + bad_driver_vq(vq, "invalid alignment in queue addresses"); /* Initialize the virtqueue and check they're all in range. */ vq->vring.num = vq->pci_config.queue_size; - vq->vring.desc = check_pointer(vq->pci_config.queue_desc_lo, + vq->vring.desc = check_pointer(vq->dev, + vq->pci_config.queue_desc_lo, sizeof(*vq->vring.desc) * vq->vring.num); - vq->vring.avail = check_pointer(vq->pci_config.queue_avail_lo, + vq->vring.avail = check_pointer(vq->dev, + vq->pci_config.queue_avail_lo, sizeof(*vq->vring.avail) + (sizeof(vq->vring.avail->ring[0]) * vq->vring.num)); - vq->vring.used = check_pointer(vq->pci_config.queue_used_lo, + vq->vring.used = check_pointer(vq->dev, + vq->pci_config.queue_used_lo, sizeof(*vq->vring.used) + (sizeof(vq->vring.used->ring[0]) * vq->vring.num)); @@ -1819,8 +1834,8 @@ static void check_virtqueue(struct device *d, struct virtqueue *vq) * when allocating the used ring. */ if (vq->vring.used->flags != 0) - errx(1, "%s: invalid initial used.flags %#x", - d->name, vq->vring.used->flags); + bad_driver_vq(vq, "invalid initial used.flags %#x", + vq->vring.used->flags); } static void start_virtqueue(struct virtqueue *vq) @@ -1877,8 +1892,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) goto feature_write_through32; case offsetof(struct virtio_pci_mmio, cfg.guest_feature_select): if (val > 1) - errx(1, "%s: Unexpected driver select %u", - d->name, val); + bad_driver(d, "Unexpected driver select %u", val); goto feature_write_through32; case offsetof(struct virtio_pci_mmio, cfg.guest_feature): if (d->mmio->cfg.guest_feature_select == 0) { @@ -1896,8 +1910,8 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * not offer */ if (d->features_accepted & ~d->features) - errx(1, "%s: over-accepted features %#llx of %#llx", - d->name, d->features_accepted, d->features); + bad_driver(d, "over-accepted features %#llx of %#llx", + d->features_accepted, d->features); goto feature_write_through32; case offsetof(struct virtio_pci_mmio, cfg.device_status): { u8 prev; @@ -1916,8 +1930,8 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) /* 2.1.1: The driver MUST NOT clear a device status bit. */ if (d->mmio->cfg.device_status & ~val) - errx(1, "%s: unset of device status bit %#x -> %#x", - d->name, d->mmio->cfg.device_status, val); + bad_driver(d, "unset of device status bit %#x -> %#x", + d->mmio->cfg.device_status, val); /* * 2.1.2: @@ -1970,12 +1984,12 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) case VIRTIO_CONFIG_S_ACKNOWLEDGE: break; default: - errx(1, "%s: unknown device status bit %#x -> %#x", - d->name, d->mmio->cfg.device_status, val); + bad_driver(d, "unknown device status bit %#x -> %#x", + d->mmio->cfg.device_status, val); } if (d->mmio->cfg.device_status != prev) - errx(1, "%s: unexpected status transition %#x -> %#x", - d->name, d->mmio->cfg.device_status, val); + bad_driver(d, "unexpected status transition %#x -> %#x", + d->mmio->cfg.device_status, val); /* If they just wrote FEATURES_OK, we make sure they read */ switch (val & ~d->mmio->cfg.device_status) { @@ -1984,8 +1998,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) break; case VIRTIO_CONFIG_S_DRIVER_OK: if (d->wrote_features_ok) - errx(1, "%s: did not re-read FEATURES_OK", - d->name); + bad_driver(d, "did not re-read FEATURES_OK"); break; } goto write_through8; @@ -2017,14 +2030,12 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * to queue_size. */ if (val & (val-1)) - errx(1, "%s: invalid queue size %u\n", d->name, val); + bad_driver(d, "invalid queue size %u", val); if (d->mmio->cfg.queue_enable) - errx(1, "%s: changing queue size on live device", - d->name); + bad_driver(d, "changing queue size on live device"); goto write_through16; case offsetof(struct virtio_pci_mmio, cfg.queue_msix_vector): - errx(1, "%s: attempt to set MSIX vector to %u", - d->name, val); + bad_driver(d, "attempt to set MSIX vector to %u", val); case offsetof(struct virtio_pci_mmio, cfg.queue_enable): { struct virtqueue *vq = vq_by_num(d, d->mmio->cfg.queue_select); @@ -2034,7 +2045,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * The driver MUST NOT write a 0 to queue_enable. */ if (val != 1) - errx(1, "%s: setting queue_enable to %u", d->name, val); + bad_driver(d, "setting queue_enable to %u", val); /* * 3.1.1: @@ -2049,7 +2060,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * they should have done that before setting DRIVER_OK. */ if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK) - errx(1, "%s: enabling vs after DRIVER_OK", d->name); + bad_driver(d, "enabling vq after DRIVER_OK"); d->mmio->cfg.queue_enable = val; save_vq_config(&d->mmio->cfg, vq); @@ -2057,7 +2068,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) goto write_through16; } case offsetof(struct virtio_pci_mmio, cfg.queue_notify_off): - errx(1, "%s: attempt to write to queue_notify_off", d->name); + bad_driver(d, "attempt to write to queue_notify_off"); case offsetof(struct virtio_pci_mmio, cfg.queue_desc_lo): case offsetof(struct virtio_pci_mmio, cfg.queue_desc_hi): case offsetof(struct virtio_pci_mmio, cfg.queue_avail_lo): @@ -2071,8 +2082,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * enabling the virtqueue with queue_enable. */ if (d->mmio->cfg.queue_enable) - errx(1, "%s: changing queue on live device", - d->name); + bad_driver(d, "changing queue on live device"); /* * 3.1.1: @@ -2083,26 +2093,25 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * accept new feature bits after this step. */ if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK)) - errx(1, "%s: enabling vs before FEATURES_OK", d->name); + bad_driver(d, "setting up vq before FEATURES_OK"); /* * 6. Re-read device status to ensure the FEATURES_OK bit is * still set... */ if (d->wrote_features_ok) - errx(1, "%s: didn't re-read FEATURES_OK before setup", - d->name); + bad_driver(d, "didn't re-read FEATURES_OK before setup"); goto write_through32; case offsetof(struct virtio_pci_mmio, notify): vq = vq_by_num(d, val); if (!vq) - errx(1, "Invalid vq notification on %u", val); + bad_driver(d, "Invalid vq notification on %u", val); /* Notify the process handling this vq by adding 1 to eventfd */ write(vq->eventfd, "\1\0\0\0\0\0\0\0", 8); goto write_through16; case offsetof(struct virtio_pci_mmio, isr): - errx(1, "%s: Unexpected write to isr", d->name); + bad_driver(d, "Unexpected write to isr"); /* Weird corner case: write to emerg_wr of console */ case sizeof(struct virtio_pci_mmio) + offsetof(struct virtio_console_config, emerg_wr): @@ -2119,7 +2128,7 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * The driver MUST NOT write to device_feature, num_queues, * config_generation or queue_notify_off. */ - errx(1, "%s: Unexpected write to offset %u", d->name, off); + bad_driver(d, "Unexpected write to offset %u", off); } feature_write_through32: @@ -2138,11 +2147,9 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) * accept new feature bits after this step. */ if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) - errx(1, "%s: feature write before VIRTIO_CONFIG_S_DRIVER", - d->name); + bad_driver(d, "feature write before VIRTIO_CONFIG_S_DRIVER"); if (d->mmio->cfg.device_status & VIRTIO_CONFIG_S_FEATURES_OK) - errx(1, "%s: feature write after VIRTIO_CONFIG_S_FEATURES_OK", - d->name); + bad_driver(d, "feature write after VIRTIO_CONFIG_S_FEATURES_OK"); /* * 4.1.3.1: @@ -2153,8 +2160,8 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) */ write_through32: if (mask != 0xFFFFFFFF) { - errx(1, "%s: non-32-bit write to offset %u (%#x)", - d->name, off, getreg(eip)); + bad_driver(d, "non-32-bit write to offset %u (%#x)", + off, getreg(eip)); return; } memcpy((char *)d->mmio + off, &val, 4); @@ -2162,15 +2169,15 @@ static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) write_through16: if (mask != 0xFFFF) - errx(1, "%s: non-16-bit (%#x) write to offset %u (%#x)", - d->name, mask, off, getreg(eip)); + bad_driver(d, "non-16-bit write to offset %u (%#x)", + off, getreg(eip)); memcpy((char *)d->mmio + off, &val, 2); return; write_through8: if (mask != 0xFF) - errx(1, "%s: non-8-bit write to offset %u (%#x)", - d->name, off, getreg(eip)); + bad_driver(d, "non-8-bit write to offset %u (%#x)", + off, getreg(eip)); memcpy((char *)d->mmio + off, &val, 1); return; } @@ -2197,11 +2204,11 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) * to the device. */ if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) - errx(1, "%s: feature read before VIRTIO_CONFIG_S_DRIVER", - d->name); + bad_driver(d, + "feature read before VIRTIO_CONFIG_S_DRIVER"); goto read_through32; case offsetof(struct virtio_pci_mmio, cfg.msix_config): - errx(1, "%s: read of msix_config", d->name); + bad_driver(d, "read of msix_config"); case offsetof(struct virtio_pci_mmio, cfg.num_queues): goto read_through16; case offsetof(struct virtio_pci_mmio, cfg.device_status): @@ -2229,13 +2236,12 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) * DRIVER_OK. */ if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER_OK)) - errx(1, "%s: notify before VIRTIO_CONFIG_S_DRIVER_OK", - d->name); + bad_driver(d, "notify before VIRTIO_CONFIG_S_DRIVER_OK"); goto read_through16; case offsetof(struct virtio_pci_mmio, isr): if (mask != 0xFF) - errx(1, "%s: non-8-bit read from offset %u (%#x)", - d->name, off, getreg(eip)); + bad_driver(d, "non-8-bit read from offset %u (%#x)", + off, getreg(eip)); isr = d->mmio->isr; /* * 4.1.4.5.1: @@ -2245,13 +2251,11 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) d->mmio->isr = 0; return isr; case offsetof(struct virtio_pci_mmio, padding): - errx(1, "%s: read from padding (%#x)", - d->name, getreg(eip)); + bad_driver(d, "read from padding (%#x)", getreg(eip)); default: /* Read from device config space, beware unaligned overflow */ if (off > d->mmio_size - 4) - errx(1, "%s: read past end (%#x)", - d->name, getreg(eip)); + bad_driver(d, "read past end (%#x)", getreg(eip)); /* * 3.1.1: @@ -2266,8 +2270,8 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) * that it can support the device before accepting it. */ if (!(d->mmio->cfg.device_status & VIRTIO_CONFIG_S_DRIVER)) - errx(1, "%s: config read before VIRTIO_CONFIG_S_DRIVER", - d->name); + bad_driver(d, + "config read before VIRTIO_CONFIG_S_DRIVER"); if (mask == 0xFFFFFFFF) goto read_through32; @@ -2286,22 +2290,22 @@ static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) */ read_through32: if (mask != 0xFFFFFFFF) - errx(1, "%s: non-32-bit read to offset %u (%#x)", - d->name, off, getreg(eip)); + bad_driver(d, "non-32-bit read to offset %u (%#x)", + off, getreg(eip)); memcpy(&val, (char *)d->mmio + off, 4); return val; read_through16: if (mask != 0xFFFF) - errx(1, "%s: non-16-bit read to offset %u (%#x)", - d->name, off, getreg(eip)); + bad_driver(d, "non-16-bit read to offset %u (%#x)", + off, getreg(eip)); memcpy(&val, (char *)d->mmio + off, 2); return val; read_through8: if (mask != 0xFF) - errx(1, "%s: non-8-bit read to offset %u (%#x)", - d->name, off, getreg(eip)); + bad_driver(d, "non-8-bit read to offset %u (%#x)", + off, getreg(eip)); memcpy(&val, (char *)d->mmio + off, 1); return val; } @@ -2943,7 +2947,7 @@ static void blk_request(struct virtqueue *vq) head = wait_for_vq_desc(vq, iov, &out_num, &in_num); /* Copy the output header from the front of the iov (adjusts iov) */ - iov_consume(iov, out_num, &out, sizeof(out)); + iov_consume(vq->dev, iov, out_num, &out, sizeof(out)); /* Find and trim end of iov input array, for our status byte. */ in = NULL; @@ -2955,7 +2959,7 @@ static void blk_request(struct virtqueue *vq) } } if (!in) - errx(1, "Bad virtblk cmd with no room for status"); + bad_driver_vq(vq, "Bad virtblk cmd with no room for status"); /* * For historical reasons, block operations are expressed in 512 byte @@ -2985,7 +2989,7 @@ static void blk_request(struct virtqueue *vq) /* Trim it back to the correct length */ ftruncate64(vblk->fd, vblk->len); /* Die, bad Guest, die. */ - errx(1, "Write past end %llu+%u", off, ret); + bad_driver_vq(vq, "Write past end %llu+%u", off, ret); } wlen = sizeof(*in); @@ -3078,7 +3082,7 @@ static void rng_input(struct virtqueue *vq) /* First we need a buffer from the Guests's virtqueue. */ head = wait_for_vq_desc(vq, iov, &out_num, &in_num); if (out_num) - errx(1, "Output buffers in rng?"); + bad_driver_vq(vq, "Output buffers in rng?"); /* * Just like the console write, we loop to cover the whole iovec. @@ -3088,7 +3092,7 @@ static void rng_input(struct virtqueue *vq) len = readv(rng_info->rfd, iov, in_num); if (len <= 0) err(1, "Read from /dev/urandom gave %i", len); - iov_consume(iov, in_num, NULL, len); + iov_consume(vq->dev, iov, in_num, NULL, len); totlen += len; } From ed9ecb0415b97b5f9f91f146e1977bb372c74c6d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:44 +1030 Subject: [PATCH 323/360] virtio: Don't expose legacy net features when VIRTIO_NET_NO_LEGACY defined. In particular, the virtio header always has the u16 num_buffers field. We define a new 'struct virtio_net_hdr_v1' for this (rather than simply calling it 'struct virtio_net_hdr', to avoid nasty type errors if some parts of a project define VIRTIO_NET_NO_LEGACY and some don't. Transitional devices (which can't define VIRTIO_NET_NO_LEGACY) will have to keep using struct virtio_net_hdr_mrg_rxbuf, which has the same byte layout as struct virtio_net_hdr_v1. Signed-off-by: Rusty Russell --- include/uapi/linux/virtio_net.h | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index b5f1677b291c97..4a9b58113d6e78 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -35,7 +35,6 @@ #define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ #define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ #define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ -#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ #define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ #define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ #define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ @@ -56,6 +55,10 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#ifndef VIRTIO_NET_NO_LEGACY +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#endif /* VIRTIO_NET_NO_LEGACY */ + #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ #define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ @@ -71,8 +74,9 @@ struct virtio_net_config { __u16 max_virtqueue_pairs; } __attribute__((packed)); +#ifndef VIRTIO_NET_NO_LEGACY /* This header comes first in the scatter-gather list. - * If VIRTIO_F_ANY_LAYOUT is not negotiated, it must + * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must * be the first element of the scatter-gather list. If you don't * specify GSO or CSUM features, you can simply ignore the header. */ struct virtio_net_hdr { @@ -97,6 +101,30 @@ struct virtio_net_hdr_mrg_rxbuf { struct virtio_net_hdr hdr; __virtio16 num_buffers; /* Number of merged rx buffers */ }; +#else /* ... VIRTIO_NET_NO_LEGACY */ +/* + * This header comes first in the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. + * + * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf. + */ +struct virtio_net_hdr_v1 { +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ +#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ + __u8 flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */ +#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ +#define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ +#define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ +#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ + __u8 gso_type; + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ + __virtio16 csum_start; /* Position to start checksumming from */ + __virtio16 csum_offset; /* Offset after that to place checksum */ + __virtio16 num_buffers; /* Number of merged rx buffers */ +}; +#endif /* ...VIRTIO_NET_NO_LEGACY */ /* * Control virtqueue data structures From 206ad06b2e88a3d826c99da8c8b3ed98e287ad87 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 13 Feb 2015 17:13:44 +1030 Subject: [PATCH 324/360] tools/lguest: don't use legacy definitions for net device in example launcher. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index eebe94b84e8c2c..e44052483ed933 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -66,6 +66,7 @@ typedef uint8_t u8; #define VIRTIO_CONFIG_NO_LEGACY #define VIRTIO_PCI_NO_LEGACY #define VIRTIO_BLK_NO_LEGACY +#define VIRTIO_NET_NO_LEGACY /* Use in-kernel ones, which defines VIRTIO_F_VERSION_1 */ #include "../../include/uapi/linux/virtio_config.h" @@ -2816,7 +2817,7 @@ static int get_tun_device(char tapif[IFNAMSIZ]) * about our expanded header (which is called * virtio_net_hdr_mrg_rxbuf in the legacy system). */ - vnet_hdr_sz = sizeof(struct virtio_net_hdr_mrg_rxbuf); + vnet_hdr_sz = sizeof(struct virtio_net_hdr_v1); if (ioctl(netfd, TUNSETVNETHDRSZ, &vnet_hdr_sz) != 0) err(1, "Setting tun header size to %u", vnet_hdr_sz); From 164c24063a3eadee11b46575c5482b2f1417be49 Mon Sep 17 00:00:00 2001 From: Chen Jie Date: Tue, 10 Feb 2015 12:49:48 -0800 Subject: [PATCH 325/360] jffs2: fix handling of corrupted summary length sm->offset maybe wrong but magic maybe right, the offset do not have CRC. Badness at c00c7580 [verbose debug info unavailable] NIP: c00c7580 LR: c00c718c CTR: 00000014 REGS: df07bb40 TRAP: 0700 Not tainted (2.6.34.13-WR4.3.0.0_standard) MSR: 00029000 CR: 22084f84 XER: 00000000 TASK = df84d6e0[908] 'mount' THREAD: df07a000 GPR00: 00000001 df07bbf0 df84d6e0 00000000 00000001 00000000 df07bb58 00000041 GPR08: 00000041 c0638860 00000000 00000010 22084f88 100636c8 df814ff8 00000000 GPR16: df84d6e0 dfa558cc c05adb90 00000048 c0452d30 00000000 000240d0 000040d0 GPR24: 00000014 c05ae734 c05be2e0 00000000 00000001 00000000 00000000 c05ae730 NIP [c00c7580] __alloc_pages_nodemask+0x4d0/0x638 LR [c00c718c] __alloc_pages_nodemask+0xdc/0x638 Call Trace: [df07bbf0] [c00c718c] __alloc_pages_nodemask+0xdc/0x638 (unreliable) [df07bc90] [c00c7708] __get_free_pages+0x20/0x48 [df07bca0] [c00f4a40] __kmalloc+0x15c/0x1ec [df07bcd0] [c01fc880] jffs2_scan_medium+0xa58/0x14d0 [df07bd70] [c01ff38c] jffs2_do_mount_fs+0x1f4/0x6b4 [df07bdb0] [c020144c] jffs2_do_fill_super+0xa8/0x260 [df07bdd0] [c020230c] jffs2_fill_super+0x104/0x184 [df07be00] [c0335814] get_sb_mtd_aux+0x9c/0xec [df07be20] [c033596c] get_sb_mtd+0x84/0x1e8 [df07be60] [c0201ed0] jffs2_get_sb+0x1c/0x2c [df07be70] [c0103898] vfs_kern_mount+0x78/0x1e8 [df07bea0] [c0103a58] do_kern_mount+0x40/0x100 [df07bec0] [c011fe90] do_mount+0x240/0x890 [df07bf10] [c0120570] sys_mount+0x90/0xd8 [df07bf40] [c00110d8] ret_from_syscall+0x0/0x4 === Exception: c01 at 0xff61a34 LR = 0x100135f0 Instruction dump: 38800005 38600000 48010f41 4bfffe1c 4bfc2d15 4bfffe8c 72e90200 4082fc28 3d20c064 39298860 8809000d 68000001 <0f000000> 2f800000 419efc0c 38000001 mount: mounting /dev/mtdblock3 on /common failed: Input/output error Signed-off-by: Chen Jie Cc: Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/scan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 7654e87b042869..9ad5ba4b299be2 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -510,6 +510,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo sumlen = c->sector_size - je32_to_cpu(sm->offset); sumptr = buf + buf_size - sumlen; + /* sm->offset maybe wrong but MAGIC maybe right */ + if (sumlen > c->sector_size) + goto full_scan; + /* Now, make sure the summary itself is available */ if (sumlen > buf_size) { /* Need to kmalloc for this. */ @@ -544,6 +548,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } } +full_scan: buf_ofs = jeb->offset; if (!buf_size) { From dd58d38fb30aa9ab52e792092cbd55c1dbc6e974 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 11 Feb 2015 13:25:09 +0300 Subject: [PATCH 326/360] mtd: hisilicon: && vs & typo The intent was to mask away some bits here, not to test true or false. Fix: 54f531f6e332 ('mtd: hisilicon: add a new NAND controller driver for hisilicon hip04 Soc') Signed-off-by: Dan Carpenter Signed-off-by: Brian Norris --- drivers/mtd/nand/hisi504_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 484e1db4d601d6..289ad3ac3e80d7 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -495,7 +495,7 @@ static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column, flag = hinfc_read(host, HINFC504_CON); if (chip->ecc.mode == NAND_ECC_HW) hinfc_write(host, - flag && ~(HINFC504_CON_ECCTYPE_MASK << + flag & ~(HINFC504_CON_ECCTYPE_MASK << HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); host->offset = 0; From b7a4fd53d2f2fe1630ef857429408e6b17031724 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Wed, 4 Feb 2015 13:03:27 +0000 Subject: [PATCH 327/360] dmaenegine: edma: fix sparse warnings this patch fixes following sparse warnings: edma.c:537:32: warning: symbol 'edma_prep_dma_memcpy' was not declared. Should it be static? edma.c:1070:6: warning: symbol 'edma_filter_fn' was not declared. Should it be static? Signed-off-by: Lad, Prabhakar Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index e95fa7dabc0c87..276157f22612dc 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -534,7 +535,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); } -struct dma_async_tx_descriptor *edma_prep_dma_memcpy( +static struct dma_async_tx_descriptor *edma_prep_dma_memcpy( struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long tx_flags) { From f39150720edcc9e6f5d61fd1ed6044eab1e5fa0d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 Jan 2015 15:52:13 +0200 Subject: [PATCH 328/360] dmaengine: rcar-dmac: Fix spinlock issues in interrupt The rcar_dmac_desc_put() function is called in interrupt context and must thus use spin_lock_irqsave() instead of spin_lock_irq(). Signed-off-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 8367578bac638d..5a6b85527107c8 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -487,16 +487,16 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp) * * The descriptor must have been removed from the channel's lists before calling * this function. - * - * Locking: Must be called in non-atomic context. */ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan, struct rcar_dmac_desc *desc) { - spin_lock_irq(&chan->lock); + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); list_splice_tail_init(&desc->chunks, &chan->desc.chunks_free); list_add_tail(&desc->node, &chan->desc.free); - spin_unlock_irq(&chan->lock); + spin_unlock_irqrestore(&chan->lock, flags); } static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan) From f7638c904bf87eac5bd823ef2debaef8251686b8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 Jan 2015 15:58:53 +0200 Subject: [PATCH 329/360] dmaengine: rcar-dmac: Fix oops due to unintialized list in error ISR The error interrupt handler stops and reinitializes all channels. This causes a crash for channels that have never been used, as their descriptor lists are uninitialized. Fix it by initializing the descriptor lists at probe time. Signed-off-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 5a6b85527107c8..2eb65e1bf938b9 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -929,11 +929,6 @@ static int rcar_dmac_alloc_chan_resources(struct dma_chan *chan) struct rcar_dmac_chan *rchan = to_rcar_dmac_chan(chan); int ret; - INIT_LIST_HEAD(&rchan->desc.free); - INIT_LIST_HEAD(&rchan->desc.pending); - INIT_LIST_HEAD(&rchan->desc.active); - INIT_LIST_HEAD(&rchan->desc.done); - INIT_LIST_HEAD(&rchan->desc.wait); INIT_LIST_HEAD(&rchan->desc.chunks_free); INIT_LIST_HEAD(&rchan->desc.pages); @@ -970,11 +965,11 @@ static void rcar_dmac_free_chan_resources(struct dma_chan *chan) rchan->mid_rid = -EINVAL; } - list_splice(&rchan->desc.free, &list); - list_splice(&rchan->desc.pending, &list); - list_splice(&rchan->desc.active, &list); - list_splice(&rchan->desc.done, &list); - list_splice(&rchan->desc.wait, &list); + list_splice_init(&rchan->desc.free, &list); + list_splice_init(&rchan->desc.pending, &list); + list_splice_init(&rchan->desc.active, &list); + list_splice_init(&rchan->desc.done, &list); + list_splice_init(&rchan->desc.wait, &list); list_for_each_entry(desc, &list, node) rcar_dmac_realloc_hwdesc(rchan, desc, 0); @@ -1519,6 +1514,12 @@ static int rcar_dmac_chan_probe(struct rcar_dmac *dmac, spin_lock_init(&rchan->lock); + INIT_LIST_HEAD(&rchan->desc.free); + INIT_LIST_HEAD(&rchan->desc.pending); + INIT_LIST_HEAD(&rchan->desc.active); + INIT_LIST_HEAD(&rchan->desc.done); + INIT_LIST_HEAD(&rchan->desc.wait); + /* Request the channel interrupt. */ sprintf(pdev_irqname, "ch%u", index); irq = platform_get_irq_byname(pdev, pdev_irqname); From 6a634808e315a148dfe8db925215cbaaa3ea1831 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 Jan 2015 15:58:53 +0200 Subject: [PATCH 330/360] dmaengine: rcar-dmac: Allocate hardware descriptors with DMAC device When wired to an IOMMU to access data, the DMAC accesses the hardware descriptors through the IOMMU as well. We're using the DMA mapping API to allocate the descriptors, but with a NULL device at the moment, which prevents IOMMU mappings from being created. Fix this by passing the DMAC device instead. Signed-off-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 2eb65e1bf938b9..bb93038c48b9aa 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -655,8 +655,8 @@ static void rcar_dmac_realloc_hwdesc(struct rcar_dmac_chan *chan, return; if (desc->hwdescs.mem) { - dma_free_coherent(NULL, desc->hwdescs.size, desc->hwdescs.mem, - desc->hwdescs.dma); + dma_free_coherent(chan->chan.device->dev, desc->hwdescs.size, + desc->hwdescs.mem, desc->hwdescs.dma); desc->hwdescs.mem = NULL; desc->hwdescs.size = 0; } @@ -664,8 +664,8 @@ static void rcar_dmac_realloc_hwdesc(struct rcar_dmac_chan *chan, if (!size) return; - desc->hwdescs.mem = dma_alloc_coherent(NULL, size, &desc->hwdescs.dma, - GFP_NOWAIT); + desc->hwdescs.mem = dma_alloc_coherent(chan->chan.device->dev, size, + &desc->hwdescs.dma, GFP_NOWAIT); if (!desc->hwdescs.mem) return; From 3f46306127bb7d8a69078ff9ef8a5827677c2159 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 Jan 2015 18:33:29 +0200 Subject: [PATCH 331/360] dmaengine: rcar-dmac: Work around descriptor mode IOMMU errata When descriptor memory is accessed through an IOMMU the DMADAR register isn't initialized automatically from the first descriptor at beginning of transfer by the DMAC like it should. Initialize it manually with the destination address of the first chunk. Signed-off-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index bb93038c48b9aa..711da01a200b61 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -325,6 +325,8 @@ static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan) rcar_dmac_chan_write(chan, RCAR_DMARS, chan->mid_rid); if (desc->hwdescs.use) { + struct rcar_dmac_xfer_chunk *chunk; + dev_dbg(chan->chan.device->dev, "chan%u: queue desc %p: %u@%pad\n", chan->index, desc, desc->nchunks, &desc->hwdescs.dma); @@ -340,6 +342,18 @@ static void rcar_dmac_chan_start_xfer(struct rcar_dmac_chan *chan) RCAR_DMACHCRB_DCNT(desc->nchunks - 1) | RCAR_DMACHCRB_DRST); + /* + * Errata: When descriptor memory is accessed through an IOMMU + * the DMADAR register isn't initialized automatically from the + * first descriptor at beginning of transfer by the DMAC like it + * should. Initialize it manually with the destination address + * of the first chunk. + */ + chunk = list_first_entry(&desc->chunks, + struct rcar_dmac_xfer_chunk, node); + rcar_dmac_chan_write(chan, RCAR_DMADAR, + chunk->dst_addr & 0xffffffff); + /* * Program the descriptor stage interrupt to occur after the end * of the first stage. From be6893e1958035cbeff281b833777c5cd3fb36ad Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 Jan 2015 19:04:10 +0200 Subject: [PATCH 332/360] dmaengine: rcar-dmac: Disable channel 0 when using IOMMU A still unconfirmed hardware bug prevents the IPMMU microTLB 0 to be flushed correctly, resulting in memory corruption. DMAC 0 channel 0 is connected to microTLB 0 on currently supported platforms, so we can't use it with the IPMMU. As the IOMMU API operates at the device level we can't disable it selectively, so ignore channel 0 for now if the device is part of an IOMMU group. Signed-off-by: Laurent Pinchart Acked-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 711da01a200b61..a18d16cc479559 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -1593,6 +1593,7 @@ static int rcar_dmac_probe(struct platform_device *pdev) DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES | DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES | DMA_SLAVE_BUSWIDTH_64_BYTES; + unsigned int channels_offset = 0; struct dma_device *engine; struct rcar_dmac *dmac; struct resource *mem; @@ -1612,6 +1613,19 @@ static int rcar_dmac_probe(struct platform_device *pdev) if (ret < 0) return ret; + /* + * A still unconfirmed hardware bug prevents the IPMMU microTLB 0 to be + * flushed correctly, resulting in memory corruption. DMAC 0 channel 0 + * is connected to microTLB 0 on currently supported platforms, so we + * can't use it with the IPMMU. As the IOMMU API operates at the device + * level we can't disable it selectively, so ignore channel 0 for now if + * the device is part of an IOMMU group. + */ + if (pdev->dev.iommu_group) { + dmac->n_channels--; + channels_offset = 1; + } + dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels, sizeof(*dmac->channels), GFP_KERNEL); if (!dmac->channels) @@ -1662,7 +1676,8 @@ static int rcar_dmac_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dmac->engine.channels); for (i = 0; i < dmac->n_channels; ++i) { - ret = rcar_dmac_chan_probe(dmac, &dmac->channels[i], i); + ret = rcar_dmac_chan_probe(dmac, &dmac->channels[i], + i + channels_offset); if (ret < 0) goto error; } From aee4d1fac887252faf6f7caf7bf1616131d5dbcd Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Wed, 11 Feb 2015 13:23:17 +0100 Subject: [PATCH 333/360] dmaengine: pl330: improve pl330_tx_status() function This patch adds possibility to read residue of DMA transfer. It's useful when we want to know how many bytes have been transferred before we terminate channel. It can take place, for example, on timeout interrupt. Signed-off-by: Lukasz Czerwinski Signed-off-by: Robert Baldyga Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 74 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 2dbc93011c0efa..944b67622916c7 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -504,6 +504,9 @@ struct dma_pl330_desc { enum desc_status status; + int bytes_requested; + bool last; + /* The channel which currently holds this desc */ struct dma_pl330_chan *pchan; @@ -2173,11 +2176,74 @@ static void pl330_free_chan_resources(struct dma_chan *chan) pm_runtime_put_autosuspend(pch->dmac->ddma.dev); } +int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, + struct dma_pl330_desc *desc) +{ + struct pl330_thread *thrd = pch->thread; + struct pl330_dmac *pl330 = pch->dmac; + void __iomem *regs = thrd->dmac->base; + u32 val, addr; + + pm_runtime_get_sync(pl330->ddma.dev); + val = addr = 0; + if (desc->rqcfg.src_inc) { + val = readl(regs + SA(thrd->id)); + addr = desc->px.src_addr; + } else { + val = readl(regs + DA(thrd->id)); + addr = desc->px.dst_addr; + } + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); + pm_runtime_put_autosuspend(pl330->ddma.dev); + return val - addr; +} + static enum dma_status pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - return dma_cookie_status(chan, cookie, txstate); + enum dma_status ret; + unsigned long flags; + struct dma_pl330_desc *desc, *running = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); + unsigned int transferred, residual = 0; + + ret = dma_cookie_status(chan, cookie, txstate); + + if (!txstate) + return ret; + + if (ret == DMA_COMPLETE) + goto out; + + spin_lock_irqsave(&pch->lock, flags); + + if (pch->thread->req_running != -1) + running = pch->thread->req[pch->thread->req_running].desc; + + /* Check in pending list */ + list_for_each_entry(desc, &pch->work_list, node) { + if (desc->status == DONE) + transferred = desc->bytes_requested; + else if (running && desc == running) + transferred = + pl330_get_current_xferred_count(pch, desc); + else + transferred = 0; + residual += desc->bytes_requested - transferred; + if (desc->txd.cookie == cookie) { + ret = desc->status; + break; + } + if (desc->last) + residual = 0; + } + spin_unlock_irqrestore(&pch->lock, flags); + +out: + dma_set_residue(txstate, residual); + + return ret; } static void pl330_issue_pending(struct dma_chan *chan) @@ -2222,12 +2288,14 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) desc->txd.callback = last->txd.callback; desc->txd.callback_param = last->txd.callback_param; } + last->last = false; dma_cookie_assign(&desc->txd); list_move_tail(&desc->node, &pch->submitted_list); } + last->last = true; cookie = dma_cookie_assign(&last->txd); list_add_tail(&last->node, &pch->submitted_list); spin_unlock_irqrestore(&pch->lock, flags); @@ -2450,6 +2518,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->rqtype = direction; desc->rqcfg.brst_size = pch->burst_sz; desc->rqcfg.brst_len = 1; + desc->bytes_requested = period_len; fill_px(&desc->px, dst, src, period_len); if (!first) @@ -2592,6 +2661,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->rqcfg.brst_size = pch->burst_sz; desc->rqcfg.brst_len = 1; desc->rqtype = direction; + desc->bytes_requested = sg_dma_len(sg); } /* Return the last desc in the chain */ @@ -2777,7 +2847,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->src_addr_widths = PL330_DMA_BUSWIDTHS; pd->dst_addr_widths = PL330_DMA_BUSWIDTHS; pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - pd->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; ret = dma_async_device_register(pd); if (ret) { From 88987d2c7534a0269f567fb101e6d71a08f0f01d Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Wed, 11 Feb 2015 13:23:18 +0100 Subject: [PATCH 334/360] dmaengine: pl330: add DMA_PAUSE feature DMA_PAUSE command is used for halting DMA transfer on chosen channel. It can be useful when we want to safely read residue before terminating all requests on channel. Otherwise there can be situation when some data is transferred before channel termination but after reading residue, which obviously results with data loss. To avoid this situation we can pause channel, read residue and then terminate all requests. This scenario is common, for example, in serial port drivers. Signed-off-by: Robert Baldyga Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 944b67622916c7..0e1f56772855d8 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2155,6 +2155,33 @@ static int pl330_terminate_all(struct dma_chan *chan) return 0; } +/* + * We don't support DMA_RESUME command because of hardware + * limitations, so after pausing the channel we cannot restore + * it to active state. We have to terminate channel and setup + * DMA transfer again. This pause feature was implemented to + * allow safely read residue before channel termination. + */ +int pl330_pause(struct dma_chan *chan) +{ + struct dma_pl330_chan *pch = to_pchan(chan); + struct pl330_dmac *pl330 = pch->dmac; + unsigned long flags; + + pm_runtime_get_sync(pl330->ddma.dev); + spin_lock_irqsave(&pch->lock, flags); + + spin_lock(&pl330->lock); + _stop(pch->thread); + spin_unlock(&pl330->lock); + + spin_unlock_irqrestore(&pch->lock, flags); + pm_runtime_mark_last_busy(pl330->ddma.dev); + pm_runtime_put_autosuspend(pl330->ddma.dev); + + return 0; +} + static void pl330_free_chan_resources(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); @@ -2842,6 +2869,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_config = pl330_config; + pd->device_pause = pl330_pause; pd->device_terminate_all = pl330_terminate_all; pd->device_issue_pending = pl330_issue_pending; pd->src_addr_widths = PL330_DMA_BUSWIDTHS; From 0a65fbf64dad890d9dd60ab1dc0ebdb9fb0a9e33 Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Wed, 11 Feb 2015 15:40:50 +0530 Subject: [PATCH 335/360] mfd: intel_soc_pmic: Add missing error check for devm_kzalloc This patch add a missing check on the return value of devm_kzalloc, which would cause a NULL pointer dereference in a OOM situation. Signed-off-by: Kiran Padwal Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index df7b0642a5b4d2..80cef048b90402 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -64,6 +64,9 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, config = (struct intel_soc_pmic_config *)id->driver_data; pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + dev_set_drvdata(dev, pmic); pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); From e084c1bd40926938ff8d26af3bde34396dd4d06d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 16 Feb 2015 14:32:03 -0500 Subject: [PATCH 336/360] Revert "locks: keep a count of locks on the flctx lists" This reverts commit 9bd0f45b7037fcfa8b575c7e27d0431d6e6dc3bb. Linus rightly pointed out that I failed to initialize the counters when adding them, so they don't work as expected. Just revert this patch for now. Reported-by: Linus Torvalds Signed-off-by: Jeff Layton --- fs/ceph/locks.c | 9 +++++++-- fs/cifs/file.c | 14 ++++++++++---- fs/locks.c | 45 ++++++++++++++++----------------------------- include/linux/fs.h | 3 --- 4 files changed, 33 insertions(+), 38 deletions(-) diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index 06ea5cd05cd9eb..4347039ecc183d 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -245,6 +245,7 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) */ void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) { + struct file_lock *lock; struct file_lock_context *ctx; *fcntl_count = 0; @@ -252,8 +253,12 @@ void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) ctx = inode->i_flctx; if (ctx) { - *fcntl_count = ctx->flc_posix_cnt; - *flock_count = ctx->flc_flock_cnt; + spin_lock(&ctx->flc_lock); + list_for_each_entry(lock, &ctx->flc_posix, fl_list) + ++(*fcntl_count); + list_for_each_entry(lock, &ctx->flc_flock, fl_list) + ++(*flock_count); + spin_unlock(&ctx->flc_lock); } dout("counted %d flock locks and %d fcntl locks", *flock_count, *fcntl_count); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 8fe1f7a21b3eaa..a94b3e67318283 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1129,7 +1129,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct file_lock *flock; struct file_lock_context *flctx = inode->i_flctx; - unsigned int i; + unsigned int count = 0, i; int rc = 0, xid, type; struct list_head locks_to_send, *el; struct lock_to_push *lck, *tmp; @@ -1140,14 +1140,20 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) if (!flctx) goto out; + spin_lock(&flctx->flc_lock); + list_for_each(el, &flctx->flc_posix) { + count++; + } + spin_unlock(&flctx->flc_lock); + INIT_LIST_HEAD(&locks_to_send); /* - * Allocating flc_posix_cnt locks is enough because no FL_POSIX locks - * can be added to the list while we are holding cinode->lock_sem that + * Allocating count locks is enough because no FL_POSIX locks can be + * added to the list while we are holding cinode->lock_sem that * protects locking operations of this inode. */ - for (i = 0; i < flctx->flc_posix_cnt; i++) { + for (i = 0; i < count; i++) { lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); if (!lck) { rc = -ENOMEM; diff --git a/fs/locks.c b/fs/locks.c index 4753218f308ed3..7998f670812ce4 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -681,21 +681,18 @@ static void locks_wake_up_blocks(struct file_lock *blocker) } static void -locks_insert_lock_ctx(struct file_lock *fl, int *counter, - struct list_head *before) +locks_insert_lock_ctx(struct file_lock *fl, struct list_head *before) { fl->fl_nspid = get_pid(task_tgid(current)); list_add_tail(&fl->fl_list, before); - ++*counter; locks_insert_global_locks(fl); } static void -locks_unlink_lock_ctx(struct file_lock *fl, int *counter) +locks_unlink_lock_ctx(struct file_lock *fl) { locks_delete_global_locks(fl); list_del_init(&fl->fl_list); - --*counter; if (fl->fl_nspid) { put_pid(fl->fl_nspid); fl->fl_nspid = NULL; @@ -704,10 +701,9 @@ locks_unlink_lock_ctx(struct file_lock *fl, int *counter) } static void -locks_delete_lock_ctx(struct file_lock *fl, int *counter, - struct list_head *dispose) +locks_delete_lock_ctx(struct file_lock *fl, struct list_head *dispose) { - locks_unlink_lock_ctx(fl, counter); + locks_unlink_lock_ctx(fl); if (dispose) list_add(&fl->fl_list, dispose); else @@ -895,7 +891,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) if (request->fl_type == fl->fl_type) goto out; found = true; - locks_delete_lock_ctx(fl, &ctx->flc_flock_cnt, &dispose); + locks_delete_lock_ctx(fl, &dispose); break; } @@ -929,7 +925,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) if (request->fl_flags & FL_ACCESS) goto out; locks_copy_lock(new_fl, request); - locks_insert_lock_ctx(new_fl, &ctx->flc_flock_cnt, &ctx->flc_flock); + locks_insert_lock_ctx(new_fl, &ctx->flc_flock); new_fl = NULL; error = 0; @@ -1046,8 +1042,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str else request->fl_end = fl->fl_end; if (added) { - locks_delete_lock_ctx(fl, &ctx->flc_posix_cnt, - &dispose); + locks_delete_lock_ctx(fl, &dispose); continue; } request = fl; @@ -1076,8 +1071,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str * one (This may happen several times). */ if (added) { - locks_delete_lock_ctx(fl, - &ctx->flc_posix_cnt, &dispose); + locks_delete_lock_ctx(fl, &dispose); continue; } /* @@ -1093,10 +1087,8 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str locks_copy_lock(new_fl, request); request = new_fl; new_fl = NULL; - locks_insert_lock_ctx(request, - &ctx->flc_posix_cnt, &fl->fl_list); - locks_delete_lock_ctx(fl, - &ctx->flc_posix_cnt, &dispose); + locks_insert_lock_ctx(request, &fl->fl_list); + locks_delete_lock_ctx(fl, &dispose); added = true; } } @@ -1124,8 +1116,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str goto out; } locks_copy_lock(new_fl, request); - locks_insert_lock_ctx(new_fl, &ctx->flc_posix_cnt, - &fl->fl_list); + locks_insert_lock_ctx(new_fl, &fl->fl_list); new_fl = NULL; } if (right) { @@ -1136,8 +1127,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str left = new_fl2; new_fl2 = NULL; locks_copy_lock(left, right); - locks_insert_lock_ctx(left, &ctx->flc_posix_cnt, - &fl->fl_list); + locks_insert_lock_ctx(left, &fl->fl_list); } right->fl_start = request->fl_end + 1; locks_wake_up_blocks(right); @@ -1321,7 +1311,6 @@ static void lease_clear_pending(struct file_lock *fl, int arg) /* We already had a lease on this file; just change its type */ int lease_modify(struct file_lock *fl, int arg, struct list_head *dispose) { - struct file_lock_context *flctx; int error = assign_type(fl, arg); if (error) @@ -1331,7 +1320,6 @@ int lease_modify(struct file_lock *fl, int arg, struct list_head *dispose) if (arg == F_UNLCK) { struct file *filp = fl->fl_file; - flctx = file_inode(filp)->i_flctx; f_delown(filp); filp->f_owner.signum = 0; fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync); @@ -1339,7 +1327,7 @@ int lease_modify(struct file_lock *fl, int arg, struct list_head *dispose) printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync); fl->fl_fasync = NULL; } - locks_delete_lock_ctx(fl, &flctx->flc_lease_cnt, dispose); + locks_delete_lock_ctx(fl, dispose); } return 0; } @@ -1456,8 +1444,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type) fl->fl_downgrade_time = break_time; } if (fl->fl_lmops->lm_break(fl)) - locks_delete_lock_ctx(fl, &ctx->flc_lease_cnt, - &dispose); + locks_delete_lock_ctx(fl, &dispose); } if (list_empty(&ctx->flc_lease)) @@ -1697,7 +1684,7 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr if (!leases_enable) goto out; - locks_insert_lock_ctx(lease, &ctx->flc_lease_cnt, &ctx->flc_lease); + locks_insert_lock_ctx(lease, &ctx->flc_lease); /* * The check in break_lease() is lockless. It's possible for another * open to race in after we did the earlier check for a conflicting @@ -1710,7 +1697,7 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr smp_mb(); error = check_conflicting_open(dentry, arg, lease->fl_flags); if (error) { - locks_unlink_lock_ctx(lease, &ctx->flc_lease_cnt); + locks_unlink_lock_ctx(lease); goto out; } diff --git a/include/linux/fs.h b/include/linux/fs.h index e49f10cc8a7383..a5a303e8a33c5d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -969,9 +969,6 @@ struct file_lock_context { struct list_head flc_flock; struct list_head flc_posix; struct list_head flc_lease; - int flc_flock_cnt; - int flc_posix_cnt; - int flc_lease_cnt; }; /* The following constant reflects the upper bound of the file/locking space */ From e68c48f97547979c91de04b487d79dc0d3be7015 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 17 Feb 2015 16:12:43 +1030 Subject: [PATCH 337/360] virtio_net: unconditionally define struct virtio_net_hdr_v1. This was introduced in commit ed9ecb0415b97b5f9f91f146e1977bb372c74c6d, but only defined if !VIRTIO_NET_NO_LEGACY. We should always define it: easier for users to have conditional legacy code. Suggested-by: "Michael S. Tsirkin" Signed-off-by: Rusty Russell --- include/uapi/linux/virtio_net.h | 54 +++++++++++++++------------------ 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index 4a9b58113d6e78..7bbee79ca2933f 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -74,39 +74,12 @@ struct virtio_net_config { __u16 max_virtqueue_pairs; } __attribute__((packed)); -#ifndef VIRTIO_NET_NO_LEGACY -/* This header comes first in the scatter-gather list. - * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must - * be the first element of the scatter-gather list. If you don't - * specify GSO or CSUM features, you can simply ignore the header. */ -struct virtio_net_hdr { -#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset -#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid - __u8 flags; -#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame -#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) -#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) -#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP -#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set - __u8 gso_type; - __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ - __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ - __virtio16 csum_start; /* Position to start checksumming from */ - __virtio16 csum_offset; /* Offset after that to place checksum */ -}; - -/* This is the version of the header to use when the MRG_RXBUF - * feature has been negotiated. */ -struct virtio_net_hdr_mrg_rxbuf { - struct virtio_net_hdr hdr; - __virtio16 num_buffers; /* Number of merged rx buffers */ -}; -#else /* ... VIRTIO_NET_NO_LEGACY */ /* * This header comes first in the scatter-gather list. If you don't * specify GSO or CSUM features, you can simply ignore the header. * - * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf. + * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf, + * only flattened. */ struct virtio_net_hdr_v1 { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ @@ -124,6 +97,29 @@ struct virtio_net_hdr_v1 { __virtio16 csum_offset; /* Offset after that to place checksum */ __virtio16 num_buffers; /* Number of merged rx buffers */ }; + +#ifndef VIRTIO_NET_NO_LEGACY +/* This header comes first in the scatter-gather list. + * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must + * be the first element of the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. */ +struct virtio_net_hdr { + /* See VIRTIO_NET_HDR_F_* */ + __u8 flags; + /* See VIRTIO_NET_HDR_GSO_* */ + __u8 gso_type; + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ + __virtio16 csum_start; /* Position to start checksumming from */ + __virtio16 csum_offset; /* Offset after that to place checksum */ +}; + +/* This is the version of the header to use when the MRG_RXBUF + * feature has been negotiated. */ +struct virtio_net_hdr_mrg_rxbuf { + struct virtio_net_hdr hdr; + __virtio16 num_buffers; /* Number of merged rx buffers */ +}; #endif /* ...VIRTIO_NET_NO_LEGACY */ /* From 5b40a7daf51812b35cf05d1601a779a7043f8414 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 17 Feb 2015 16:12:44 +1030 Subject: [PATCH 338/360] virtio: don't set VIRTIO_CONFIG_S_DRIVER_OK twice. I noticed this with the console device. It's not *wrong*, just a bit weird. Signed-off-by: Rusty Russell --- drivers/virtio/virtio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index b9f70dfc475185..5ce2aa48fc6e09 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -236,7 +236,10 @@ static int virtio_dev_probe(struct device *_d) if (err) goto err; - add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); + /* If probe didn't do it, mark device DRIVER_OK ourselves. */ + if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) + virtio_device_ready(dev); + if (drv->scan) drv->scan(dev); From c4e136cda11cb5f87683dd5b154a2d15ea5898b3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 16 Feb 2015 19:37:42 -0500 Subject: [PATCH 339/360] locks: only remove leases associated with the file being closed We don't want to remove all leases just because one filp was closed. Signed-off-by: Jeff Layton --- fs/locks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/locks.c b/fs/locks.c index 7998f670812ce4..fe8f9f46445bca 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2435,7 +2435,8 @@ locks_remove_lease(struct file *filp) spin_lock(&ctx->flc_lock); list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) - lease_modify(fl, F_UNLCK, &dispose); + if (filp == fl->fl_file) + lease_modify(fl, F_UNLCK, &dispose); spin_unlock(&ctx->flc_lock); locks_dispose_list(&dispose); } From 267f1128583074b575b90a58de4dcb12dd25af96 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Feb 2015 14:44:08 -0500 Subject: [PATCH 340/360] locks: remove conditional lock release in middle of flock_lock_file As Linus pointed out: Say we have an existing flock, and now do a new one that conflicts. I see what looks like three separate bugs. - We go through the first loop, find a lock of another type, and delete it in preparation for replacing it - we *drop* the lock context spinlock. - BUG #1? So now there is no lock at all, and somebody can come in and see that unlocked state. Is that really valid? - another thread comes in while the first thread dropped the lock context lock, and wants to add its own lock. It doesn't see the deleted or pending locks, so it just adds it - the first thread gets the context spinlock again, and adds the lock that replaced the original - BUG #2? So now there are *two* locks on the thing, and the next time you do an unlock (or when you close the file), it will only remove/replace the first one. ...remove the "drop the spinlock" code in the middle of this function as it has always been suspicious. This should eliminate the potential race that can leave two locks for the same struct file on the list. He also pointed out another thing as a bug -- namely that you flock_lock_file removes the lock from the list unconditionally when doing a lock upgrade, without knowing whether it'll be able to set the new lock. Bruce pointed out that this is expected behavior and may help prevent certain deadlock situations. We may want to revisit that at some point, but it's probably best that we do so in the context of a different patchset. Reported-by: Linus Torvalds Signed-off-by: Jeff Layton --- fs/locks.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index fe8f9f46445bca..90b652ad306f12 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -901,16 +901,6 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) goto out; } - /* - * If a higher-priority process was blocked on the old file lock, - * give it the opportunity to lock the file. - */ - if (found) { - spin_unlock(&ctx->flc_lock); - cond_resched(); - spin_lock(&ctx->flc_lock); - } - find_conflict: list_for_each_entry(fl, &ctx->flc_flock, fl_list) { if (!flock_locks_conflict(request, fl)) From 93937669e9b5873808e4f5dfd6cace53bdc57f17 Mon Sep 17 00:00:00 2001 From: Naidu Tellapati Date: Tue, 6 Jan 2015 10:19:34 -0300 Subject: [PATCH 341/360] watchdog: ImgTec PDC Watchdog Timer Driver This commit adds support for ImgTec PowerDown Controller Watchdog Timer. Reviewed-by: Andrew Bresticker Signed-off-by: Naidu Tellapati Signed-off-by: Jude Abraham [ezequiel: Minor style fixes] Signed-off-by: Ezequiel Garcia Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 11 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/imgpdc_wdt.c | 289 ++++++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 drivers/watchdog/imgpdc_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 08f41add146134..0a1396b70b95a7 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1235,6 +1235,17 @@ config BCM_KONA_WDT_DEBUG If in doubt, say 'N'. +config IMGPDC_WDT + tristate "Imagination Technologies PDC Watchdog Timer" + depends on HAS_IOMEM + depends on METAG || MIPS || COMPILE_TEST + help + Driver for Imagination Technologies PowerDown Controller + Watchdog Timer. + + To compile this driver as a loadable module, choose M here. + The module will be called imgpdc_wdt. + config LANTIQ_WDT tristate "Lantiq SoC watchdog" depends on LANTIQ diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c569ec8f8a76b9..d4dfbb4fd01a6f 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -142,6 +142,7 @@ obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o +obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o # PARISC Architecture diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c new file mode 100644 index 00000000000000..c8def68d9e4cf3 --- /dev/null +++ b/drivers/watchdog/imgpdc_wdt.c @@ -0,0 +1,289 @@ +/* + * Imagination Technologies PowerDown Controller Watchdog Timer. + * + * Copyright (c) 2014 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione + * 2012 Henrik Nordstrom + */ + +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define PDC_WDT_SOFT_RESET 0x00 +#define PDC_WDT_CONFIG 0x04 + #define PDC_WDT_CONFIG_ENABLE BIT(31) + #define PDC_WDT_CONFIG_DELAY_MASK 0x1f + +#define PDC_WDT_TICKLE1 0x08 +#define PDC_WDT_TICKLE1_MAGIC 0xabcd1234 +#define PDC_WDT_TICKLE2 0x0c +#define PDC_WDT_TICKLE2_MAGIC 0x4321dcba + +#define PDC_WDT_TICKLE_STATUS_MASK 0x7 +#define PDC_WDT_TICKLE_STATUS_SHIFT 0 +#define PDC_WDT_TICKLE_STATUS_HRESET 0x0 /* Hard reset */ +#define PDC_WDT_TICKLE_STATUS_TIMEOUT 0x1 /* Timeout */ +#define PDC_WDT_TICKLE_STATUS_TICKLE 0x2 /* Tickled incorrectly */ +#define PDC_WDT_TICKLE_STATUS_SRESET 0x3 /* Soft reset */ +#define PDC_WDT_TICKLE_STATUS_USER 0x4 /* User reset */ + +/* Timeout values are in seconds */ +#define PDC_WDT_MIN_TIMEOUT 1 +#define PDC_WDT_DEF_TIMEOUT 64 + +static int heartbeat; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " + "(default = " __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct pdc_wdt_dev { + struct watchdog_device wdt_dev; + struct clk *wdt_clk; + struct clk *sys_clk; + void __iomem *base; +}; + +static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) +{ + struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); + + writel(PDC_WDT_TICKLE1_MAGIC, wdt->base + PDC_WDT_TICKLE1); + writel(PDC_WDT_TICKLE2_MAGIC, wdt->base + PDC_WDT_TICKLE2); + + return 0; +} + +static int pdc_wdt_stop(struct watchdog_device *wdt_dev) +{ + unsigned int val; + struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); + + val = readl(wdt->base + PDC_WDT_CONFIG); + val &= ~PDC_WDT_CONFIG_ENABLE; + writel(val, wdt->base + PDC_WDT_CONFIG); + + /* Must tickle to finish the stop */ + pdc_wdt_keepalive(wdt_dev); + + return 0; +} + +static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int new_timeout) +{ + unsigned int val; + struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); + unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); + + wdt->wdt_dev.timeout = new_timeout; + + val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; + val |= order_base_2(new_timeout * clk_rate) - 1; + writel(val, wdt->base + PDC_WDT_CONFIG); + + return 0; +} + +/* Start the watchdog timer (delay should already be set) */ +static int pdc_wdt_start(struct watchdog_device *wdt_dev) +{ + unsigned int val; + struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); + + val = readl(wdt->base + PDC_WDT_CONFIG); + val |= PDC_WDT_CONFIG_ENABLE; + writel(val, wdt->base + PDC_WDT_CONFIG); + + return 0; +} + +static struct watchdog_info pdc_wdt_info = { + .identity = "IMG PDC Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops pdc_wdt_ops = { + .owner = THIS_MODULE, + .start = pdc_wdt_start, + .stop = pdc_wdt_stop, + .ping = pdc_wdt_keepalive, + .set_timeout = pdc_wdt_set_timeout, +}; + +static int pdc_wdt_probe(struct platform_device *pdev) +{ + int ret, val; + unsigned long clk_rate; + struct resource *res; + struct pdc_wdt_dev *pdc_wdt; + + pdc_wdt = devm_kzalloc(&pdev->dev, sizeof(*pdc_wdt), GFP_KERNEL); + if (!pdc_wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pdc_wdt->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdc_wdt->base)) + return PTR_ERR(pdc_wdt->base); + + pdc_wdt->sys_clk = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(pdc_wdt->sys_clk)) { + dev_err(&pdev->dev, "failed to get the sys clock\n"); + return PTR_ERR(pdc_wdt->sys_clk); + } + + pdc_wdt->wdt_clk = devm_clk_get(&pdev->dev, "wdt"); + if (IS_ERR(pdc_wdt->wdt_clk)) { + dev_err(&pdev->dev, "failed to get the wdt clock\n"); + return PTR_ERR(pdc_wdt->wdt_clk); + } + + ret = clk_prepare_enable(pdc_wdt->sys_clk); + if (ret) { + dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); + return ret; + } + + ret = clk_prepare_enable(pdc_wdt->wdt_clk); + if (ret) { + dev_err(&pdev->dev, "could not prepare or enable wdt clock\n"); + goto disable_sys_clk; + } + + /* We use the clock rate to calculate the max timeout */ + clk_rate = clk_get_rate(pdc_wdt->wdt_clk); + if (clk_rate == 0) { + dev_err(&pdev->dev, "failed to get clock rate\n"); + ret = -EINVAL; + goto disable_wdt_clk; + } + + if (order_base_2(clk_rate) > PDC_WDT_CONFIG_DELAY_MASK + 1) { + dev_err(&pdev->dev, "invalid clock rate\n"); + ret = -EINVAL; + goto disable_wdt_clk; + } + + if (order_base_2(clk_rate) == 0) + pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT + 1; + else + pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT; + + pdc_wdt->wdt_dev.info = &pdc_wdt_info; + pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; + pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; + pdc_wdt->wdt_dev.parent = &pdev->dev; + + ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); + if (ret < 0) { + pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout; + dev_warn(&pdev->dev, + "Initial timeout out of range! setting max timeout\n"); + } + + pdc_wdt_stop(&pdc_wdt->wdt_dev); + + /* Find what caused the last reset */ + val = readl(pdc_wdt->base + PDC_WDT_TICKLE1); + val = (val & PDC_WDT_TICKLE_STATUS_MASK) >> PDC_WDT_TICKLE_STATUS_SHIFT; + switch (val) { + case PDC_WDT_TICKLE_STATUS_TICKLE: + case PDC_WDT_TICKLE_STATUS_TIMEOUT: + pdc_wdt->wdt_dev.bootstatus |= WDIOF_CARDRESET; + dev_info(&pdev->dev, + "watchdog module last reset due to timeout\n"); + break; + case PDC_WDT_TICKLE_STATUS_HRESET: + dev_info(&pdev->dev, + "watchdog module last reset due to hard reset\n"); + break; + case PDC_WDT_TICKLE_STATUS_SRESET: + dev_info(&pdev->dev, + "watchdog module last reset due to soft reset\n"); + break; + case PDC_WDT_TICKLE_STATUS_USER: + dev_info(&pdev->dev, + "watchdog module last reset due to user reset\n"); + break; + default: + dev_info(&pdev->dev, + "contains an illegal status code (%08x)\n", val); + break; + } + + watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout); + + platform_set_drvdata(pdev, pdc_wdt); + watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); + + ret = watchdog_register_device(&pdc_wdt->wdt_dev); + if (ret) + goto disable_wdt_clk; + + return 0; + +disable_wdt_clk: + clk_disable_unprepare(pdc_wdt->wdt_clk); +disable_sys_clk: + clk_disable_unprepare(pdc_wdt->sys_clk); + return ret; +} + +static void pdc_wdt_shutdown(struct platform_device *pdev) +{ + struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); + + pdc_wdt_stop(&pdc_wdt->wdt_dev); +} + +static int pdc_wdt_remove(struct platform_device *pdev) +{ + struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); + + pdc_wdt_stop(&pdc_wdt->wdt_dev); + watchdog_unregister_device(&pdc_wdt->wdt_dev); + clk_disable_unprepare(pdc_wdt->wdt_clk); + clk_disable_unprepare(pdc_wdt->sys_clk); + + return 0; +} + +static const struct of_device_id pdc_wdt_match[] = { + { .compatible = "img,pdc-wdt" }, + {} +}; +MODULE_DEVICE_TABLE(of, pdc_wdt_match); + +static struct platform_driver pdc_wdt_driver = { + .driver = { + .name = "imgpdc-wdt", + .of_match_table = pdc_wdt_match, + }, + .probe = pdc_wdt_probe, + .remove = pdc_wdt_remove, + .shutdown = pdc_wdt_shutdown, +}; +module_platform_driver(pdc_wdt_driver); + +MODULE_AUTHOR("Jude Abraham "); +MODULE_AUTHOR("Naidu Tellapati "); +MODULE_DESCRIPTION("Imagination Technologies PDC Watchdog Timer Driver"); +MODULE_LICENSE("GPL v2"); From 1888e7ad568835debfc7f6dc9d722b2efc55c55d Mon Sep 17 00:00:00 2001 From: Naidu Tellapati Date: Tue, 6 Jan 2015 10:19:35 -0300 Subject: [PATCH 342/360] DT: watchdog: Add ImgTec PDC Watchdog Timer binding documentation Add the devicetree binding document for ImgTec PDC Watchdog Timer. Reviewed-by: Andrew Bresticker Signed-off-by: Naidu Tellapati Signed-off-by: Jude Abraham Signed-off-by: Ezequiel Garcia Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../bindings/watchdog/imgpdc-wdt.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/imgpdc-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/imgpdc-wdt.txt b/Documentation/devicetree/bindings/watchdog/imgpdc-wdt.txt new file mode 100644 index 00000000000000..b2fa11fd43dee5 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/imgpdc-wdt.txt @@ -0,0 +1,19 @@ +*ImgTec PowerDown Controller (PDC) Watchdog Timer (WDT) + +Required properties: +- compatible : Should be "img,pdc-wdt" +- reg : Should contain WDT registers location and length +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Should contain "wdt" and "sys"; the watchdog counter + clock and register interface clock respectively. +- interrupts : Should contain WDT interrupt + +Examples: + +watchdog@18102100 { + compatible = "img,pdc-wdt"; + reg = <0x18102100 0x100>; + clocks = <&pdc_wdt_clk>, <&sys_clk>; + clock-names = "wdt", "sys"; + interrupts = <0 52 IRQ_TYPE_LEVEL_HIGH>; +}; From 4bd8ce33c0046e81dfc2b4d5886b6b253741261c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2015 10:09:17 +0100 Subject: [PATCH 343/360] watchdog: imx2: Constify struct regmap_config and watchdog_ops The regmap_config struct may be const because it is not modified by the driver and regmap_init() accepts pointer to const. Make struct watchdog_ops const as well. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/imx2_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 5142bbabe0279f..5e6d808d358a5c 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -205,7 +205,7 @@ static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog) } } -static struct watchdog_ops imx2_wdt_ops = { +static const struct watchdog_ops imx2_wdt_ops = { .owner = THIS_MODULE, .start = imx2_wdt_start, .stop = imx2_wdt_stop, @@ -213,7 +213,7 @@ static struct watchdog_ops imx2_wdt_ops = { .set_timeout = imx2_wdt_set_timeout, }; -static struct regmap_config imx2_wdt_regmap_config = { +static const struct regmap_config imx2_wdt_regmap_config = { .reg_bits = 16, .reg_stride = 2, .val_bits = 16, From f83918fb5cb0fd257fd05d588e0c0b3472ef18b0 Mon Sep 17 00:00:00 2001 From: Paolo Teti Date: Sun, 19 Oct 2014 21:39:33 +0200 Subject: [PATCH 344/360] watchdog: it87_wdt: add IT8783 ID IT8783 watchdog works as in IT872x Tested on Adlink cPCI-6520 boards Signed-off-by: Paolo Teti Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/it87_wdt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 0b93739c0106b1..e54839b12650a5 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -12,8 +12,8 @@ * http://www.ite.com.tw/ * * Support of the watchdog timers, which are available on - * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 - * and IT8728. + * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, + * IT8728 and IT8783. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -87,6 +87,7 @@ #define IT8721_ID 0x8721 #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ #define IT8728_ID 0x8728 +#define IT8783_ID 0x8783 /* GPIO Configuration Registers LDN=0x07 */ #define WDTCTRL 0x71 @@ -633,6 +634,7 @@ static int __init it87_wdt_init(void) case IT8720_ID: case IT8721_ID: case IT8728_ID: + case IT8783_ID: max_units = 65535; try_gameport = 0; break; From b91b5be5ba92f2bc8018a900239cd07150639b5b Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 22 Oct 2014 20:24:35 +0900 Subject: [PATCH 345/360] watchdog: hpwdt: Fix initialization message in hpwdt.c allow_kdump was enabled as default since following commit. commit a089361cf5f1d6a5295aa5385238bd044998e1e9, watchdog: hpwdt: Unregister NMI events on exit. But the initialization message was not modified. So it still shows HP Watchdog Timer Driver: NMI decoding initialized, allow kernel dump: ON (default = 0/OFF) <= This "default = 0/OFF" message may confuse users. Fix it as "default = 1/ON". Signed-off-by: Masanari Iida Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/hpwdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 75d2243b94f51c..ada3e44f993284 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -745,7 +745,7 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) dev_info(&dev->dev, "HP Watchdog Timer Driver: NMI decoding initialized" - ", allow kernel dump: %s (default = 0/OFF)\n", + ", allow kernel dump: %s (default = 1/ON)\n", (allow_kdump == 0) ? "OFF" : "ON"); return 0; From a6f8f81ec77ba5e5f0b3249dfd3fc554ac5db117 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 16 Oct 2014 22:01:05 +0200 Subject: [PATCH 346/360] watchdog: rt2880_wdt: minor clean up Replace device_reset() with devm_reset_control_get() + reset_control_deassert(). Make use of watchdog_init_timeout() instead of setting the timeout manually. Signed-off-by: John Crispin Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rt2880_wdt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c index 11aad5b7aafe8f..a6f7e2e29bebbd 100644 --- a/drivers/watchdog/rt2880_wdt.c +++ b/drivers/watchdog/rt2880_wdt.c @@ -45,6 +45,7 @@ static struct clk *rt288x_wdt_clk; static unsigned long rt288x_wdt_freq; static void __iomem *rt288x_wdt_base; +static struct reset_control *rt288x_wdt_reset; static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); @@ -151,16 +152,18 @@ static int rt288x_wdt_probe(struct platform_device *pdev) if (IS_ERR(rt288x_wdt_clk)) return PTR_ERR(rt288x_wdt_clk); - device_reset(&pdev->dev); + rt288x_wdt_reset = devm_reset_control_get(&pdev->dev, NULL); + if (!IS_ERR(rt288x_wdt_reset)) + reset_control_deassert(rt288x_wdt_reset); rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; rt288x_wdt_dev.dev = &pdev->dev; rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); - rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); - rt288x_wdt_dev.timeout = rt288x_wdt_dev.max_timeout; + watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout, + &pdev->dev); watchdog_set_nowayout(&rt288x_wdt_dev, nowayout); ret = watchdog_register_device(&rt288x_wdt_dev); From fb1cbeaeed0f41965ead2714bfc9c579188c6146 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 14 Oct 2014 12:25:19 -0700 Subject: [PATCH 347/360] watchdog: Fix omap watchdogs to enable the magic close bit This allows testing the watchdog easily with distros just by doing pkill -9 watchdog. Reported-by: Thomas Dziedzic Signed-off-by: Tony Lindgren Acked-by: Aaro Koskinen Reviewed-by: Felipe Balbi Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/omap_wdt.c | 2 +- drivers/watchdog/retu_wdt.c | 2 +- drivers/watchdog/twl4030_wdt.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 9f2709db61ca92..1e6be9e4057798 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -189,7 +189,7 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog, } static const struct watchdog_info omap_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "OMAP Watchdog", }; diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c index a7a0695971e468..b7c68e275aeb35 100644 --- a/drivers/watchdog/retu_wdt.c +++ b/drivers/watchdog/retu_wdt.c @@ -94,7 +94,7 @@ static int retu_wdt_set_timeout(struct watchdog_device *wdog, } static const struct watchdog_info retu_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Retu watchdog", }; diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 12c15903d0989d..2c1db6fa9a2724 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c @@ -57,7 +57,7 @@ static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, } static const struct watchdog_info twl4030_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "TWL4030 Watchdog", }; From a44a45536f7bc2a5349cd44ee5d8cccd9aae0612 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Tue, 13 Jan 2015 13:28:55 +0100 Subject: [PATCH 348/360] watchdog: Add driver for Mediatek watchdog This patch adds a driver for the Mediatek SoC integrated watchdog. This driver supports watchdog and software reset for mt65xx and mt81xx SoCs. Signed-off-by: Matthias Brugger Tested-by: Eddie Huang Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 10 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/mtk_wdt.c | 251 +++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 drivers/watchdog/mtk_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0a1396b70b95a7..4fd4a13cb2615e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -505,6 +505,16 @@ config MESON_WATCHDOG To compile this driver as a module, choose M here: the module will be called meson_wdt. +config MEDIATEK_WATCHDOG + tristate "Mediatek SoCs watchdog support" + depends on ARCH_MEDIATEK + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Mediatek SoCs. + To compile this driver as a module, choose M here: the + module will be called mtk_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d4dfbb4fd01a6f..5c19294d1c3015 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o +obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c new file mode 100644 index 00000000000000..a87f6df6e85f32 --- /dev/null +++ b/drivers/watchdog/mtk_wdt.c @@ -0,0 +1,251 @@ +/* + * Mediatek Watchdog Driver + * + * Copyright (C) 2014 Matthias Brugger + * + * Matthias Brugger + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based on sunxi_wdt.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WDT_MAX_TIMEOUT 31 +#define WDT_MIN_TIMEOUT 1 +#define WDT_LENGTH_TIMEOUT(n) ((n) << 5) + +#define WDT_LENGTH 0x04 +#define WDT_LENGTH_KEY 0x8 + +#define WDT_RST 0x08 +#define WDT_RST_RELOAD 0x1971 + +#define WDT_MODE 0x00 +#define WDT_MODE_EN (1 << 0) +#define WDT_MODE_EXT_POL_LOW (0 << 1) +#define WDT_MODE_EXT_POL_HIGH (1 << 1) +#define WDT_MODE_EXRST_EN (1 << 2) +#define WDT_MODE_IRQ_EN (1 << 3) +#define WDT_MODE_AUTO_START (1 << 4) +#define WDT_MODE_DUAL_EN (1 << 6) +#define WDT_MODE_KEY 0x22000000 + +#define WDT_SWRST 0x14 +#define WDT_SWRST_KEY 0x1209 + +#define DRV_NAME "mtk-wdt" +#define DRV_VERSION "1.0" + +static bool nowayout = WATCHDOG_NOWAYOUT; +static unsigned int timeout = WDT_MAX_TIMEOUT; + +struct mtk_wdt_dev { + struct watchdog_device wdt_dev; + void __iomem *wdt_base; + struct notifier_block restart_handler; +}; + +static int mtk_reset_handler(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct mtk_wdt_dev *mtk_wdt; + void __iomem *wdt_base; + + mtk_wdt = container_of(this, struct mtk_wdt_dev, restart_handler); + wdt_base = mtk_wdt->wdt_base; + + while (1) { + writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST); + mdelay(5); + } + + return NOTIFY_DONE; +} + +static int mtk_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = mtk_wdt->wdt_base; + + iowrite32(WDT_RST_RELOAD, wdt_base + WDT_RST); + + return 0; +} + +static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = mtk_wdt->wdt_base; + u32 reg; + + wdt_dev->timeout = timeout; + + /* + * One bit is the value of 512 ticks + * The clock has 32 KHz + */ + reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY; + iowrite32(reg, wdt_base + WDT_LENGTH); + + mtk_wdt_ping(wdt_dev); + + return 0; +} + +static int mtk_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = mtk_wdt->wdt_base; + u32 reg; + + reg = readl(wdt_base + WDT_MODE); + reg &= ~WDT_MODE_EN; + iowrite32(reg, wdt_base + WDT_MODE); + + return 0; +} + +static int mtk_wdt_start(struct watchdog_device *wdt_dev) +{ + u32 reg; + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base = mtk_wdt->wdt_base; + u32 ret; + + ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); + if (ret < 0) + return ret; + + reg = ioread32(wdt_base + WDT_MODE); + reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); + reg |= (WDT_MODE_EN | WDT_MODE_KEY); + iowrite32(reg, wdt_base + WDT_MODE); + + return 0; +} + +static const struct watchdog_info mtk_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops mtk_wdt_ops = { + .owner = THIS_MODULE, + .start = mtk_wdt_start, + .stop = mtk_wdt_stop, + .ping = mtk_wdt_ping, + .set_timeout = mtk_wdt_set_timeout, +}; + +static int mtk_wdt_probe(struct platform_device *pdev) +{ + struct mtk_wdt_dev *mtk_wdt; + struct resource *res; + int err; + + mtk_wdt = devm_kzalloc(&pdev->dev, sizeof(*mtk_wdt), GFP_KERNEL); + if (!mtk_wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, mtk_wdt); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mtk_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mtk_wdt->wdt_base)) + return PTR_ERR(mtk_wdt->wdt_base); + + mtk_wdt->wdt_dev.info = &mtk_wdt_info; + mtk_wdt->wdt_dev.ops = &mtk_wdt_ops; + mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; + mtk_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT; + mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; + mtk_wdt->wdt_dev.parent = &pdev->dev; + + watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, &pdev->dev); + watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout); + + watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); + + mtk_wdt_stop(&mtk_wdt->wdt_dev); + + err = watchdog_register_device(&mtk_wdt->wdt_dev); + if (unlikely(err)) + return err; + + mtk_wdt->restart_handler.notifier_call = mtk_reset_handler; + mtk_wdt->restart_handler.priority = 128; + err = register_restart_handler(&mtk_wdt->restart_handler); + if (err) + dev_warn(&pdev->dev, + "cannot register restart handler (err=%d)\n", err); + + dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", + mtk_wdt->wdt_dev.timeout, nowayout); + + return 0; +} + +static int mtk_wdt_remove(struct platform_device *pdev) +{ + struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); + + unregister_restart_handler(&mtk_wdt->restart_handler); + + watchdog_unregister_device(&mtk_wdt->wdt_dev); + + return 0; +} + +static const struct of_device_id mtk_wdt_dt_ids[] = { + { .compatible = "mediatek,mt6589-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); + +static struct platform_driver mtk_wdt_driver = { + .probe = mtk_wdt_probe, + .remove = mtk_wdt_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = mtk_wdt_dt_ids, + }, +}; + +module_platform_driver(mtk_wdt_driver); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matthias Brugger "); +MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver"); +MODULE_VERSION(DRV_VERSION); From 9a4c88016458424e53084ed3c26bfbae8cd8af22 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Tue, 13 Jan 2015 13:28:56 +0100 Subject: [PATCH 349/360] ARM: mediatek: dts: Add bindings for watchdog Signed-off-by: Matthias Brugger Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../devicetree/bindings/watchdog/mtk-wdt.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/mtk-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt new file mode 100644 index 00000000000000..af9eb5b8a2534a --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt @@ -0,0 +1,13 @@ +Mediatek SoCs Watchdog timer + +Required properties: + +- compatible : should be "mediatek,mt6589-wdt" +- reg : Specifies base physical address and size of the registers. + +Example: + +wdt: watchdog@010000000 { + compatible = "mediatek,mt6589-wdt"; + reg = <0x10000000 0x18>; +}; From 396f163ceba3ac2829e3076764efcfb10797293c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 29 Jan 2015 15:26:05 +0100 Subject: [PATCH 350/360] watchdog: da9063: Add restart handler support Register a restart handler for the da9063 watchdog. System restart is triggered by sending the shutdown command to the PMIC. As more-suitable restart handlers may exist, the priority of the watchdog restart handler is set to 128. The actual restart method was inspired by a platform-specific patch from the BSP by Hisashi Nakamura . Signed-off-by: Geert Uytterhoeven Acked-by: Steve Twiss Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9063_wdt.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 2cd6b2c2dd2a69..e2fe2ebdebd4d6 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include /* @@ -38,6 +39,7 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; struct da9063_watchdog { struct da9063 *da9063; struct watchdog_device wdtdev; + struct notifier_block restart_handler; }; static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) @@ -119,6 +121,23 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd, return ret; } +static int da9063_wdt_restart_handler(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct da9063_watchdog *wdt = container_of(this, + struct da9063_watchdog, + restart_handler); + int ret; + + ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, + DA9063_SHUTDOWN); + if (ret) + dev_alert(wdt->da9063->dev, "Failed to shutdown (err = %d)\n", + ret); + + return NOTIFY_DONE; +} + static const struct watchdog_info da9063_watchdog_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "DA9063 Watchdog", @@ -163,14 +182,25 @@ static int da9063_wdt_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, wdt); ret = watchdog_register_device(&wdt->wdtdev); + if (ret) + return ret; - return ret; + wdt->restart_handler.notifier_call = da9063_wdt_restart_handler; + wdt->restart_handler.priority = 128; + ret = register_restart_handler(&wdt->restart_handler); + if (ret) + dev_err(wdt->da9063->dev, + "Failed to register restart handler (err = %d)\n", ret); + + return 0; } static int da9063_wdt_remove(struct platform_device *pdev) { struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); + unregister_restart_handler(&wdt->restart_handler); + watchdog_unregister_device(&wdt->wdtdev); return 0; From ba804a9510df555c42c2be6c340960879afe39d2 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Wed, 14 Jan 2015 07:28:29 +0100 Subject: [PATCH 351/360] watchdog: gpio_wdt: Add "always_running" feature to GPIO watchdog On some chips, like the TPS386000, the trigger cannot be disabled and the CPU must keep toggling the line at all times. Add a switch "always_running" to keep toggling the GPIO line regardless of the state of the soft part of the watchdog. The "armed" member keeps track of whether a timeout must also cause a reset. Signed-off-by: Mike Looijmans Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../devicetree/bindings/watchdog/gpio-wdt.txt | 5 +++ drivers/watchdog/gpio_wdt.c | 37 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/watchdog/gpio-wdt.txt b/Documentation/devicetree/bindings/watchdog/gpio-wdt.txt index 37afec194949a3..198794963786ba 100644 --- a/Documentation/devicetree/bindings/watchdog/gpio-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/gpio-wdt.txt @@ -13,6 +13,11 @@ Required Properties: by the GPIO flags. - hw_margin_ms: Maximum time to reset watchdog circuit (milliseconds). +Optional Properties: +- always-running: If the watchdog timer cannot be disabled, add this flag to + have the driver keep toggling the signal without a client. It will only cease + to toggle the signal when the device is open and the timeout elapsed. + Example: watchdog: watchdog { /* ADM706 */ diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index bbdb19b4533268..cbc313d37c59f1 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -31,6 +31,8 @@ struct gpio_wdt_priv { int gpio; bool active_low; bool state; + bool always_running; + bool armed; unsigned int hw_algo; unsigned int hw_margin; unsigned long last_jiffies; @@ -48,14 +50,20 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv) gpio_direction_input(priv->gpio); } -static int gpio_wdt_start(struct watchdog_device *wdd) +static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv) { - struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - priv->state = priv->active_low; gpio_direction_output(priv->gpio, priv->state); priv->last_jiffies = jiffies; mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin); +} + +static int gpio_wdt_start(struct watchdog_device *wdd) +{ + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + gpio_wdt_start_impl(priv); + priv->armed = true; return 0; } @@ -64,8 +72,11 @@ static int gpio_wdt_stop(struct watchdog_device *wdd) { struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - mod_timer(&priv->timer, 0); - gpio_wdt_disable(priv); + priv->armed = false; + if (!priv->always_running) { + mod_timer(&priv->timer, 0); + gpio_wdt_disable(priv); + } return 0; } @@ -91,8 +102,8 @@ static void gpio_wdt_hwping(unsigned long data) struct watchdog_device *wdd = (struct watchdog_device *)data; struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - if (time_after(jiffies, priv->last_jiffies + - msecs_to_jiffies(wdd->timeout * 1000))) { + if (priv->armed && time_after(jiffies, priv->last_jiffies + + msecs_to_jiffies(wdd->timeout * 1000))) { dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); return; } @@ -197,6 +208,9 @@ static int gpio_wdt_probe(struct platform_device *pdev) /* Use safe value (1/2 of real timeout) */ priv->hw_margin = msecs_to_jiffies(hw_margin / 2); + priv->always_running = of_property_read_bool(pdev->dev.of_node, + "always-running"); + watchdog_set_drvdata(&priv->wdd, priv); priv->wdd.info = &gpio_wdt_ident; @@ -216,8 +230,15 @@ static int gpio_wdt_probe(struct platform_device *pdev) priv->notifier.notifier_call = gpio_wdt_notify_sys; ret = register_reboot_notifier(&priv->notifier); if (ret) - watchdog_unregister_device(&priv->wdd); + goto error_unregister; + if (priv->always_running) + gpio_wdt_start_impl(priv); + + return 0; + +error_unregister: + watchdog_unregister_device(&priv->wdd); return ret; } From 1cc7495c60879eeeda52385a70c99c4cbaace7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 25 Jan 2015 11:40:57 +0100 Subject: [PATCH 352/360] watchdog: bcm47xx_wdt.c: add restart handler support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like in case of other watchdog drivers, use the new kernel core API to provide restart support. Signed-off-by: Rafał Miłecki Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm47xx_wdt.c | 21 ++++++++++++++++++++- include/linux/bcm47xx_wdt.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index 9816485f682522..b28a072abf78fb 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -169,6 +169,17 @@ static int bcm47xx_wdt_notify_sys(struct notifier_block *this, return NOTIFY_DONE; } +static int bcm47xx_wdt_restart(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct bcm47xx_wdt *wdt; + + wdt = container_of(this, struct bcm47xx_wdt, restart_handler); + wdt->timer_set(wdt, 1); + + return NOTIFY_DONE; +} + static struct watchdog_ops bcm47xx_wdt_soft_ops = { .owner = THIS_MODULE, .start = bcm47xx_wdt_soft_start, @@ -209,15 +220,23 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev) if (ret) goto err_timer; - ret = watchdog_register_device(&wdt->wdd); + wdt->restart_handler.notifier_call = &bcm47xx_wdt_restart; + wdt->restart_handler.priority = 64; + ret = register_restart_handler(&wdt->restart_handler); if (ret) goto err_notifier; + ret = watchdog_register_device(&wdt->wdd); + if (ret) + goto err_handler; + dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n", timeout, nowayout ? ", nowayout" : "", soft ? ", Software Timer" : ""); return 0; +err_handler: + unregister_restart_handler(&wdt->restart_handler); err_notifier: unregister_reboot_notifier(&wdt->notifier); err_timer: diff --git a/include/linux/bcm47xx_wdt.h b/include/linux/bcm47xx_wdt.h index b708786d4cbf6d..5582c211f594e0 100644 --- a/include/linux/bcm47xx_wdt.h +++ b/include/linux/bcm47xx_wdt.h @@ -16,6 +16,7 @@ struct bcm47xx_wdt { struct watchdog_device wdd; struct notifier_block notifier; + struct notifier_block restart_handler; struct timer_list soft_timer; atomic_t soft_ticks; From a77841d59eb54ceb7b97b5e23053cd205e3a4c00 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 26 Jan 2015 08:53:56 -0800 Subject: [PATCH 353/360] watchdog: w83627hf_wdt: Add support for NCT6791 and NCT6792 The watchdog functionality in both chips is almost identical to NCT6779. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 2 ++ drivers/watchdog/w83627hf_wdt.c | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4fd4a13cb2615e..60a2bf400082e1 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1015,6 +1015,8 @@ config W83627HF_WDT NCT6775 NCT6776 NCT6779 + NCT6791 + NCT6792 This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 7165704a3e33e2..5824e25eebbbab 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -50,7 +50,7 @@ static int cr_wdt_control; /* WDT control register */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, - w83667hg_b, nct6775, nct6776, nct6779 }; + w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -95,6 +95,8 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6775_ID 0xb4 #define NCT6776_ID 0xc3 #define NCT6779_ID 0xc5 +#define NCT6791_ID 0xc8 +#define NCT6792_ID 0xc9 #define W83627HF_WDT_TIMEOUT 0xf6 #define W83697HF_WDT_TIMEOUT 0xf4 @@ -195,6 +197,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6775: case nct6776: case nct6779: + case nct6791: + case nct6792: /* * These chips have a fixed WDTO# output pin (W83627UHG), * or support more than one WDTO# output pin. @@ -395,6 +399,12 @@ static int wdt_find(int addr) case NCT6779_ID: ret = nct6779; break; + case NCT6791_ID: + ret = nct6791; + break; + case NCT6792_ID: + ret = nct6792; + break; case 0xff: ret = -ENODEV; break; @@ -428,6 +438,8 @@ static int __init wdt_init(void) "NCT6775", "NCT6776", "NCT6779", + "NCT6791", + "NCT6792", }; wdt_io = 0x2e; From a00850107eb050bf6427a8f3a0445bce9441b5df Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 27 Jan 2015 14:25:16 -0800 Subject: [PATCH 354/360] watchdog: dw_wdt: pat the watchdog before enabling it On some dw_wdt implementations the "top" register may be initted to 0 at bootup. In such a case, each "pat" of the watchdog will reset the timer to 0xffff. That's pretty short. The input clock of the wdt can be any of a wide range of values. On an rk3288 system, I've seen the wdt clock be 24.75 MHz. That means each tick is ~40ns and we'll count to 0xffff in ~2.6ms. Because of the above two facts, it's a really good idea to pat the watchdog after initting the "top" register properly and before enabling the watchdog. If you don't then there's no way we'll get the next heartbeat in time. Jisheng Zhang fixed this problem on some dw_wdt versions by using the TOP_INIT feature. However, the dw_wdt on rk3288 doesn't have TOP_INIT so it's a good idea to also pat the watchdog manually. Signed-off-by: Doug Anderson Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index b34a2e4e4e43c3..3dde6de117fa18 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -96,6 +96,12 @@ static inline void dw_wdt_set_next_heartbeat(void) dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ; } +static void dw_wdt_keepalive(void) +{ + writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + + WDOG_COUNTER_RESTART_REG_OFFSET); +} + static int dw_wdt_set_top(unsigned top_s) { int i, top_val = DW_WDT_MAX_TOP; @@ -110,21 +116,27 @@ static int dw_wdt_set_top(unsigned top_s) break; } - /* Set the new value in the watchdog. */ + /* + * Set the new value in the watchdog. Some versions of dw_wdt + * have have TOPINIT in the TIMEOUT_RANGE register (as per + * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1). On those we + * effectively get a pat of the watchdog right here. + */ writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + /* + * Add an explicit pat to handle versions of the watchdog that + * don't have TOPINIT. This won't hurt on versions that have + * it. + */ + dw_wdt_keepalive(); + dw_wdt_set_next_heartbeat(); return dw_wdt_top_in_seconds(top_val); } -static void dw_wdt_keepalive(void) -{ - writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + - WDOG_COUNTER_RESTART_REG_OFFSET); -} - static int dw_wdt_restart_handle(struct notifier_block *this, unsigned long mode, void *cmd) { From b5ade9bc8dca839fb06cd2788046cfe923c06980 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 27 Jan 2015 14:25:17 -0800 Subject: [PATCH 355/360] watchdog: dw_wdt: Try to get a 30 second watchdog by default The dw_wdt_set_top() function takes in a value in seconds. In dw_wdt_open() we were calling it with a value that's supposed to represent the maximum value programmed into the "top" register with a comment saying that we were trying to set the watchdog to its maximum value. Instead we ended up setting the watchdog to ~15 seconds. Let's fix this. However, setting things to the "max" gives me an 86 second watchdog in the system I'm looking at. 86 seconds feels a little too long. We'll explicitly choose 30 seconds as a more reasonable value. NOTE: Ideally this driver should be transitioned to be a real watchdog driver. Then we could use "watchdog_init_timeout" and let the timeout be specified in a number of ways (device tree, module parameter, etc). This patch should be considered a bit of a stopgap solution. Signed-off-by: Doug Anderson Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 3dde6de117fa18..d0bb9499d12caa 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -51,6 +51,8 @@ /* The maximum TOP (timeout period) value that can be set in the watchdog. */ #define DW_WDT_MAX_TOP 15 +#define DW_WDT_DEFAULT_SECONDS 30 + static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " @@ -179,9 +181,9 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) if (!dw_wdt_is_enabled()) { /* * The watchdog is not currently enabled. Set the timeout to - * the maximum and then start it. + * something reasonable and then start it. */ - dw_wdt_set_top(DW_WDT_MAX_TOP); + dw_wdt_set_top(DW_WDT_DEFAULT_SECONDS); writel(WDOG_CONTROL_REG_WDT_EN_MASK, dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); } From 73af15205be6b5977d35619208fe97621903d4de Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Tue, 3 Feb 2015 10:25:47 +0000 Subject: [PATCH 356/360] dt: watchdog: Add DT binding documentation for jz4740 watchdog timer Add binding for jz4740 watchdog timer. It is a simple watchdog timer. Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../bindings/watchdog/ingenic,jz4740-wdt.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt b/Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt new file mode 100644 index 00000000000000..e27763ef00497d --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt @@ -0,0 +1,12 @@ +Ingenic Watchdog Timer (WDT) Controller for JZ4740 + +Required properties: +compatible: "ingenic,jz4740-watchdog" +reg: Register address and length for watchdog registers + +Example: + +watchdog: jz4740-watchdog@0x10002000 { + compatible = "ingenic,jz4740-watchdog"; + reg = <0x10002000 0x100>; +}; From 6b96c72279cd73c1a03e97265548ce067128203a Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Tue, 3 Feb 2015 10:25:48 +0000 Subject: [PATCH 357/360] watchdog: jz4740: Add DT support Add DT support to the jz4740 driver. Simple of_match_ptr. No other modification for probe needed Signed-off-by: Zubair Lutfullah Kakakhel Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/jz4740_wdt.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index 18e41afa4da38c..4c2cc09c0c5780 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -142,6 +143,14 @@ static const struct watchdog_ops jz4740_wdt_ops = { .set_timeout = jz4740_wdt_set_timeout, }; +#ifdef CONFIG_OF +static const struct of_device_id jz4740_wdt_of_matches[] = { + { .compatible = "ingenic,jz4740-watchdog", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches) +#endif + static int jz4740_wdt_probe(struct platform_device *pdev) { struct jz4740_wdt_drvdata *drvdata; @@ -211,6 +220,7 @@ static struct platform_driver jz4740_wdt_driver = { .remove = jz4740_wdt_remove, .driver = { .name = "jz4740-wdt", + .of_match_table = of_match_ptr(jz4740_wdt_of_matches), }, }; From 94613431619b555ac1299634efabde2ffd0eb2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 7 Feb 2015 18:04:10 +0100 Subject: [PATCH 358/360] watchdog: bcm47xx_wdt.c: allow enabling on BCM5301X arch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCM5301X (ARCH_BCM_5301X) is a new Broadcom architecture using the same SoC bus driver (bcma) as BCM47XX but based on ARM instead of MIPS. Signed-off-by: Rafał Miłecki Reviewed-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 60a2bf400082e1..16f20235099768 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1113,7 +1113,7 @@ config ATH79_WDT config BCM47XX_WDT tristate "Broadcom BCM47xx Watchdog Timer" - depends on BCM47XX + depends on BCM47XX || ARCH_BCM_5301X select WATCHDOG_CORE help Hardware driver for the Broadcom BCM47xx Watchdog Timer. From 2e2f756f81edd7c3ba6ed384385ae1d6491652eb Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Feb 2015 17:08:23 -0500 Subject: [PATCH 359/360] locks: fix list insertion when lock is split in two In the case where we're splitting a lock in two, the current code the new "left" lock in the incorrect spot. It's inserted just before "right" when it should instead be inserted just before the new lock. When we add a new lock, set "fl" to that value so that we can add "left" before it. Reported-by: Al Viro Signed-off-by: Jeff Layton --- fs/locks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/locks.c b/fs/locks.c index 90b652ad306f12..365c82e1b3a9a6 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1107,6 +1107,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str } locks_copy_lock(new_fl, request); locks_insert_lock_ctx(new_fl, &fl->fl_list); + fl = new_fl; new_fl = NULL; } if (right) { From b65af27ad89de60ca55721f9368b18d49ba1f269 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 18 Feb 2015 08:40:29 +0100 Subject: [PATCH 360/360] pwm: tegra: Use NSEC_PER_SEC Instead of using the literal value for the number of nanoseconds per second, use the macro instead to increase readability. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 5b97cae5423a0f..cabd7d8e05cc0f 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -87,7 +87,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * cycles at the PWM clock rate will take period_ns nanoseconds. */ rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH; - hz = 1000000000ul / period_ns; + hz = NSEC_PER_SEC / period_ns; rate = (rate + (hz / 2)) / hz;