From 8df4053f0532df8fe47d0434af51676b0fa65491 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:56 +0300 Subject: [PATCH 001/101] platform_data: edma: Be precise with the paRAM struct The edmacc_param struct should follow the layout of the paRAM area in the HW. Be explicit on the size of the fields (u32) and also mark the struct as packed to avoid any padding on non 32bit architectures. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- include/linux/platform_data/edma.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index f50821cb64be..923f8a3e4ce0 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -43,15 +43,15 @@ /* PaRAM slots are laid out like this */ struct edmacc_param { - unsigned int opt; - unsigned int src; - unsigned int a_b_cnt; - unsigned int dst; - unsigned int src_dst_bidx; - unsigned int link_bcntrld; - unsigned int src_dst_cidx; - unsigned int ccnt; -}; + u32 opt; + u32 src; + u32 a_b_cnt; + u32 dst; + u32 src_dst_bidx; + u32 link_bcntrld; + u32 src_dst_cidx; + u32 ccnt; +} __packed; /* fields in edmacc_param.opt */ #define SAM BIT(0) From 7cf2af90cd515bf27f1b7183f3d6f91b151990ed Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:57 +0300 Subject: [PATCH 002/101] arm: common: edma: Save the number of event queues/TCs For later use save the number of queues available for the CC. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 41bca32409fc..999266bf69b9 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1768,6 +1768,9 @@ static int edma_probe(struct platform_device *pdev) map_queue_tc(j, queue_tc_mapping[i][0], queue_tc_mapping[i][1]); + /* Save the number of TCs */ + edma_cc[j]->num_tc = i; + /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, From b2b617de04081931cc10fd862b76b9fcaa9489e2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:58 +0300 Subject: [PATCH 003/101] dmaengine: edma: Correct the handling of src/dst_maxburst == 0 When clients asks for maxburst = 0 it is basically the same case as if they were asking for maxburst = 1 since in both case ASYNC need to be used and the eDMA is expected to write/read one word per DMA request. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 926360c2db6a..d7649d229755 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -287,6 +287,10 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, int absync; acnt = dev_width; + + /* src/dst_maxburst == 0 is the same case as src/dst_maxburst == 1 */ + if (!burst) + burst = 1; /* * If the maxburst is equal to the fifo width, use * A-synced transfers. This allows for large contiguous From 72c7b67affc3ee63fc3df6e4a28d452a4e82e332 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:59 +0300 Subject: [PATCH 004/101] dmaengine: edma: Add support for DMA_PAUSE/RESUME operation Pause/Resume can be used by the audio stack when the stream is paused/resumed The edma platform code has support for this and the legacy audio stack used this. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index d7649d229755..35db3f282bdb 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -242,6 +242,26 @@ static int edma_slave_config(struct edma_chan *echan, return 0; } +static int edma_dma_pause(struct edma_chan *echan) +{ + /* Pause/Resume only allowed with cyclic mode */ + if (!echan->edesc->cyclic) + return -EINVAL; + + edma_pause(echan->ch_num); + return 0; +} + +static int edma_dma_resume(struct edma_chan *echan) +{ + /* Pause/Resume only allowed with cyclic mode */ + if (!echan->edesc->cyclic) + return -EINVAL; + + edma_resume(echan->ch_num); + return 0; +} + static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -257,6 +277,14 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, 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; } From 232b223d8281d33820053bb711f91864b8612d8e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:00 +0300 Subject: [PATCH 005/101] dmaengine: edma: Set DMA_CYCLIC capability flag Indicate that the edma dmaengine driver has support for cyclic mode. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 1 + drivers/dma/edma.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 999266bf69b9..0b37f7734d0f 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1574,6 +1574,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, return ERR_PTR(ret); dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap); + dma_cap_set(DMA_CYCLIC, edma_filter_info.dma_cap); of_dma_controller_register(dev->of_node, of_dma_simple_xlate, &edma_filter_info); diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 35db3f282bdb..eef51644b69f 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -893,6 +893,7 @@ static int edma_probe(struct platform_device *pdev) dma_cap_zero(ecc->dma_slave.cap_mask); dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask); + dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask); edma_dma_init(ecc, &ecc->dma_slave, &pdev->dev); From 83bb3126cc63620fe23c2a0539800ab7f8cf7ba4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:02 +0300 Subject: [PATCH 006/101] dmaengine: edma: Reduce debug print verbosity for non verbose debugging Do not print the paRAM information when verbose debugging is not asked and also reduce the number of lines printed in edma_prep_dma_cyclic() Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index eef51644b69f..5c24b6024278 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -141,7 +141,7 @@ static void edma_execute(struct edma_chan *echan) for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j]); - dev_dbg(echan->vchan.chan.device->dev, + dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n" @@ -562,9 +562,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; - dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots); - dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len); - dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len); + dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", + __func__, echan->ch_num, nslots, period_len, buf_len); for (i = 0; i < nslots; i++) { /* Allocate a PaRAM slot, if needed */ @@ -598,8 +597,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( else src_addr += period_len; - dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i); - dev_dbg(dev, + dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i); + dev_vdbg(dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n" From 2c88ee6b6b9a715f2caba87b7c993ca485f68c0d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:01 +0300 Subject: [PATCH 007/101] dmaengine: edma: Implement device_slave_caps callback With the callback implemented omap-dma can provide information to client drivers regarding to supported address widths, directions, residue granularity, etc. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 5c24b6024278..ce230f8f6d98 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -853,6 +853,23 @@ static void __init edma_chan_init(struct edma_cc *ecc, } } +#define EDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_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->dstn_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_DESCRIPTOR; + + return 0; +} + static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, struct device *dev) { @@ -863,6 +880,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_issue_pending = edma_issue_pending; dma->device_tx_status = edma_tx_status; dma->device_control = edma_control; + dma->device_slave_caps = edma_dma_device_slave_caps; dma->dev = dev; INIT_LIST_HEAD(&dma->channels); From c594c8912bd6d0dfa45cc08b58a60dcbb8010ecb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:03 +0300 Subject: [PATCH 008/101] dmaengine: edma: Prefix debug prints where the text were identical in prep callbacks prep_slave_sg and prep_dma_cyclic callbacks have mostly same failure cases with the same texts printed in case we hit them. It helps when debugging if we know exactly which callback generated the errors. At the same time change the debug level for descriptor allocation failure from dbg to err since all other error cases are dev_err and this failure is similarly fatal as the other ones. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index ce230f8f6d98..571f3b1ef2c2 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -438,14 +438,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { - dev_err(dev, "Undefined slave buswidth\n"); + dev_err(dev, "%s: Undefined slave buswidth\n", __func__); return NULL; } edesc = kzalloc(sizeof(*edesc) + sg_len * sizeof(edesc->pset[0]), GFP_ATOMIC); if (!edesc) { - dev_dbg(dev, "Failed to allocate a descriptor\n"); + dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__); return NULL; } @@ -461,7 +461,8 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( EDMA_SLOT_ANY); if (echan->slot[i] < 0) { kfree(edesc); - dev_err(dev, "Failed to allocate slot\n"); + dev_err(dev, "%s: Failed to allocate slot\n", + __func__); return NULL; } } @@ -530,7 +531,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( } if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { - dev_err(dev, "Undefined slave buswidth\n"); + dev_err(dev, "%s: Undefined slave buswidth\n", __func__); return NULL; } @@ -555,7 +556,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc = kzalloc(sizeof(*edesc) + nslots * sizeof(edesc->pset[0]), GFP_ATOMIC); if (!edesc) { - dev_dbg(dev, "Failed to allocate a descriptor\n"); + dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__); return NULL; } @@ -573,7 +574,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( EDMA_SLOT_ANY); if (echan->slot[i] < 0) { kfree(edesc); - dev_err(dev, "Failed to allocate slot\n"); + dev_err(dev, "%s: Failed to allocate slot\n", + __func__); return NULL; } } From e6fad592b0e8a6205f23a3e55b2e682e4f36d32f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:05 +0300 Subject: [PATCH 009/101] dmaengine: edma: Print the direction value as well when it is not supported In case of not supported direction it is better to print the direction also. It is unlikely, but in such an event it helps with the debugging. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 571f3b1ef2c2..ea04b2192822 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -433,7 +433,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else { - dev_err(dev, "%s: bad direction?\n", __func__); + dev_err(dev, "%s: bad direction: %d\n", __func__, direction); return NULL; } @@ -526,7 +526,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else { - dev_err(dev, "%s: bad direction?\n", __func__); + dev_err(dev, "%s: bad direction: %d\n", __func__, direction); return NULL; } From 8cc3e30bea9a90f9ab7a1bc4612792c40ad7ae95 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Fri, 18 Apr 2014 21:50:33 -0500 Subject: [PATCH 010/101] dmaengine: edma: Add DMA memcpy support We add DMA memcpy support to EDMA driver. Successful tests performed using dmatest kernel module. Copy alignment is set to DMA_SLAVE_BUSWIDTH_4_BYTES and users must ensure length is aligned so that copy is performed fully. Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index ea04b2192822..43f56a7d9d61 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -379,6 +379,11 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, src_cidx = 0; dst_bidx = acnt; dst_cidx = cidx; + } else if (direction == DMA_MEM_TO_MEM) { + src_bidx = acnt; + src_cidx = cidx; + dst_bidx = acnt; + dst_cidx = cidx; } else { dev_err(dev, "%s: direction not implemented yet\n", __func__); return -EINVAL; @@ -499,6 +504,44 @@ 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( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long tx_flags) +{ + int ret; + struct edma_desc *edesc; + struct device *dev = chan->device->dev; + struct edma_chan *echan = to_edma_chan(chan); + + if (unlikely(!echan || !len)) + return NULL; + + edesc = kzalloc(sizeof(*edesc) + sizeof(edesc->pset[0]), GFP_ATOMIC); + if (!edesc) { + dev_dbg(dev, "Failed to allocate a descriptor\n"); + return NULL; + } + + edesc->pset_nr = 1; + + ret = edma_config_pset(chan, &edesc->pset[0], src, dest, 1, + DMA_SLAVE_BUSWIDTH_4_BYTES, len, DMA_MEM_TO_MEM); + if (ret < 0) + return NULL; + + edesc->absync = ret; + + /* + * Enable intermediate transfer chaining to re-trigger channel + * on completion of every TR, and enable transfer-completion + * interrupt on completion of the whole transfer. + */ + edesc->pset[0].opt |= ITCCHEN; + edesc->pset[0].opt |= TCINTEN; + + return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); +} + static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, @@ -877,6 +920,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, { dma->device_prep_slave_sg = edma_prep_slave_sg; dma->device_prep_dma_cyclic = edma_prep_dma_cyclic; + dma->device_prep_dma_memcpy = edma_prep_dma_memcpy; dma->device_alloc_chan_resources = edma_alloc_chan_resources; dma->device_free_chan_resources = edma_free_chan_resources; dma->device_issue_pending = edma_issue_pending; @@ -885,6 +929,12 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_slave_caps = edma_dma_device_slave_caps; dma->dev = dev; + /* + * code using dma memcpy must make sure alignment of + * length is at dma->copy_align boundary. + */ + dma->copy_align = DMA_SLAVE_BUSWIDTH_4_BYTES; + INIT_LIST_HEAD(&dma->channels); } @@ -913,6 +963,7 @@ static int edma_probe(struct platform_device *pdev) dma_cap_zero(ecc->dma_slave.cap_mask); dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask); dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask); + dma_cap_set(DMA_MEMCPY, ecc->dma_slave.cap_mask); edma_dma_init(ecc, &ecc->dma_slave, &pdev->dev); From 406efb1a745c1dc512dc9c3c859e302e7b7f907e Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Thu, 17 Apr 2014 00:58:33 -0500 Subject: [PATCH 011/101] dmaengine: edma: No need save/restore interrupt flags during spin_lock in IRQ The vchan lock in edma_callback is acquired in hard interrupt context. As interrupts are already disabled, there's no point in save/restoring interrupt mask bit or cpsr flags. Get rid of flags local variable and use spin_lock instead of spin_lock_irqsave. Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 43f56a7d9d61..fa87fd52b0ad 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -682,7 +682,6 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) struct edma_chan *echan = data; struct device *dev = echan->vchan.chan.device->dev; struct edma_desc *edesc; - unsigned long flags; struct edmacc_param p; edesc = echan->edesc; @@ -693,7 +692,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) switch (ch_status) { case EDMA_DMA_COMPLETE: - spin_lock_irqsave(&echan->vchan.lock, flags); + spin_lock(&echan->vchan.lock); if (edesc) { if (edesc->cyclic) { @@ -709,11 +708,11 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) } } - spin_unlock_irqrestore(&echan->vchan.lock, flags); + spin_unlock(&echan->vchan.lock); break; case EDMA_DMA_CC_ERROR: - spin_lock_irqsave(&echan->vchan.lock, flags); + spin_lock(&echan->vchan.lock); edma_read_slot(EDMA_CHAN_SLOT(echan->slot[0]), &p); @@ -744,7 +743,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) edma_trigger_channel(echan->ch_num); } - spin_unlock_irqrestore(&echan->vchan.lock, flags); + spin_unlock(&echan->vchan.lock); break; default: From a39a493932dfc534cb761d6f95861a281e57c1e7 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Tue, 15 Apr 2014 17:06:44 -0500 Subject: [PATCH 012/101] reset: add driver for socfpga Add a reset-controller driver for the socfpga platform. The reset-controller has four banks with up to 32 entries all encapsulated in one module block. Signed-off-by: Steffen Trumtrar Acked-by: Philipp Zabel Signed-off-by: Dinh Nguyen --- Notes: Changes since v2: - remove superfluous ret in probe function - add Acked-by Changes since v1: - use BITS_PER_LONG everywhere instead of MAX_BANK_WIDTH - print pdev->dev.of_node->full_name on error - use proper IS_ERR/PTR_ERR --- drivers/reset/Makefile | 1 + drivers/reset/reset-socfpga.c | 146 ++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 drivers/reset/reset-socfpga.c diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 4f60caf750ce..60fed3d7820b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o +obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c new file mode 100644 index 000000000000..79c32ca84ef1 --- /dev/null +++ b/drivers/reset/reset-socfpga.c @@ -0,0 +1,146 @@ +/* + * Copyright 2014 Steffen Trumtrar + * + * based on + * Allwinner SoCs Reset Controller driver + * + * Copyright 2013 Maxime Ripard + * + * Maxime Ripard + * + * 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 + +#define NR_BANKS 4 +#define OFFSET_MODRST 0x10 + +struct socfpga_reset_data { + spinlock_t lock; + void __iomem *membase; + struct reset_controller_dev rcdev; +}; + +static int socfpga_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct socfpga_reset_data *data = container_of(rcdev, + struct socfpga_reset_data, + rcdev); + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); + writel(reg | BIT(offset), data->membase + OFFSET_MODRST + + (bank * NR_BANKS)); + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int socfpga_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct socfpga_reset_data *data = container_of(rcdev, + struct socfpga_reset_data, + rcdev); + + int bank = id / BITS_PER_LONG; + int offset = id % BITS_PER_LONG; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&data->lock, flags); + + reg = readl(data->membase + OFFSET_MODRST + (bank * NR_BANKS)); + writel(reg & ~BIT(offset), data->membase + OFFSET_MODRST + + (bank * NR_BANKS)); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static struct reset_control_ops socfpga_reset_ops = { + .assert = socfpga_reset_assert, + .deassert = socfpga_reset_deassert, +}; + +static int socfpga_reset_probe(struct platform_device *pdev) +{ + struct socfpga_reset_data *data; + struct resource *res; + + /* + * The binding was mainlined without the required property. + * Do not continue, when we encounter an old DT. + */ + if (!of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) { + dev_err(&pdev->dev, "%s missing #reset-cells property\n", + pdev->dev.of_node->full_name); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->membase)) + return PTR_ERR(data->membase); + + spin_lock_init(&data->lock); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = NR_BANKS * BITS_PER_LONG; + data->rcdev.ops = &socfpga_reset_ops; + data->rcdev.of_node = pdev->dev.of_node; + reset_controller_register(&data->rcdev); + + return 0; +} + +static int socfpga_reset_remove(struct platform_device *pdev) +{ + struct socfpga_reset_data *data = platform_get_drvdata(pdev); + + reset_controller_unregister(&data->rcdev); + + return 0; +} + +static const struct of_device_id socfpga_reset_dt_ids[] = { + { .compatible = "altr,rst-mgr", }, + { /* sentinel */ }, +}; + +static struct platform_driver socfpga_reset_driver = { + .probe = socfpga_reset_probe, + .remove = socfpga_reset_remove, + .driver = { + .name = "socfpga-reset", + .owner = THIS_MODULE, + .of_match_table = socfpga_reset_dt_ids, + }, +}; +module_platform_driver(socfpga_reset_driver); + +MODULE_AUTHOR("Steffen Trumtrar Date: Thu, 24 Apr 2014 10:29:50 +0300 Subject: [PATCH 013/101] dmaengine: edma: Add channel number to debug prints It helps to identify issues if we have some information regarding to the channel which the event is associated. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index fa87fd52b0ad..473155d34d7b 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -183,7 +183,8 @@ static void edma_execute(struct edma_chan *echan) } if (edesc->processed <= MAX_NR_SG) { - dev_dbg(dev, "first transfer starting %d\n", echan->ch_num); + dev_dbg(dev, "first transfer starting on channel %d\n", + echan->ch_num); edma_start(echan->ch_num); } else { dev_dbg(dev, "chan: %d: completed %d elements, resuming\n", @@ -197,7 +198,7 @@ static void edma_execute(struct edma_chan *echan) * MAX_NR_SG */ if (echan->missed) { - dev_dbg(dev, "missed event in execute detected\n"); + dev_dbg(dev, "missed event on channel %d\n", echan->ch_num); edma_clean_channel(echan->ch_num); edma_stop(echan->ch_num); edma_start(echan->ch_num); @@ -779,7 +780,7 @@ static int edma_alloc_chan_resources(struct dma_chan *chan) echan->alloced = true; echan->slot[0] = echan->ch_num; - dev_dbg(dev, "allocated channel for %u:%u\n", + dev_dbg(dev, "allocated channel %d for %u:%u\n", echan->ch_num, EDMA_CTLR(echan->ch_num), EDMA_CHAN_SLOT(echan->ch_num)); return 0; From 71e2e5d39770325c6acccedbe4629cad4336f6d4 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 22 Apr 2014 23:26:09 +0200 Subject: [PATCH 014/101] memory: mvebu-devbus: use ARMADA_ prefix in defines The mvebu-devbus driver currently only supports the Armada 370/XP family, but it can also cover the Orion5x family. However, the Orion5x family has a different organization of the register. Therefore, in preparation to the introduction of Orion5x support, we rename the Armada 370/XP specific definitions to have an ARMADA_ prefix. Signed-off-by: Thomas Petazzoni Acked-by: Sebastian Hesselbarth Tested-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398202002-28530-6-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- drivers/memory/mvebu-devbus.c | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index b59a17fb7c3e..e66de7f3c33c 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -30,19 +30,19 @@ #include /* Register definitions */ -#define DEV_WIDTH_BIT 30 -#define BADR_SKEW_BIT 28 -#define RD_HOLD_BIT 23 -#define ACC_NEXT_BIT 17 -#define RD_SETUP_BIT 12 -#define ACC_FIRST_BIT 6 +#define ARMADA_DEV_WIDTH_BIT 30 +#define ARMADA_BADR_SKEW_BIT 28 +#define ARMADA_RD_HOLD_BIT 23 +#define ARMADA_ACC_NEXT_BIT 17 +#define ARMADA_RD_SETUP_BIT 12 +#define ARMADA_ACC_FIRST_BIT 6 -#define SYNC_ENABLE_BIT 24 -#define WR_HIGH_BIT 16 -#define WR_LOW_BIT 8 +#define ARMADA_SYNC_ENABLE_BIT 24 +#define ARMADA_WR_HIGH_BIT 16 +#define ARMADA_WR_LOW_BIT 8 -#define READ_PARAM_OFFSET 0x0 -#define WRITE_PARAM_OFFSET 0x4 +#define ARMADA_READ_PARAM_OFFSET 0x0 +#define ARMADA_WRITE_PARAM_OFFSET 0x4 struct devbus_read_params { u32 bus_width; @@ -178,31 +178,31 @@ static int devbus_set_timing_params(struct devbus *devbus, return err; /* Set read timings */ - value = r.bus_width << DEV_WIDTH_BIT | - r.badr_skew << BADR_SKEW_BIT | - r.rd_hold << RD_HOLD_BIT | - r.acc_next << ACC_NEXT_BIT | - r.rd_setup << RD_SETUP_BIT | - r.acc_first << ACC_FIRST_BIT | + value = r.bus_width << ARMADA_DEV_WIDTH_BIT | + r.badr_skew << ARMADA_BADR_SKEW_BIT | + r.rd_hold << ARMADA_RD_HOLD_BIT | + r.acc_next << ARMADA_ACC_NEXT_BIT | + r.rd_setup << ARMADA_RD_SETUP_BIT | + r.acc_first << ARMADA_ACC_FIRST_BIT | r.turn_off; dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n", - devbus->base + READ_PARAM_OFFSET, + devbus->base + ARMADA_READ_PARAM_OFFSET, value); - writel(value, devbus->base + READ_PARAM_OFFSET); + writel(value, devbus->base + ARMADA_READ_PARAM_OFFSET); /* Set write timings */ - value = w.sync_enable << SYNC_ENABLE_BIT | - w.wr_low << WR_LOW_BIT | - w.wr_high << WR_HIGH_BIT | + value = w.sync_enable << ARMADA_SYNC_ENABLE_BIT | + w.wr_low << ARMADA_WR_LOW_BIT | + w.wr_high << ARMADA_WR_HIGH_BIT | w.ale_wr; dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n", - devbus->base + WRITE_PARAM_OFFSET, + devbus->base + ARMADA_WRITE_PARAM_OFFSET, value); - writel(value, devbus->base + WRITE_PARAM_OFFSET); + writel(value, devbus->base + ARMADA_WRITE_PARAM_OFFSET); return 0; } From 8a33692ee8f10f1ceb38001101a4f55de921d726 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 22 Apr 2014 23:26:10 +0200 Subject: [PATCH 015/101] memory: mvebu-devbus: use _SHIFT suffixes instead of _BIT As noted by Sebastian Hesselbarth, the definitions in mvebu-devbus.c are not bit definition, but rather shift values, so a _SHIFT prefix would make more sense. This commit therefore replaces the *_BIT definitions by *_SHIFT definitions. Signed-off-by: Thomas Petazzoni Acked-by: Sebastian Hesselbarth Tested-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398202002-28530-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- drivers/memory/mvebu-devbus.c | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index e66de7f3c33c..0f196b36cc3e 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -30,16 +30,16 @@ #include /* Register definitions */ -#define ARMADA_DEV_WIDTH_BIT 30 -#define ARMADA_BADR_SKEW_BIT 28 -#define ARMADA_RD_HOLD_BIT 23 -#define ARMADA_ACC_NEXT_BIT 17 -#define ARMADA_RD_SETUP_BIT 12 -#define ARMADA_ACC_FIRST_BIT 6 +#define ARMADA_DEV_WIDTH_SHIFT 30 +#define ARMADA_BADR_SKEW_SHIFT 28 +#define ARMADA_RD_HOLD_SHIFT 23 +#define ARMADA_ACC_NEXT_SHIFT 17 +#define ARMADA_RD_SETUP_SHIFT 12 +#define ARMADA_ACC_FIRST_SHIFT 6 -#define ARMADA_SYNC_ENABLE_BIT 24 -#define ARMADA_WR_HIGH_BIT 16 -#define ARMADA_WR_LOW_BIT 8 +#define ARMADA_SYNC_ENABLE_SHIFT 24 +#define ARMADA_WR_HIGH_SHIFT 16 +#define ARMADA_WR_LOW_SHIFT 8 #define ARMADA_READ_PARAM_OFFSET 0x0 #define ARMADA_WRITE_PARAM_OFFSET 0x4 @@ -178,12 +178,12 @@ static int devbus_set_timing_params(struct devbus *devbus, return err; /* Set read timings */ - value = r.bus_width << ARMADA_DEV_WIDTH_BIT | - r.badr_skew << ARMADA_BADR_SKEW_BIT | - r.rd_hold << ARMADA_RD_HOLD_BIT | - r.acc_next << ARMADA_ACC_NEXT_BIT | - r.rd_setup << ARMADA_RD_SETUP_BIT | - r.acc_first << ARMADA_ACC_FIRST_BIT | + value = r.bus_width << ARMADA_DEV_WIDTH_SHIFT | + r.badr_skew << ARMADA_BADR_SKEW_SHIFT | + r.rd_hold << ARMADA_RD_HOLD_SHIFT | + r.acc_next << ARMADA_ACC_NEXT_SHIFT | + r.rd_setup << ARMADA_RD_SETUP_SHIFT | + r.acc_first << ARMADA_ACC_FIRST_SHIFT | r.turn_off; dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n", @@ -193,9 +193,9 @@ static int devbus_set_timing_params(struct devbus *devbus, writel(value, devbus->base + ARMADA_READ_PARAM_OFFSET); /* Set write timings */ - value = w.sync_enable << ARMADA_SYNC_ENABLE_BIT | - w.wr_low << ARMADA_WR_LOW_BIT | - w.wr_high << ARMADA_WR_HIGH_BIT | + value = w.sync_enable << ARMADA_SYNC_ENABLE_SHIFT | + w.wr_low << ARMADA_WR_LOW_SHIFT | + w.wr_high << ARMADA_WR_HIGH_SHIFT | w.ale_wr; dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n", From 30bd30b603462ae64a0b261a704a8b2b070c6688 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 22 Apr 2014 23:26:11 +0200 Subject: [PATCH 016/101] memory: mvebu-devbus: split functions The mvebu-devbus driver currently only supports the Armada 370/XP family, but it can also cover the Orion5x family. However, the Orion5x family has a different organization of the registers. Therefore, in preparation to the introduction of Orion5x support, we separate into two functions the code that 1/ retrieves the timing parameters from the Device Tree and 2/ applies those timings parameters into the hardware registers. Signed-off-by: Thomas Petazzoni Acked-by: Sebastian Hesselbarth Tested-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398202002-28530-8-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- drivers/memory/mvebu-devbus.c | 90 ++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index 0f196b36cc3e..5dc9c6360943 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -89,19 +89,15 @@ static int get_timing_param_ps(struct devbus *devbus, return 0; } -static int devbus_set_timing_params(struct devbus *devbus, - struct device_node *node) +static int devbus_get_timing_params(struct devbus *devbus, + struct device_node *node, + struct devbus_read_params *r, + struct devbus_write_params *w) { - struct devbus_read_params r; - struct devbus_write_params w; - u32 value; int err; - dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", - devbus->tick_ps); - /* Get read timings */ - err = of_property_read_u32(node, "devbus,bus-width", &r.bus_width); + err = of_property_read_u32(node, "devbus,bus-width", &r->bus_width); if (err < 0) { dev_err(devbus->dev, "%s has no 'devbus,bus-width' property\n", @@ -113,48 +109,48 @@ static int devbus_set_timing_params(struct devbus *devbus, * The bus width is encoded into the register as 0 for 8 bits, * and 1 for 16 bits, so we do the necessary conversion here. */ - if (r.bus_width == 8) - r.bus_width = 0; - else if (r.bus_width == 16) - r.bus_width = 1; + if (r->bus_width == 8) + r->bus_width = 0; + else if (r->bus_width == 16) + r->bus_width = 1; else { - dev_err(devbus->dev, "invalid bus width %d\n", r.bus_width); + dev_err(devbus->dev, "invalid bus width %d\n", r->bus_width); return -EINVAL; } err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", - &r.badr_skew); + &r->badr_skew); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps", - &r.turn_off); + &r->turn_off); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps", - &r.acc_first); + &r->acc_first); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps", - &r.acc_next); + &r->acc_next); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", - &r.rd_setup); + &r->rd_setup); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", - &r.rd_hold); + &r->rd_hold); if (err < 0) return err; /* Get write timings */ err = of_property_read_u32(node, "devbus,sync-enable", - &w.sync_enable); + &w->sync_enable); if (err < 0) { dev_err(devbus->dev, "%s has no 'devbus,sync-enable' property\n", @@ -163,28 +159,38 @@ static int devbus_set_timing_params(struct devbus *devbus, } err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", - &w.ale_wr); + &w->ale_wr); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps", - &w.wr_low); + &w->wr_low); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps", - &w.wr_high); + &w->wr_high); if (err < 0) return err; + return 0; +} + +static void devbus_armada_set_timing_params(struct devbus *devbus, + struct device_node *node, + struct devbus_read_params *r, + struct devbus_write_params *w) +{ + u32 value; + /* Set read timings */ - value = r.bus_width << ARMADA_DEV_WIDTH_SHIFT | - r.badr_skew << ARMADA_BADR_SKEW_SHIFT | - r.rd_hold << ARMADA_RD_HOLD_SHIFT | - r.acc_next << ARMADA_ACC_NEXT_SHIFT | - r.rd_setup << ARMADA_RD_SETUP_SHIFT | - r.acc_first << ARMADA_ACC_FIRST_SHIFT | - r.turn_off; + value = r->bus_width << ARMADA_DEV_WIDTH_SHIFT | + r->badr_skew << ARMADA_BADR_SKEW_SHIFT | + r->rd_hold << ARMADA_RD_HOLD_SHIFT | + r->acc_next << ARMADA_ACC_NEXT_SHIFT | + r->rd_setup << ARMADA_RD_SETUP_SHIFT | + r->acc_first << ARMADA_ACC_FIRST_SHIFT | + r->turn_off; dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n", devbus->base + ARMADA_READ_PARAM_OFFSET, @@ -193,24 +199,24 @@ static int devbus_set_timing_params(struct devbus *devbus, writel(value, devbus->base + ARMADA_READ_PARAM_OFFSET); /* Set write timings */ - value = w.sync_enable << ARMADA_SYNC_ENABLE_SHIFT | - w.wr_low << ARMADA_WR_LOW_SHIFT | - w.wr_high << ARMADA_WR_HIGH_SHIFT | - w.ale_wr; + value = w->sync_enable << ARMADA_SYNC_ENABLE_SHIFT | + w->wr_low << ARMADA_WR_LOW_SHIFT | + w->wr_high << ARMADA_WR_HIGH_SHIFT | + w->ale_wr; dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n", devbus->base + ARMADA_WRITE_PARAM_OFFSET, value); writel(value, devbus->base + ARMADA_WRITE_PARAM_OFFSET); - - return 0; } static int mvebu_devbus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; + struct devbus_read_params r; + struct devbus_write_params w; struct devbus *devbus; struct resource *res; struct clk *clk; @@ -240,11 +246,17 @@ static int mvebu_devbus_probe(struct platform_device *pdev) rate = clk_get_rate(clk) / 1000; devbus->tick_ps = 1000000000 / rate; - /* Read the device tree node and set the new timing parameters */ - err = devbus_set_timing_params(devbus, node); + dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", + devbus->tick_ps); + + /* Read the Device Tree node */ + err = devbus_get_timing_params(devbus, node, &r, &w); if (err < 0) return err; + /* Set the new timing parameters */ + devbus_armada_set_timing_params(devbus, node, &r, &w); + /* * We need to create a child device explicitly from here to * guarantee that the child will be probed after the timing From c4ec7430c30a4012a04aa5535919845f543f075d Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 22 Apr 2014 23:26:12 +0200 Subject: [PATCH 017/101] memory: mvebu-devbus: add Orion5x support This commit adds support for the Orion5x family of Marvell processors into the mvebu-devbus driver. It differs from the already supported Armada 370/XP by: * Having a single register (instead of two) for doing all the timing configuration. * Having a few less timing configuration parameters. For this reason, a separate compatible string "marvell,orion-devbus" is introduced. Signed-off-by: Thomas Petazzoni Acked-by: Sebastian Hesselbarth Tested-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398202002-28530-9-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../memory-controllers/mvebu-devbus.txt | 25 ++++- drivers/memory/mvebu-devbus.c | 103 ++++++++++++++---- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt index 653c90c34a71..55adde214627 100644 --- a/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt +++ b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt @@ -6,10 +6,11 @@ The actual devices are instantiated from the child nodes of a Device Bus node. Required properties: - - compatible: Currently only Armada 370/XP SoC are supported, - with this compatible string: + - compatible: Armada 370/XP SoC are supported using the + "marvell,mvebu-devbus" compatible string. - marvell,mvebu-devbus + Orion5x SoC are supported using the + "marvell,orion-devbus" compatible string. - reg: A resource specifier for the register space. This is the base address of a chip select within @@ -22,7 +23,7 @@ Required properties: integer values for each chip-select line in use: 0 -Mandatory timing properties for child nodes: +Timing properties for child nodes: Read parameters: @@ -30,21 +31,26 @@ Read parameters: drive the AD bus after the completion of a device read. This prevents contentions on the Device Bus after a read cycle from a slow device. + Mandatory. - - devbus,bus-width: Defines the bus width (e.g. <16>) + - devbus,bus-width: Defines the bus width, in bits (e.g. <16>). + Mandatory. - devbus,badr-skew-ps: Defines the time delay from from A[2:0] toggle, to read data sample. This parameter is useful for synchronous pipelined devices, where the address precedes the read data by one or two cycles. + Mandatory. - devbus,acc-first-ps: Defines the time delay from the negation of ALE[0] to the cycle that the first read data is sampled by the controller. + Mandatory. - devbus,acc-next-ps: Defines the time delay between the cycle that samples data N and the cycle that samples data N+1 (in burst accesses). + Mandatory. - devbus,rd-setup-ps: Defines the time delay between DEV_CSn assertion to DEV_OEn assertion. If set to 0 (default), @@ -52,6 +58,8 @@ Read parameters: This parameter has no affect on parameter (no affect on first data sample). Set to a value smaller than . + Mandatory for "marvell,mvebu-devbus" + compatible string, ignored otherwise. - devbus,rd-hold-ps: Defines the time between the last data sample to the de-assertion of DEV_CSn. If set to 0 (default), @@ -62,16 +70,20 @@ Read parameters: last data sampled. Also this parameter has no affect on parameter. Set to a value smaller than . + Mandatory for "marvell,mvebu-devbus" + compatible string, ignored otherwise. Write parameters: - devbus,ale-wr-ps: Defines the time delay from the ALE[0] negation cycle to the DEV_WEn assertion. + Mandatory. - devbus,wr-low-ps: Defines the time during which DEV_WEn is active. A[2:0] and Data are kept valid as long as DEV_WEn is active. This parameter defines the setup time of address and data to DEV_WEn rise. + Mandatory. - devbus,wr-high-ps: Defines the time during which DEV_WEn is kept inactive (high) between data beats of a burst write. @@ -79,10 +91,13 @@ Write parameters: - ps. This parameter defines the hold time of address and data after DEV_WEn rise. + Mandatory. - devbus,sync-enable: Synchronous device enable. 1: True 0: False + Mandatory for "marvell,mvebu-devbus" compatible + string, ignored otherwise. An example for an Armada XP GP board, with a 16 MiB NOR device as child is showed below. Note that the Device Bus driver is in charge of allocating diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index 5dc9c6360943..c8f3dad8a825 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -2,7 +2,7 @@ * Marvell EBU SoC Device Bus Controller * (memory controller for NOR/NAND/SRAM/FPGA devices) * - * Copyright (C) 2013 Marvell + * Copyright (C) 2013-2014 Marvell * * 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 @@ -44,6 +44,34 @@ #define ARMADA_READ_PARAM_OFFSET 0x0 #define ARMADA_WRITE_PARAM_OFFSET 0x4 +#define ORION_RESERVED (0x2 << 30) +#define ORION_BADR_SKEW_SHIFT 28 +#define ORION_WR_HIGH_EXT_BIT BIT(27) +#define ORION_WR_HIGH_EXT_MASK 0x8 +#define ORION_WR_LOW_EXT_BIT BIT(26) +#define ORION_WR_LOW_EXT_MASK 0x8 +#define ORION_ALE_WR_EXT_BIT BIT(25) +#define ORION_ALE_WR_EXT_MASK 0x8 +#define ORION_ACC_NEXT_EXT_BIT BIT(24) +#define ORION_ACC_NEXT_EXT_MASK 0x10 +#define ORION_ACC_FIRST_EXT_BIT BIT(23) +#define ORION_ACC_FIRST_EXT_MASK 0x10 +#define ORION_TURN_OFF_EXT_BIT BIT(22) +#define ORION_TURN_OFF_EXT_MASK 0x8 +#define ORION_DEV_WIDTH_SHIFT 20 +#define ORION_WR_HIGH_SHIFT 17 +#define ORION_WR_HIGH_MASK 0x7 +#define ORION_WR_LOW_SHIFT 14 +#define ORION_WR_LOW_MASK 0x7 +#define ORION_ALE_WR_SHIFT 11 +#define ORION_ALE_WR_MASK 0x7 +#define ORION_ACC_NEXT_SHIFT 7 +#define ORION_ACC_NEXT_MASK 0xF +#define ORION_ACC_FIRST_SHIFT 3 +#define ORION_ACC_FIRST_MASK 0xF +#define ORION_TURN_OFF_SHIFT 0 +#define ORION_TURN_OFF_MASK 0x7 + struct devbus_read_params { u32 bus_width; u32 badr_skew; @@ -96,7 +124,6 @@ static int devbus_get_timing_params(struct devbus *devbus, { int err; - /* Get read timings */ err = of_property_read_u32(node, "devbus,bus-width", &r->bus_width); if (err < 0) { dev_err(devbus->dev, @@ -138,24 +165,25 @@ static int devbus_get_timing_params(struct devbus *devbus, if (err < 0) return err; - err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", - &r->rd_setup); - if (err < 0) - return err; + if (of_device_is_compatible(devbus->dev->of_node, "marvell,mvebu-devbus")) { + err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", + &r->rd_setup); + if (err < 0) + return err; - err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", - &r->rd_hold); - if (err < 0) - return err; + err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", + &r->rd_hold); + if (err < 0) + return err; - /* Get write timings */ - err = of_property_read_u32(node, "devbus,sync-enable", - &w->sync_enable); - if (err < 0) { - dev_err(devbus->dev, - "%s has no 'devbus,sync-enable' property\n", - node->full_name); - return err; + err = of_property_read_u32(node, "devbus,sync-enable", + &w->sync_enable); + if (err < 0) { + dev_err(devbus->dev, + "%s has no 'devbus,sync-enable' property\n", + node->full_name); + return err; + } } err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", @@ -176,6 +204,39 @@ static int devbus_get_timing_params(struct devbus *devbus, return 0; } +static void devbus_orion_set_timing_params(struct devbus *devbus, + struct device_node *node, + struct devbus_read_params *r, + struct devbus_write_params *w) +{ + u32 value; + + /* + * The hardware designers found it would be a good idea to + * split most of the values in the register into two fields: + * one containing all the low-order bits, and another one + * containing just the high-order bit. For all of those + * fields, we have to split the value into these two parts. + */ + value = (r->turn_off & ORION_TURN_OFF_MASK) << ORION_TURN_OFF_SHIFT | + (r->acc_first & ORION_ACC_FIRST_MASK) << ORION_ACC_FIRST_SHIFT | + (r->acc_next & ORION_ACC_NEXT_MASK) << ORION_ACC_NEXT_SHIFT | + (w->ale_wr & ORION_ALE_WR_MASK) << ORION_ALE_WR_SHIFT | + (w->wr_low & ORION_WR_LOW_MASK) << ORION_WR_LOW_SHIFT | + (w->wr_high & ORION_WR_HIGH_MASK) << ORION_WR_HIGH_SHIFT | + r->bus_width << ORION_DEV_WIDTH_SHIFT | + ((r->turn_off & ORION_TURN_OFF_EXT_MASK) ? ORION_TURN_OFF_EXT_BIT : 0) | + ((r->acc_first & ORION_ACC_FIRST_EXT_MASK) ? ORION_ACC_FIRST_EXT_BIT : 0) | + ((r->acc_next & ORION_ACC_NEXT_EXT_MASK) ? ORION_ACC_NEXT_EXT_BIT : 0) | + ((w->ale_wr & ORION_ALE_WR_EXT_MASK) ? ORION_ALE_WR_EXT_BIT : 0) | + ((w->wr_low & ORION_WR_LOW_EXT_MASK) ? ORION_WR_LOW_EXT_BIT : 0) | + ((w->wr_high & ORION_WR_HIGH_EXT_MASK) ? ORION_WR_HIGH_EXT_BIT : 0) | + (r->badr_skew << ORION_BADR_SKEW_SHIFT) | + ORION_RESERVED; + + writel(value, devbus->base); +} + static void devbus_armada_set_timing_params(struct devbus *devbus, struct device_node *node, struct devbus_read_params *r, @@ -255,7 +316,10 @@ static int mvebu_devbus_probe(struct platform_device *pdev) return err; /* Set the new timing parameters */ - devbus_armada_set_timing_params(devbus, node, &r, &w); + if (of_device_is_compatible(node, "marvell,orion-devbus")) + devbus_orion_set_timing_params(devbus, node, &r, &w); + else + devbus_armada_set_timing_params(devbus, node, &r, &w); /* * We need to create a child device explicitly from here to @@ -271,6 +335,7 @@ static int mvebu_devbus_probe(struct platform_device *pdev) static const struct of_device_id mvebu_devbus_of_match[] = { { .compatible = "marvell,mvebu-devbus" }, + { .compatible = "marvell,orion-devbus" }, {}, }; MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match); From 0456d3300edba52e47c6b5582cfe9d87756f5590 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 22 Apr 2014 23:26:13 +0200 Subject: [PATCH 018/101] memory: mvebu-devbus: add a devbus, keep-config property Currently, the mvebu-devbus Device Tree binding makes defining the timing parameters mandatory. However, in practice, when converting Orion5x platforms to the Device Tree, we may not necessarily have easy access to the hardware platforms to fetch those values which were not defined in old-style board files: all these platforms rely on the bootloader setting the timing parameters correctly. In order to facilitate the migration to the Device Tree of this platform, this commit relaxes the mvebu-devbus Device Tree binding by introducing a 'devbus,keep-config' boolean property, which, if defined, will ignore all timing parameters passed in the Device Tree, and simply rely on the timing values already defined by the bootloader. Signed-off-by: Thomas Petazzoni Acked-by: Sebastian Hesselbarth Tested-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398202002-28530-10-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../memory-controllers/mvebu-devbus.txt | 29 ++++++++++++------- drivers/memory/mvebu-devbus.c | 20 +++++++------ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt index 55adde214627..1ee3bc09f319 100644 --- a/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt +++ b/Documentation/devicetree/bindings/memory-controllers/mvebu-devbus.txt @@ -23,6 +23,13 @@ Required properties: integer values for each chip-select line in use: 0 +Optional properties: + + - devbus,keep-config This property can optionally be used to keep + using the timing parameters set by the + bootloader. It makes all the timing properties + described below unused. + Timing properties for child nodes: Read parameters: @@ -31,26 +38,26 @@ Read parameters: drive the AD bus after the completion of a device read. This prevents contentions on the Device Bus after a read cycle from a slow device. - Mandatory. + Mandatory, except if devbus,keep-config is used. - devbus,bus-width: Defines the bus width, in bits (e.g. <16>). - Mandatory. + Mandatory, except if devbus,keep-config is used. - devbus,badr-skew-ps: Defines the time delay from from A[2:0] toggle, to read data sample. This parameter is useful for synchronous pipelined devices, where the address precedes the read data by one or two cycles. - Mandatory. + Mandatory, except if devbus,keep-config is used. - devbus,acc-first-ps: Defines the time delay from the negation of ALE[0] to the cycle that the first read data is sampled by the controller. - Mandatory. + Mandatory, except if devbus,keep-config is used. - devbus,acc-next-ps: Defines the time delay between the cycle that samples data N and the cycle that samples data N+1 (in burst accesses). - Mandatory. + Mandatory, except if devbus,keep-config is used. - devbus,rd-setup-ps: Defines the time delay between DEV_CSn assertion to DEV_OEn assertion. If set to 0 (default), @@ -58,8 +65,8 @@ Read parameters: This parameter has no affect on parameter (no affect on first data sample). Set to a value smaller than . - Mandatory for "marvell,mvebu-devbus" - compatible string, ignored otherwise. + Mandatory for "marvell,mvebu-devbus" compatible string, + except if devbus,keep-config is used. - devbus,rd-hold-ps: Defines the time between the last data sample to the de-assertion of DEV_CSn. If set to 0 (default), @@ -70,8 +77,8 @@ Read parameters: last data sampled. Also this parameter has no affect on parameter. Set to a value smaller than . - Mandatory for "marvell,mvebu-devbus" - compatible string, ignored otherwise. + Mandatory for "marvell,mvebu-devbus" compatible string, + except if devbus,keep-config is used. Write parameters: @@ -96,8 +103,8 @@ Write parameters: - devbus,sync-enable: Synchronous device enable. 1: True 0: False - Mandatory for "marvell,mvebu-devbus" compatible - string, ignored otherwise. + Mandatory for "marvell,mvebu-devbus" compatible string, + except if devbus,keep-config is used. An example for an Armada XP GP board, with a 16 MiB NOR device as child is showed below. Note that the Device Bus driver is in charge of allocating diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index c8f3dad8a825..ff7138fd66d1 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -310,16 +310,18 @@ static int mvebu_devbus_probe(struct platform_device *pdev) dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", devbus->tick_ps); - /* Read the Device Tree node */ - err = devbus_get_timing_params(devbus, node, &r, &w); - if (err < 0) - return err; + if (!of_property_read_bool(node, "devbus,keep-config")) { + /* Read the Device Tree node */ + err = devbus_get_timing_params(devbus, node, &r, &w); + if (err < 0) + return err; - /* Set the new timing parameters */ - if (of_device_is_compatible(node, "marvell,orion-devbus")) - devbus_orion_set_timing_params(devbus, node, &r, &w); - else - devbus_armada_set_timing_params(devbus, node, &r, &w); + /* Set the new timing parameters */ + if (of_device_is_compatible(node, "marvell,orion-devbus")) + devbus_orion_set_timing_params(devbus, node, &r, &w); + else + devbus_armada_set_timing_params(devbus, node, &r, &w); + } /* * We need to create a child device explicitly from here to From b6205c39011e52e2f7983c3dba01ee51524ae29c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:18:45 -0500 Subject: [PATCH 019/101] dmaengine: edma: Sanitize residue reporting The residue reporting in edma_tx_status() is just broken. It blindly walks the psets and recalculates the lenght of the transfer from the hardware parameters. For cyclic transfers it adds the link pset, which results in interestingly large residues. For non-cyclic it adds the dummy pset, which is stupid as well. Aside of that it's silly to walk through the pset params when the per descriptor residue is known at the point of creating it. Store the information in edma_desc and use it. Signed-off-by: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 473155d34d7b..30cbbde52364 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -64,6 +64,7 @@ struct edma_desc { int absync; int pset_nr; int processed; + u32 residue; struct edmacc_param pset[0]; }; @@ -456,6 +457,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } edesc->pset_nr = sg_len; + edesc->residue = 0; /* Allocate a PaRAM slot, if needed */ nslots = min_t(unsigned, MAX_NR_SG, sg_len); @@ -491,6 +493,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } edesc->absync = ret; + edesc->residue += sg_dma_len(sg); /* If this is the last in a current SG set of transactions, enable interrupts so that next set is processed */ @@ -606,6 +609,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; + edesc->residue = buf_len; dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", __func__, echan->ch_num, nslots, period_len, buf_len); @@ -700,6 +704,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) vchan_cyclic_callback(&edesc->vdesc); } else if (edesc->processed == edesc->pset_nr) { dev_dbg(dev, "Transfer complete, stopping channel %d\n", ch_num); + edesc->residue = 0; edma_stop(echan->ch_num); vchan_cookie_complete(&edesc->vdesc); edma_execute(echan); @@ -832,25 +837,6 @@ static void edma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&echan->vchan.lock, flags); } -static size_t edma_desc_size(struct edma_desc *edesc) -{ - int i; - size_t size; - - if (edesc->absync) - for (size = i = 0; i < edesc->pset_nr; i++) - size += (edesc->pset[i].a_b_cnt & 0xffff) * - (edesc->pset[i].a_b_cnt >> 16) * - edesc->pset[i].ccnt; - else - size = (edesc->pset[0].a_b_cnt & 0xffff) * - (edesc->pset[0].a_b_cnt >> 16) + - (edesc->pset[0].a_b_cnt & 0xffff) * - (SZ_64K - 1) * edesc->pset[0].ccnt; - - return size; -} - /* Check request completion status */ static enum dma_status edma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, @@ -867,12 +853,10 @@ static enum dma_status edma_tx_status(struct dma_chan *chan, spin_lock_irqsave(&echan->vchan.lock, flags); vdesc = vchan_find_desc(&echan->vchan, cookie); - if (vdesc) { - txstate->residue = edma_desc_size(to_edma_desc(&vdesc->tx)); - } else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) { - struct edma_desc *edesc = echan->edesc; - txstate->residue = edma_desc_size(edesc); - } + if (vdesc) + txstate->residue = to_edma_desc(&vdesc->tx)->residue; + else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) + txstate->residue = echan->edesc->residue; spin_unlock_irqrestore(&echan->vchan.lock, flags); return ret; From de135939716dcdc8a6ea62e9228feb2eec0fca11 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:19:51 -0500 Subject: [PATCH 020/101] dmaengine: edma: Check the current decriptor first in tx_status() It's likely that the caller investigates the status of a currently active descriptor. Make that simple check first and only rumage in the vchan list if that fails. Signed-off-by: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 30cbbde52364..cfc267e819eb 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -852,11 +852,10 @@ static enum dma_status edma_tx_status(struct dma_chan *chan, return ret; spin_lock_irqsave(&echan->vchan.lock, flags); - vdesc = vchan_find_desc(&echan->vchan, cookie); - if (vdesc) - txstate->residue = to_edma_desc(&vdesc->tx)->residue; - else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) + if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) txstate->residue = echan->edesc->residue; + else if ((vdesc = vchan_find_desc(&echan->vchan, cookie))) + txstate->residue = to_edma_desc(&vdesc->tx)->residue; spin_unlock_irqrestore(&echan->vchan.lock, flags); return ret; From b5088ad9630c0aa477a4ed57747b8b3fa8e4b86b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:23:55 -0500 Subject: [PATCH 021/101] dmaengine: edma: Create private pset struct Preparatory patch to support finer grained accounting. Move the edma_params array out of edma_desc so we can add further per pset data to it. Signed-off-by: Thomas Gleixner [joelf@ti.com: Fixed up hunk #3 in original patch to apply] Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 67 +++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index cfc267e819eb..c6f60e9af8af 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -57,6 +57,10 @@ #define EDMA_MAX_SLOTS MAX_NR_SG #define EDMA_DESCRIPTORS 16 +struct edma_pset { + struct edmacc_param param; +}; + struct edma_desc { struct virt_dma_desc vdesc; struct list_head node; @@ -65,7 +69,7 @@ struct edma_desc { int pset_nr; int processed; u32 residue; - struct edmacc_param pset[0]; + struct edma_pset pset[0]; }; struct edma_cc; @@ -141,7 +145,7 @@ static void edma_execute(struct edma_chan *echan) /* Write descriptor PaRAM set(s) */ for (i = 0; i < nslots; i++) { j = i + edesc->processed; - edma_write_slot(echan->slot[i], &edesc->pset[j]); + edma_write_slot(echan->slot[i], &edesc->pset[j].param); dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" @@ -155,14 +159,14 @@ static void edma_execute(struct edma_chan *echan) " cidx\t%08x\n" " lkrld\t%08x\n", j, echan->ch_num, echan->slot[i], - edesc->pset[j].opt, - edesc->pset[j].src, - edesc->pset[j].dst, - edesc->pset[j].a_b_cnt, - edesc->pset[j].ccnt, - edesc->pset[j].src_dst_bidx, - edesc->pset[j].src_dst_cidx, - edesc->pset[j].link_bcntrld); + edesc->pset[j].param.opt, + edesc->pset[j].param.src, + edesc->pset[j].param.dst, + edesc->pset[j].param.a_b_cnt, + edesc->pset[j].param.ccnt, + edesc->pset[j].param.src_dst_bidx, + edesc->pset[j].param.src_dst_cidx, + edesc->pset[j].param.link_bcntrld); /* Link to the previous slot if not the last set */ if (i != (nslots - 1)) edma_link(echan->slot[i], echan->slot[i+1]); @@ -305,13 +309,14 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * @dma_length: Total length of the DMA transfer * @direction: Direction of the transfer */ -static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, +static int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset, dma_addr_t src_addr, dma_addr_t dst_addr, u32 burst, enum dma_slave_buswidth dev_width, unsigned int dma_length, enum dma_transfer_direction direction) { struct edma_chan *echan = to_edma_chan(chan); struct device *dev = chan->device->dev; + struct edmacc_param *param = &epset->param; int acnt, bcnt, ccnt, cidx; int src_bidx, dst_bidx, src_cidx, dst_cidx; int absync; @@ -391,26 +396,26 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, return -EINVAL; } - pset->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num)); + param->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num)); /* Configure A or AB synchronized transfers */ if (absync) - pset->opt |= SYNCDIM; + param->opt |= SYNCDIM; - pset->src = src_addr; - pset->dst = dst_addr; + param->src = src_addr; + param->dst = dst_addr; - pset->src_dst_bidx = (dst_bidx << 16) | src_bidx; - pset->src_dst_cidx = (dst_cidx << 16) | src_cidx; + param->src_dst_bidx = (dst_bidx << 16) | src_bidx; + param->src_dst_cidx = (dst_cidx << 16) | src_cidx; - pset->a_b_cnt = bcnt << 16 | acnt; - pset->ccnt = ccnt; + param->a_b_cnt = bcnt << 16 | acnt; + param->ccnt = ccnt; /* * Only time when (bcntrld) auto reload is required is for * A-sync case, and in this case, a requirement of reload value * of SZ_64K-1 only is assured. 'link' is initially set to NULL * and then later will be populated by edma_execute. */ - pset->link_bcntrld = 0xffffffff; + param->link_bcntrld = 0xffffffff; return absync; } @@ -498,11 +503,11 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( /* If this is the last in a current SG set of transactions, enable interrupts so that next set is processed */ if (!((i+1) % MAX_NR_SG)) - edesc->pset[i].opt |= TCINTEN; + edesc->pset[i].param.opt |= TCINTEN; /* If this is the last set, enable completion interrupt flag */ if (i == sg_len - 1) - edesc->pset[i].opt |= TCINTEN; + edesc->pset[i].param.opt |= TCINTEN; } return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); @@ -661,14 +666,14 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( " cidx\t%08x\n" " lkrld\t%08x\n", i, echan->ch_num, echan->slot[i], - edesc->pset[i].opt, - edesc->pset[i].src, - edesc->pset[i].dst, - edesc->pset[i].a_b_cnt, - edesc->pset[i].ccnt, - edesc->pset[i].src_dst_bidx, - edesc->pset[i].src_dst_cidx, - edesc->pset[i].link_bcntrld); + edesc->pset[i].param.opt, + edesc->pset[i].param.src, + edesc->pset[i].param.dst, + edesc->pset[i].param.a_b_cnt, + edesc->pset[i].param.ccnt, + edesc->pset[i].param.src_dst_bidx, + edesc->pset[i].param.src_dst_cidx, + edesc->pset[i].param.link_bcntrld); edesc->absync = ret; @@ -676,7 +681,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( * Enable interrupts for every period because callback * has to be called for every period. */ - edesc->pset[i].opt |= TCINTEN; + edesc->pset[i].param.opt |= TCINTEN; } return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); From c2da2340e5818aa72b2e847f1f24b036742ea5c7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:29:57 -0500 Subject: [PATCH 022/101] dmaengine: edma: Store transfer data in edma_desc and edma_pset For granular accounting we need to store the direction and the information for the individual psets: - source or destination address, depending on direction - length Signed-off-by: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index c6f60e9af8af..6e230006eb08 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -58,12 +58,15 @@ #define EDMA_DESCRIPTORS 16 struct edma_pset { + u32 len; + dma_addr_t addr; struct edmacc_param param; }; struct edma_desc { struct virt_dma_desc vdesc; struct list_head node; + enum dma_transfer_direction direction; int cyclic; int absync; int pset_nr; @@ -376,16 +379,20 @@ static int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset, cidx = acnt * bcnt; } + epset->len = dma_length; + if (direction == DMA_MEM_TO_DEV) { src_bidx = acnt; src_cidx = cidx; dst_bidx = 0; dst_cidx = 0; + epset->addr = src_addr; } else if (direction == DMA_DEV_TO_MEM) { src_bidx = 0; src_cidx = 0; dst_bidx = acnt; dst_cidx = cidx; + epset->addr = dst_addr; } else if (direction == DMA_MEM_TO_MEM) { src_bidx = acnt; src_cidx = cidx; @@ -463,6 +470,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( edesc->pset_nr = sg_len; edesc->residue = 0; + edesc->direction = direction; /* Allocate a PaRAM slot, if needed */ nslots = min_t(unsigned, MAX_NR_SG, sg_len); @@ -615,6 +623,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; edesc->residue = buf_len; + edesc->direction = direction; dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", __func__, echan->ch_num, nslots, period_len, buf_len); From cdae05a0f0f7d15837dfd6f4200e8caea03c9cbf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 10:49:43 +0000 Subject: [PATCH 023/101] dmaengine: edma: Make reading the position of active channels work As Joel pointed out, edma_read_position() uses memcpy_fromio() to read the parameter ram. That's not synchronized with the internal update as it does a byte by byte copy. We need to do a 32bit read to get a consistent value. Further reading destination and source is pointless. In DEV_TO_MEM transfers we are only interested in the destination, in MEM_TO_DEV we care about the source. In MEM_TO_MEM it really does not matter which one you read. Simple solution: Remove the pointers, select dest/source via a bool and return the read value. Remove the export of this function while at it. The only potential user is the dmaengine and that's always builtin. Signed-off-by: Thomas Gleixner Acked-by: Sekhar Nori Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 24 +++++++++--------------- include/linux/platform_data/edma.h | 2 +- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 0b37f7734d0f..25fa735abc6c 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -994,29 +994,23 @@ void edma_set_dest(unsigned slot, dma_addr_t dest_port, EXPORT_SYMBOL(edma_set_dest); /** - * edma_get_position - returns the current transfer points + * edma_get_position - returns the current transfer point * @slot: parameter RAM slot being examined - * @src: pointer to source port position - * @dst: pointer to destination port position + * @dst: true selects the dest position, false the source * - * Returns current source and destination addresses for a particular - * parameter RAM slot. Its channel should not be active when this is called. + * Returns the position of the current active slot */ -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) +dma_addr_t edma_get_position(unsigned slot, bool dst) { - struct edmacc_param temp; - unsigned ctlr; + u32 offs, ctlr = EDMA_CTLR(slot); - ctlr = EDMA_CTLR(slot); slot = EDMA_CHAN_SLOT(slot); - edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); - if (src != NULL) - *src = temp.src; - if (dst != NULL) - *dst = temp.dst; + offs = PARM_OFFSET(slot); + offs += dst ? PARM_DST : PARM_SRC; + + return edma_read(ctlr, offs); } -EXPORT_SYMBOL(edma_get_position); /** * edma_set_src_index - configure DMA source address indexing diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 923f8a3e4ce0..12f134b1493c 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -130,7 +130,7 @@ void edma_set_src(unsigned slot, dma_addr_t src_port, enum address_mode mode, enum fifo_width); void edma_set_dest(unsigned slot, dma_addr_t dest_port, enum address_mode mode, enum fifo_width); -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); +dma_addr_t edma_get_position(unsigned slot, bool dst); void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, From 740b41f7882162fc9339262b020757b741c4f1ac Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:34:11 -0500 Subject: [PATCH 024/101] dmaengine: edma: Provide granular accounting The first slot in the ParamRAM of EDMA holds the current active subtransfer. Depending on the direction we read either the source or the destination address from there. In the internal psets we have the address of the buffer(s). In the cyclic case we only use the internal pset[0] which holds the start address of the circular buffer and calculate the remaining room to the end of the buffer. In the SG case we read the current address and compare it to the internal psets address and length. - If the current address is outside of this range, the pset has been processed already and we mark it done, update the residue_stat value and process the next set. That avoids that we need to walk all processed psets for every invocation of tx_status. - If its inside the range we know that we look at the current active set and stop the walk. - In case of intermediate transfers we update the stats in the interrupt callback function before starting the next batch of transfers. The tx_status callback and the interrupt callback are serialized via vchan.lock. Signed-off-by: Thomas Gleixner [joelf@ti.com: Hunk #2 in original patch manually applied] Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 67 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 6e230006eb08..5d9f57f27ffb 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -71,7 +71,11 @@ struct edma_desc { int absync; int pset_nr; int processed; + int processed_stat; u32 residue; + u32 sg_len; + u32 residue_stat; + struct edma_chan *echan; struct edma_pset pset[0]; }; @@ -144,11 +148,13 @@ static void edma_execute(struct edma_chan *echan) /* Find out how many left */ left = edesc->pset_nr - edesc->processed; nslots = min(MAX_NR_SG, left); + edesc->sg_len = 0; /* Write descriptor PaRAM set(s) */ for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j].param); + edesc->sg_len += edesc->pset[j].len; dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" @@ -471,6 +477,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( edesc->pset_nr = sg_len; edesc->residue = 0; edesc->direction = direction; + edesc->echan = echan; /* Allocate a PaRAM slot, if needed */ nslots = min_t(unsigned, MAX_NR_SG, sg_len); @@ -517,6 +524,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (i == sg_len - 1) edesc->pset[i].param.opt |= TCINTEN; } + edesc->residue_stat = edesc->residue; return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); } @@ -622,8 +630,9 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; - edesc->residue = buf_len; + edesc->residue = edesc->residue_stat = buf_len; edesc->direction = direction; + edesc->echan = echan; dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", __func__, echan->ch_num, nslots, period_len, buf_len); @@ -724,6 +733,12 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) edma_execute(echan); } else { dev_dbg(dev, "Intermediate transfer complete on channel %d\n", ch_num); + + /* Update statistics for tx_status */ + edesc->residue -= edesc->sg_len; + edesc->residue_stat = edesc->residue; + edesc->processed_stat = edesc->processed; + edma_execute(echan); } } @@ -851,6 +866,54 @@ static void edma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&echan->vchan.lock, flags); } +static u32 edma_residue(struct edma_desc *edesc) +{ + bool dst = edesc->direction == DMA_DEV_TO_MEM; + struct edma_pset *pset = edesc->pset; + dma_addr_t done, pos; + int i; + + /* + * We always read the dst/src position from the first RamPar + * pset. That's the one which is active now. + */ + pos = edma_get_position(edesc->echan->slot[0], dst); + + /* + * Cyclic is simple. Just subtract pset[0].addr from pos. + * + * We never update edesc->residue in the cyclic case, so we + * can tell the remaining room to the end of the circular + * buffer. + */ + if (edesc->cyclic) { + done = pos - pset->addr; + edesc->residue_stat = edesc->residue - done; + return edesc->residue_stat; + } + + /* + * For SG operation we catch up with the last processed + * status. + */ + pset += edesc->processed_stat; + + for (i = edesc->processed_stat; i < edesc->processed; i++, pset++) { + /* + * If we are inside this pset address range, we know + * this is the active one. Get the current delta and + * stop walking the psets. + */ + if (pos >= pset->addr && pos < pset->addr + pset->len) + return edesc->residue_stat - (pos - pset->addr); + + /* Otherwise mark it done and update residue_stat. */ + edesc->processed_stat++; + edesc->residue_stat -= pset->len; + } + return edesc->residue_stat; +} + /* Check request completion status */ static enum dma_status edma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, @@ -867,7 +930,7 @@ static enum dma_status edma_tx_status(struct dma_chan *chan, spin_lock_irqsave(&echan->vchan.lock, flags); if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) - txstate->residue = echan->edesc->residue; + txstate->residue = edma_residue(echan->edesc); else if ((vdesc = vchan_find_desc(&echan->vchan, cookie))) txstate->residue = to_edma_desc(&vdesc->tx)->residue; spin_unlock_irqrestore(&echan->vchan.lock, flags); From 04361d887fc5d217bcb9cbd3c32980cdc34dc91f Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 28 Apr 2014 15:19:31 -0500 Subject: [PATCH 025/101] dmaengine: edma: Document variables used for residue accounting The granular residue accounting code uses certain variables specifically for residue accounting. Document these in the structure declaration. Also move around some elements and group them together. Cc: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 5d9f57f27ffb..18c833fa1646 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -70,12 +70,34 @@ struct edma_desc { int cyclic; int absync; int pset_nr; - int processed; - int processed_stat; - u32 residue; - u32 sg_len; - u32 residue_stat; struct edma_chan *echan; + int processed; + + /* + * The following 4 elements are used for residue accounting. + * + * - processed_stat: the number of SG elements we have traversed + * so far to cover accounting. This is updated directly to processed + * during edma_callback and is always <= processed, because processed + * refers to the number of pending transfer (programmed to EDMA + * controller), where as processed_stat tracks number of transfers + * accounted for so far. + * + * - residue: The amount of bytes we have left to transfer for this desc + * + * - residue_stat: The residue in bytes of data we have covered + * so far for accounting. This is updated directly to residue + * during callbacks to keep it current. + * + * - sg_len: Tracks the length of the current intermediate transfer, + * this is required to update the residue during intermediate transfer + * completion callback. + */ + int processed_stat; + u32 sg_len; + u32 residue; + u32 residue_stat; + struct edma_pset pset[0]; }; From b0cce4ca3e740b5224d75634aa9d9abe9dfceabb Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 28 Apr 2014 15:30:32 -0500 Subject: [PATCH 026/101] dmaengine: edma: update DMA memcpy to use new param element edma param struct is now within an edma_pset struct introduced in Thomas Gleixner's edma tx status series. Update memcpy function for the same. Cc: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 18c833fa1646..d08c4dedef35 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -583,8 +583,8 @@ struct dma_async_tx_descriptor *edma_prep_dma_memcpy( * on completion of every TR, and enable transfer-completion * interrupt on completion of the whole transfer. */ - edesc->pset[0].opt |= ITCCHEN; - edesc->pset[0].opt |= TCINTEN; + edesc->pset[0].param.opt |= ITCCHEN; + edesc->pset[0].param.opt |= TCINTEN; return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); } From 93bfb769752babdc4d3665a1fb166bb4e3ff927b Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 2 May 2014 22:27:01 +0900 Subject: [PATCH 027/101] clocksource: exynos_mct: register sched_clock callback Use the clocksource mct-frc for sched_clock Signed-off-by: Vincent Guittot Reviewed-by: Tomasz Figa Acked-by: Daniel Lezcano Signed-off-by: Kukjin Kim --- drivers/clocksource/exynos_mct.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index a6ee6d7cd63f..ea4dfc097790 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -24,6 +24,7 @@ #include #include #include +#include #define EXYNOS4_MCTREG(x) (x) #define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100) @@ -192,12 +193,19 @@ struct clocksource mct_frc = { .resume = exynos4_frc_resume, }; +static u64 notrace exynos4_read_sched_clock(void) +{ + return exynos4_frc_read(&mct_frc); +} + static void __init exynos4_clocksource_init(void) { exynos4_mct_frc_start(0, 0); if (clocksource_register_hz(&mct_frc, clk_rate)) panic("%s: can't register clocksource\n", mct_frc.name); + + sched_clock_register(exynos4_read_sched_clock, 64, clk_rate); } static void exynos4_mct_comp0_stop(void) From bae74510179bc6d2c71168eeef33b7b157e244d0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 1 Apr 2014 16:23:46 +0300 Subject: [PATCH 028/101] drivers: bus: omap_l3: Convert to use devm_kzalloc We can remove the kfree() calls from probe and remove. Signed-off-by: Peter Ujfalusi Reviewed-by: Santosh Shilimkar Acked-by: Tony Lindgren Signed-off-by: Nishanth Menon Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index feeecae623f6..d25d727e7cfb 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -134,7 +134,7 @@ static int omap4_l3_probe(struct platform_device *pdev) struct resource *res; int ret; - l3 = kzalloc(sizeof(*l3), GFP_KERNEL); + l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL); if (!l3) return -ENOMEM; @@ -142,15 +142,13 @@ static int omap4_l3_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "couldn't find resource 0\n"); - ret = -ENODEV; - goto err0; + return -ENODEV; } l3->l3_base[0] = ioremap(res->start, resource_size(res)); if (!l3->l3_base[0]) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -214,8 +212,6 @@ err2: iounmap(l3->l3_base[1]); err1: iounmap(l3->l3_base[0]); -err0: - kfree(l3); return ret; } @@ -228,7 +224,6 @@ static int omap4_l3_remove(struct platform_device *pdev) iounmap(l3->l3_base[0]); iounmap(l3->l3_base[1]); iounmap(l3->l3_base[2]); - kfree(l3); return 0; } From 56c4a0224add82ced3af81c3a898a228a0560720 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 1 Apr 2014 16:23:47 +0300 Subject: [PATCH 029/101] drivers: bus: omap_l3: Convert to use devm_ioremap_resource() We can then remove the iounmap() calls from probe and remove. Since the driver requests the resources via index we can do the mem resource request within a for loop. Signed-off-by: Peter Ujfalusi Reviewed-by: Santosh Shilimkar Acked-by: Tony Lindgren Signed-off-by: Nishanth Menon Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 59 ++++++++------------------------------- 1 file changed, 11 insertions(+), 48 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index d25d727e7cfb..6f58be3c2f76 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -131,52 +131,24 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) static int omap4_l3_probe(struct platform_device *pdev) { static struct omap4_l3 *l3; - struct resource *res; - int ret; + int ret, i; l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL); if (!l3) return -ENOMEM; platform_set_drvdata(pdev, l3); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "couldn't find resource 0\n"); - return -ENODEV; - } - l3->l3_base[0] = ioremap(res->start, resource_size(res)); - if (!l3->l3_base[0]) { - dev_err(&pdev->dev, "ioremap failed\n"); - return -ENOMEM; - } + /* Get mem resources */ + for (i = 0; i < L3_MODULES; i++) { + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, i); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "couldn't find resource 1\n"); - ret = -ENODEV; - goto err1; - } - - l3->l3_base[1] = ioremap(res->start, resource_size(res)); - if (!l3->l3_base[1]) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err1; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!res) { - dev_err(&pdev->dev, "couldn't find resource 2\n"); - ret = -ENODEV; - goto err2; - } - - l3->l3_base[2] = ioremap(res->start, resource_size(res)); - if (!l3->l3_base[2]) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err2; + l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(l3->l3_base[i])) { + dev_err(&pdev->dev, "ioremap %d failed\n", i); + return PTR_ERR(l3->l3_base[i]); + } } /* @@ -189,7 +161,7 @@ static int omap4_l3_probe(struct platform_device *pdev) if (ret) { pr_crit("L3: request_irq failed to register for 0x%x\n", l3->debug_irq); - goto err3; + return ret; } l3->app_irq = platform_get_irq(pdev, 1); @@ -206,12 +178,6 @@ static int omap4_l3_probe(struct platform_device *pdev) err4: free_irq(l3->debug_irq, l3); -err3: - iounmap(l3->l3_base[2]); -err2: - iounmap(l3->l3_base[1]); -err1: - iounmap(l3->l3_base[0]); return ret; } @@ -221,9 +187,6 @@ static int omap4_l3_remove(struct platform_device *pdev) free_irq(l3->app_irq, l3); free_irq(l3->debug_irq, l3); - iounmap(l3->l3_base[0]); - iounmap(l3->l3_base[1]); - iounmap(l3->l3_base[2]); return 0; } From a0ef78f353d6edc9f88d3247601f1dc5ad8f4b84 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 1 Apr 2014 16:23:48 +0300 Subject: [PATCH 030/101] drivers: bus: omap_l3: Convert to use devm_request_irq() With this we can remove the free_irq() calls from probe and remove. Signed-off-by: Peter Ujfalusi Reviewed-by: Santosh Shilimkar Acked-by: Tony Lindgren Signed-off-by: Nishanth Menon Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 6f58be3c2f76..25bcb60be880 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -155,9 +155,8 @@ static int omap4_l3_probe(struct platform_device *pdev) * Setup interrupt Handlers */ l3->debug_irq = platform_get_irq(pdev, 0); - ret = request_irq(l3->debug_irq, - l3_interrupt_handler, - IRQF_DISABLED, "l3-dbg-irq", l3); + ret = devm_request_irq(&pdev->dev, l3->debug_irq, l3_interrupt_handler, + IRQF_DISABLED, "l3-dbg-irq", l3); if (ret) { pr_crit("L3: request_irq failed to register for 0x%x\n", l3->debug_irq); @@ -165,29 +164,17 @@ static int omap4_l3_probe(struct platform_device *pdev) } l3->app_irq = platform_get_irq(pdev, 1); - ret = request_irq(l3->app_irq, - l3_interrupt_handler, - IRQF_DISABLED, "l3-app-irq", l3); - if (ret) { + ret = devm_request_irq(&pdev->dev, l3->app_irq, l3_interrupt_handler, + IRQF_DISABLED, "l3-app-irq", l3); + if (ret) pr_crit("L3: request_irq failed to register for 0x%x\n", l3->app_irq); - goto err4; - } - return 0; - -err4: - free_irq(l3->debug_irq, l3); return ret; } static int omap4_l3_remove(struct platform_device *pdev) { - struct omap4_l3 *l3 = platform_get_drvdata(pdev); - - free_irq(l3->app_irq, l3); - free_irq(l3->debug_irq, l3); - return 0; } From 442a4da7cad64cf7270cbbbed9ad69dc4a1bc263 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 1 Apr 2014 16:23:49 +0300 Subject: [PATCH 031/101] drivers: bus: omap_l3: Remove the platform driver's remove function It is NOP after the devm_* conversion. Signed-off-by: Peter Ujfalusi Reviewed-by: Santosh Shilimkar Acked-by: Tony Lindgren Signed-off-by: Nishanth Menon Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 25bcb60be880..0eff48585ae3 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -173,11 +173,6 @@ static int omap4_l3_probe(struct platform_device *pdev) return ret; } -static int omap4_l3_remove(struct platform_device *pdev) -{ - return 0; -} - #if defined(CONFIG_OF) static const struct of_device_id l3_noc_match[] = { {.compatible = "ti,omap4-l3-noc", }, @@ -190,7 +185,6 @@ MODULE_DEVICE_TABLE(of, l3_noc_match); static struct platform_driver omap4_l3_driver = { .probe = omap4_l3_probe, - .remove = omap4_l3_remove, .driver = { .name = "omap_l3_noc", .owner = THIS_MODULE, From ae22598a1199c1257b2de5643695221434ace1c3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 1 Apr 2014 16:23:50 +0300 Subject: [PATCH 032/101] drivers: bus: omap_l3: Change pr_crit() to dev_err() when IRQ request fails Use dev_err() which will going to print the driver's name as well and the KERN_ERR level is sufficient in this case (we also print via dev_err when there is an error with the mem resources) Signed-off-by: Peter Ujfalusi Reviewed-by: Santosh Shilimkar Acked-by: Tony Lindgren Signed-off-by: Nishanth Menon Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 0eff48585ae3..972691a668a3 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -158,8 +158,8 @@ static int omap4_l3_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, l3->debug_irq, l3_interrupt_handler, IRQF_DISABLED, "l3-dbg-irq", l3); if (ret) { - pr_crit("L3: request_irq failed to register for 0x%x\n", - l3->debug_irq); + dev_err(&pdev->dev, "request_irq failed for %d\n", + l3->debug_irq); return ret; } @@ -167,8 +167,7 @@ static int omap4_l3_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, l3->app_irq, l3_interrupt_handler, IRQF_DISABLED, "l3-app-irq", l3); if (ret) - pr_crit("L3: request_irq failed to register for 0x%x\n", - l3->app_irq); + dev_err(&pdev->dev, "request_irq failed for %d\n", l3->app_irq); return ret; } From c5f2aea0ef47b2934b46374abfac9015de79aecb Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 13:15:43 -0500 Subject: [PATCH 033/101] bus: omap_l3_noc: Fix copyright information This is an embarrassing patch :(. Texas Corporation does not make OMAP. Texas Instruments Inc does. For that matter I dont seem to be able to find a Texas Corporation on the internet either. While at it, update coverage to the current year and update the template to remove redundant information and use the standard boiler plate licensing. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 18 ++++++------------ drivers/bus/omap_l3_noc.h | 18 ++++++------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 972691a668a3..1eb6d85c19dc 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -1,24 +1,18 @@ /* * OMAP4XXX L3 Interconnect error handling driver * - * Copyright (C) 2011 Texas Corporation + * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar * Sricharan * * 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. + * it under the terms of the GNU General Public License 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 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA */ #include #include diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index a6ce34dc4814..cc4b1b1788f0 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -1,24 +1,18 @@ /* * OMAP4XXX L3 Interconnect error handling driver header * - * Copyright (C) 2011 Texas Corporation + * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar * sricharan * * 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. + * it under the terms of the GNU General Public License 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 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA */ #ifndef __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H #define __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H From c10d5c9e1200e2b8c23329173f279caa86c6af45 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Fri, 11 Apr 2014 13:09:36 -0500 Subject: [PATCH 034/101] bus: omap_l3_noc: rename functions and data to omap_l3 Since omap_l3_noc driver is now being used for OMAP5 and reusable with DRA7 and AM437x, using omap4 specific naming is misleading. Signed-off-by: Sricharan R Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 24 ++++++++++++------------ drivers/bus/omap_l3_noc.h | 11 ++++++----- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 1eb6d85c19dc..800486c88cc3 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -1,5 +1,5 @@ /* - * OMAP4XXX L3 Interconnect error handling driver + * OMAP L3 Interconnect error handling driver * * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar @@ -50,7 +50,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) { - struct omap4_l3 *l3 = _l3; + struct omap_l3 *l3 = _l3; int inttype, i, k; int err_src = 0; u32 std_err_main, err_reg, clear, masterid; @@ -122,9 +122,9 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) return IRQ_HANDLED; } -static int omap4_l3_probe(struct platform_device *pdev) +static int omap_l3_probe(struct platform_device *pdev) { - static struct omap4_l3 *l3; + static struct omap_l3 *l3; int ret, i; l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL); @@ -176,8 +176,8 @@ MODULE_DEVICE_TABLE(of, l3_noc_match); #define l3_noc_match NULL #endif -static struct platform_driver omap4_l3_driver = { - .probe = omap4_l3_probe, +static struct platform_driver omap_l3_driver = { + .probe = omap_l3_probe, .driver = { .name = "omap_l3_noc", .owner = THIS_MODULE, @@ -185,14 +185,14 @@ static struct platform_driver omap4_l3_driver = { }, }; -static int __init omap4_l3_init(void) +static int __init omap_l3_init(void) { - return platform_driver_register(&omap4_l3_driver); + return platform_driver_register(&omap_l3_driver); } -postcore_initcall_sync(omap4_l3_init); +postcore_initcall_sync(omap_l3_init); -static void __exit omap4_l3_exit(void) +static void __exit omap_l3_exit(void) { - platform_driver_unregister(&omap4_l3_driver); + platform_driver_unregister(&omap_l3_driver); } -module_exit(omap4_l3_exit); +module_exit(omap_l3_exit); diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index cc4b1b1788f0..b3c81f2fe7a2 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -1,5 +1,5 @@ /* - * OMAP4XXX L3 Interconnect error handling driver header + * OMAP L3 Interconnect error handling driver header * * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar @@ -14,8 +14,8 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ -#ifndef __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H -#define __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H +#ifndef __OMAP_L3_NOC_H +#define __OMAP_L3_NOC_H #define L3_MODULES 3 #define CLEAR_STDERR_LOG (1 << 31) @@ -157,7 +157,7 @@ static u32 *l3_targ[L3_MODULES] = { l3_targ_inst_clk3, }; -struct omap4_l3 { +struct omap_l3 { struct device *dev; struct clk *ick; @@ -167,4 +167,5 @@ struct omap4_l3 { int debug_irq; int app_irq; }; -#endif + +#endif /* __OMAP_L3_NOC_H */ From 73cecc46dd3c3fc3fdf906f4de1448a4279cb1e2 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 13:55:22 -0500 Subject: [PATCH 035/101] bus: omap_l3_noc: remove iclk from omap_l3 struct we do not use iclk directly anymore. And, even if we had to, we should be using pm_runtime APIs to do the same to be completely SoC independent. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index b3c81f2fe7a2..31984cf949cc 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -159,7 +159,6 @@ static u32 *l3_targ[L3_MODULES] = { struct omap_l3 { struct device *dev; - struct clk *ick; /* memory base */ void __iomem *l3_base[L3_MODULES]; From ca6a34935c2c95482b901d76116f785ea356412c Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 12:04:01 -0500 Subject: [PATCH 036/101] bus: omap_l3_noc: populate l3->dev and use it l3->dev is not populated, so populate it and use it to print information relevant to the device instead of using a generic pr_*. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 800486c88cc3..37d71b715053 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -131,6 +131,7 @@ static int omap_l3_probe(struct platform_device *pdev) if (!l3) return -ENOMEM; + l3->dev = &pdev->dev; platform_set_drvdata(pdev, l3); /* Get mem resources */ @@ -140,7 +141,7 @@ static int omap_l3_probe(struct platform_device *pdev) l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(l3->l3_base[i])) { - dev_err(&pdev->dev, "ioremap %d failed\n", i); + dev_err(l3->dev, "ioremap %d failed\n", i); return PTR_ERR(l3->l3_base[i]); } } @@ -149,19 +150,19 @@ static int omap_l3_probe(struct platform_device *pdev) * Setup interrupt Handlers */ l3->debug_irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(&pdev->dev, l3->debug_irq, l3_interrupt_handler, + ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler, IRQF_DISABLED, "l3-dbg-irq", l3); if (ret) { - dev_err(&pdev->dev, "request_irq failed for %d\n", + dev_err(l3->dev, "request_irq failed for %d\n", l3->debug_irq); return ret; } l3->app_irq = platform_get_irq(pdev, 1); - ret = devm_request_irq(&pdev->dev, l3->app_irq, l3_interrupt_handler, + ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler, IRQF_DISABLED, "l3-app-irq", l3); if (ret) - dev_err(&pdev->dev, "request_irq failed for %d\n", l3->app_irq); + dev_err(l3->dev, "request_irq failed for %d\n", l3->app_irq); return ret; } From 9e224c8ff19e4c4a9c9272171d5a0169d5cb1bef Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 11:21:47 -0500 Subject: [PATCH 037/101] bus: omap_l3_noc: switch over to relaxed variants of readl/writel Currently we use __raw_readl and writel in this driver. Considering there is no specific need for a memory barrier, replacing writel with endian-neutral writel_relaxed and replacing __raw_readls with the corresponding endian-neutral readl_relaxed allows us to have a standard set of register operations for the driver. While at it, simplify address computation using variables for register. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Felipe Balbi Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 37d71b715053..c8facb00bb53 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -55,6 +55,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) int err_src = 0; u32 std_err_main, err_reg, clear, masterid; void __iomem *base, *l3_targ_base; + void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; char *target_name, *master_name = "UN IDENTIFIED"; /* Get the Type of interrupt */ @@ -66,8 +67,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) * to determine the source */ base = l3->l3_base[i]; - err_reg = __raw_readl(base + l3_flagmux[i] + - + L3_FLAGMUX_REGERR0 + (inttype << 3)); + err_reg = readl_relaxed(base + l3_flagmux[i] + + L3_FLAGMUX_REGERR0 + (inttype << 3)); /* Get the corresponding error and analyse */ if (err_reg) { @@ -76,10 +77,14 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) /* Read the stderrlog_main_source from clk domain */ l3_targ_base = base + *(l3_targ[i] + err_src); - std_err_main = __raw_readl(l3_targ_base + - L3_TARG_STDERRLOG_MAIN); - masterid = __raw_readl(l3_targ_base + - L3_TARG_STDERRLOG_MSTADDR); + l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; + l3_targ_slvofslsb = l3_targ_base + + L3_TARG_STDERRLOG_SLVOFSLSB; + l3_targ_mstaddr = l3_targ_base + + L3_TARG_STDERRLOG_MSTADDR; + + std_err_main = readl_relaxed(l3_targ_stderr); + masterid = readl_relaxed(l3_targ_mstaddr); switch (std_err_main & CUSTOM_ERROR) { case STANDARD_ERROR: @@ -87,12 +92,10 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) l3_targ_inst_name[i][err_src]; WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n", target_name, - __raw_readl(l3_targ_base + - L3_TARG_STDERRLOG_SLVOFSLSB)); + readl_relaxed(l3_targ_slvofslsb)); /* clear the std error log*/ clear = std_err_main | CLEAR_STDERR_LOG; - writel(clear, l3_targ_base + - L3_TARG_STDERRLOG_MAIN); + writel_relaxed(clear, l3_targ_stderr); break; case CUSTOM_ERROR: @@ -107,8 +110,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) master_name, target_name); /* clear the std error log*/ clear = std_err_main | CLEAR_STDERR_LOG; - writel(clear, l3_targ_base + - L3_TARG_STDERRLOG_MAIN); + writel_relaxed(clear, l3_targ_stderr); break; default: From add6f74b9baf6676c4465b99967c972c464e59cc Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 11:24:42 -0500 Subject: [PATCH 038/101] bus: omap_l3_noc: un-obfuscate l3_targ address computation just simplify derefencing that is equivalent. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index c8facb00bb53..f7d3bf4f7284 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -76,7 +76,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) err_src = __ffs(err_reg); /* Read the stderrlog_main_source from clk domain */ - l3_targ_base = base + *(l3_targ[i] + err_src); + l3_targ_base = base + l3_targ[i][err_src]; l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB; From f0a6e654d8db2dfa3eb8b99380ad449d5e092c33 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 10:11:59 -0500 Subject: [PATCH 039/101] bus: omap_l3_noc: move L3 master data structure out Move the L3 master structure out of the static definition to enable reuse for other SoCs. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 31984cf949cc..059c707fdc84 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -33,6 +33,16 @@ #define NUM_OF_L3_MASTERS (sizeof(l3_masters)/sizeof(l3_masters[0])) +/** + * struct l3_masters_data - L3 Master information + * @id: ID of the L3 Master + * @name: master name + */ +struct l3_masters_data { + u32 id; + char *name; +}; + static u32 l3_flagmux[L3_MODULES] = { 0x500, 0x1000, @@ -80,10 +90,7 @@ static u32 l3_targ_inst_clk3[] = { 0x0 /* HOST CLK3 */ }; -static struct l3_masters_data { - u32 id; - char name[10]; -} l3_masters[] = { +static struct l3_masters_data l3_masters[] = { { 0x0 , "MPU"}, { 0x10, "CS_ADP"}, { 0x14, "xxx"}, From 3ae9af7c90f8113365cf2600797115ee35e42d0d Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 11:38:10 -0500 Subject: [PATCH 040/101] bus: omap_l3_noc: convert target information into a structure Currently the target instance information is organized indexed by bit field offset into multiple arrays. 1. We currently have offsets specific to each target associated with each clock domains are in seperate arrays: l3_targ_inst_clk1 l3_targ_inst_clk2 l3_targ_inst_clk3 2. Then they are organized per master index in l3_targ. 3. We have names in l3_targ_inst_name as an array to array of strings corresponding to the above with offsets. Simplify the same by defining a structure for information containing both target offset and name. this is then stored in arrays per domain and organized into an array indexed off domain. The array is still indexed based on bit field offset. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 9 ++- drivers/bus/omap_l3_noc.h | 129 +++++++++++++++----------------------- 2 files changed, 54 insertions(+), 84 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index f7d3bf4f7284..343f002a06f7 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -57,6 +57,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) void __iomem *base, *l3_targ_base; void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; char *target_name, *master_name = "UN IDENTIFIED"; + struct l3_target_data *l3_targ_inst; /* Get the Type of interrupt */ inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; @@ -74,9 +75,11 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) if (err_reg) { /* Identify the source from control status register */ err_src = __ffs(err_reg); + l3_targ_inst = &l3_targ[i][err_src]; + target_name = l3_targ_inst->name; + l3_targ_base = base + l3_targ_inst->offset; /* Read the stderrlog_main_source from clk domain */ - l3_targ_base = base + l3_targ[i][err_src]; l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB; @@ -88,8 +91,6 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) switch (std_err_main & CUSTOM_ERROR) { case STANDARD_ERROR: - target_name = - l3_targ_inst_name[i][err_src]; WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n", target_name, readl_relaxed(l3_targ_slvofslsb)); @@ -99,8 +100,6 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) break; case CUSTOM_ERROR: - target_name = - l3_targ_inst_name[i][err_src]; for (k = 0; k < NUM_OF_L3_MASTERS; k++) { if (masterid == l3_masters[k].id) master_name = diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 059c707fdc84..ae2878464efa 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -43,51 +43,62 @@ struct l3_masters_data { char *name; }; +/** + * struct l3_target_data - L3 Target information + * @offset: Offset from base for L3 Target + * @name: Target name + * + * Target information is organized indexed by bit field definitions. + */ +struct l3_target_data { + u32 offset; + char *name; +}; + static u32 l3_flagmux[L3_MODULES] = { 0x500, 0x1000, 0X0200 }; -/* L3 Target standard Error register offsets */ -static u32 l3_targ_inst_clk1[] = { - 0x100, /* DMM1 */ - 0x200, /* DMM2 */ - 0x300, /* ABE */ - 0x400, /* L4CFG */ - 0x600, /* CLK2 PWR DISC */ - 0x0, /* Host CLK1 */ - 0x900 /* L4 Wakeup */ +static struct l3_target_data l3_target_inst_data_clk1[] = { + {0x100, "DMM1",}, + {0x200, "DMM2",}, + {0x300, "ABE",}, + {0x400, "L4CFG",}, + {0x600, "CLK2PWRDISC",}, + {0x0, "HOSTCLK1",}, + {0x900, "L4WAKEUP",}, }; -static u32 l3_targ_inst_clk2[] = { - 0x500, /* CORTEX M3 */ - 0x300, /* DSS */ - 0x100, /* GPMC */ - 0x400, /* ISS */ - 0x700, /* IVAHD */ - 0xD00, /* missing in TRM corresponds to AES1*/ - 0x900, /* L4 PER0*/ - 0x200, /* OCMRAM */ - 0x100, /* missing in TRM corresponds to GPMC sERROR*/ - 0x600, /* SGX */ - 0x800, /* SL2 */ - 0x1600, /* C2C */ - 0x1100, /* missing in TRM corresponds PWR DISC CLK1*/ - 0xF00, /* missing in TRM corrsponds to SHA1*/ - 0xE00, /* missing in TRM corresponds to AES2*/ - 0xC00, /* L4 PER3 */ - 0xA00, /* L4 PER1*/ - 0xB00, /* L4 PER2*/ - 0x0, /* HOST CLK2 */ - 0x1800, /* CAL */ - 0x1700 /* LLI */ +static struct l3_target_data l3_target_inst_data_clk2[] = { + {0x500, "CORTEXM3",}, + {0x300, "DSS",}, + {0x100, "GPMC",}, + {0x400, "ISS",}, + {0x700, "IVAHD",}, + {0xD00, "AES1",}, + {0x900, "L4PER0",}, + {0x200, "OCMRAM",}, + {0x100, "GPMCsERROR",}, + {0x600, "SGX",}, + {0x800, "SL2",}, + {0x1600, "C2C",}, + {0x1100, "PWRDISCCLK1",}, + {0xF00, "SHA1",}, + {0xE00, "AES2",}, + {0xC00, "L4PER3",}, + {0xA00, "L4PER1",}, + {0xB00, "L4PER2",}, + {0x0, "HOSTCLK2",}, + {0x1800, "CAL",}, + {0x1700, "LLI",}, }; -static u32 l3_targ_inst_clk3[] = { - 0x0100 /* EMUSS */, - 0x0300, /* DEBUGSS_CT_TBR */ - 0x0 /* HOST CLK3 */ +static struct l3_target_data l3_target_inst_data_clk3[] = { + {0x0100, "EMUSS",}, + {0x0300, "DEBUG SOURCE",}, + {0x0, "HOST CLK3",}, }; static struct l3_masters_data l3_masters[] = { @@ -118,50 +129,10 @@ static struct l3_masters_data l3_masters[] = { { 0xC8, "USBHOSTFS"} }; -static char *l3_targ_inst_name[L3_MODULES][21] = { - { - "DMM1", - "DMM2", - "ABE", - "L4CFG", - "CLK2 PWR DISC", - "HOST CLK1", - "L4 WAKEUP" - }, - { - "CORTEX M3" , - "DSS ", - "GPMC ", - "ISS ", - "IVAHD ", - "AES1", - "L4 PER0", - "OCMRAM ", - "GPMC sERROR", - "SGX ", - "SL2 ", - "C2C ", - "PWR DISC CLK1", - "SHA1", - "AES2", - "L4 PER3", - "L4 PER1", - "L4 PER2", - "HOST CLK2", - "CAL", - "LLI" - }, - { - "EMUSS", - "DEBUG SOURCE", - "HOST CLK3" - }, -}; - -static u32 *l3_targ[L3_MODULES] = { - l3_targ_inst_clk1, - l3_targ_inst_clk2, - l3_targ_inst_clk3, +static struct l3_target_data *l3_targ[L3_MODULES] = { + l3_target_inst_data_clk1, + l3_target_inst_data_clk2, + l3_target_inst_data_clk3, }; struct omap_l3 { From 3340d739f8e1273abd408c59ad1843ea2ac35566 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 10 Apr 2014 11:31:33 -0500 Subject: [PATCH 041/101] bus: omap_l3_noc: Add support for discountinous flag mux input numbers On DRA7, unlike on OMAP4 and OMAP5, the flag mux input numbers used to indicate the source of errors are not continous. Have a way in the driver to catch these and WARN the user of the flag mux input thats either undocumented or wrong. In the similar vein, Timeout errors in AM43x can't be cleared per h/w team, neither does it have a STDERRLOG_MAIN to clear the error. Further, the mux bit offset might not even be indexed into our array of known mux input description, in which case we'd have a abort. So, define a static range check for bit description and any definition which has target_name set to NULL (the ones that are not populated or ones that are specifically marked in the case of discontinous input numbers), can handle the same gracefully. Upon occurance of error from such sources, mask it. Otherwise, we'd have an infinite interrupt source without any means to clear it. NOTE: follow on patch ensures that these masked bits are ignored. [nm@ti.com: rebase, squash and improve] Signed-off-by: Rajendra Nayak Signed-off-by: Afzal Mohammed Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 31 +++++++++++++++++++++++++++++++ drivers/bus/omap_l3_noc.h | 11 ++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 343f002a06f7..7743e86e88b1 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -75,10 +75,41 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) if (err_reg) { /* Identify the source from control status register */ err_src = __ffs(err_reg); + + /* We DONOT expect err_src to go out of bounds */ + BUG_ON(err_src > MAX_CLKDM_TARGETS); + l3_targ_inst = &l3_targ[i][err_src]; target_name = l3_targ_inst->name; l3_targ_base = base + l3_targ_inst->offset; + /* + * If we do not know of a register offset to decode + * and clear, then mask. + */ + if (target_name == L3_TARGET_NOT_SUPPORTED) { + u32 mask_val; + void __iomem *mask_reg; + + /* + * Certain plaforms may have "undocumented" + * status pending on boot.. So dont generate + * a severe warning here. + */ + dev_err(l3->dev, + "L3 %s error: target %d mod:%d %s\n", + inttype ? "debug" : "application", + err_src, i, "(unclearable)"); + + mask_reg = base + l3_flagmux[i] + + L3_FLAGMUX_MASK0 + (inttype << 3); + mask_val = readl_relaxed(mask_reg); + mask_val &= ~(1 << err_src); + writel_relaxed(mask_val, mask_reg); + + break; + } + /* Read the stderrlog_main_source from clk domain */ l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; l3_targ_slvofslsb = l3_targ_base + diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index ae2878464efa..66caeceaf123 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -30,6 +30,11 @@ #define L3_TARG_STDERRLOG_SLVOFSLSB 0x5c #define L3_TARG_STDERRLOG_MSTADDR 0x68 #define L3_FLAGMUX_REGERR0 0xc +#define L3_FLAGMUX_MASK0 0x8 + +#define L3_TARGET_NOT_SUPPORTED NULL + +#define MAX_CLKDM_TARGETS 31 #define NUM_OF_L3_MASTERS (sizeof(l3_masters)/sizeof(l3_masters[0])) @@ -61,7 +66,7 @@ static u32 l3_flagmux[L3_MODULES] = { 0X0200 }; -static struct l3_target_data l3_target_inst_data_clk1[] = { +static struct l3_target_data l3_target_inst_data_clk1[MAX_CLKDM_TARGETS] = { {0x100, "DMM1",}, {0x200, "DMM2",}, {0x300, "ABE",}, @@ -71,7 +76,7 @@ static struct l3_target_data l3_target_inst_data_clk1[] = { {0x900, "L4WAKEUP",}, }; -static struct l3_target_data l3_target_inst_data_clk2[] = { +static struct l3_target_data l3_target_inst_data_clk2[MAX_CLKDM_TARGETS] = { {0x500, "CORTEXM3",}, {0x300, "DSS",}, {0x100, "GPMC",}, @@ -95,7 +100,7 @@ static struct l3_target_data l3_target_inst_data_clk2[] = { {0x1700, "LLI",}, }; -static struct l3_target_data l3_target_inst_data_clk3[] = { +static struct l3_target_data l3_target_inst_data_clk3[MAX_CLKDM_TARGETS] = { {0x0100, "EMUSS",}, {0x0300, "DEBUG SOURCE",}, {0x0, "HOST CLK3",}, From 0659452dd2b2602058b80cfdcc673e98f2a67184 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Tue, 26 Nov 2013 07:38:23 -0600 Subject: [PATCH 042/101] bus: omap_l3_noc: use of_match_data to pick up SoC information DRA7xx SoC has the same l3-noc interconnect ip (as OMAP4 and OMAP5), but AM437x SoC has just 2 modules instead of 3 which other SoCs have. So, stop using direct access of array indices and use of->match data and simplify implementation to benefit future usage. While at it, rename a few very generic variables to make them omap specific. This helps us differentiate from DRA7 and AM43xx data in the future. NOTE: None of the platforms that use omap_l3_noc are non-device tree anymore. So, it is safe to assume OF match here. Signed-off-by: Sricharan R Signed-off-by: Rajendra Nayak [nm@ti.com: split, refactor and optimize logic] Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 55 +++++++++++++++++------------- drivers/bus/omap_l3_noc.h | 72 ++++++++++++++++++++++++++++----------- 2 files changed, 84 insertions(+), 43 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 7743e86e88b1..7e0a988ad579 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -14,12 +14,14 @@ * 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 "omap_l3_noc.h" @@ -58,17 +60,18 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; char *target_name, *master_name = "UN IDENTIFIED"; struct l3_target_data *l3_targ_inst; + struct l3_masters_data *master; /* Get the Type of interrupt */ inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; - for (i = 0; i < L3_MODULES; i++) { + for (i = 0; i < l3->num_modules; i++) { /* * Read the regerr register of the clock domain * to determine the source */ base = l3->l3_base[i]; - err_reg = readl_relaxed(base + l3_flagmux[i] + + err_reg = readl_relaxed(base + l3->l3_flagmux[i] + L3_FLAGMUX_REGERR0 + (inttype << 3)); /* Get the corresponding error and analyse */ @@ -79,7 +82,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) /* We DONOT expect err_src to go out of bounds */ BUG_ON(err_src > MAX_CLKDM_TARGETS); - l3_targ_inst = &l3_targ[i][err_src]; + l3_targ_inst = &l3->l3_targ[i][err_src]; target_name = l3_targ_inst->name; l3_targ_base = base + l3_targ_inst->offset; @@ -101,7 +104,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) inttype ? "debug" : "application", err_src, i, "(unclearable)"); - mask_reg = base + l3_flagmux[i] + + mask_reg = base + l3->l3_flagmux[i] + L3_FLAGMUX_MASK0 + (inttype << 3); mask_val = readl_relaxed(mask_reg); mask_val &= ~(1 << err_src); @@ -131,10 +134,12 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) break; case CUSTOM_ERROR: - for (k = 0; k < NUM_OF_L3_MASTERS; k++) { - if (masterid == l3_masters[k].id) - master_name = - l3_masters[k].name; + for (k = 0, master = l3->l3_masters; + k < l3->num_masters; k++, master++) { + if (masterid == master->id) { + master_name = master->name; + break; + } } WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n", master_name, target_name); @@ -154,20 +159,34 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) return IRQ_HANDLED; } +static const struct of_device_id l3_noc_match[] = { + {.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, l3_noc_match); + static int omap_l3_probe(struct platform_device *pdev) { + const struct of_device_id *of_id; static struct omap_l3 *l3; int ret, i; + of_id = of_match_device(l3_noc_match, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "OF data missing\n"); + return -EINVAL; + } + l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL); if (!l3) return -ENOMEM; + memcpy(l3, of_id->data, sizeof(*l3)); l3->dev = &pdev->dev; platform_set_drvdata(pdev, l3); /* Get mem resources */ - for (i = 0; i < L3_MODULES; i++) { + for (i = 0; i < l3->num_modules; i++) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, i); @@ -199,22 +218,12 @@ static int omap_l3_probe(struct platform_device *pdev) return ret; } -#if defined(CONFIG_OF) -static const struct of_device_id l3_noc_match[] = { - {.compatible = "ti,omap4-l3-noc", }, - {}, -}; -MODULE_DEVICE_TABLE(of, l3_noc_match); -#else -#define l3_noc_match NULL -#endif - static struct platform_driver omap_l3_driver = { .probe = omap_l3_probe, .driver = { .name = "omap_l3_noc", .owner = THIS_MODULE, - .of_match_table = l3_noc_match, + .of_match_table = of_match_ptr(l3_noc_match), }, }; diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 66caeceaf123..e60865fe5965 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -17,7 +17,9 @@ #ifndef __OMAP_L3_NOC_H #define __OMAP_L3_NOC_H -#define L3_MODULES 3 +#define OMAP_L3_MODULES 3 +#define MAX_L3_MODULES 3 + #define CLEAR_STDERR_LOG (1 << 31) #define CUSTOM_ERROR 0x2 #define STANDARD_ERROR 0x0 @@ -36,8 +38,6 @@ #define MAX_CLKDM_TARGETS 31 -#define NUM_OF_L3_MASTERS (sizeof(l3_masters)/sizeof(l3_masters[0])) - /** * struct l3_masters_data - L3 Master information * @id: ID of the L3 Master @@ -60,13 +60,47 @@ struct l3_target_data { char *name; }; -static u32 l3_flagmux[L3_MODULES] = { + +/** + * struct omap_l3 - Description of data relevant for L3 bus. + * @dev: device representing the bus (populated runtime) + * @l3_base: base addresses of modules (populated runtime) + * @l3_flag_mux: array containing offsets to flag mux per module + * offset from corresponding module base indexed per + * module. + * @num_modules: number of clock domains / modules. + * @l3_masters: array pointing to master data containing name and register + * offset for the master. + * @num_master: number of masters + * @l3_targ: array indexed by flagmux index (bit offset) pointing to the + * target data. unsupported ones are marked with + * L3_TARGET_NOT_SUPPORTED + * @debug_irq: irq number of the debug interrupt (populated runtime) + * @app_irq: irq number of the application interrupt (populated runtime) + */ +struct omap_l3 { + struct device *dev; + + void __iomem *l3_base[MAX_L3_MODULES]; + u32 *l3_flagmux; + int num_modules; + + struct l3_masters_data *l3_masters; + int num_masters; + + struct l3_target_data **l3_targ; + + int debug_irq; + int app_irq; +}; + +static u32 omap_l3_flagmux[OMAP_L3_MODULES] = { 0x500, 0x1000, 0X0200 }; -static struct l3_target_data l3_target_inst_data_clk1[MAX_CLKDM_TARGETS] = { +static struct l3_target_data omap_l3_target_data_clk1[MAX_CLKDM_TARGETS] = { {0x100, "DMM1",}, {0x200, "DMM2",}, {0x300, "ABE",}, @@ -76,7 +110,7 @@ static struct l3_target_data l3_target_inst_data_clk1[MAX_CLKDM_TARGETS] = { {0x900, "L4WAKEUP",}, }; -static struct l3_target_data l3_target_inst_data_clk2[MAX_CLKDM_TARGETS] = { +static struct l3_target_data omap_l3_target_data_clk2[MAX_CLKDM_TARGETS] = { {0x500, "CORTEXM3",}, {0x300, "DSS",}, {0x100, "GPMC",}, @@ -100,13 +134,13 @@ static struct l3_target_data l3_target_inst_data_clk2[MAX_CLKDM_TARGETS] = { {0x1700, "LLI",}, }; -static struct l3_target_data l3_target_inst_data_clk3[MAX_CLKDM_TARGETS] = { +static struct l3_target_data omap_l3_target_data_clk3[MAX_CLKDM_TARGETS] = { {0x0100, "EMUSS",}, {0x0300, "DEBUG SOURCE",}, {0x0, "HOST CLK3",}, }; -static struct l3_masters_data l3_masters[] = { +static struct l3_masters_data omap_l3_masters[] = { { 0x0 , "MPU"}, { 0x10, "CS_ADP"}, { 0x14, "xxx"}, @@ -134,20 +168,18 @@ static struct l3_masters_data l3_masters[] = { { 0xC8, "USBHOSTFS"} }; -static struct l3_target_data *l3_targ[L3_MODULES] = { - l3_target_inst_data_clk1, - l3_target_inst_data_clk2, - l3_target_inst_data_clk3, +static struct l3_target_data *omap_l3_targ[OMAP_L3_MODULES] = { + omap_l3_target_data_clk1, + omap_l3_target_data_clk2, + omap_l3_target_data_clk3, }; -struct omap_l3 { - struct device *dev; - - /* memory base */ - void __iomem *l3_base[L3_MODULES]; - - int debug_irq; - int app_irq; +static const struct omap_l3 omap_l3_data = { + .l3_flagmux = omap_l3_flagmux, + .num_modules = OMAP_L3_MODULES, + .l3_masters = omap_l3_masters, + .num_masters = ARRAY_SIZE(omap_l3_masters), + .l3_targ = omap_l3_targ, }; #endif /* __OMAP_L3_NOC_H */ From 97708c08c9955306742872ff7f2e47faec864ee7 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 14 Apr 2014 09:57:50 -0500 Subject: [PATCH 043/101] bus: omap_l3_noc: convert flagmux information into a structure This allows us to encompass target information and flag mux offset that points to the target information into a singular structure. This saves us the need to look up two different arrays indexed by module ID for information. This allows us to reduce the static target information allocation to just the ones that are documented. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 16 ++++++--- drivers/bus/omap_l3_noc.h | 70 +++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 7e0a988ad579..9524452ee12c 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -60,6 +60,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; char *target_name, *master_name = "UN IDENTIFIED"; struct l3_target_data *l3_targ_inst; + struct l3_flagmux_data *flag_mux; struct l3_masters_data *master; /* Get the Type of interrupt */ @@ -71,7 +72,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) * to determine the source */ base = l3->l3_base[i]; - err_reg = readl_relaxed(base + l3->l3_flagmux[i] + + flag_mux = l3->l3_flagmux[i]; + err_reg = readl_relaxed(base + flag_mux->offset + L3_FLAGMUX_REGERR0 + (inttype << 3)); /* Get the corresponding error and analyse */ @@ -82,9 +84,13 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) /* We DONOT expect err_src to go out of bounds */ BUG_ON(err_src > MAX_CLKDM_TARGETS); - l3_targ_inst = &l3->l3_targ[i][err_src]; - target_name = l3_targ_inst->name; - l3_targ_base = base + l3_targ_inst->offset; + if (err_src < flag_mux->num_targ_data) { + l3_targ_inst = &flag_mux->l3_targ[err_src]; + target_name = l3_targ_inst->name; + l3_targ_base = base + l3_targ_inst->offset; + } else { + target_name = L3_TARGET_NOT_SUPPORTED; + } /* * If we do not know of a register offset to decode @@ -104,7 +110,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) inttype ? "debug" : "application", err_src, i, "(unclearable)"); - mask_reg = base + l3->l3_flagmux[i] + + mask_reg = base + flag_mux->offset + L3_FLAGMUX_MASK0 + (inttype << 3); mask_val = readl_relaxed(mask_reg); mask_val &= ~(1 << err_src); diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index e60865fe5965..64869fe656e5 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -17,8 +17,8 @@ #ifndef __OMAP_L3_NOC_H #define __OMAP_L3_NOC_H -#define OMAP_L3_MODULES 3 #define MAX_L3_MODULES 3 +#define MAX_CLKDM_TARGETS 31 #define CLEAR_STDERR_LOG (1 << 31) #define CUSTOM_ERROR 0x2 @@ -36,8 +36,6 @@ #define L3_TARGET_NOT_SUPPORTED NULL -#define MAX_CLKDM_TARGETS 31 - /** * struct l3_masters_data - L3 Master information * @id: ID of the L3 Master @@ -60,21 +58,32 @@ struct l3_target_data { char *name; }; +/** + * struct l3_flagmux_data - Flag Mux information + * @offset: offset from base for flagmux register + * @l3_targ: array indexed by flagmux index (bit offset) pointing to the + * target data. unsupported ones are marked with + * L3_TARGET_NOT_SUPPORTED + * @num_targ_data: number of entries in target data + */ +struct l3_flagmux_data { + u32 offset; + struct l3_target_data *l3_targ; + u8 num_targ_data; +}; + /** * struct omap_l3 - Description of data relevant for L3 bus. * @dev: device representing the bus (populated runtime) * @l3_base: base addresses of modules (populated runtime) - * @l3_flag_mux: array containing offsets to flag mux per module + * @l3_flag_mux: array containing flag mux data per module * offset from corresponding module base indexed per * module. * @num_modules: number of clock domains / modules. * @l3_masters: array pointing to master data containing name and register * offset for the master. * @num_master: number of masters - * @l3_targ: array indexed by flagmux index (bit offset) pointing to the - * target data. unsupported ones are marked with - * L3_TARGET_NOT_SUPPORTED * @debug_irq: irq number of the debug interrupt (populated runtime) * @app_irq: irq number of the application interrupt (populated runtime) */ @@ -82,25 +91,17 @@ struct omap_l3 { struct device *dev; void __iomem *l3_base[MAX_L3_MODULES]; - u32 *l3_flagmux; + struct l3_flagmux_data **l3_flagmux; int num_modules; struct l3_masters_data *l3_masters; int num_masters; - struct l3_target_data **l3_targ; - int debug_irq; int app_irq; }; -static u32 omap_l3_flagmux[OMAP_L3_MODULES] = { - 0x500, - 0x1000, - 0X0200 -}; - -static struct l3_target_data omap_l3_target_data_clk1[MAX_CLKDM_TARGETS] = { +static struct l3_target_data omap_l3_target_data_clk1[] = { {0x100, "DMM1",}, {0x200, "DMM2",}, {0x300, "ABE",}, @@ -110,7 +111,14 @@ static struct l3_target_data omap_l3_target_data_clk1[MAX_CLKDM_TARGETS] = { {0x900, "L4WAKEUP",}, }; -static struct l3_target_data omap_l3_target_data_clk2[MAX_CLKDM_TARGETS] = { +static struct l3_flagmux_data omap_l3_flagmux_clk1 = { + .offset = 0x500, + .l3_targ = omap_l3_target_data_clk1, + .num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk1), +}; + + +static struct l3_target_data omap_l3_target_data_clk2[] = { {0x500, "CORTEXM3",}, {0x300, "DSS",}, {0x100, "GPMC",}, @@ -134,12 +142,25 @@ static struct l3_target_data omap_l3_target_data_clk2[MAX_CLKDM_TARGETS] = { {0x1700, "LLI",}, }; -static struct l3_target_data omap_l3_target_data_clk3[MAX_CLKDM_TARGETS] = { +static struct l3_flagmux_data omap_l3_flagmux_clk2 = { + .offset = 0x1000, + .l3_targ = omap_l3_target_data_clk2, + .num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk2), +}; + + +static struct l3_target_data omap_l3_target_data_clk3[] = { {0x0100, "EMUSS",}, {0x0300, "DEBUG SOURCE",}, {0x0, "HOST CLK3",}, }; +static struct l3_flagmux_data omap_l3_flagmux_clk3 = { + .offset = 0x0200, + .l3_targ = omap_l3_target_data_clk3, + .num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk3), +}; + static struct l3_masters_data omap_l3_masters[] = { { 0x0 , "MPU"}, { 0x10, "CS_ADP"}, @@ -168,18 +189,17 @@ static struct l3_masters_data omap_l3_masters[] = { { 0xC8, "USBHOSTFS"} }; -static struct l3_target_data *omap_l3_targ[OMAP_L3_MODULES] = { - omap_l3_target_data_clk1, - omap_l3_target_data_clk2, - omap_l3_target_data_clk3, +static struct l3_flagmux_data *omap_l3_flagmux[] = { + &omap_l3_flagmux_clk1, + &omap_l3_flagmux_clk2, + &omap_l3_flagmux_clk3, }; static const struct omap_l3 omap_l3_data = { .l3_flagmux = omap_l3_flagmux, - .num_modules = OMAP_L3_MODULES, + .num_modules = ARRAY_SIZE(omap_l3_flagmux), .l3_masters = omap_l3_masters, .num_masters = ARRAY_SIZE(omap_l3_masters), - .l3_targ = omap_l3_targ, }; #endif /* __OMAP_L3_NOC_H */ From d4d8819e205854cc102e366f30b8eadd60a58e97 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 16 Apr 2014 11:01:02 -0500 Subject: [PATCH 044/101] bus: omap_l3_noc: fix masterid detection As per Documentation (OMAP4+), then masterid is infact encoded as follows: "L3_TARG_STDERRLOG_MSTADDR[7:0] STDERRLOG_MSTADDR stores the NTTP master address. The master address is the concatenation of Prefix & Initiator ConnID. It is defined on 8 bits. The 6 MSBs are used to distinguish the different initiators." So, when we matchup currently with the master ID list, we never get a proper match other than when MPU is the master (thanks to 0). Now, on other platforms such as AM437x, this tends to be bits[5:0]. Fix this by using the relevant 6MSBits to identify the master ID for standard and custom errors. Reported-by: Darren Etheridge Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 6 +++++- drivers/bus/omap_l3_noc.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 9524452ee12c..8a1926daacd7 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -127,7 +127,11 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) L3_TARG_STDERRLOG_MSTADDR; std_err_main = readl_relaxed(l3_targ_stderr); - masterid = readl_relaxed(l3_targ_mstaddr); + + /* STDERRLOG_MSTADDR Stores the NTTP master address. */ + masterid = (readl_relaxed(l3_targ_mstaddr) & + l3->mst_addr_mask) >> + __ffs(l3->mst_addr_mask); switch (std_err_main & CUSTOM_ERROR) { case STANDARD_ERROR: diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 64869fe656e5..ba8692444665 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -84,6 +84,7 @@ struct l3_flagmux_data { * @l3_masters: array pointing to master data containing name and register * offset for the master. * @num_master: number of masters + * @mst_addr_mask: Mask representing MSTADDR information of NTTP packet * @debug_irq: irq number of the debug interrupt (populated runtime) * @app_irq: irq number of the application interrupt (populated runtime) */ @@ -96,6 +97,7 @@ struct omap_l3 { struct l3_masters_data *l3_masters; int num_masters; + u32 mst_addr_mask; int debug_irq; int app_irq; @@ -200,6 +202,8 @@ static const struct omap_l3 omap_l3_data = { .num_modules = ARRAY_SIZE(omap_l3_flagmux), .l3_masters = omap_l3_masters, .num_masters = ARRAY_SIZE(omap_l3_masters), + /* The 6 MSBs of register field used to distinguish initiator */ + .mst_addr_mask = 0xFC, }; #endif /* __OMAP_L3_NOC_H */ From c98aa7aaa24b7687a170b93c4bf3111a6d166069 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 12:24:56 -0500 Subject: [PATCH 045/101] bus: omap_l3_noc: make error reporting and handling common The logic between handling CUSTOM_ERROR and STANDARD_ERROR is just the reporting style. So make it generic, simplify and standardize the reporting with both master and target information printed to log. Handle the register address difference for master code for standard error and custom error as well. While at it, fix a minor indentation error. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 80 ++++++++++++++++++++++++--------------- drivers/bus/omap_l3_noc.h | 3 +- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 8a1926daacd7..42e411457494 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -62,6 +62,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) struct l3_target_data *l3_targ_inst; struct l3_flagmux_data *flag_mux; struct l3_masters_data *master; + char *err_description; + char err_string[30] = { 0 }; /* Get the Type of interrupt */ inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; @@ -78,6 +80,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) /* Get the corresponding error and analyse */ if (err_reg) { + bool std_err = true; + /* Identify the source from control status register */ err_src = __ffs(err_reg); @@ -123,47 +127,61 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB; - l3_targ_mstaddr = l3_targ_base + - L3_TARG_STDERRLOG_MSTADDR; std_err_main = readl_relaxed(l3_targ_stderr); + switch (std_err_main & CUSTOM_ERROR) { + case STANDARD_ERROR: + err_description = "Standard"; + snprintf(err_string, sizeof(err_string), + ": At Address: 0x%08X ", + readl_relaxed(l3_targ_slvofslsb)); + + l3_targ_mstaddr = l3_targ_base + + L3_TARG_STDERRLOG_MSTADDR; + break; + + case CUSTOM_ERROR: + err_description = "Custom"; + + l3_targ_mstaddr = l3_targ_base + + L3_TARG_STDERRLOG_CINFO_MSTADDR; + break; + + default: + std_err = false; + /* Nothing to be handled here as of now */ + break; + } + + if (!std_err) + break; + /* STDERRLOG_MSTADDR Stores the NTTP master address. */ masterid = (readl_relaxed(l3_targ_mstaddr) & l3->mst_addr_mask) >> __ffs(l3->mst_addr_mask); - switch (std_err_main & CUSTOM_ERROR) { - case STANDARD_ERROR: - WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n", - target_name, - readl_relaxed(l3_targ_slvofslsb)); - /* clear the std error log*/ - clear = std_err_main | CLEAR_STDERR_LOG; - writel_relaxed(clear, l3_targ_stderr); - break; - - case CUSTOM_ERROR: - for (k = 0, master = l3->l3_masters; - k < l3->num_masters; k++, master++) { - if (masterid == master->id) { - master_name = master->name; - break; - } + for (k = 0, master = l3->l3_masters; + k < l3->num_masters; k++, master++) { + if (masterid == master->id) { + master_name = master->name; + break; } - WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n", - master_name, target_name); - /* clear the std error log*/ - clear = std_err_main | CLEAR_STDERR_LOG; - writel_relaxed(clear, l3_targ_stderr); - break; - - default: - /* Nothing to be handled here as of now */ - break; } - /* Error found so break the for loop */ - break; + + WARN(true, + "%s:L3 %s Error: MASTER %s TARGET %s%s\n", + dev_name(l3->dev), + err_description, + master_name, target_name, + err_string); + /* clear the std error log*/ + clear = std_err_main | CLEAR_STDERR_LOG; + writel_relaxed(clear, l3_targ_stderr); + + /* Error found so break the for loop */ + break; } } return IRQ_HANDLED; diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index ba8692444665..ea2f51c984f1 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -29,8 +29,9 @@ /* L3 TARG register offsets */ #define L3_TARG_STDERRLOG_MAIN 0x48 +#define L3_TARG_STDERRLOG_MSTADDR 0x50 #define L3_TARG_STDERRLOG_SLVOFSLSB 0x5c -#define L3_TARG_STDERRLOG_MSTADDR 0x68 +#define L3_TARG_STDERRLOG_CINFO_MSTADDR 0x68 #define L3_FLAGMUX_REGERR0 0xc #define L3_FLAGMUX_MASK0 0x8 From e4be3f3a040432398225d3634d44fc21f4807b7a Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 17 Apr 2014 12:33:50 -0500 Subject: [PATCH 046/101] bus: omap_l3_noc: improve readability by using helper for slave event parsing Current interrupt handler does the first level parse to identify the slave and then handles the slave even identification, reporting and clearing of event as well. It is hence logical to split the handler into two where the primary handler just parses the flagmux till it identifies a slave and the slave handling, reporting and clearing is done in a helper function. While at it update the documentation in kerneldoc style. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 211 ++++++++++++++++++++------------------ 1 file changed, 113 insertions(+), 98 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 42e411457494..0691e6d9c1e4 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -26,14 +26,20 @@ #include "omap_l3_noc.h" -/* - * Interrupt Handler for L3 error detection. - * 1) Identify the L3 clockdomain partition to which the error belongs to. - * 2) Identify the slave where the error information is logged - * 3) Print the logged information. - * 4) Add dump stack to provide kernel trace. +/** + * l3_handle_target() - Handle Target specific parse and reporting + * @l3: pointer to l3 struct + * @base: base address of clkdm + * @flag_mux: flagmux corresponding to the event + * @err_src: error source index of the slave (target) * - * Two Types of errors : + * This does the second part of the error interrupt handling: + * 3) Parse in the slave information + * 4) Print the logged information. + * 5) Add dump stack to provide kernel trace. + * 6) Clear the source if known. + * + * This handles two types of errors: * 1) Custom errors in L3 : * Target like DMM/FW/EMIF generates SRESP=ERR error * 2) Standard L3 error: @@ -49,22 +55,107 @@ * can be trapped as well. But the trapping is implemented as part * secure software and hence need not be implemented here. */ -static irqreturn_t l3_interrupt_handler(int irq, void *_l3) +static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, + struct l3_flagmux_data *flag_mux, int err_src) { - - struct omap_l3 *l3 = _l3; - int inttype, i, k; - int err_src = 0; - u32 std_err_main, err_reg, clear, masterid; - void __iomem *base, *l3_targ_base; + int k; + u32 std_err_main, clear, masterid; + void __iomem *l3_targ_base; void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; - char *target_name, *master_name = "UN IDENTIFIED"; struct l3_target_data *l3_targ_inst; - struct l3_flagmux_data *flag_mux; struct l3_masters_data *master; + char *target_name, *master_name = "UN IDENTIFIED"; char *err_description; char err_string[30] = { 0 }; + /* We DONOT expect err_src to go out of bounds */ + BUG_ON(err_src > MAX_CLKDM_TARGETS); + + if (err_src < flag_mux->num_targ_data) { + l3_targ_inst = &flag_mux->l3_targ[err_src]; + target_name = l3_targ_inst->name; + l3_targ_base = base + l3_targ_inst->offset; + } else { + target_name = L3_TARGET_NOT_SUPPORTED; + } + + if (target_name == L3_TARGET_NOT_SUPPORTED) + return -ENODEV; + + /* Read the stderrlog_main_source from clk domain */ + l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; + l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB; + + std_err_main = readl_relaxed(l3_targ_stderr); + + switch (std_err_main & CUSTOM_ERROR) { + case STANDARD_ERROR: + err_description = "Standard"; + snprintf(err_string, sizeof(err_string), + ": At Address: 0x%08X ", + readl_relaxed(l3_targ_slvofslsb)); + + l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_MSTADDR; + break; + + case CUSTOM_ERROR: + err_description = "Custom"; + + l3_targ_mstaddr = l3_targ_base + + L3_TARG_STDERRLOG_CINFO_MSTADDR; + break; + + default: + /* Nothing to be handled here as of now */ + return 0; + } + + /* STDERRLOG_MSTADDR Stores the NTTP master address. */ + masterid = (readl_relaxed(l3_targ_mstaddr) & + l3->mst_addr_mask) >> __ffs(l3->mst_addr_mask); + + for (k = 0, master = l3->l3_masters; k < l3->num_masters; + k++, master++) { + if (masterid == master->id) { + master_name = master->name; + break; + } + } + + WARN(true, + "%s:L3 %s Error: MASTER %s TARGET %s%s\n", + dev_name(l3->dev), + err_description, + master_name, target_name, + err_string); + + /* clear the std error log*/ + clear = std_err_main | CLEAR_STDERR_LOG; + writel_relaxed(clear, l3_targ_stderr); + + return 0; +} + +/** + * l3_interrupt_handler() - interrupt handler for l3 events + * @irq: irq number + * @_l3: pointer to l3 structure + * + * Interrupt Handler for L3 error detection. + * 1) Identify the L3 clockdomain partition to which the error belongs to. + * 2) Identify the slave where the error information is logged + * ... handle the slave event.. + * 7) if the slave is unknown, mask out the slave. + */ +static irqreturn_t l3_interrupt_handler(int irq, void *_l3) +{ + struct omap_l3 *l3 = _l3; + int inttype, i, ret; + int err_src = 0; + u32 err_reg, mask_val; + void __iomem *base, *mask_reg; + struct l3_flagmux_data *flag_mux; + /* Get the Type of interrupt */ inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; @@ -80,35 +171,18 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) /* Get the corresponding error and analyse */ if (err_reg) { - bool std_err = true; - /* Identify the source from control status register */ err_src = __ffs(err_reg); - /* We DONOT expect err_src to go out of bounds */ - BUG_ON(err_src > MAX_CLKDM_TARGETS); - - if (err_src < flag_mux->num_targ_data) { - l3_targ_inst = &flag_mux->l3_targ[err_src]; - target_name = l3_targ_inst->name; - l3_targ_base = base + l3_targ_inst->offset; - } else { - target_name = L3_TARGET_NOT_SUPPORTED; - } + ret = l3_handle_target(l3, base, flag_mux, err_src); /* - * If we do not know of a register offset to decode - * and clear, then mask. + * Certain plaforms may have "undocumented" status + * pending on boot. So dont generate a severe warning + * here. Just mask it off to prevent the error from + * reoccuring and locking up the system. */ - if (target_name == L3_TARGET_NOT_SUPPORTED) { - u32 mask_val; - void __iomem *mask_reg; - - /* - * Certain plaforms may have "undocumented" - * status pending on boot.. So dont generate - * a severe warning here. - */ + if (ret) { dev_err(l3->dev, "L3 %s error: target %d mod:%d %s\n", inttype ? "debug" : "application", @@ -119,67 +193,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) mask_val = readl_relaxed(mask_reg); mask_val &= ~(1 << err_src); writel_relaxed(mask_val, mask_reg); - - break; } - /* Read the stderrlog_main_source from clk domain */ - l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; - l3_targ_slvofslsb = l3_targ_base + - L3_TARG_STDERRLOG_SLVOFSLSB; - - std_err_main = readl_relaxed(l3_targ_stderr); - - switch (std_err_main & CUSTOM_ERROR) { - case STANDARD_ERROR: - err_description = "Standard"; - snprintf(err_string, sizeof(err_string), - ": At Address: 0x%08X ", - readl_relaxed(l3_targ_slvofslsb)); - - l3_targ_mstaddr = l3_targ_base + - L3_TARG_STDERRLOG_MSTADDR; - break; - - case CUSTOM_ERROR: - err_description = "Custom"; - - l3_targ_mstaddr = l3_targ_base + - L3_TARG_STDERRLOG_CINFO_MSTADDR; - break; - - default: - std_err = false; - /* Nothing to be handled here as of now */ - break; - } - - if (!std_err) - break; - - /* STDERRLOG_MSTADDR Stores the NTTP master address. */ - masterid = (readl_relaxed(l3_targ_mstaddr) & - l3->mst_addr_mask) >> - __ffs(l3->mst_addr_mask); - - for (k = 0, master = l3->l3_masters; - k < l3->num_masters; k++, master++) { - if (masterid == master->id) { - master_name = master->name; - break; - } - } - - WARN(true, - "%s:L3 %s Error: MASTER %s TARGET %s%s\n", - dev_name(l3->dev), - err_description, - master_name, target_name, - err_string); - /* clear the std error log*/ - clear = std_err_main | CLEAR_STDERR_LOG; - writel_relaxed(clear, l3_targ_stderr); - /* Error found so break the for loop */ break; } From 2100b595b756db29a0b71de49c3bf73ae76c679b Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Fri, 25 Apr 2014 17:38:11 -0500 Subject: [PATCH 047/101] bus: omap_l3_noc: ignore masked out unclearable targets Errors that cannot be cleared (determined by reading REGERR register) are currently handled by masking it. Documentation states that REGERR "Checks which application/debug error sources are active" - it does not indicate that this is "interrupt status" - masked out status represented eventually in the irq line to MPU. For example: Lets say module 0 bit 8(0x100) was unclearable, we do the mask it from generating further errors. However in the following cases: a) bit 9 of Module 0 OR b) any bit of Module 1+ occur, the interrupt handler wrongly assumes that the raw interrupt status of module 0 bit 8 is the root cause of the interrupt, and returns. This causes unhandled interrupt and resultant infinite interrupts. Fix this scenario by storing the events we masked out and masking raw status with masked ones before identifying and handling the error. Reported-by: Vaibhav Hiremath Signed-off-by: Afzal Mohammed Tested-by: Vaibhav Hiremath Signed-off-by: Sekhar Nori Signed-off-by: Nishanth Menon Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 9 +++++++++ drivers/bus/omap_l3_noc.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 0691e6d9c1e4..00e4fed4a39b 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -169,6 +169,9 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) err_reg = readl_relaxed(base + flag_mux->offset + L3_FLAGMUX_REGERR0 + (inttype << 3)); + err_reg &= ~(inttype ? flag_mux->mask_app_bits : + flag_mux->mask_dbg_bits); + /* Get the corresponding error and analyse */ if (err_reg) { /* Identify the source from control status register */ @@ -193,6 +196,12 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) mask_val = readl_relaxed(mask_reg); mask_val &= ~(1 << err_src); writel_relaxed(mask_val, mask_reg); + + /* Mark these bits as to be ignored */ + if (inttype) + flag_mux->mask_app_bits |= 1 << err_src; + else + flag_mux->mask_dbg_bits |= 1 << err_src; } /* Error found so break the for loop */ diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index ea2f51c984f1..4e18307470f6 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -66,11 +66,15 @@ struct l3_target_data { * target data. unsupported ones are marked with * L3_TARGET_NOT_SUPPORTED * @num_targ_data: number of entries in target data + * @mask_app_bits: ignore these from raw application irq status + * @mask_dbg_bits: ignore these from raw debug irq status */ struct l3_flagmux_data { u32 offset; struct l3_target_data *l3_targ; u8 num_targ_data; + u32 mask_app_bits; + u32 mask_dbg_bits; }; From 7f9de02d603c439890c4c94631a326c73e2b5b4c Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 16 Apr 2014 15:47:28 -0500 Subject: [PATCH 048/101] bus: omap_l3_noc: add information about the type of operation Today we get error such as L3 Custom Error: MASTER MPU TARGET L4PER2 But since the actual instruction triggerring the error Vs the point at which we report error may not be aligned, it makes sense to try and provide additional information - example the type of operation that was attempted to being performed can help narrow the debug down further. This helps provide log such as: L3 Custom Error: MASTER MPU TARGET L4PER2 (Read) Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 9 ++++++++- drivers/bus/omap_l3_noc.h | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 00e4fed4a39b..9d021d002bec 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -60,8 +60,10 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, { int k; u32 std_err_main, clear, masterid; + u8 op_code; void __iomem *l3_targ_base; void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; + void __iomem *l3_targ_hdr; struct l3_target_data *l3_targ_inst; struct l3_masters_data *master; char *target_name, *master_name = "UN IDENTIFIED"; @@ -96,6 +98,7 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, readl_relaxed(l3_targ_slvofslsb)); l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_MSTADDR; + l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_HDR; break; case CUSTOM_ERROR: @@ -103,6 +106,7 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_MSTADDR; + l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_OPCODE; break; default: @@ -122,11 +126,14 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, } } + op_code = readl_relaxed(l3_targ_hdr) & 0x7; + WARN(true, - "%s:L3 %s Error: MASTER %s TARGET %s%s\n", + "%s:L3 %s Error: MASTER %s TARGET %s (%s)%s\n", dev_name(l3->dev), err_description, master_name, target_name, + l3_transaction_type[op_code], err_string); /* clear the std error log*/ diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 4e18307470f6..6670fd98ce7c 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -29,14 +29,27 @@ /* L3 TARG register offsets */ #define L3_TARG_STDERRLOG_MAIN 0x48 +#define L3_TARG_STDERRLOG_HDR 0x4c #define L3_TARG_STDERRLOG_MSTADDR 0x50 #define L3_TARG_STDERRLOG_SLVOFSLSB 0x5c #define L3_TARG_STDERRLOG_CINFO_MSTADDR 0x68 +#define L3_TARG_STDERRLOG_CINFO_OPCODE 0x6c #define L3_FLAGMUX_REGERR0 0xc #define L3_FLAGMUX_MASK0 0x8 #define L3_TARGET_NOT_SUPPORTED NULL +static const char * const l3_transaction_type[] = { + /* 0 0 0 */ "Idle", + /* 0 0 1 */ "Write", + /* 0 1 0 */ "Read", + /* 0 1 1 */ "ReadEx", + /* 1 0 0 */ "Read Link", + /* 1 0 1 */ "Write Non-Posted", + /* 1 1 0 */ "Write Conditional", + /* 1 1 1 */ "Write Broadcast", +}; + /** * struct l3_masters_data - L3 Master information * @id: ID of the L3 Master From cf52b2ecd719ca7acb19c0fd74bcfcce9dc6a362 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 16 Apr 2014 17:23:33 -0500 Subject: [PATCH 049/101] bus: omap_l3_noc: Add information about the context of operation L3 error may be triggered using Debug interface (example JTAG) or due to other errors, for example an opcode fetch (due to function pointer or stack corruption) or a data access (due to some other failure). NOC registers contain additional information to help aid debug information. With this, we can enhance the error information to more detailed form: " L3 Custom Error: MASTER MPU TARGET L4PER2 (Read): Data Access in User mode during Functional access " Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 18 ++++++++++++++---- drivers/bus/omap_l3_noc.h | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 9d021d002bec..08344b03fda6 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -60,15 +60,16 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, { int k; u32 std_err_main, clear, masterid; - u8 op_code; + u8 op_code, m_req_info; void __iomem *l3_targ_base; void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; - void __iomem *l3_targ_hdr; + void __iomem *l3_targ_hdr, *l3_targ_info; struct l3_target_data *l3_targ_inst; struct l3_masters_data *master; char *target_name, *master_name = "UN IDENTIFIED"; char *err_description; char err_string[30] = { 0 }; + char info_string[60] = { 0 }; /* We DONOT expect err_src to go out of bounds */ BUG_ON(err_src > MAX_CLKDM_TARGETS); @@ -99,6 +100,7 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_MSTADDR; l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_HDR; + l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_INFO; break; case CUSTOM_ERROR: @@ -107,6 +109,7 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_MSTADDR; l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_OPCODE; + l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_CINFO_INFO; break; default: @@ -128,13 +131,20 @@ static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, op_code = readl_relaxed(l3_targ_hdr) & 0x7; + m_req_info = readl_relaxed(l3_targ_info) & 0xF; + snprintf(info_string, sizeof(info_string), + ": %s in %s mode during %s access", + (m_req_info & BIT(0)) ? "Opcode Fetch" : "Data Access", + (m_req_info & BIT(1)) ? "Supervisor" : "User", + (m_req_info & BIT(3)) ? "Debug" : "Functional"); + WARN(true, - "%s:L3 %s Error: MASTER %s TARGET %s (%s)%s\n", + "%s:L3 %s Error: MASTER %s TARGET %s (%s)%s%s\n", dev_name(l3->dev), err_description, master_name, target_name, l3_transaction_type[op_code], - err_string); + err_string, info_string); /* clear the std error log*/ clear = std_err_main | CLEAR_STDERR_LOG; diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 6670fd98ce7c..36dc48b3dc00 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -31,7 +31,9 @@ #define L3_TARG_STDERRLOG_MAIN 0x48 #define L3_TARG_STDERRLOG_HDR 0x4c #define L3_TARG_STDERRLOG_MSTADDR 0x50 +#define L3_TARG_STDERRLOG_INFO 0x58 #define L3_TARG_STDERRLOG_SLVOFSLSB 0x5c +#define L3_TARG_STDERRLOG_CINFO_INFO 0x64 #define L3_TARG_STDERRLOG_CINFO_MSTADDR 0x68 #define L3_TARG_STDERRLOG_CINFO_OPCODE 0x6c #define L3_FLAGMUX_REGERR0 0xc From f33ddf745cbcd4145fcb2f8239f5dbba089fb8ff Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Fri, 11 Apr 2014 14:37:03 -0500 Subject: [PATCH 050/101] bus: omap_l3_noc: introduce concept of submodule While OMAP4 and OMAP5 had 3 separate clock domains, DRA7 has only 2 and the first one then is internally divided into 2 sub clock domains. To better represent this in the driver, we use the concept of submodule. The address defintions in the devicetree is as per the high level clock domain(module) base, the sub clockdomain/subdomain which shares the same register space of a clockdomain is marked in the SoC data as L3_BASE_IS_SUBMODULE. L3_BASE_IS_SUBMODULE is used as an indication that it's base address is the same as the parent module and offsets are considered from the same base address as they are usually intermingled. Other than the base address, the submodule is same as a module as it is functionally so. Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- drivers/bus/omap_l3_noc.c | 15 +++++++++++---- drivers/bus/omap_l3_noc.h | 6 +++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 08344b03fda6..0eba07ac6008 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -238,7 +238,7 @@ static int omap_l3_probe(struct platform_device *pdev) { const struct of_device_id *of_id; static struct omap_l3 *l3; - int ret, i; + int ret, i, res_idx; of_id = of_match_device(l3_noc_match, &pdev->dev); if (!of_id) { @@ -255,15 +255,22 @@ static int omap_l3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, l3); /* Get mem resources */ - for (i = 0; i < l3->num_modules; i++) { - struct resource *res = platform_get_resource(pdev, - IORESOURCE_MEM, i); + for (i = 0, res_idx = 0; i < l3->num_modules; i++) { + struct resource *res; + if (l3->l3_base[i] == L3_BASE_IS_SUBMODULE) { + /* First entry cannot be submodule */ + BUG_ON(i == 0); + l3->l3_base[i] = l3->l3_base[i - 1]; + continue; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, res_idx); l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(l3->l3_base[i])) { dev_err(l3->dev, "ioremap %d failed\n", i); return PTR_ERR(l3->l3_base[i]); } + res_idx++; } /* diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 36dc48b3dc00..aced4c546783 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -41,6 +41,8 @@ #define L3_TARGET_NOT_SUPPORTED NULL +#define L3_BASE_IS_SUBMODULE ((void __iomem *)(1 << 0)) + static const char * const l3_transaction_type[] = { /* 0 0 0 */ "Idle", /* 0 0 1 */ "Write", @@ -96,7 +98,9 @@ struct l3_flagmux_data { /** * struct omap_l3 - Description of data relevant for L3 bus. * @dev: device representing the bus (populated runtime) - * @l3_base: base addresses of modules (populated runtime) + * @l3_base: base addresses of modules (populated runtime if 0) + * if set to L3_BASE_IS_SUBMODULE, then uses previous + * module index as the base address * @l3_flag_mux: array containing flag mux data per module * offset from corresponding module base indexed per * module. From 53a848be0a65c6fb105eb5ecb8b8b3edfa0f91ad Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 10 Apr 2014 11:33:13 -0500 Subject: [PATCH 051/101] bus: omap_l3_noc: Add DRA7 interconnect error data DRA7 is distinctly different from OMAP4 in terms of masters and clock domain organization. There two main clock domains which is divided as follows: <0x44000000 0x1000000> is clk1 and clk2 is the sub clock domain <0x45000000 0x1000> is clk3 Add all the data needed to handle L3 error handling on DRA7 devices and mark clk2 as subdomain and provide a compatible flag for functionality. Other than the data difference the hardware blocks involved are essentially the same. Signed-off-by: Rajendra Nayak [nm@ti.com: bugfixes and generic improvements, documentation] Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- .../devicetree/bindings/arm/omap/l3-noc.txt | 1 + drivers/bus/omap_l3_noc.c | 1 + drivers/bus/omap_l3_noc.h | 151 ++++++++++++++++++ 3 files changed, 153 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/omap/l3-noc.txt b/Documentation/devicetree/bindings/arm/omap/l3-noc.txt index c0105de55cbd..45d0fc23de2d 100644 --- a/Documentation/devicetree/bindings/arm/omap/l3-noc.txt +++ b/Documentation/devicetree/bindings/arm/omap/l3-noc.txt @@ -6,6 +6,7 @@ provided by Arteris. Required properties: - compatible : Should be "ti,omap3-l3-smx" for OMAP3 family Should be "ti,omap4-l3-noc" for OMAP4 family + Should be "ti,dra7-l3-noc" for DRA7 family - reg: Contains L3 register address range for each noc domain. - ti,hwmods: "l3_main_1", ... One hwmod for each noc domain. diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 0eba07ac6008..6cdd02ef0909 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -230,6 +230,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) static const struct of_device_id l3_noc_match[] = { {.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data}, + {.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data}, {}, }; MODULE_DEVICE_TABLE(of, l3_noc_match); diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index aced4c546783..9562a75259c8 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -230,4 +230,155 @@ static const struct omap_l3 omap_l3_data = { .mst_addr_mask = 0xFC, }; +/* DRA7 data */ +static struct l3_target_data dra_l3_target_data_clk1[] = { + {0x2a00, "AES1",}, + {0x0200, "DMM_P1",}, + {0x0600, "DSP2_SDMA",}, + {0x0b00, "EVE2",}, + {0x1300, "DMM_P2",}, + {0x2c00, "AES2",}, + {0x0300, "DSP1_SDMA",}, + {0x0a00, "EVE1",}, + {0x0c00, "EVE3",}, + {0x0d00, "EVE4",}, + {0x2900, "DSS",}, + {0x0100, "GPMC",}, + {0x3700, "PCIE1",}, + {0x1600, "IVA_CONFIG",}, + {0x1800, "IVA_SL2IF",}, + {0x0500, "L4_CFG",}, + {0x1d00, "L4_WKUP",}, + {0x3800, "PCIE2",}, + {0x3300, "SHA2_1",}, + {0x1200, "GPU",}, + {0x1000, "IPU1",}, + {0x1100, "IPU2",}, + {0x2000, "TPCC_EDMA",}, + {0x2e00, "TPTC1_EDMA",}, + {0x2b00, "TPTC2_EDMA",}, + {0x0700, "VCP1",}, + {0x2500, "L4_PER2_P3",}, + {0x0e00, "L4_PER3_P3",}, + {0x2200, "MMU1",}, + {0x1400, "PRUSS1",}, + {0x1500, "PRUSS2"}, + {0x0800, "VCP1",}, +}; + +static struct l3_flagmux_data dra_l3_flagmux_clk1 = { + .offset = 0x803500, + .l3_targ = dra_l3_target_data_clk1, + .num_targ_data = ARRAY_SIZE(dra_l3_target_data_clk1), +}; + +static struct l3_target_data dra_l3_target_data_clk2[] = { + {0x0, "HOST CLK1",}, + {0x0, "HOST CLK2",}, + {0xdead, L3_TARGET_NOT_SUPPORTED,}, + {0x3400, "SHA2_2",}, + {0x0900, "BB2D",}, + {0xdead, L3_TARGET_NOT_SUPPORTED,}, + {0x2100, "L4_PER1_P3",}, + {0x1c00, "L4_PER1_P1",}, + {0x1f00, "L4_PER1_P2",}, + {0x2300, "L4_PER2_P1",}, + {0x2400, "L4_PER2_P2",}, + {0x2600, "L4_PER3_P1",}, + {0x2700, "L4_PER3_P2",}, + {0x2f00, "MCASP1",}, + {0x3000, "MCASP2",}, + {0x3100, "MCASP3",}, + {0x2800, "MMU2",}, + {0x0f00, "OCMC_RAM1",}, + {0x1700, "OCMC_RAM2",}, + {0x1900, "OCMC_RAM3",}, + {0x1e00, "OCMC_ROM",}, + {0x3900, "QSPI",}, +}; + +static struct l3_flagmux_data dra_l3_flagmux_clk2 = { + .offset = 0x803600, + .l3_targ = dra_l3_target_data_clk2, + .num_targ_data = ARRAY_SIZE(dra_l3_target_data_clk2), +}; + +static struct l3_target_data dra_l3_target_data_clk3[] = { + {0x0100, "L3_INSTR"}, + {0x0300, "DEBUGSS_CT_TBR"}, + {0x0, "HOST CLK3"}, +}; + +static struct l3_flagmux_data dra_l3_flagmux_clk3 = { + .offset = 0x200, + .l3_targ = dra_l3_target_data_clk3, + .num_targ_data = ARRAY_SIZE(dra_l3_target_data_clk3), +}; + +static struct l3_masters_data dra_l3_masters[] = { + { 0x0, "MPU" }, + { 0x4, "CS_DAP" }, + { 0x5, "IEEE1500_2_OCP" }, + { 0x8, "DSP1_MDMA" }, + { 0x9, "DSP1_CFG" }, + { 0xA, "DSP1_DMA" }, + { 0xB, "DSP2_MDMA" }, + { 0xC, "DSP2_CFG" }, + { 0xD, "DSP2_DMA" }, + { 0xE, "IVA" }, + { 0x10, "EVE1_P1" }, + { 0x11, "EVE2_P1" }, + { 0x12, "EVE3_P1" }, + { 0x13, "EVE4_P1" }, + { 0x14, "PRUSS1 PRU1" }, + { 0x15, "PRUSS1 PRU2" }, + { 0x16, "PRUSS2 PRU1" }, + { 0x17, "PRUSS2 PRU2" }, + { 0x18, "IPU1" }, + { 0x19, "IPU2" }, + { 0x1A, "SDMA" }, + { 0x1B, "CDMA" }, + { 0x1C, "TC1_EDMA" }, + { 0x1D, "TC2_EDMA" }, + { 0x20, "DSS" }, + { 0x21, "MMU1" }, + { 0x22, "PCIE1" }, + { 0x23, "MMU2" }, + { 0x24, "VIP1" }, + { 0x25, "VIP2" }, + { 0x26, "VIP3" }, + { 0x27, "VPE" }, + { 0x28, "GPU_P1" }, + { 0x29, "BB2D" }, + { 0x29, "GPU_P2" }, + { 0x2B, "GMAC_SW" }, + { 0x2C, "USB3" }, + { 0x2D, "USB2_SS" }, + { 0x2E, "USB2_ULPI_SS1" }, + { 0x2F, "USB2_ULPI_SS2" }, + { 0x30, "CSI2_1" }, + { 0x31, "CSI2_2" }, + { 0x33, "SATA" }, + { 0x34, "EVE1_P2" }, + { 0x35, "EVE2_P2" }, + { 0x36, "EVE3_P2" }, + { 0x37, "EVE4_P2" } +}; + +static struct l3_flagmux_data *dra_l3_flagmux[] = { + &dra_l3_flagmux_clk1, + &dra_l3_flagmux_clk2, + &dra_l3_flagmux_clk3, +}; + +static const struct omap_l3 dra_l3_data = { + .l3_base = { [1] = L3_BASE_IS_SUBMODULE }, + .l3_flagmux = dra_l3_flagmux, + .num_modules = ARRAY_SIZE(dra_l3_flagmux), + .l3_masters = dra_l3_masters, + .num_masters = ARRAY_SIZE(dra_l3_masters), + /* The 6 MSBs of register field used to distinguish initiator */ + .mst_addr_mask = 0xFC, +}; + #endif /* __OMAP_L3_NOC_H */ From 27b7d5f3cc49f2e5cd6c005d73696058b7140c5c Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Mon, 2 Dec 2013 17:48:57 +0530 Subject: [PATCH 052/101] bus: omap_l3_noc: Add AM4372 interconnect error data Add AM4372 information to handle L3 error. AM4372 has two clk domains 100f and 200s. Provide flagmux and data associated with it. NOTE: Timeout doesn't have STDERRLOG_MAIN register. And per hardware team, L3 timeout error cannot be cleared the normal way (by setting bit 31 in STDERRLOG_MAIN), instead it may be required to do system reset. L3 error handler can't help in such scenarios. Hence indicate timeout target offset as L3_TARGET_NOT_SUPPORTED as done for undocumented bits. Signed-off-by: Dave Gerlach Signed-off-by: Afzal Mohammed Signed-off-by: Sekhar Nori Signed-off-by: Nishanth Menon Acked-by: Santosh Shilimkar Acked-by: Peter Ujfalusi Tested-by: Darren Etheridge Tested-by: Sekhar Nori --- .../devicetree/bindings/arm/omap/l3-noc.txt | 1 + drivers/bus/omap_l3_noc.c | 1 + drivers/bus/omap_l3_noc.h | 91 +++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/omap/l3-noc.txt b/Documentation/devicetree/bindings/arm/omap/l3-noc.txt index 45d0fc23de2d..974624ea68f6 100644 --- a/Documentation/devicetree/bindings/arm/omap/l3-noc.txt +++ b/Documentation/devicetree/bindings/arm/omap/l3-noc.txt @@ -7,6 +7,7 @@ Required properties: - compatible : Should be "ti,omap3-l3-smx" for OMAP3 family Should be "ti,omap4-l3-noc" for OMAP4 family Should be "ti,dra7-l3-noc" for DRA7 family + Should be "ti,am4372-l3-noc" for AM43 family - reg: Contains L3 register address range for each noc domain. - ti,hwmods: "l3_main_1", ... One hwmod for each noc domain. diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 6cdd02ef0909..531ae591783b 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -231,6 +231,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) static const struct of_device_id l3_noc_match[] = { {.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data}, {.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data}, + {.compatible = "ti,am4372-l3-noc", .data = &am4372_l3_data}, {}, }; MODULE_DEVICE_TABLE(of, l3_noc_match); diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 9562a75259c8..551e01061434 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -381,4 +381,95 @@ static const struct omap_l3 dra_l3_data = { .mst_addr_mask = 0xFC, }; +/* AM4372 data */ +static struct l3_target_data am4372_l3_target_data_200f[] = { + {0xf00, "EMIF",}, + {0x1200, "DES",}, + {0x400, "OCMCRAM",}, + {0x700, "TPTC0",}, + {0x800, "TPTC1",}, + {0x900, "TPTC2"}, + {0xb00, "TPCC",}, + {0xd00, "DEBUGSS",}, + {0xdead, L3_TARGET_NOT_SUPPORTED,}, + {0x200, "SHA",}, + {0xc00, "SGX530",}, + {0x500, "AES0",}, + {0xa00, "L4_FAST",}, + {0x300, "MPUSS_L2_RAM",}, + {0x100, "ICSS",}, +}; + +static struct l3_flagmux_data am4372_l3_flagmux_200f = { + .offset = 0x1000, + .l3_targ = am4372_l3_target_data_200f, + .num_targ_data = ARRAY_SIZE(am4372_l3_target_data_200f), +}; + +static struct l3_target_data am4372_l3_target_data_100s[] = { + {0x100, "L4_PER_0",}, + {0x200, "L4_PER_1",}, + {0x300, "L4_PER_2",}, + {0x400, "L4_PER_3",}, + {0x800, "McASP0",}, + {0x900, "McASP1",}, + {0xC00, "MMCHS2",}, + {0x700, "GPMC",}, + {0xD00, "L4_FW",}, + {0xdead, L3_TARGET_NOT_SUPPORTED,}, + {0x500, "ADCTSC",}, + {0xE00, "L4_WKUP",}, + {0xA00, "MAG_CARD",}, +}; + +static struct l3_flagmux_data am4372_l3_flagmux_100s = { + .offset = 0x600, + .l3_targ = am4372_l3_target_data_100s, + .num_targ_data = ARRAY_SIZE(am4372_l3_target_data_100s), +}; + +static struct l3_masters_data am4372_l3_masters[] = { + { 0x0, "M1 (128-bit)"}, + { 0x1, "M2 (64-bit)"}, + { 0x4, "DAP"}, + { 0x5, "P1500"}, + { 0xC, "ICSS0"}, + { 0xD, "ICSS1"}, + { 0x14, "Wakeup Processor"}, + { 0x18, "TPTC0 Read"}, + { 0x19, "TPTC0 Write"}, + { 0x1A, "TPTC1 Read"}, + { 0x1B, "TPTC1 Write"}, + { 0x1C, "TPTC2 Read"}, + { 0x1D, "TPTC2 Write"}, + { 0x20, "SGX530"}, + { 0x21, "OCP WP Traffic Probe"}, + { 0x22, "OCP WP DMA Profiling"}, + { 0x23, "OCP WP Event Trace"}, + { 0x25, "DSS"}, + { 0x28, "Crypto DMA RD"}, + { 0x29, "Crypto DMA WR"}, + { 0x2C, "VPFE0"}, + { 0x2D, "VPFE1"}, + { 0x30, "GEMAC"}, + { 0x34, "USB0 RD"}, + { 0x35, "USB0 WR"}, + { 0x36, "USB1 RD"}, + { 0x37, "USB1 WR"}, +}; + +static struct l3_flagmux_data *am4372_l3_flagmux[] = { + &am4372_l3_flagmux_200f, + &am4372_l3_flagmux_100s, +}; + +static const struct omap_l3 am4372_l3_data = { + .l3_flagmux = am4372_l3_flagmux, + .num_modules = ARRAY_SIZE(am4372_l3_flagmux), + .l3_masters = am4372_l3_masters, + .num_masters = ARRAY_SIZE(am4372_l3_masters), + /* All 6 bits of register field used to distinguish initiator */ + .mst_addr_mask = 0x3F, +}; + #endif /* __OMAP_L3_NOC_H */ From 1e84443e69c1fdbef76fb3897294e924461a6c0a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 14 May 2014 14:38:15 +0200 Subject: [PATCH 053/101] reset: sunxi: document sunxi's reset controllers bindings Add DT bindings documentation for sunxi's reset controllers. Signed-off-by: Boris BREZILLON Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard Acked-by: Philipp Zabel --- .../reset/allwinner,sunxi-clock-reset.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/allwinner,sunxi-clock-reset.txt diff --git a/Documentation/devicetree/bindings/reset/allwinner,sunxi-clock-reset.txt b/Documentation/devicetree/bindings/reset/allwinner,sunxi-clock-reset.txt new file mode 100644 index 000000000000..c8f775714887 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/allwinner,sunxi-clock-reset.txt @@ -0,0 +1,21 @@ +Allwinner sunxi Peripheral Reset Controller +=========================================== + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required properties: +- compatible: Should be one of the following: + "allwinner,sun6i-a31-ahb1-reset" + "allwinner,sun6i-a31-clock-reset" +- reg: should be register base and length as documented in the + datasheet +- #reset-cells: 1, see below + +example: + +ahb1_rst: reset@01c202c0 { + #reset-cells = <1>; + compatible = "allwinner,sun6i-a31-ahb1-reset"; + reg = <0x01c202c0 0xc>; +}; From cd90f0cf4454861a4f87bdfdcf01e8b98cc94869 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 14 May 2014 14:38:16 +0200 Subject: [PATCH 054/101] reset: sunxi: allow MFD subdevices probe The current implementation uses sunxi_reset_init function for both early init and platform device probe. The sunxi_reset_init function uses DT to retrieve device resources, which will be an issue if reset controllers are registered from an MFD device that define resources from mfd_cell definition. Moreover, we can make of devm functions when we're in the probe context. Signed-off-by: Boris BREZILLON Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard Acked-by: Philipp Zabel --- drivers/reset/reset-sunxi.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index 695bd3496eba..a94e7a7820b4 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -145,7 +145,24 @@ MODULE_DEVICE_TABLE(of, sunxi_reset_dt_ids); static int sunxi_reset_probe(struct platform_device *pdev) { - return sunxi_reset_init(pdev->dev.of_node); + struct sunxi_reset_data *data; + struct resource *res; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->membase)) + return PTR_ERR(data->membase); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = resource_size(res) * 32; + data->rcdev.ops = &sunxi_reset_ops; + data->rcdev.of_node = pdev->dev.of_node; + + return reset_controller_register(&data->rcdev); } static int sunxi_reset_remove(struct platform_device *pdev) @@ -153,8 +170,6 @@ static int sunxi_reset_remove(struct platform_device *pdev) struct sunxi_reset_data *data = platform_get_drvdata(pdev); reset_controller_unregister(&data->rcdev); - iounmap(data->membase); - kfree(data); return 0; } From 441617672810482b1f92878b7f82a56cd4f0fcf6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 May 2014 10:26:01 +0300 Subject: [PATCH 055/101] ARM: edma: Clean up and simplify the code around irq request Get the two interrupt line number at the same time by merging the two instance of if(node){}else{} places. replace the &pdev->dev with the already existing dev which makes it possible to collapse lines with devm_request_irq() Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 25fa735abc6c..b9bd42ad2d6e 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1598,7 +1598,6 @@ static int edma_probe(struct platform_device *pdev) struct resource *r[EDMA_MAX_CC] = {NULL}; struct resource res[EDMA_MAX_CC]; char res_name[10]; - char irq_name[10]; struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; @@ -1720,14 +1719,21 @@ static int edma_probe(struct platform_device *pdev) if (node) { irq[j] = irq_of_parse_and_map(node, 0); + err_irq[j] = irq_of_parse_and_map(node, 2); } else { + char irq_name[10]; + sprintf(irq_name, "edma%d", j); irq[j] = platform_get_irq_byname(pdev, irq_name); + + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); } edma_cc[j]->irq_res_start = irq[j]; - status = devm_request_irq(&pdev->dev, irq[j], - dma_irq_handler, 0, "edma", - &pdev->dev); + edma_cc[j]->irq_res_end = err_irq[j]; + + status = devm_request_irq(dev, irq[j], dma_irq_handler, 0, + "edma", dev); if (status < 0) { dev_dbg(&pdev->dev, "devm_request_irq %d failed --> %d\n", @@ -1735,16 +1741,8 @@ static int edma_probe(struct platform_device *pdev) return status; } - if (node) { - err_irq[j] = irq_of_parse_and_map(node, 2); - } else { - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); - } - edma_cc[j]->irq_res_end = err_irq[j]; - status = devm_request_irq(&pdev->dev, err_irq[j], - dma_ccerr_handler, 0, - "edma_error", &pdev->dev); + status = devm_request_irq(dev, err_irq[j], dma_ccerr_handler, 0, + "edma_error", dev); if (status < 0) { dev_dbg(&pdev->dev, "devm_request_irq %d failed --> %d\n", From 89df4bed0f25157700c0ade5ac5f0296150eaecd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:08 +0300 Subject: [PATCH 056/101] ARM: edma: No need to clean the pdata in edma_of_parse_dt() The pdata has been just allocated with devm_kzalloc() in edma_setup_info_from_dt() and passed to this function. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index b9bd42ad2d6e..fade9ada81f8 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1490,8 +1490,6 @@ static int edma_of_parse_dt(struct device *dev, struct edma_rsv_info *rsv_info; s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; - memset(pdata, 0, sizeof(struct edma_soc_info)); - ret = of_property_read_u32(node, "dma-channels", &value); if (ret < 0) return ret; From cf4afc3d2b6e11e507692c422eaf2b2e691e00d7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:09 +0300 Subject: [PATCH 057/101] ARM: edma: Take the number of tc from edma_soc_info (pdata) Instead of saving the for loop length, take the num_tc value from the pdata. In case of DT boot set the n_tc to 3 as it is hardwired in edma_of_parse_dt() This is a temporary state since upcoming patch(es) will change how we are dealing with these parameters. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index fade9ada81f8..fde56e2ba203 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1506,6 +1506,7 @@ static int edma_of_parse_dt(struct device *dev, pdata->n_slot = value; pdata->n_cc = 1; + pdata->n_tc = 3; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); if (!rsv_info) @@ -1666,6 +1667,7 @@ static int edma_probe(struct platform_device *pdev) EDMA_MAX_PARAMENTRY); edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, EDMA_MAX_CC); + edma_cc[j]->num_tc = info[j]->n_tc; edma_cc[j]->default_queue = info[j]->default_queue; @@ -1759,9 +1761,6 @@ static int edma_probe(struct platform_device *pdev) map_queue_tc(j, queue_tc_mapping[i][0], queue_tc_mapping[i][1]); - /* Save the number of TCs */ - edma_cc[j]->num_tc = i; - /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, From c3dd3389dbed93d5675205cc25ff7be67a738573 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:10 +0300 Subject: [PATCH 058/101] ARM: edma: Do not change TC -> Queue mapping, leave it to default. There is no need to change the default TC -> Queue mapping. By default the mapping is: TC0 -> Q0, TC1 -> Q1, etc. Changing this has no benefits at all and all the board files are just setting the same mapping back to the HW. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index fde56e2ba203..4df5d443b0b3 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -290,12 +290,6 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no, ~(0x7 << bit), queue_no << bit); } -static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); -} - static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, int priority) { @@ -1488,7 +1482,7 @@ static int edma_of_parse_dt(struct device *dev, struct property *prop; size_t sz; struct edma_rsv_info *rsv_info; - s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; + s8 (*queue_priority_map)[2]; ret = of_property_read_u32(node, "dma-channels", &value); if (ret < 0) @@ -1513,19 +1507,6 @@ static int edma_of_parse_dt(struct device *dev, return -ENOMEM; pdata->rsv = rsv_info; - queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); - if (!queue_tc_map) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - queue_tc_map[i][0] = i; - queue_tc_map[i][1] = i; - } - queue_tc_map[i][0] = -1; - queue_tc_map[i][1] = -1; - - pdata->queue_tc_mapping = queue_tc_map; - queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); if (!queue_priority_map) return -ENOMEM; @@ -1586,7 +1567,6 @@ static int edma_probe(struct platform_device *pdev) struct edma_soc_info **info = pdev->dev.platform_data; struct edma_soc_info *ninfo[EDMA_MAX_CC] = {NULL}; s8 (*queue_priority_mapping)[2]; - s8 (*queue_tc_mapping)[2]; int i, j, off, ln, found = 0; int status = -1; const s16 (*rsv_chans)[2]; @@ -1753,14 +1733,8 @@ static int edma_probe(struct platform_device *pdev) for (i = 0; i < edma_cc[j]->num_channels; i++) map_dmach_queue(j, i, info[j]->default_queue); - queue_tc_mapping = info[j]->queue_tc_mapping; queue_priority_mapping = info[j]->queue_priority_mapping; - /* Event queue to TC mapping */ - for (i = 0; queue_tc_mapping[i][0] != -1; i++) - map_queue_tc(j, queue_tc_mapping[i][0], - queue_tc_mapping[i][1]); - /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, From 82ba61228467db9e8fe7d253cba0a5974e562974 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:11 +0300 Subject: [PATCH 059/101] ARM: davinci: Remove eDMA3 queue_tc_mapping data from edma_soc_info It is ignored by the edma driver since we are just setting back the default mapping of TC -> Queue. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/devices-da8xx.c | 16 ---------------- arch/arm/mach-davinci/dm355.c | 9 --------- arch/arm/mach-davinci/dm365.c | 11 ----------- arch/arm/mach-davinci/dm644x.c | 9 --------- arch/arm/mach-davinci/dm646x.c | 11 ----------- 5 files changed, 56 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 56ea41d5f849..7f376e54b266 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -134,13 +134,6 @@ struct platform_device da8xx_serial_device[] = { } }; -static s8 da8xx_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1} -}; - static s8 da8xx_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -148,12 +141,6 @@ static s8 da8xx_queue_priority_mapping[][2] = { {-1, -1} }; -static s8 da850_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {-1, -1} -}; - static s8 da850_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -166,7 +153,6 @@ static struct edma_soc_info da830_edma_cc0_info = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }; @@ -182,7 +168,6 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }, @@ -192,7 +177,6 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_slot = 128, .n_tc = 1, .n_cc = 1, - .queue_tc_mapping = da850_queue_tc_mapping, .queue_priority_mapping = da850_queue_priority_mapping, .default_queue = EVENTQ_0, }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 07381d8cea62..e27f7ff54570 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -568,14 +568,6 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static s8 -queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1}, -}; - static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -590,7 +582,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 08a61b938333..88835b0aaead 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -852,16 +852,6 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = { }; /* Four Transfer Controllers on DM365 */ -static s8 -dm365_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {2, 2}, - {3, 3}, - {-1, -1}, -}; - static s8 dm365_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -878,7 +868,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 256, .n_tc = 4, .n_cc = 1, - .queue_tc_mapping = dm365_queue_tc_mapping, .queue_priority_mapping = dm365_queue_priority_mapping, .default_queue = EVENTQ_3, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5debffba4b24..8ea34be879b4 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -498,14 +498,6 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static s8 -queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1}, -}; - static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -520,7 +512,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 332d00d24dc2..97e90dc5ed43 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -532,16 +532,6 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ /* Four Transfer Controllers on DM646x */ -static s8 -dm646x_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {2, 2}, - {3, 3}, - {-1, -1}, -}; - static s8 dm646x_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -558,7 +548,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 512, .n_tc = 4, .n_cc = 1, - .queue_tc_mapping = dm646x_queue_tc_mapping, .queue_priority_mapping = dm646x_queue_priority_mapping, .default_queue = EVENTQ_1, }; From db885bf82883f9743efe09d91775c579c0ed6842 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:12 +0300 Subject: [PATCH 060/101] ARM: edma: Remove queue_tc_mapping data from edma_soc_info It is no longer in use by the driver or board files. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- include/linux/platform_data/edma.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 12f134b1493c..633e196ebdf2 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -175,7 +175,6 @@ struct edma_soc_info { /* Resource reservation for other cores */ struct edma_rsv_info *rsv; - s8 (*queue_tc_mapping)[2]; s8 (*queue_priority_mapping)[2]; const s16 (*xbar_chans)[2]; }; From 6710ea7a0287be4380ac81c37aa6e2683a04b153 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:13 +0300 Subject: [PATCH 061/101] ARM: edma: Remove num_cc member from struct edma The struct edma is allocated per CC bases so the member num_cc does not make any sense. One CC is one CC, it does not have sub CCs. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 4df5d443b0b3..90dd7b6013bb 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -233,7 +233,6 @@ struct edma { unsigned num_region; unsigned num_slots; unsigned num_tc; - unsigned num_cc; enum dma_event_q default_queue; /* list of channels with no even trigger; terminated by "-1" */ @@ -1499,7 +1498,6 @@ static int edma_of_parse_dt(struct device *dev, return ret; pdata->n_slot = value; - pdata->n_cc = 1; pdata->n_tc = 3; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); @@ -1645,8 +1643,6 @@ static int edma_probe(struct platform_device *pdev) EDMA_MAX_DMACH); edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, - EDMA_MAX_CC); edma_cc[j]->num_tc = info[j]->n_tc; edma_cc[j]->default_queue = info[j]->default_queue; From 643efcff5208b5521060779f769593ca3837887d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:14 +0300 Subject: [PATCH 062/101] ARM: edma: Save number of regions from pdata to struct edma To be consistent in the code that we take parameters from edma_cc[j] struct and not randomly from info[j] as well. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 90dd7b6013bb..79097d880d50 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1646,6 +1646,7 @@ static int edma_probe(struct platform_device *pdev) edma_cc[j]->num_tc = info[j]->n_tc; edma_cc[j]->default_queue = info[j]->default_queue; + edma_cc[j]->num_region = info[j]->n_region; dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", edmacc_regs_base[j]); @@ -1743,7 +1744,7 @@ static int edma_probe(struct platform_device *pdev) if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) map_dmach_param(j); - for (i = 0; i < info[j]->n_region; i++) { + for (i = 0; i < edma_cc[j]->num_region; i++) { edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); edma_write_array(j, EDMA_QRAE, i, 0x0); From 6d10c3950bf4d42a5bd28bfd7572834225acb031 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:15 +0300 Subject: [PATCH 063/101] ARM: edma: Get IP configuration from HW (number of channels, tc, etc) From CCCFG register of eDMA3 we can get all the needed information for the driver about the IP: Number of channels: NUM_DMACH Number of regions: NUM_REGN Number of slots (PaRAM sets): NUM_PAENTRY Number of TC/EQ: NUM_EVQUE In case when booted with DT or the queue_priority_mapping is not provided set up a default priority map. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 115 ++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 79097d880d50..eeea011480eb 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -102,7 +102,13 @@ #define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) #define EDMA_DCHMAP 0x0100 /* 64 registers */ -#define CHMAP_EXIST BIT(24) + +/* CCCFG register */ +#define GET_NUM_DMACH(x) (x & 0x7) /* bits 0-2 */ +#define GET_NUM_PAENTRY(x) ((x & 0x7000) >> 12) /* bits 12-14 */ +#define GET_NUM_EVQUE(x) ((x & 0x70000) >> 16) /* bits 16-18 */ +#define GET_NUM_REGN(x) ((x & 0x300000) >> 20) /* bits 20-21 */ +#define CHMAP_EXIST BIT(24) #define EDMA_MAX_DMACH 64 #define EDMA_MAX_PARAMENTRY 512 @@ -1408,6 +1414,67 @@ void edma_clear_event(unsigned channel) } EXPORT_SYMBOL(edma_clear_event); +static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata, + struct edma *edma_cc) +{ + int i; + u32 value, cccfg; + s8 (*queue_priority_map)[2]; + + /* Decode the eDMA3 configuration from CCCFG register */ + cccfg = edma_read(0, EDMA_CCCFG); + + value = GET_NUM_REGN(cccfg); + edma_cc->num_region = BIT(value); + + value = GET_NUM_DMACH(cccfg); + edma_cc->num_channels = BIT(value + 1); + + value = GET_NUM_PAENTRY(cccfg); + edma_cc->num_slots = BIT(value + 4); + + value = GET_NUM_EVQUE(cccfg); + edma_cc->num_tc = value + 1; + + dev_dbg(dev, "eDMA3 HW configuration (cccfg: 0x%08x):\n", cccfg); + dev_dbg(dev, "num_region: %u\n", edma_cc->num_region); + dev_dbg(dev, "num_channel: %u\n", edma_cc->num_channels); + dev_dbg(dev, "num_slot: %u\n", edma_cc->num_slots); + dev_dbg(dev, "num_tc: %u\n", edma_cc->num_tc); + + /* Nothing need to be done if queue priority is provided */ + if (pdata->queue_priority_mapping) + return 0; + + /* + * Configure TC/queue priority as follows: + * Q0 - priority 0 + * Q1 - priority 1 + * Q2 - priority 2 + * ... + * The meaning of priority numbers: 0 highest priority, 7 lowest + * priority. So Q0 is the highest priority queue and the last queue has + * the lowest priority. + */ + queue_priority_map = devm_kzalloc(dev, + (edma_cc->num_tc + 1) * sizeof(s8), + GFP_KERNEL); + if (!queue_priority_map) + return -ENOMEM; + + for (i = 0; i < edma_cc->num_tc; i++) { + queue_priority_map[i][0] = i; + queue_priority_map[i][1] = i; + } + queue_priority_map[i][0] = -1; + queue_priority_map[i][1] = -1; + + pdata->queue_priority_mapping = queue_priority_map; + pdata->default_queue = 0; + + return 0; +} + #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) static int edma_of_read_u32_to_s16_array(const struct device_node *np, @@ -1476,50 +1543,16 @@ static int edma_of_parse_dt(struct device *dev, struct device_node *node, struct edma_soc_info *pdata) { - int ret = 0, i; - u32 value; + int ret = 0; struct property *prop; size_t sz; struct edma_rsv_info *rsv_info; - s8 (*queue_priority_map)[2]; - - ret = of_property_read_u32(node, "dma-channels", &value); - if (ret < 0) - return ret; - pdata->n_channel = value; - - ret = of_property_read_u32(node, "ti,edma-regions", &value); - if (ret < 0) - return ret; - pdata->n_region = value; - - ret = of_property_read_u32(node, "ti,edma-slots", &value); - if (ret < 0) - return ret; - pdata->n_slot = value; - - pdata->n_tc = 3; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); if (!rsv_info) return -ENOMEM; pdata->rsv = rsv_info; - queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); - if (!queue_priority_map) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - queue_priority_map[i][0] = i; - queue_priority_map[i][1] = i; - } - queue_priority_map[i][0] = -1; - queue_priority_map[i][1] = -1; - - pdata->queue_priority_mapping = queue_priority_map; - - pdata->default_queue = 0; - prop = of_find_property(node, "ti,edma-xbar-event-map", &sz); if (prop) ret = edma_xbar_event_map(dev, node, pdata, sz); @@ -1639,14 +1672,12 @@ static int edma_probe(struct platform_device *pdev) if (!edma_cc[j]) return -ENOMEM; - edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, - EDMA_MAX_DMACH); - edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, - EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_tc = info[j]->n_tc; + /* Get eDMA3 configuration from IP */ + ret = edma_setup_from_hw(dev, info[j], edma_cc[j]); + if (ret) + return ret; edma_cc[j]->default_queue = info[j]->default_queue; - edma_cc[j]->num_region = info[j]->n_region; dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", edmacc_regs_base[j]); From efc24e148009bb7965439a3a29abfb778a45061e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:16 +0300 Subject: [PATCH 064/101] dt/bindings: ti,edma: Remove redundant properties from documentation From CCCFG register of eDMA3 we can get all the needed information for the driver about the IP: Number of channels: NUM_DMACH Number of regions: NUM_REGN Number of slots (PaRAM sets): NUM_PAENTRY Number of TC/EQ: NUM_EVQUE The ti,edma-regions; ti,edma-slots and dma-channels in DT are redundant since the very same information can be obtained from the HW. The mentioned properties are deprecated. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/dma/ti-edma.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt index 9fbbdb783a72..ad67c41c38df 100644 --- a/Documentation/devicetree/bindings/dma/ti-edma.txt +++ b/Documentation/devicetree/bindings/dma/ti-edma.txt @@ -2,11 +2,8 @@ TI EDMA Required properties: - compatible : "ti,edma3" -- ti,edma-regions: Number of regions -- ti,edma-slots: Number of slots - #dma-cells: Should be set to <1> Clients should use a single channel number per DMA request. -- dma-channels: Specify total DMA channels per CC - reg: Memory map for accessing module - interrupt-parent: Interrupt controller the interrupt is routed through - interrupts: Exactly 3 interrupts need to be specified in the order: @@ -17,6 +14,13 @@ Optional properties: - ti,hwmods: Name of the hwmods associated to the EDMA - ti,edma-xbar-event-map: Crossbar event to channel map +Deprecated properties: +Listed here in case one wants to boot an old kernel with new DTB. These +properties might need to be added to the new DTS files. +- ti,edma-regions: Number of regions +- ti,edma-slots: Number of slots +- dma-channels: Specify total DMA channels per CC + Example: edma: edma@49000000 { @@ -26,9 +30,6 @@ edma: edma@49000000 { compatible = "ti,edma3"; ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2"; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; ti,edma-xbar-event-map = <1 12 2 13>; }; From 86a1dddc1c027cca707326e06d1a876f6349e3b4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:17 +0300 Subject: [PATCH 065/101] ARM: dts: am33xx: Remove obsolete properties from edma node dma-channels, ti,edma-regions and ti,edma-slots no longer needed in DT since the the same information is available in the IP's CCCFG register. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Sekhar Nori --- arch/arm/boot/dts/am33xx.dtsi | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 9770e35f2536..50d8d2f93490 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -147,9 +147,6 @@ <0x44e10f90 0x10>; interrupts = <12 13 14>; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; }; gpio0: gpio@44e07000 { From 27f12c5bc3d7368bb62a66a2d79b5f862372057f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:18 +0300 Subject: [PATCH 066/101] ARM: dts: am4372: Remove obsolete properties from edma node dma-channels, ti,edma-regions and ti,edma-slots no longer needed in DT since the the same information is available in the IP's CCCFG register. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Sekhar Nori --- arch/arm/boot/dts/am4372.dtsi | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index 36d523a26831..94bdcc1b1ab9 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -108,9 +108,6 @@ , ; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; }; uart0: serial@44e09000 { From d5fc0e8dd9b3ddc26374d5334455c38ad84b7fab Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:19 +0300 Subject: [PATCH 067/101] ARM: davinci: Remove redundant/unused parameters for edma The following parameters are no longer needed by the edma driver since the information can be obtained from the IP's CCCFG register: n_channel, n_region, n_slot and n_tc. Remove the initialization of n_cc as well since in this context it has no meaning. We have separate edma_soc_info struct/eDMA3_CC instance so this member does not make any sense (and the driver no longer uses it). Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/devices-da8xx.c | 15 --------------- arch/arm/mach-davinci/dm355.c | 5 ----- arch/arm/mach-davinci/dm365.c | 5 ----- arch/arm/mach-davinci/dm644x.c | 5 ----- arch/arm/mach-davinci/dm646x.c | 5 ----- 5 files changed, 35 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 7f376e54b266..b85b781b05fd 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -148,11 +148,6 @@ static s8 da850_queue_priority_mapping[][2] = { }; static struct edma_soc_info da830_edma_cc0_info = { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }; @@ -163,20 +158,10 @@ static struct edma_soc_info *da830_edma_info[EDMA_MAX_CC] = { static struct edma_soc_info da850_edma_cc_info[] = { { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }, { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 1, - .n_cc = 1, .queue_priority_mapping = da850_queue_priority_mapping, .default_queue = EVENTQ_0, }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index e27f7ff54570..2f3ed3a58d57 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -577,11 +577,6 @@ queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 88835b0aaead..0ae8114f5cc9 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -863,11 +863,6 @@ dm365_queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 256, - .n_tc = 4, - .n_cc = 1, .queue_priority_mapping = dm365_queue_priority_mapping, .default_queue = EVENTQ_3, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 8ea34be879b4..dc52657909c4 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -507,11 +507,6 @@ queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 97e90dc5ed43..6c3bbea7d77d 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -543,11 +543,6 @@ dm646x_queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 6, /* 0-1, 4-7 */ - .n_slot = 512, - .n_tc = 4, - .n_cc = 1, .queue_priority_mapping = dm646x_queue_priority_mapping, .default_queue = EVENTQ_1, }; From 903ed4913c7fe78d2746445564634264291c7493 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:20 +0300 Subject: [PATCH 068/101] ARM: edma: Remove redundant/unused parameters from edma_soc_info The following parameters are no longer needed by the edma driver since the information can be obtained from the IP's CCCFG register: n_channel, n_region, n_slot and n_tc. Remove the n_cc as well since in this context it has no meaning. We have separate edma_soc_info struct/eDMA3_CC instance so this member does not make any sense (and the driver no longer uses it). Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- include/linux/platform_data/edma.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 633e196ebdf2..eb8d5627d080 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -158,13 +158,6 @@ struct edma_rsv_info { /* platform_data for EDMA driver */ struct edma_soc_info { - - /* how many dma resources of each type */ - unsigned n_channel; - unsigned n_region; - unsigned n_slot; - unsigned n_tc; - unsigned n_cc; /* * Default queue is expected to be a low-priority queue. * This way, long transfers on the default queue started From 1be7f5520a3885747174008d2905ae551f74ea78 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 6 May 2014 21:44:20 -0500 Subject: [PATCH 069/101] power: reset: Add Allwinner A31 reset code That code used to be in the machine code, but it's more fit here with other restart hooks. That will allow to cleanup the machine directory, while waiting for a proper watchdog driver for the A31. Signed-off-by: Maxime Ripard Acked-by: Arnd Bergmann --- drivers/power/reset/Kconfig | 7 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/sun6i-reboot.c | 85 ++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 drivers/power/reset/sun6i-reboot.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index fa0e4e057b99..67aeb6ec08f9 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -43,6 +43,13 @@ config POWER_RESET_RESTART Instead they restart, and u-boot holds the SoC until the user presses a key. u-boot then boots into Linux. +config POWER_RESET_SUN6I + bool "Allwinner A31 SoC reset driver" + depends on ARCH_SUNXI + depends on POWER_RESET + help + Reboot support for the Allwinner A31 SoCs. + config POWER_RESET_VEXPRESS bool "ARM Versatile Express power-off and reset driver" depends on ARM || ARM64 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a5b4a77d1a41..950fdc011c7a 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o +obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o diff --git a/drivers/power/reset/sun6i-reboot.c b/drivers/power/reset/sun6i-reboot.c new file mode 100644 index 000000000000..af2cd7ff2fe8 --- /dev/null +++ b/drivers/power/reset/sun6i-reboot.c @@ -0,0 +1,85 @@ +/* + * Allwinner A31 SoCs reset code + * + * Copyright (C) 2012-2014 Maxime Ripard + * + * Maxime Ripard + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define SUN6I_WATCHDOG1_IRQ_REG 0x00 +#define SUN6I_WATCHDOG1_CTRL_REG 0x10 +#define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0) +#define SUN6I_WATCHDOG1_CONFIG_REG 0x14 +#define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0) +#define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1) +#define SUN6I_WATCHDOG1_MODE_REG 0x18 +#define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0) + +static void __iomem *wdt_base; + +static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd) +{ + if (!wdt_base) + return; + + /* Disable interrupts */ + writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG); + + /* We want to disable the IRQ and just reset the whole system */ + writel(SUN6I_WATCHDOG1_CONFIG_RESTART, + wdt_base + SUN6I_WATCHDOG1_CONFIG_REG); + + /* Enable timer. The default and lowest interval value is 0.5s */ + writel(SUN6I_WATCHDOG1_MODE_ENABLE, + wdt_base + SUN6I_WATCHDOG1_MODE_REG); + + /* Restart the watchdog. */ + writel(SUN6I_WATCHDOG1_CTRL_RESTART, + wdt_base + SUN6I_WATCHDOG1_CTRL_REG); + + while (1) { + mdelay(5); + writel(SUN6I_WATCHDOG1_MODE_ENABLE, + wdt_base + SUN6I_WATCHDOG1_MODE_REG); + } +} + +static int sun6i_reboot_probe(struct platform_device *pdev) +{ + wdt_base = of_iomap(pdev->dev.of_node, 0); + if (!wdt_base) { + WARN(1, "failed to map watchdog base address"); + return -ENODEV; + } + + arm_pm_restart = sun6i_wdt_restart; + + return 0; +} + +static struct of_device_id sun6i_reboot_of_match[] = { + { .compatible = "allwinner,sun6i-a31-wdt" }, + {} +}; + +static struct platform_driver sun6i_reboot_driver = { + .probe = sun6i_reboot_probe, + .driver = { + .name = "sun6i-reboot", + .of_match_table = sun6i_reboot_of_match, + }, +}; +module_platform_driver(sun6i_reboot_driver); From 3a6e08218f36baa9c49282ad2fe0dfbf001d8f23 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Wed, 23 Apr 2014 19:46:17 -0400 Subject: [PATCH 070/101] soc: Introduce drivers/soc place-holder for SOC specific drivers Based on earlier thread "https://lkml.org/lkml/2013/10/7/662" and discussion at Kernel Summit'2013, it was agreed to create 'driver/soc' for drivers which are quite SOC specific. Further discussion on the subject is in response to the earlier version of the patch is here: http://lwn.net/Articles/588942/ Cc: Greg Kroah-Hartman Cc: Kumar Gala Cc: Paul Walmsley Cc: Olof Johansson Cc: Arnd Bergmann Signed-off-by: Sandeep Nair Signed-off-by: Santosh Shilimkar Signed-off-by: Kumar Gala --- drivers/Kconfig | 2 ++ drivers/Makefile | 3 +++ drivers/soc/Kconfig | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 drivers/soc/Kconfig diff --git a/drivers/Kconfig b/drivers/Kconfig index 0a0a90f52d26..0e87a34b6472 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -132,6 +132,8 @@ source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" +source "drivers/soc/Kconfig" + source "drivers/clk/Kconfig" source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index e3ced91b1784..5367b51d6efd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -33,6 +33,9 @@ obj-y += amba/ # really early. obj-$(CONFIG_DMADEVICES) += dma/ +# SOC specific infrastructure drivers. +obj-y += soc/ + obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_XEN) += xen/ diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig new file mode 100644 index 000000000000..339baa87336f --- /dev/null +++ b/drivers/soc/Kconfig @@ -0,0 +1,3 @@ +menu "SOC (System On Chip) specific Drivers" + +endmenu From 5d144e36b7c51612922fa05d37ff3a869261cc82 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Thu, 24 Apr 2014 11:31:21 -0500 Subject: [PATCH 071/101] soc: qcom: Add GSBI driver The GSBI (General Serial Bus Interface) driver controls the overarching configuration of the shared serial bus infrastructure on APQ8064, IPQ8064, and earlier QCOM processors. The GSBI supports UART, I2C, SPI, and UIM functionality in various combinations. Signed-off-by: Andy Gross Signed-off-by: Kumar Gala --- drivers/soc/Kconfig | 2 + drivers/soc/Makefile | 5 +++ drivers/soc/qcom/Kconfig | 11 +++++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qcom_gsbi.c | 84 ++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 drivers/soc/Makefile create mode 100644 drivers/soc/qcom/Kconfig create mode 100644 drivers/soc/qcom/Makefile create mode 100644 drivers/soc/qcom/qcom_gsbi.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 339baa87336f..c8543855aa82 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,3 +1,5 @@ menu "SOC (System On Chip) specific Drivers" +source "drivers/soc/qcom/Kconfig" + endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile new file mode 100644 index 000000000000..0f7c44793b29 --- /dev/null +++ b/drivers/soc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Linux Kernel SOC specific device drivers. +# + +obj-$(CONFIG_ARCH_QCOM) += qcom/ diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig new file mode 100644 index 000000000000..7bd2c94f54a4 --- /dev/null +++ b/drivers/soc/qcom/Kconfig @@ -0,0 +1,11 @@ +# +# QCOM Soc drivers +# +config QCOM_GSBI + tristate "QCOM General Serial Bus Interface" + depends on ARCH_QCOM + help + Say y here to enable GSBI support. The GSBI provides control + functions for connecting the underlying serial UART, SPI, and I2C + devices to the output pins. + diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile new file mode 100644 index 000000000000..438901257ac1 --- /dev/null +++ b/drivers/soc/qcom/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c new file mode 100644 index 000000000000..061dd0632dbd --- /dev/null +++ b/drivers/soc/qcom/qcom_gsbi.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, The Linux foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License rev 2 and + * only rev 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 + +#define GSBI_CTRL_REG 0x0000 +#define GSBI_PROTOCOL_SHIFT 4 + +static int gsbi_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct resource *res; + void __iomem *base; + struct clk *hclk; + u32 mode, crci = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + if (of_property_read_u32(node, "qcom,mode", &mode)) { + dev_err(&pdev->dev, "missing mode configuration\n"); + return -EINVAL; + } + + /* not required, so default to 0 if not present */ + of_property_read_u32(node, "qcom,crci", &crci); + + dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n", mode, crci); + + hclk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(hclk)) + return PTR_ERR(hclk); + + clk_prepare_enable(hclk); + + writel_relaxed((mode << GSBI_PROTOCOL_SHIFT) | crci, + base + GSBI_CTRL_REG); + + /* make sure the gsbi control write is not reordered */ + wmb(); + + clk_disable_unprepare(hclk); + + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); +} + +static const struct of_device_id gsbi_dt_match[] = { + { .compatible = "qcom,gsbi-v1.0.0", }, +}; + +MODULE_DEVICE_TABLE(of, gsbi_dt_match); + +static struct platform_driver gsbi_driver = { + .driver = { + .name = "gsbi", + .owner = THIS_MODULE, + .of_match_table = gsbi_dt_match, + }, + .probe = gsbi_probe, +}; + +module_platform_driver(gsbi_driver); + +MODULE_AUTHOR("Andy Gross "); +MODULE_DESCRIPTION("QCOM GSBI driver"); +MODULE_LICENSE("GPL v2"); From 44127b771d9c31dcb5ab90d9093a4d48877738bc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 19 May 2014 13:05:59 -0700 Subject: [PATCH 072/101] bus: add Broadcom GISB bus arbiter timeout/error handler This patch adds support for the Broadcom GISB arbiter bus timeout/error handler. GISB is a proprietary bus used by Broadcom Set Top Box System-on-a-chip devices (BCM7xxx) which allows multiple masters and clients to be interfaced with each other. The bus arbiter offers support for generating two interrupts towards the host CPU, thus allowing us to "catch" clock gated masters, or masters being volontarily blocked for powersaving purposes, or do general system troubleshooting. We also register a hook with the ARM fault exception handling to allow printing a more informative message than "imprecise external abort at 0x00000000" for instance. Signed-off-by: Florian Fainelli Signed-off-by: Arnd Bergmann --- drivers/bus/Kconfig | 8 + drivers/bus/Makefile | 1 + drivers/bus/brcmstb_gisb.c | 289 +++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 drivers/bus/brcmstb_gisb.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 552373c4e362..d40d155f4234 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -4,6 +4,14 @@ menu "Bus devices" +config BRCMSTB_GISB_ARB + bool "Broadcom STB GISB bus arbiter" + depends on ARM + help + Driver for the Broadcom Set Top Box System-on-a-chip internal bus + arbiter. This driver provides timeout and target abort error handling + and internal bus master decoding. + config IMX_WEIM bool "Freescale EIM DRIVER" depends on ARCH_MXC diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8947bdd0de8b..41fa45b3f061 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -2,6 +2,7 @@ # Makefile for the bus drivers. # +obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c new file mode 100644 index 000000000000..6159b7752a64 --- /dev/null +++ b/drivers/bus/brcmstb_gisb.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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. + * + * 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 +#include + +#define ARB_TIMER 0x008 +#define ARB_ERR_CAP_CLR 0x7e4 +#define ARB_ERR_CAP_CLEAR (1 << 0) +#define ARB_ERR_CAP_HI_ADDR 0x7e8 +#define ARB_ERR_CAP_ADDR 0x7ec +#define ARB_ERR_CAP_DATA 0x7f0 +#define ARB_ERR_CAP_STATUS 0x7f4 +#define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12) +#define ARB_ERR_CAP_STATUS_TEA (1 << 11) +#define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2) +#define ARB_ERR_CAP_STATUS_BS_MASK 0x3c +#define ARB_ERR_CAP_STATUS_WRITE (1 << 1) +#define ARB_ERR_CAP_STATUS_VALID (1 << 0) +#define ARB_ERR_CAP_MASTER 0x7f8 + +struct brcmstb_gisb_arb_device { + void __iomem *base; + struct mutex lock; + struct list_head next; + u32 valid_mask; + const char *master_names[sizeof(u32) * BITS_PER_BYTE]; +}; + +static LIST_HEAD(brcmstb_gisb_arb_device_list); + +static ssize_t gisb_arb_get_timeout(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + u32 timeout; + + mutex_lock(&gdev->lock); + timeout = ioread32(gdev->base + ARB_TIMER); + mutex_unlock(&gdev->lock); + + return sprintf(buf, "%d", timeout); +} + +static ssize_t gisb_arb_set_timeout(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + int val, ret; + + ret = kstrtoint(buf, 10, &val); + if (ret < 0) + return ret; + + if (val == 0 || val >= 0xffffffff) + return -EINVAL; + + mutex_lock(&gdev->lock); + iowrite32(val, gdev->base + ARB_TIMER); + mutex_unlock(&gdev->lock); + + return count; +} + +static const char * +brcmstb_gisb_master_to_str(struct brcmstb_gisb_arb_device *gdev, + u32 masters) +{ + u32 mask = gdev->valid_mask & masters; + + if (hweight_long(mask) != 1) + return NULL; + + return gdev->master_names[ffs(mask) - 1]; +} + +static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, + const char *reason) +{ + u32 cap_status; + unsigned long arb_addr; + u32 master; + const char *m_name; + char m_fmt[11]; + + cap_status = ioread32(gdev->base + ARB_ERR_CAP_STATUS); + + /* Invalid captured address, bail out */ + if (!(cap_status & ARB_ERR_CAP_STATUS_VALID)) + return 1; + + /* Read the address and master */ + arb_addr = ioread32(gdev->base + ARB_ERR_CAP_ADDR) & 0xffffffff; +#if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) + arb_addr |= (u64)ioread32(gdev->base + ARB_ERR_CAP_HI_ADDR) << 32; +#endif + master = ioread32(gdev->base + ARB_ERR_CAP_MASTER); + + m_name = brcmstb_gisb_master_to_str(gdev, master); + if (!m_name) { + snprintf(m_fmt, sizeof(m_fmt), "0x%08x", master); + m_name = m_fmt; + } + + pr_crit("%s: %s at 0x%lx [%c %s], core: %s\n", + __func__, reason, arb_addr, + cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R', + cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "", + m_name); + + /* clear the GISB error */ + iowrite32(ARB_ERR_CAP_CLEAR, gdev->base + ARB_ERR_CAP_CLR); + + return 0; +} + +static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + int ret = 0; + struct brcmstb_gisb_arb_device *gdev; + + /* iterate over each GISB arb registered handlers */ + list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next) + ret |= brcmstb_gisb_arb_decode_addr(gdev, "bus error"); + /* + * If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10)) + regs->ARM_pc += 4; + + return ret; +} + +void __init brcmstb_hook_fault_code(void) +{ + hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, + "imprecise external abort"); +} + +static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) +{ + brcmstb_gisb_arb_decode_addr(dev_id, "timeout"); + + return IRQ_HANDLED; +} + +static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id) +{ + brcmstb_gisb_arb_decode_addr(dev_id, "target abort"); + + return IRQ_HANDLED; +} + +static DEVICE_ATTR(gisb_arb_timeout, S_IWUSR | S_IRUGO, + gisb_arb_get_timeout, gisb_arb_set_timeout); + +static struct attribute *gisb_arb_sysfs_attrs[] = { + &dev_attr_gisb_arb_timeout.attr, + NULL, +}; + +static struct attribute_group gisb_arb_sysfs_attr_group = { + .attrs = gisb_arb_sysfs_attrs, +}; + +static int brcmstb_gisb_arb_probe(struct platform_device *pdev) +{ + struct device_node *dn = pdev->dev.of_node; + struct brcmstb_gisb_arb_device *gdev; + struct resource *r; + int err, timeout_irq, tea_irq; + unsigned int num_masters, j = 0; + int i, first, last; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + timeout_irq = platform_get_irq(pdev, 0); + tea_irq = platform_get_irq(pdev, 1); + + gdev = devm_kzalloc(&pdev->dev, sizeof(*gdev), GFP_KERNEL); + if (!gdev) + return -ENOMEM; + + mutex_init(&gdev->lock); + INIT_LIST_HEAD(&gdev->next); + + gdev->base = devm_request_and_ioremap(&pdev->dev, r); + if (!gdev->base) + return -ENOMEM; + + err = devm_request_irq(&pdev->dev, timeout_irq, + brcmstb_gisb_timeout_handler, 0, pdev->name, + gdev); + if (err < 0) + return err; + + err = devm_request_irq(&pdev->dev, tea_irq, + brcmstb_gisb_tea_handler, 0, pdev->name, + gdev); + if (err < 0) + return err; + + /* If we do not have a valid mask, assume all masters are enabled */ + if (of_property_read_u32(dn, "brcm,gisb-arb-master-mask", + &gdev->valid_mask)) + gdev->valid_mask = 0xffffffff; + + /* Proceed with reading the litteral names if we agree on the + * number of masters + */ + num_masters = of_property_count_strings(dn, + "brcm,gisb-arb-master-names"); + if (hweight_long(gdev->valid_mask) == num_masters) { + first = ffs(gdev->valid_mask) - 1; + last = fls(gdev->valid_mask) - 1; + + for (i = first; i < last; i++) { + if (!(gdev->valid_mask & BIT(i))) + continue; + + of_property_read_string_index(dn, + "brcm,gisb-arb-master-names", j, + &gdev->master_names[i]); + j++; + } + } + + err = sysfs_create_group(&pdev->dev.kobj, &gisb_arb_sysfs_attr_group); + if (err) + return err; + + platform_set_drvdata(pdev, gdev); + + list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); + + dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", + gdev->base, timeout_irq, tea_irq); + + return 0; +} + +static const struct of_device_id brcmstb_gisb_arb_of_match[] = { + { .compatible = "brcm,gisb-arb" }, + { }, +}; + +static struct platform_driver brcmstb_gisb_arb_driver = { + .probe = brcmstb_gisb_arb_probe, + .driver = { + .name = "brcm-gisb-arb", + .owner = THIS_MODULE, + .of_match_table = brcmstb_gisb_arb_of_match, + }, +}; + +static int __init brcm_gisb_driver_init(void) +{ + return platform_driver_register(&brcmstb_gisb_arb_driver); +} + +module_init(brcm_gisb_driver_init); From e53c07724809bd3e103174fd27c5b6f00582a252 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 19 May 2014 13:06:00 -0700 Subject: [PATCH 073/101] Documentation: devicetree: add Broadcom GISB arbiter bindings This patch adds the Broadcom GISB arbiter Device Tree binding that is used on all Broadcom Set-top-box System-on-a-chip. Signed-off-by: Florian Fainelli Signed-off-by: Arnd Bergmann --- .../devicetree/bindings/bus/brcm,gisb-arb.txt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt diff --git a/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt b/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt new file mode 100644 index 000000000000..e2d501d20c9a --- /dev/null +++ b/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt @@ -0,0 +1,30 @@ +Broadcom GISB bus Arbiter controller + +Required properties: + +- compatible: should be "brcm,gisb-arb" +- reg: specifies the base physical address and size of the registers +- interrupt-parent: specifies the phandle to the parent interrupt controller + this arbiter gets interrupt line from +- interrupts: specifies the two interrupts (timeout and TEA) to be used from + the parent interrupt controller + +Optional properties: + +- brcm,gisb-arb-master-mask: 32-bits wide bitmask used to specify which GISB + masters are valid at the system level +- brcm,gisb-arb-master-names: string list of the litteral name of the GISB + masters. Should match the number of bits set in brcm,gisb-master-mask and + the order in which they appear + +Example: + +gisb-arb@f0400000 { + compatible = "brcm,gisb-arb"; + reg = <0xf0400000 0x800>; + interrupts = <0>, <2>; + interrupt-parent = <&sun_l2_intc>; + + brcm,gisb-arb-master-mask = <0x7>; + brcm,gisb-arb-master-names = "bsp_0", "scpu_0", "cpu_0"; +}; From 7a1002e5ba6ebf17f8040f5176502527df8c38e8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 19 May 2014 13:06:01 -0700 Subject: [PATCH 074/101] Documentation: sysfs: add Broadcom GISB arbiter sysfs properties This patch documents the sysfs properties exposed by the Broadcom GISB bus arbiter hardware. Signed-off-by: Florian Fainelli Signed-off-by: Arnd Bergmann --- Documentation/ABI/testing/sysfs-platform-brcmstb-gisb-arb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-brcmstb-gisb-arb diff --git a/Documentation/ABI/testing/sysfs-platform-brcmstb-gisb-arb b/Documentation/ABI/testing/sysfs-platform-brcmstb-gisb-arb new file mode 100644 index 000000000000..f1bad92bbe27 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-brcmstb-gisb-arb @@ -0,0 +1,8 @@ +What: /sys/devices/../../gisb_arb_timeout +Date: May 2014 +KernelVersion: 3.17 +Contact: Florian Fainelli +Description: + Returns the currently configured raw timeout value of the + Broadcom Set Top Box internal GISB bus arbiter. Minimum value + is 1, and maximum value is 0xffffffff. From 9a8f39950d276bc77d3eb22bfc798c4612ee3c29 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Fri, 9 May 2014 06:43:26 +0900 Subject: [PATCH 075/101] ARM: EXYNOS: Move arm core power down clock to exynos5250 common clock Now with common clock support added for exynos5250 it is necessary to move this code to exynos5250 common clock driver as clock registers should be handled there. This change is tested in exynos5250 based arndale platform. Cc: Abhilash Kesavan Cc: Thomas Abraham Acked-by: Kukjin Kim Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Amit Daniel Kachhap [t.figa: Rebased onto current kernel sources.] Signed-off-by: Tomasz Figa Signed-off-by: Daniel Lezcano Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 54 ---------------------------- drivers/clk/samsung/clk-exynos5250.c | 42 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index c57cae0e8779..8125a1534093 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -41,25 +41,6 @@ #define S5P_CHECK_AFTR 0xFCBA0D10 -#define EXYNOS5_PWR_CTRL1 (S5P_VA_CMU + 0x01020) -#define EXYNOS5_PWR_CTRL2 (S5P_VA_CMU + 0x01024) - -#define PWR_CTRL1_CORE2_DOWN_RATIO (7 << 28) -#define PWR_CTRL1_CORE1_DOWN_RATIO (7 << 16) -#define PWR_CTRL1_DIV2_DOWN_EN (1 << 9) -#define PWR_CTRL1_DIV1_DOWN_EN (1 << 8) -#define PWR_CTRL1_USE_CORE1_WFE (1 << 5) -#define PWR_CTRL1_USE_CORE0_WFE (1 << 4) -#define PWR_CTRL1_USE_CORE1_WFI (1 << 1) -#define PWR_CTRL1_USE_CORE0_WFI (1 << 0) - -#define PWR_CTRL2_DIV2_UP_EN (1 << 25) -#define PWR_CTRL2_DIV1_UP_EN (1 << 24) -#define PWR_CTRL2_DUR_STANDBY2_VAL (1 << 16) -#define PWR_CTRL2_DUR_STANDBY1_VAL (1 << 8) -#define PWR_CTRL2_CORE2_UP_RATIO (1 << 4) -#define PWR_CTRL2_CORE1_UP_RATIO (1 << 0) - static int exynos4_enter_lowpower(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); @@ -182,46 +163,11 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev, return exynos4_enter_core0_aftr(dev, drv, new_index); } -static void __init exynos5_core_down_clk(void) -{ - unsigned int tmp; - - /* - * Enable arm clock down (in idle) and set arm divider - * ratios in WFI/WFE state. - */ - tmp = PWR_CTRL1_CORE2_DOWN_RATIO | \ - PWR_CTRL1_CORE1_DOWN_RATIO | \ - PWR_CTRL1_DIV2_DOWN_EN | \ - PWR_CTRL1_DIV1_DOWN_EN | \ - PWR_CTRL1_USE_CORE1_WFE | \ - PWR_CTRL1_USE_CORE0_WFE | \ - PWR_CTRL1_USE_CORE1_WFI | \ - PWR_CTRL1_USE_CORE0_WFI; - __raw_writel(tmp, EXYNOS5_PWR_CTRL1); - - /* - * Enable arm clock up (on exiting idle). Set arm divider - * ratios when not in idle along with the standby duration - * ratios. - */ - tmp = PWR_CTRL2_DIV2_UP_EN | \ - PWR_CTRL2_DIV1_UP_EN | \ - PWR_CTRL2_DUR_STANDBY2_VAL | \ - PWR_CTRL2_DUR_STANDBY1_VAL | \ - PWR_CTRL2_CORE2_UP_RATIO | \ - PWR_CTRL2_CORE1_UP_RATIO; - __raw_writel(tmp, EXYNOS5_PWR_CTRL2); -} - static int exynos_cpuidle_probe(struct platform_device *pdev) { int cpu_id, ret; struct cpuidle_device *device; - if (soc_is_exynos5250()) - exynos5_core_down_clk(); - if (soc_is_exynos5440()) exynos4_idle_driver.state_count = 1; diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 88488596c00b..1416c9703266 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -24,6 +24,8 @@ #define APLL_CON0 0x100 #define SRC_CPU 0x200 #define DIV_CPU0 0x500 +#define PWR_CTRL1 0x1020 +#define PWR_CTRL2 0x1024 #define MPLL_LOCK 0x4000 #define MPLL_CON0 0x4100 #define SRC_CORE1 0x4204 @@ -82,6 +84,23 @@ #define SRC_CDREX 0x20200 #define PLL_DIV2_SEL 0x20a24 +/*Below definitions are used for PWR_CTRL settings*/ +#define PWR_CTRL1_CORE2_DOWN_RATIO (7 << 28) +#define PWR_CTRL1_CORE1_DOWN_RATIO (7 << 16) +#define PWR_CTRL1_DIV2_DOWN_EN (1 << 9) +#define PWR_CTRL1_DIV1_DOWN_EN (1 << 8) +#define PWR_CTRL1_USE_CORE1_WFE (1 << 5) +#define PWR_CTRL1_USE_CORE0_WFE (1 << 4) +#define PWR_CTRL1_USE_CORE1_WFI (1 << 1) +#define PWR_CTRL1_USE_CORE0_WFI (1 << 0) + +#define PWR_CTRL2_DIV2_UP_EN (1 << 25) +#define PWR_CTRL2_DIV1_UP_EN (1 << 24) +#define PWR_CTRL2_DUR_STANDBY2_VAL (1 << 16) +#define PWR_CTRL2_DUR_STANDBY1_VAL (1 << 8) +#define PWR_CTRL2_CORE2_UP_RATIO (1 << 4) +#define PWR_CTRL2_CORE1_UP_RATIO (1 << 0) + /* list of PLLs to be registered */ enum exynos5250_plls { apll, mpll, cpll, epll, vpll, gpll, bpll, @@ -100,6 +119,8 @@ static struct samsung_clk_reg_dump *exynos5250_save; static unsigned long exynos5250_clk_regs[] __initdata = { SRC_CPU, DIV_CPU0, + PWR_CTRL1, + PWR_CTRL2, SRC_CORE1, SRC_TOP0, SRC_TOP1, @@ -701,6 +722,7 @@ static struct of_device_id ext_clk_match[] __initdata = { static void __init exynos5250_clk_init(struct device_node *np) { struct samsung_clk_provider *ctx; + unsigned int tmp; if (np) { reg_base = of_iomap(np, 0); @@ -741,6 +763,26 @@ static void __init exynos5250_clk_init(struct device_node *np) samsung_clk_register_gate(ctx, exynos5250_gate_clks, ARRAY_SIZE(exynos5250_gate_clks)); + /* + * Enable arm clock down (in idle) and set arm divider + * ratios in WFI/WFE state. + */ + tmp = (PWR_CTRL1_CORE2_DOWN_RATIO | PWR_CTRL1_CORE1_DOWN_RATIO | + PWR_CTRL1_DIV2_DOWN_EN | PWR_CTRL1_DIV1_DOWN_EN | + PWR_CTRL1_USE_CORE1_WFE | PWR_CTRL1_USE_CORE0_WFE | + PWR_CTRL1_USE_CORE1_WFI | PWR_CTRL1_USE_CORE0_WFI); + __raw_writel(tmp, reg_base + PWR_CTRL1); + + /* + * Enable arm clock up (on exiting idle). Set arm divider + * ratios when not in idle along with the standby duration + * ratios. + */ + tmp = (PWR_CTRL2_DIV2_UP_EN | PWR_CTRL2_DIV1_UP_EN | + PWR_CTRL2_DUR_STANDBY2_VAL | PWR_CTRL2_DUR_STANDBY1_VAL | + PWR_CTRL2_CORE2_UP_RATIO | PWR_CTRL2_CORE1_UP_RATIO); + __raw_writel(tmp, reg_base + PWR_CTRL2); + exynos5250_clk_sleep_init(); pr_info("Exynos5250: clock setup completed, armclk=%ld\n", From 53af16a1a8d6776b19747291b39f0d4cbda42d92 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:26 +0900 Subject: [PATCH 076/101] ARM: EXYNOS: Prevent forward declaration for cpuidle Move the structure below the 'exynos4_enter_lowpower' function so no more need of forward declaration. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 40 +++++++++++++++------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 8125a1534093..56780c64a620 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -41,30 +41,8 @@ #define S5P_CHECK_AFTR 0xFCBA0D10 -static int exynos4_enter_lowpower(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index); - static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device); -static struct cpuidle_driver exynos4_idle_driver = { - .name = "exynos4_idle", - .owner = THIS_MODULE, - .states = { - [0] = ARM_CPUIDLE_WFI_STATE, - [1] = { - .enter = exynos4_enter_lowpower, - .exit_latency = 300, - .target_residency = 100000, - .flags = CPUIDLE_FLAG_TIME_VALID, - .name = "C1", - .desc = "ARM power down", - }, - }, - .state_count = 2, - .safe_state_index = 0, -}; - /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ static void exynos4_set_wakeupmask(void) { @@ -163,6 +141,24 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev, return exynos4_enter_core0_aftr(dev, drv, new_index); } +static struct cpuidle_driver exynos4_idle_driver = { + .name = "exynos4_idle", + .owner = THIS_MODULE, + .states = { + [0] = ARM_CPUIDLE_WFI_STATE, + [1] = { + .enter = exynos4_enter_lowpower, + .exit_latency = 300, + .target_residency = 100000, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "C1", + .desc = "ARM power down", + }, + }, + .state_count = 2, + .safe_state_index = 0, +}; + static int exynos_cpuidle_probe(struct platform_device *pdev) { int cpu_id, ret; From 043c86b6f2d54cfee8ec67e1f7f49156a68b2bd2 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:26 +0900 Subject: [PATCH 077/101] ARM: EXYNOS: Use cpuidle_register Use the cpuidle generic function 'cpuidle_register'. That saves us from some extra lines of code and unneeded variables. A side effect of this change is a bug fix where before the cpuidle driver was registered for each_online_cpu and now it is for each_possible_cpu. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 56780c64a620..0093d369f099 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -41,8 +41,6 @@ #define S5P_CHECK_AFTR 0xFCBA0D10 -static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device); - /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ static void exynos4_set_wakeupmask(void) { @@ -161,29 +159,17 @@ static struct cpuidle_driver exynos4_idle_driver = { static int exynos_cpuidle_probe(struct platform_device *pdev) { - int cpu_id, ret; - struct cpuidle_device *device; + int ret; if (soc_is_exynos5440()) exynos4_idle_driver.state_count = 1; - ret = cpuidle_register_driver(&exynos4_idle_driver); + ret = cpuidle_register(&exynos4_idle_driver, NULL); if (ret) { dev_err(&pdev->dev, "failed to register cpuidle driver\n"); return ret; } - for_each_online_cpu(cpu_id) { - device = &per_cpu(exynos4_cpuidle_device, cpu_id); - device->cpu = cpu_id; - - ret = cpuidle_register_device(device); - if (ret) { - dev_err(&pdev->dev, "failed to register cpuidle device\n"); - return ret; - } - } - return 0; } From 7880e45ed0f898fa2c45415a9cb5e9cb3860d1a3 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:26 +0900 Subject: [PATCH 078/101] ARM: EXYNOS: Change function name prefix for cpuidle The driver was initially written for exynos4 but the driver is used also for exynos5. Change the function prefix name exynos4 -> exynos Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 0093d369f099..15b9bda1ab6e 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-exynos4/cpuidle.c +/* linux/arch/arm/mach-exynos/cpuidle.c * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com @@ -42,7 +42,7 @@ #define S5P_CHECK_AFTR 0xFCBA0D10 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ -static void exynos4_set_wakeupmask(void) +static void exynos_set_wakeupmask(void) { __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); } @@ -73,13 +73,13 @@ static int idle_finisher(unsigned long flags) return 1; } -static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, +static int exynos_enter_core0_aftr(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { unsigned long tmp; - exynos4_set_wakeupmask(); + exynos_set_wakeupmask(); /* Set value of power down register for aftr mode */ exynos_sys_powerdown_conf(SYS_AFTR); @@ -123,7 +123,7 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, return index; } -static int exynos4_enter_lowpower(struct cpuidle_device *dev, +static int exynos_enter_lowpower(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { @@ -136,16 +136,16 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev, if (new_index == 0) return arm_cpuidle_simple_enter(dev, drv, new_index); else - return exynos4_enter_core0_aftr(dev, drv, new_index); + return exynos_enter_core0_aftr(dev, drv, new_index); } -static struct cpuidle_driver exynos4_idle_driver = { - .name = "exynos4_idle", +static struct cpuidle_driver exynos_idle_driver = { + .name = "exynos_idle", .owner = THIS_MODULE, .states = { [0] = ARM_CPUIDLE_WFI_STATE, [1] = { - .enter = exynos4_enter_lowpower, + .enter = exynos_enter_lowpower, .exit_latency = 300, .target_residency = 100000, .flags = CPUIDLE_FLAG_TIME_VALID, @@ -162,9 +162,9 @@ static int exynos_cpuidle_probe(struct platform_device *pdev) int ret; if (soc_is_exynos5440()) - exynos4_idle_driver.state_count = 1; + exynos_idle_driver.state_count = 1; - ret = cpuidle_register(&exynos4_idle_driver, NULL); + ret = cpuidle_register(&exynos_idle_driver, NULL); if (ret) { dev_err(&pdev->dev, "failed to register cpuidle driver\n"); return ret; From 309e08c4ca5446ab9f3e8c17581e5515855d339d Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:27 +0900 Subject: [PATCH 079/101] ARM: EXYNOS: Encapsulate register access inside a function for pm That makes the code cleaner and encapsulted. The function will be reused in the next patch. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/pm.c | 63 +++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 3f2ae864582e..86a46607969f 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -169,6 +169,42 @@ int exynos_cluster_power_state(int cluster) /* For Cortex-A9 Diagnostic and Power control register */ static unsigned int save_arm_register[2]; +static void exynos_cpu_save_register(void) +{ + unsigned long tmp; + + /* Save Power control register */ + asm ("mrc p15, 0, %0, c15, c0, 0" + : "=r" (tmp) : : "cc"); + + save_arm_register[0] = tmp; + + /* Save Diagnostic register */ + asm ("mrc p15, 0, %0, c15, c0, 1" + : "=r" (tmp) : : "cc"); + + save_arm_register[1] = tmp; +} + +static void exynos_cpu_restore_register(void) +{ + unsigned long tmp; + + /* Restore Power control register */ + tmp = save_arm_register[0]; + + asm volatile ("mcr p15, 0, %0, c15, c0, 0" + : : "r" (tmp) + : "cc"); + + /* Restore Diagnostic register */ + tmp = save_arm_register[1]; + + asm volatile ("mcr p15, 0, %0, c15, c0, 1" + : : "r" (tmp) + : "cc"); +} + static int exynos_cpu_suspend(unsigned long arg) { #ifdef CONFIG_CACHE_L2X0 @@ -228,17 +264,8 @@ static int exynos_pm_suspend(void) tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0); __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); - if (!soc_is_exynos5250()) { - /* Save Power control register */ - asm ("mrc p15, 0, %0, c15, c0, 0" - : "=r" (tmp) : : "cc"); - save_arm_register[0] = tmp; - - /* Save Diagnostic register */ - asm ("mrc p15, 0, %0, c15, c0, 1" - : "=r" (tmp) : : "cc"); - save_arm_register[1] = tmp; - } + if (!soc_is_exynos5250()) + exynos_cpu_save_register(); return 0; } @@ -262,19 +289,9 @@ static void exynos_pm_resume(void) /* No need to perform below restore code */ goto early_wakeup; } - if (!soc_is_exynos5250()) { - /* Restore Power control register */ - tmp = save_arm_register[0]; - asm volatile ("mcr p15, 0, %0, c15, c0, 0" - : : "r" (tmp) - : "cc"); - /* Restore Diagnostic register */ - tmp = save_arm_register[1]; - asm volatile ("mcr p15, 0, %0, c15, c0, 1" - : : "r" (tmp) - : "cc"); - } + if (!soc_is_exynos5250()) + exynos_cpu_restore_register(); /* For release retention */ From 20115fa874ce948f309b49d7b641d2cf55e09ad4 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:27 +0900 Subject: [PATCH 080/101] ARM: EXYNOS: Move some code inside the idle_finisher for cpuidle Move the code around to differentiate different section of code and prepare it to be factored out in the next patches. The call order changed but hat doesn't have a side effect because they are independent. The important call is cpu_do_idle() which must be done the last. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 15b9bda1ab6e..144ff1e7bbf8 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -69,7 +69,16 @@ static void restore_cpu_arch_register(void) static int idle_finisher(unsigned long flags) { + exynos_set_wakeupmask(); + + __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR); + __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); + + /* Set value of power down register for aftr mode */ + exynos_sys_powerdown_conf(SYS_AFTR); + cpu_do_idle(); + return 1; } @@ -79,14 +88,6 @@ static int exynos_enter_core0_aftr(struct cpuidle_device *dev, { unsigned long tmp; - exynos_set_wakeupmask(); - - /* Set value of power down register for aftr mode */ - exynos_sys_powerdown_conf(SYS_AFTR); - - __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR); - __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); - save_cpu_arch_register(); /* Setting Central Sequence Register for power down mode */ From 9bd5544af89618f2f5bee31a7ef24d987f2b3f1d Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:27 +0900 Subject: [PATCH 081/101] ARM: EXYNOS: Fix S5P_WAKEUP_STAT call for cpuidle This function should be called only when the powerdown sequence fails. Even if the current code does not hurt, by moving this line, we have the same code than the one in pm.c. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 144ff1e7bbf8..87f9f129883d 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -116,11 +116,10 @@ static int exynos_enter_core0_aftr(struct cpuidle_device *dev, if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { tmp |= S5P_CENTRAL_LOWPWR_CFG; __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); + /* Clear wakeup state register */ + __raw_writel(0x0, S5P_WAKEUP_STAT); } - /* Clear wakeup state register */ - __raw_writel(0x0, S5P_WAKEUP_STAT); - return index; } From 85f9f90808b4437bbdff1dff2c80663dae57ab51 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:43:27 +0900 Subject: [PATCH 082/101] ARM: EXYNOS: Use the cpu_pm notifier for pm Use the cpu_pm_enter/exit notifier to group some pm code inside the pm file. The save and restore code is duplicated across pm.c and cpuidle.c. By using the cpu_pm notifier, we can factor out the routine. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 24 ------------------------ arch/arm/mach-exynos/pm.c | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 87f9f129883d..7a2d01bbef6b 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -47,26 +47,6 @@ static void exynos_set_wakeupmask(void) __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); } -static unsigned int g_pwr_ctrl, g_diag_reg; - -static void save_cpu_arch_register(void) -{ - /*read power control register*/ - asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl) : : "cc"); - /*read diagnostic register*/ - asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); - return; -} - -static void restore_cpu_arch_register(void) -{ - /*write power control register*/ - asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl) : "cc"); - /*write diagnostic register*/ - asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); - return; -} - static int idle_finisher(unsigned long flags) { exynos_set_wakeupmask(); @@ -88,8 +68,6 @@ static int exynos_enter_core0_aftr(struct cpuidle_device *dev, { unsigned long tmp; - save_cpu_arch_register(); - /* Setting Central Sequence Register for power down mode */ tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); tmp &= ~S5P_CENTRAL_LOWPWR_CFG; @@ -104,8 +82,6 @@ static int exynos_enter_core0_aftr(struct cpuidle_device *dev, #endif cpu_pm_exit(); - restore_cpu_arch_register(); - /* * If PMU failed while entering sleep mode, WFI will be * ignored by PMU and then exiting cpu_do_idle(). diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 86a46607969f..3eb80cd4607d 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -387,10 +388,36 @@ static const struct platform_suspend_ops exynos_suspend_ops = { .valid = suspend_valid_only_mem, }; +static int exynos_cpu_pm_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + int cpu = smp_processor_id(); + + switch (cmd) { + case CPU_PM_ENTER: + if (cpu == 0) + exynos_cpu_save_register(); + break; + + case CPU_PM_EXIT: + if (cpu == 0) + exynos_cpu_restore_register(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block exynos_cpu_pm_notifier_block = { + .notifier_call = exynos_cpu_pm_notifier, +}; + void __init exynos_pm_init(void) { u32 tmp; + cpu_pm_register_notifier(&exynos_cpu_pm_notifier_block); + /* Platform-specific GIC callback */ gic_arch_extn.irq_set_wake = exynos_irq_set_wake; From 795537daae8a14dd05923ee78b687a821460d4f1 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:50:16 +0900 Subject: [PATCH 083/101] ARM: EXYNOS: Move scu_enable in the cpu_pm notifier We make the cpuidle code less arch dependent. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 6 ------ arch/arm/mach-exynos/pm.c | 7 ++++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 7a2d01bbef6b..bf1bf87af00e 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -75,11 +74,6 @@ static int exynos_enter_core0_aftr(struct cpuidle_device *dev, cpu_pm_enter(); cpu_suspend(0, idle_finisher); - -#ifdef CONFIG_SMP - if (!soc_is_exynos5250()) - scu_enable(S5P_VA_SCU); -#endif cpu_pm_exit(); /* diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 3eb80cd4607d..fb1d9160991a 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -400,8 +400,13 @@ static int exynos_cpu_pm_notifier(struct notifier_block *self, break; case CPU_PM_EXIT: - if (cpu == 0) + if (cpu == 0) { +#ifdef CONFIG_SMP + if (!soc_is_exynos5250()) + scu_enable(S5P_VA_SCU); +#endif exynos_cpu_restore_register(); + } break; } From 58afbc61a33928a764460bfa4ea85c9dbe9d0a77 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:53:26 +0900 Subject: [PATCH 084/101] ARM: EXYNOS: Remove ifdef for scu_enable in pm The scu_enable function is already a noop in the scu's header file is CONFIG_SMP=n, so no need to use these macros in the code. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/pm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index fb1d9160991a..24c638d2afd4 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -310,7 +310,7 @@ static void exynos_pm_resume(void) s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); - if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250()) + if (!soc_is_exynos5250()) scu_enable(S5P_VA_SCU); early_wakeup: @@ -401,10 +401,8 @@ static int exynos_cpu_pm_notifier(struct notifier_block *self, case CPU_PM_EXIT: if (cpu == 0) { -#ifdef CONFIG_SMP if (!soc_is_exynos5250()) scu_enable(S5P_VA_SCU); -#endif exynos_cpu_restore_register(); } break; From 623f22665c790f82a571653c01e949834490a55a Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:52:59 +0900 Subject: [PATCH 085/101] ARM: EXYNOS: Pass wakeup mask parameter to function for cpuidle Pass the wakeup mask to 'exynos_set_wakeupmask' as this function could be used for different idle states with different mask. Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index bf1bf87af00e..717dbf28ede7 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -41,14 +41,14 @@ #define S5P_CHECK_AFTR 0xFCBA0D10 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ -static void exynos_set_wakeupmask(void) +static void exynos_set_wakeupmask(long mask) { - __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); + __raw_writel(mask, S5P_WAKEUP_MASK); } static int idle_finisher(unsigned long flags) { - exynos_set_wakeupmask(); + exynos_set_wakeupmask(0x0000ff3e); __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR); __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); From dcef663d49e3beb09bc3cb436c997ce40aaf80fa Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:52:59 +0900 Subject: [PATCH 086/101] ARM: EXYNOS: Encapsulate boot vector code into a function for cpuidle Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 717dbf28ede7..4e46a12b4056 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -46,13 +46,16 @@ static void exynos_set_wakeupmask(long mask) __raw_writel(mask, S5P_WAKEUP_MASK); } +static void exynos_cpu_set_boot_vector(long flags) +{ + __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR); + __raw_writel(flags, REG_DIRECTGO_FLAG); +} + static int idle_finisher(unsigned long flags) { exynos_set_wakeupmask(0x0000ff3e); - - __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR); - __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); - + exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); /* Set value of power down register for aftr mode */ exynos_sys_powerdown_conf(SYS_AFTR); From 70ecb842ba19d205a1e5d6c3de5656e1cb986284 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:52:59 +0900 Subject: [PATCH 087/101] ARM: EXYNOS: Disable cpuidle for exynos5440 There is no point to register the cpuidle driver for the 5440 as it has only one WFI state which is the default idle function when the cpuidle driver is disabled. By disabling cpuidle we prevent to enter to the governor computation for nothing, thus saving a lot of processing time. The only drawback is the statistic via sysfs on this state which is lost but it is meaningless and it could be retrieved from the ftrace easily. Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Acked-by: Amit Kucheria Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 3 --- arch/arm/mach-exynos/exynos.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 4e46a12b4056..862d6beae4af 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -134,9 +134,6 @@ static int exynos_cpuidle_probe(struct platform_device *pdev) { int ret; - if (soc_is_exynos5440()) - exynos_idle_driver.state_count = 1; - ret = cpuidle_register(&exynos_idle_driver, NULL); if (ret) { dev_err(&pdev->dev, "failed to register cpuidle driver\n"); diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index e973ff5de7b3..89872405cccd 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -177,6 +177,9 @@ static struct platform_device exynos_cpuidle = { void __init exynos_cpuidle_init(void) { + if (soc_is_exynos5440()) + return; + platform_device_register(&exynos_cpuidle); } From 712bb69e89c575b793954eb641958f992f4b5f78 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:52:59 +0900 Subject: [PATCH 088/101] ARM: EXYNOS: Encapsulate the AFTR code into a function Let's encapsulate the AFTR state specific call into a single function. Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 862d6beae4af..d8b80c053e42 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -52,13 +52,17 @@ static void exynos_cpu_set_boot_vector(long flags) __raw_writel(flags, REG_DIRECTGO_FLAG); } -static int idle_finisher(unsigned long flags) +static void exynos_enter_aftr(void) { exynos_set_wakeupmask(0x0000ff3e); exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); /* Set value of power down register for aftr mode */ exynos_sys_powerdown_conf(SYS_AFTR); +} +static int idle_finisher(unsigned long flags) +{ + exynos_enter_aftr(); cpu_do_idle(); return 1; From 3681bafeb1e4781bdeaecd19aa8c9f6d0db90f6f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:53:00 +0900 Subject: [PATCH 089/101] ARM: EXYNOS: Move the AFTR state function into pm.c In order to remove depedency on pm code, let's move the 'exynos_enter_aftr' function into the pm.c file as well as the other helper functions. Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/common.h | 1 + arch/arm/mach-exynos/cpuidle.c | 29 ----------------------------- arch/arm/mach-exynos/pm.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 7876ed04b7a5..27c9abdeb79e 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -69,5 +69,6 @@ extern int exynos_cpu_power_state(int cpu); extern void exynos_cluster_power_down(int cluster); extern void exynos_cluster_power_up(int cluster); extern int exynos_cluster_power_state(int cluster); +extern void exynos_enter_aftr(void); #endif /* __ARCH_ARM_MACH_EXYNOS_COMMON_H */ diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index d8b80c053e42..eea3eb9cecef 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -31,35 +31,6 @@ #include "common.h" #include "regs-pmu.h" -#define REG_DIRECTGO_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ - S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ - (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0)) -#define REG_DIRECTGO_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ - S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ - (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) - -#define S5P_CHECK_AFTR 0xFCBA0D10 - -/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ -static void exynos_set_wakeupmask(long mask) -{ - __raw_writel(mask, S5P_WAKEUP_MASK); -} - -static void exynos_cpu_set_boot_vector(long flags) -{ - __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR); - __raw_writel(flags, REG_DIRECTGO_FLAG); -} - -static void exynos_enter_aftr(void) -{ - exynos_set_wakeupmask(0x0000ff3e); - exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); - /* Set value of power down register for aftr mode */ - exynos_sys_powerdown_conf(SYS_AFTR); -} - static int idle_finisher(unsigned long flags) { exynos_enter_aftr(); diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 24c638d2afd4..179f7e09e20d 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -167,6 +167,35 @@ int exynos_cluster_power_state(int cluster) S5P_CORE_LOCAL_PWR_EN); } +#define EXYNOS_BOOT_VECTOR_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ + S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ + (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0)) +#define EXYNOS_BOOT_VECTOR_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ + S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ + (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) + +#define S5P_CHECK_AFTR 0xFCBA0D10 + +/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ +static void exynos_set_wakeupmask(long mask) +{ + __raw_writel(mask, S5P_WAKEUP_MASK); +} + +static void exynos_cpu_set_boot_vector(long flags) +{ + __raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR); + __raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG); +} + +void exynos_enter_aftr(void) +{ + exynos_set_wakeupmask(0x0000ff3e); + exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); + /* Set value of power down register for aftr mode */ + exynos_sys_powerdown_conf(SYS_AFTR); +} + /* For Cortex-A9 Diagnostic and Power control register */ static unsigned int save_arm_register[2]; From 0ebc13e2a2353f76ecdca11cf4d49da0b4e77f09 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:55:12 +0900 Subject: [PATCH 090/101] ARM: EXYNOS: Move the power sequence call in the cpu_pm notifier The code to initiate and exit the powerdown sequence is the same in pm.c and cpuidle.c. Let's split the common part in the pm.c and reuse it from the cpu_pm notifier. That is one more step forward to make the cpuidle driver arch indenpendant. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 22 ---------------------- arch/arm/mach-exynos/pm.c | 27 ++++++++++++++++++++++----- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index eea3eb9cecef..cac51d852605 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -29,7 +29,6 @@ #include #include "common.h" -#include "regs-pmu.h" static int idle_finisher(unsigned long flags) { @@ -43,31 +42,10 @@ static int exynos_enter_core0_aftr(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - unsigned long tmp; - - /* Setting Central Sequence Register for power down mode */ - tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); - tmp &= ~S5P_CENTRAL_LOWPWR_CFG; - __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); - cpu_pm_enter(); cpu_suspend(0, idle_finisher); cpu_pm_exit(); - /* - * If PMU failed while entering sleep mode, WFI will be - * ignored by PMU and then exiting cpu_do_idle(). - * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically - * in this situation. - */ - tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); - if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { - tmp |= S5P_CENTRAL_LOWPWR_CFG; - __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); - /* Clear wakeup state register */ - __raw_writel(0x0, S5P_WAKEUP_STAT); - } - return index; } diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 179f7e09e20d..8b268b5b42fa 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -279,15 +279,21 @@ static void exynos_pm_prepare(void) __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); } -static int exynos_pm_suspend(void) +static void exynos_pm_central_suspend(void) { unsigned long tmp; /* Setting Central Sequence Register for power down mode */ - tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); tmp &= ~S5P_CENTRAL_LOWPWR_CFG; __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); +} + +static int exynos_pm_suspend(void) +{ + unsigned long tmp; + + exynos_pm_central_suspend(); /* Setting SEQ_OPTION register */ @@ -300,7 +306,7 @@ static int exynos_pm_suspend(void) return 0; } -static void exynos_pm_resume(void) +static int exynos_pm_central_resume(void) { unsigned long tmp; @@ -317,9 +323,17 @@ static void exynos_pm_resume(void) /* clear the wakeup state register */ __raw_writel(0x0, S5P_WAKEUP_STAT); /* No need to perform below restore code */ - goto early_wakeup; + return -1; } + return 0; +} + +static void exynos_pm_resume(void) +{ + if (exynos_pm_central_resume()) + goto early_wakeup; + if (!soc_is_exynos5250()) exynos_cpu_restore_register(); @@ -424,8 +438,10 @@ static int exynos_cpu_pm_notifier(struct notifier_block *self, switch (cmd) { case CPU_PM_ENTER: - if (cpu == 0) + if (cpu == 0) { + exynos_pm_central_suspend(); exynos_cpu_save_register(); + } break; case CPU_PM_EXIT: @@ -433,6 +449,7 @@ static int exynos_cpu_pm_notifier(struct notifier_block *self, if (!soc_is_exynos5250()) scu_enable(S5P_VA_SCU); exynos_cpu_restore_register(); + exynos_pm_central_resume(); } break; } From e30b154bd2df3be95deda85ce5bc5a05a18896ef Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:56:24 +0900 Subject: [PATCH 091/101] ARM: EXYNOS: Move S5P_CHECK_SLEEP into pm.c This macro is only used there. Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/pm.c | 3 ++- arch/arm/mach-exynos/regs-pmu.h | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 8b268b5b42fa..f7b0b77582f6 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -174,7 +174,8 @@ int exynos_cluster_power_state(int cluster) S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) -#define S5P_CHECK_AFTR 0xFCBA0D10 +#define S5P_CHECK_AFTR 0xFCBA0D10 +#define S5P_CHECK_SLEEP 0x00000BAD /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ static void exynos_set_wakeupmask(long mask) diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h index 4179f6a6d595..1d13b08708f0 100644 --- a/arch/arm/mach-exynos/regs-pmu.h +++ b/arch/arm/mach-exynos/regs-pmu.h @@ -129,8 +129,6 @@ #define S5P_CORE_LOCAL_PWR_EN 0x3 #define S5P_INT_LOCAL_PWR_EN 0x7 -#define S5P_CHECK_SLEEP 0x00000BAD - /* Only for EXYNOS4210 */ #define S5P_CMU_CLKSTOP_LCD1_LOWPWR S5P_PMUREG(0x1154) #define S5P_CMU_RESET_LCD1_LOWPWR S5P_PMUREG(0x1174) From 277f50464d8b2e68a05cfcac765a0e54fd382d1f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:56:29 +0900 Subject: [PATCH 092/101] ARM: EXYNOS: Pass the AFTR callback to the platform_data No more dependency on the arch code. The platform_data field is used to set the PM callback as the other cpuidle drivers. Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 4 +++- arch/arm/mach-exynos/exynos.c | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index cac51d852605..05cb00c79406 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -28,7 +28,7 @@ #include -#include "common.h" +static void (*exynos_enter_aftr)(void); static int idle_finisher(unsigned long flags) { @@ -87,6 +87,8 @@ static int exynos_cpuidle_probe(struct platform_device *pdev) { int ret; + exynos_enter_aftr = (void *)(pdev->dev.platform_data); + ret = cpuidle_register(&exynos_idle_driver, NULL); if (ret) { dev_err(&pdev->dev, "failed to register cpuidle driver\n"); diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 89872405cccd..7d9d8762b56e 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -171,8 +171,9 @@ void exynos_restart(enum reboot_mode mode, const char *cmd) } static struct platform_device exynos_cpuidle = { - .name = "exynos_cpuidle", - .id = -1, + .name = "exynos_cpuidle", + .dev.platform_data = exynos_enter_aftr, + .id = -1, }; void __init exynos_cpuidle_init(void) From ce4305d2a5ad76d80c8edfd2f847ae34bcda32ab Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:57:30 +0900 Subject: [PATCH 093/101] ARM: EXYNOS: Cleanup all unneeded headers from cpuidle.c Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/cpuidle.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index 05cb00c79406..7c0151263828 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -8,26 +8,16 @@ * published by the Free Software Foundation. */ -#include -#include #include #include -#include #include #include -#include #include #include #include -#include #include -#include -#include - -#include - static void (*exynos_enter_aftr)(void); static int idle_finisher(unsigned long flags) From ff6a9c039dc7da12fcfc70c28ccc9893d85f8e1f Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 9 May 2014 06:57:35 +0900 Subject: [PATCH 094/101] ARM: EXYNOS: Move the driver to drivers/cpuidle directory Signed-off-by: Daniel Lezcano Reviewed-by: Viresh Kumar Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/Makefile | 1 - drivers/cpuidle/Kconfig.arm | 6 ++++++ drivers/cpuidle/Makefile | 1 + .../cpuidle.c => drivers/cpuidle/cpuidle-exynos.c | 0 4 files changed, 7 insertions(+), 1 deletion(-) rename arch/arm/mach-exynos/cpuidle.c => drivers/cpuidle/cpuidle-exynos.c (100%) diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 01bc9b94a032..3705048f744a 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos.o obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o -obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_EXYNOS) += pmu.o diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 97ccc31dbdd8..695507ae345a 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -44,3 +44,9 @@ config ARM_AT91_CPUIDLE depends on ARCH_AT91 help Select this to enable cpuidle for AT91 processors + +config ARM_EXYNOS_CPUIDLE + bool "Cpu Idle Driver for the Exynos processors" + depends on ARCH_EXYNOS + help + Select this to enable cpuidle for Exynos processors diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index f71ae1b373c5..0d1540adaf78 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o +obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o ############################################################################### # POWERPC drivers diff --git a/arch/arm/mach-exynos/cpuidle.c b/drivers/cpuidle/cpuidle-exynos.c similarity index 100% rename from arch/arm/mach-exynos/cpuidle.c rename to drivers/cpuidle/cpuidle-exynos.c From cd245f59aae5d6ef265f46aa6cea78354d61b629 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 26 May 2014 04:50:34 +0900 Subject: [PATCH 095/101] ARM: EXYNOS: Fix kernel panic when unplugging CPU1 on exynos A look at the code reveals use of S5P_VA_SYSRAM macro, in case of certain SoC revisions, which is not valid any longer, after SYSRAM started to be mapped dynamically. The new dynamic mapping is stored in sysram_base_addr variable, which is declared static in platsmp.c This fix makes sysram_base_addr non-static, declared it in common.h and used in pm.c instead of S5P_VA_SYSRAM. Suggested-by: Tomasz Figa Signed-off-by: Daniel Lezcano Reviewed-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/common.h | 1 + arch/arm/mach-exynos/platsmp.c | 2 +- arch/arm/mach-exynos/pm.c | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 27c9abdeb79e..5848d3cc29e9 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -19,6 +19,7 @@ void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1); struct map_desc; extern void __iomem *sysram_ns_base_addr; +extern void __iomem *sysram_base_addr; void exynos_init_io(void); void exynos_restart(enum reboot_mode mode, const char *cmd); void exynos_cpuidle_init(void); diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 78002c7344b3..9e5e230575dd 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -34,7 +34,7 @@ extern void exynos4_secondary_startup(void); -static void __iomem *sysram_base_addr; +void __iomem *sysram_base_addr; void __iomem *sysram_ns_base_addr; static void __init exynos_smp_prepare_sysram(void) diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index f7b0b77582f6..0d1a46e7b021 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -169,10 +169,10 @@ int exynos_cluster_power_state(int cluster) #define EXYNOS_BOOT_VECTOR_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ - (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0)) + (sysram_base_addr + 0x24) : S5P_INFORM0)) #define EXYNOS_BOOT_VECTOR_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ - (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) + (sysram_base_addr + 0x20) : S5P_INFORM1)) #define S5P_CHECK_AFTR 0xFCBA0D10 #define S5P_CHECK_SLEEP 0x00000BAD From 1b7f0c7b931864642e825a6fd01cde5881b064cc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 26 May 2014 18:07:05 +0200 Subject: [PATCH 096/101] soc: qcom: fix of_device_id table The match tables must be zero-terminated, and Kbuild now helpfully fails to link the kernel if that isn't the case. Signed-off-by: Arnd Bergmann --- drivers/soc/qcom/qcom_gsbi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c index 061dd0632dbd..447458e696a9 100644 --- a/drivers/soc/qcom/qcom_gsbi.c +++ b/drivers/soc/qcom/qcom_gsbi.c @@ -64,6 +64,7 @@ static int gsbi_probe(struct platform_device *pdev) static const struct of_device_id gsbi_dt_match[] = { { .compatible = "qcom,gsbi-v1.0.0", }, + { }, }; MODULE_DEVICE_TABLE(of, gsbi_dt_match); From caee0055337ab96a5af844fba6e72d518d7287dd Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Fri, 23 May 2014 16:32:39 -0400 Subject: [PATCH 097/101] Documentation: dt: add bindings for keystone reset driver This node is intended to allow SoC reset in case of software reset or appropriate watchdogs. The Keystone SoCs can contain up to 4 watchdog timers to reset SoC. Each watchdog timer event input is connected to the Reset Mux block. The Reset Mux block can be configured to cause reset or not. Additionally soft or hard reset can be configured. Reviewed-by: Arnd Bergmann Signed-off-by: Ivan Khoronzhuk [santosh.shilimkar@ti.com: Fixed the subject line] Signed-off-by: Santosh Shilimkar --- .../bindings/power/reset/keystone-reset.txt | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/reset/keystone-reset.txt diff --git a/Documentation/devicetree/bindings/power/reset/keystone-reset.txt b/Documentation/devicetree/bindings/power/reset/keystone-reset.txt new file mode 100644 index 000000000000..c82f12e2d85c --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/keystone-reset.txt @@ -0,0 +1,67 @@ +* Device tree bindings for Texas Instruments keystone reset + +This node is intended to allow SoC reset in case of software reset +of selected watchdogs. + +The Keystone SoCs can contain up to 4 watchdog timers to reset +SoC. Each watchdog timer event input is connected to the Reset Mux +block. The Reset Mux block can be configured to cause reset or not. + +Additionally soft or hard reset can be configured. + +Required properties: + +- compatible: ti,keystone-reset + +- ti,syscon-pll: phandle/offset pair. The phandle to syscon used to + access pll controller registers and the offset to use + reset control registers. + +- ti,syscon-dev: phandle/offset pair. The phandle to syscon used to + access device state control registers and the offset + in order to use mux block registers for all watchdogs. + +Optional properties: + +- ti,soft-reset: Boolean option indicating soft reset. + By default hard reset is used. + +- ti,wdt-list: WDT list that can cause SoC reset. It's not related + to WDT driver, it's just needed to enable a SoC related + reset that's triggered by one of WDTs. The list is + in format: <0>, <2>; It can be in random order and + begins from 0 to 3, as keystone can contain up to 4 SoC + reset watchdogs and can be in random order. + +Example 1: +Setup keystone reset so that in case software reset or +WDT0 is triggered it issues hard reset for SoC. + +pllctrl: pll-controller@02310000 { + compatible = "ti,keystone-pllctrl", "syscon"; + reg = <0x02310000 0x200>; +}; + +devctrl: device-state-control@02620000 { + compatible = "ti,keystone-devctrl", "syscon"; + reg = <0x02620000 0x1000>; +}; + +rstctrl: reset-controller { + compatible = "ti,keystone-reset"; + ti,syscon-pll = <&pllctrl 0xe4>; + ti,syscon-dev = <&devctrl 0x328>; + ti,wdt-list = <0>; +}; + +Example 2: +Setup keystone reset so that in case of software reset or +WDT0 or WDT2 is triggered it issues soft reset for SoC. + +rstctrl: reset-controller { + compatible = "ti,keystone-reset"; + ti,syscon-pll = <&pllctrl 0xe4>; + ti,syscon-dev = <&devctrl 0x328>; + ti,wdt-list = <0>, <2>; + ti,soft-reset; +}; From d30982b93a79aafa688e7df1f6948ad28bb94e89 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Fri, 23 May 2014 16:32:39 -0400 Subject: [PATCH 098/101] Documentation: dt: add bindings for keystone pll control controller The main pll controller used to drive theC66x CorePacs, the switch fabric, and a majority of the peripheral clocks (all but the ARM CorePacs, DDR3 and the NETCP modules) requires a PLL Controller to manage the various clock divisions, gating, and synchronization. Reviewed-by: Arnd Bergmann Acked-by: Mike Turquette Signed-off-by: Ivan Khoronzhuk [santosh.shilimkar@ti.com: Fixed the subject line] Signed-off-by: Santosh Shilimkar --- .../bindings/clock/ti-keystone-pllctrl.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/ti-keystone-pllctrl.txt diff --git a/Documentation/devicetree/bindings/clock/ti-keystone-pllctrl.txt b/Documentation/devicetree/bindings/clock/ti-keystone-pllctrl.txt new file mode 100644 index 000000000000..3e6a81e99804 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti-keystone-pllctrl.txt @@ -0,0 +1,20 @@ +* Device tree bindings for Texas Instruments keystone pll controller + +The main pll controller used to drive theC66x CorePacs, the switch fabric, +and a majority of the peripheral clocks (all but the ARM CorePacs, DDR3 and +the NETCP modules) requires a PLL Controller to manage the various clock +divisions, gating, and synchronization. + +Required properties: + +- compatible: "ti,keystone-pllctrl", "syscon" + +- reg: contains offset/length value for pll controller + registers space. + +Example: + +pllctrl: pll-controller@0x02310000 { + compatible = "ti,keystone-pllctrl", "syscon"; + reg = <0x02310000 0x200>; +}; From a3e01e8022932a1fbfbad72058967e5da78657e5 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Fri, 23 May 2014 16:32:39 -0400 Subject: [PATCH 099/101] power: reset: keystone-reset: introduce keystone reset driver The keystone SoC can be rebooted in several ways. By external reset pin, by soft and by watchdogs. To allow keystone SoC reset if watchdog is triggered we have to enable it in reset mux configuration register regarding of watchdog configuration. Also we need to set soft/hard reset we are going to use. So add keystone reset driver to handle all this stuff. Reviewed-by: Arnd Bergmann Signed-off-by: Ivan Khoronzhuk Signed-off-by: Santosh Shilimkar --- drivers/power/reset/Kconfig | 8 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/keystone-reset.c | 166 +++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 drivers/power/reset/keystone-reset.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index fa0e4e057b99..d095e5b1c505 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -57,3 +57,11 @@ config POWER_RESET_XGENE depends on POWER_RESET help Reboot support for the APM SoC X-Gene Eval boards. + +config POWER_RESET_KEYSTONE + bool "Keystone reset driver" + depends on ARCH_KEYSTONE + select MFD_SYSCON + help + Reboot support for the KEYSTONE SoCs. + diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a5b4a77d1a41..802a420741ab 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o +obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c new file mode 100644 index 000000000000..408a18fd91cb --- /dev/null +++ b/drivers/power/reset/keystone-reset.c @@ -0,0 +1,166 @@ +/* + * TI keystone reboot driver + * + * Copyright (C) 2014 Texas Instruments Incorporated. http://www.ti.com/ + * + * Author: Ivan Khoronzhuk + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RSTYPE_RG 0x0 +#define RSCTRL_RG 0x4 +#define RSCFG_RG 0x8 +#define RSISO_RG 0xc + +#define RSCTRL_KEY_MASK 0x0000ffff +#define RSCTRL_RESET_MASK BIT(16) +#define RSCTRL_KEY 0x5a69 + +#define RSMUX_OMODE_MASK 0xe +#define RSMUX_OMODE_RESET_ON 0xa +#define RSMUX_OMODE_RESET_OFF 0x0 +#define RSMUX_LOCK_MASK 0x1 +#define RSMUX_LOCK_SET 0x1 + +#define RSCFG_RSTYPE_SOFT 0x300f +#define RSCFG_RSTYPE_HARD 0x0 + +#define WDT_MUX_NUMBER 0x4 + +static int rspll_offset; +static struct regmap *pllctrl_regs; + +/** + * rsctrl_enable_rspll_write - enable access to RSCTRL, RSCFG + * To be able to access to RSCTRL, RSCFG registers + * we have to write a key before + */ +static inline int rsctrl_enable_rspll_write(void) +{ + return regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG, + RSCTRL_KEY_MASK, RSCTRL_KEY); +} + +static void rsctrl_restart(enum reboot_mode mode, const char *cmd) +{ + /* enable write access to RSTCTRL */ + rsctrl_enable_rspll_write(); + + /* reset the SOC */ + regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG, + RSCTRL_RESET_MASK, 0); +} + +static struct of_device_id rsctrl_of_match[] = { + {.compatible = "ti,keystone-reset", }, + {}, +}; + +static int rsctrl_probe(struct platform_device *pdev) +{ + int i; + int ret; + u32 val; + unsigned int rg; + u32 rsmux_offset; + struct regmap *devctrl_regs; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + if (!np) + return -ENODEV; + + /* get regmaps */ + pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll"); + if (IS_ERR(pllctrl_regs)) + return PTR_ERR(pllctrl_regs); + + devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); + if (IS_ERR(devctrl_regs)) + return PTR_ERR(devctrl_regs); + + ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset); + if (ret) { + dev_err(dev, "couldn't read the reset pll offset!\n"); + return -EINVAL; + } + + ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset); + if (ret) { + dev_err(dev, "couldn't read the rsmux offset!\n"); + return -EINVAL; + } + + /* set soft/hard reset */ + val = of_property_read_bool(np, "ti,soft-reset"); + val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD; + + ret = rsctrl_enable_rspll_write(); + if (ret) + return ret; + + ret = regmap_write(pllctrl_regs, rspll_offset + RSCFG_RG, val); + if (ret) + return ret; + + arm_pm_restart = rsctrl_restart; + + /* disable a reset isolation for all module clocks */ + ret = regmap_write(pllctrl_regs, rspll_offset + RSISO_RG, 0); + if (ret) + return ret; + + /* enable a reset for watchdogs from wdt-list */ + for (i = 0; i < WDT_MUX_NUMBER; i++) { + ret = of_property_read_u32_index(np, "ti,wdt-list", i, &val); + if (ret == -EOVERFLOW && !i) { + dev_err(dev, "ti,wdt-list property has to contain at" + "least one entry\n"); + return -EINVAL; + } else if (ret) { + break; + } + + if (val >= WDT_MUX_NUMBER) { + dev_err(dev, "ti,wdt-list property can contain" + "only numbers < 4\n"); + return -EINVAL; + } + + rg = rsmux_offset + val * 4; + + ret = regmap_update_bits(devctrl_regs, rg, RSMUX_OMODE_MASK, + RSMUX_OMODE_RESET_ON | + RSMUX_LOCK_SET); + if (ret) + return ret; + } + + return 0; +} + +static struct platform_driver rsctrl_driver = { + .probe = rsctrl_probe, + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .of_match_table = rsctrl_of_match, + }, +}; +module_platform_driver(rsctrl_driver); + +MODULE_AUTHOR("Ivan Khoronzhuk "); +MODULE_DESCRIPTION("Texas Instruments keystone reset driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); From 31964ffebbb958c6cf289c7c12edd7530d6221b9 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Thu, 24 Apr 2014 11:31:22 -0500 Subject: [PATCH 100/101] tty: serial: msm: Remove direct access to GSBI This patch removes direct access of the GSBI registers. GSBI configuration should be done through the GSBI driver directly. Signed-off-by: Andy Gross Signed-off-by: Kumar Gala --- drivers/tty/serial/msm_serial.c | 48 ++------------------------------- drivers/tty/serial/msm_serial.h | 5 ---- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 053b98eb46c8..778e376f197e 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -52,7 +52,6 @@ struct msm_port { struct clk *clk; struct clk *pclk; unsigned int imr; - void __iomem *gsbi_base; int is_uartdm; unsigned int old_snap_state; }; @@ -599,9 +598,7 @@ static const char *msm_type(struct uart_port *port) static void msm_release_port(struct uart_port *port) { struct platform_device *pdev = to_platform_device(port->dev); - struct msm_port *msm_port = UART_TO_MSM(port); struct resource *uart_resource; - struct resource *gsbi_resource; resource_size_t size; uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -612,28 +609,12 @@ static void msm_release_port(struct uart_port *port) release_mem_region(port->mapbase, size); iounmap(port->membase); port->membase = NULL; - - if (msm_port->gsbi_base) { - writel_relaxed(GSBI_PROTOCOL_IDLE, - msm_port->gsbi_base + GSBI_CONTROL); - - gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (unlikely(!gsbi_resource)) - return; - - size = resource_size(gsbi_resource); - release_mem_region(gsbi_resource->start, size); - iounmap(msm_port->gsbi_base); - msm_port->gsbi_base = NULL; - } } static int msm_request_port(struct uart_port *port) { - struct msm_port *msm_port = UART_TO_MSM(port); struct platform_device *pdev = to_platform_device(port->dev); struct resource *uart_resource; - struct resource *gsbi_resource; resource_size_t size; int ret; @@ -652,30 +633,8 @@ static int msm_request_port(struct uart_port *port) goto fail_release_port; } - gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); - /* Is this a GSBI-based port? */ - if (gsbi_resource) { - size = resource_size(gsbi_resource); - - if (!request_mem_region(gsbi_resource->start, size, - "msm_serial")) { - ret = -EBUSY; - goto fail_release_port_membase; - } - - msm_port->gsbi_base = ioremap(gsbi_resource->start, size); - if (!msm_port->gsbi_base) { - ret = -EBUSY; - goto fail_release_gsbi; - } - } - return 0; -fail_release_gsbi: - release_mem_region(gsbi_resource->start, size); -fail_release_port_membase: - iounmap(port->membase); fail_release_port: release_mem_region(port->mapbase, size); return ret; @@ -683,7 +642,6 @@ fail_release_port: static void msm_config_port(struct uart_port *port, int flags) { - struct msm_port *msm_port = UART_TO_MSM(port); int ret; if (flags & UART_CONFIG_TYPE) { port->type = PORT_MSM; @@ -691,9 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags) if (ret) return; } - if (msm_port->gsbi_base) - writel_relaxed(GSBI_PROTOCOL_UART, - msm_port->gsbi_base + GSBI_CONTROL); } static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) @@ -1110,6 +1065,7 @@ static struct of_device_id msm_match_table[] = { static struct platform_driver msm_platform_driver = { .remove = msm_serial_remove, + .probe = msm_serial_probe, .driver = { .name = "msm_serial", .owner = THIS_MODULE, @@ -1125,7 +1081,7 @@ static int __init msm_serial_init(void) if (unlikely(ret)) return ret; - ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); + ret = platform_driver_register(&msm_platform_driver); if (unlikely(ret)) uart_unregister_driver(&msm_uart_driver); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 1e9b68b6f9eb..d98d45efdf86 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -109,11 +109,6 @@ #define UART_ISR 0x0014 #define UART_ISR_TX_READY (1 << 7) -#define GSBI_CONTROL 0x0 -#define GSBI_PROTOCOL_CODE 0x30 -#define GSBI_PROTOCOL_UART 0x40 -#define GSBI_PROTOCOL_IDLE 0x0 - #define UARTDM_RXFS 0x50 #define UARTDM_RXFS_BUF_SHIFT 0x7 #define UARTDM_RXFS_BUF_MASK 0x7 From 4c8d819343461d3c3b8d99874cb2ae0ec59ad34a Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 26 May 2014 06:26:03 +0900 Subject: [PATCH 101/101] cpufreq: exynos: Fix driver compilation with ARCH_MULTIPLATFORM Currently Exynos cpufreq drivers rely on globally mapped clock controller registers to configure frequency of CPU cores. This is obviously wrong and will be removed in near future, but to enable support for multi-platform builds without introducing a regression it needs to be worked around. This patch hacks the code to look for clock controller node in device tree and map its registers using of_iomap(), instead of relying on global mapping, so dependencies on platform headers are removed and the driver can compile again with multiplatform support. Signed-off-by: Tomasz Figa Acked-by: Viresh Kumar Signed-off-by: Kukjin Kim --- drivers/cpufreq/Kconfig.arm | 6 ++-- drivers/cpufreq/exynos-cpufreq.c | 2 -- drivers/cpufreq/exynos-cpufreq.h | 30 +++++++++---------- drivers/cpufreq/exynos4210-cpufreq.c | 39 +++++++++++++++++++++---- drivers/cpufreq/exynos4x12-cpufreq.c | 40 ++++++++++++++++++++++---- drivers/cpufreq/exynos5250-cpufreq.c | 43 ++++++++++++++++++++++------ 6 files changed, 119 insertions(+), 41 deletions(-) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 580503513f0f..d2c7b4b8ffd5 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -30,7 +30,7 @@ config ARM_EXYNOS_CPUFREQ config ARM_EXYNOS4210_CPUFREQ bool "SAMSUNG EXYNOS4210" - depends on CPU_EXYNOS4210 && !ARCH_MULTIPLATFORM + depends on CPU_EXYNOS4210 default y select ARM_EXYNOS_CPUFREQ help @@ -41,7 +41,7 @@ config ARM_EXYNOS4210_CPUFREQ config ARM_EXYNOS4X12_CPUFREQ bool "SAMSUNG EXYNOS4x12" - depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM + depends on SOC_EXYNOS4212 || SOC_EXYNOS4412 default y select ARM_EXYNOS_CPUFREQ help @@ -52,7 +52,7 @@ config ARM_EXYNOS4X12_CPUFREQ config ARM_EXYNOS5250_CPUFREQ bool "SAMSUNG EXYNOS5250" - depends on SOC_EXYNOS5250 && !ARCH_MULTIPLATFORM + depends on SOC_EXYNOS5250 default y select ARM_EXYNOS_CPUFREQ help diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index e8a4a7ed38c1..348c8bafe436 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -19,8 +19,6 @@ #include #include -#include - #include "exynos-cpufreq.h" static struct exynos_dvfs_info *exynos_info; diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h index f189547bb447..51af42e1b7fe 100644 --- a/drivers/cpufreq/exynos-cpufreq.h +++ b/drivers/cpufreq/exynos-cpufreq.h @@ -49,6 +49,7 @@ struct exynos_dvfs_info { struct cpufreq_frequency_table *freq_table; void (*set_freq)(unsigned int, unsigned int); bool (*need_apll_change)(unsigned int, unsigned int); + void __iomem *cmu_regs; }; #ifdef CONFIG_ARM_EXYNOS4210_CPUFREQ @@ -76,24 +77,21 @@ static inline int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) } #endif -#include -#include +#define EXYNOS4_CLKSRC_CPU 0x14200 +#define EXYNOS4_CLKMUX_STATCPU 0x14400 -#define EXYNOS4_CLKSRC_CPU (S5P_VA_CMU + 0x14200) -#define EXYNOS4_CLKMUX_STATCPU (S5P_VA_CMU + 0x14400) - -#define EXYNOS4_CLKDIV_CPU (S5P_VA_CMU + 0x14500) -#define EXYNOS4_CLKDIV_CPU1 (S5P_VA_CMU + 0x14504) -#define EXYNOS4_CLKDIV_STATCPU (S5P_VA_CMU + 0x14600) -#define EXYNOS4_CLKDIV_STATCPU1 (S5P_VA_CMU + 0x14604) +#define EXYNOS4_CLKDIV_CPU 0x14500 +#define EXYNOS4_CLKDIV_CPU1 0x14504 +#define EXYNOS4_CLKDIV_STATCPU 0x14600 +#define EXYNOS4_CLKDIV_STATCPU1 0x14604 #define EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT (16) #define EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK (0x7 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT) -#define EXYNOS5_APLL_LOCK (S5P_VA_CMU + 0x00000) -#define EXYNOS5_APLL_CON0 (S5P_VA_CMU + 0x00100) -#define EXYNOS5_CLKMUX_STATCPU (S5P_VA_CMU + 0x00400) -#define EXYNOS5_CLKDIV_CPU0 (S5P_VA_CMU + 0x00500) -#define EXYNOS5_CLKDIV_CPU1 (S5P_VA_CMU + 0x00504) -#define EXYNOS5_CLKDIV_STATCPU0 (S5P_VA_CMU + 0x00600) -#define EXYNOS5_CLKDIV_STATCPU1 (S5P_VA_CMU + 0x00604) +#define EXYNOS5_APLL_LOCK 0x00000 +#define EXYNOS5_APLL_CON0 0x00100 +#define EXYNOS5_CLKMUX_STATCPU 0x00400 +#define EXYNOS5_CLKDIV_CPU0 0x00500 +#define EXYNOS5_CLKDIV_CPU1 0x00504 +#define EXYNOS5_CLKDIV_STATCPU0 0x00600 +#define EXYNOS5_CLKDIV_STATCPU1 0x00604 diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 6384e5b9a347..61a54310a1b9 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "exynos-cpufreq.h" @@ -23,6 +25,7 @@ static struct clk *cpu_clk; static struct clk *moutcore; static struct clk *mout_mpll; static struct clk *mout_apll; +static struct exynos_dvfs_info *cpufreq; static unsigned int exynos4210_volt_table[] = { 1250000, 1150000, 1050000, 975000, 950000, @@ -60,20 +63,20 @@ static void exynos4210_set_clkdiv(unsigned int div_index) tmp = apll_freq_4210[div_index].clk_div_cpu0; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU); + __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU); do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU); + tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU); } while (tmp & 0x1111111); /* Change Divider - CPU1 */ tmp = apll_freq_4210[div_index].clk_div_cpu1; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); + __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1); do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1); + tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1); } while (tmp & 0x11); } @@ -85,7 +88,7 @@ static void exynos4210_set_apll(unsigned int index) clk_set_parent(moutcore, mout_mpll); do { - tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU) + tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU) >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); tmp &= 0x7; } while (tmp != 0x2); @@ -96,7 +99,7 @@ static void exynos4210_set_apll(unsigned int index) clk_set_parent(moutcore, mout_apll); do { - tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU); + tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU); tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); } @@ -115,8 +118,30 @@ static void exynos4210_set_frequency(unsigned int old_index, int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) { + struct device_node *np; unsigned long rate; + /* + * HACK: This is a temporary workaround to get access to clock + * controller registers directly and remove static mappings and + * dependencies on platform headers. It is necessary to enable + * Exynos multi-platform support and will be removed together with + * this whole driver as soon as Exynos gets migrated to use + * cpufreq-cpu0 driver. + */ + np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); + if (!np) { + pr_err("%s: failed to find clock controller DT node\n", + __func__); + return -ENODEV; + } + + info->cmu_regs = of_iomap(np, 0); + if (!info->cmu_regs) { + pr_err("%s: failed to map CMU registers\n", __func__); + return -EFAULT; + } + cpu_clk = clk_get(NULL, "armclk"); if (IS_ERR(cpu_clk)) return PTR_ERR(cpu_clk); @@ -143,6 +168,8 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) info->freq_table = exynos4210_freq_table; info->set_freq = exynos4210_set_frequency; + cpufreq = info; + return 0; err_mout_apll: diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 63a3907ce578..351a2074cfea 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "exynos-cpufreq.h" @@ -23,6 +25,7 @@ static struct clk *cpu_clk; static struct clk *moutcore; static struct clk *mout_mpll; static struct clk *mout_apll; +static struct exynos_dvfs_info *cpufreq; static unsigned int exynos4x12_volt_table[] = { 1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500, @@ -105,19 +108,20 @@ static void exynos4x12_set_clkdiv(unsigned int div_index) tmp = apll_freq_4x12[div_index].clk_div_cpu0; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU); + __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU); - while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111) + while (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU) + & 0x11111111) cpu_relax(); /* Change Divider - CPU1 */ tmp = apll_freq_4x12[div_index].clk_div_cpu1; - __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1); + __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1); do { cpu_relax(); - tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1); + tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1); } while (tmp != 0x0); } @@ -130,7 +134,7 @@ static void exynos4x12_set_apll(unsigned int index) do { cpu_relax(); - tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU) + tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU) >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); tmp &= 0x7; } while (tmp != 0x2); @@ -142,7 +146,7 @@ static void exynos4x12_set_apll(unsigned int index) do { cpu_relax(); - tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU); + tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU); tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); } @@ -161,8 +165,30 @@ static void exynos4x12_set_frequency(unsigned int old_index, int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) { + struct device_node *np; unsigned long rate; + /* + * HACK: This is a temporary workaround to get access to clock + * controller registers directly and remove static mappings and + * dependencies on platform headers. It is necessary to enable + * Exynos multi-platform support and will be removed together with + * this whole driver as soon as Exynos gets migrated to use + * cpufreq-cpu0 driver. + */ + np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock"); + if (!np) { + pr_err("%s: failed to find clock controller DT node\n", + __func__); + return -ENODEV; + } + + info->cmu_regs = of_iomap(np, 0); + if (!info->cmu_regs) { + pr_err("%s: failed to map CMU registers\n", __func__); + return -EFAULT; + } + cpu_clk = clk_get(NULL, "armclk"); if (IS_ERR(cpu_clk)) return PTR_ERR(cpu_clk); @@ -194,6 +220,8 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) info->freq_table = exynos4x12_freq_table; info->set_freq = exynos4x12_set_frequency; + cpufreq = info; + return 0; err_mout_apll: diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index 363a0b3fe1b1..c91ce69dc631 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -16,8 +16,8 @@ #include #include #include - -#include +#include +#include #include "exynos-cpufreq.h" @@ -25,6 +25,7 @@ static struct clk *cpu_clk; static struct clk *moutcore; static struct clk *mout_mpll; static struct clk *mout_apll; +static struct exynos_dvfs_info *cpufreq; static unsigned int exynos5250_volt_table[] = { 1300000, 1250000, 1225000, 1200000, 1150000, @@ -87,17 +88,18 @@ static void set_clkdiv(unsigned int div_index) tmp = apll_freq_5250[div_index].clk_div_cpu0; - __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0); + __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS5_CLKDIV_CPU0); - while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111) + while (__raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKDIV_STATCPU0) + & 0x11111111) cpu_relax(); /* Change Divider - CPU1 */ tmp = apll_freq_5250[div_index].clk_div_cpu1; - __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1); + __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS5_CLKDIV_CPU1); - while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11) + while (__raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKDIV_STATCPU1) & 0x11) cpu_relax(); } @@ -111,7 +113,8 @@ static void set_apll(unsigned int index) do { cpu_relax(); - tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16); + tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKMUX_STATCPU) + >> 16); tmp &= 0x7; } while (tmp != 0x2); @@ -122,7 +125,7 @@ static void set_apll(unsigned int index) do { cpu_relax(); - tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); + tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS5_CLKMUX_STATCPU); tmp &= (0x7 << 16); } while (tmp != (0x1 << 16)); } @@ -141,8 +144,30 @@ static void exynos5250_set_frequency(unsigned int old_index, int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) { + struct device_node *np; unsigned long rate; + /* + * HACK: This is a temporary workaround to get access to clock + * controller registers directly and remove static mappings and + * dependencies on platform headers. It is necessary to enable + * Exynos multi-platform support and will be removed together with + * this whole driver as soon as Exynos gets migrated to use + * cpufreq-cpu0 driver. + */ + np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock"); + if (!np) { + pr_err("%s: failed to find clock controller DT node\n", + __func__); + return -ENODEV; + } + + info->cmu_regs = of_iomap(np, 0); + if (!info->cmu_regs) { + pr_err("%s: failed to map CMU registers\n", __func__); + return -EFAULT; + } + cpu_clk = clk_get(NULL, "armclk"); if (IS_ERR(cpu_clk)) return PTR_ERR(cpu_clk); @@ -169,6 +194,8 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) info->freq_table = exynos5250_freq_table; info->set_freq = exynos5250_set_frequency; + cpufreq = info; + return 0; err_mout_apll: