* Problem with "ARM: tegra: Move tegra_common_init to tegra_init_early"
From: Colin Cross @ 2011-02-23 7:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <74CDBE0F657A3D45AFBB94109FB122FF03112A5C22@HQMAIL01.nvidia.com>
On Tue, Feb 22, 2011 at 7:12 PM, Stephen Warren <swarren@nvidia.com> wrote:
> The following commit makes the Tegra APB DMA engine fail to initialize correctly:
>
> commit 0cf6230af909a86f81907455eca2a5c9b8f68fe6
>
> ? ?ARM: tegra: Move tegra_common_init to tegra_init_early
>
> ? ?Move tegra_common_init to tegra_init_early, and set it
> ? ?as the init_early entry in the machine struct.
> ? ?Initializes the clocks earlier so that timers can enable
> ? ?their clocks.
>
> ? ?Also reorders the members in the Harmony and Trimslice
> ? ?boards' machine structs to match the order they are
> ? ?called in.
>
> The reason is that tegra_init_early_ calls tegra_dma_init which calls
> request_threaded_irq, which fails since the IRQ hasn't yet been marked valid;
> that only happens in tegra_init_irq, which gets called after tegra_init_early.
>
> This used to work OK, since tegra_init_early was tegra_common_init, which got
> called after tegra_init_irq, basically from the beginning of tegra_harmony_init.
>
> I tried moving the call to tegra_dma_init back to each of the very end of
> tegra_init_irq and the very beginning of tegra_harmony_init. Both of these
> resulted in a kernel that wouldn't boot.
>
> Simply removing the call to tegra_dma_init completely does allow the kernel
> to boot, but obviously, DMA isn't available.
>
> I'm a little stumped why moving tegra_dma_init causes a boot failure. Does
> anyone have any helpful ideas, or any alternative ways to fix the underlying
> problem?
I don't know why it still fails if you put it back at the beginning of
the machine init, and it boots without errors on my board if I move
tegra_dma_init to a postcore_initcall, but I don't have any drivers on
my board that use DMA.
Can you provide kernel logs?
^ permalink raw reply
* [PATCH] ARM: EXYNOS4: Fix wrong constants in the hotplug assembly code.
From: Kyungmin Park @ 2011-02-23 7:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298444621-3739-1-git-send-email-kgene.kim@samsung.com>
Hi,
At that time Russell modified the s5cv310, tegra, and realview also. I
think you also modify it.
Thank you,
Kyungmin Park
On Wed, Feb 23, 2011 at 4:03 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> This patch fixes wrong constants in the hotplug assembly code for
> Exynos4 such as Russell's changing in vexpress hotplug and fixes
> hard-coded control register constatns also.
>
> Reported-by: Changhwan Youn <chaos.youn@samsung.com>
> Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> ?arch/arm/mach-exynos4/hotplug.c | ? ?8 ++++----
> ?1 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/mach-exynos4/hotplug.c b/arch/arm/mach-exynos4/hotplug.c
> index 4c42f9c..2b5909e 100644
> --- a/arch/arm/mach-exynos4/hotplug.c
> +++ b/arch/arm/mach-exynos4/hotplug.c
> @@ -30,13 +30,13 @@ static inline void cpu_enter_lowpower(void)
> ? ? ? ? * Turn off coherency
> ? ? ? ? */
> ? ? ? ?" ? ? ? mrc ? ? p15, 0, %0, c1, c0, 1\n"
> - ? ? ? " ? ? ? bic ? ? %0, %0, #0x20\n"
> + ? ? ? " ? ? ? bic ? ? %0, %0, %3\n"
> ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 1\n"
> ? ? ? ?" ? ? ? mrc ? ? p15, 0, %0, c1, c0, 0\n"
> ? ? ? ?" ? ? ? bic ? ? %0, %0, %2\n"
> ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 0\n"
> ? ? ? ? ?: "=&r" (v)
> - ? ? ? ? : "r" (0), "Ir" (CR_C)
> + ? ? ? ? : "r" (0), "Ir" (CR_C), "Ir" (0x40)
> ? ? ? ? ?: "cc");
> ?}
>
> @@ -49,10 +49,10 @@ static inline void cpu_leave_lowpower(void)
> ? ? ? ?" ? ? ? orr ? ? %0, %0, %1\n"
> ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 0\n"
> ? ? ? ?" ? ? ? mrc ? ? p15, 0, %0, c1, c0, 1\n"
> - ? ? ? " ? ? ? orr ? ? %0, %0, #0x20\n"
> + ? ? ? " ? ? ? orr ? ? %0, %0, %2\n"
> ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 1\n"
> ? ? ? ? ?: "=&r" (v)
> - ? ? ? ? : "Ir" (CR_C)
> + ? ? ? ? : "Ir" (CR_C), "Ir" (0x40)
> ? ? ? ? ?: "cc");
> ?}
>
> --
> 1.7.1
>
> --
> 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 v2 2/5] ARM: imx51/53: add sdhc3/4 clock
From: Sascha Hauer @ 2011-02-23 7:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298369606-29972-2-git-send-email-Hong-Xing.Zhu@freescale.com>
On Tue, Feb 22, 2011 at 06:13:23PM +0800, Richard Zhu wrote:
> Signed-off-by: Richard Zhao <richard.zhao@freescale.com>
> ---
> arch/arm/mach-mx5/clock-mx51-mx53.c | 100 ++++++++++++++++++++++++++++++++++-
> arch/arm/mach-mx5/crm_regs.h | 7 +++
> 2 files changed, 106 insertions(+), 1 deletions(-)
>
> diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c
> index 8164b1d..2ca97de 100644
> --- a/arch/arm/mach-mx5/clock-mx51-mx53.c
> +++ b/arch/arm/mach-mx5/clock-mx51-mx53.c
> @@ -42,6 +42,9 @@ static struct clk usboh3_clk;
> static struct clk emi_fast_clk;
> static struct clk ipu_clk;
> static struct clk mipi_hsc1_clk;
> +static struct clk esdhc1_clk;
> +static struct clk esdhc2_clk;
> +static struct clk esdhc3_mx53_clk;
>
> #define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */
>
> @@ -1138,15 +1141,45 @@ static struct clk ecspi_main_clk = {
> .set_parent = clk_ecspi_set_parent,
> };
>
> +#define SDHC_SET_PARENT_SHORT(name, parent2, bitsname) \
> +static int clk_##name##_set_parent(struct clk *clk, struct clk *parent) \
> +{ \
> + u32 reg; \
> + \
> + reg = __raw_readl(MXC_CCM_CSCMR1); \
> + if (parent == &esdhc1_clk) \
> + reg &= ~MXC_CCM_CSCMR1_##bitsname##_CLK_SEL; \
> + else if (parent == &parent2) \
> + reg |= MXC_CCM_CSCMR1_##bitsname##_CLK_SEL; \
> + else \
> + return -EINVAL; \
> + __raw_writel(reg, MXC_CCM_CSCMR1); \
> + \
> + return 0; \
> +}
Please don't do this. I should have rejected this kind of stuff for the
i.MX23/28. This ## stuff looks short in the source code but expands to
duplicated binary code. Also it's hard to make changes in such code.
To answer your question about cleaning up the i.MX clock code you asked
few days ago: Yes, I definitely want to proceed on this once Jeremys
patches are ready. Then this can become a clock multiplexer consuming
not much space in both binary and source code.
For now I suggest that you just duplicate the code in real functions
without macro voodoo.
Sascha
> +
> /* eSDHC */
> CLK_GET_RATE(esdhc1, 1, ESDHC1_MSHC1)
> CLK_SET_PARENT(esdhc1, 1, ESDHC1_MSHC1)
> CLK_SET_RATE(esdhc1, 1, ESDHC1_MSHC1)
>
> +/* mx51 specific */
> CLK_GET_RATE(esdhc2, 1, ESDHC2_MSHC2)
> CLK_SET_PARENT(esdhc2, 1, ESDHC2_MSHC2)
> CLK_SET_RATE(esdhc2, 1, ESDHC2_MSHC2)
>
> +SDHC_SET_PARENT_SHORT(esdhc3, esdhc2_clk, ESDHC3)
> +SDHC_SET_PARENT_SHORT(esdhc4, esdhc2_clk, ESDHC4)
> +
> +/* mx53 specific */
> +SDHC_SET_PARENT_SHORT(esdhc2_mx53, esdhc3_mx53_clk, ESDHC2_MSHC2_MX53)
> +
> +CLK_GET_RATE(esdhc3_mx53, 1, ESDHC3_MX53)
> +CLK_SET_PARENT(esdhc3_mx53, 1, ESDHC3_MX53)
> +CLK_SET_RATE(esdhc3_mx53, 1, ESDHC3_MX53)
> +
> +SDHC_SET_PARENT_SHORT(esdhc4_mx53, esdhc3_mx53_clk, ESDHC4)
> +
> #define DEFINE_CLOCK_FULL(name, i, er, es, gr, sr, e, d, p, s) \
> static struct clk name = { \
> .id = i, \
> @@ -1251,9 +1284,62 @@ DEFINE_CLOCK_MAX(esdhc1_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG1_OFFSET,
> clk_esdhc1, &pll2_sw_clk, &esdhc1_ipg_clk);
> DEFINE_CLOCK_FULL(esdhc2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG2_OFFSET,
> NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL);
> +DEFINE_CLOCK_FULL(esdhc3_ipg_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG4_OFFSET,
> + NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL);
> +DEFINE_CLOCK_FULL(esdhc4_ipg_clk, 3, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG6_OFFSET,
> + NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL);
> +
> +/* mx51 specific */
> DEFINE_CLOCK_MAX(esdhc2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG3_OFFSET,
> clk_esdhc2, &pll2_sw_clk, &esdhc2_ipg_clk);
>
> +static struct clk esdhc3_clk = {
> + .id = 2,
> + .parent = &esdhc1_clk,
> + .set_parent = clk_esdhc3_set_parent,
> + .enable_reg = MXC_CCM_CCGR3,
> + .enable_shift = MXC_CCM_CCGRx_CG5_OFFSET,
> + .enable = _clk_max_enable,
> + .disable = _clk_max_disable,
> + .secondary = &esdhc3_ipg_clk,
> +};
> +static struct clk esdhc4_clk = {
> + .id = 3,
> + .parent = &esdhc1_clk,
> + .set_parent = clk_esdhc4_set_parent,
> + .enable_reg = MXC_CCM_CCGR3,
> + .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET,
> + .enable = _clk_max_enable,
> + .disable = _clk_max_disable,
> + .secondary = &esdhc4_ipg_clk,
> +};
> +
> +/* mx53 specific */
> +static struct clk esdhc2_mx53_clk = {
> + .id = 2,
> + .parent = &esdhc1_clk,
> + .set_parent = clk_esdhc2_mx53_set_parent,
> + .enable_reg = MXC_CCM_CCGR3,
> + .enable_shift = MXC_CCM_CCGRx_CG3_OFFSET,
> + .enable = _clk_max_enable,
> + .disable = _clk_max_disable,
> + .secondary = &esdhc3_ipg_clk,
> +};
> +
> +DEFINE_CLOCK_MAX(esdhc3_mx53_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG5_OFFSET,
> + clk_esdhc3_mx53, &pll2_sw_clk, &esdhc2_ipg_clk);
> +
> +static struct clk esdhc4_mx53_clk = {
> + .id = 3,
> + .parent = &esdhc1_clk,
> + .set_parent = clk_esdhc4_mx53_set_parent,
> + .enable_reg = MXC_CCM_CCGR3,
> + .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET,
> + .enable = _clk_max_enable,
> + .disable = _clk_max_disable,
> + .secondary = &esdhc4_ipg_clk,
> +};
> +
> DEFINE_CLOCK(mipi_esc_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG5_OFFSET, NULL, NULL, NULL, &pll2_sw_clk);
> DEFINE_CLOCK(mipi_hsc2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG4_OFFSET, NULL, NULL, &mipi_esc_clk, &pll2_sw_clk);
> DEFINE_CLOCK(mipi_hsc1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG3_OFFSET, NULL, NULL, &mipi_hsc2_clk, &pll2_sw_clk);
> @@ -1312,6 +1398,8 @@ static struct clk_lookup mx51_lookups[] = {
> _REGISTER_CLOCK("imx51-cspi.0", NULL, cspi_clk)
> _REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk)
> _REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_clk)
> + _REGISTER_CLOCK("sdhci-esdhc-imx.2", NULL, esdhc3_clk)
> + _REGISTER_CLOCK("sdhci-esdhc-imx.3", NULL, esdhc4_clk)
> _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk)
> _REGISTER_CLOCK(NULL, "iim_clk", iim_clk)
> _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk)
> @@ -1332,7 +1420,9 @@ static struct clk_lookup mx53_lookups[] = {
> _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk)
> _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk)
> _REGISTER_CLOCK("sdhci-esdhc-imx.0", NULL, esdhc1_clk)
> - _REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_clk)
> + _REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc2_mx53_clk)
> + _REGISTER_CLOCK("sdhci-esdhc-imx.2", NULL, esdhc3_mx53_clk)
> + _REGISTER_CLOCK("sdhci-esdhc-imx.3", NULL, esdhc4_mx53_clk)
> _REGISTER_CLOCK("imx53-ecspi.0", NULL, ecspi1_clk)
> _REGISTER_CLOCK("imx53-ecspi.1", NULL, ecspi2_clk)
> _REGISTER_CLOCK("imx53-cspi.0", NULL, cspi_clk)
> @@ -1425,6 +1515,14 @@ int __init mx53_clocks_init(unsigned long ckil, unsigned long osc,
> mx53_revision();
> clk_disable(&iim_clk);
>
> + /* Set SDHC parents to be PLL2 */
> + clk_set_parent(&esdhc1_clk, &pll2_sw_clk);
> + clk_set_parent(&esdhc3_mx53_clk, &pll2_sw_clk);
> +
> + /* set SDHC root clock as 200MHZ*/
> + clk_set_rate(&esdhc1_clk, 200000000);
> + clk_set_rate(&esdhc3_mx53_clk, 200000000);
> +
> /* System timer */
> mxc_timer_init(&gpt_clk, MX53_IO_ADDRESS(MX53_GPT1_BASE_ADDR),
> MX53_INT_GPT);
> diff --git a/arch/arm/mach-mx5/crm_regs.h b/arch/arm/mach-mx5/crm_regs.h
> index b462c22..87c0c58 100644
> --- a/arch/arm/mach-mx5/crm_regs.h
> +++ b/arch/arm/mach-mx5/crm_regs.h
> @@ -217,9 +217,12 @@
> #define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET (20)
> #define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK (0x3 << 20)
> #define MXC_CCM_CSCMR1_ESDHC3_CLK_SEL (0x1 << 19)
> +#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL (0x1 << 19)
> #define MXC_CCM_CSCMR1_ESDHC4_CLK_SEL (0x1 << 18)
> #define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET (16)
> #define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK (0x3 << 16)
> +#define MXC_CCM_CSCMR1_ESDHC3_MX53_CLK_SEL_OFFSET (16)
> +#define MXC_CCM_CSCMR1_ESDHC3_MX53_CLK_SEL_MASK (0x3 << 16)
> #define MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET (14)
> #define MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK (0x3 << 14)
> #define MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET (12)
> @@ -271,6 +274,10 @@
> #define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK (0x7 << 22)
> #define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET (19)
> #define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK (0x7 << 19)
> +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PRED_OFFSET (22)
> +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PRED_MASK (0x7 << 22)
> +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PODF_OFFSET (19)
> +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PODF_MASK (0x7 << 19)
> #define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET (16)
> #define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK (0x7 << 16)
> #define MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET (14)
> --
> 1.7.1
>
>
>
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* [PATCH v3] dmaengine: mxs-dma: add dma support for i.MX23/28
From: Lothar Waßmann @ 2011-02-23 7:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223055709.GC20346@S2100-06.ap.freescale.net>
Hi,
Shawn Guo writes:
> On Wed, Feb 23, 2011 at 09:31:41AM +0530, Koul, Vinod wrote:
> > On Mon, 2011-02-21 at 15:51 +0530, Shawn Guo wrote:
> > > This patch adds dma support for Freescale MXS-based SoC i.MX23/28,
> > > including apbh-dma and apbx-dma.
> > >
> > > * apbh-dma and apbx-dma are supported in the driver as two mxs-dma
> > > instances.
> > >
> > > * apbh-dma is different between mx23 and mx28, hardware version
> > > register is used to differentiate.
> > >
> > > * mxs-dma supports pio function besides data transfer. The driver
> > > uses dma_data_direction DMA_NONE to identify the pio mode, and
> > > steals sgl and sg_len to get pio words and numbers from clients.
> > >
> > > * mxs dmaengine has some very specific features, like sense function
> > > and the special NAND support (nand_lock, nand_wait4ready). These
> > > are too specific to implemented in generic dmaengine driver.
> > >
> > > * The driver refers to imx-sdma and only a single descriptor is
> > > statically assigned to each channel.
> > >
> > > Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
> > > ---
> > > arch/arm/mach-mxs/include/mach/dma.h | 26 ++
> > > drivers/dma/Kconfig | 8 +
> > > drivers/dma/Makefile | 1 +
> > > drivers/dma/mxs-dma.c | 719 ++++++++++++++++++++++++++++++++++
> > > 4 files changed, 754 insertions(+), 0 deletions(-)
> > > create mode 100644 arch/arm/mach-mxs/include/mach/dma.h
> > > create mode 100644 drivers/dma/mxs-dma.c
> > [snip]
> >
> > > +#define MXS_DMA_APBH 0
> > > +#define MXS_DMA_APBX 1
> > > +#define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH)
> > > +
> > > +#define APBH_VERSION_LATEST 3
> > > +#define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST)
> > > +
> > > +#define HW_APBHX_CTRL0 0x000
> > > +#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
> > > +#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
> > > +#define BP_APBH_CTRL0_CLKGATE_CHANNEL (8)
> > > +#define BP_APBH_CTRL0_RESET_CHANNEL (16)
>
There is no need to enclose bare number in macro definitions in
parenthesis.
...
Lothar Wa?mann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstra?e 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Gesch?ftsf?hrer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________
^ permalink raw reply
* [PATCH] ARM: mx51_defconfig: change CONFIG_LEDS_CLASS 'm' to 'y'
From: Shawn Guo @ 2011-02-23 8:06 UTC (permalink / raw)
To: linux-arm-kernel
This patch is to fix the warning below.
arch/arm/configs/mx51_defconfig:113:warning: symbol value 'm' invalid for LEDS_CLASS
Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
---
This warning bothers me for quite some time. I'm unsure if this is
the right fix, and I just want to draw people's attention with this
patch.
arch/arm/configs/mx51_defconfig | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/configs/mx51_defconfig b/arch/arm/configs/mx51_defconfig
index 9cba68c..e3c9032 100644
--- a/arch/arm/configs/mx51_defconfig
+++ b/arch/arm/configs/mx51_defconfig
@@ -110,7 +110,7 @@ CONFIG_MMC=y
CONFIG_MMC_BLOCK=m
CONFIG_MMC_SDHCI=m
CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=m
+CONFIG_LEDS_CLASS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
CONFIG_EXT2_FS=y
--
1.7.1
^ permalink raw reply related
* [PATCH] wip: convert imx27 to common struct clk
From: Uwe Kleine-König @ 2011-02-23 8:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D648A3D.7020402@codeaurora.org>
Hi Saravana,
On Tue, Feb 22, 2011 at 08:17:01PM -0800, Saravana Kannan wrote:
> On 02/22/2011 03:33 PM, Uwe Kleine-K?nig wrote:
> >I had to degrade one WARN_ON to WARN_ON_ONCE in drivers/clk/clk.c to
> >actually make it work. Otherwise console output results in a warning
> >that results in console output ...
>
> You won't be able to do mainline the WARN_ON_ONCE because that will
> only warn for the first clock that violates whatever condition it's
> warning about.
I wonder if it is more sensible while the warning is new. But I don't
care much because I'm going to fix the serial driver before switching.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [PATCH] ARM: EXYNOS4: Fix wrong constants in the hotplug assembly code.
From: Kukjin Kim @ 2011-02-23 8:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTinpjGwd+pFr1oKQb1jiaih2h81Wp9G7Df8J1LLk@mail.gmail.com>
Kyungmin Park wrote:
>
> Hi,
>
Hi :)
> At that time Russell modified the s5cv310, tegra, and realview also. I
> think you also modify it.
>
Maybe you mean commit 30b99d07 (ARM: fix wrongly patched constants).
It was due to his patch commit e3d9c625 (ARM: CPU hotplug: fix hard-coded
control register constants) but this is different with that.
Firstly this patch fixes wrong constants in the hotplug assembly code.
Then secondly, fixes hard-coded control register constants.
Of course, first reason is more important on this.
And I'm not sure whether need to fix on tegra also.
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
> On Wed, Feb 23, 2011 at 4:03 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> > This patch fixes wrong constants in the hotplug assembly code for
> > Exynos4 such as Russell's changing in vexpress hotplug and fixes
> > hard-coded control register constatns also.
> >
> > Reported-by: Changhwan Youn <chaos.youn@samsung.com>
> > Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > ---
> > ?arch/arm/mach-exynos4/hotplug.c | ? ?8 ++++----
> > ?1 files changed, 4 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm/mach-exynos4/hotplug.c b/arch/arm/mach-
> exynos4/hotplug.c
> > index 4c42f9c..2b5909e 100644
> > --- a/arch/arm/mach-exynos4/hotplug.c
> > +++ b/arch/arm/mach-exynos4/hotplug.c
> > @@ -30,13 +30,13 @@ static inline void cpu_enter_lowpower(void)
> > ? ? ? ? * Turn off coherency
> > ? ? ? ? */
> > ? ? ? ?" ? ? ? mrc ? ? p15, 0, %0, c1, c0, 1\n"
> > - ? ? ? " ? ? ? bic ? ? %0, %0, #0x20\n"
> > + ? ? ? " ? ? ? bic ? ? %0, %0, %3\n"
> > ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 1\n"
> > ? ? ? ?" ? ? ? mrc ? ? p15, 0, %0, c1, c0, 0\n"
> > ? ? ? ?" ? ? ? bic ? ? %0, %0, %2\n"
> > ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 0\n"
> > ? ? ? ? ?: "=&r" (v)
> > - ? ? ? ? : "r" (0), "Ir" (CR_C)
> > + ? ? ? ? : "r" (0), "Ir" (CR_C), "Ir" (0x40)
> > ? ? ? ? ?: "cc");
> > ?}
> >
> > @@ -49,10 +49,10 @@ static inline void cpu_leave_lowpower(void)
> > ? ? ? ?" ? ? ? orr ? ? %0, %0, %1\n"
> > ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 0\n"
> > ? ? ? ?" ? ? ? mrc ? ? p15, 0, %0, c1, c0, 1\n"
> > - ? ? ? " ? ? ? orr ? ? %0, %0, #0x20\n"
> > + ? ? ? " ? ? ? orr ? ? %0, %0, %2\n"
> > ? ? ? ?" ? ? ? mcr ? ? p15, 0, %0, c1, c0, 1\n"
> > ? ? ? ? ?: "=&r" (v)
> > - ? ? ? ? : "Ir" (CR_C)
> > + ? ? ? ? : "Ir" (CR_C), "Ir" (0x40)
> > ? ? ? ? ?: "cc");
> > ?}
> >
> > --
> > 1.7.1
^ permalink raw reply
* [PATCH 8/8] OMAP2+: clockevent: late-init GPTIMER clockevent hwmodright before timer init
From: Santosh Shilimkar @ 2011-02-23 8:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223071153.5874.58923.stgit@twilight.localdomain>
Paul,
> -----Original Message-----
> From: Paul Walmsley [mailto:paul at pwsan.com]
> Sent: Wednesday, February 23, 2011 12:42 PM
> To: linux-omap at vger.kernel.org; linux-arm-kernel at lists.infradead.org
> Cc: Tony Lindgren; Kevin Hilman; Santosh Shilimkar; Beno?t Cousson
> Subject: [PATCH 8/8] OMAP2+: clockevent: late-init GPTIMER
> clockevent hwmodright before timer init
>
> Late-initialize the GPTIMER hwmod used for the clockevent source
> immediately
> before it is used. This avoids the need to late-initialize all of
> the hwmods
> until the boot process is further along. (In general, we want to
> defer
> as much as possible until late in the boot process.)
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Beno?t Cousson <b-cousson@ti.com>
> Cc: Tony Lindgren <tony@atomide.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
> ---
> arch/arm/mach-omap2/timer-gp.c | 8 +++++++-
> 1 files changed, 7 insertions(+), 1 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-
> omap2/timer-gp.c
> index 0fc550e..a4e51a2 100644
> --- a/arch/arm/mach-omap2/timer-gp.c
> +++ b/arch/arm/mach-omap2/timer-gp.c
> @@ -40,10 +40,11 @@
> #include <plat/dmtimer.h>
> #include <asm/localtimer.h>
> #include <asm/sched_clock.h>
> +#include <plat/common.h>
>
> #include "timer-gp.h"
> +#include <plat/omap_hwmod.h>
>
> -#include <plat/common.h>
>
> /* MAX_GPTIMER_ID: number of GPTIMERs on the chip */
> #define MAX_GPTIMER_ID 12
> @@ -133,9 +134,13 @@ static void __init
> omap2_gp_clockevent_init(void)
> {
> u32 tick_rate;
> int src;
> + const char *clockevent_hwmod_name;
>
> inited = 1;
>
> + clockevent_hwmod_name = (gptimer_id == 12) ? "timer12" :
> "timer1";
> + omap_hwmod_late_init_one(clockevent_hwmod_name);
> +
Do we need above hard-coding ? This takes away flexibility of
choosing system timer from board files, right ?
Am I missing something here?
> gptimer = omap_dm_timer_request_specific(gptimer_id);
> BUG_ON(gptimer == NULL);
> gptimer_wakeup = gptimer;
> @@ -250,6 +255,7 @@ static void __init omap2_gp_timer_init(void)
> BUG_ON(!twd_base);
> }
> #endif
> +
> omap_dm_timer_init();
>
> omap2_gp_clockevent_init();
>
^ permalink raw reply
* [PATCH] ARM: mx51_defconfig: change CONFIG_LEDS_CLASS 'm' to 'y'
From: Uwe Kleine-König @ 2011-02-23 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298448415-21986-1-git-send-email-shawn.guo@freescale.com>
Hello Shawn,
On Wed, Feb 23, 2011 at 04:06:55PM +0800, Shawn Guo wrote:
> This patch is to fix the warning below.
>
> arch/arm/configs/mx51_defconfig:113:warning: symbol value 'm' invalid for LEDS_CLASS
I fixed that in my local tree, too, together with some more things.
Actually I thought I already sent it out, but my mailboxes seem to
not agree. I wanted to update my pull request for Sascha today anyhow
and can put that one in, too.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [PATCH] ARM: mx51_defconfig: change CONFIG_LEDS_CLASS 'm' to 'y'
From: Shawn Guo @ 2011-02-23 9:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223085530.GO22310@pengutronix.de>
Hi Uwe,
On Wed, Feb 23, 2011 at 09:55:30AM +0100, Uwe Kleine-K?nig wrote:
> Hello Shawn,
>
> On Wed, Feb 23, 2011 at 04:06:55PM +0800, Shawn Guo wrote:
> > This patch is to fix the warning below.
> >
> > arch/arm/configs/mx51_defconfig:113:warning: symbol value 'm' invalid for LEDS_CLASS
> I fixed that in my local tree, too, together with some more things.
> Actually I thought I already sent it out, but my mailboxes seem to
> not agree. I wanted to update my pull request for Sascha today anyhow
> and can put that one in, too.
>
Feel free to drop mine, as long as we get it fixed.
--
Regards,
Shawn
^ permalink raw reply
* [PATCH 0/5] my imx patches for 2.6.39
From: Uwe Kleine-König @ 2011-02-23 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110218213022.GJ22310@pengutronix.de>
Hello
On Fri, Feb 18, 2011 at 10:30:22PM +0100, Uwe Kleine-K?nig wrote:
> apart from the patches below there is another patch not yet applied.
> That's "mxs: irq_data conversion" that was already sent on this list.
> Because I assumed that it belongs to Russells domain I sent it to the
> patch system as
> http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6744/1
> . If you prefer, I can add it to this series; just tell me.
Russell asked Sascha to ack this patch, so I assume it will not go via
Sascha.
I found another patch that should be added to .39:
Uwe Kleine-K?nig (1):
ARM: mx51_defconfig: enable various options
Will send this in reply to this mail.
> The following changes since commit b807afe41ceefef11a0d965cd9970142afc53d25:
>
> ARM: imx50: correct iomux-mx50.h wrong daisy chain settings (2011-02-18 11:01:34 +0100)
>
> are available in the git repository at:
> git://git.pengutronix.de/git/ukl/linux-2.6.git imx-for-2.6.39
>
> Lothar Wa?mann (2):
> ARM: mxs: free dma_mask in error path
> ARM: mxs: add a dma mask to fec devices
>
> Sascha Hauer (1):
> ARM: mxs: Add pwm clocks and device registration
>
> Uwe Kleine-K?nig (2):
> ARM: mxc: free dma_mask in error path
> ARM: mxc: add a dma mask to fec devices
>
> arch/arm/mach-mxs/Kconfig | 2 ++
> arch/arm/mach-mxs/clock-mx23.c | 6 +++++-
> arch/arm/mach-mxs/clock-mx28.c | 9 ++++++++-
> arch/arm/mach-mxs/devices-mx23.h | 2 ++
> arch/arm/mach-mxs/devices-mx28.h | 2 ++
> arch/arm/mach-mxs/devices.c | 2 ++
> arch/arm/mach-mxs/devices/Kconfig | 3 +++
> arch/arm/mach-mxs/devices/Makefile | 1 +
> arch/arm/mach-mxs/devices/platform-fec.c | 5 +++--
> arch/arm/mach-mxs/devices/platform-mxs-pwm.c | 22 ++++++++++++++++++++++
> arch/arm/mach-mxs/include/mach/devices-common.h | 4 ++++
> arch/arm/plat-mxc/devices.c | 2 ++
> arch/arm/plat-mxc/devices/platform-fec.c | 4 ++--
> 13 files changed, 58 insertions(+), 6 deletions(-)
> create mode 100644 arch/arm/mach-mxs/devices/platform-mxs-pwm.c
The new diffstat is:
arch/arm/configs/mx51_defconfig | 53 +++++++++++++++++++----
arch/arm/mach-mxs/Kconfig | 2 +
arch/arm/mach-mxs/clock-mx23.c | 6 ++-
arch/arm/mach-mxs/clock-mx28.c | 9 +++-
arch/arm/mach-mxs/devices-mx23.h | 2 +
arch/arm/mach-mxs/devices-mx28.h | 2 +
arch/arm/mach-mxs/devices.c | 2 +
arch/arm/mach-mxs/devices/Kconfig | 3 +
arch/arm/mach-mxs/devices/Makefile | 1 +
arch/arm/mach-mxs/devices/platform-fec.c | 5 +-
arch/arm/mach-mxs/devices/platform-mxs-pwm.c | 22 +++++++++
arch/arm/mach-mxs/include/mach/devices-common.h | 4 ++
arch/arm/plat-mxc/devices.c | 2 +
arch/arm/plat-mxc/devices/platform-fec.c | 4 +-
14 files changed, 102 insertions(+), 15 deletions(-)
create mode 100644 arch/arm/mach-mxs/devices/platform-mxs-pwm.c
(only arch/arm/configs/mx51_defconfig added and a different scale for the other
lines)
Sascha, I hope that's OK for you.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [PATCH 8/8] OMAP2+: clockevent: late-init GPTIMER clockevent hwmod right before timer init
From: DebBarma, Tarun Kanti @ 2011-02-23 9:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223071153.5874.58923.stgit@twilight.localdomain>
> -----Original Message-----
> From: linux-omap-owner at vger.kernel.org [mailto:linux-omap-
> owner at vger.kernel.org] On Behalf Of Paul Walmsley
> Sent: Wednesday, February 23, 2011 12:42 PM
> To: linux-omap at vger.kernel.org; linux-arm-kernel at lists.infradead.org
> Cc: Tony Lindgren; Hilman, Kevin; Shilimkar, Santosh; Cousson, Benoit
> Subject: [PATCH 8/8] OMAP2+: clockevent: late-init GPTIMER clockevent
> hwmod right before timer init
I am not able to apply this patch.
Patch 1-7 applied successfully on top of:
04aa67d Merge branch 'for-tony' of git://gitorious.org/usb/usb into omap-for-linus
--
Tarun
>
> Late-initialize the GPTIMER hwmod used for the clockevent source
> immediately
> before it is used. This avoids the need to late-initialize all of the
> hwmods
> until the boot process is further along. (In general, we want to defer
> as much as possible until late in the boot process.)
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Beno?t Cousson <b-cousson@ti.com>
> Cc: Tony Lindgren <tony@atomide.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
> ---
> arch/arm/mach-omap2/timer-gp.c | 8 +++++++-
> 1 files changed, 7 insertions(+), 1 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-
> gp.c
> index 0fc550e..a4e51a2 100644
> --- a/arch/arm/mach-omap2/timer-gp.c
> +++ b/arch/arm/mach-omap2/timer-gp.c
> @@ -40,10 +40,11 @@
> #include <plat/dmtimer.h>
> #include <asm/localtimer.h>
> #include <asm/sched_clock.h>
> +#include <plat/common.h>
>
> #include "timer-gp.h"
> +#include <plat/omap_hwmod.h>
>
> -#include <plat/common.h>
>
> /* MAX_GPTIMER_ID: number of GPTIMERs on the chip */
> #define MAX_GPTIMER_ID 12
> @@ -133,9 +134,13 @@ static void __init omap2_gp_clockevent_init(void)
> {
> u32 tick_rate;
> int src;
> + const char *clockevent_hwmod_name;
>
> inited = 1;
>
> + clockevent_hwmod_name = (gptimer_id == 12) ? "timer12" : "timer1";
> + omap_hwmod_late_init_one(clockevent_hwmod_name);
> +
> gptimer = omap_dm_timer_request_specific(gptimer_id);
> BUG_ON(gptimer == NULL);
> gptimer_wakeup = gptimer;
> @@ -250,6 +255,7 @@ static void __init omap2_gp_timer_init(void)
> BUG_ON(!twd_base);
> }
> #endif
> +
> omap_dm_timer_init();
>
> omap2_gp_clockevent_init();
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" 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] ARM: mx51_defconfig: enable various options
From: Uwe Kleine-König @ 2011-02-23 9:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223091207.GP22310@pengutronix.de>
- all currently available machines
- MXC_PWM
- CAN_MCP251X (not yet available but pending)
- MTD_NAND_MXC and UBI
- I2C_IMX and SPI_IMX
- mc13xxx including regulator, rtc and touch support
- SND_IMX_SOC
- IMX_SDMA
The change CONFIG_LEDS_CLASS=m -> y was needed to fix a kconfig warning
arch/arm/configs/mx51_defconfig:113:warning: symbol value 'm' invalid for LEDS_CLASS
and was suggested independently by Shawn Guo, too.
The other changes are either needed to get the options above or just update
noise.
Signed-off-by: Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>
---
arch/arm/configs/mx51_defconfig | 53 ++++++++++++++++++++++++++++++++------
1 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/arch/arm/configs/mx51_defconfig b/arch/arm/configs/mx51_defconfig
index 9cba68c..11a9904 100644
--- a/arch/arm/configs/mx51_defconfig
+++ b/arch/arm/configs/mx51_defconfig
@@ -17,6 +17,12 @@ CONFIG_ARCH_MX5=y
CONFIG_MACH_MX51_BABBAGE=y
CONFIG_MACH_MX51_3DS=y
CONFIG_MACH_EUKREA_CPUIMX51=y
+CONFIG_MACH_EUKREA_CPUIMX51SD=y
+CONFIG_MACH_MX51_EFIKAMX=y
+CONFIG_MACH_MX53_EVK=y
+CONFIG_MACH_MX53_SMD=y
+CONFIG_MACH_MX53_LOCO=y
+CONFIG_MXC_PWM=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT_VOLUNTARY=y
@@ -41,13 +47,26 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set
+CONFIG_CAN=y
+CONFIG_CAN_RAW=y
+CONFIG_CAN_DEV=y
+CONFIG_CAN_CALC_BITTIMING=y
+CONFIG_CAN_MCP251X=y
# CONFIG_WIRELESS is not set
# CONFIG_STANDALONE is not set
CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_MXC=y
+CONFIG_MTD_UBI=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=65536
-# CONFIG_MISC_DEVICES is not set
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
@@ -58,6 +77,7 @@ CONFIG_SCSI_SCAN_ASYNC=y
# CONFIG_SCSI_LOWLEVEL is not set
CONFIG_ATA=m
CONFIG_NETDEVICES=y
+CONFIG_MII=m
CONFIG_MARVELL_PHY=y
CONFIG_DAVICOM_PHY=y
CONFIG_QSEMI_PHY=y
@@ -74,7 +94,6 @@ CONFIG_LSI_ET1011C_PHY=y
CONFIG_MDIO_BITBANG=y
CONFIG_MDIO_GPIO=y
CONFIG_NET_ETHERNET=y
-CONFIG_MII=m
CONFIG_FEC=y
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
@@ -82,10 +101,12 @@ CONFIG_FEC=y
CONFIG_INPUT_FF_MEMLESS=m
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_EVDEV=y
-CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
CONFIG_MOUSE_PS2=m
CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_MC13783=y
CONFIG_SERIO_SERPORT=m
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_DEVKMEM is not set
@@ -100,8 +121,21 @@ CONFIG_I2C_CHARDEV=m
CONFIG_I2C_ALGOBIT=m
CONFIG_I2C_ALGOPCF=m
CONFIG_I2C_ALGOPCA=m
+CONFIG_I2C_IMX=y
+CONFIG_SPI=y
+CONFIG_SPI_IMX=y
+CONFIG_SPI_SPIDEV=y
CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
+CONFIG_MFD_MC13XXX=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_MC13783=y
+CONFIG_REGULATOR_MC13892=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+# CONFIG_SND_SUPPORT_OLD_API is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_IMX_SOC=y
# CONFIG_HID_SUPPORT is not set
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
@@ -110,9 +144,12 @@ CONFIG_MMC=y
CONFIG_MMC_BLOCK=m
CONFIG_MMC_SDHCI=m
CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=m
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PWM=y
CONFIG_RTC_CLASS=y
-CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_RTC_DRV_MC13XXX=y
+CONFIG_DMADEVICES=y
+CONFIG_IMX_SDMA=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
@@ -126,7 +163,6 @@ CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
# CONFIG_PRINT_QUOTA_WARNING is not set
-CONFIG_AUTOFS_FS=y
CONFIG_AUTOFS4_FS=y
CONFIG_FUSE_FS=y
CONFIG_ISO9660_FS=m
@@ -153,14 +189,13 @@ CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_SCHED_DEBUG is not set
# CONFIG_DEBUG_BUGVERBOSE is not set
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_FTRACE is not set
# CONFIG_ARM_UNWIND is not set
CONFIG_DEBUG_LL=y
CONFIG_EARLY_PRINTK=y
CONFIG_SECURITYFS=y
-CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=m
--
1.7.2.3
^ permalink raw reply related
* [PATCH v3 2/2] OMAP: IOMMU: add support to callback during fault handling
From: David Cohen @ 2011-02-23 9:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTikZu+pqwpPXExFirPouN+1i3Yp0QHfomHN4decx@mail.gmail.com>
On Wed, Feb 23, 2011 at 3:17 AM, Guzman Lugo, Fernando
<fernando.lugo@ti.com> wrote:
> On Wed, Feb 16, 2011 at 1:35 PM, David Cohen <dacohen@gmail.com> wrote:
>> Add support to register an isr for IOMMU fault situations and adapt it
>> to allow such (*isr)() to be used as fault callback. Drivers using IOMMU
>> module might want to be informed when errors happen in order to debug it
>> or react.
>>
>> Signed-off-by: David Cohen <dacohen@gmail.com>
>> ---
>> ?arch/arm/mach-omap2/iommu2.c ? ? ? ? ? ?| ? 17 +++++++++-
>> ?arch/arm/plat-omap/include/plat/iommu.h | ? 14 ++++++++-
>> ?arch/arm/plat-omap/iommu.c ? ? ? ? ? ? ?| ? 52 ++++++++++++++++++++++---------
>> ?3 files changed, 65 insertions(+), 18 deletions(-)
>>
> ....
>
>> @@ -917,6 +912,33 @@ void iommu_put(struct iommu *obj)
>> ?}
>> ?EXPORT_SYMBOL_GPL(iommu_put);
>>
>> +int iommu_set_isr(const char *name,
>> + ? ? ? ? ? ? ? ? int (*isr)(struct iommu *obj, u32 da, u32 iommu_errs,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?void *priv),
>> + ? ? ? ? ? ? ? ? void *isr_priv)
>> +{
>> + ? ? ? struct device *dev;
>> + ? ? ? struct iommu *obj;
>> +
>
> if the driver support multiple user for the same iommu why can only
> one callback be registered? should it support register multiple
> callback function (one per user)?
Can you define a scenario for that?
On OMAP3 ISP the multiple users are the multiple ISP submodule, but I
don't think it's necessary all submodule to have a specific callback.
ISP core layer should handle.
Br,
David
>
> Regards,
> Fernando.
>
^ permalink raw reply
* [PATCH 0/3] OMAP2+ hwmod fixes
From: Rajendra Nayak @ 2011-02-23 10:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.00.1102221200260.22636@utopia.booyaka.com>
Hi Paul,
> -----Original Message-----
> From: Paul Walmsley [mailto:paul at pwsan.com]
> Sent: Wednesday, February 23, 2011 12:40 AM
> To: Rajendra Nayak; Cousson, Benoit
> Cc: linux-omap at vger.kernel.org; linux-arm-kernel at lists.infradead.org
> Subject: RE: [PATCH 0/3] OMAP2+ hwmod fixes
>
> Hi Rajendra
>
> On Tue, 22 Feb 2011, Rajendra Nayak wrote:
>
> > The original behavior of the iterators, to terminate upon
> > encountering an error, seems fine to me. The only problem
> > I faced was that they fail silently and go undetected, unless
> > their user catches the return value and WARN's, which I found
> > was not the case with most users, mainly those of
> > omap_hwmod_for_each_by_class.
> > I was thinking of keeping the behaviour of these iterators
> > same for now and add WARN's in these iterators itself upon
> > an error, so its seen even if the user fails to catch it.
>
> What's your opinion on adding the pr_err() or WARN() into the code that
> the iterator calls for each hwmod? That code should know why something
> fails, so it should be able to provide a more detailed error message.
Of
> course, it is not as general a solution...
I agree, if the callback functions are written with proper errors
or WARN's, they are the right place where most of the details'
exist. So maybe we don't need these in the iterator's after all.
Regards,
Rajendra
>
>
> - Paul
^ permalink raw reply
* [PATCH v3] dmaengine: mxs-dma: add dma support for i.MX23/28
From: Russell King - ARM Linux @ 2011-02-23 10:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298440516.23371.258.camel@vkoul-udesk3>
On Wed, Feb 23, 2011 at 11:25:16AM +0530, Koul, Vinod wrote:
> Well in that case you are calling the callback from irq handler which is
> not so good. The tasklet is other extreme (slow).
tasklets are run on the exit path of interrupts.
> Given that this is
> also intended for audio I am not sure if calling period elapsed from
> tasklet callback will not cause any audio over/underruns, last I tried
> it used to break quite often in stressed situations.
Nevertheless, the DMA engine API specifies that callbacks shall be called
from tasklet context.
^ permalink raw reply
* [PATCH 2/5] ARM: Samsung: cleanup S5P gpio interrupt code
From: Kukjin Kim @ 2011-02-23 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297927527-1338-3-git-send-email-m.szyprowski@samsung.com>
Marek Szyprowski wrote:
>
> This patch performs a global cleanup in s5p gpio interrupt support code.
> The code is prepared for upcoming support for gpio interrupts on S5PC210
> platform, which has 2 gpio banks (regions) instead of one (like on
> S5PC110 and S5PC100).
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> arch/arm/plat-s5p/irq-gpioint.c | 106
+++++++++++++++++--------------------
> --
> 1 files changed, 46 insertions(+), 60 deletions(-)
>
> diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-
> gpioint.c
> index 3b6bf89..af10328 100644
> --- a/arch/arm/plat-s5p/irq-gpioint.c
> +++ b/arch/arm/plat-s5p/irq-gpioint.c
> @@ -22,77 +22,64 @@
> #include <plat/gpio-core.h>
> #include <plat/gpio-cfg.h>
>
> -#define S5P_GPIOREG(x) (S5P_VA_GPIO + (x))
> +#define GPIO_BASE(chip) (((unsigned long)(chip)->base) &
> ~(SZ_4K - 1))
>
Need SZ_4K here instead of 0xFFFFF000?
> -#define GPIOINT_CON_OFFSET 0x700
> -#define GPIOINT_MASK_OFFSET 0x900
> -#define GPIOINT_PEND_OFFSET 0xA00
> +#define CON_OFFSET 0x700
> +#define MASK_OFFSET 0x900
> +#define PEND_OFFSET 0xA00
I don't know why need to change above definitions...
> +#define REG_OFFSET(x) ((x) << 2)
>
Actually, this is used instead of "group << 2" in this file.
So how about "GPIOINT_REG_OFFSET(x)" like others?
> static struct s3c_gpio_chip *irq_chips[S5P_GPIOINT_GROUP_MAXNR];
>
> -static int s5p_gpioint_get_group(struct irq_data *data)
> -{
> - struct gpio_chip *chip = irq_data_get_irq_data(data);
> - struct s3c_gpio_chip *s3c_chip = container_of(chip,
> - struct s3c_gpio_chip, chip);
> - int group;
> -
> - for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++)
> - if (s3c_chip == irq_chips[group])
> - break;
> -
> - return group;
> -}
> -
> static int s5p_gpioint_get_offset(struct irq_data *data)
> {
> - struct gpio_chip *chip = irq_data_get_irq_data(data);
> - struct s3c_gpio_chip *s3c_chip = container_of(chip,
> - struct s3c_gpio_chip, chip);
> -
> - return data->irq - s3c_chip->irq_base;
> + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
> + return data->irq - chip->irq_base;
> }
>
> static void s5p_gpioint_ack(struct irq_data *data)
> {
> + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
> int group, offset, pend_offset;
> unsigned int value;
>
> - group = s5p_gpioint_get_group(data);
> + group = chip->group;
> offset = s5p_gpioint_get_offset(data);
> - pend_offset = group << 2;
> + pend_offset = REG_OFFSET(group);
>
> - value = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset);
> - value |= 1 << offset;
> - __raw_writel(value, S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset);
> + value = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
> + value |= BIT(offset);
No need inclusion <linux/bitops.h>?
> + __raw_writel(value, GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
> }
>
> static void s5p_gpioint_mask(struct irq_data *data)
> {
> + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
> int group, offset, mask_offset;
> unsigned int value;
>
> - group = s5p_gpioint_get_group(data);
> + group = chip->group;
> offset = s5p_gpioint_get_offset(data);
> - mask_offset = group << 2;
> + mask_offset = REG_OFFSET(group);
>
> - value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset);
> - value |= 1 << offset;
> - __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset);
> + value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
> + value |= BIT(offset);
> + __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
> }
>
> static void s5p_gpioint_unmask(struct irq_data *data)
> {
> + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
> int group, offset, mask_offset;
> unsigned int value;
>
> - group = s5p_gpioint_get_group(data);
> + group = chip->group;
> offset = s5p_gpioint_get_offset(data);
> - mask_offset = group << 2;
> + mask_offset = REG_OFFSET(group);
>
> - value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset);
> - value &= ~(1 << offset);
> - __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset);
> + value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
> + value &= ~BIT(offset);
> + __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
> }
>
> static void s5p_gpioint_mask_ack(struct irq_data *data)
> @@ -103,12 +90,13 @@ static void s5p_gpioint_mask_ack(struct irq_data
*data)
>
> static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type)
> {
> + struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
> int group, offset, con_offset;
> unsigned int value;
>
> - group = s5p_gpioint_get_group(data);
> + group = chip->group;
> offset = s5p_gpioint_get_offset(data);
> - con_offset = group << 2;
> + con_offset = REG_OFFSET(group);
>
> switch (type) {
> case IRQ_TYPE_EDGE_RISING:
> @@ -132,15 +120,15 @@ static int s5p_gpioint_set_type(struct irq_data
*data,
> unsigned int type)
> return -EINVAL;
> }
>
> - value = __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset);
> + value = __raw_readl(GPIO_BASE(chip) + CON_OFFSET + con_offset);
> value &= ~(0x7 << (offset * 0x4));
> value |= (type << (offset * 0x4));
> - __raw_writel(value, S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset);
> + __raw_writel(value, GPIO_BASE(chip) + CON_OFFSET + con_offset);
>
> return 0;
> }
>
> -struct irq_chip s5p_gpioint = {
> +static struct irq_chip s5p_gpioint = {
> .name = "s5p_gpioint",
> .irq_ack = s5p_gpioint_ack,
> .irq_mask = s5p_gpioint_mask,
> @@ -151,30 +139,28 @@ struct irq_chip s5p_gpioint = {
>
> static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc)
> {
> - int group, offset, pend_offset, mask_offset;
> - int real_irq;
> + int group, pend_offset, mask_offset;
> unsigned int pend, mask;
>
> for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) {
> - pend_offset = group << 2;
> - pend = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) +
> - pend_offset);
> + struct s3c_gpio_chip *chip = irq_chips[group];
> + if (!chip)
> + continue;
> +
> + pend_offset = REG_OFFSET(group);
> + pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET +
> pend_offset);
> if (!pend)
> continue;
>
> - mask_offset = group << 2;
> - mask = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) +
> - mask_offset);
> + mask_offset = REG_OFFSET(group);
> + mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET +
> mask_offset);
> pend &= ~mask;
>
> - for (offset = 0; offset < 8; offset++) {
> - if (pend & (1 << offset)) {
> - struct s3c_gpio_chip *chip =
irq_chips[group];
> - if (chip) {
> - real_irq = chip->irq_base + offset;
> - generic_handle_irq(real_irq);
> - }
> - }
> + while (pend) {
> + int offset = fls(pend) - 1;
__ffs?
And hmm...do we really need while loop here?
> + int real_irq = chip->irq_base + offset;
> + generic_handle_irq(real_irq);
> + pend &= ~BIT(offset);
> }
> }
> }
> @@ -202,7 +188,7 @@ static __init int s5p_gpioint_add(struct s3c_gpio_chip
> *chip)
> for (i = 0; i < chip->chip.ngpio; i++) {
> irq = chip->irq_base + i;
> set_irq_chip(irq, &s5p_gpioint);
> - set_irq_data(irq, &chip->chip);
> + set_irq_data(irq, chip);
?
> set_irq_handler(irq, handle_level_irq);
> set_irq_flags(irq, IRQF_VALID);
> }
> --
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
^ permalink raw reply
* MMC quirks relating to performance/lifetime.
From: Andrei Warkentin @ 2011-02-23 10:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102221800.17196.arnd@arndb.de>
On Tue, Feb 22, 2011 at 11:00 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>>
>> Do you think there is any need for runtime tuning of the MMC
>> workarounds (disregarding ones that really belong in the I/O
>> scheduler)? Should the workarounds be simply platform callbacks, or
>> should they be something heftier ("policies")?
>
> The platform hook seems the wrong place, because you might use
> the same chip in multiple platforms, and a single platform might
> have a large number of different boards, all of which require
> separate workarounds.
>
That's a good point. At best it would result in massive copy-paste/
> A per-card quirk table does not seem so bad, we have that in
> other subsystems as well. I wouldn't necessarily make it
> a list of possible quirks, but rather a __devinit function that
> is called for a new card on insertion, in order to tweak various
> parameters.
>
That sounds good! In fact, for any quirks enabled for a particular
card, I'll expose the tuneables through sysfs attributes, something
like /sys/block/mmcblk0/device/quirks/quirk-name/attr-names.
Quirks will have block intervals and access size intervals over which
they are valid, along with any other quirk-specific parameter.
Interval overlap will not be allowed for quirks in the same operation
type (r/w/e). The goal here is to make the changes to issue_*_rq as
small as possible, and not to pollute block.c at all with the quirks
stuff. Quirks are looked up inside issue_*_rq based on req type and
[start,end) interval. The resulting found quirks structure will
contain a callback used inside issue_*_rq to modify mmc block request
structures prior to generating actual MMC commands.
Quirks consist of a callback called inside of mmc issue_*_rq,
configurable attributes, and the sysfs interface. Quirk groups are
defined per-card. At card insertion time, a matching quirk group is
found, and is enabled. The quirk group enable function then enables
the relevant quirks with the right parameters (adds them to per
mmc_blk_data quirk interval tree). Some sane defaults for the tunables
are used. If the tunables are modified through sysfs, care is taken
that an interval overlap never happens, otherwise the tunable is not
modified and a kernel error message is logged.
I hope I explained the tentative idea clearly... Thoughts?
A
^ permalink raw reply
* [PATCH v3] dmaengine: mxs-dma: add dma support for i.MX23/28
From: Russell King - ARM Linux @ 2011-02-23 10:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110223100547.GG29559@n2100.arm.linux.org.uk>
On Wed, Feb 23, 2011 at 10:05:47AM +0000, Russell King - ARM Linux wrote:
> On Wed, Feb 23, 2011 at 11:25:16AM +0530, Koul, Vinod wrote:
> > Well in that case you are calling the callback from irq handler which is
> > not so good. The tasklet is other extreme (slow).
>
> tasklets are run on the exit path of interrupts.
>
> > Given that this is
> > also intended for audio I am not sure if calling period elapsed from
> > tasklet callback will not cause any audio over/underruns, last I tried
> > it used to break quite often in stressed situations.
>
> Nevertheless, the DMA engine API specifies that callbacks shall be called
> from tasklet context.
Note also that the DMA engine API is supposed to work as follows:
1. You submit DMA jobs to it.
2. The DMA engine starts the first job
3. When the first job is complete, it retires it to a retired list and
marks the tasklet to run
4. If there is another job waiting, the DMA engine starts the next job
5. Repeat 3 until no further jobs are queued
The tasklet then processes the list of retired jobs, cleaning them
up and calling their callbacks.
So the way to efficiently handle audio is to initially submit several
'period' sized buffers for the DMA engine. As each buffer completes,
you call snd_pcm_elapsed() to tell ALSA that a period has finished
and submit the next buffer. As there's always several buffers submitted,
the DMA engine never waits for the callback to complete. And ALSA is
capable of handling latency in snd_pcm_elapsed().
^ permalink raw reply
* [PATCH 1/2] ARM: S5PC110: disable unused power regulators on Goni board
From: Kukjin Kim @ 2011-02-23 10:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297760784-8486-2-git-send-email-m.szyprowski@samsung.com>
Marek Szyprowski wrote:
>
> A lot of power regulator has been enabled by default causing the board
> to consume a lot of power. This patch fixes this issue.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> arch/arm/mach-s5pv210/mach-goni.c | 10 +---------
> 1 files changed, 1 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm/mach-s5pv210/mach-goni.c
b/arch/arm/mach-s5pv210/mach-
> goni.c
> index 43c1c1b..10f754b 100644
> --- a/arch/arm/mach-s5pv210/mach-goni.c
> +++ b/arch/arm/mach-s5pv210/mach-goni.c
> @@ -292,7 +292,6 @@ static struct regulator_init_data goni_ldo3_data = {
> .min_uV = 1100000,
> .max_uV = 1100000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -311,6 +310,7 @@ static struct regulator_init_data goni_ldo5_data = {
> .min_uV = 2800000,
> .max_uV = 2800000,
> .apply_uV = 1,
> + .valid_ops_mask = REGULATOR_CHANGE_STATUS,
> },
> .num_consumer_supplies = ARRAY_SIZE(goni_ldo5_consumers),
> .consumer_supplies = goni_ldo5_consumers,
> @@ -341,7 +341,6 @@ static struct regulator_init_data goni_ldo8_data = {
> .min_uV = 3300000,
> .max_uV = 3300000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -351,7 +350,6 @@ static struct regulator_init_data goni_ldo9_data = {
> .min_uV = 2800000,
> .max_uV = 2800000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -371,7 +369,6 @@ static struct regulator_init_data goni_ldo11_data = {
> .min_uV = 2800000,
> .max_uV = 2800000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -381,7 +378,6 @@ static struct regulator_init_data goni_ldo12_data = {
> .min_uV = 1200000,
> .max_uV = 1200000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -391,7 +387,6 @@ static struct regulator_init_data goni_ldo13_data = {
> .min_uV = 2800000,
> .max_uV = 2800000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -401,7 +396,6 @@ static struct regulator_init_data goni_ldo14_data = {
> .min_uV = 1800000,
> .max_uV = 1800000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -411,7 +405,6 @@ static struct regulator_init_data goni_ldo15_data = {
> .min_uV = 3300000,
> .max_uV = 3300000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> @@ -421,7 +414,6 @@ static struct regulator_init_data goni_ldo16_data = {
> .min_uV = 1800000,
> .max_uV = 1800000,
> .apply_uV = 1,
> - .always_on = 1,
> },
> };
>
> --
Ok...will apply this.
And how about "mach-aquila.c"?
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
^ permalink raw reply
* [PATCH 2/2] ARM: S5PC110: add power consumers for hsotg for Goni board
From: Kukjin Kim @ 2011-02-23 10:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297760784-8486-3-git-send-email-m.szyprowski@samsung.com>
Marek Szyprowski wrote:
>
> This patch adds definitions for power consumers for s3c-hsotg driver
> on Goni board. The consumers are defined as arrays, because later more
> entries for MIPI and ADC will be defined.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> arch/arm/mach-s5pv210/mach-goni.c | 14 ++++++++++++++
> 1 files changed, 14 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-s5pv210/mach-goni.c
b/arch/arm/mach-s5pv210/mach-
> goni.c
> index 10f754b..796625c 100644
> --- a/arch/arm/mach-s5pv210/mach-goni.c
> +++ b/arch/arm/mach-s5pv210/mach-goni.c
> @@ -269,10 +269,18 @@ static void __init goni_tsp_init(void)
> /* MAX8998 regulators */
> #if defined(CONFIG_REGULATOR_MAX8998) ||
> defined(CONFIG_REGULATOR_MAX8998_MODULE)
>
> +static struct regulator_consumer_supply goni_ldo3_consumers[] = {
> + REGULATOR_SUPPLY("vusb_a", "s3c-hsotg"),
> +};
> +
> static struct regulator_consumer_supply goni_ldo5_consumers[] = {
> REGULATOR_SUPPLY("vmmc", "s3c-sdhci.0"),
> };
>
> +static struct regulator_consumer_supply goni_ldo8_consumers[] = {
> + REGULATOR_SUPPLY("vusb_d", "s3c-hsotg"),
> +};
> +
> static struct regulator_init_data goni_ldo2_data = {
> .constraints = {
> .name = "VALIVE_1.1V",
> @@ -292,7 +300,10 @@ static struct regulator_init_data goni_ldo3_data = {
> .min_uV = 1100000,
> .max_uV = 1100000,
> .apply_uV = 1,
> + .valid_ops_mask = REGULATOR_CHANGE_STATUS,
> },
> + .num_consumer_supplies = ARRAY_SIZE(goni_ldo3_consumers),
> + .consumer_supplies = goni_ldo3_consumers,
> };
>
> static struct regulator_init_data goni_ldo4_data = {
> @@ -341,7 +352,10 @@ static struct regulator_init_data goni_ldo8_data = {
> .min_uV = 3300000,
> .max_uV = 3300000,
> .apply_uV = 1,
> + .valid_ops_mask = REGULATOR_CHANGE_STATUS,
> },
> + .num_consumer_supplies = ARRAY_SIZE(goni_ldo8_consumers),
> + .consumer_supplies = goni_ldo8_consumers,
> };
>
> static struct regulator_init_data goni_ldo9_data = {
> --
Ok...will apply :)
Thanks.
Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.
^ permalink raw reply
* [PATCH V11 0/4] ptp: IEEE 1588 hardware clock support
From: Richard Cochran @ 2011-02-23 10:37 UTC (permalink / raw)
To: linux-arm-kernel
This just might be the last round of review of the PTP hardware clock
patch series. These patches apply on top of the timers/core branch in
the tip tree. Patches 1 and 4 have changed since the last version.
* Why all the CCs?
- One driver is for PowerPC, and adds device tree stuff.
- One driver is for the ARM Xscale IXP465.
* Patch ChangeLog
** v11
- added more padding to the structures in the user space ABI
- tweaked Kconfig to make dependencies more clear
- fixed locking on time stamp event queue
- added John Stultz's acks on patches 2 and 3.
- now the phyter driver handles multiple PHYs
Richard Cochran (4):
ptp: Added a brand new class driver for ptp clocks.
ptp: Added a clock that uses the eTSEC found on the MPC85xx.
ptp: Added a clock driver for the IXP46x.
ptp: Added a clock driver for the National Semiconductor PHYTER.
Documentation/ABI/testing/sysfs-ptp | 98 ++
.../devicetree/bindings/net/fsl-tsec-phy.txt | 57 ++
Documentation/ptp/ptp.txt | 89 ++
Documentation/ptp/testptp.c | 352 +++++++
Documentation/ptp/testptp.mk | 33 +
arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h | 78 ++
arch/powerpc/boot/dts/mpc8313erdb.dts | 14 +
arch/powerpc/boot/dts/mpc8572ds.dts | 14 +
arch/powerpc/boot/dts/p2020ds.dts | 14 +
arch/powerpc/boot/dts/p2020rdb.dts | 14 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/net/Makefile | 1 +
drivers/net/arm/ixp4xx_eth.c | 192 ++++-
drivers/net/gianfar_ptp.c | 448 +++++++++
drivers/net/gianfar_ptp_reg.h | 113 +++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/dp83640.c | 1012 ++++++++++++++++++++
drivers/net/phy/dp83640_reg.h | 267 +++++
drivers/ptp/Kconfig | 75 ++
drivers/ptp/Makefile | 7 +
drivers/ptp/ptp_chardev.c | 156 +++
drivers/ptp/ptp_clock.c | 320 ++++++
drivers/ptp/ptp_ixp46x.c | 332 +++++++
drivers/ptp/ptp_private.h | 86 ++
drivers/ptp/ptp_sysfs.c | 230 +++++
include/linux/Kbuild | 1 +
include/linux/ptp_clock.h | 84 ++
include/linux/ptp_clock_kernel.h | 139 +++
29 files changed, 4227 insertions(+), 3 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-ptp
create mode 100644 Documentation/ptp/ptp.txt
create mode 100644 Documentation/ptp/testptp.c
create mode 100644 Documentation/ptp/testptp.mk
create mode 100644 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
create mode 100644 drivers/net/gianfar_ptp.c
create mode 100644 drivers/net/gianfar_ptp_reg.h
create mode 100644 drivers/net/phy/dp83640.c
create mode 100644 drivers/net/phy/dp83640_reg.h
create mode 100644 drivers/ptp/Kconfig
create mode 100644 drivers/ptp/Makefile
create mode 100644 drivers/ptp/ptp_chardev.c
create mode 100644 drivers/ptp/ptp_clock.c
create mode 100644 drivers/ptp/ptp_ixp46x.c
create mode 100644 drivers/ptp/ptp_private.h
create mode 100644 drivers/ptp/ptp_sysfs.c
create mode 100644 include/linux/ptp_clock.h
create mode 100644 include/linux/ptp_clock_kernel.h
^ permalink raw reply
* [PATCH V11 1/4] ptp: Added a brand new class driver for ptp clocks.
From: Richard Cochran @ 2011-02-23 10:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1298447722.git.richard.cochran@omicron.at>
This patch adds an infrastructure for hardware clocks that implement
IEEE 1588, the Precision Time Protocol (PTP). A class driver offers a
registration method to particular hardware clock drivers. Each clock is
presented as a standard POSIX clock.
The ancillary clock features are exposed in two different ways, via
the sysfs and by a character device.
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
Documentation/ABI/testing/sysfs-ptp | 98 ++++++++++
Documentation/ptp/ptp.txt | 89 +++++++++
Documentation/ptp/testptp.c | 352 +++++++++++++++++++++++++++++++++++
Documentation/ptp/testptp.mk | 33 ++++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ptp/Kconfig | 30 +++
drivers/ptp/Makefile | 6 +
drivers/ptp/ptp_chardev.c | 156 ++++++++++++++++
drivers/ptp/ptp_clock.c | 320 +++++++++++++++++++++++++++++++
drivers/ptp/ptp_private.h | 86 +++++++++
drivers/ptp/ptp_sysfs.c | 230 +++++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/ptp_clock.h | 84 +++++++++
include/linux/ptp_clock_kernel.h | 139 ++++++++++++++
15 files changed, 1627 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-ptp
create mode 100644 Documentation/ptp/ptp.txt
create mode 100644 Documentation/ptp/testptp.c
create mode 100644 Documentation/ptp/testptp.mk
create mode 100644 drivers/ptp/Kconfig
create mode 100644 drivers/ptp/Makefile
create mode 100644 drivers/ptp/ptp_chardev.c
create mode 100644 drivers/ptp/ptp_clock.c
create mode 100644 drivers/ptp/ptp_private.h
create mode 100644 drivers/ptp/ptp_sysfs.c
create mode 100644 include/linux/ptp_clock.h
create mode 100644 include/linux/ptp_clock_kernel.h
diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
new file mode 100644
index 0000000..d40d2b5
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -0,0 +1,98 @@
+What: /sys/class/ptp/
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This directory contains files and directories
+ providing a standardized interface to the ancillary
+ features of PTP hardware clocks.
+
+What: /sys/class/ptp/ptpN/
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This directory contains the attributes of the Nth PTP
+ hardware clock registered into the PTP class driver
+ subsystem.
+
+What: /sys/class/ptp/ptpN/clock_name
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file contains the name of the PTP hardware clock
+ as a human readable string.
+
+What: /sys/class/ptp/ptpN/max_adjustment
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file contains the PTP hardware clock's maximum
+ frequency adjustment value (a positive integer) in
+ parts per billion.
+
+What: /sys/class/ptp/ptpN/n_alarms
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file contains the number of periodic or one shot
+ alarms offer by the PTP hardware clock.
+
+What: /sys/class/ptp/ptpN/n_external_timestamps
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file contains the number of external timestamp
+ channels offered by the PTP hardware clock.
+
+What: /sys/class/ptp/ptpN/n_periodic_outputs
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file contains the number of programmable periodic
+ output channels offered by the PTP hardware clock.
+
+What: /sys/class/ptp/ptpN/pps_avaiable
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file indicates whether the PTP hardware clock
+ supports a Pulse Per Second to the host CPU. Reading
+ "1" means that the PPS is supported, while "0" means
+ not supported.
+
+What: /sys/class/ptp/ptpN/extts_enable
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This write-only file enables or disables external
+ timestamps. To enable external timestamps, write the
+ channel index followed by a "1" into the file.
+ To disable external timestamps, write the channel
+ index followed by a "0" into the file.
+
+What: /sys/class/ptp/ptpN/fifo
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This file provides timestamps on external events, in
+ the form of three integers: channel index, seconds,
+ and nanoseconds.
+
+What: /sys/class/ptp/ptpN/period
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This write-only file enables or disables periodic
+ outputs. To enable a periodic output, write five
+ integers into the file: channel index, start time
+ seconds, start time nanoseconds, period seconds, and
+ period nanoseconds. To disable a periodic output, set
+ all the seconds and nanoseconds values to zero.
+
+What: /sys/class/ptp/ptpN/pps_enable
+Date: September 2010
+Contact: Richard Cochran <richardcochran@gmail.com>
+Description:
+ This write-only file enables or disables delivery of
+ PPS events to the Linux PPS subsystem. To enable PPS
+ events, write a "1" into the file. To disable events,
+ write a "0" into the file.
diff --git a/Documentation/ptp/ptp.txt b/Documentation/ptp/ptp.txt
new file mode 100644
index 0000000..ae8fef8
--- /dev/null
+++ b/Documentation/ptp/ptp.txt
@@ -0,0 +1,89 @@
+
+* PTP hardware clock infrastructure for Linux
+
+ This patch set introduces support for IEEE 1588 PTP clocks in
+ Linux. Together with the SO_TIMESTAMPING socket options, this
+ presents a standardized method for developing PTP user space
+ programs, synchronizing Linux with external clocks, and using the
+ ancillary features of PTP hardware clocks.
+
+ A new class driver exports a kernel interface for specific clock
+ drivers and a user space interface. The infrastructure supports a
+ complete set of PTP hardware clock functionality.
+
+ + Basic clock operations
+ - Set time
+ - Get time
+ - Shift the clock by a given offset atomically
+ - Adjust clock frequency
+
+ + Ancillary clock features
+ - One short or periodic alarms, with signal delivery to user program
+ - Time stamp external events
+ - Period output signals configurable from user space
+ - Synchronization of the Linux system time via the PPS subsystem
+
+** PTP hardware clock kernel API
+
+ A PTP clock driver registers itself with the class driver. The
+ class driver handles all of the dealings with user space. The
+ author of a clock driver need only implement the details of
+ programming the clock hardware. The clock driver notifies the class
+ driver of asynchronous events (alarms and external time stamps) via
+ a simple message passing interface.
+
+ The class driver supports multiple PTP clock drivers. In normal use
+ cases, only one PTP clock is needed. However, for testing and
+ development, it can be useful to have more than one clock in a
+ single system, in order to allow performance comparisons.
+
+** PTP hardware clock user space API
+
+ The class driver also creates a character device for each
+ registered clock. User space can use an open file descriptor from
+ the character device as a POSIX clock id and may call
+ clock_gettime, clock_settime, and clock_adjtime. These calls
+ implement the basic clock operations.
+
+ User space programs may control the clock using standardized
+ ioctls. A program may query, enable, configure, and disable the
+ ancillary clock features. User space can receive time stamped
+ events via blocking read() and poll(). One shot and periodic
+ signals may be configured via the POSIX timer_settime() system
+ call.
+
+** Writing clock drivers
+
+ Clock drivers include include/linux/ptp_clock_kernel.h and register
+ themselves by presenting a 'struct ptp_clock_info' to the
+ registration method. Clock drivers must implement all of the
+ functions in the interface. If a clock does not offer a particular
+ ancillary feature, then the driver should just return -EOPNOTSUPP
+ from those functions.
+
+ Drivers must ensure that all of the methods in interface are
+ reentrant. Since most hardware implementations treat the time value
+ as a 64 bit integer accessed as two 32 bit registers, drivers
+ should use spin_lock_irqsave/spin_unlock_irqrestore to protect
+ against concurrent access. This locking cannot be accomplished in
+ class driver, since the lock may also be needed by the clock
+ driver's interrupt service routine.
+
+** Supported hardware
+
+ + Freescale eTSEC gianfar
+ - 2 Time stamp external triggers, programmable polarity (opt. interrupt)
+ - 2 Alarm registers (optional interrupt)
+ - 3 Periodic signals (optional interrupt)
+
+ + National DP83640
+ - 6 GPIOs programmable as inputs or outputs
+ - 6 GPIOs with dedicated functions (LED/JTAG/clock) can also be
+ used as general inputs or outputs
+ - GPIO inputs can time stamp external triggers
+ - GPIO outputs can produce periodic signals
+ - 1 interrupt pin
+
+ + Intel IXP465
+ - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
+ - Target Time (optional interrupt)
diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
new file mode 100644
index 0000000..a1d2bf1
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,352 @@
+/*
+ * PTP 1588 clock support - User space test program
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/ptp_clock.h>
+
+#define DEVICE "/dev/ptp0"
+
+#ifndef ADJ_SETOFFSET
+#define ADJ_SETOFFSET 0x0100
+#endif
+
+#ifndef CLOCK_INVALID
+#define CLOCK_INVALID -1
+#endif
+
+/* When glibc offers the syscall, this will go away. */
+#include <sys/syscall.h>
+static int clock_adjtime(clockid_t id, struct timex *tx)
+{
+ return syscall(__NR_clock_adjtime, id, tx);
+}
+
+static clockid_t get_clockid(int fd)
+{
+#define CLOCKFD 3
+#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
+
+ return FD_TO_CLOCKID(fd);
+}
+
+static void handle_alarm(int s)
+{
+ printf("received signal %d\n", s);
+}
+
+static int install_handler(int signum, void (*handler)(int))
+{
+ struct sigaction action;
+ sigset_t mask;
+
+ /* Unblock the signal. */
+ sigemptyset(&mask);
+ sigaddset(&mask, signum);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+ /* Install the signal handler. */
+ action.sa_handler = handler;
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+ sigaction(signum, &action, NULL);
+
+ return 0;
+}
+
+static void usage(char *progname)
+{
+ fprintf(stderr,
+ "usage: %s [options]\n"
+ " -a val request a one-shot alarm after 'val' seconds\n"
+ " -A val request a periodic alarm every 'val' seconds\n"
+ " -c query the ptp clock's capabilities\n"
+ " -d name device to open\n"
+ " -e val read 'val' external time stamp events\n"
+ " -f val adjust the ptp clock frequency by 'val' ppb\n"
+ " -g get the ptp clock time\n"
+ " -h prints this message\n"
+ " -p val enable output with a period of 'val' nanoseconds\n"
+ " -P val enable or disable (val=1|0) the system clock PPS\n"
+ " -s set the ptp clock time from the system time\n"
+ " -t val shift the ptp clock time by 'val' seconds\n",
+ progname);
+}
+
+int main(int argc, char *argv[])
+{
+ struct ptp_clock_caps caps;
+ struct ptp_extts_event event;
+ struct ptp_extts_request extts_request;
+ struct ptp_perout_request perout_request;
+ struct timespec ts;
+ struct timex tx;
+
+ static timer_t timerid;
+ struct itimerspec timeout;
+ struct sigevent sigevent;
+
+ char *progname;
+ int c, cnt, fd;
+
+ char *device = DEVICE;
+ clockid_t clkid;
+ int adjfreq = 0x7fffffff;
+ int adjtime = 0;
+ int capabilities = 0;
+ int extts = 0;
+ int gettime = 0;
+ int oneshot = 0;
+ int periodic = 0;
+ int perout = -1;
+ int pps = -1;
+ int settime = 0;
+
+ progname = strrchr(argv[0], '/');
+ progname = progname ? 1+progname : argv[0];
+ while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghp:P:st:v"))) {
+ switch (c) {
+ case 'a':
+ oneshot = atoi(optarg);
+ break;
+ case 'A':
+ periodic = atoi(optarg);
+ break;
+ case 'c':
+ capabilities = 1;
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case 'e':
+ extts = atoi(optarg);
+ break;
+ case 'f':
+ adjfreq = atoi(optarg);
+ break;
+ case 'g':
+ gettime = 1;
+ break;
+ case 'p':
+ perout = atoi(optarg);
+ break;
+ case 'P':
+ pps = atoi(optarg);
+ break;
+ case 's':
+ settime = 1;
+ break;
+ case 't':
+ adjtime = atoi(optarg);
+ break;
+ case 'h':
+ usage(progname);
+ return 0;
+ case '?':
+ default:
+ usage(progname);
+ return -1;
+ }
+ }
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
+ return -1;
+ }
+
+ clkid = get_clockid(fd);
+ if (CLOCK_INVALID == clkid) {
+ fprintf(stderr, "failed to read clock id\n");
+ return -1;
+ }
+
+ if (capabilities) {
+ if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+ perror("PTP_CLOCK_GETCAPS");
+ } else {
+ printf("capabilities:\n"
+ " %d maximum frequency adjustment (ppb)\n"
+ " %d programmable alarms\n"
+ " %d external time stamp channels\n"
+ " %d programmable periodic signals\n"
+ " %d pulse per second\n",
+ caps.max_adj,
+ caps.n_alarm,
+ caps.n_ext_ts,
+ caps.n_per_out,
+ caps.pps);
+ }
+ }
+
+ if (0x7fffffff != adjfreq) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_FREQUENCY;
+ tx.freq = (long) (adjfreq * 65.536);
+ if (clock_adjtime(clkid, &tx)) {
+ perror("clock_adjtime");
+ } else {
+ puts("frequency adjustment okay");
+ }
+ }
+
+ if (adjtime) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_SETOFFSET;
+ tx.time.tv_sec = adjtime;
+ tx.time.tv_usec = 0;
+ if (clock_adjtime(clkid, &tx) < 0) {
+ perror("clock_adjtime");
+ } else {
+ puts("time shift okay");
+ }
+ }
+
+ if (gettime) {
+ if (clock_gettime(clkid, &ts)) {
+ perror("clock_gettime");
+ } else {
+ printf("clock time: %ld.%09ld or %s",
+ ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
+ }
+ }
+
+ if (settime) {
+ clock_gettime(CLOCK_REALTIME, &ts);
+ if (clock_settime(clkid, &ts)) {
+ perror("clock_settime");
+ } else {
+ puts("set time okay");
+ }
+ }
+
+ if (extts) {
+ memset(&extts_request, 0, sizeof(extts_request));
+ extts_request.index = 0;
+ extts_request.flags = PTP_ENABLE_FEATURE;
+ if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
+ perror("PTP_EXTTS_REQUEST");
+ extts = 0;
+ } else {
+ puts("external time stamp request okay");
+ }
+ for (; extts; extts--) {
+ cnt = read(fd, &event, sizeof(event));
+ if (cnt != sizeof(event)) {
+ perror("read");
+ break;
+ }
+ printf("event index %u@%lld.%09u\n", event.index,
+ event.t.sec, event.t.nsec);
+ fflush(stdout);
+ }
+ /* Disable the feature again. */
+ extts_request.flags = 0;
+ if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
+ perror("PTP_EXTTS_REQUEST");
+ }
+ }
+
+ if (oneshot) {
+ install_handler(SIGALRM, handle_alarm);
+ /* Create a timer. */
+ sigevent.sigev_notify = SIGEV_SIGNAL;
+ sigevent.sigev_signo = SIGALRM;
+ if (timer_create(clkid, &sigevent, &timerid)) {
+ perror("timer_create");
+ return -1;
+ }
+ /* Start the timer. */
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.it_value.tv_sec = oneshot;
+ if (timer_settime(timerid, 0, &timeout, NULL)) {
+ perror("timer_settime");
+ return -1;
+ }
+ pause();
+ timer_delete(timerid);
+ }
+
+ if (periodic) {
+ install_handler(SIGALRM, handle_alarm);
+ /* Create a timer. */
+ sigevent.sigev_notify = SIGEV_SIGNAL;
+ sigevent.sigev_signo = SIGALRM;
+ if (timer_create(clkid, &sigevent, &timerid)) {
+ perror("timer_create");
+ return -1;
+ }
+ /* Start the timer. */
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.it_interval.tv_sec = periodic;
+ timeout.it_value.tv_sec = periodic;
+ if (timer_settime(timerid, 0, &timeout, NULL)) {
+ perror("timer_settime");
+ return -1;
+ }
+ while (1) {
+ pause();
+ }
+ timer_delete(timerid);
+ }
+
+ if (perout >= 0) {
+ if (clock_gettime(clkid, &ts)) {
+ perror("clock_gettime");
+ return -1;
+ }
+ memset(&perout_request, 0, sizeof(perout_request));
+ perout_request.index = 0;
+ perout_request.start.sec = ts.tv_sec + 2;
+ perout_request.start.nsec = 0;
+ perout_request.period.sec = 0;
+ perout_request.period.nsec = perout;
+ if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
+ perror("PTP_PEROUT_REQUEST");
+ } else {
+ puts("periodic output request okay");
+ }
+ }
+
+ if (pps != -1) {
+ int enable = pps ? 1 : 0;
+ if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
+ perror("PTP_ENABLE_PPS");
+ } else {
+ puts("pps for system time request okay");
+ }
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/Documentation/ptp/testptp.mk b/Documentation/ptp/testptp.mk
new file mode 100644
index 0000000..4ef2d97
--- /dev/null
+++ b/Documentation/ptp/testptp.mk
@@ -0,0 +1,33 @@
+# PTP 1588 clock support - User space test program
+#
+# Copyright (C) 2010 OMICRON electronics GmbH
+#
+# 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, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+CC = $(CROSS_COMPILE)gcc
+INC = -I$(KBUILD_OUTPUT)/usr/include
+CFLAGS = -Wall $(INC)
+LDLIBS = -lrt
+PROGS = testptp
+
+all: $(PROGS)
+
+testptp: testptp.o
+
+clean:
+ rm -f testptp.o
+
+distclean: clean
+ rm -f $(PROGS)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 9bfb71f..82ddceb 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,6 +54,8 @@ source "drivers/spi/Kconfig"
source "drivers/pps/Kconfig"
+source "drivers/ptp/Kconfig"
+
source "drivers/gpio/Kconfig"
source "drivers/w1/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index b423bb1..f9b3087 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_RTC_LIB) += rtc/
obj-y += i2c/ media/
obj-$(CONFIG_PPS) += pps/
+obj-$(CONFIG_PTP_1588_CLOCK) += ptp/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_POWER_SUPPLY) += power/
obj-$(CONFIG_HWMON) += hwmon/
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
new file mode 100644
index 0000000..70d4bb1
--- /dev/null
+++ b/drivers/ptp/Kconfig
@@ -0,0 +1,30 @@
+#
+# PTP clock support configuration
+#
+
+menu "PTP clock support"
+
+comment "Enable Device Drivers -> PPS to see the PTP clock options."
+ depends on PPS=n
+
+config PTP_1588_CLOCK
+ tristate "PTP clock support"
+ depends on EXPERIMENTAL
+ depends on PPS
+ help
+ The IEEE 1588 standard defines a method to precisely
+ synchronize distributed clocks over Ethernet networks. The
+ standard defines a Precision Time Protocol (PTP), which can
+ be used to achieve synchronization within a few dozen
+ microseconds. In addition, with the help of special hardware
+ time stamping units, it can be possible to achieve
+ synchronization to within a few hundred nanoseconds.
+
+ This driver adds support for PTP clocks as character
+ devices. If you want to use a PTP clock, then you should
+ also enable at least one clock driver as well.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp.
+
+endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
new file mode 100644
index 0000000..480e2af
--- /dev/null
+++ b/drivers/ptp/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for PTP 1588 clock support.
+#
+
+ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o
+obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
new file mode 100644
index 0000000..c806192
--- /dev/null
+++ b/drivers/ptp/ptp_chardev.c
@@ -0,0 +1,156 @@
+/*
+ * PTP 1588 clock support - character device implementation.
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/module.h>
+#include <linux/posix-clock.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+
+#include "ptp_private.h"
+
+int ptp_open(struct posix_clock *pc, fmode_t fmode)
+{
+ return 0;
+}
+
+long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
+{
+ struct ptp_clock_caps caps;
+ struct ptp_clock_request req;
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct ptp_clock_info *ops = ptp->info;
+ int enable, err = 0;
+
+ switch (cmd) {
+
+ case PTP_CLOCK_GETCAPS:
+ memset(&caps, 0, sizeof(caps));
+ caps.max_adj = ptp->info->max_adj;
+ caps.n_alarm = ptp->info->n_alarm;
+ caps.n_ext_ts = ptp->info->n_ext_ts;
+ caps.n_per_out = ptp->info->n_per_out;
+ caps.pps = ptp->info->pps;
+ err = copy_to_user((void __user *)arg, &caps, sizeof(caps));
+ break;
+
+ case PTP_EXTTS_REQUEST:
+ if (copy_from_user(&req.extts, (void __user *)arg,
+ sizeof(req.extts))) {
+ err = -EFAULT;
+ break;
+ }
+ if (req.extts.index >= ops->n_ext_ts) {
+ err = -EINVAL;
+ break;
+ }
+ req.type = PTP_CLK_REQ_EXTTS;
+ enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
+ err = ops->enable(ops, &req, enable);
+ break;
+
+ case PTP_PEROUT_REQUEST:
+ if (copy_from_user(&req.perout, (void __user *)arg,
+ sizeof(req.perout))) {
+ err = -EFAULT;
+ break;
+ }
+ if (req.perout.index >= ops->n_per_out) {
+ err = -EINVAL;
+ break;
+ }
+ req.type = PTP_CLK_REQ_PEROUT;
+ enable = req.perout.period.sec || req.perout.period.nsec;
+ err = ops->enable(ops, &req, enable);
+ break;
+
+ case PTP_ENABLE_PPS:
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+ req.type = PTP_CLK_REQ_PPS;
+ enable = arg ? 1 : 0;
+ err = ops->enable(ops, &req, enable);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+ return err;
+}
+
+uint ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
+{
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+
+ poll_wait(fp, &ptp->tsev_wq, wait);
+
+ return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
+}
+
+ssize_t ptp_read(struct posix_clock *pc,
+ uint rdflags, char __user *buf, size_t cnt)
+{
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct timestamp_event_queue *queue = &ptp->tsevq;
+ struct ptp_extts_event event[PTP_BUF_TIMESTAMPS];
+ unsigned long flags;
+ size_t qcnt, i;
+
+ if (cnt > sizeof(event))
+ cnt = sizeof(event);
+
+ cnt = cnt / sizeof(struct ptp_extts_event);
+
+ if (mutex_lock_interruptible(&ptp->tsevq_mux))
+ return -ERESTARTSYS;
+
+ if (wait_event_interruptible(ptp->tsev_wq, queue_cnt(queue))) {
+ mutex_unlock(&ptp->tsevq_mux);
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irqsave(&queue->lock, flags);
+
+ qcnt = queue_cnt(queue);
+
+ if (cnt > qcnt)
+ cnt = qcnt;
+
+ for (i = 0; i < cnt; i++) {
+ event[i] = queue->buf[queue->head];
+ queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
+ }
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ cnt = cnt * sizeof(struct ptp_extts_event);
+
+ if (copy_to_user(buf, event, cnt)) {
+ mutex_unlock(&ptp->tsevq_mux);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&ptp->tsevq_mux);
+ return cnt;
+}
+
+int ptp_release(struct posix_clock *pc)
+{
+ return 0;
+}
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 0000000..1786a74
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,320 @@
+/*
+ * PTP 1588 clock support
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/posix-clock.h>
+#include <linux/pps_kernel.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+
+#include "ptp_private.h"
+
+#define PTP_MAX_ALARMS 4
+#define PTP_MAX_CLOCKS 8
+#define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT)
+#define PTP_PPS_EVENT PPS_CAPTUREASSERT
+#define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
+
+/* private globals */
+
+static dev_t ptp_devt;
+static struct class *ptp_class;
+
+static DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS);
+static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */
+
+/* time stamp event queue operations */
+
+static inline int queue_free(struct timestamp_event_queue *q)
+{
+ return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
+}
+
+static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
+ struct ptp_clock_event *src)
+{
+ struct ptp_extts_event *dst;
+ unsigned long flags;
+ u32 remainder;
+
+ dst = &queue->buf[queue->tail];
+
+ dst->index = src->index;
+ dst->t.sec = div_u64_rem(src->timestamp, 1000000000, &remainder);
+ dst->t.nsec = remainder;
+
+ spin_lock_irqsave(&queue->lock, flags);
+
+ if (!queue_free(queue))
+ queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
+
+ queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+/* posix clock implementation */
+
+static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp)
+{
+ return 1; /* always round timer functions to one nanosecond */
+}
+
+static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp)
+{
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ return ptp->info->settime(ptp->info, tp);
+}
+
+static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp)
+{
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ return ptp->info->gettime(ptp->info, tp);
+}
+
+static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx)
+{
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+ struct ptp_clock_info *ops;
+ int err = -EOPNOTSUPP;
+
+ ops = ptp->info;
+
+ if (tx->modes & ADJ_SETOFFSET) {
+ struct timespec ts;
+ ktime_t kt;
+ s64 delta;
+
+ ts.tv_sec = tx->time.tv_sec;
+ ts.tv_nsec = tx->time.tv_usec;
+
+ if (!(tx->modes & ADJ_NANO))
+ ts.tv_nsec *= 1000;
+
+ if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ kt = timespec_to_ktime(ts);
+ delta = ktime_to_ns(kt);
+ err = ops->adjtime(ops, delta);
+
+ } else if (tx->modes & ADJ_FREQUENCY) {
+ s64 ppb = 1 + tx->freq;
+ ppb *= 125;
+ ppb >>= 13;
+ err = ops->adjfreq(ops, (s32)ppb);
+ }
+
+ return err;
+}
+
+static struct posix_clock_operations ptp_clock_ops = {
+ .owner = THIS_MODULE,
+ .clock_adjtime = ptp_clock_adjtime,
+ .clock_gettime = ptp_clock_gettime,
+ .clock_getres = ptp_clock_getres,
+ .clock_settime = ptp_clock_settime,
+ .ioctl = ptp_ioctl,
+ .open = ptp_open,
+ .poll = ptp_poll,
+ .read = ptp_read,
+ .release = ptp_release,
+};
+
+static void delete_ptp_clock(struct posix_clock *pc)
+{
+ struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+
+ /* Release the clock's resources. */
+ if (ptp->info->pps)
+ pps_unregister_source(ptp->pps_source);
+ ptp_cleanup_sysfs(ptp);
+ device_destroy(ptp_class, ptp->devid);
+ mutex_destroy(&ptp->tsevq_mux);
+
+ /* Remove the clock from the bit map. */
+ mutex_lock(&ptp_clocks_mutex);
+ clear_bit(ptp->index, ptp_clocks_map);
+ mutex_unlock(&ptp_clocks_mutex);
+
+ kfree(ptp);
+}
+
+/* public interface */
+
+struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
+{
+ struct ptp_clock *ptp;
+ int err = 0, index, major = MAJOR(ptp_devt);
+
+ if (info->n_alarm > PTP_MAX_ALARMS)
+ return ERR_PTR(-EINVAL);
+
+ /* Find a free clock slot and reserve it. */
+ err = -EBUSY;
+ mutex_lock(&ptp_clocks_mutex);
+ index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS);
+ if (index < PTP_MAX_CLOCKS)
+ set_bit(index, ptp_clocks_map);
+ else
+ goto no_slot;
+
+ /* Initialize a clock structure. */
+ err = -ENOMEM;
+ ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
+ if (ptp == NULL)
+ goto no_memory;
+
+ ptp->clock.ops = ptp_clock_ops;
+ ptp->clock.release = delete_ptp_clock;
+ ptp->info = info;
+ ptp->devid = MKDEV(major, index);
+ ptp->index = index;
+ spin_lock_init(&ptp->tsevq.lock);
+ mutex_init(&ptp->tsevq_mux);
+ init_waitqueue_head(&ptp->tsev_wq);
+
+ /* Create a new device in our class. */
+ ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
+ "ptp%d", ptp->index);
+ if (IS_ERR(ptp->dev))
+ goto no_device;
+
+ dev_set_drvdata(ptp->dev, ptp);
+
+ err = ptp_populate_sysfs(ptp);
+ if (err)
+ goto no_sysfs;
+
+ /* Register a new PPS source. */
+ if (info->pps) {
+ struct pps_source_info pps;
+ memset(&pps, 0, sizeof(pps));
+ snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index);
+ pps.mode = PTP_PPS_MODE;
+ pps.owner = info->owner;
+ ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
+ if (!ptp->pps_source) {
+ pr_err("failed to register pps source\n");
+ goto no_pps;
+ }
+ }
+
+ /* Create a posix clock. */
+ err = posix_clock_register(&ptp->clock, ptp->devid);
+ if (err) {
+ pr_err("failed to create posix clock\n");
+ goto no_clock;
+ }
+
+ mutex_unlock(&ptp_clocks_mutex);
+ return ptp;
+
+no_clock:
+ if (ptp->info->pps)
+ pps_unregister_source(ptp->pps_source);
+no_pps:
+ ptp_cleanup_sysfs(ptp);
+no_sysfs:
+ device_destroy(ptp_class, ptp->devid);
+no_device:
+ mutex_destroy(&ptp->tsevq_mux);
+ kfree(ptp);
+no_memory:
+ clear_bit(index, ptp_clocks_map);
+no_slot:
+ mutex_unlock(&ptp_clocks_mutex);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+ posix_clock_unregister(&ptp->clock);
+ return 0;
+}
+EXPORT_SYMBOL(ptp_clock_unregister);
+
+void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
+{
+ struct pps_event_time evt;
+
+ switch (event->type) {
+
+ case PTP_CLOCK_ALARM:
+ break;
+
+ case PTP_CLOCK_EXTTS:
+ enqueue_external_timestamp(&ptp->tsevq, event);
+ wake_up_interruptible(&ptp->tsev_wq);
+ break;
+
+ case PTP_CLOCK_PPS:
+ pps_get_ts(&evt);
+ pps_event(ptp->pps_source, &evt, PTP_PPS_EVENT, NULL);
+ break;
+ }
+}
+EXPORT_SYMBOL(ptp_clock_event);
+
+/* module operations */
+
+static void __exit ptp_exit(void)
+{
+ class_destroy(ptp_class);
+ unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
+}
+
+static int __init ptp_init(void)
+{
+ int err;
+
+ ptp_class = class_create(THIS_MODULE, "ptp");
+ if (IS_ERR(ptp_class)) {
+ pr_err("ptp: failed to allocate class\n");
+ return PTR_ERR(ptp_class);
+ }
+
+ err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
+ if (err < 0) {
+ pr_err("ptp: failed to allocate device region\n");
+ goto no_region;
+ }
+
+ ptp_class->dev_attrs = ptp_dev_attrs;
+ pr_info("PTP clock support registered\n");
+ return 0;
+
+no_region:
+ class_destroy(ptp_class);
+ return err;
+}
+
+subsys_initcall(ptp_init);
+module_exit(ptp_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clocks support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
new file mode 100644
index 0000000..2f76266
--- /dev/null
+++ b/drivers/ptp/ptp_private.h
@@ -0,0 +1,86 @@
+/*
+ * PTP 1588 clock support - private declarations for the core module.
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _PTP_PRIVATE_H_
+#define _PTP_PRIVATE_H_
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/posix-clock.h>
+#include <linux/ptp_clock.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/time.h>
+
+#define PTP_MAX_TIMESTAMPS 128
+#define PTP_BUF_TIMESTAMPS 30
+
+struct timestamp_event_queue {
+ struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
+ int head;
+ int tail;
+ spinlock_t lock;
+};
+
+struct ptp_clock {
+ struct posix_clock clock;
+ struct device *dev;
+ struct ptp_clock_info *info;
+ dev_t devid;
+ int index; /* index into clocks.map */
+ struct pps_device *pps_source;
+ struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
+ struct mutex tsevq_mux; /* one process at a time reading the fifo */
+ wait_queue_head_t tsev_wq;
+};
+
+static inline int queue_cnt(struct timestamp_event_queue *q)
+{
+ int cnt = q->tail - q->head;
+ return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
+}
+
+/*
+ * see ptp_chardev.c
+ */
+
+long ptp_ioctl(struct posix_clock *pc,
+ unsigned int cmd, unsigned long arg);
+
+int ptp_open(struct posix_clock *pc, fmode_t fmode);
+
+ssize_t ptp_read(struct posix_clock *pc,
+ uint flags, char __user *buf, size_t cnt);
+
+int ptp_release(struct posix_clock *pc);
+
+uint ptp_poll(struct posix_clock *pc,
+ struct file *fp, poll_table *wait);
+
+/*
+ * see ptp_sysfs.c
+ */
+
+extern struct device_attribute ptp_dev_attrs[];
+
+int ptp_cleanup_sysfs(struct ptp_clock *ptp);
+
+int ptp_populate_sysfs(struct ptp_clock *ptp);
+
+#endif
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
new file mode 100644
index 0000000..2f93926
--- /dev/null
+++ b/drivers/ptp/ptp_sysfs.c
@@ -0,0 +1,230 @@
+/*
+ * PTP 1588 clock support - sysfs interface.
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/capability.h>
+
+#include "ptp_private.h"
+
+static ssize_t clock_name_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name);
+}
+
+#define PTP_SHOW_INT(name) \
+static ssize_t name##_show(struct device *dev, \
+ struct device_attribute *attr, char *page) \
+{ \
+ struct ptp_clock *ptp = dev_get_drvdata(dev); \
+ return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->name); \
+}
+
+PTP_SHOW_INT(max_adj);
+PTP_SHOW_INT(n_alarm);
+PTP_SHOW_INT(n_ext_ts);
+PTP_SHOW_INT(n_per_out);
+PTP_SHOW_INT(pps);
+
+#define PTP_RO_ATTR(_var, _name) { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = _var##_show, \
+}
+
+struct device_attribute ptp_dev_attrs[] = {
+ PTP_RO_ATTR(clock_name, clock_name),
+ PTP_RO_ATTR(max_adj, max_adjustment),
+ PTP_RO_ATTR(n_alarm, n_alarms),
+ PTP_RO_ATTR(n_ext_ts, n_external_timestamps),
+ PTP_RO_ATTR(n_per_out, n_periodic_outputs),
+ PTP_RO_ATTR(pps, pps_available),
+ __ATTR_NULL,
+};
+
+static ssize_t extts_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ struct ptp_clock_info *ops = ptp->info;
+ struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS };
+ int cnt, enable;
+ int err = -EINVAL;
+
+ cnt = sscanf(buf, "%u %d", &req.extts.index, &enable);
+ if (cnt != 2)
+ goto out;
+ if (req.extts.index >= ops->n_ext_ts)
+ goto out;
+
+ err = ops->enable(ops, &req, enable ? 1 : 0);
+ if (err)
+ goto out;
+
+ return count;
+out:
+ return err;
+}
+
+static ssize_t extts_fifo_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ struct timestamp_event_queue *queue = &ptp->tsevq;
+ struct ptp_extts_event event;
+ unsigned long flags;
+ size_t qcnt;
+ int cnt = 0;
+
+ memset(&event, 0, sizeof(event));
+
+ if (mutex_lock_interruptible(&ptp->tsevq_mux))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ qcnt = queue_cnt(queue);
+ if (qcnt) {
+ event = queue->buf[queue->head];
+ queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ if (!qcnt)
+ goto out;
+
+ cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n",
+ event.index, event.t.sec, event.t.nsec);
+out:
+ mutex_unlock(&ptp->tsevq_mux);
+ return cnt;
+}
+
+static ssize_t period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ struct ptp_clock_info *ops = ptp->info;
+ struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT };
+ int cnt, enable, err = -EINVAL;
+
+ cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index,
+ &req.perout.start.sec, &req.perout.start.nsec,
+ &req.perout.period.sec, &req.perout.period.nsec);
+ if (cnt != 5)
+ goto out;
+ if (req.perout.index >= ops->n_per_out)
+ goto out;
+
+ enable = req.perout.period.sec || req.perout.period.nsec;
+ err = ops->enable(ops, &req, enable);
+ if (err)
+ goto out;
+
+ return count;
+out:
+ return err;
+}
+
+static ssize_t pps_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ struct ptp_clock_info *ops = ptp->info;
+ struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS };
+ int cnt, enable;
+ int err = -EINVAL;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+
+ cnt = sscanf(buf, "%d", &enable);
+ if (cnt != 1)
+ goto out;
+
+ err = ops->enable(ops, &req, enable ? 1 : 0);
+ if (err)
+ goto out;
+
+ return count;
+out:
+ return err;
+}
+
+static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
+static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL);
+static DEVICE_ATTR(period, 0220, NULL, period_store);
+static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store);
+
+int ptp_cleanup_sysfs(struct ptp_clock *ptp)
+{
+ struct device *dev = ptp->dev;
+ struct ptp_clock_info *info = ptp->info;
+
+ if (info->n_ext_ts) {
+ device_remove_file(dev, &dev_attr_extts_enable);
+ device_remove_file(dev, &dev_attr_fifo);
+ }
+ if (info->n_per_out)
+ device_remove_file(dev, &dev_attr_period);
+
+ if (info->pps)
+ device_remove_file(dev, &dev_attr_pps_enable);
+
+ return 0;
+}
+
+int ptp_populate_sysfs(struct ptp_clock *ptp)
+{
+ struct device *dev = ptp->dev;
+ struct ptp_clock_info *info = ptp->info;
+ int err;
+
+ if (info->n_ext_ts) {
+ err = device_create_file(dev, &dev_attr_extts_enable);
+ if (err)
+ goto out1;
+ err = device_create_file(dev, &dev_attr_fifo);
+ if (err)
+ goto out2;
+ }
+ if (info->n_per_out) {
+ err = device_create_file(dev, &dev_attr_period);
+ if (err)
+ goto out3;
+ }
+ if (info->pps) {
+ err = device_create_file(dev, &dev_attr_pps_enable);
+ if (err)
+ goto out4;
+ }
+ return 0;
+out4:
+ if (info->n_per_out)
+ device_remove_file(dev, &dev_attr_period);
+out3:
+ if (info->n_ext_ts)
+ device_remove_file(dev, &dev_attr_fifo);
+out2:
+ if (info->n_ext_ts)
+ device_remove_file(dev, &dev_attr_extts_enable);
+out1:
+ return err;
+}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index b0ada6f..985ab2a 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -300,6 +300,7 @@ header-y += ppp-comp.h
header-y += ppp_defs.h
header-y += pps.h
header-y += prctl.h
+header-y += ptp_clock.h
header-y += ptrace.h
header-y += qnx4_fs.h
header-y += qnxtypes.h
diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
new file mode 100644
index 0000000..94e981f
--- /dev/null
+++ b/include/linux/ptp_clock.h
@@ -0,0 +1,84 @@
+/*
+ * PTP 1588 clock support - user space interface
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PTP_CLOCK_H_
+#define _PTP_CLOCK_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* PTP_xxx bits, for the flags field within the request structures. */
+#define PTP_ENABLE_FEATURE (1<<0)
+#define PTP_RISING_EDGE (1<<1)
+#define PTP_FALLING_EDGE (1<<2)
+
+/*
+ * struct ptp_clock_time - represents a time value
+ *
+ * The sign of the seconds field applies to the whole value. The
+ * nanoseconds field is always unsigned. The reserved field is
+ * included for sub-nanosecond resolution, should the demand for
+ * this ever appear.
+ *
+ */
+struct ptp_clock_time {
+ __s64 sec; /* seconds */
+ __u32 nsec; /* nanoseconds */
+ __u32 reserved;
+};
+
+struct ptp_clock_caps {
+ int max_adj; /* Maximum frequency adjustment in parts per billon. */
+ int n_alarm; /* Number of programmable alarms. */
+ int n_ext_ts; /* Number of external time stamp channels. */
+ int n_per_out; /* Number of programmable periodic signals. */
+ int pps; /* Whether the clock supports a PPS callback. */
+ int rsv[15]; /* Reserved for future use. */
+};
+
+struct ptp_extts_request {
+ unsigned int index; /* Which channel to configure. */
+ unsigned int flags; /* Bit field for PTP_xxx flags. */
+ unsigned int rsv[2]; /* Reserved for future use. */
+};
+
+struct ptp_perout_request {
+ struct ptp_clock_time start; /* Absolute start time. */
+ struct ptp_clock_time period; /* Desired period, zero means disable. */
+ unsigned int index; /* Which channel to configure. */
+ unsigned int flags; /* Reserved for future use. */
+ unsigned int rsv[4]; /* Reserved for future use. */
+};
+
+#define PTP_CLK_MAGIC '='
+
+#define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps)
+#define PTP_EXTTS_REQUEST _IOW(PTP_CLK_MAGIC, 2, struct ptp_extts_request)
+#define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request)
+#define PTP_ENABLE_PPS _IOW(PTP_CLK_MAGIC, 4, int)
+
+struct ptp_extts_event {
+ struct ptp_clock_time t; /* Time event occured. */
+ unsigned int index; /* Which channel produced the event. */
+ unsigned int flags; /* Reserved for future use. */
+ unsigned int rsv[2]; /* Reserved for future use. */
+};
+
+#endif
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
new file mode 100644
index 0000000..dd2e44f
--- /dev/null
+++ b/include/linux/ptp_clock_kernel.h
@@ -0,0 +1,139 @@
+/*
+ * PTP 1588 clock support
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PTP_CLOCK_KERNEL_H_
+#define _PTP_CLOCK_KERNEL_H_
+
+#include <linux/ptp_clock.h>
+
+
+struct ptp_clock_request {
+ enum {
+ PTP_CLK_REQ_EXTTS,
+ PTP_CLK_REQ_PEROUT,
+ PTP_CLK_REQ_PPS,
+ } type;
+ union {
+ struct ptp_extts_request extts;
+ struct ptp_perout_request perout;
+ };
+};
+
+/**
+ * struct ptp_clock_info - decribes a PTP hardware clock
+ *
+ * @owner: The clock driver should set to THIS_MODULE.
+ * @name: A short name to identify the clock.
+ * @max_adj: The maximum possible frequency adjustment, in parts per billon.
+ * @n_alarm: The number of programmable alarms.
+ * @n_ext_ts: The number of external time stamp channels.
+ * @n_per_out: The number of programmable periodic signals.
+ * @pps: Indicates whether the clock supports a PPS callback.
+ *
+ * clock operations
+ *
+ * @adjfreq: Adjusts the frequency of the hardware clock.
+ * parameter delta: Desired period change in parts per billion.
+ *
+ * @adjtime: Shifts the time of the hardware clock.
+ * parameter delta: Desired change in nanoseconds.
+ *
+ * @gettime: Reads the current time from the hardware clock.
+ * parameter ts: Holds the result.
+ *
+ * @settime: Set the current time on the hardware clock.
+ * parameter ts: Time value to set.
+ *
+ * @enable: Request driver to enable or disable an ancillary feature.
+ * parameter request: Desired resource to enable or disable.
+ * parameter on: Caller passes one to enable or zero to disable.
+ *
+ * Drivers should embed their ptp_clock_info within a private
+ * structure, obtaining a reference to it using container_of().
+ *
+ * The callbacks must all return zero on success, non-zero otherwise.
+ */
+
+struct ptp_clock_info {
+ struct module *owner;
+ char name[16];
+ s32 max_adj;
+ int n_alarm;
+ int n_ext_ts;
+ int n_per_out;
+ int pps;
+ int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
+ int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
+ int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts);
+ int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts);
+ int (*enable)(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *request, int on);
+};
+
+struct ptp_clock;
+
+/**
+ * ptp_clock_register() - register a PTP hardware clock driver
+ *
+ * @info: Structure describing the new clock.
+ */
+
+extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info);
+
+/**
+ * ptp_clock_unregister() - unregister a PTP hardware clock driver
+ *
+ * @ptp: The clock to remove from service.
+ */
+
+extern int ptp_clock_unregister(struct ptp_clock *ptp);
+
+
+enum ptp_clock_events {
+ PTP_CLOCK_ALARM,
+ PTP_CLOCK_EXTTS,
+ PTP_CLOCK_PPS,
+};
+
+/**
+ * struct ptp_clock_event - decribes a PTP hardware clock event
+ *
+ * @type: One of the ptp_clock_events enumeration values.
+ * @index: Identifies the source of the event.
+ * @timestamp: When the event occured.
+ */
+
+struct ptp_clock_event {
+ int type;
+ int index;
+ u64 timestamp;
+};
+
+/**
+ * ptp_clock_event() - notify the PTP layer about an event
+ *
+ * @ptp: The clock obtained from ptp_clock_register().
+ * @event: Message structure describing the event.
+ */
+
+extern void ptp_clock_event(struct ptp_clock *ptp,
+ struct ptp_clock_event *event);
+
+#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V11 2/4] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
From: Richard Cochran @ 2011-02-23 10:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1298447722.git.richard.cochran@omicron.at>
The eTSEC includes a PTP clock with quite a few features. This patch adds
support for the basic clock adjustment functions, plus two external time
stamps, one alarm, and the PPS callback.
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
Acked-by: John Stultz <johnstul@us.ibm.com>
---
.../devicetree/bindings/net/fsl-tsec-phy.txt | 57 +++
arch/powerpc/boot/dts/mpc8313erdb.dts | 14 +
arch/powerpc/boot/dts/mpc8572ds.dts | 14 +
arch/powerpc/boot/dts/p2020ds.dts | 14 +
arch/powerpc/boot/dts/p2020rdb.dts | 14 +
drivers/net/Makefile | 1 +
drivers/net/gianfar_ptp.c | 448 ++++++++++++++++++++
drivers/net/gianfar_ptp_reg.h | 113 +++++
drivers/ptp/Kconfig | 13 +
9 files changed, 688 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/gianfar_ptp.c
create mode 100644 drivers/net/gianfar_ptp_reg.h
diff --git a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
index edb7ae1..f6edbb8 100644
--- a/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
+++ b/Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
@@ -74,3 +74,60 @@ Example:
interrupt-parent = <&mpic>;
phy-handle = <&phy0>
};
+
+* Gianfar PTP clock nodes
+
+General Properties:
+
+ - compatible Should be "fsl,etsec-ptp"
+ - reg Offset and length of the register set for the device
+ - interrupts There should be at least two interrupts. Some devices
+ have as many as four PTP related interrupts.
+
+Clock Properties:
+
+ - tclk-period Timer reference clock period in nanoseconds.
+ - tmr-prsc Prescaler, divides the output clock.
+ - tmr-add Frequency compensation value.
+ - cksel 0= external clock, 1= eTSEC system clock, 3= RTC clock input.
+ Currently the driver only supports choice "1".
+ - tmr-fiper1 Fixed interval period pulse generator.
+ - tmr-fiper2 Fixed interval period pulse generator.
+ - max-adj Maximum frequency adjustment in parts per billion.
+
+ These properties set the operational parameters for the PTP
+ clock. You must choose these carefully for the clock to work right.
+ Here is how to figure good values:
+
+ TimerOsc = system clock MHz
+ tclk_period = desired clock period nanoseconds
+ NominalFreq = 1000 / tclk_period MHz
+ FreqDivRatio = TimerOsc / NominalFreq (must be greater that 1.0)
+ tmr_add = ceil(2^32 / FreqDivRatio)
+ OutputClock = NominalFreq / tmr_prsc MHz
+ PulseWidth = 1 / OutputClock microseconds
+ FiperFreq1 = desired frequency in Hz
+ FiperDiv1 = 1000000 * OutputClock / FiperFreq1
+ tmr_fiper1 = tmr_prsc * tclk_period * FiperDiv1 - tclk_period
+ max_adj = 1000000000 * (FreqDivRatio - 1.0) - 1
+
+ The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
+ driver expects that tmr_fiper1 will be correctly set to produce a 1
+ Pulse Per Second (PPS) signal, since this will be offered to the PPS
+ subsystem to synchronize the Linux clock.
+
+Example:
+
+ ptp_clock at 24E00 {
+ compatible = "fsl,etsec-ptp";
+ reg = <0x24E00 0xB0>;
+ interrupts = <12 0x8 13 0x8>;
+ interrupt-parent = < &ipic >;
+ tclk-period = <10>;
+ tmr-prsc = <100>;
+ tmr-add = <0x999999A4>;
+ cksel = <0x1>;
+ tmr-fiper1 = <0x3B9AC9F6>;
+ tmr-fiper2 = <0x00018696>;
+ max-adj = <659999998>;
+ };
diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts
index 183f2aa..85a7eaa 100644
--- a/arch/powerpc/boot/dts/mpc8313erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
@@ -208,6 +208,20 @@
sleep = <&pmc 0x00300000>;
};
+ ptp_clock at 24E00 {
+ compatible = "fsl,etsec-ptp";
+ reg = <0x24E00 0xB0>;
+ interrupts = <12 0x8 13 0x8>;
+ interrupt-parent = < &ipic >;
+ tclk-period = <10>;
+ tmr-prsc = <100>;
+ tmr-add = <0x999999A4>;
+ cksel = <0x1>;
+ tmr-fiper1 = <0x3B9AC9F6>;
+ tmr-fiper2 = <0x00018696>;
+ max-adj = <659999998>;
+ };
+
enet0: ethernet at 24000 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts
index cafc128..74208cd 100644
--- a/arch/powerpc/boot/dts/mpc8572ds.dts
+++ b/arch/powerpc/boot/dts/mpc8572ds.dts
@@ -324,6 +324,20 @@
};
};
+ ptp_clock at 24E00 {
+ compatible = "fsl,etsec-ptp";
+ reg = <0x24E00 0xB0>;
+ interrupts = <68 2 69 2 70 2 71 2>;
+ interrupt-parent = < &mpic >;
+ tclk-period = <5>;
+ tmr-prsc = <200>;
+ tmr-add = <0xAAAAAAAB>;
+ cksel = <1>;
+ tmr-fiper1 = <0x3B9AC9FB>;
+ tmr-fiper2 = <0x3B9AC9FB>;
+ max-adj = <499999999>;
+ };
+
enet0: ethernet at 24000 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts
index 1101914..39d73bb 100644
--- a/arch/powerpc/boot/dts/p2020ds.dts
+++ b/arch/powerpc/boot/dts/p2020ds.dts
@@ -336,6 +336,20 @@
phy_type = "ulpi";
};
+ ptp_clock at 24E00 {
+ compatible = "fsl,etsec-ptp";
+ reg = <0x24E00 0xB0>;
+ interrupts = <68 2 69 2 70 2>;
+ interrupt-parent = < &mpic >;
+ tclk-period = <5>;
+ tmr-prsc = <200>;
+ tmr-add = <0xCCCCCCCD>;
+ cksel = <1>;
+ tmr-fiper1 = <0x3B9AC9FB>;
+ tmr-fiper2 = <0x0001869B>;
+ max-adj = <249999999>;
+ };
+
enet0: ethernet at 24000 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts
index da4cb0d..5498fb9 100644
--- a/arch/powerpc/boot/dts/p2020rdb.dts
+++ b/arch/powerpc/boot/dts/p2020rdb.dts
@@ -396,6 +396,20 @@
phy_type = "ulpi";
};
+ ptp_clock at 24E00 {
+ compatible = "fsl,etsec-ptp";
+ reg = <0x24E00 0xB0>;
+ interrupts = <68 2 69 2 70 2>;
+ interrupt-parent = < &mpic >;
+ tclk-period = <5>;
+ tmr-prsc = <200>;
+ tmr-add = <0xCCCCCCCD>;
+ cksel = <1>;
+ tmr-fiper1 = <0x3B9AC9FB>;
+ tmr-fiper2 = <0x0001869B>;
+ max-adj = <249999999>;
+ };
+
enet0: ethernet at 24000 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..c303f5f 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_ATL2) += atlx/
obj-$(CONFIG_ATL1E) += atl1e/
obj-$(CONFIG_ATL1C) += atl1c/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
obj-$(CONFIG_TEHUTI) += tehuti.o
obj-$(CONFIG_ENIC) += enic/
obj-$(CONFIG_JME) += jme.o
diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c
new file mode 100644
index 0000000..84fff15
--- /dev/null
+++ b/drivers/net/gianfar_ptp.c
@@ -0,0 +1,448 @@
+/*
+ * PTP 1588 clock using the eTSEC
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+#include "gianfar_ptp_reg.h"
+#include "gianfar.h"
+
+#define DRIVER "gianfar_ptp"
+#define N_ALARM 1 /* first alarm is used internally to reset fipers */
+#define N_EXT_TS 2
+#define REG_SIZE sizeof(struct gianfar_ptp_registers)
+
+struct etsects {
+ struct gianfar_ptp_registers *regs;
+ struct ptp_clock *clock;
+ struct ptp_clock_info caps;
+ int irq;
+ u64 alarm_interval; /* for periodic alarm */
+ u64 alarm_value;
+ u32 tclk_period; /* nanoseconds */
+ u32 tmr_prsc;
+ u32 tmr_add;
+ u32 cksel;
+ u32 tmr_fiper1;
+ u32 tmr_fiper2;
+};
+
+/* Private globals */
+static struct etsects the_clock;
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static u64 tmr_cnt_read(struct etsects *etsects)
+{
+ u64 ns;
+ u32 lo, hi;
+
+ lo = gfar_read(&etsects->regs->tmr_cnt_l);
+ hi = gfar_read(&etsects->regs->tmr_cnt_h);
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ return ns;
+}
+
+static void tmr_cnt_write(struct etsects *etsects, u64 ns)
+{
+ u32 hi = ns >> 32;
+ u32 lo = ns & 0xffffffff;
+
+ gfar_write(&etsects->regs->tmr_cnt_l, lo);
+ gfar_write(&etsects->regs->tmr_cnt_h, hi);
+}
+
+static void set_alarm(struct etsects *etsects)
+{
+ u64 ns;
+ u32 lo, hi;
+
+ ns = tmr_cnt_read(etsects) + 1500000000ULL;
+ ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
+ ns -= etsects->tclk_period;
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+ gfar_write(&etsects->regs->tmr_alarm1_l, lo);
+ gfar_write(&etsects->regs->tmr_alarm1_h, hi);
+}
+
+static void set_fipers(struct etsects *etsects)
+{
+ u32 tmr_ctrl = gfar_read(&etsects->regs->tmr_ctrl);
+
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl & (~TE));
+ gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
+ gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+ gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+ set_alarm(etsects);
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|TE);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+ struct etsects *etsects = priv;
+ struct ptp_clock_event event;
+ u64 ns;
+ u32 ack = 0, lo, hi, mask, val;
+
+ val = gfar_read(&etsects->regs->tmr_tevent);
+
+ if (val & ETS1) {
+ ack |= ETS1;
+ hi = gfar_read(&etsects->regs->tmr_etts1_h);
+ lo = gfar_read(&etsects->regs->tmr_etts1_l);
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 0;
+ event.timestamp = ((u64) hi) << 32;
+ event.timestamp |= lo;
+ ptp_clock_event(etsects->clock, &event);
+ }
+
+ if (val & ETS2) {
+ ack |= ETS2;
+ hi = gfar_read(&etsects->regs->tmr_etts2_h);
+ lo = gfar_read(&etsects->regs->tmr_etts2_l);
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 1;
+ event.timestamp = ((u64) hi) << 32;
+ event.timestamp |= lo;
+ ptp_clock_event(etsects->clock, &event);
+ }
+
+ if (val & ALM2) {
+ ack |= ALM2;
+ if (etsects->alarm_value) {
+ event.type = PTP_CLOCK_ALARM;
+ event.index = 0;
+ event.timestamp = etsects->alarm_value;
+ ptp_clock_event(etsects->clock, &event);
+ }
+ if (etsects->alarm_interval) {
+ ns = etsects->alarm_value + etsects->alarm_interval;
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+ spin_lock(®ister_lock);
+ gfar_write(&etsects->regs->tmr_alarm2_l, lo);
+ gfar_write(&etsects->regs->tmr_alarm2_h, hi);
+ spin_unlock(®ister_lock);
+ etsects->alarm_value = ns;
+ } else {
+ gfar_write(&etsects->regs->tmr_tevent, ALM2);
+ spin_lock(®ister_lock);
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ mask &= ~ALM2EN;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+ spin_unlock(®ister_lock);
+ etsects->alarm_value = 0;
+ etsects->alarm_interval = 0;
+ }
+ }
+
+ if (val & PP1) {
+ ack |= PP1;
+ event.type = PTP_CLOCK_PPS;
+ ptp_clock_event(etsects->clock, &event);
+ }
+
+ if (ack) {
+ gfar_write(&etsects->regs->tmr_tevent, ack);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_gianfar_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ u64 adj;
+ u32 diff, tmr_add;
+ int neg_adj = 0;
+ struct etsects *etsects = container_of(ptp, struct etsects, caps);
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ tmr_add = etsects->tmr_add;
+ adj = tmr_add;
+ adj *= ppb;
+ diff = div_u64(adj, 1000000000ULL);
+
+ tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
+
+ gfar_write(&etsects->regs->tmr_add, tmr_add);
+
+ return 0;
+}
+
+static int ptp_gianfar_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ s64 now;
+ unsigned long flags;
+ struct etsects *etsects = container_of(ptp, struct etsects, caps);
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ now = tmr_cnt_read(etsects);
+ now += delta;
+ tmr_cnt_write(etsects, now);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ set_fipers(etsects);
+
+ return 0;
+}
+
+static int ptp_gianfar_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ u64 ns;
+ u32 remainder;
+ unsigned long flags;
+ struct etsects *etsects = container_of(ptp, struct etsects, caps);
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ ns = tmr_cnt_read(etsects);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int ptp_gianfar_settime(struct ptp_clock_info *ptp,
+ const struct timespec *ts)
+{
+ u64 ns;
+ unsigned long flags;
+ struct etsects *etsects = container_of(ptp, struct etsects, caps);
+
+ ns = ts->tv_sec * 1000000000ULL;
+ ns += ts->tv_nsec;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ tmr_cnt_write(etsects, ns);
+ set_fipers(etsects);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ return 0;
+}
+
+static int ptp_gianfar_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct etsects *etsects = container_of(ptp, struct etsects, caps);
+ unsigned long flags;
+ u32 bit, mask;
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_EXTTS:
+ switch (rq->extts.index) {
+ case 0:
+ bit = ETS1EN;
+ break;
+ case 1:
+ bit = ETS2EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+ spin_lock_irqsave(®ister_lock, flags);
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ if (on)
+ mask |= bit;
+ else
+ mask &= ~bit;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+ spin_unlock_irqrestore(®ister_lock, flags);
+ return 0;
+
+ case PTP_CLK_REQ_PPS:
+ spin_lock_irqsave(®ister_lock, flags);
+ mask = gfar_read(&etsects->regs->tmr_temask);
+ if (on)
+ mask |= PP1EN;
+ else
+ mask &= ~PP1EN;
+ gfar_write(&etsects->regs->tmr_temask, mask);
+ spin_unlock_irqrestore(®ister_lock, flags);
+ return 0;
+
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_gianfar_caps = {
+ .owner = THIS_MODULE,
+ .name = "gianfar clock",
+ .max_adj = 512000,
+ .n_alarm = N_ALARM,
+ .n_ext_ts = N_EXT_TS,
+ .n_per_out = 0,
+ .pps = 1,
+ .adjfreq = ptp_gianfar_adjfreq,
+ .adjtime = ptp_gianfar_adjtime,
+ .gettime = ptp_gianfar_gettime,
+ .settime = ptp_gianfar_settime,
+ .enable = ptp_gianfar_enable,
+};
+
+/* OF device tree */
+
+static int get_of_u32(struct device_node *node, char *str, u32 *val)
+{
+ int plen;
+ const u32 *prop = of_get_property(node, str, &plen);
+
+ if (!prop || plen != sizeof(*prop))
+ return -1;
+ *val = *prop;
+ return 0;
+}
+
+static int gianfar_ptp_probe(struct platform_device *dev,
+ const struct of_device_id *match)
+{
+ struct device_node *node = dev->dev.of_node;
+ struct etsects *etsects = &the_clock;
+ struct timespec now;
+ u32 tmr_ctrl;
+
+ etsects->caps = ptp_gianfar_caps;
+
+ if (get_of_u32(node, "tclk-period", &etsects->tclk_period) ||
+ get_of_u32(node, "tmr-prsc", &etsects->tmr_prsc) ||
+ get_of_u32(node, "tmr-add", &etsects->tmr_add) ||
+ get_of_u32(node, "cksel", &etsects->cksel) ||
+ get_of_u32(node, "tmr-fiper1", &etsects->tmr_fiper1) ||
+ get_of_u32(node, "tmr-fiper2", &etsects->tmr_fiper2) ||
+ get_of_u32(node, "max-adj", &etsects->caps.max_adj)) {
+ pr_err("device tree node missing required elements\n");
+ return -ENODEV;
+ }
+
+ etsects->irq = irq_of_parse_and_map(node, 0);
+
+ if (etsects->irq == NO_IRQ) {
+ pr_err("irq not in device tree\n");
+ return -ENODEV;
+ }
+ if (request_irq(etsects->irq, isr, 0, DRIVER, etsects)) {
+ pr_err("request_irq failed\n");
+ return -ENODEV;
+ }
+ etsects->regs = of_iomap(node, 0);
+ if (!etsects->regs) {
+ pr_err("of_iomap ptp registers failed\n");
+ return -EINVAL;
+ }
+ getnstimeofday(&now);
+ ptp_gianfar_settime(&etsects->caps, &now);
+
+ tmr_ctrl =
+ (etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
+ (etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT;
+
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl);
+ gfar_write(&etsects->regs->tmr_add, etsects->tmr_add);
+ gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
+ gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+ gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+ set_alarm(etsects);
+ gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE);
+
+ etsects->clock = ptp_clock_register(&etsects->caps);
+
+ return IS_ERR(etsects->clock) ? PTR_ERR(etsects->clock) : 0;
+}
+
+static int gianfar_ptp_remove(struct platform_device *dev)
+{
+ gfar_write(&the_clock.regs->tmr_temask, 0);
+ gfar_write(&the_clock.regs->tmr_ctrl, 0);
+
+ ptp_clock_unregister(the_clock.clock);
+ free_irq(the_clock.irq, &the_clock);
+ iounmap(the_clock.regs);
+
+ return 0;
+}
+
+static struct of_device_id match_table[] = {
+ { .compatible = "fsl,etsec-ptp" },
+ {},
+};
+
+static struct of_platform_driver gianfar_ptp_driver = {
+ .driver = {
+ .name = "gianfar_ptp",
+ .of_match_table = match_table,
+ .owner = THIS_MODULE,
+ },
+ .probe = gianfar_ptp_probe,
+ .remove = gianfar_ptp_remove,
+};
+
+/* module operations */
+
+static int __init ptp_gianfar_init(void)
+{
+ return of_register_platform_driver(&gianfar_ptp_driver);
+}
+
+module_init(ptp_gianfar_init);
+
+static void __exit ptp_gianfar_exit(void)
+{
+ of_unregister_platform_driver(&gianfar_ptp_driver);
+}
+
+module_exit(ptp_gianfar_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the eTSEC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/gianfar_ptp_reg.h b/drivers/net/gianfar_ptp_reg.h
new file mode 100644
index 0000000..95e171f
--- /dev/null
+++ b/drivers/net/gianfar_ptp_reg.h
@@ -0,0 +1,113 @@
+/* gianfar_ptp_reg.h
+ * Generated by regen.tcl on Thu May 13 01:38:57 PM CEST 2010
+ *
+ * PTP 1588 clock using the gianfar eTSEC
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _GIANFAR_PTP_REG_H_
+#define _GIANFAR_PTP_REG_H_
+
+struct gianfar_ptp_registers {
+ u32 tmr_ctrl; /* Timer control register */
+ u32 tmr_tevent; /* Timestamp event register */
+ u32 tmr_temask; /* Timer event mask register */
+ u32 tmr_pevent; /* Timestamp event register */
+ u32 tmr_pemask; /* Timer event mask register */
+ u32 tmr_stat; /* Timestamp status register */
+ u32 tmr_cnt_h; /* Timer counter high register */
+ u32 tmr_cnt_l; /* Timer counter low register */
+ u32 tmr_add; /* Timer drift compensation addend register */
+ u32 tmr_acc; /* Timer accumulator register */
+ u32 tmr_prsc; /* Timer prescale */
+ u8 res1[4];
+ u32 tmroff_h; /* Timer offset high */
+ u32 tmroff_l; /* Timer offset low */
+ u8 res2[8];
+ u32 tmr_alarm1_h; /* Timer alarm 1 high register */
+ u32 tmr_alarm1_l; /* Timer alarm 1 high register */
+ u32 tmr_alarm2_h; /* Timer alarm 2 high register */
+ u32 tmr_alarm2_l; /* Timer alarm 2 high register */
+ u8 res3[48];
+ u32 tmr_fiper1; /* Timer fixed period interval */
+ u32 tmr_fiper2; /* Timer fixed period interval */
+ u32 tmr_fiper3; /* Timer fixed period interval */
+ u8 res4[20];
+ u32 tmr_etts1_h; /* Timestamp of general purpose external trigger */
+ u32 tmr_etts1_l; /* Timestamp of general purpose external trigger */
+ u32 tmr_etts2_h; /* Timestamp of general purpose external trigger */
+ u32 tmr_etts2_l; /* Timestamp of general purpose external trigger */
+};
+
+/* Bit definitions for the TMR_CTRL register */
+#define ALM1P (1<<31) /* Alarm1 output polarity */
+#define ALM2P (1<<30) /* Alarm2 output polarity */
+#define FS (1<<28) /* FIPER start indication */
+#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */
+#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */
+#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */
+#define TCLK_PERIOD_MASK (0x3ff)
+#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */
+#define FRD (1<<14) /* FIPER Realignment Disable */
+#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */
+#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */
+#define ETEP2 (1<<9) /* External trigger 2 edge polarity */
+#define ETEP1 (1<<8) /* External trigger 1 edge polarity */
+#define COPH (1<<7) /* Generated clock (TSEC_1588_GCLK) output phase. */
+#define CIPH (1<<6) /* External oscillator input clock phase. */
+#define TMSR (1<<5) /* Timer soft reset. When enabled, it resets all the timer registers and state machines. */
+#define BYP (1<<3) /* Bypass drift compensated clock */
+#define TE (1<<2) /* 1588 timer enable. If not enabled, all the timer registers and state machines are disabled. */
+#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source select. */
+#define CKSEL_MASK (0x3)
+
+/* Bit definitions for the TMR_TEVENT register */
+#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */
+#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */
+#define ALM2 (1<<17) /* Current time equaled alarm time register 2 */
+#define ALM1 (1<<16) /* Current time equaled alarm time register 1 */
+#define PP1 (1<<7) /* Indicates that a periodic pulse has been generated based on FIPER1 register */
+#define PP2 (1<<6) /* Indicates that a periodic pulse has been generated based on FIPER2 register */
+#define PP3 (1<<5) /* Indicates that a periodic pulse has been generated based on FIPER3 register */
+
+/* Bit definitions for the TMR_TEMASK register */
+#define ETS2EN (1<<25) /* External trigger 2 timestamp sample event enable */
+#define ETS1EN (1<<24) /* External trigger 1 timestamp sample event enable */
+#define ALM2EN (1<<17) /* Timer ALM2 event enable */
+#define ALM1EN (1<<16) /* Timer ALM1 event enable */
+#define PP1EN (1<<7) /* Periodic pulse event 1 enable */
+#define PP2EN (1<<6) /* Periodic pulse event 2 enable */
+
+/* Bit definitions for the TMR_PEVENT register */
+#define TXP2 (1<<9) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS2 register */
+#define TXP1 (1<<8) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS1 register */
+#define RXP (1<<0) /* Indicates that a PTP frame has been received */
+
+/* Bit definitions for the TMR_PEMASK register */
+#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */
+#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */
+#define RXPEN (1<<0) /* Receive PTP packet event enable */
+
+/* Bit definitions for the TMR_STAT register */
+#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */
+#define STAT_VEC_MASK (0x3f)
+
+/* Bit definitions for the TMR_PRSC register */
+#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */
+#define PRSC_OCK_MASK (0xffff)
+
+#endif
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 70d4bb1..12eb844 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -27,4 +27,17 @@ config PTP_1588_CLOCK
To compile this driver as a module, choose M here: the module
will be called ptp.
+config PTP_1588_CLOCK_GIANFAR
+ tristate "Freescale eTSEC as PTP clock"
+ depends on PTP_1588_CLOCK
+ depends on GIANFAR
+ help
+ This driver adds support for using the eTSEC as a PTP
+ clock. This clock is only useful if your PTP programs are
+ getting hardware time stamps on the PTP Ethernet packets
+ using the SO_TIMESTAMPING API.
+
+ To compile this driver as a module, choose M here: the module
+ will be called gianfar_ptp.
+
endmenu
--
1.7.0.4
^ permalink raw reply related
* [PATCH V11 3/4] ptp: Added a clock driver for the IXP46x.
From: Richard Cochran @ 2011-02-23 10:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1298447722.git.richard.cochran@omicron.at>
This patch adds a driver for the hardware time stamping unit found on the
IXP465. The basic clock operations and an external trigger are implemented.
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
Acked-by: John Stultz <johnstul@us.ibm.com>
---
arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h | 78 ++++++
drivers/net/arm/ixp4xx_eth.c | 192 ++++++++++++++-
drivers/ptp/Kconfig | 13 +
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_ixp46x.c | 332 +++++++++++++++++++++++++
5 files changed, 613 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
create mode 100644 drivers/ptp/ptp_ixp46x.c
diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
new file mode 100644
index 0000000..292d55e
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
@@ -0,0 +1,78 @@
+/*
+ * PTP 1588 clock using the IXP46X
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _IXP46X_TS_H_
+#define _IXP46X_TS_H_
+
+#define DEFAULT_ADDEND 0xF0000029
+#define TICKS_NS_SHIFT 4
+
+struct ixp46x_channel_ctl {
+ u32 ch_control; /* 0x40 Time Synchronization Channel Control */
+ u32 ch_event; /* 0x44 Time Synchronization Channel Event */
+ u32 tx_snap_lo; /* 0x48 Transmit Snapshot Low Register */
+ u32 tx_snap_hi; /* 0x4C Transmit Snapshot High Register */
+ u32 rx_snap_lo; /* 0x50 Receive Snapshot Low Register */
+ u32 rx_snap_hi; /* 0x54 Receive Snapshot High Register */
+ u32 src_uuid_lo; /* 0x58 Source UUID0 Low Register */
+ u32 src_uuid_hi; /* 0x5C Sequence Identifier/Source UUID0 High */
+};
+
+struct ixp46x_ts_regs {
+ u32 control; /* 0x00 Time Sync Control Register */
+ u32 event; /* 0x04 Time Sync Event Register */
+ u32 addend; /* 0x08 Time Sync Addend Register */
+ u32 accum; /* 0x0C Time Sync Accumulator Register */
+ u32 test; /* 0x10 Time Sync Test Register */
+ u32 unused; /* 0x14 */
+ u32 rsystime_lo; /* 0x18 RawSystemTime_Low Register */
+ u32 rsystime_hi; /* 0x1C RawSystemTime_High Register */
+ u32 systime_lo; /* 0x20 SystemTime_Low Register */
+ u32 systime_hi; /* 0x24 SystemTime_High Register */
+ u32 trgt_lo; /* 0x28 TargetTime_Low Register */
+ u32 trgt_hi; /* 0x2C TargetTime_High Register */
+ u32 asms_lo; /* 0x30 Auxiliary Slave Mode Snapshot Low */
+ u32 asms_hi; /* 0x34 Auxiliary Slave Mode Snapshot High */
+ u32 amms_lo; /* 0x38 Auxiliary Master Mode Snapshot Low */
+ u32 amms_hi; /* 0x3C Auxiliary Master Mode Snapshot High */
+
+ struct ixp46x_channel_ctl channel[3];
+};
+
+/* 0x00 Time Sync Control Register Bits */
+#define TSCR_AMM (1<<3)
+#define TSCR_ASM (1<<2)
+#define TSCR_TTM (1<<1)
+#define TSCR_RST (1<<0)
+
+/* 0x04 Time Sync Event Register Bits */
+#define TSER_SNM (1<<3)
+#define TSER_SNS (1<<2)
+#define TTIPEND (1<<1)
+
+/* 0x40 Time Synchronization Channel Control Register Bits */
+#define MASTER_MODE (1<<0)
+#define TIMESTAMP_ALL (1<<1)
+
+/* 0x44 Time Synchronization Channel Event Register Bits */
+#define TX_SNAPSHOT_LOCKED (1<<0)
+#define RX_SNAPSHOT_LOCKED (1<<1)
+
+#endif
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 9eb9b98..fa08c17 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -30,9 +30,12 @@
#include <linux/etherdevice.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
+#include <linux/ptp_classify.h>
#include <linux/slab.h>
+#include <mach/ixp46x_ts.h>
#include <mach/npe.h>
#include <mach/qmgr.h>
@@ -67,6 +70,10 @@
#define RXFREE_QUEUE(port_id) (NPE_ID(port_id) + 26)
#define TXDONE_QUEUE 31
+#define PTP_SLAVE_MODE 1
+#define PTP_MASTER_MODE 2
+#define PORT2CHANNEL(p) NPE_ID(p->id)
+
/* TX Control Registers */
#define TX_CNTRL0_TX_EN 0x01
#define TX_CNTRL0_HALFDUPLEX 0x02
@@ -171,6 +178,8 @@ struct port {
int id; /* logical port ID */
int speed, duplex;
u8 firmware[4];
+ int hwts_tx_en;
+ int hwts_rx_en;
};
/* NPE message structure */
@@ -246,6 +255,169 @@ static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
+static struct sock_filter ptp_filter[] = {
+ PTP_FILTER
+};
+
+static int match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seq)
+{
+ unsigned int type;
+ u16 *hi, *id;
+ u8 *lo, *data = skb->data;
+
+ type = sk_run_filter(skb, ptp_filter);
+
+ if (PTP_CLASS_V1_IPV4 == type) {
+
+ id = (u16 *)(data + 42 + 30);
+ hi = (u16 *)(data + 42 + 22);
+ lo = data + 42 + 24;
+
+ return (uid_hi == *hi &&
+ 0 == memcmp(&uid_lo, lo, sizeof(uid_lo)) &&
+ seq == *id);
+ }
+
+ return 0;
+}
+
+static void do_rx_timestamp(struct port *port, struct sk_buff *skb)
+{
+ struct skb_shared_hwtstamps *shhwtstamps;
+ struct ixp46x_ts_regs *regs;
+ u64 ns;
+ u32 ch, hi, lo, val;
+ u16 uid, seq;
+
+ if (!port->hwts_rx_en)
+ return;
+
+ ch = PORT2CHANNEL(port);
+
+ regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+ val = __raw_readl(®s->channel[ch].ch_event);
+
+ if (!(val & RX_SNAPSHOT_LOCKED))
+ return;
+
+ lo = __raw_readl(®s->channel[ch].src_uuid_lo);
+ hi = __raw_readl(®s->channel[ch].src_uuid_hi);
+
+ uid = hi & 0xffff;
+ seq = (hi >> 16) & 0xffff;
+
+ if (!match(skb, htons(uid), htonl(lo), htons(seq)))
+ goto out;
+
+ lo = __raw_readl(®s->channel[ch].rx_snap_lo);
+ hi = __raw_readl(®s->channel[ch].rx_snap_hi);
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ ns <<= TICKS_NS_SHIFT;
+
+ shhwtstamps = skb_hwtstamps(skb);
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(ns);
+out:
+ __raw_writel(RX_SNAPSHOT_LOCKED, ®s->channel[ch].ch_event);
+}
+
+static void do_tx_timestamp(struct port *port, struct sk_buff *skb)
+{
+ struct skb_shared_hwtstamps shhwtstamps;
+ struct ixp46x_ts_regs *regs;
+ struct skb_shared_info *shtx;
+ u64 ns;
+ u32 ch, cnt, hi, lo, val;
+
+ shtx = skb_shinfo(skb);
+ if (unlikely(shtx->tx_flags & SKBTX_HW_TSTAMP && port->hwts_tx_en))
+ shtx->tx_flags |= SKBTX_IN_PROGRESS;
+ else
+ return;
+
+ ch = PORT2CHANNEL(port);
+
+ regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+ /*
+ * This really stinks, but we have to poll for the Tx time stamp.
+ * Usually, the time stamp is ready after 4 to 6 microseconds.
+ */
+ for (cnt = 0; cnt < 100; cnt++) {
+ val = __raw_readl(®s->channel[ch].ch_event);
+ if (val & TX_SNAPSHOT_LOCKED)
+ break;
+ udelay(1);
+ }
+ if (!(val & TX_SNAPSHOT_LOCKED)) {
+ shtx->tx_flags &= ~SKBTX_IN_PROGRESS;
+ return;
+ }
+
+ lo = __raw_readl(®s->channel[ch].tx_snap_lo);
+ hi = __raw_readl(®s->channel[ch].tx_snap_hi);
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ ns <<= TICKS_NS_SHIFT;
+
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ shhwtstamps.hwtstamp = ns_to_ktime(ns);
+ skb_tstamp_tx(skb, &shhwtstamps);
+
+ __raw_writel(TX_SNAPSHOT_LOCKED, ®s->channel[ch].ch_event);
+}
+
+static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct hwtstamp_config cfg;
+ struct ixp46x_ts_regs *regs;
+ struct port *port = netdev_priv(netdev);
+ int ch;
+
+ if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+ return -EFAULT;
+
+ if (cfg.flags) /* reserved for future extensions */
+ return -EINVAL;
+
+ ch = PORT2CHANNEL(port);
+ regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+ switch (cfg.tx_type) {
+ case HWTSTAMP_TX_OFF:
+ port->hwts_tx_en = 0;
+ break;
+ case HWTSTAMP_TX_ON:
+ port->hwts_tx_en = 1;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (cfg.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ port->hwts_rx_en = 0;
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ port->hwts_rx_en = PTP_SLAVE_MODE;
+ __raw_writel(0, ®s->channel[ch].ch_control);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ port->hwts_rx_en = PTP_MASTER_MODE;
+ __raw_writel(MASTER_MODE, ®s->channel[ch].ch_control);
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* Clear out any old time stamps. */
+ __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
+ ®s->channel[ch].ch_event);
+
+ return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location,
int write, u16 cmd)
@@ -573,6 +745,7 @@ static int eth_poll(struct napi_struct *napi, int budget)
debug_pkt(dev, "eth_poll", skb->data, skb->len);
+ do_rx_timestamp(port, skb);
skb->protocol = eth_type_trans(skb, dev);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
@@ -679,14 +852,12 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4);
- dev_kfree_skb(skb);
#endif
phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE);
if (dma_mapping_error(&dev->dev, phys)) {
-#ifdef __ARMEB__
dev_kfree_skb(skb);
-#else
+#ifndef __ARMEB__
kfree(mem);
#endif
dev->stats.tx_dropped++;
@@ -728,6 +899,13 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
#if DEBUG_TX
printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name);
#endif
+
+ do_tx_timestamp(port, skb);
+ skb_tx_timestamp(skb);
+
+#ifndef __ARMEB__
+ dev_kfree_skb(skb);
+#endif
return NETDEV_TX_OK;
}
@@ -783,6 +961,9 @@ static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
if (!netif_running(dev))
return -EINVAL;
+ if (cpu_is_ixp46x() && cmd == SIOCSHWTSTAMP)
+ return hwtstamp_ioctl(dev, req, cmd);
+
return phy_mii_ioctl(port->phydev, req, cmd);
}
@@ -1171,6 +1352,11 @@ static int __devinit eth_init_one(struct platform_device *pdev)
char phy_id[MII_BUS_ID_SIZE + 3];
int err;
+ if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
+ pr_err("ixp4xx_eth: bad ptp filter\n");
+ return -EINVAL;
+ }
+
if (!(dev = alloc_etherdev(sizeof(struct port))))
return -ENOMEM;
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 12eb844..fcfafd0 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -40,4 +40,17 @@ config PTP_1588_CLOCK_GIANFAR
To compile this driver as a module, choose M here: the module
will be called gianfar_ptp.
+config PTP_1588_CLOCK_IXP46X
+ tristate "Intel IXP46x as PTP clock"
+ depends on PTP_1588_CLOCK
+ depends on IXP4XX_ETH
+ help
+ This driver adds support for using the IXP46X as a PTP
+ clock. This clock is only useful if your PTP programs are
+ getting hardware time stamps on the PTP Ethernet packets
+ using the SO_TIMESTAMPING API.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp_ixp46x.
+
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 480e2af..f6933e8 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -4,3 +4,4 @@
ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o
obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
+obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o
diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
new file mode 100644
index 0000000..e28b416
--- /dev/null
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -0,0 +1,332 @@
+/*
+ * PTP 1588 clock using the IXP46X
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/ptp_clock_kernel.h>
+#include <mach/ixp46x_ts.h>
+
+#define DRIVER "ptp_ixp46x"
+#define N_EXT_TS 2
+#define MASTER_GPIO 8
+#define MASTER_IRQ 25
+#define SLAVE_GPIO 7
+#define SLAVE_IRQ 24
+
+struct ixp_clock {
+ struct ixp46x_ts_regs *regs;
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info caps;
+ int exts0_enabled;
+ int exts1_enabled;
+};
+
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static u64 sys_time_read(struct ixp46x_ts_regs *regs)
+{
+ u64 ns;
+ u32 lo, hi;
+
+ lo = __raw_readl(®s->systime_lo);
+ hi = __raw_readl(®s->systime_hi);
+
+ ns = ((u64) hi) << 32;
+ ns |= lo;
+ ns <<= TICKS_NS_SHIFT;
+
+ return ns;
+}
+
+static void sys_time_write(struct ixp46x_ts_regs *regs, u64 ns)
+{
+ u32 hi, lo;
+
+ ns >>= TICKS_NS_SHIFT;
+ hi = ns >> 32;
+ lo = ns & 0xffffffff;
+
+ __raw_writel(lo, ®s->systime_lo);
+ __raw_writel(hi, ®s->systime_hi);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+ struct ixp_clock *ixp_clock = priv;
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+ struct ptp_clock_event event;
+ u32 ack = 0, lo, hi, val;
+
+ val = __raw_readl(®s->event);
+
+ if (val & TSER_SNS) {
+ ack |= TSER_SNS;
+ if (ixp_clock->exts0_enabled) {
+ hi = __raw_readl(®s->asms_hi);
+ lo = __raw_readl(®s->asms_lo);
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 0;
+ event.timestamp = ((u64) hi) << 32;
+ event.timestamp |= lo;
+ event.timestamp <<= TICKS_NS_SHIFT;
+ ptp_clock_event(ixp_clock->ptp_clock, &event);
+ }
+ }
+
+ if (val & TSER_SNM) {
+ ack |= TSER_SNM;
+ if (ixp_clock->exts1_enabled) {
+ hi = __raw_readl(®s->amms_hi);
+ lo = __raw_readl(®s->amms_lo);
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 1;
+ event.timestamp = ((u64) hi) << 32;
+ event.timestamp |= lo;
+ event.timestamp <<= TICKS_NS_SHIFT;
+ ptp_clock_event(ixp_clock->ptp_clock, &event);
+ }
+ }
+
+ if (val & TTIPEND)
+ ack |= TTIPEND; /* this bit seems to be always set */
+
+ if (ack) {
+ __raw_writel(ack, ®s->event);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ u64 adj;
+ u32 diff, addend;
+ int neg_adj = 0;
+ struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ addend = DEFAULT_ADDEND;
+ adj = addend;
+ adj *= ppb;
+ diff = div_u64(adj, 1000000000ULL);
+
+ addend = neg_adj ? addend - diff : addend + diff;
+
+ __raw_writel(addend, ®s->addend);
+
+ return 0;
+}
+
+static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ s64 now;
+ unsigned long flags;
+ struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ now = sys_time_read(regs);
+ now += delta;
+ sys_time_write(regs, now);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ return 0;
+}
+
+static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ u64 ns;
+ u32 remainder;
+ unsigned long flags;
+ struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ ns = sys_time_read(regs);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int ptp_ixp_settime(struct ptp_clock_info *ptp,
+ const struct timespec *ts)
+{
+ u64 ns;
+ unsigned long flags;
+ struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+ ns = ts->tv_sec * 1000000000ULL;
+ ns += ts->tv_nsec;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ sys_time_write(regs, ns);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ return 0;
+}
+
+static int ptp_ixp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_EXTTS:
+ switch (rq->extts.index) {
+ case 0:
+ ixp_clock->exts0_enabled = on ? 1 : 0;
+ break;
+ case 1:
+ ixp_clock->exts1_enabled = on ? 1 : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_ixp_caps = {
+ .owner = THIS_MODULE,
+ .name = "IXP46X timer",
+ .max_adj = 66666655,
+ .n_ext_ts = N_EXT_TS,
+ .pps = 0,
+ .adjfreq = ptp_ixp_adjfreq,
+ .adjtime = ptp_ixp_adjtime,
+ .gettime = ptp_ixp_gettime,
+ .settime = ptp_ixp_settime,
+ .enable = ptp_ixp_enable,
+};
+
+/* module operations */
+
+static struct ixp_clock ixp_clock;
+
+static int setup_interrupt(int gpio)
+{
+ int irq;
+
+ gpio_line_config(gpio, IXP4XX_GPIO_IN);
+
+ irq = gpio_to_irq(gpio);
+
+ if (NO_IRQ == irq)
+ return NO_IRQ;
+
+ if (set_irq_type(irq, IRQF_TRIGGER_FALLING)) {
+ pr_err("cannot set trigger type for irq %d\n", irq);
+ return NO_IRQ;
+ }
+
+ if (request_irq(irq, isr, 0, DRIVER, &ixp_clock)) {
+ pr_err("request_irq failed for irq %d\n", irq);
+ return NO_IRQ;
+ }
+
+ return irq;
+}
+
+static void __exit ptp_ixp_exit(void)
+{
+ free_irq(MASTER_IRQ, &ixp_clock);
+ free_irq(SLAVE_IRQ, &ixp_clock);
+ ptp_clock_unregister(ixp_clock.ptp_clock);
+}
+
+static int __init ptp_ixp_init(void)
+{
+ if (!cpu_is_ixp46x())
+ return -ENODEV;
+
+ ixp_clock.regs =
+ (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+ ixp_clock.caps = ptp_ixp_caps;
+
+ ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps);
+
+ if (IS_ERR(ixp_clock.ptp_clock))
+ return PTR_ERR(ixp_clock.ptp_clock);
+
+ __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
+ __raw_writel(1, &ixp_clock.regs->trgt_lo);
+ __raw_writel(0, &ixp_clock.regs->trgt_hi);
+ __raw_writel(TTIPEND, &ixp_clock.regs->event);
+
+ if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
+ pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
+ goto no_master;
+ }
+ if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
+ pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
+ goto no_slave;
+ }
+
+ return 0;
+no_slave:
+ free_irq(MASTER_IRQ, &ixp_clock);
+no_master:
+ ptp_clock_unregister(ixp_clock.ptp_clock);
+ return -ENODEV;
+}
+
+module_init(ptp_ixp_init);
+module_exit(ptp_ixp_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
+MODULE_LICENSE("GPL");
--
1.7.0.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