From patchwork Mon Jan 9 14:07:44 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Rusko X-Patchwork-Id: 135026 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AE932B6F6F for ; Tue, 10 Jan 2012 01:10:45 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1RkFtE-0001f5-GQ; Mon, 09 Jan 2012 14:07:56 +0000 Received: from fw2.prolan.hu ([193.68.50.107]) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1RkFt7-0001er-60 for linux-arm-kernel@lists.infradead.org; Mon, 09 Jan 2012 14:07:53 +0000 Received: from fw2.prolan.hu (localhost.localdomain [127.0.0.1]) by localhost (Postfix) with ESMTP id DE59326C084; Mon, 9 Jan 2012 15:07:46 +0100 (CET) Received: from saturn2.intranet.prolan.hu (webmail.prolan.hu [193.91.83.237]) by fw2.prolan.hu (Postfix) with ESMTP id D26AB26C0DE; Mon, 9 Jan 2012 15:07:46 +0100 (CET) Received: from [193.91.83.115] (193.91.83.115) by saturn2.intranet.prolan.hu (193.91.83.237) with Microsoft SMTP Server (TLS) id 14.1.355.2; Mon, 9 Jan 2012 15:07:46 +0100 Message-ID: <4F0AF4B0.4030403@prolan.hu> Date: Mon, 9 Jan 2012 15:07:44 +0100 From: Peter Rusko User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:8.0) Gecko/20111124 Thunderbird/8.0 MIME-Version: 1.0 To: Shawn Guo Subject: Re: MX28 fec clock frequency References: <4F06D643.6080602@prolan.hu> <20120108033238.GA19721@S2101-09.ap.freescale.net> In-Reply-To: <20120108033238.GA19721@S2101-09.ap.freescale.net> X-Originating-IP: [193.91.83.115] X-EsetResult: clean, is OK X-EsetId: 1FF26921224739024BB635 X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org On 2012-01-08 04:32, Shawn Guo wrote: > Hi Peter, > > On Fri, Jan 06, 2012 at 12:08:51PM +0100, Peter Rusko wrote: >> Hi all, >> >> I'm trying to get the fec clock frequency on the i.MX28 processor. I0 >> need it for a PTP clock and it seems that I get a wrong value: >> clk_get_rate returns 151578947. >> > > The ptp clock (CLK_ENET_TIME) was missed from the initial imx28 clock > support. The fec_clk which is one child of hbus_clk (151 MHz) is > taken as MDIO clock in fec driver. > I'm working on a ptp driver now and I can't deal with the clock. I've added the CLK_ENET_TIME clock and set it to 40MHz. It should give a 25ns period for the counter in ptp. However the ptp clock is too slow. If i increase the number (up to the maximum, which is 0x7f), the clock is still slower then normal. It's like the CLK_ENET_TIME frequency is less then I think. What can be the problem? I've attached a patch. Regards, diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c index 229ae34..4991e76 100644 --- a/arch/arm/mach-mxs/clock-mx28.c +++ b/arch/arm/mach-mxs/clock-mx28.c @@ -267,6 +267,7 @@ _CLK_GET_RATE1(gpmi_clk, GPMI) _CLK_GET_RATE1(lcdif_clk, DIS_LCDIF) _CLK_GET_RATE1(saif0_clk, SAIF0) _CLK_GET_RATE1(saif1_clk, SAIF1) +_CLK_GET_RATE1(enet_clk, ENET) #define _CLK_GET_RATE_STUB(name) \ static unsigned long name##_get_rate(struct clk *clk) \ @@ -523,6 +524,36 @@ _CLK_SET_PARENT_STUB(fec_clk) _CLK_SET_PARENT_STUB(can0_clk) _CLK_SET_PARENT_STUB(can1_clk) +static int enet_clk_set_parent(struct clk *clk, struct clk *parent) +{ + int src; + + do { + if (parent == clk->parent) + return 0; + if (parent == &ref_xtal_clk) { + src = 0x00; + break; + } + if (parent == &pll0_clk) { + src = 0x01; + break; + } + if (parent == &pll2_clk) { + src = 0x03; + break; + } + return -EINVAL; + } while (0); + + + __raw_writel(BF_CLKCTRL_ENET_TIME_SEL(src), + CLKCTRL_BASE_ADDR + HW_CLKCTRL_ENET); + clk->parent = parent; + + return 0; +} + /* * clk definition */ @@ -571,6 +602,12 @@ static struct clk usb1_clk = { .parent = &pll1_clk, }; +static struct clk enet_clk = { + .get_rate = enet_clk_get_rate, + .set_parent = enet_clk_set_parent, + .parent = &pll2_clk, +}; + #define _DEFINE_CLOCK(name, er, es, p) \ static struct clk name = { \ .enable_reg = CLKCTRL_BASE_ADDR + HW_CLKCTRL_##er, \ @@ -614,6 +651,8 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("duart", NULL, uart_clk) _REGISTER_CLOCK("imx28-fec.0", NULL, fec_clk) _REGISTER_CLOCK("imx28-fec.1", NULL, fec_clk) + _REGISTER_CLOCK("imx28-fec.0", "enet_clk", enet_clk) + _REGISTER_CLOCK("imx28-fec.1", "enet_clk", enet_clk) _REGISTER_CLOCK("mxs-auart.0", NULL, uart_clk) _REGISTER_CLOCK("mxs-auart.1", NULL, uart_clk) _REGISTER_CLOCK("mxs-auart.2", NULL, uart_clk) @@ -672,6 +711,11 @@ static int clk_misc_init(void) saif1_clk.parent = (reg & BM_CLKCTRL_CLKSEQ_BYPASS_SAIF1) ? &ref_xtal_clk : &pll0_clk; + /* ENET_CLK setup */ + enet_clk.set_parent(&enet_clk, &pll0_clk); + __raw_writel(BF_CLKCTRL_ENET_DIV(12), + CLKCTRL_BASE_ADDR + HW_CLKCTRL_ENET); + /* Use int div over frac when both are available */ __raw_writel(BM_CLKCTRL_CPU_DIV_XTAL_FRAC_EN, CLKCTRL_BASE_ADDR + HW_CLKCTRL_CPU_CLR); diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index 1752488..4b2af6d 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_FEC) += fec.o +obj-$(CONFIG_PTP_1588_CLOCK_FEC) += fec_ptp.o obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 1124ce0..4be9cea 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -49,6 +49,8 @@ #include #include +#include + #include #ifndef CONFIG_ARM @@ -57,6 +59,9 @@ #endif #include "fec.h" +#ifdef CONFIG_PTP_1588_CLOCK_FEC +#include "fec_ptp.h" +#endif #if defined(CONFIG_ARM) #define FEC_ALIGNMENT 0xf @@ -138,25 +143,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #endif #endif /* CONFIG_M5272 */ -/* The number of Tx and Rx buffers. These are allocated from the page - * pool. The code may assume these are power of two, so it it best - * to keep them that size. - * We don't need to allocate pages for the transmitter. We just use - * the skbuffer directly. - */ -#define FEC_ENET_RX_PAGES 8 -#define FEC_ENET_RX_FRSIZE 2048 -#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) -#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) -#define FEC_ENET_TX_FRSIZE 2048 -#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) -#define TX_RING_SIZE 16 /* Must be power of two */ -#define TX_RING_MOD_MASK 15 /* for this to work */ - -#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE) -#error "FEC: descriptor ring size constants too large" -#endif - /* Interrupt events/masks. */ #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ #define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ @@ -177,9 +163,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define PKT_MINBUF_SIZE 64 #define PKT_MAXBLR_SIZE 1520 -/* This device has up to three irqs on some platforms */ -#define FEC_IRQ_NUM 3 - /* * The 5270/5271/5280/5282/532x RX control register also contains maximum frame * size bits. Other FEC hardware does not, so we need to take that into @@ -192,59 +175,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define OPT_FRAME_SIZE 0 #endif -/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and - * tx_bd_base always point to the base of the buffer descriptors. The - * cur_rx and cur_tx point to the currently available buffer. - * The dirty_tx tracks the current buffer that is being sent by the - * controller. The cur_tx and dirty_tx are equal under both completely - * empty and completely full conditions. The empty/ready indicator in - * the buffer descriptor determines the actual condition. - */ -struct fec_enet_private { - /* Hardware registers of the FEC device */ - void __iomem *hwp; - - struct net_device *netdev; - - struct clk *clk; - - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - unsigned char *tx_bounce[TX_RING_SIZE]; - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - ushort skb_cur; - ushort skb_dirty; - - /* CPM dual port RAM relative addresses */ - dma_addr_t bd_dma; - /* Address of Rx and Tx buffers */ - struct bufdesc *rx_bd_base; - struct bufdesc *tx_bd_base; - /* The next free ring entry */ - struct bufdesc *cur_rx, *cur_tx; - /* The ring entries to be free()ed */ - struct bufdesc *dirty_tx; - - uint tx_full; - /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ - spinlock_t hw_lock; - - struct platform_device *pdev; - - int opened; - - /* Phylib and MDIO interface */ - struct mii_bus *mii_bus; - struct phy_device *phy_dev; - int mii_timeout; - uint phy_speed; - phy_interface_t phy_interface; - int link; - int full_duplex; - struct completion mdio_done; - int irq[FEC_IRQ_NUM]; -}; - /* FEC MII MMFR bits definition */ #define FEC_MMFR_ST (1 << 30) #define FEC_MMFR_OP_READ (2 << 28) @@ -385,9 +315,11 @@ fec_restart(struct net_device *ndev, int duplex) u32 rcntl = OPT_FRAME_SIZE | 0x04; u32 ecntl = 0x2; /* ETHEREN */ + mxs_ptp_store(fep); // Store ptp registers before reset /* Whack a reset. We should wait for this. */ writel(1, fep->hwp + FEC_ECNTRL); udelay(10); + mxs_ptp_restore(fep); // restore registers /* * enet-mac reset will reset mac address registers too, @@ -524,9 +456,11 @@ fec_stop(struct net_device *ndev) printk("fec_stop : Graceful transmit stop did not complete !\n"); } + mxs_ptp_store(fep); // Store ptp registers before reset /* Whack a reset. We should wait for this. */ writel(1, fep->hwp + FEC_ECNTRL); udelay(10); + mxs_ptp_restore(fep); // restore registers writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); @@ -773,6 +707,14 @@ fec_enet_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; complete(&fep->mdio_done); } + +#ifdef CONFIG_PTP_1588_CLOCK_FEC + /* PTP interrupts */ + if (int_events & FEC_ENET_PTP_IRMASK) { + ret = mxs_ptp_interrupt(int_events & + FEC_ENET_PTP_IRMASK, fep); + } +#endif } while (int_events); return ret; @@ -1521,6 +1463,7 @@ fec_probe(struct platform_device *pdev) int i, irq, ret = 0; struct resource *r; const struct of_device_id *of_id; +struct clk *pclk; of_id = of_match_device(fec_dt_ids, &pdev->dev); if (of_id) @@ -1605,6 +1548,8 @@ fec_probe(struct platform_device *pdev) if (ret) goto failed_register; + mxs_ptp_init(fep); + return 0; failed_register: diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 8b2c6d7..2493ff5 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -13,6 +13,16 @@ #define FEC_H /****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) @@ -143,6 +153,99 @@ struct bufdesc { #define BD_ENET_TX_CSL ((ushort)0x0001) #define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it it best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ + +#define FEC_ENET_RX_PAGES 8 +#define FEC_ENET_RX_FRSIZE 2048 +#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +#define FEC_ENET_TX_FRSIZE 2048 +#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) +#define TX_RING_SIZE 16 /* Must be power of two */ +#define TX_RING_MOD_MASK 15 /* for this to work */ + +#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE) +#error "FEC: descriptor ring size constants too large" +#endif + +/* This device has up to three irqs on some platforms */ +#define FEC_IRQ_NUM 3 + +/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct fec_enet_private { + /* Hardware registers of the FEC device */ + void __iomem *hwp; + + struct net_device *netdev; + + struct clk *clk; + + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + unsigned char *tx_bounce[TX_RING_SIZE]; + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses */ + dma_addr_t bd_dma; + /* Address of Rx and Tx buffers */ + struct bufdesc *rx_bd_base; + struct bufdesc *tx_bd_base; + /* The next free ring entry */ + struct bufdesc *cur_rx, *cur_tx; + /* The ring entries to be free()ed */ + struct bufdesc *dirty_tx; + + uint tx_full; + /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ + spinlock_t hw_lock; + + struct platform_device *pdev; + + int opened; + + /* Phylib and MDIO interface */ + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + int mii_timeout; + uint phy_speed; + phy_interface_t phy_interface; + int link; + int full_duplex; + struct completion mdio_done; + int irq[FEC_IRQ_NUM]; + +#ifdef CONFIG_PTP_1588_CLOCK_FEC + /* PTP support */ + struct clk *enet_clk; + + struct ptp_clock *ptp_clock; + struct ptp_clock_info clock_info; + + // Software clock representing seconds + long ptp_sec; + + u32 ptp_rate; + int ptp_pps_enabled; + +#define PTP_REG_SIZE 7 + u32 reg_store[PTP_REG_SIZE]; +#endif +}; /****************************************************************************/ #endif /* FEC_H */ + diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c new file mode 100644 index 0000000..95998e3 --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include + +#include "fec.h" +#include "fec_ptp.h" + +#define ENET_MAC_ATIME_CTRL 0x400 +#define BP_CAPTURE 11 +#define BP_EVT_PERIOD_RST 5 +#define BP_EVT_PERIOD_ENA 4 +#define BP_EVT_OFFSET_RST 3 +#define BP_EVT_OFFSET_ENA 2 +#define BP_ENABLE 0 + +#define ENET_MAC_ATIME 0x404 + +#define ENET_MAC_ATIME_OFFSET 0x408 + +#define ENET_MAC_ATIME_CORR 0x410 + +#define ENET_MAC_ATIME_INC 0x414 +#define BM_ATIME_INC_CORR 0x00007F00 +#define BP_ATIME_INC_CORR 8 +#define BF_ATIME_INC_CORR(v) \ + (((v) << BP_ATIME_INC_CORR) & BM_ATIME_INC_CORR) +#define BM_ATIME_INC 0x0000007F +#define BP_ATIME_INC 0 +#define BF_ATIME_INC(v) \ + (((v) << BP_ATIME_INC) & BM_ATIME_INC) + +#undef to_fec_enet_private +#define to_fec_enet_private(i) container_of(i, struct fec_enet_private, \ + clock_info) + +u32 mxs_ptp_capture(struct fec_enet_private *fep) { + int i; + + writel(1<hwp + ENET_MAC_ATIME_CTRL); + for (i=100000; i && (readl(fep->hwp + ENET_MAC_ATIME_CTRL) & (1<hwp + ENET_MAC_ATIME); +} + +int mxs_ptp_interrupt(u32 reg, struct fec_enet_private *fep) +{ + if (reg & FEC_ENET_TS_AVAIL) { + } + if (reg & FEC_ENET_TS_TIMER) { + fep->ptp_sec++; + if (fep->ptp_pps_enabled) + { + struct ptp_clock_event event; + + event.type = PTP_CLOCK_PPS; + + ptp_clock_event(fep->ptp_clock, &event); + } + } + + return IRQ_HANDLED; +} + +static void calc_atime(unsigned long new_rate, u32 *atime_corr, u32 *atime_norm_inc, u32 *atime_corr_inc) +{ + // TODO: complete calculation + *atime_corr = 0; + *atime_corr_inc = 0; + *atime_norm_inc = DIV_ROUND_CLOSEST (NSEC_PER_SEC, new_rate); +} + +static int mxs_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) +{ + struct fec_enet_private *fep = to_fec_enet_private(ptp); + u32 atime_corr, atime_norm_inc, atime_corr_inc; + u32 reg; + + fep->ptp_rate += delta; + calc_atime(clk_get_rate(fep->enet_clk) + fep->ptp_rate, &atime_corr, &atime_norm_inc, &atime_corr_inc); + + writel(atime_corr, fep->hwp + ENET_MAC_ATIME_CORR); + writel(BF_ATIME_INC(atime_norm_inc) | BF_ATIME_INC_CORR(atime_corr_inc), + fep->hwp + ENET_MAC_ATIME_INC); + + return 0; +} + +static int mxs_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct fec_enet_private *fep = to_fec_enet_private(ptp); + u32 reg; + + // for now we only allow negative change + if (delta >= NSEC_PER_SEC || delta < 0) // 1 sec + return -EINVAL; + + writel(NSEC_PER_SEC-delta, fep->hwp + ENET_MAC_ATIME_OFFSET); + + reg = readl(fep->hwp + ENET_MAC_ATIME_CTRL); + writel(reg | (1<hwp + ENET_MAC_ATIME_CTRL); + + return 0; +} + + +static int mxs_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts) +{ + struct fec_enet_private *fep = to_fec_enet_private(ptp); + u32 reg; + + reg = mxs_ptp_capture(fep); + + ts->tv_nsec = reg; + ts->tv_sec = fep->ptp_sec; + + return 0; +} + +static int mxs_ptp_settime(struct ptp_clock_info *ptp, const struct timespec *ts) +{ + struct fec_enet_private *fep = to_fec_enet_private(ptp); + + fep->ptp_sec = ts->tv_sec; + writel(ts->tv_nsec, fep->hwp + ENET_MAC_ATIME); + + return 0; +} + +static int mxs_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *request, int on) +{ + struct fec_enet_private *fep = to_fec_enet_private(ptp); + + switch (request->type) { + case PTP_CLK_REQ_PPS: + fep->ptp_pps_enabled = on; + return 0; + default: + break; + } + + return -EOPNOTSUPP; +} + +static void mxs_ptp_registers_set(struct fec_enet_private *fep) +{ + u32 reg; + + reg = readl(fep->hwp + FEC_ECNTRL); + writel(reg | 1<<4, fep->hwp + FEC_ECNTRL); + + reg = readl(fep->hwp + FEC_IMASK); + writel(reg | (FEC_ENET_TS_TIMER), fep->hwp + FEC_IMASK); +}; + +static struct ptp_clock_info mxs_ptp_info = { + .owner = THIS_MODULE, + .name = "mxs-ptpclock", + .max_adj = NSEC_PER_SEC, + .n_alarm = 1, + .n_ext_ts = 4, + .n_per_out = 4, + .pps = 1, + + .adjfreq = mxs_ptp_adjfreq, + .adjtime = mxs_ptp_adjtime, + .gettime = mxs_ptp_gettime, + .settime = mxs_ptp_settime, + .enable = mxs_ptp_enable, +}; + +int mxs_ptp_init(struct fec_enet_private *fep) +{ + struct ptp_clock *clk; + u32 atime_corr, atime_norm_inc, atime_corr_inc; + + fep->enet_clk = clk_get(&fep->pdev->dev, "enet_clk"); + + mxs_ptp_registers_set(fep); + + calc_atime(clk_get_rate(fep->enet_clk) + fep->ptp_rate, &atime_corr, &atime_norm_inc, &atime_corr_inc); + writel(BF_ATIME_INC_CORR(atime_corr) | BF_ATIME_INC(atime_norm_inc), fep->hwp + ENET_MAC_ATIME_INC); + + writel((1<hwp + ENET_MAC_ATIME_CTRL); + + fep->clock_info = mxs_ptp_info; + + clk = ptp_clock_register(&fep->clock_info); + if (IS_ERR(clk)) + return PTR_ERR(clk); + fep->ptp_clock = clk; + + return 0; +} + + +void mxs_ptp_store(struct fec_enet_private *fep) +{ + int i; + + for (i=0; ireg_store[i] = readl(fep->hwp + ENET_MAC_ATIME_CTRL + i*4); + } +} + +void mxs_ptp_restore(struct fec_enet_private *fep) +{ + int i; + + mxs_ptp_registers_set(fep); + + for (i=0; ireg_store[i], fep->hwp + ENET_MAC_ATIME_CTRL + i*4); + } +} diff --git a/drivers/net/ethernet/freescale/fec_ptp.h b/drivers/net/ethernet/freescale/fec_ptp.h new file mode 100644 index 0000000..0d41a98 --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_ptp.h @@ -0,0 +1,15 @@ +#ifndef FEC_PTP_H +#define FEC_PTP_H + +#include + +#define FEC_ENET_TS_AVAIL ((uint)0x00010000) /* Timestamp available */ +#define FEC_ENET_TS_TIMER ((uint)0x00008000) /* Timer interrupt */ +#define FEC_ENET_PTP_IRMASK (FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER) + +int mxs_ptp_interrupt(u32 reg, struct fec_enet_private *fep); +int mxs_ptp_init(struct fec_enet_private *fep); +void mxs_ptp_store(struct fec_enet_private *fep); +void mxs_ptp_restore(struct fec_enet_private *fep); + +#endif diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index 68d7201..af7b95d 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -72,4 +72,17 @@ config DP83640_PHY In order for this to work, your MAC driver must also implement the skb_tx_timetamp() function. +config PTP_1588_CLOCK_FEC + tristate "Freescale FEC as PTP clock" + depends on PTP_1588_CLOCK + depends on FEC + help + This driver adds support for using the FEC 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 fec_ptp. + endmenu