* [PATCH 1/3] mmc: tmio-mmc: add DMA SG synchronisation
From: Ben Dooks @ 2014-02-06 12:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <Pine.LNX.4.64.1401311248320.3234@axis700.grange>
On 31/01/14 11:54, Guennadi Liakhovetski wrote:
> According to the DMA API data has to be synchronised before starting
> a DMA transfer to device and after completing a DMA transfer from device.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
I've got 1/3 and 2/3, but not 3/3 ?
--
Ben Dooks http://www.codethink.co.uk/
Senior Engineer Codethink - Providing Genius
^ permalink raw reply
* [PATCH 1/2 v3] i2c: exynos5: add support for HSI2C on Exynos5260 SoC
From: Naveen Krishna Chatradhi @ 2014-02-06 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1385100851-32254-1-git-send-email-ch.naveen@samsung.com>
This patch implements a variant struct to handle the differences
(like fifo_depths) in the HSI2C modules across SoCs.
Adds a new compatible to support HSI2C module on Exynos5260.
Also resets the module as an init sequence (Needed by Exynos5260).
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v2:
1. Used variant struct as suggested by Tomasz Figa.
2. Change compatible strings from samsung,exynos5-hsi2c to
samsung,exynos5250-hsi2c based on the first SoC to see the feature.
3. Using reset as init sequences.
4. Merged the 2 patches into one.
.../devicetree/bindings/i2c/i2c-exynos5.txt | 8 ++-
drivers/i2c/busses/i2c-exynos5.c | 64 ++++++++++++++++----
2 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
index 056732c..5bc4998 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
@@ -5,7 +5,11 @@ at various speeds ranging from 100khz to 3.4Mhz.
Required properties:
- compatible: value should be.
- -> "samsung,exynos5-hsi2c", for i2c compatible with exynos5 hsi2c.
+ -> "samsung,exynos5250-hsi2c", for i2c compatible with HSI2C available
+ on Exynos5250 and Exynos5420 SoCs.
+ -> "samsung,exynos5260-hsi2c", for i2c compatible with HSI2C available
+ on Exynos5260 SoCs.
+
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
@@ -26,7 +30,7 @@ Optional properties:
Example:
hsi2c at 12ca0000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12ca0000 0x100>;
interrupts = <56>;
clock-frequency = <100000>;
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 9fd711c..5052e8f 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -76,12 +76,6 @@
#define HSI2C_RXFIFO_TRIGGER_LEVEL(x) ((x) << 4)
#define HSI2C_TXFIFO_TRIGGER_LEVEL(x) ((x) << 16)
-/* As per user manual FIFO max depth is 64bytes */
-#define HSI2C_FIFO_MAX 0x40
-/* default trigger levels for Tx and Rx FIFOs */
-#define HSI2C_DEF_TXFIFO_LVL (HSI2C_FIFO_MAX - 0x30)
-#define HSI2C_DEF_RXFIFO_LVL (HSI2C_FIFO_MAX - 0x10)
-
/* I2C_TRAILING_CTL Register bits */
#define HSI2C_TRAILING_COUNT (0xf)
@@ -183,14 +177,51 @@ struct exynos5_i2c {
* 2. Fast speed upto 1Mbps
*/
int speed_mode;
+
+ /* Version of HS-I2C Hardware */
+ struct exynos_hsi2c_variant *variant;
+};
+
+/**
+ * struct exynos_hsi2c_variant - platform specific HSI2C driver data
+ * @fifo_depth: the fifo depth supported by the HSI2C module
+ *
+ * Specifies platform specific configuration of HSI2C module.
+ * Note: A structure for driver specific platform data is used for future
+ * expansion of its usage.
+ */
+struct exynos_hsi2c_variant {
+ unsigned int fifo_depth;
+};
+
+static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = {
+ .fifo_depth = 64,
+};
+
+static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = {
+ .fifo_depth = 16,
};
static const struct of_device_id exynos5_i2c_match[] = {
- { .compatible = "samsung,exynos5-hsi2c" },
- {},
+ {
+ .compatible = "samsung,exynos5250-hsi2c",
+ .data = &exynos5250_hsi2c_data
+ }, {
+ .compatible = "samsung,exynos5260-hsi2c",
+ .data = &exynos5260_hsi2c_data
+ }, {},
};
MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
+static inline struct exynos_hsi2c_variant *exynos5_i2c_get_variant
+ (struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(exynos5_i2c_match, pdev->dev.of_node);
+ return (struct exynos_hsi2c_variant *)match->data;
+}
+
static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c)
{
writel(readl(i2c->regs + HSI2C_INT_STATUS),
@@ -415,7 +446,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
fifo_level = HSI2C_TX_FIFO_LVL(fifo_status);
- len = HSI2C_FIFO_MAX - fifo_level;
+ len = i2c->variant->fifo_depth - fifo_level;
if (len > (i2c->msg->len - i2c->msg_ptr))
len = i2c->msg->len - i2c->msg_ptr;
@@ -483,6 +514,7 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
u32 i2c_auto_conf = 0;
u32 fifo_ctl;
unsigned long flags;
+ unsigned short trig_lvl;
i2c_ctl = readl(i2c->regs + HSI2C_CTL);
i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
@@ -493,13 +525,19 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
i2c_auto_conf = HSI2C_READ_WRITE;
- fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(HSI2C_DEF_TXFIFO_LVL);
+ trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
+ (i2c->variant->fifo_depth * 3/4) : i2c->msg->len;
+ fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(trig_lvl);
+
int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
HSI2C_INT_TRAILING_EN);
} else {
i2c_ctl |= HSI2C_TXCHON;
- fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(HSI2C_DEF_RXFIFO_LVL);
+ trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
+ (i2c->variant->fifo_depth * 1/4) : i2c->msg->len;
+ fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(trig_lvl);
+
int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
}
@@ -691,7 +729,9 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
if (ret)
goto err_clk;
- exynos5_i2c_init(i2c);
+ i2c->variant = exynos5_i2c_get_variant(pdev);
+
+ exynos5_i2c_reset(i2c);
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
--
1.7.9.5
^ permalink raw reply related
* [PATCH] ARM: DTS: exynos5420: Rename hsi2c compatible to exynos5250-hsi2c
From: Naveen Krishna Chatradhi @ 2014-02-06 12:07 UTC (permalink / raw)
To: linux-arm-kernel
As per the changes submitted for the i2c-exynos5.c driver with the
compatible string being named after the first SoC it is observed on.
This patch modifes the existing hsi2c compatible strings in
arch/arm/boot/dts.
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
arch/arm/boot/dts/exynos5420.dtsi | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 11dd202..c5187d1 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -486,7 +486,7 @@
};
hsi2c_4: i2c at 12CA0000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12CA0000 0x1000>;
interrupts = <0 60 0>;
#address-cells = <1>;
@@ -499,7 +499,7 @@
};
hsi2c_5: i2c at 12CB0000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12CB0000 0x1000>;
interrupts = <0 61 0>;
#address-cells = <1>;
@@ -512,7 +512,7 @@
};
hsi2c_6: i2c at 12CC0000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12CC0000 0x1000>;
interrupts = <0 62 0>;
#address-cells = <1>;
@@ -525,7 +525,7 @@
};
hsi2c_7: i2c at 12CD0000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12CD0000 0x1000>;
interrupts = <0 63 0>;
#address-cells = <1>;
@@ -538,7 +538,7 @@
};
hsi2c_8: i2c at 12E00000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12E00000 0x1000>;
interrupts = <0 87 0>;
#address-cells = <1>;
@@ -551,7 +551,7 @@
};
hsi2c_9: i2c at 12E10000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12E10000 0x1000>;
interrupts = <0 88 0>;
#address-cells = <1>;
@@ -564,7 +564,7 @@
};
hsi2c_10: i2c at 12E20000 {
- compatible = "samsung,exynos5-hsi2c";
+ compatible = "samsung,exynos5250-hsi2c";
reg = <0x12E20000 0x1000>;
interrupts = <0 203 0>;
#address-cells = <1>;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/6] irqchip: gic: use writel instead of dsb + writel_relaxed
From: Will Deacon @ 2014-02-06 12:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140206120035.GQ29446@arm.com>
On Thu, Feb 06, 2014 at 12:00:35PM +0000, Catalin Marinas wrote:
> On Thu, Feb 06, 2014 at 11:57:39AM +0000, Will Deacon wrote:
> > On Thu, Feb 06, 2014 at 11:54:30AM +0000, Catalin Marinas wrote:
> > > On Thu, Feb 06, 2014 at 11:51:21AM +0000, Will Deacon wrote:
> > > > On Thu, Feb 06, 2014 at 11:45:59AM +0000, Catalin Marinas wrote:
> > > > > On Thu, Feb 06, 2014 at 11:30:50AM +0000, Will Deacon wrote:
> > > > > > - /* this always happens on GIC0 */
> > > > > > - writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
> > > > > > + writel(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
> > > > >
> > > > > That's heavier than a dsb() since with outer caches on ARM we also get
> > > > > an outer_sync() call.
> > > >
> > > > Yes, which I think we actually need in this case, since we're trying to make
> > > > normal writes visible to a CPU before a device write hits the GIC.
> > >
> > > If they are all in the inner shareable domain and with the caches
> > > enabled, we don't need to flush the outer cache (as in the PL310 case
> > > which is common to all CPUs; other saner outer caches propagate the
> > > barrier ;). The outer_sync is needed when the memory accesses are
> > > non-cacheable and we need to drain both the CPU write-buffer and the
> > > PL310 one.
> > >
> > > For our case here, we only need to ensure the visibility of writes on a
> > > CPU to another but assuming SMP and caches enabled, so DSB is enough.
> >
> > Hmm, but we *do* use this for boot and need to ensure that any mailboxes are
> > visible. Maybe we have enough cacheflushing/barriers for that already, but
> > I'm really uncomfortable making this a simple dsb(ishst).
>
> For boot we explicitly flush the caches for the shared data, so we don't
> need this. The dsb() here is for standard smp_call_* etc. We didn't have
> outer_sync() before, so you are slightly changing the functionality here
> (arguably, ishst is relaxing the requirements but I'm not worried about
> this, I consider that's the standard use-case for this function).
Ok, so if we assume that a dsb(ishst) is sufficient because the CPU we're
talking to is either (a) coherent in the inner-shareable domain or (b)
incoherent, and we flushed everything to PoC, then why wouldn't a dmb(ishst)
work?
Will
^ permalink raw reply
* [PATCH 3/6] irqchip: gic: use writel instead of dsb + writel_relaxed
From: Catalin Marinas @ 2014-02-06 12:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140206121350.GQ26035@mudshark.cambridge.arm.com>
On Thu, Feb 06, 2014 at 12:13:50PM +0000, Will Deacon wrote:
> On Thu, Feb 06, 2014 at 12:00:35PM +0000, Catalin Marinas wrote:
> > On Thu, Feb 06, 2014 at 11:57:39AM +0000, Will Deacon wrote:
> > > On Thu, Feb 06, 2014 at 11:54:30AM +0000, Catalin Marinas wrote:
> > > > On Thu, Feb 06, 2014 at 11:51:21AM +0000, Will Deacon wrote:
> > > > > On Thu, Feb 06, 2014 at 11:45:59AM +0000, Catalin Marinas wrote:
> > > > > > On Thu, Feb 06, 2014 at 11:30:50AM +0000, Will Deacon wrote:
> > > > > > > - /* this always happens on GIC0 */
> > > > > > > - writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
> > > > > > > + writel(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
> > > > > >
> > > > > > That's heavier than a dsb() since with outer caches on ARM we also get
> > > > > > an outer_sync() call.
> > > > >
> > > > > Yes, which I think we actually need in this case, since we're trying to make
> > > > > normal writes visible to a CPU before a device write hits the GIC.
> > > >
> > > > If they are all in the inner shareable domain and with the caches
> > > > enabled, we don't need to flush the outer cache (as in the PL310 case
> > > > which is common to all CPUs; other saner outer caches propagate the
> > > > barrier ;). The outer_sync is needed when the memory accesses are
> > > > non-cacheable and we need to drain both the CPU write-buffer and the
> > > > PL310 one.
> > > >
> > > > For our case here, we only need to ensure the visibility of writes on a
> > > > CPU to another but assuming SMP and caches enabled, so DSB is enough.
> > >
> > > Hmm, but we *do* use this for boot and need to ensure that any mailboxes are
> > > visible. Maybe we have enough cacheflushing/barriers for that already, but
> > > I'm really uncomfortable making this a simple dsb(ishst).
> >
> > For boot we explicitly flush the caches for the shared data, so we don't
> > need this. The dsb() here is for standard smp_call_* etc. We didn't have
> > outer_sync() before, so you are slightly changing the functionality here
> > (arguably, ishst is relaxing the requirements but I'm not worried about
> > this, I consider that's the standard use-case for this function).
>
> Ok, so if we assume that a dsb(ishst) is sufficient because the CPU we're
> talking to is either (a) coherent in the inner-shareable domain or (b)
> incoherent, and we flushed everything to PoC, then why wouldn't a dmb(ishst)
> work?
Because you want to guarantee the ordering between a store to Normal
Cacheable memory vs store to Device for the IPI (see the mailbox example
in the Barrier Litmus section ;)). The second is just a slave access, DMB
guarantees observability from the master access perspective.
--
Catalin
^ permalink raw reply
* [PATCH] ARM: imx6: Initialize low-power mode early again
From: Shawn Guo @ 2014-02-06 12:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391011804-22516-1-git-send-email-p.zabel@pengutronix.de>
On Wed, Jan 29, 2014 at 05:10:04PM +0100, Philipp Zabel wrote:
> Since commit 9e8147bb5ec5d1dda2141da70f96b98985a306cb
> "ARM: imx6q: move low-power code out of clock driver"
> the kernel fails to boot on i.MX6Q/D if preemption is
> enabled (CONFIG_PREEMPT=y). The kernel just hangs
> before the console comes up.
>
> The above commit moved the initalization of the low-power
> mode setting (enabling clocked WAIT states), which was
> introduced in commit 83ae20981ae924c37d02a42c829155fc3851260c
> "ARM: imx: correct low-power mode setting", from
> imx6q_clks_init to imx6q_pm_init. Now it is called
> much later, after all cores are enabled.
>
> This patch moves the low-power mode initialization back
> to imx6q_clks_init again (and to imx6sl_clks_init).
>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Applied, thanks.
^ permalink raw reply
* [RFC/RFT 1/2] ARM: mm: introduce arch hooks for dma address translation routines
From: Arnd Bergmann @ 2014-02-06 12:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140205162325.GB2248@e103592.cambridge.arm.com>
On Wednesday 05 February 2014, Dave Martin wrote:
> On Tue, Feb 04, 2014 at 06:04:56PM +0100, Arnd Bergmann wrote:
> > On Tuesday 04 February 2014 11:38:32 Santosh Shilimkar wrote:
> > > On Tuesday 04 February 2014 11:15 AM, Arnd Bergmann wrote:
> > >
> > > > 1. DMA is coherent
> > > > 2. DMA space is offset from phys space
> > > > 3. DMA space is smaller than 32-bit
> > > > 4. DMA space is larger than 32-bit
> > > > 5. DMA goes through an IOMMU
>
> As you explain above, these are properties of end-to-end paths between
> a bus-mastering device and the destination. They aren't properties
> of a device, or of a bus.
>
> For example, we can have the following system, which ePAPR can't describe
> and wouldn't occur with PCI (or, at least would occur in a transparent
> way so that software does not need to understand the difference between
> this structure and a simple CPU->devices tree).
>
> C
> |
> v
> I ---+
> / \ \
> / \ \
> v v \
> A ----> B \
> \ v
> +---------> D
>
> This follows from the unidirectional and minimalistic nature of ARM SoC
> buses (AMBA family, AHB, APB etc. ... and most likely many others too).
>
> To describe A's DMA mappings correctly, the additional links must be
> described, even though thay are irrelevant for direct CPU->device
> transactions.
Can you be more specific about what kind of hardware would use such
a mapping? The interesting cases are normally all about accessing
RAM, while your example seems to be for device-to-device DMA and
that doesn't have to go through dma-ranges.
> dma-ranges does work for simpler cases. In particular, it works where all
> bus-mastering children of a bus node can a) access each other using the
> address space of the bus node, and b) all have the same view of the rest
> of the system (which may be different from the view from outside the bus:
> the dma-ranges property on the bus describes the difference).
>
> Sometimes, we may be able to describe an otherwise undescribable situation
> by introducing additional fake bus nodes. But if there are cross-links
> between devices, this won't always work.
>
>
> This may not be the common case, but it does happen: we need to decide
> whether to describe it propertly, or to describe a fantasy in the DT
> and bodge around it elsewhere when it happens.
Do you think this could be fully described if we add a "dma-parent"
property that can redirect the "dma-ranges" parent address space to
another node?
If there are devices that have parts of their DMA address space on
various buses, how about a "dma-ranges-ext" property that contains
tuples of <&parent-phandle local-address parent-address size> rather
than just <local-address parent-address size>?
> Similarly, for IOMMU, the ARM SMMU is an independent component which is
> not directly associated with a bus: nor is there guaranteed to be a 1:1
> correspondence. Simply wedging properties in a bus or device node to say
> "this is associated with an IOMMU" is not always going to work: it is
> what you flow through on a given device->device path that matters, and
> that can vary from path to path.
Right, I'm aware that the IOMMU may be per device rather than per-bus.
This could be handled by faking extra buses, or possibly better with
the dma-parent approach above, if that is allowed to point to either
a bus or an IOMMU.
Arnd
^ permalink raw reply
* [PATCH] can: xilinx CAN controller support.
From: Marc Kleine-Budde @ 2014-02-06 12:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6c2bcce0-9897-4d1d-a8b9-47924e40f73c@VA3EHSMHS008.ehs.local>
On 02/06/2014 11:19 AM, Kedareswara rao Appana wrote:
> This patch adds xilinx CAN controller support.
> This driver supports both ZYNQ CANPS IP and
> Soft IP AXI CAN controller.
>
> Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
First review, see comments inline.
Marc
> ---
> This patch is rebased on the 3.14 rc1 kernel.
> ---
> .../devicetree/bindings/net/can/xilinx_can.txt | 43 +
> drivers/net/can/Kconfig | 8 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/xilinx_can.c | 1150 ++++++++++++++++++++
> 4 files changed, 1202 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/net/can/xilinx_can.txt
> create mode 100644 drivers/net/can/xilinx_can.c
>
> diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
> new file mode 100644
> index 0000000..34f9643
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
> @@ -0,0 +1,43 @@
> +Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
> +---------------------------------------------------------
> +
> +Required properties:
> +- compatible : Should be "xlnx,zynq-can-1.00.a" for Zynq CAN
> + controllers and "xlnx,axi-can-1.00.a" for Axi CAN
> + controllers.
> +- reg : Physical base address and size of the Axi CAN/Zynq
> + CANPS registers map.
> +- interrupts : Property with a value describing the interrupt
> + number.
> +- interrupt-parent : Must be core interrupt controller
> +- clock-names : List of input clock names - "ref_clk", "aper_clk"
> + (See clock bindings for details. Two clocks are
> + required for Zynq CAN. For Axi CAN
> + case it is one(ref_clk)).
> +- clocks : Clock phandles (see clock bindings for details).
> +- xlnx,can-tx-dpth : Can Tx fifo depth (Required for Axi CAN).
> +- xlnx,can-rx-dpth : Can Rx fifo depth (Required for Axi CAN).
> +
> +
> +Example:
> +
> +For Zynq CANPS Dts file:
> + zynq_can_0: zynq-can at e0008000 {
> + compatible = "xlnx,zynq-can-1.00.a";
> + clocks = <&clkc 19>, <&clkc 36>;
> + clock-names = "ref_clk", "aper_clk";
> + reg = <0xe0008000 0x1000>;
> + interrupts = <0 28 4>;
> + interrupt-parent = <&intc>;
Above xlnx,can-{rx,tx}-dpth is mentioned as required, but it's not in
the Zynq example.
> + };
> +For Axi CAN Dts file:
> + axi_can_0: axi-can at 40000000 {
> + compatible = "xlnx,axi-can-1.00.a";
> + clocks = <&clkc 0>;
> + clock-names = "ref_clk" ;
> + reg = <0x40000000 0x10000>;
> + interrupt-parent = <&intc>;
> + interrupts = <0 59 1>;
> + xlnx,can-tx-dpth = <0x40>;
> + xlnx,can-rx-dpth = <0x40>;
> + };
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index d447b88..2344fb5 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -125,6 +125,14 @@ config CAN_GRCAN
> endian syntheses of the cores would need some modifications on
> the hardware level to work.
>
> +config CAN_XILINXCAN
> + tristate "Xilinx CAN"
> + depends on ARCH_ZYNQ || MICROBLAZE
> + default n
"default n" is default, please remove.
> + ---help---
> + Xilinx CAN driver. This driver supports both soft AXI CAN IP and
> + Zynq CANPS IP.
> +
> source "drivers/net/can/mscan/Kconfig"
>
> source "drivers/net/can/sja1000/Kconfig"
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index c744039..0b8e11e 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
> obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
> obj-$(CONFIG_PCH_CAN) += pch_can.o
> obj-$(CONFIG_CAN_GRCAN) += grcan.o
> +obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
> new file mode 100644
> index 0000000..c1b2b9d
> --- /dev/null
> +++ b/drivers/net/can/xilinx_can.c
> @@ -0,0 +1,1150 @@
> +/* Xilinx CAN device driver
> + *
> + * Copyright (C) 2012 - 2014 Xilinx, Inc.
> + * Copyright (C) 2009 PetaLogix. All rights reserved.
> + *
> + * Description:
> + * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/skbuff.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/can/led.h>
> +
> +#define DRIVER_NAME "XILINX_CAN"
> +
> +/* CAN registers set */
> +#define XCAN_SRR_OFFSET 0x00 /* Software reset */
> +#define XCAN_MSR_OFFSET 0x04 /* Mode select */
> +#define XCAN_BRPR_OFFSET 0x08 /* Baud rate prescaler */
> +#define XCAN_BTR_OFFSET 0x0C /* Bit timing */
> +#define XCAN_ECR_OFFSET 0x10 /* Error counter */
> +#define XCAN_ESR_OFFSET 0x14 /* Error status */
> +#define XCAN_SR_OFFSET 0x18 /* Status */
> +#define XCAN_ISR_OFFSET 0x1C /* Interrupt status */
> +#define XCAN_IER_OFFSET 0x20 /* Interrupt enable */
> +#define XCAN_ICR_OFFSET 0x24 /* Interrupt clear */
> +#define XCAN_TXFIFO_ID_OFFSET 0x30 /* TX FIFO ID */
> +#define XCAN_TXFIFO_DLC_OFFSET 0x34 /* TX FIFO DLC */
> +#define XCAN_TXFIFO_DW1_OFFSET 0x38 /* TX FIFO Data Word 1 */
> +#define XCAN_TXFIFO_DW2_OFFSET 0x3C /* TX FIFO Data Word 2 */
> +#define XCAN_RXFIFO_ID_OFFSET 0x50 /* RX FIFO ID */
> +#define XCAN_RXFIFO_DLC_OFFSET 0x54 /* RX FIFO DLC */
> +#define XCAN_RXFIFO_DW1_OFFSET 0x58 /* RX FIFO Data Word 1 */
> +#define XCAN_RXFIFO_DW2_OFFSET 0x5C /* RX FIFO Data Word 2 */
> +
> +/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
> +#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */
> +#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */
> +#define XCAN_MSR_LBACK_MASK 0x00000002 /* Loop back mode select */
> +#define XCAN_MSR_SLEEP_MASK 0x00000001 /* Sleep mode select */
> +#define XCAN_BRPR_BRP_MASK 0x000000FF /* Baud rate prescaler */
> +#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */
> +#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */
> +#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */
> +#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */
> +#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */
> +#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */
> +#define XCAN_ESR_BERR_MASK 0x00000008 /* Bit error */
> +#define XCAN_ESR_STER_MASK 0x00000004 /* Stuff error */
> +#define XCAN_ESR_FMER_MASK 0x00000002 /* Form error */
> +#define XCAN_ESR_CRCER_MASK 0x00000001 /* CRC error */
> +#define XCAN_SR_TXFLL_MASK 0x00000400 /* TX FIFO is full */
> +#define XCAN_SR_ESTAT_MASK 0x00000180 /* Error status */
> +#define XCAN_SR_ERRWRN_MASK 0x00000040 /* Error warning */
> +#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */
> +#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */
> +#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */
> +#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */
> +#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */
> +#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */
> +#define XCAN_IXR_BSOFF_MASK 0x00000200 /* Bus off interrupt */
> +#define XCAN_IXR_ERROR_MASK 0x00000100 /* Error interrupt */
> +#define XCAN_IXR_RXNEMP_MASK 0x00000080 /* RX FIFO NotEmpty intr */
> +#define XCAN_IXR_RXOFLW_MASK 0x00000040 /* RX FIFO Overflow intr */
> +#define XCAN_IXR_RXOK_MASK 0x00000010 /* Message received intr */
> +#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */
> +#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */
> +#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */
> +#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */
> +#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */
> +#define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */
> +#define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */
> +#define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */
> +
> +#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
> + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
> + XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
> + XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
> +
> +/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
> +#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
> +#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */
> +#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */
> +#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */
> +#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */
> +#define XCAN_ESR_REC_SHIFT 8 /* Rx Error Count */
> +
> +/* CAN frame length constants */
> +#define XCAN_ECHO_SKB_MAX 64
> +#define XCAN_NAPI_WEIGHT 64
> +#define XCAN_FRAME_MAX_DATA_LEN 8
> +#define XCAN_TIMEOUT (50 * HZ)
> +
> +/**
> + * struct xcan_priv - This definition define CAN driver instance
> + * @can: CAN private data structure.
> + * @open_time: For holding timeout values
> + * @waiting_ech_skb_index: Pointer for skb
> + * @ech_skb_next: This tell the next packet in the queue
> + * @waiting_ech_skb_num: Gives the number of packets waiting
> + * @xcan_echo_skb_max_tx: Maximum number packets the driver can send
> + * @xcan_echo_skb_max_rx: Maximum number packets the driver can receive
> + * @napi: NAPI structure
> + * @ech_skb_lock: For spinlock purpose
> + * @read_reg: For reading data from CAN registers
> + * @write_reg: For writing data to CAN registers
> + * @dev: Network device data structure
> + * @reg_base: Ioremapped address to registers
> + * @irq_flags: For request_irq()
> + * @aperclk: Pointer to struct clk
> + * @devclk: Pointer to struct clk
> + */
> +struct xcan_priv {
> + struct can_priv can;
> + int open_time;
> + int waiting_ech_skb_index;
> + int ech_skb_next;
> + int waiting_ech_skb_num;
> + int xcan_echo_skb_max_tx;
> + int xcan_echo_skb_max_rx;
> + struct napi_struct napi;
> + spinlock_t ech_skb_lock;
> + u32 (*read_reg)(const struct xcan_priv *priv, int reg);
> + void (*write_reg)(const struct xcan_priv *priv, int reg, u32 val);
Why do you have {read,write}_reg function here?
> + struct net_device *dev;
> + void __iomem *reg_base;
> + unsigned long irq_flags;
> + struct clk *aperclk;
> + struct clk *devclk;
> +};
> +
> +/* CAN Bittiming constants as per Xilinx CAN specs */
> +static struct can_bittiming_const xcan_bittiming_const = {
> + .name = DRIVER_NAME,
> + .tseg1_min = 1,
> + .tseg1_max = 16,
> + .tseg2_min = 1,
> + .tseg2_max = 8,
> + .sjw_max = 4,
> + .brp_min = 1,
> + .brp_max = 256,
> + .brp_inc = 1,
> +};
> +
> +/**
> + * xcan_write_reg - Write a value to the device register
> + * @priv: Driver private data structure
> + * @reg: Register offset
> + * @val: Value to write at the Register offset
> + *
> + * Write data to the paricular CAN register
> + */
> +static void xcan_write_reg(const struct xcan_priv *priv, int reg, u32 val)
> +{
> + writel(val, priv->reg_base + reg);
> +}
> +
> +/**
> + * xcan_read_reg - Read a value from the device register
> + * @priv: Driver private data structure
> + * @reg: Register offset
> + *
> + * Read data from the particular CAN register
> + * Return: value read from the CAN register
> + */
> +static u32 xcan_read_reg(const struct xcan_priv *priv, int reg)
> +{
> + return readl(priv->reg_base + reg);
> +}
> +
> +/**
> + * set_reset_mode - Resets the CAN device mode
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver reset mode routine.The driver
> + * enters into configuration mode.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int set_reset_mode(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + unsigned long timeout;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_OFFSET);
> +
> + timeout = jiffies + XCAN_TIMEOUT;
> + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
> + if (time_after(jiffies, timeout)) {
> + netdev_warn(ndev, "timedout waiting for config mode\n");
> + return -ETIMEDOUT;
> + }
> + schedule_timeout(1);
better use usleep_range()
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_set_bittiming - CAN set bit timing routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver set bittiming routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_set_bittiming(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + u32 btr0, btr1;
> + u32 is_config_mode;
> +
> + /* Check whether Xilinx CAN is in configuration mode.
> + * It cannot set bit timing if Xilinx CAN is not in configuration mode.
> + */
> + is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
> + XCAN_SR_CONFIG_MASK;
> + if (!is_config_mode) {
> + netdev_alert(ndev,
> + "Cannot set bittiming can is not in config mode\n");
> + return -EPERM;
> + }
> +
> + netdev_dbg(ndev, "brp=%d,prop=%d,phase_seg1:%d,phase_reg2=%d,sjw=%d\n",
> + bt->brp, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
> + bt->sjw);
I think this dbg can be removed, as it just prints the userspace values.
> +
> + /* Setting Baud Rate prescalar value in BRPR Register */
> + btr0 = (bt->brp - 1) & XCAN_BRPR_BRP_MASK;
> +
> + /* Setting Time Segment 1 in BTR Register */
> + btr1 = (bt->prop_seg + bt->phase_seg1 - 1) & XCAN_BTR_TS1_MASK;
> +
> + /* Setting Time Segment 2 in BTR Register */
> + btr1 |= ((bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT) &
> + XCAN_BTR_TS2_MASK;
> +
> + /* Setting Synchronous jump width in BTR Register */
> + btr1 |= ((bt->sjw - 1) << XCAN_BTR_SJW_SHIFT) & XCAN_BTR_SJW_MASK;
> +
> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> + netdev_info(ndev, "Doesn't support Triple Sampling\n");
no need to check, it's not passed to the driver until you advertise you
support it (see priv->can.ctrlmode_supported).
> + netdev_dbg(ndev, "Setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
> +
> + priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
> + priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
> +
> + netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
> + priv->read_reg(priv, XCAN_BRPR_OFFSET),
> + priv->read_reg(priv, XCAN_BTR_OFFSET));
One of the dbg should be enough.
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_start - This the drivers start routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers start routine.
> + * Based on the State of the CAN device it puts
> + * the CAN device into a proper mode.
> + *
> + * Return: 0 always
> + */
> +static int xcan_start(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + /* Check if it is in reset mode */
> + if (priv->can.state != CAN_STATE_STOPPED)
> + set_reset_mode(ndev);
Please check return value of set_reset_mode
> +
> + /* Enable interrupts */
> + priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
> +
> + /* Check whether it is loopback mode or normal mode */
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
> + /* Put device into loopback mode */
> + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_LBACK_MASK);
> + else
> + /* The device is in normal mode */
> + priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
> +
> + if (priv->can.state == CAN_STATE_STOPPED) {
I think your driver is always in CAN_STATE_STOPPED, right?
> + /* Enable Xilinx CAN */
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
> + while ((priv->read_reg(priv, XCAN_SR_OFFSET) &
> + XCAN_SR_LBACK_MASK) == 0)
> + ;
> + } else {
> + while ((priv->read_reg(priv, XCAN_SR_OFFSET)
> + & XCAN_SR_NORMAL_MASK) == 0)
> + ;
Please add a timeout to the loops.
> + }
> + netdev_dbg(ndev, "status:#x%08x\n",
> + priv->read_reg(priv, XCAN_SR_OFFSET));
> + }
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + return 0;
> +}
> +
> +/**
> + * xcan_do_set_mode - This sets the mode of the driver
> + * @ndev: Pointer to net_device structure
> + * @mode: Tells the mode of the driver
> + *
> + * This check the drivers state and calls the
> + * the corresponding modes to set.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int ret;
> +
> + netdev_dbg(ndev, "Setting the mode of the driver%s\n", __func__);
please remove dbg
> +
> + if (!priv->open_time)
> + return -EINVAL;
please remove open_time completely.
> +
> + switch (mode) {
> + case CAN_MODE_START:
> + ret = xcan_start(ndev);
> + if (ret < 0)
> + netdev_err(ndev, "xcan_start failed!\n");
> +
> + if (netif_queue_stopped(ndev))
> + netif_wake_queue(ndev);
just call wake_queue
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * xcan_start_xmit - Starts the transmission
> + * @skb: sk_buff pointer that contains data to be Txed
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from upper layers to initiate transmission. This
> + * function uses the next available free txbuff and populates their fields to
> + * start the transmission.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + u32 id, dlc, tmp_dw1, tmp_dw2 = 0, data1, data2 = 0;
> + unsigned long flags;
> +
Please add here:
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
> + /* Check if the TX buffer is full */
> + if (priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_TXFLL_MASK) {
> + netif_stop_queue(ndev);
> + netdev_err(ndev, "TX register is still full!\n");
> + return NETDEV_TX_BUSY;
> + } else if (priv->waiting_ech_skb_num == priv->xcan_echo_skb_max_tx) {
> + netif_stop_queue(ndev);
> + netdev_err(ndev, "waiting:0x%08x, max:0x%08x\n",
> + priv->waiting_ech_skb_num, priv->xcan_echo_skb_max_tx);
> + return NETDEV_TX_BUSY;
> + }
You should handle flow control after you put the CAN frame into the
hardware, but before activating the TX complete interrutp.
> + /* Watch carefully on the bit sequence */
> + if ((cf->can_id & CAN_EFF_FLAG) == 0) {
Nitpick easier to read is:
if (cf->can_id & CAN_EFF_FLAG) {
/* EFF handling */
} else {
/* STD handling */
}
> + /* Standard CAN ID format */
> + id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
> + XCAN_IDR_ID1_MASK;
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + /* Extended frames remote TX request */
> + id |= XCAN_IDR_SRR_MASK;
> + } else {
> + /* Extended CAN ID format */
> + id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
> + XCAN_IDR_ID2_MASK;
> + id |= (((cf->can_id & CAN_EFF_MASK) >>
> + (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
> + XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
> +
> + /* The substibute remote TX request bit should be "1"
> + * for extended frames as in the Xilinx CAN datasheet
> + */
> + id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + /* Extended frames remote TX request */
> + id |= XCAN_IDR_RTR_MASK;
> + }
> +
> + dlc = (cf->can_dlc & 0xf) << XCAN_DLCR_DLC_SHIFT;
With the above check can_dlc is valid.
> +
> + tmp_dw1 = le32_to_cpup((u32 *)(cf->data));
> + data1 = htonl(tmp_dw1);
This looks broken. cf->data is in big endian, what is the endianess of
your registers?
> + if (dlc > 4) {
> + tmp_dw2 = le32_to_cpup((u32 *)(cf->data + 4));
> + data2 = htonl(tmp_dw2);
> + }
> +
> + netdev_dbg(ndev, "tx:id=0x%08x,dlc=0x%08x,d1=0x%08x,d2=0x%08x\n",
> + id, dlc, data1, data2);
please remove the dbg
> + /* Write the Frame to Xilinx CAN TX FIFO */
> + priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
> + priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
> + priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data1);
> + priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data2);
Which write triggers the transmission?
> + stats->tx_bytes += cf->can_dlc;
Can you move the tx_bytes += to your tx-complete routine?
> + ndev->trans_start = jiffies;
Please remove
> +
> + can_put_echo_skb(skb, ndev, priv->ech_skb_next);
This looks racy, first fill the echo_skb, then start the transmission.
> +
> + priv->ech_skb_next = (priv->ech_skb_next + 1) %
> + priv->xcan_echo_skb_max_tx;
> +
> + spin_lock_irqsave(&priv->ech_skb_lock, flags);
> + priv->waiting_ech_skb_num++;
> + spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
> +
Please move the flow controll handling here.
> + return NETDEV_TX_OK;
> +}
> +
> +/**
> + * xcan_rx - Is called from CAN isr to complete the received
> + * frame processing
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from the CAN isr(poll) to process the Rx frames. It
> + * does minimal processing and invokes "netif_receive_skb" to complete further
> + * processing.
> + * Return: 0 on success and negative error value on error
> + */
> +static int xcan_rx(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + u32 id_xcan, dlc, data1, data2;
> +
> + skb = alloc_can_skb(ndev, &cf);
> + if (!skb)
> + return -ENOMEM;
> +
> + /* Read a frame from Xilinx zynq CANPS */
> + id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
> + dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) & XCAN_DLCR_DLC_MASK;
> + data1 = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
> + data2 = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
If you don't use data? below, don't read them in the first place. Better
move the read below, where you fill the data of the can_frame.
> + netdev_dbg(ndev, "rx:id=0x%08x,dlc=0x%08x,d1=0x%08x,d2=0x%08x\n",
> + id_xcan, dlc, data1, data2);
>
please remove dbg
+
> + /* Change Xilinx CAN data length format to socketCAN data format */
> + cf->can_dlc = get_can_dlc((dlc & XCAN_DLCR_DLC_MASK) >>
> + XCAN_DLCR_DLC_SHIFT);
> +
> + /* Change Xilinx CAN ID format to socketCAN ID format */
> + if (id_xcan & XCAN_IDR_IDE_MASK) {
> + /* The received frame is an Extended format frame */
> + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
> + cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
> + XCAN_IDR_ID2_SHIFT;
> + cf->can_id |= CAN_EFF_FLAG;
> + if (id_xcan & XCAN_IDR_RTR_MASK)
> + cf->can_id |= CAN_RTR_FLAG;
> + } else {
> + /* The received frame is a standard format frame */
> + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
> + XCAN_IDR_ID1_SHIFT;
> + if (id_xcan & XCAN_IDR_RTR_MASK)
> + cf->can_id |= CAN_RTR_FLAG;
> + }
> +
> + /* Change Xilinx CAN data format to socketCAN data format */
Don't fill cf->data if RTR is set. The endianess handling looks weird
here, too.
> + *(u32 *)(cf->data) = ntohl(data1);
> + if (cf->can_dlc > 4)
> + *(u32 *)(cf->data + 4) = ntohl(data2);
> + else
> + *(u32 *)(cf->data + 4) = 0;
no need to set to zero
> + stats->rx_bytes += cf->can_dlc;
please group rx_bytes and rx_packets handling
> +
> + can_led_event(ndev, CAN_LED_EVENT_RX);
> +
> + netif_receive_skb(skb);
> +
> + stats->rx_packets++;
> + return 0;
> +}
> +
> +/**
> + * xcan_err_interrupt - error frame Isr
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This is the CAN error interrupt and it will
> + * check the the type of error and forward the error
> + * frame to upper layers.
> + */
> +static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + u32 err_status, status;
> +
> + skb = alloc_can_err_skb(ndev, &cf);
> + if (!skb) {
> + netdev_err(ndev, "alloc_can_err_skb() failed!\n");
> + return;
> + }
> +
> + err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
> + priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
> + status = priv->read_reg(priv, XCAN_SR_OFFSET);
> +
> + if (isr & XCAN_IXR_BSOFF_MASK) {
> + priv->can.state = CAN_STATE_BUS_OFF;
> + cf->can_id |= CAN_ERR_BUSOFF;
> + priv->can.can_stats.bus_off++;
> + /* Leave device in Config Mode in bus-off state */
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + can_bus_off(ndev);
> + } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> + priv->can.can_stats.error_passive++;
> + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE |
> + CAN_ERR_CRTL_TX_PASSIVE;
> + } else if (status & XCAN_SR_ERRWRN_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + priv->can.state = CAN_STATE_ERROR_WARNING;
> + priv->can.can_stats.error_warning++;
> + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING |
> + CAN_ERR_CRTL_TX_WARNING;
> + }
> +
> + /* Check for Arbitration lost interrupt */
> + if (isr & XCAN_IXR_ARBLST_MASK) {
> + cf->can_id |= CAN_ERR_LOSTARB;
> + cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
> + priv->can.can_stats.arbitration_lost++;
> + }
> +
> + /* Check for RX FIFO Overflow interrupt */
> + if (isr & XCAN_IXR_RXOFLW_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + }
> +
> + /* Check for error interrupt */
> + if (isr & XCAN_IXR_ERROR_MASK) {
> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> + cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +
> + /* Check for Ack error interrupt */
> + if (err_status & XCAN_ESR_ACKER_MASK) {
> + cf->can_id |= CAN_ERR_ACK;
> + cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
> + stats->tx_errors++;
> + }
> +
> + /* Check for Bit error interrupt */
> + if (err_status & XCAN_ESR_BERR_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_BIT;
> + stats->tx_errors++;
> + }
> +
> + /* Check for Stuff error interrupt */
> + if (err_status & XCAN_ESR_STER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_STUFF;
> + stats->rx_errors++;
> + }
> +
> + /* Check for Form error interrupt */
> + if (err_status & XCAN_ESR_FMER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_FORM;
> + stats->rx_errors++;
> + }
> +
> + /* Check for CRC error interrupt */
> + if (err_status & XCAN_ESR_CRCER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL;
> + stats->rx_errors++;
> + }
> + priv->can.can_stats.bus_error++;
> + }
> +
> + netif_rx(skb);
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +
> + netdev_dbg(ndev, "%s: error status register:0x%x\n",
> + __func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
> +}
> +
> +/**
> + * xcan_state_interrupt - It will check the state of the CAN device
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This will checks the state of the CAN device
> + * and puts the device into appropriate state.
> + */
> +static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + /* Check for Sleep interrupt if set put CAN device in sleep state */
> + if (isr & XCAN_IXR_SLP_MASK)
> + priv->can.state = CAN_STATE_SLEEPING;
> +
> + /* Check for Wake up interrupt if set put CAN device in Active state */
> + if (isr & XCAN_IXR_WKUP_MASK)
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +}
> +
> +/**
> + * xcan_rx_poll - Poll routine for rx packets (NAPI)
> + * @napi: napi structure pointer
> + * @quota: Max number of rx packets to be processed.
> + *
> + * This is the poll routine for rx part.
> + * It will process the packets maximux quota value.
> + *
> + * Return: number of packets received
> + */
> +static int xcan_rx_poll(struct napi_struct *napi, int quota)
> +{
> + struct net_device *ndev = napi->dev;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 isr, ier;
> + int work_done = 0;
> +
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
> + if (isr & XCAN_IXR_RXOK_MASK) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET,
> + XCAN_IXR_RXOK_MASK);
> + if (xcan_rx(ndev) < 0)
> + return work_done;
> + work_done++;
> + } else {
> + priv->write_reg(priv, XCAN_ICR_OFFSET,
> + XCAN_IXR_RXNEMP_MASK);
> + break;
> + }
> + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + }
> +
> + if (work_done < quota) {
> + napi_complete(napi);
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + }
> + return work_done;
> +}
> +
> +/**
> + * xcan_tx_interrupt - Tx Done Isr
> + * @ndev: net_device pointer
> + */
> +static void xcan_tx_interrupt(struct net_device *ndev)
> +{
> + unsigned long flags;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + u32 processed = 0, txpackets;
> +
> + stats->tx_packets++;
> + netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
> + priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
> +
> + txpackets = priv->waiting_ech_skb_num;
> +
> + if (txpackets) {
> + can_get_echo_skb(ndev, priv->waiting_ech_skb_index);
> + priv->waiting_ech_skb_index =
> + (priv->waiting_ech_skb_index + 1) %
> + priv->xcan_echo_skb_max_tx;
> + processed++;
> + txpackets--;
> + }
> +
> + spin_lock_irqsave(&priv->ech_skb_lock, flags);
> + priv->waiting_ech_skb_num -= processed;
> + spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
> +
> + netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
> + priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
> +
> + netif_wake_queue(ndev);
> +
> + can_led_event(ndev, CAN_LED_EVENT_TX);
> +}
> +
> +/**
> + * xcan_interrupt - CAN Isr
> + * @irq: irq number
> + * @dev_id: device id poniter
> + *
> + * This is the xilinx CAN Isr. It checks for the type of interrupt
> + * and invokes the corresponding ISR.
> + *
> + * Return:
> + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
> + */
> +static irqreturn_t xcan_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *)dev_id;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 isr, ier;
> +
> + if (priv->can.state == CAN_STATE_STOPPED)
This should not happen, please remove.
> + return IRQ_NONE;
> +
> + /* Get the interrupt status from Xilinx CAN */
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + if (!isr)
> + return IRQ_NONE;
> +
> + netdev_dbg(ndev, "%s: isr:#x%08x, err:#x%08x\n", __func__,
> + isr, priv->read_reg(priv, XCAN_ESR_OFFSET));
> +
> + /* Check for the type of interrupt and Processing it */
> + if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
> + XCAN_IXR_WKUP_MASK));
> + xcan_state_interrupt(ndev, isr);
> + }
> +
> + /* Check for Tx interrupt and Processing it */
> + if (isr & XCAN_IXR_TXOK_MASK) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
> + xcan_tx_interrupt(ndev);
> + }
> +
> + /* Check for the type of error interrupt and Processing it */
> + if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
> + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
> + XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
> + XCAN_IXR_ARBLST_MASK));
> + xcan_err_interrupt(ndev, isr);
> + }
> +
> + /* Check for the type of receive interrupt and Processing it */
> + if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + napi_schedule(&priv->napi);
> + }
> + return IRQ_HANDLED;
> +}
> +
> +/**
> + * xcan_stop - Driver stop routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers stop routine. It will disable the
> + * interrupts and put the device into configuration mode.
> + */
> +static void xcan_stop(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 ier;
> +
> + /* Disable interrupts and leave the can in configuration mode */
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier &= ~XCAN_INTR_ALL;
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + priv->can.state = CAN_STATE_STOPPED;
> +}
> +
> +/**
> + * xcan_open - Driver open routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver open routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_open(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int err;
> +
> + /* Set chip into reset mode */
> + err = set_reset_mode(ndev);
> + if (err < 0)
> + netdev_err(ndev, "mode resetting failed failed!\n");
> +
> + /* Common open */
> + err = open_candev(ndev);
> + if (err)
> + return err;
> +
> + err = xcan_start(ndev);
> + if (err < 0)
> + netdev_err(ndev, "xcan_start failed!\n");
> +
> +
> + can_led_event(ndev, CAN_LED_EVENT_OPEN);
> + napi_enable(&priv->napi);
> + netif_start_queue(ndev);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_close - Driver close routine
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 always
> + */
> +static int xcan_close(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + netif_stop_queue(ndev);
> + napi_disable(&priv->napi);
> + xcan_stop(ndev);
> + close_candev(ndev);
> +
> + can_led_event(ndev, CAN_LED_EVENT_STOP);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_get_berr_counter - error counter routine
> + * @ndev: Pointer to net_device structure
> + * @bec: Pointer to can_berr_counter structure
> + *
> + * This is the driver error counter routine.
> + * Return: 0 always
> + */
> +static int xcan_get_berr_counter(const struct net_device *ndev,
> + struct can_berr_counter *bec)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
> + bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
> + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
> + return 0;
> +}
> +
> +static const struct net_device_ops xcan_netdev_ops = {
> + .ndo_open = xcan_open,
> + .ndo_stop = xcan_close,
> + .ndo_start_xmit = xcan_start_xmit,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/**
> + * xcan_suspend - Suspend method for the driver
> + * @_dev: Address of the platform_device structure
> + *
> + * Put the driver into low power mode.
> + * Return: 0 always
> + */
> +static int xcan_suspend(struct device *_dev)
> +{
> + struct platform_device *pdev = container_of(_dev,
> + struct platform_device, dev);
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + if (netif_running(ndev)) {
> + netif_stop_queue(ndev);
> + netif_device_detach(ndev);
> + }
> +
> + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
> + priv->can.state = CAN_STATE_SLEEPING;
> +
> + clk_disable(priv->aperclk);
> + clk_disable(priv->devclk);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_resume - Resume from suspend
> + * @dev: Address of the platformdevice structure
> + *
> + * Resume operation after suspend.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_resume(struct device *dev)
> +{
> + struct platform_device *pdev = container_of(dev,
> + struct platform_device, dev);
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int ret;
> +
> + ret = clk_enable(priv->aperclk);
> + if (ret) {
> + dev_err(dev, "Cannot enable clock.\n");
> + return ret;
> + }
> + ret = clk_enable(priv->devclk);
> + if (ret) {
> + dev_err(dev, "Cannot enable clock.\n");
> + return ret;
> + }
> +
> + priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + if (netif_running(ndev)) {
> + netif_device_attach(ndev);
> + netif_start_queue(ndev);
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
> +
> +/**
> + * xcan_probe - Platform registration call
> + * @pdev: Handle to the platform device structure
> + *
> + * This function does all the memory allocation and registration for the CAN
> + * device.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_probe(struct platform_device *pdev)
> +{
> + struct resource *res; /* IO mem resources */
> + struct net_device *ndev;
> + struct xcan_priv *priv;
> + int ret, fifodep;
> +
> + /* Create a CAN device instance */
> + ndev = alloc_candev(sizeof(struct xcan_priv), XCAN_ECHO_SKB_MAX);
> + if (!ndev)
> + return -ENOMEM;
> +
> + priv = netdev_priv(ndev);
> + priv->dev = ndev;
> + priv->can.bittiming_const = &xcan_bittiming_const;
> + priv->can.do_set_bittiming = xcan_set_bittiming;
> + priv->can.do_set_mode = xcan_do_set_mode;
> + priv->can.do_get_berr_counter = xcan_get_berr_counter;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
> + CAN_CTRLMODE_BERR_REPORTING;
> + priv->xcan_echo_skb_max_tx = XCAN_ECHO_SKB_MAX;
> + priv->xcan_echo_skb_max_rx = XCAN_NAPI_WEIGHT;
> +
> + /* Get IRQ for the device */
> + ndev->irq = platform_get_irq(pdev, 0);
> + ret = devm_request_irq(&pdev->dev, ndev->irq, &xcan_interrupt,
> + priv->irq_flags, dev_name(&pdev->dev),
> + (void *)ndev);
We usually request the interrupt on in the open() function
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Irq allocation for CAN failed\n");
> + goto err_free;
> + }
> +
> + spin_lock_init(&priv->ech_skb_lock);
> + ndev->flags |= IFF_ECHO; /* We support local echo */
> +
> + platform_set_drvdata(pdev, ndev);
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> + ndev->netdev_ops = &xcan_netdev_ops;
> +
> + /* Get the virtual base address for the device */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reg_base)) {
> + ret = PTR_ERR(priv->reg_base);
> + goto err_free;
> + }
> + ndev->mem_start = res->start;
> + ndev->mem_end = res->end;
> +
> + priv->write_reg = xcan_write_reg;
> + priv->read_reg = xcan_read_reg;
> +
> + /* Getting the CAN devclk info */
> + priv->devclk = devm_clk_get(&pdev->dev, "ref_clk");
> + if (IS_ERR(priv->devclk)) {
> + dev_err(&pdev->dev, "Device clock not found.\n");
> + ret = PTR_ERR(priv->devclk);
> + goto err_free;
> + }
> +
> + /* Check for type of CAN device */
> + if (of_device_is_compatible(pdev->dev.of_node,
> + "xlnx,zynq-can-1.00.a")) {
> + priv->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
> + if (IS_ERR(priv->aperclk)) {
> + dev_err(&pdev->dev, "aper clock not found\n");
> + ret = PTR_ERR(priv->aperclk);
> + goto err_free;
> + }
> + } else {
> + priv->aperclk = priv->devclk;
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "xlnx,can-tx-dpth", &fifodep);
> + if (ret < 0)
> + goto err_free;
> + priv->xcan_echo_skb_max_tx = fifodep;
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "xlnx,can-rx-dpth", &fifodep);
> + if (ret < 0)
> + goto err_free;
> + priv->xcan_echo_skb_max_rx = fifodep;
> + }
> +
> + ret = clk_prepare_enable(priv->devclk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable device clock\n");
> + goto err_free;
> + }
> +
> + ret = clk_prepare_enable(priv->aperclk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable aper clock\n");
> + goto err_unprepar_disabledev;
> + }
> +
> + priv->can.clock.freq = clk_get_rate(priv->devclk);
> +
> + netif_napi_add(ndev, &priv->napi, xcan_rx_poll,
> + priv->xcan_echo_skb_max_rx);
> + ret = register_candev(ndev);
> + if (ret) {
> + dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
> + goto err_unprepar_disableaper;
> + }
> +
> + devm_can_led_init(ndev);
> + dev_info(&pdev->dev,
> + "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
> + priv->reg_base, ndev->irq, priv->can.clock.freq,
> + priv->xcan_echo_skb_max_tx);
> +
> + return 0;
> +
> +err_unprepar_disableaper:
> + clk_disable_unprepare(priv->aperclk);
> +err_unprepar_disabledev:
> + clk_disable_unprepare(priv->devclk);
> +err_free:
> + free_candev(ndev);
> +
> + return ret;
> +}
> +
> +/**
> + * xcan_remove - Unregister the device after releasing the resources
> + * @pdev: Handle to the platform device structure
> + *
> + * This function frees all the resources allocated to the device.
> + * Return: 0 always
> + */
> +static int xcan_remove(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + if (set_reset_mode(ndev) < 0)
> + netdev_err(ndev, "mode resetting failed!\n");
> +
> + unregister_candev(ndev);
> + netif_napi_del(&priv->napi);
> + clk_disable_unprepare(priv->aperclk);
> + clk_disable_unprepare(priv->devclk);
> +
> + free_candev(ndev);
> +
> + return 0;
> +}
> +
> +/* Match table for OF platform binding */
> +static struct of_device_id xcan_of_match[] = {
> + { .compatible = "xlnx,zynq-can-1.00.a", },
> + { .compatible = "xlnx,axi-can-1.00.a", },
> + { /* end of list */ },
> +};
> +MODULE_DEVICE_TABLE(of, xcan_of_match);
> +
> +static struct platform_driver xcan_driver = {
> + .probe = xcan_probe,
> + .remove = xcan_remove,
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = DRIVER_NAME,
> + .pm = &xcan_dev_pm_ops,
> + .of_match_table = xcan_of_match,
> + },
> +};
> +
> +module_platform_driver(xcan_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xilinx Inc");
> +MODULE_DESCRIPTION("Xilinx CAN interface");
>
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 242 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140206/c00b86e9/attachment-0001.sig>
^ permalink raw reply
* [PATCH v4] gpio: davinci: reuse for keystone soc
From: Linus Walleij @ 2014-02-06 13:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391615244-12014-1-git-send-email-grygorii.strashko@ti.com>
On Wed, Feb 5, 2014 at 4:47 PM, Grygorii Strashko
<grygorii.strashko@ti.com> wrote:
> The similar GPIO HW block is used by keystone SoCs as
> in Davinci SoCs.
> Hence, reuse Davinci GPIO driver for Keystone taking into
> account that Keystone contains ARM GIC IRQ controller which
> is implemented using IRQ Chip.
>
> Documentation:
> http://www.ti.com/lit/ug/sprugv1/sprugv1.pdf
>
> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> ---
> Changes in v4:
> - rebased on top of v3.14 +
> [patch] gpio: davinci: signedness bug in davinci_gpio_irq_setup()
Are you taking this through ARM SoC or is this something
I should be merging?
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH V2 2/3] ARM: dts: add dts files for exynos5260 SoC
From: Tomasz Figa @ 2014-02-06 13:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391577375-17625-3-git-send-email-rahul.sharma@samsung.com>
Hi Rahul, Pankaj, Arun,
[adding linux-arm-kernel, devicetree MLs and DT people on Cc]
I think it's good time to stop accepting DTS files like this and force
new ones to use the proper structure with soc node, labels for every
node and node references.
In case of previous Exynos 5 SoCs I hadn't complained, because they
shared a lot of data with already existing exynos5.dtsi, but since
Exynos5260 is completely different, I'd say it should be converted to
the new layout.
As an example you can look at arch/arm/boot/dts/s3c64xx.dtsi and files
that include it or, for more complete structures, DTS of other
platforms, such as imx6*.
Btw. Please remember that linux-samsung-soc mailing list is just a
convenient utility for reviewers of Samsung-specific patches to have all
of them in one place. Sending patches to it alone is not enough - a
general kernel ML list needs to be CCed as well, in this case
linux-arm-kernel.
Also, please see my comments inline, for review comments unrelated to
the issue described above.
On 05.02.2014 06:16, Rahul Sharma wrote:
> The patch adds the dts files for exynos5260.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
> Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
> ---
> arch/arm/boot/dts/exynos5260-pinctrl.dtsi | 572 +++++++++++++++++++++++++++++
> arch/arm/boot/dts/exynos5260.dtsi | 317 ++++++++++++++++
> 2 files changed, 889 insertions(+)
> create mode 100644 arch/arm/boot/dts/exynos5260-pinctrl.dtsi
> create mode 100644 arch/arm/boot/dts/exynos5260.dtsi
>
> diff --git a/arch/arm/boot/dts/exynos5260-pinctrl.dtsi b/arch/arm/boot/dts/exynos5260-pinctrl.dtsi
> new file mode 100644
> index 0000000..3f2c5c4
> --- /dev/null
> +++ b/arch/arm/boot/dts/exynos5260-pinctrl.dtsi
> @@ -0,0 +1,572 @@
> +/*
> + * Samsung's Exynos5260 SoC pin-mux and pin-config device tree source
> + *
> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung's Exynos5260 SoC pin-mux and pin-config options are listed as device
> + * tree nodes are listed in this file.
> + *
> + * 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.
> +*/
> +
> +/ {
> + pinctrl at 11600000 {
[snip]
> + spi0_bus: spi0-bus {
> + samsung,pins = "gpa2-0", "gpa2-1", "gpa2-2", "gpa2-3";
What is the reason for SPI0 to have 4 pins, while SPI1 has just 3?
> + samsung,pin-function = <2>;
> + samsung,pin-pud = <3>;
> + samsung,pin-drv = <0>;
> + };
[snip]
> diff --git a/arch/arm/boot/dts/exynos5260.dtsi b/arch/arm/boot/dts/exynos5260.dtsi
> new file mode 100644
> index 0000000..32a95c7
> --- /dev/null
> +++ b/arch/arm/boot/dts/exynos5260.dtsi
> @@ -0,0 +1,317 @@
> +/*
> + * SAMSUNG EXYNOS5260 SoC device tree source
> + *
> + * Copyright (c) 2013 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * 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 "skeleton.dtsi"
> +#include "exynos5260-pinctrl.dtsi"
> +
> +#include <dt-bindings/clk/exynos5260-clk.h>
> +
> +/ {
> + compatible = "samsung,exynos5260";
> + interrupt-parent = <&gic>;
> +
> + aliases {
> + pinctrl0 = &pinctrl_0;
> + pinctrl1 = &pinctrl_1;
> + pinctrl2 = &pinctrl_2;
> + };
> +
> + chipid at 10000000 {
> + compatible = "samsung,exynos4210-chipid";
> + reg = <0x10000000 0x100>;
> + };
> +
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + cpu at 0 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a15";
> + reg = <0>;
nit: Please make this consistent with CPUs 10x below, by using hex here
as well.
> + cci-control-port = <&cci_control1>;
> + };
nit: Please keep 1 blank line of spacing between nodes.
> + cpu at 1 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a15";
> + reg = <1>;
> + cci-control-port = <&cci_control1>;
> + };
> + cpu at 100 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a7";
> + reg = <0x100>;
> + cci-control-port = <&cci_control0>;
> + };
> + cpu at 101 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a7";
> + reg = <0x101>;
> + cci-control-port = <&cci_control0>;
> + };
> + cpu at 102 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a7";
> + reg = <0x102>;
> + cci-control-port = <&cci_control0>;
> + };
> + cpu at 103 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a7";
> + reg = <0x103>;
> + cci-control-port = <&cci_control0>;
> + };
> + };
> +
> + cmus {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> +
I don't think there is a need to group these nodes under a parent node
that doesn't give any additional information, especially when the CMUs
are scattered trough the whole address space, while we'd like to keep
the nodes ordered by their addresses, as most platforms do.
> + cmu_top: clock-controller at 10010000 {
> + compatible = "exynos5260-cmu-top", "samsung,exynos5260-clock";
I don't think that having the "samsung,exynos5260-clock" compatible
string for every CMU is appropriate here, because there is no way to
automatically recognize which CMU it is. Since every CMU instance is
different, they need to have different compatible strings.
> + reg = <0x10010000 0x10000>;
> + #clock-cells = <1>;
> + };
[snip]
> + mct at 100B0000 {
> + compatible = "samsung,exynos4210-mct";
> + reg = <0x100B0000 0x1000>;
> + interrupt-controller;
> + #interrups-cells = <1>;
MCT is not an interrupt controller, so the 2 properties above are incorrect.
> + interrupt-parent = <&mct_map>;
> + interrupts = <0>, <1>, <2>, <3>,
> + <4>, <5>, <6>, <7>,
> + <8>, <9>, <10>, <11>;
> + clocks = <&cmu_top FIN_PLL>, <&cmu_peri PERI_CLK_MCT>;
> + clock-names = "fin_pll", "mct";
> +
> + mct_map: mct-map {
> + #interrupt-cells = <1>;
> + #address-cells = <0>;
> + #size-cells = <0>;
> + interrupt-map = <0 &gic 0 104 0>,
> + <1 &gic 0 105 0>,
> + <2 &gic 0 106 0>,
> + <3 &gic 0 107 0>,
> + <4 &gic 0 122 0>,
> + <5 &gic 0 123 0>,
> + <6 &gic 0 124 0>,
> + <7 &gic 0 125 0>,
> + <8 &gic 0 126 0>,
> + <9 &gic 0 127 0>,
> + <10 &gic 0 128 0>,
> + <11 &gic 0 129 0>;
> + };
There is no need for interrupt-map here, because all the interrupts are
from GIC.
> + };
[snip]
> + mmc_0: mmc0 at 12140000 {
> + compatible = "samsung,exynos5250-dw-mshc";
> + reg = <0x12140000 0x2000>;
> + interrupts = <0 156 0>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + clocks = <&cmu_fsys FSYS_CLK_MMC0>, <&cmu_top TOP_SCLK_MMC0>;
> + clock-names = "biu", "ciu";
> + fifo-depth = <0x40>;
nit: It might be more readable to use decimal 64 here.
Best regards,
Tomasz
^ permalink raw reply
* [PATCH v2 4/5] ARM: init: add support for reserved memory defined by device tree
From: Marek Szyprowski @ 2014-02-06 13:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140205101510.59A95C40A89@trevor.secretlab.ca>
Hello,
On 2014-02-05 11:15, Grant Likely wrote:
> On Tue, 04 Feb 2014 13:09:32 +0100, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> > Enable reserved memory initialization from device tree.
> >
> > Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > Cc: Laura Abbott <lauraa@codeaurora.org>
> > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> > ---
> > arch/arm/mm/init.c | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> > diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
> > index 804d61566a53..ebafdb479410 100644
> > --- a/arch/arm/mm/init.c
> > +++ b/arch/arm/mm/init.c
> > @@ -17,6 +17,7 @@
> > #include <linux/nodemask.h>
> > #include <linux/initrd.h>
> > #include <linux/of_fdt.h>
> > +#include <linux/of_reserved_mem.h>
> > #include <linux/highmem.h>
> > #include <linux/gfp.h>
> > #include <linux/memblock.h>
> > @@ -323,6 +324,8 @@ void __init arm_memblock_init(struct meminfo *mi,
> > if (mdesc->reserve)
> > mdesc->reserve();
> >
> > + early_init_dt_scan_reserved_mem();
> > +
>
> The new binding is being made fundamental. If the reserved-memory node
> is present, then it needs to be honored, even if the kernel doesn't know
> how to use the regions. Therefore, This needs to be unconditional for
> all architectures. The hook should be called in early_init_dt_scan()
> (drivers/of/fdt.c) immediately after the early_init_dt_scan_memory()
> hook.
In theory this will be the best solution, but it practice there is a
problem. early_init_dt_scan() is called as the first function from kernel
booting code. That time there is no memory yet added to the system, so it
would be really hard to reserve anything. Memory nodes are being added
later either with memblock_add() or by some other arch specific way.
Finally, once all memory has been added to the system we can parse and
reserve all regions defined in the device tree. This really requires
creating another function which will be called by arch specific code.
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply
* [PATCH 3/6] irqchip: gic: use writel instead of dsb + writel_relaxed
From: Will Deacon @ 2014-02-06 13:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140206122340.GC32520@arm.com>
On Thu, Feb 06, 2014 at 12:23:40PM +0000, Catalin Marinas wrote:
> On Thu, Feb 06, 2014 at 12:13:50PM +0000, Will Deacon wrote:
> > Ok, so if we assume that a dsb(ishst) is sufficient because the CPU we're
> > talking to is either (a) coherent in the inner-shareable domain or (b)
> > incoherent, and we flushed everything to PoC, then why wouldn't a dmb(ishst)
> > work?
>
> Because you want to guarantee the ordering between a store to Normal
> Cacheable memory vs store to Device for the IPI (see the mailbox example
> in the Barrier Litmus section ;)). The second is just a slave access, DMB
> guarantees observability from the master access perspective.
Ok, my reasoning is as follows:
- CPU0 tries to message CPU1. It writes to a location in normal memory,
then writes to the GICD to send the SGI
- We need to ensure that CPU1 observes the write to normal memory before
the write to GICD reaches the distributor. This is *not* about end-point
ordering (the usual non-coherent DMA example).
- A dmb ishst ensures that the two writes are observed in order by CPU1
(and, in fact, the inner-shareable domain containing CPU0).
so the only way this can break is if the GICD write reaches the distributor
before being observed by CPU1 (otherwise, we know the mailbox write was
observed by CPU1). I dread to think how you would build such a beast
(dual-ported GICD with no serialisation to the same locations?)...
Furthermore, if we decide that device writes can reach their endpoints
before being observed by other inner-shareable observers, then doesn't that
pose a potential problem for spinlocks? If I take a lock and write to a
device, the write can hit the device before the lock appears to be taken.
That doesn't sound right to me.
Using a dsb(ishst) will ensure that we don't issue the GICD write until the
mailbox is visible to CPU1, but may be overkill.
Will
^ permalink raw reply
* [PATCH 1/2 v3] i2c: exynos5: add support for HSI2C on Exynos5260 SoC
From: Tomasz Figa @ 2014-02-06 13:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391688408-8077-1-git-send-email-ch.naveen@samsung.com>
Hi Naveen,
On 06.02.2014 13:06, Naveen Krishna Chatradhi wrote:
> This patch implements a variant struct to handle the differences
> (like fifo_depths) in the HSI2C modules across SoCs.
>
> Adds a new compatible to support HSI2C module on Exynos5260.
> Also resets the module as an init sequence (Needed by Exynos5260).
>
> Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
> ---
> Changes since v2:
> 1. Used variant struct as suggested by Tomasz Figa.
> 2. Change compatible strings from samsung,exynos5-hsi2c to
> samsung,exynos5250-hsi2c based on the first SoC to see the feature.
> 3. Using reset as init sequences.
> 4. Merged the 2 patches into one.
>
> .../devicetree/bindings/i2c/i2c-exynos5.txt | 8 ++-
> drivers/i2c/busses/i2c-exynos5.c | 64 ++++++++++++++++----
> 2 files changed, 58 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
> index 056732c..5bc4998 100644
> --- a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
> +++ b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
> @@ -5,7 +5,11 @@ at various speeds ranging from 100khz to 3.4Mhz.
>
> Required properties:
> - compatible: value should be.
> - -> "samsung,exynos5-hsi2c", for i2c compatible with exynos5 hsi2c.
Device tree bindings need to be backwards compatible, so you need to
keep this compatible string supported, just marked as (DEPRECATED).
Driver-wise, it will use the same driver data / variant struct as
"samsung,exynos5250-hsi2c", just one more entry in OF match table is needed.
> + -> "samsung,exynos5250-hsi2c", for i2c compatible with HSI2C available
> + on Exynos5250 and Exynos5420 SoCs.
> + -> "samsung,exynos5260-hsi2c", for i2c compatible with HSI2C available
> + on Exynos5260 SoCs.
> +
> - reg: physical base address of the controller and length of memory mapped
> region.
> - interrupts: interrupt number to the cpu.
> @@ -26,7 +30,7 @@ Optional properties:
> Example:
>
> hsi2c at 12ca0000 {
> - compatible = "samsung,exynos5-hsi2c";
> + compatible = "samsung,exynos5250-hsi2c";
> reg = <0x12ca0000 0x100>;
> interrupts = <56>;
> clock-frequency = <100000>;
[snip]
> @@ -483,6 +514,7 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
> u32 i2c_auto_conf = 0;
> u32 fifo_ctl;
> unsigned long flags;
> + unsigned short trig_lvl;
>
> i2c_ctl = readl(i2c->regs + HSI2C_CTL);
> i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
> @@ -493,13 +525,19 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
>
> i2c_auto_conf = HSI2C_READ_WRITE;
>
> - fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(HSI2C_DEF_TXFIFO_LVL);
> + trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
> + (i2c->variant->fifo_depth * 3/4) : i2c->msg->len;
> + fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(trig_lvl);
> +
This is a rather serious semantic change, that doesn't look to belong to
this patch. If this is needed, it should be done in a separate patch.
> int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
> HSI2C_INT_TRAILING_EN);
> } else {
> i2c_ctl |= HSI2C_TXCHON;
>
> - fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(HSI2C_DEF_RXFIFO_LVL);
> + trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
> + (i2c->variant->fifo_depth * 1/4) : i2c->msg->len;
> + fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(trig_lvl);
> +
Ditto.
Best regards,
Tomasz
^ permalink raw reply
* [PATCH v2] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Srikanth Thokala @ 2014-02-06 13:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52F26716.3010203@metafoo.de>
On Wed, Feb 5, 2014 at 10:00 PM, Lars-Peter Clausen <lars@metafoo.de> wrote:
> On 02/05/2014 05:25 PM, Srikanth Thokala wrote:
>>
>> On Fri, Jan 31, 2014 at 12:21 PM, Srikanth Thokala <sthokal@xilinx.com>
>> wrote:
>>>
>>> Hi Vinod,
>>>
>>> On Tue, Jan 28, 2014 at 8:43 AM, Vinod Koul <vinod.koul@intel.com> wrote:
>>>>
>>>> On Mon, Jan 27, 2014 at 06:42:36PM +0530, Srikanth Thokala wrote:
>>>>>
>>>>> Hi Lars/Vinod,
>>>>>>>
>>>>>>> The question here i think would be waht this device supports? Is the
>>>>>>> hardware
>>>>>>> capable of doing interleaved transfers, then would make sense.
>>>>>>
>>>>>>
>>>>>> The hardware does 2D transfers. The parameters for a transfer are
>>>>>> height,
>>>>>> width and stride. That's only a subset of what interleaved transfers
>>>>>> can be
>>>>>> (xt->num_frames must be one for 2d transfers). But if I remember
>>>>>> correctly
>>>>>> there has been some discussion on this in the past and the result of
>>>>>> that
>>>>>> discussion was that using interleaved transfers for 2D transfers is
>>>>>> preferred over adding a custom API for 2D transfers.
>>>>>
>>>>>
>>>>> I went through the prep_interleaved_dma API and I see only one
>>>>> descriptor
>>>>> is prepared per API call (i.e. per frame). As our IP supports upto 16
>>>>> frame
>>>>> buffers (can be more in future), isn't it less efficient compared to
>>>>> the
>>>>> prep_slave_sg where we get a single sg list and can prepare all the
>>>>> descriptors
>>>>> (of non-contiguous buffers) in one go? Correct me, if am wrong and let
>>>>> me
>>>>> know your opinions.
>>>>
>>>> Well the descriptor maybe one, but that can represent multiple frames,
>>>> for
>>>> example 16 as in your case. Can you read up the documentation of how
>>>> multiple
>>>> frames are passed. Pls see include/linux/dmaengine.h
>>>>
>>>> /**
>>>> * Interleaved Transfer Request
>>>> * ----------------------------
>>>> * A chunk is collection of contiguous bytes to be transfered.
>>>> * The gap(in bytes) between two chunks is called inter-chunk-gap(ICG).
>>>> * ICGs may or maynot change between chunks.
>>>> * A FRAME is the smallest series of contiguous {chunk,icg} pairs,
>>>> * that when repeated an integral number of times, specifies the
>>>> transfer.
>>>> * A transfer template is specification of a Frame, the number of times
>>>> * it is to be repeated and other per-transfer attributes.
>>>> *
>>>> * Practically, a client driver would have ready a template for each
>>>> * type of transfer it is going to need during its lifetime and
>>>> * set only 'src_start' and 'dst_start' before submitting the
>>>> requests.
>>>> *
>>>> *
>>>> * | Frame-1 | Frame-2 | ~ |
>>>> Frame-'numf' |
>>>> * |====....==.===...=...|====....==.===...=...| ~
>>>> |====....==.===...=...|
>>>> *
>>>> * == Chunk size
>>>> * ... ICG
>>>> */
>>>
>>>
>>> Yes, it can handle multiple frames specified by 'numf' each of size
>>> 'frame_size * sgl[0].size'.
>>> But, I see it only works if all the frames' memory is contiguous and
>>> in this case we
>>> can just increment 'src_start' by the total frame size 'numf' number
>>> of times to fill in
>>> for each HW descriptor (each frame is one HW descriptor). So, there
>>> is no issue when the
>>> memory is contiguous. If the frames are non contiguous, we have to
>>> call this API for each
>>> frame (hence for each descriptor), as the src_start for each frame is
>>> different. Is it correct?
>>>
>>> FYI: This hardware has an inbuilt Scatter-Gather engine.
>>>
>>
>> Ping?
>
>
> If you want to submit multiple frames at once I think you should look at how
> the current dmaengine API can be extended to allow that. And also provide an
> explanation on how this is superior over submitting them one by one.
Sure. I would start with explaning the current implementation of this driver.
Using prep_slave_sg(), we can define multiple segments in a
async_tx_descriptor where each frame is defined by a segment (a sg
list entry). So, the slave device could DMA the data (of multiple
frames) with a descriptor by calling tx_submit in a transaction i.e.,
prep_slave_sg(16) -> tx_submit(1) -> interrupt (16 frames)
Using interleaved_dma(), we could not divide into segments when we
have scattered memory (for the reasons mentioned in above thread).
This implies we are restricting the slave device to process frame by
frame i.e.,
interleaved_dma(1) -> tx_submit(1) -> interrupt -> interleaved_dma(2)
-> tx_submit (2) -> interrupt -> ........ tx_submit(16) -> interrupt
This implementation makes the hardware to wait until the next frame is
submitted.
To overcome this, I feel it would be a good option if we could extend
interleaved_dma template to modify src_start/dest_start to be a
pointer to an array of addresses. Here, number of addresses will be
defined by numf. The other option would be to include scatterlist in
the interleaved template. This way we can handle scattered memory
using this API.
Srikanth
>
> - Lars
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* [PATCH 02/03] pinctrl: sh-pfc: r8a7790: Break out USB0 OVC/VBUS
From: Magnus Damm @ 2014-02-06 13:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4378236.uXc5XaI3pM@avalon>
Hi Laurent,
On Thu, Feb 6, 2014 at 8:01 PM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> Hi Magnus,
>
> On Friday 31 January 2014 12:10:05 Magnus Damm wrote:
>> On Fri, Jan 31, 2014 at 10:17 AM, Laurent Pinchart wrote:
>> > On Thursday 30 January 2014 08:10:19 Magnus Damm wrote:
>> >> From: Magnus Damm <damm@opensource.se>
>> >>
>> >> Create a new group for the USB0 OVC/VBUS pin by itself. This
>> >> allows us to monitor PWEN as GPIO on the Lager board.
>> >>
>> >> Signed-off-by: Magnus Damm <damm@opensource.se>
>> >> ---
>> >>
>> >> drivers/pinctrl/sh-pfc/pfc-r8a7790.c | 9 +++++++++
>> >> 1 file changed, 9 insertions(+)
>> >>
>> >> --- 0001/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
>> >> +++ work/drivers/pinctrl/sh-pfc/pfc-r8a7790.c 2014-01-24
>> > 10:23:32.000000000
>> >> +0900 @@ -3231,6 +3231,13 @@ static const unsigned int usb0_pins[] =
>> >> static const unsigned int usb0_mux[] = {
>> >> USB0_PWEN_MARK, USB0_OVC_VBUS_MARK,
>> >> };
>> >> +static const unsigned int usb0_ovc_vbus_pins[] = {
>> >> + /* OVC/VBUS */
>> >> + RCAR_GP_PIN(5, 19),
>> >> +};
>> >> +static const unsigned int usb0_ovc_vbus_mux[] = {
>> >> + USB0_OVC_VBUS_MARK,
>> >> +};
>> >
>> > Another option would have been to split the existing usb0 group in
>> > usb0_pwen and usb0_ovc. I'm not sure which is better though, I'd just
>> > like to know if you had given it a thought.
>>
>> I actually did just that in my first local attempt, but I decided not
>> to since it will only cause potential breakage.
>
> OK. I assume that using PWEN without OVC/VBUS doesn't make sense, right ?
Correct!
>> > Regardless, what about naming the new group usb0_ovc instead of
>> > usb0_ovc_bus to keep names short ?
>>
>> Is there any particular reason why you want shorter names?
>
> When it doesn't reduce clarity I prefer to keep names short, as that makes the
> code easier to read and write, and (slightly) lowers the memory footprint.
That sounds sane. =)
>> From my side, I prefer to keep the names in sync with the data sheet. In
>> this particular case it is a shared pin so OVC is used for Host while VBUS
>> is used for gadget, so if you're proposing to ditch VBUS then this feels
>> somewhat inconsistent with the current gadget use case. =)
>
> I thought the pin was used for over current detection only, but that doesn't
> make sense for function mode, you're right. Let's keep the name as-is then.
>
> Provided PWEN without OVC/VBUS doesn't make sense and won't be needed,
>
> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Thanks,
/ magnus
^ permalink raw reply
* Freescale FEC packet loss
From: Marek Vasut @ 2014-02-06 13:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAH9NwWeK17kfty-BAA9Y977BGTYBcJLRSRESrfbCvQ_BiVxa0A@mail.gmail.com>
On Thursday, February 06, 2014 at 10:42:00 AM, Christian Gmeiner wrote:
[...]
> Are there still problems with 3.13.1 kernel regarding FEC networking?
They are on 3.13.0 and I don't see any network-related fixes in 3.13.1 .
> Does this only
> affect the SabreLite?
No, this is not board-specific. This affects different boards and different MX6
CPUs.
Best regards,
Marek Vasut
^ permalink raw reply
* [GIT PULL] ARM: imx: device tree changes for 3.15, take 1
From: Arnd Bergmann @ 2014-02-06 13:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140205122258.GA13821@S2101-09.ap.freescale.net>
On Wednesday 05 February 2014, Shawn Guo wrote:
> Hi arm-soc folks,
>
> This is basically imx-dt-3.14 pull request that missed the 3.14 merge
> window with the pingrp removal series applied on top of. It also
> includes a few additional board support I collected since imx-dt-3.14.
> There will be another round of IMX DT changes for 3.15 later, but this
> one should be the majority. Please pull, thanks.
Hi Shawn,
no objections to the stuff you add here, but the way it's organized
is not good. Instead of adding the controversial patches first and
then reverting them, please redo the series so you don't actually
have the patches in the history. I see that you have rebased the
patches on 3.14-rc1 already so there really shouldn't be any cross-
tree dependencies that make it necessary to keep them in.
Also, because of the pure size of the pull request:
105 files changed, 14073 insertions(+), 3127 deletions(-)
it would be nice to split it up into smaller units. A good
separation would be to have new board support in one pull
request and the changes to existing boards in another one.
Arnd
^ permalink raw reply
* [PATCH 1/2 v3] i2c: exynos5: add support for HSI2C on Exynos5260 SoC
From: Tomasz Figa @ 2014-02-06 13:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52F38EA4.10805@samsung.com>
Also, please use correct addresses of DT ML and Wolfram's e-mail (fixed
in this message).
Best regards,
Tomasz
On 06.02.2014 14:31, Tomasz Figa wrote:
> Hi Naveen,
>
> On 06.02.2014 13:06, Naveen Krishna Chatradhi wrote:
>> This patch implements a variant struct to handle the differences
>> (like fifo_depths) in the HSI2C modules across SoCs.
>>
>> Adds a new compatible to support HSI2C module on Exynos5260.
>> Also resets the module as an init sequence (Needed by Exynos5260).
>>
>> Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
>> ---
>> Changes since v2:
>> 1. Used variant struct as suggested by Tomasz Figa.
>> 2. Change compatible strings from samsung,exynos5-hsi2c to
>> samsung,exynos5250-hsi2c based on the first SoC to see the feature.
>> 3. Using reset as init sequences.
>> 4. Merged the 2 patches into one.
>>
>> .../devicetree/bindings/i2c/i2c-exynos5.txt | 8 ++-
>> drivers/i2c/busses/i2c-exynos5.c | 64
>> ++++++++++++++++----
>> 2 files changed, 58 insertions(+), 14 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
>> b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
>> index 056732c..5bc4998 100644
>> --- a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
>> +++ b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
>> @@ -5,7 +5,11 @@ at various speeds ranging from 100khz to 3.4Mhz.
>>
>> Required properties:
>> - compatible: value should be.
>> - -> "samsung,exynos5-hsi2c", for i2c compatible with exynos5 hsi2c.
>
> Device tree bindings need to be backwards compatible, so you need to
> keep this compatible string supported, just marked as (DEPRECATED).
>
> Driver-wise, it will use the same driver data / variant struct as
> "samsung,exynos5250-hsi2c", just one more entry in OF match table is
> needed.
>
>> + -> "samsung,exynos5250-hsi2c", for i2c compatible with HSI2C
>> available
>> + on Exynos5250 and Exynos5420 SoCs.
>> + -> "samsung,exynos5260-hsi2c", for i2c compatible with HSI2C
>> available
>> + on Exynos5260 SoCs.
>> +
>> - reg: physical base address of the controller and length of
>> memory mapped
>> region.
>> - interrupts: interrupt number to the cpu.
>> @@ -26,7 +30,7 @@ Optional properties:
>> Example:
>>
>> hsi2c at 12ca0000 {
>> - compatible = "samsung,exynos5-hsi2c";
>> + compatible = "samsung,exynos5250-hsi2c";
>> reg = <0x12ca0000 0x100>;
>> interrupts = <56>;
>> clock-frequency = <100000>;
>
> [snip]
>
>> @@ -483,6 +514,7 @@ static void exynos5_i2c_message_start(struct
>> exynos5_i2c *i2c, int stop)
>> u32 i2c_auto_conf = 0;
>> u32 fifo_ctl;
>> unsigned long flags;
>> + unsigned short trig_lvl;
>>
>> i2c_ctl = readl(i2c->regs + HSI2C_CTL);
>> i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
>> @@ -493,13 +525,19 @@ static void exynos5_i2c_message_start(struct
>> exynos5_i2c *i2c, int stop)
>>
>> i2c_auto_conf = HSI2C_READ_WRITE;
>>
>> - fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(HSI2C_DEF_TXFIFO_LVL);
>> + trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
>> + (i2c->variant->fifo_depth * 3/4) : i2c->msg->len;
>> + fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(trig_lvl);
>> +
>
> This is a rather serious semantic change, that doesn't look to belong to
> this patch. If this is needed, it should be done in a separate patch.
>
>> int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
>> HSI2C_INT_TRAILING_EN);
>> } else {
>> i2c_ctl |= HSI2C_TXCHON;
>>
>> - fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(HSI2C_DEF_RXFIFO_LVL);
>> + trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
>> + (i2c->variant->fifo_depth * 1/4) : i2c->msg->len;
>> + fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(trig_lvl);
>> +
>
> Ditto.
>
> Best regards,
> Tomasz
> --
> To unsubscribe from this list: send the line "unsubscribe
> linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* [PATCH v5 1/3] clocksource: timer-keystone: introduce clocksource driver for Keystone
From: Ivan Khoronzhuk @ 2014-02-06 14:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.02.1402052123560.24986@ionos.tec.linutronix.de>
On 02/05/2014 10:27 PM, Thomas Gleixner wrote:
> On Wed, 5 Feb 2014, Ivan Khoronzhuk wrote:
>> + /* here we have to be sure the timer has been disabled */
> Sigh. This is not a proper explanation for a barrier, really. You want
> to explain what it serializes against what. i.e. you want to explain
> why you are using the relaxed functions and avoid a separate non
> relaxed variant in favour of an explicit barrier.
>
>> + __iowmb();
> The proper thing is to have an inline function key_stone_barrier() and
> a full explanation of the issue in exactly that place instead of
> handwaving comments here and there.
>
> Thanks,
>
> tglx
I can add new inline function like:
/**
* keystone_timer_barrier: write memory barrier
* use explicit barrier to avoid using readl/writel non relaxed function
* variants, because in our case relaxed variants hide the true places
* where barrier is needed.
*/
static inline void keystone_timer_barrier(void)
{
__iowmb();
}
and use it where it is needed.
Are you OK with it?
And I propose to leave comments under the barriers in order to be
able to understand why they are used.
--
Regards,
Ivan Khoronzhuk
^ permalink raw reply
* [PATCH v5 2/3] clocksource: keystone: add bindings for keystone timer
From: Ivan Khoronzhuk @ 2014-02-06 14:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAL_JsqLSNrTvH2SFfA5KB8RsDqcj9zks1QaVmvYya6WQ--W7jg@mail.gmail.com>
On 02/06/2014 01:36 AM, Rob Herring wrote:
> On Wed, Feb 5, 2014 at 12:52 PM, Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> wrote:
>> On 02/05/2014 07:41 PM, Rob Herring wrote:
>>> On Wed, Feb 5, 2014 at 10:18 AM, Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
>>> wrote:
>>>> On 02/05/2014 04:39 PM, Rob Herring wrote:
>>>>> On Wed, Feb 5, 2014 at 7:47 AM, Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
>>>>> wrote:
>>>>>> This patch provides bindings for the 64-bit timer in the KeyStone
>>>>>> architecture devices. The timer can be configured as a general-purpose
>>>>>> 64-bit
>>>>>> timer, dual general-purpose 32-bit timers. When configured as dual
>>>>>> 32-bit
>>>>>> timers, each half can operate in conjunction (chain mode) or
>>>>>> independently
>>>>>> (unchained mode) of each other.
>>>>> This is software configurable or h/w design time configurations?
>>>>>
>>>>> Rob
>>>>>
>>>> This is h/w design time configurations
>>> Then it seems like the binding should provide for describing those
>>> differences either with a property or different compatible strings.
>>>
>>> Rob
>> Oh..sorry, seems I didn't catch, this is configurable by software.
>> These configurations are like modes in which timer can work
>> and they are not different hardware IPs. It depends on driver in
>> which mode it should work.
> In that case,
>
> Acked-by: Rob Herring <robh@kernel.org>
Thanks
--
Regards,
Ivan Khoronzhuk
^ permalink raw reply
* [PATCH v5 1/3] clocksource: timer-keystone: introduce clocksource driver for Keystone
From: Ivan Khoronzhuk @ 2014-02-06 14:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140206003526.GQ20228@joshc.qualcomm.com>
On 02/06/2014 02:35 AM, Josh Cartwright wrote:
> Hey Ivan-
>
> On Wed, Feb 05, 2014 at 03:47:38PM +0200, Ivan Khoronzhuk wrote:
>> Add broadcast clock-event device for the Keystone arch.
>>
>> The timer can be configured as a general-purpose 64-bit timer,
>> dual general-purpose 32-bit timers. When configured as dual 32-bit
>> timers, each half can operate in conjunction (chain mode) or
>> independently (unchained mode) of each other.
>>
>> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
>> Acked-by: Santosh shilimkar <santosh.shilimkar@ti.com>
>> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
>> ---
>> drivers/clocksource/Makefile | 1 +
>> drivers/clocksource/timer-keystone.c | 233 +++++++++++++++++++++++++++++++++++
>> 2 files changed, 234 insertions(+)
>> create mode 100644 drivers/clocksource/timer-keystone.c
>>
>> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
>> index c7ca50a..4abe5aa 100644
>> --- a/drivers/clocksource/Makefile
>> +++ b/drivers/clocksource/Makefile
>> @@ -37,3 +37,4 @@ obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
>> obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
>> obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
>> obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
>> +obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o
>> diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c
>> new file mode 100644
>> index 0000000..2299666
>> --- /dev/null
>> +++ b/drivers/clocksource/timer-keystone.c
>> +static void __init keystone_timer_init(struct device_node *np)
>> +{
>> + struct clock_event_device *event_dev = &timer.event_dev;
>> + unsigned long rate;
>> + struct clk *clk;
>> + int irq, error;
>> + u32 tgcr;
>> +
>> + irq = irq_of_parse_and_map(np, 0);
>> + if (irq == NO_IRQ) {
>> + pr_err("%s: failed to map interrupts\n", __func__);
>> + return;
>> + }
>> +
>> + timer.base = of_iomap(np, 0);
>> + if (!timer.base) {
>> + pr_err("%s: failed to map registers\n", __func__);
>> + return;
>> + }
>> +
>> + clk = of_clk_get(np, 0);
>> + if (!clk) {
> This condition should be IS_ERR(clk).
Thanks Josh,
I'll fix it.
--
Regards,
Ivan Khoronzhuk
^ permalink raw reply
* [PATCH v2 6/6] cpu/idle.c: move to sched/idle.c
From: Nicolas Pitre @ 2014-02-06 14:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140130162753.GF5002@laptop.programming.kicks-ass.net>
On Thu, 30 Jan 2014, Peter Zijlstra wrote:
> On Thu, Jan 30, 2014 at 11:03:31AM -0500, Nicolas Pitre wrote:
> > > This is not a valid patch for PATCH(1). Please try again.
> >
> > Don't you use git? ;-)
>
> Nah, git and me don't get along well.
>
> > Here's a plain patch:
>
> Thanks!
Hi Peter,
Did you merge those patches in your tree? If so, is it published
somewhere? That would be a good idea if that could appear in linux-next
so to prevent people from adding more calls to cpuidle_idle_call() from
architecture code. I'm sending you 2 additional patches right away to
remove those that appeared in v3.14-rc1.
Nicolas
^ permalink raw reply
* [PATCH 08/12] clk: samsung: add clock controller driver for s3c2410, s3c2440 and s3c2442
From: Mike Turquette @ 2014-02-06 14:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201312131401.52029.heiko@sntech.de>
Quoting Heiko St?bner (2013-12-13 05:01:51)
> This driver can handle the clock controllers of the socs mentioned above,
> as they share a common clock tree with only small differences.
>
> The clock structure is built according to the manuals of the included
> SoCs and might include changes in comparison to the previous clock
> structure.
>
> As pll-rate-tables only the 12mhz variants are currently included.
> The original code was wrongly checking for 169mhz xti values [a 0 to much
> at the end], so the original 16mhz pll table would have never been
> included and its values are so obscure that I have no possibility to
> at least check their sane-ness. When using the formula from the manual
> the resulting frequency is near the table value but still slightly off.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Mike Turquette <mturquette@linaro.org>
> ---
> drivers/clk/samsung/Makefile | 1 +
> drivers/clk/samsung/clk-s3c2410.c | 428 +++++++++++++++++++++
> include/dt-bindings/clock/samsung,s3c2410-clock.h | 64 +++
> 3 files changed, 493 insertions(+)
> create mode 100644 drivers/clk/samsung/clk-s3c2410.c
> create mode 100644 include/dt-bindings/clock/samsung,s3c2410-clock.h
>
> diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
> index 568683c..60748b2 100644
> --- a/drivers/clk/samsung/Makefile
> +++ b/drivers/clk/samsung/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
> obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
> obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o
> obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o
> +obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o
> obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o
> obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o
> obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o
> diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
> new file mode 100644
> index 0000000..8358cad
> --- /dev/null
> +++ b/drivers/clk/samsung/clk-s3c2410.c
> @@ -0,0 +1,428 @@
> +/*
> + * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
> + *
> + * 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.
> + *
> + * Common Clock Framework support for S3C2410 and following SoCs.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +
> +#include <dt-bindings/clock/samsung,s3c2410-clock.h>
> +
> +#include "clk.h"
> +#include "clk-pll.h"
> +
> +#define LOCKTIME 0x00
> +#define MPLLCON 0x04
> +#define UPLLCON 0x08
> +#define CLKCON 0x0c
> +#define CLKSLOW 0x10
> +#define CLKDIVN 0x14
> +#define CAMDIVN 0x18
> +
> +/* the soc types */
> +enum supported_socs {
> + S3C2410,
> + S3C2440,
> + S3C2442,
> +};
> +
> +/* list of PLLs to be registered */
> +enum s3c2410_plls {
> + mpll, upll,
> +};
> +
> +/*
> + * list of controller registers to be saved and restored during a
> + * suspend/resume cycle.
> + */
> +static unsigned long s3c2410_clk_regs[] __initdata = {
> + LOCKTIME,
> + MPLLCON,
> + UPLLCON,
> + CLKCON,
> + CLKSLOW,
> + CLKDIVN,
> + CAMDIVN,
> +};
> +
> +PNAME(fclk_p) = { "mpll", "div_slow" };
> +
> +struct samsung_mux_clock s3c2410_common_muxes[] __initdata = {
> + MUX(FCLK, "fclk", fclk_p, CLKSLOW, 4, 1),
> +};
> +
> +static struct clk_div_table divslow_d[] = {
> + { .val = 0, .div = 1 },
> + { .val = 1, .div = 2 },
> + { .val = 2, .div = 4 },
> + { .val = 3, .div = 6 },
> + { .val = 4, .div = 8 },
> + { .val = 5, .div = 10 },
> + { .val = 6, .div = 12 },
> + { .val = 7, .div = 14 },
> + { .div = 0 },
> +};
> +
> +struct samsung_div_clock s3c2410_common_dividers[] __initdata = {
> + DIV_T(0, "div_slow", "xti", CLKSLOW, 0, 3, divslow_d),
> + DIV(PCLK, "pclk", "hclk", CLKDIVN, 0, 1),
> +};
> +
> +struct samsung_gate_clock s3c2410_common_gates[] __initdata = {
> + GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0),
> + GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0),
> + GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0),
> + GATE(PCLK_ADC, "adc", "pclk", CLKCON, 15, 0, 0),
> + GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 14, 0, 0),
> + GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 13, CLK_IGNORE_UNUSED, 0),
> + GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 12, 0, 0),
> + GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 11, 0, 0),
> + GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 10, 0, 0),
> + GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 9, 0, 0),
> + GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 8, 0, 0),
> + GATE(HCLK_USBD, "usb-device", "hclk", CLKCON, 7, 0, 0),
> + GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0),
> + GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0),
> + GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0),
> +};
> +
> +/* should be added _after_ the soc-specific clocks are created */
> +struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
> + ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"),
> + ALIAS(PCLK_ADC, NULL, "adc"),
> + ALIAS(PCLK_RTC, NULL, "rtc"),
> + ALIAS(PCLK_PWM, NULL, "timers"),
> + ALIAS(HCLK_LCD, NULL, "lcd"),
> + ALIAS(HCLK_USBD, NULL, "usb-device"),
> + ALIAS(HCLK_USBH, NULL, "usb-host"),
> + ALIAS(UCLK, NULL, "usb-bus-host"),
> + ALIAS(UCLK, NULL, "usb-bus-gadget"),
> + ALIAS(ARMCLK, NULL, "armclk"),
> + ALIAS(UCLK, NULL, "uclk"),
> + ALIAS(HCLK, NULL, "hclk"),
> + ALIAS(MPLL, NULL, "mpll"),
> + ALIAS(FCLK, NULL, "fclk"),
> +};
> +
> +/* S3C2410 specific clocks */
> +
> +static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = {
> + /* sorted in descending order */
> + /* 2410A extras */
> + PLL_35XX_RATE(270000000, 127, 1, 1),
> + PLL_35XX_RATE(268000000, 126, 1, 1),
> + PLL_35XX_RATE(266000000, 125, 1, 1),
> + PLL_35XX_RATE(226000000, 105, 1, 1),
> + PLL_35XX_RATE(210000000, 132, 2, 1),
> + /* 2410 common */
> + PLL_35XX_RATE(203000000, 161, 3, 1),
> + PLL_35XX_RATE(192000000, 88, 1, 1),
> + PLL_35XX_RATE(186000000, 85, 1, 1),
> + PLL_35XX_RATE(180000000, 82, 1, 1),
> + PLL_35XX_RATE(170000000, 77, 1, 1),
> + PLL_35XX_RATE(158000000, 71, 1, 1),
> + PLL_35XX_RATE(152000000, 68, 1, 1),
> + PLL_35XX_RATE(147000000, 90, 2, 1),
> + PLL_35XX_RATE(135000000, 82, 2, 1),
> + PLL_35XX_RATE(124000000, 116, 1, 2),
> + PLL_35XX_RATE(118000000, 150, 2, 2),
> + PLL_35XX_RATE(113000000, 105, 1, 2),
> + PLL_35XX_RATE(101000000, 127, 2, 2),
> + PLL_35XX_RATE(90000000, 112, 2, 2),
> + PLL_35XX_RATE(85000000, 105, 2, 2),
> + PLL_35XX_RATE(79000000, 71, 1, 2),
> + PLL_35XX_RATE(68000000, 82, 2, 2),
> + PLL_35XX_RATE(56000000, 142, 2, 3),
> + PLL_35XX_RATE(48000000, 120, 2, 3),
> + PLL_35XX_RATE(51000000, 161, 3, 3),
> + PLL_35XX_RATE(45000000, 82, 1, 3),
> + PLL_35XX_RATE(34000000, 82, 2, 3),
> + { },
> +};
> +
> +static struct samsung_pll_clock s3c2410_plls[] __initdata = {
> + [mpll] = PLL(pll_s3c2410_mpll, MPLL, "mpll", "xti",
> + LOCKTIME, MPLLCON, NULL),
> + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti",
> + LOCKTIME, UPLLCON, NULL),
> +};
> +
> +struct samsung_div_clock s3c2410_dividers[] __initdata = {
> + DIV(HCLK, "hclk", "mpll", CLKDIVN, 1, 1),
> +};
> +
> +struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = {
> + /*
> + * armclk is directly supplied by the fclk, without
> + * switching possibility like on the s3c244x below.
> + */
> + FFACTOR(ARMCLK, "armclk", "fclk", 1, 1, 0),
> +
> + /* uclk is fed from the unmodified upll */
> + FFACTOR(UCLK, "uclk", "upll", 1, 1, 0),
> +};
> +
> +struct samsung_clock_alias s3c2410_aliases[] __initdata = {
> + ALIAS(PCLK_UART0, "s3c2410-uart.0", "uart"),
> + ALIAS(PCLK_UART1, "s3c2410-uart.1", "uart"),
> + ALIAS(PCLK_UART2, "s3c2410-uart.2", "uart"),
> + ALIAS(PCLK_UART0, "s3c2410-uart.0", "clk_uart_baud0"),
> + ALIAS(PCLK_UART1, "s3c2410-uart.1", "clk_uart_baud0"),
> + ALIAS(PCLK_UART2, "s3c2410-uart.2", "clk_uart_baud0"),
> + ALIAS(UCLK, NULL, "clk_uart_baud1"),
> +};
> +
> +/* S3C244x specific clocks */
> +
> +static struct samsung_pll_rate_table pll_s3c244x_12mhz_tbl[] __initdata = {
> + /* sorted in descending order */
> + PLL_35XX_RATE(400000000, 0x5c, 1, 1),
> + PLL_35XX_RATE(390000000, 0x7a, 2, 1),
> + PLL_35XX_RATE(380000000, 0x57, 1, 1),
> + PLL_35XX_RATE(370000000, 0xb1, 4, 1),
> + PLL_35XX_RATE(360000000, 0x70, 2, 1),
> + PLL_35XX_RATE(350000000, 0xa7, 4, 1),
> + PLL_35XX_RATE(340000000, 0x4d, 1, 1),
> + PLL_35XX_RATE(330000000, 0x66, 2, 1),
> + PLL_35XX_RATE(320000000, 0x98, 4, 1),
> + PLL_35XX_RATE(310000000, 0x93, 4, 1),
> + PLL_35XX_RATE(300000000, 0x75, 3, 1),
> + PLL_35XX_RATE(240000000, 0x70, 1, 2),
> + PLL_35XX_RATE(230000000, 0x6b, 1, 2),
> + PLL_35XX_RATE(220000000, 0x66, 1, 2),
> + PLL_35XX_RATE(210000000, 0x84, 2, 2),
> + PLL_35XX_RATE(200000000, 0x5c, 1, 2),
> + PLL_35XX_RATE(190000000, 0x57, 1, 2),
> + PLL_35XX_RATE(180000000, 0x70, 2, 2),
> + PLL_35XX_RATE(170000000, 0x4d, 1, 2),
> + PLL_35XX_RATE(160000000, 0x98, 4, 2),
> + PLL_35XX_RATE(150000000, 0x75, 3, 2),
> + PLL_35XX_RATE(120000000, 0x70, 1, 3),
> + PLL_35XX_RATE(110000000, 0x66, 1, 3),
> + PLL_35XX_RATE(100000000, 0x5c, 1, 3),
> + PLL_35XX_RATE(90000000, 0x70, 2, 3),
> + PLL_35XX_RATE(80000000, 0x98, 4, 3),
> + PLL_35XX_RATE(75000000, 0x75, 3, 3),
> +};
> +
> +static struct samsung_pll_clock s3c244x_common_plls[] __initdata = {
> + [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti",
> + LOCKTIME, MPLLCON, NULL),
> + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti",
> + LOCKTIME, UPLLCON, NULL),
> +};
> +
> +PNAME(hclk_p) = { "fclk", "div_hclk_2", "div_hclk_4", "div_hclk_3" };
> +PNAME(armclk_p) = { "fclk", "hclk" };
> +
> +struct samsung_mux_clock s3c244x_common_muxes[] __initdata = {
> + MUX(HCLK, "hclk", hclk_p, CLKDIVN, 1, 2),
> + MUX(ARMCLK, "armclk", armclk_p, CAMDIVN, 12, 1),
> +};
> +
> +struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = {
> + FFACTOR(0, "div_hclk_2", "fclk", 1, 2, 0),
> + FFACTOR(0, "ff_cam", "div_cam", 2, 1, CLK_SET_RATE_PARENT),
> +};
> +
> +static struct clk_div_table div_hclk_4_d[] = {
> + { .val = 0, .div = 4 },
> + { .val = 1, .div = 8 },
> +};
> +
> +static struct clk_div_table div_hclk_3_d[] = {
> + { .val = 0, .div = 3 },
> + { .val = 1, .div = 6 },
> +};
> +
> +struct samsung_div_clock s3c244x_common_dividers[] __initdata = {
> + DIV(UCLK, "uclk", "upll", CLKDIVN, 3, 1),
> + DIV(0, "div_hclk", "fclk", CLKDIVN, 1, 1),
> + DIV_T(0, "div_hclk_4", "fclk", CAMDIVN, 9, 1, div_hclk_4_d),
> + DIV_T(0, "div_hclk_3", "fclk", CAMDIVN, 8, 1, div_hclk_3_d),
> + DIV(0, "div_cam", "upll", CAMDIVN, 0, 3),
> +};
> +
> +struct samsung_gate_clock s3c244x_common_gates[] __initdata = {
> + GATE(HCLK_CAM, "cam", "hclk", CLKCON, 19, 0, 0),
> +};
> +
> +struct samsung_clock_alias s3c244x_common_aliases[] __initdata = {
> + ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"),
> + ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"),
> + ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"),
> + ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"),
> + ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"),
> + ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"),
> + ALIAS(HCLK_CAM, NULL, "camif"),
> + ALIAS(CAMIF, NULL, "camif-upll"),
> +};
> +
> +/* S3C2440 specific clocks */
> +
> +PNAME(s3c2440_camif_p) = { "upll", "ff_cam" };
> +
> +struct samsung_mux_clock s3c2440_muxes[] __initdata = {
> + MUX(CAMIF, "camif", s3c2440_camif_p, CAMDIVN, 4, 1),
> +};
> +
> +struct samsung_gate_clock s3c2440_gates[] __initdata = {
> + GATE(PCLK_AC97, "ac97", "pclk", CLKCON, 20, 0, 0),
> +};
> +
> +/* S3C2442 specific clocks */
> +
> +struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = {
> + FFACTOR(0, "upll_3", "upll", 1, 3, 0),
> +};
> +
> +PNAME(s3c2442_camif_p) = { "upll", "ff_cam", "upll", "upll_3" };
> +
> +struct samsung_mux_clock s3c2442_muxes[] __initdata = {
> + MUX(CAMIF, "camif", s3c2442_camif_p, CAMDIVN, 4, 2),
> +};
> +
> +/*
> + * fixed rate clocks generated outside the soc
> + * Only necessary until the devicetree-move is complete
> + */
> +struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = {
> + FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0),
> +};
> +
> +static void __init s3c2410_common_clk_register_fixed_ext(unsigned long xti_f)
> +{
> + struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal");
> +
> + s3c2410_common_frate_clks[0].fixed_rate = xti_f;
> + samsung_clk_register_fixed_rate(s3c2410_common_frate_clks,
> + ARRAY_SIZE(s3c2410_common_frate_clks));
> +
> + samsung_clk_register_alias(&xti_alias, 1);
> +}
> +
> +void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
> + int current_soc,
> + void __iomem *reg_base)
> +{
> + if (np) {
> + reg_base = of_iomap(np, 0);
> + if (!reg_base)
> + panic("%s: failed to map registers\n", __func__);
> + }
> +
> + samsung_clk_init(np, reg_base, NR_CLKS,
> + s3c2410_clk_regs, ARRAY_SIZE(s3c2410_clk_regs), NULL, 0);
> +
> + /* Register external clocks only in non-dt cases */
> + if (!np)
> + s3c2410_common_clk_register_fixed_ext(xti_f);
> +
> + if (current_soc == 2410) {
> + if (_get_rate("xti") == 12 * MHZ) {
> + s3c2410_plls[mpll].rate_table = pll_s3c2410_12mhz_tbl;
> + s3c2410_plls[upll].rate_table = pll_s3c2410_12mhz_tbl;
> + }
> +
> + /* Register PLLs. */
> + samsung_clk_register_pll(s3c2410_plls,
> + ARRAY_SIZE(s3c2410_plls), reg_base);
> +
> + } else { /* S3C2440, S3C2442 */
> + if (_get_rate("xti") == 12 * MHZ) {
> + /*
> + * plls follow different calculation schemes, with the
> + * upll following the same scheme as the s3c2410 plls
> + */
> + s3c244x_common_plls[mpll].rate_table =
> + pll_s3c244x_12mhz_tbl;
> + s3c244x_common_plls[upll].rate_table =
> + pll_s3c2410_12mhz_tbl;
> + }
> +
> + /* Register PLLs. */
> + samsung_clk_register_pll(s3c244x_common_plls,
> + ARRAY_SIZE(s3c244x_common_plls), reg_base);
> + }
> +
> + /* Register common internal clocks. */
> + samsung_clk_register_mux(s3c2410_common_muxes,
> + ARRAY_SIZE(s3c2410_common_muxes));
> + samsung_clk_register_div(s3c2410_common_dividers,
> + ARRAY_SIZE(s3c2410_common_dividers));
> + samsung_clk_register_gate(s3c2410_common_gates,
> + ARRAY_SIZE(s3c2410_common_gates));
> +
> + if (current_soc == S3C2440 || current_soc == S3C2442) {
> + samsung_clk_register_div(s3c244x_common_dividers,
> + ARRAY_SIZE(s3c244x_common_dividers));
> + samsung_clk_register_gate(s3c244x_common_gates,
> + ARRAY_SIZE(s3c244x_common_gates));
> + samsung_clk_register_mux(s3c244x_common_muxes,
> + ARRAY_SIZE(s3c244x_common_muxes));
> + samsung_clk_register_fixed_factor(s3c244x_common_ffactor,
> + ARRAY_SIZE(s3c244x_common_ffactor));
> + }
> +
> + /* Register SoC-specific clocks. */
> + switch (current_soc) {
> + case S3C2410:
> + samsung_clk_register_div(s3c2410_dividers,
> + ARRAY_SIZE(s3c2410_dividers));
> + samsung_clk_register_fixed_factor(s3c2410_ffactor,
> + ARRAY_SIZE(s3c2410_ffactor));
> + samsung_clk_register_alias(s3c2410_aliases,
> + ARRAY_SIZE(s3c2410_common_aliases));
> + break;
> + case S3C2440:
> + samsung_clk_register_mux(s3c2440_muxes,
> + ARRAY_SIZE(s3c2440_muxes));
> + samsung_clk_register_gate(s3c2440_gates,
> + ARRAY_SIZE(s3c2440_gates));
> + break;
> + case S3C2442:
> + samsung_clk_register_mux(s3c2442_muxes,
> + ARRAY_SIZE(s3c2442_muxes));
> + samsung_clk_register_fixed_factor(s3c2442_ffactor,
> + ARRAY_SIZE(s3c2442_ffactor));
> + break;
> + }
> +
> + /*
> + * Register common aliases at the end, as some of the aliased clocks
> + * are SoC specific.
> + */
> + samsung_clk_register_alias(s3c2410_common_aliases,
> + ARRAY_SIZE(s3c2410_common_aliases));
> +
> + if (current_soc == S3C2440 || current_soc == S3C2442) {
> + samsung_clk_register_alias(s3c244x_common_aliases,
> + ARRAY_SIZE(s3c244x_common_aliases));
> + }
> +}
> +
> +static void __init s3c2410_clk_init(struct device_node *np)
> +{
> + s3c2410_common_clk_init(np, 0, S3C2410, 0);
> +}
> +CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init);
> +
> +static void __init s3c2440_clk_init(struct device_node *np)
> +{
> + s3c2410_common_clk_init(np, 0, S3C2440, 0);
> +}
> +CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init);
> +
> +static void __init s3c2442_clk_init(struct device_node *np)
> +{
> + s3c2410_common_clk_init(np, 0, S3C2442, 0);
> +}
> +CLK_OF_DECLARE(s3c2442_clk, "samsung,s3c2442-clock", s3c2442_clk_init);
> diff --git a/include/dt-bindings/clock/samsung,s3c2410-clock.h b/include/dt-bindings/clock/samsung,s3c2410-clock.h
> new file mode 100644
> index 0000000..66c278f
> --- /dev/null
> +++ b/include/dt-bindings/clock/samsung,s3c2410-clock.h
> @@ -0,0 +1,64 @@
> +/*
> + * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
> + *
> + * 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.
> + *
> + * Device Tree binding constants clock controllers of Samsung S3C2410 and later.
> + */
> +
> +#ifndef _DT_BINDINGS_CLOCK_SAMSUNG_S3C2410_CLOCK_H
> +#define _DT_BINDINGS_CLOCK_SAMSUNG_S3C2410_CLOCK_H
> +
> +/*
> + * Let each exported clock get a unique index, which is used on DT-enabled
> + * platforms to lookup the clock from a clock specifier. These indices are
> + * therefore considered an ABI and so must not be changed. This implies
> + * that new clocks should be added either in free spaces between clock groups
> + * or at the end.
> + */
> +
> +/* Core clocks. */
> +
> +
> +#define MPLL 1
> +#define UPLL 2
> +#define FCLK 3
> +#define HCLK 4
> +#define PCLK 5
> +#define UCLK 6
> +#define ARMCLK 7
> +
> +#define XTI 8
> +
> +/* pclk-gates */
> +#define PCLK_UART0 16
> +#define PCLK_UART1 17
> +#define PCLK_UART2 18
> +#define PCLK_I2C 19
> +#define PCLK_SDI 20
> +#define PCLK_SPI 21
> +#define PCLK_ADC 22
> +#define PCLK_AC97 23
> +#define PCLK_I2S 24
> +#define PCLK_PWM 25
> +#define PCLK_RTC 26
> +#define PCLK_GPIO 27
> +
> +
> +/* hclk-gates */
> +#define HCLK_LCD 32
> +#define HCLK_USBH 33
> +#define HCLK_USBD 34
> +#define HCLK_NAND 35
> +#define HCLK_CAM 36
> +
> +
> +#define CAMIF 40
> +
> +
> +/* Total number of clocks. */
> +#define NR_CLKS (CAMIF + 1)
> +
> +#endif /* _DT_BINDINGS_CLOCK_SAMSUNG_S3C2443_CLOCK_H */
> --
> 1.7.10.4
>
^ permalink raw reply
* [PATCH 0/3] arm64: Use pte manipulation functions for THP
From: Steve Capper @ 2014-02-06 14:16 UTC (permalink / raw)
To: linux-arm-kernel
This series replaces the Transparent HugePage pmd manipulation
functions with calls to the standard pte functions. This allows the THP
code to take advantage of the new PTE_WRITE logic, and provides better
parity with the HugeTLB code (which already uses the pte functions).
Testing was done on the Fast Model with LTP THP tests, and the 3.14-rc1
kernel was used.
Steve Capper (3):
arm64: mm: Remove PMD_BIT_FUNC macro
arm64: mm: Route pmd thp functions through pte equivalents
arm64: mm: Correct definition of pmd_mknotpresent
arch/arm64/include/asm/pgtable.h | 91 +++++++++++++++++++++++++++-------------
1 file changed, 62 insertions(+), 29 deletions(-)
--
1.8.1.4
^ permalink raw reply
* [PATCH 1/3] arm64: mm: Remove PMD_BIT_FUNC macro
From: Steve Capper @ 2014-02-06 14:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391696171-8922-1-git-send-email-steve.capper@linaro.org>
Expand out the pmd thp manipulation functions. This makes our life
easier when using things like tags and cscope.
Signed-off-by: Steve Capper <steve.capper@linaro.org>
---
arch/arm64/include/asm/pgtable.h | 51 ++++++++++++++++++++++++++++++++--------
1 file changed, 41 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index b524dcd..a3fb1e4 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -247,16 +247,47 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
#define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING)
#endif
-#define PMD_BIT_FUNC(fn,op) \
-static inline pmd_t pmd_##fn(pmd_t pmd) { pmd_val(pmd) op; return pmd; }
-
-PMD_BIT_FUNC(wrprotect, |= PMD_SECT_RDONLY);
-PMD_BIT_FUNC(mkold, &= ~PMD_SECT_AF);
-PMD_BIT_FUNC(mksplitting, |= PMD_SECT_SPLITTING);
-PMD_BIT_FUNC(mkwrite, &= ~PMD_SECT_RDONLY);
-PMD_BIT_FUNC(mkdirty, |= PMD_SECT_DIRTY);
-PMD_BIT_FUNC(mkyoung, |= PMD_SECT_AF);
-PMD_BIT_FUNC(mknotpresent, &= ~PMD_TYPE_MASK);
+static inline pmd_t pmd_wrprotect(pmd_t pmd)
+{
+ pmd_val(pmd) |= PMD_SECT_RDONLY;
+ return pmd;
+}
+
+static inline pmd_t pmd_mkold(pmd_t pmd)
+{
+ pmd_val(pmd) &= ~PMD_SECT_AF;
+ return pmd;
+}
+
+static inline pmd_t pmd_mksplitting(pmd_t pmd)
+{
+ pmd_val(pmd) |= PMD_SECT_SPLITTING;
+ return pmd;
+}
+
+static inline pmd_t pmd_mkwrite(pmd_t pmd)
+{
+ pmd_val(pmd) &= ~PMD_SECT_RDONLY;
+ return pmd;
+}
+
+static inline pmd_t pmd_mkdirty(pmd_t pmd)
+{
+ pmd_val(pmd) |= PMD_SECT_DIRTY;
+ return pmd;
+}
+
+static inline pmd_t pmd_mkyoung(pmd_t pmd)
+{
+ pmd_val(pmd) |= PMD_SECT_AF;
+ return pmd;
+}
+
+static inline pmd_t pmd_mknotpresent(pmd_t pmd)
+{
+ pmd_val(pmd) &= ~PMD_TYPE_MASK;
+ return pmd;
+}
#define pmd_mkhuge(pmd) (__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT))
--
1.8.1.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox