aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h78
-rw-r--r--drivers/net/arm/ixp4xx_eth.c195
-rw-r--r--drivers/ptp/Kconfig13
-rw-r--r--drivers/ptp/Makefile1
-rw-r--r--drivers/ptp/ptp_ixp46x.c332
5 files changed, 616 insertions, 3 deletions
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 000000000000..292d55ed2113
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
@@ -0,0 +1,78 @@
1/*
2 * PTP 1588 clock using the IXP46X
3 *
4 * Copyright (C) 2010 OMICRON electronics GmbH
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#ifndef _IXP46X_TS_H_
22#define _IXP46X_TS_H_
23
24#define DEFAULT_ADDEND 0xF0000029
25#define TICKS_NS_SHIFT 4
26
27struct ixp46x_channel_ctl {
28 u32 ch_control; /* 0x40 Time Synchronization Channel Control */
29 u32 ch_event; /* 0x44 Time Synchronization Channel Event */
30 u32 tx_snap_lo; /* 0x48 Transmit Snapshot Low Register */
31 u32 tx_snap_hi; /* 0x4C Transmit Snapshot High Register */
32 u32 rx_snap_lo; /* 0x50 Receive Snapshot Low Register */
33 u32 rx_snap_hi; /* 0x54 Receive Snapshot High Register */
34 u32 src_uuid_lo; /* 0x58 Source UUID0 Low Register */
35 u32 src_uuid_hi; /* 0x5C Sequence Identifier/Source UUID0 High */
36};
37
38struct ixp46x_ts_regs {
39 u32 control; /* 0x00 Time Sync Control Register */
40 u32 event; /* 0x04 Time Sync Event Register */
41 u32 addend; /* 0x08 Time Sync Addend Register */
42 u32 accum; /* 0x0C Time Sync Accumulator Register */
43 u32 test; /* 0x10 Time Sync Test Register */
44 u32 unused; /* 0x14 */
45 u32 rsystime_lo; /* 0x18 RawSystemTime_Low Register */
46 u32 rsystime_hi; /* 0x1C RawSystemTime_High Register */
47 u32 systime_lo; /* 0x20 SystemTime_Low Register */
48 u32 systime_hi; /* 0x24 SystemTime_High Register */
49 u32 trgt_lo; /* 0x28 TargetTime_Low Register */
50 u32 trgt_hi; /* 0x2C TargetTime_High Register */
51 u32 asms_lo; /* 0x30 Auxiliary Slave Mode Snapshot Low */
52 u32 asms_hi; /* 0x34 Auxiliary Slave Mode Snapshot High */
53 u32 amms_lo; /* 0x38 Auxiliary Master Mode Snapshot Low */
54 u32 amms_hi; /* 0x3C Auxiliary Master Mode Snapshot High */
55
56 struct ixp46x_channel_ctl channel[3];
57};
58
59/* 0x00 Time Sync Control Register Bits */
60#define TSCR_AMM (1<<3)
61#define TSCR_ASM (1<<2)
62#define TSCR_TTM (1<<1)
63#define TSCR_RST (1<<0)
64
65/* 0x04 Time Sync Event Register Bits */
66#define TSER_SNM (1<<3)
67#define TSER_SNS (1<<2)
68#define TTIPEND (1<<1)
69
70/* 0x40 Time Synchronization Channel Control Register Bits */
71#define MASTER_MODE (1<<0)
72#define TIMESTAMP_ALL (1<<1)
73
74/* 0x44 Time Synchronization Channel Event Register Bits */
75#define TX_SNAPSHOT_LOCKED (1<<0)
76#define RX_SNAPSHOT_LOCKED (1<<1)
77
78#endif
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 9eb9b98a7ae3..de51e8453c13 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -30,9 +30,12 @@
30#include <linux/etherdevice.h> 30#include <linux/etherdevice.h>
31#include <linux/io.h> 31#include <linux/io.h>
32#include <linux/kernel.h> 32#include <linux/kernel.h>
33#include <linux/net_tstamp.h>
33#include <linux/phy.h> 34#include <linux/phy.h>
34#include <linux/platform_device.h> 35#include <linux/platform_device.h>
36#include <linux/ptp_classify.h>
35#include <linux/slab.h> 37#include <linux/slab.h>
38#include <mach/ixp46x_ts.h>
36#include <mach/npe.h> 39#include <mach/npe.h>
37#include <mach/qmgr.h> 40#include <mach/qmgr.h>
38 41
@@ -67,6 +70,10 @@
67#define RXFREE_QUEUE(port_id) (NPE_ID(port_id) + 26) 70#define RXFREE_QUEUE(port_id) (NPE_ID(port_id) + 26)
68#define TXDONE_QUEUE 31 71#define TXDONE_QUEUE 31
69 72
73#define PTP_SLAVE_MODE 1
74#define PTP_MASTER_MODE 2
75#define PORT2CHANNEL(p) NPE_ID(p->id)
76
70/* TX Control Registers */ 77/* TX Control Registers */
71#define TX_CNTRL0_TX_EN 0x01 78#define TX_CNTRL0_TX_EN 0x01
72#define TX_CNTRL0_HALFDUPLEX 0x02 79#define TX_CNTRL0_HALFDUPLEX 0x02
@@ -171,6 +178,8 @@ struct port {
171 int id; /* logical port ID */ 178 int id; /* logical port ID */
172 int speed, duplex; 179 int speed, duplex;
173 u8 firmware[4]; 180 u8 firmware[4];
181 int hwts_tx_en;
182 int hwts_rx_en;
174}; 183};
175 184
176/* NPE message structure */ 185/* NPE message structure */
@@ -246,6 +255,172 @@ static int ports_open;
246static struct port *npe_port_tab[MAX_NPES]; 255static struct port *npe_port_tab[MAX_NPES];
247static struct dma_pool *dma_pool; 256static struct dma_pool *dma_pool;
248 257
258static struct sock_filter ptp_filter[] = {
259 PTP_FILTER
260};
261
262static int ixp_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid)
263{
264 u8 *data = skb->data;
265 unsigned int offset;
266 u16 *hi, *id;
267 u32 lo;
268
269 if (sk_run_filter(skb, ptp_filter) != PTP_CLASS_V1_IPV4)
270 return 0;
271
272 offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
273
274 if (skb->len < offset + OFF_PTP_SEQUENCE_ID + sizeof(seqid))
275 return 0;
276
277 hi = (u16 *)(data + offset + OFF_PTP_SOURCE_UUID);
278 id = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
279
280 memcpy(&lo, &hi[1], sizeof(lo));
281
282 return (uid_hi == ntohs(*hi) &&
283 uid_lo == ntohl(lo) &&
284 seqid == ntohs(*id));
285}
286
287static void ixp_rx_timestamp(struct port *port, struct sk_buff *skb)
288{
289 struct skb_shared_hwtstamps *shhwtstamps;
290 struct ixp46x_ts_regs *regs;
291 u64 ns;
292 u32 ch, hi, lo, val;
293 u16 uid, seq;
294
295 if (!port->hwts_rx_en)
296 return;
297
298 ch = PORT2CHANNEL(port);
299
300 regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
301
302 val = __raw_readl(&regs->channel[ch].ch_event);
303
304 if (!(val & RX_SNAPSHOT_LOCKED))
305 return;
306
307 lo = __raw_readl(&regs->channel[ch].src_uuid_lo);
308 hi = __raw_readl(&regs->channel[ch].src_uuid_hi);
309
310 uid = hi & 0xffff;
311 seq = (hi >> 16) & 0xffff;
312
313 if (!ixp_ptp_match(skb, htons(uid), htonl(lo), htons(seq)))
314 goto out;
315
316 lo = __raw_readl(&regs->channel[ch].rx_snap_lo);
317 hi = __raw_readl(&regs->channel[ch].rx_snap_hi);
318 ns = ((u64) hi) << 32;
319 ns |= lo;
320 ns <<= TICKS_NS_SHIFT;
321
322 shhwtstamps = skb_hwtstamps(skb);
323 memset(shhwtstamps, 0, sizeof(*shhwtstamps));
324 shhwtstamps->hwtstamp = ns_to_ktime(ns);
325out:
326 __raw_writel(RX_SNAPSHOT_LOCKED, &regs->channel[ch].ch_event);
327}
328
329static void ixp_tx_timestamp(struct port *port, struct sk_buff *skb)
330{
331 struct skb_shared_hwtstamps shhwtstamps;
332 struct ixp46x_ts_regs *regs;
333 struct skb_shared_info *shtx;
334 u64 ns;
335 u32 ch, cnt, hi, lo, val;
336
337 shtx = skb_shinfo(skb);
338 if (unlikely(shtx->tx_flags & SKBTX_HW_TSTAMP && port->hwts_tx_en))
339 shtx->tx_flags |= SKBTX_IN_PROGRESS;
340 else
341 return;
342
343 ch = PORT2CHANNEL(port);
344
345 regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
346
347 /*
348 * This really stinks, but we have to poll for the Tx time stamp.
349 * Usually, the time stamp is ready after 4 to 6 microseconds.
350 */
351 for (cnt = 0; cnt < 100; cnt++) {
352 val = __raw_readl(&regs->channel[ch].ch_event);
353 if (val & TX_SNAPSHOT_LOCKED)
354 break;
355 udelay(1);
356 }
357 if (!(val & TX_SNAPSHOT_LOCKED)) {
358 shtx->tx_flags &= ~SKBTX_IN_PROGRESS;
359 return;
360 }
361
362 lo = __raw_readl(&regs->channel[ch].tx_snap_lo);
363 hi = __raw_readl(&regs->channel[ch].tx_snap_hi);
364 ns = ((u64) hi) << 32;
365 ns |= lo;
366 ns <<= TICKS_NS_SHIFT;
367
368 memset(&shhwtstamps, 0, sizeof(shhwtstamps));
369 shhwtstamps.hwtstamp = ns_to_ktime(ns);
370 skb_tstamp_tx(skb, &shhwtstamps);
371
372 __raw_writel(TX_SNAPSHOT_LOCKED, &regs->channel[ch].ch_event);
373}
374
375static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
376{
377 struct hwtstamp_config cfg;
378 struct ixp46x_ts_regs *regs;
379 struct port *port = netdev_priv(netdev);
380 int ch;
381
382 if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
383 return -EFAULT;
384
385 if (cfg.flags) /* reserved for future extensions */
386 return -EINVAL;
387
388 ch = PORT2CHANNEL(port);
389 regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
390
391 switch (cfg.tx_type) {
392 case HWTSTAMP_TX_OFF:
393 port->hwts_tx_en = 0;
394 break;
395 case HWTSTAMP_TX_ON:
396 port->hwts_tx_en = 1;
397 break;
398 default:
399 return -ERANGE;
400 }
401
402 switch (cfg.rx_filter) {
403 case HWTSTAMP_FILTER_NONE:
404 port->hwts_rx_en = 0;
405 break;
406 case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
407 port->hwts_rx_en = PTP_SLAVE_MODE;
408 __raw_writel(0, &regs->channel[ch].ch_control);
409 break;
410 case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
411 port->hwts_rx_en = PTP_MASTER_MODE;
412 __raw_writel(MASTER_MODE, &regs->channel[ch].ch_control);
413 break;
414 default:
415 return -ERANGE;
416 }
417
418 /* Clear out any old time stamps. */
419 __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
420 &regs->channel[ch].ch_event);
421
422 return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
423}
249 424
250static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location, 425static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location,
251 int write, u16 cmd) 426 int write, u16 cmd)
@@ -573,6 +748,7 @@ static int eth_poll(struct napi_struct *napi, int budget)
573 748
574 debug_pkt(dev, "eth_poll", skb->data, skb->len); 749 debug_pkt(dev, "eth_poll", skb->data, skb->len);
575 750
751 ixp_rx_timestamp(port, skb);
576 skb->protocol = eth_type_trans(skb, dev); 752 skb->protocol = eth_type_trans(skb, dev);
577 dev->stats.rx_packets++; 753 dev->stats.rx_packets++;
578 dev->stats.rx_bytes += skb->len; 754 dev->stats.rx_bytes += skb->len;
@@ -679,14 +855,12 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
679 return NETDEV_TX_OK; 855 return NETDEV_TX_OK;
680 } 856 }
681 memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4); 857 memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4);
682 dev_kfree_skb(skb);
683#endif 858#endif
684 859
685 phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE); 860 phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE);
686 if (dma_mapping_error(&dev->dev, phys)) { 861 if (dma_mapping_error(&dev->dev, phys)) {
687#ifdef __ARMEB__
688 dev_kfree_skb(skb); 862 dev_kfree_skb(skb);
689#else 863#ifndef __ARMEB__
690 kfree(mem); 864 kfree(mem);
691#endif 865#endif
692 dev->stats.tx_dropped++; 866 dev->stats.tx_dropped++;
@@ -728,6 +902,13 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
728#if DEBUG_TX 902#if DEBUG_TX
729 printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name); 903 printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name);
730#endif 904#endif
905
906 ixp_tx_timestamp(port, skb);
907 skb_tx_timestamp(skb);
908
909#ifndef __ARMEB__
910 dev_kfree_skb(skb);
911#endif
731 return NETDEV_TX_OK; 912 return NETDEV_TX_OK;
732} 913}
733 914
@@ -783,6 +964,9 @@ static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
783 if (!netif_running(dev)) 964 if (!netif_running(dev))
784 return -EINVAL; 965 return -EINVAL;
785 966
967 if (cpu_is_ixp46x() && cmd == SIOCSHWTSTAMP)
968 return hwtstamp_ioctl(dev, req, cmd);
969
786 return phy_mii_ioctl(port->phydev, req, cmd); 970 return phy_mii_ioctl(port->phydev, req, cmd);
787} 971}
788 972
@@ -1171,6 +1355,11 @@ static int __devinit eth_init_one(struct platform_device *pdev)
1171 char phy_id[MII_BUS_ID_SIZE + 3]; 1355 char phy_id[MII_BUS_ID_SIZE + 3];
1172 int err; 1356 int err;
1173 1357
1358 if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
1359 pr_err("ixp4xx_eth: bad ptp filter\n");
1360 return -EINVAL;
1361 }
1362
1174 if (!(dev = alloc_etherdev(sizeof(struct port)))) 1363 if (!(dev = alloc_etherdev(sizeof(struct port))))
1175 return -ENOMEM; 1364 return -ENOMEM;
1176 1365
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 12eb8447c26b..fcfafd091fdd 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -40,4 +40,17 @@ config PTP_1588_CLOCK_GIANFAR
40 To compile this driver as a module, choose M here: the module 40 To compile this driver as a module, choose M here: the module
41 will be called gianfar_ptp. 41 will be called gianfar_ptp.
42 42
43config PTP_1588_CLOCK_IXP46X
44 tristate "Intel IXP46x as PTP clock"
45 depends on PTP_1588_CLOCK
46 depends on IXP4XX_ETH
47 help
48 This driver adds support for using the IXP46X as a PTP
49 clock. This clock is only useful if your PTP programs are
50 getting hardware time stamps on the PTP Ethernet packets
51 using the SO_TIMESTAMPING API.
52
53 To compile this driver as a module, choose M here: the module
54 will be called ptp_ixp46x.
55
43endmenu 56endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 480e2afdc999..f6933e83de72 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -4,3 +4,4 @@
4 4
5ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o 5ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o
6obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o 6obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
7obj-$(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 000000000000..803d665b15ef
--- /dev/null
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -0,0 +1,332 @@
1/*
2 * PTP 1588 clock using the IXP46X
3 *
4 * Copyright (C) 2010 OMICRON electronics GmbH
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20#include <linux/device.h>
21#include <linux/err.h>
22#include <linux/gpio.h>
23#include <linux/init.h>
24#include <linux/interrupt.h>
25#include <linux/io.h>
26#include <linux/irq.h>
27#include <linux/kernel.h>
28#include <linux/module.h>
29
30#include <linux/ptp_clock_kernel.h>
31#include <mach/ixp46x_ts.h>
32
33#define DRIVER "ptp_ixp46x"
34#define N_EXT_TS 2
35#define MASTER_GPIO 8
36#define MASTER_IRQ 25
37#define SLAVE_GPIO 7
38#define SLAVE_IRQ 24
39
40struct ixp_clock {
41 struct ixp46x_ts_regs *regs;
42 struct ptp_clock *ptp_clock;
43 struct ptp_clock_info caps;
44 int exts0_enabled;
45 int exts1_enabled;
46};
47
48DEFINE_SPINLOCK(register_lock);
49
50/*
51 * Register access functions
52 */
53
54static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)
55{
56 u64 ns;
57 u32 lo, hi;
58
59 lo = __raw_readl(&regs->systime_lo);
60 hi = __raw_readl(&regs->systime_hi);
61
62 ns = ((u64) hi) << 32;
63 ns |= lo;
64 ns <<= TICKS_NS_SHIFT;
65
66 return ns;
67}
68
69static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)
70{
71 u32 hi, lo;
72
73 ns >>= TICKS_NS_SHIFT;
74 hi = ns >> 32;
75 lo = ns & 0xffffffff;
76
77 __raw_writel(lo, &regs->systime_lo);
78 __raw_writel(hi, &regs->systime_hi);
79}
80
81/*
82 * Interrupt service routine
83 */
84
85static irqreturn_t isr(int irq, void *priv)
86{
87 struct ixp_clock *ixp_clock = priv;
88 struct ixp46x_ts_regs *regs = ixp_clock->regs;
89 struct ptp_clock_event event;
90 u32 ack = 0, lo, hi, val;
91
92 val = __raw_readl(&regs->event);
93
94 if (val & TSER_SNS) {
95 ack |= TSER_SNS;
96 if (ixp_clock->exts0_enabled) {
97 hi = __raw_readl(&regs->asms_hi);
98 lo = __raw_readl(&regs->asms_lo);
99 event.type = PTP_CLOCK_EXTTS;
100 event.index = 0;
101 event.timestamp = ((u64) hi) << 32;
102 event.timestamp |= lo;
103 event.timestamp <<= TICKS_NS_SHIFT;
104 ptp_clock_event(ixp_clock->ptp_clock, &event);
105 }
106 }
107
108 if (val & TSER_SNM) {
109 ack |= TSER_SNM;
110 if (ixp_clock->exts1_enabled) {
111 hi = __raw_readl(&regs->amms_hi);
112 lo = __raw_readl(&regs->amms_lo);
113 event.type = PTP_CLOCK_EXTTS;
114 event.index = 1;
115 event.timestamp = ((u64) hi) << 32;
116 event.timestamp |= lo;
117 event.timestamp <<= TICKS_NS_SHIFT;
118 ptp_clock_event(ixp_clock->ptp_clock, &event);
119 }
120 }
121
122 if (val & TTIPEND)
123 ack |= TTIPEND; /* this bit seems to be always set */
124
125 if (ack) {
126 __raw_writel(ack, &regs->event);
127 return IRQ_HANDLED;
128 } else
129 return IRQ_NONE;
130}
131
132/*
133 * PTP clock operations
134 */
135
136static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
137{
138 u64 adj;
139 u32 diff, addend;
140 int neg_adj = 0;
141 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
142 struct ixp46x_ts_regs *regs = ixp_clock->regs;
143
144 if (ppb < 0) {
145 neg_adj = 1;
146 ppb = -ppb;
147 }
148 addend = DEFAULT_ADDEND;
149 adj = addend;
150 adj *= ppb;
151 diff = div_u64(adj, 1000000000ULL);
152
153 addend = neg_adj ? addend - diff : addend + diff;
154
155 __raw_writel(addend, &regs->addend);
156
157 return 0;
158}
159
160static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
161{
162 s64 now;
163 unsigned long flags;
164 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
165 struct ixp46x_ts_regs *regs = ixp_clock->regs;
166
167 spin_lock_irqsave(&register_lock, flags);
168
169 now = ixp_systime_read(regs);
170 now += delta;
171 ixp_systime_write(regs, now);
172
173 spin_unlock_irqrestore(&register_lock, flags);
174
175 return 0;
176}
177
178static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
179{
180 u64 ns;
181 u32 remainder;
182 unsigned long flags;
183 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
184 struct ixp46x_ts_regs *regs = ixp_clock->regs;
185
186 spin_lock_irqsave(&register_lock, flags);
187
188 ns = ixp_systime_read(regs);
189
190 spin_unlock_irqrestore(&register_lock, flags);
191
192 ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
193 ts->tv_nsec = remainder;
194 return 0;
195}
196
197static int ptp_ixp_settime(struct ptp_clock_info *ptp,
198 const struct timespec *ts)
199{
200 u64 ns;
201 unsigned long flags;
202 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
203 struct ixp46x_ts_regs *regs = ixp_clock->regs;
204
205 ns = ts->tv_sec * 1000000000ULL;
206 ns += ts->tv_nsec;
207
208 spin_lock_irqsave(&register_lock, flags);
209
210 ixp_systime_write(regs, ns);
211
212 spin_unlock_irqrestore(&register_lock, flags);
213
214 return 0;
215}
216
217static int ptp_ixp_enable(struct ptp_clock_info *ptp,
218 struct ptp_clock_request *rq, int on)
219{
220 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
221
222 switch (rq->type) {
223 case PTP_CLK_REQ_EXTTS:
224 switch (rq->extts.index) {
225 case 0:
226 ixp_clock->exts0_enabled = on ? 1 : 0;
227 break;
228 case 1:
229 ixp_clock->exts1_enabled = on ? 1 : 0;
230 break;
231 default:
232 return -EINVAL;
233 }
234 return 0;
235 default:
236 break;
237 }
238
239 return -EOPNOTSUPP;
240}
241
242static struct ptp_clock_info ptp_ixp_caps = {
243 .owner = THIS_MODULE,
244 .name = "IXP46X timer",
245 .max_adj = 66666655,
246 .n_ext_ts = N_EXT_TS,
247 .pps = 0,
248 .adjfreq = ptp_ixp_adjfreq,
249 .adjtime = ptp_ixp_adjtime,
250 .gettime = ptp_ixp_gettime,
251 .settime = ptp_ixp_settime,
252 .enable = ptp_ixp_enable,
253};
254
255/* module operations */
256
257static struct ixp_clock ixp_clock;
258
259static int setup_interrupt(int gpio)
260{
261 int irq;
262
263 gpio_line_config(gpio, IXP4XX_GPIO_IN);
264
265 irq = gpio_to_irq(gpio);
266
267 if (NO_IRQ == irq)
268 return NO_IRQ;
269
270 if (irq_set_irq_type(irq, IRQF_TRIGGER_FALLING)) {
271 pr_err("cannot set trigger type for irq %d\n", irq);
272 return NO_IRQ;
273 }
274
275 if (request_irq(irq, isr, 0, DRIVER, &ixp_clock)) {
276 pr_err("request_irq failed for irq %d\n", irq);
277 return NO_IRQ;
278 }
279
280 return irq;
281}
282
283static void __exit ptp_ixp_exit(void)
284{
285 free_irq(MASTER_IRQ, &ixp_clock);
286 free_irq(SLAVE_IRQ, &ixp_clock);
287 ptp_clock_unregister(ixp_clock.ptp_clock);
288}
289
290static int __init ptp_ixp_init(void)
291{
292 if (!cpu_is_ixp46x())
293 return -ENODEV;
294
295 ixp_clock.regs =
296 (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
297
298 ixp_clock.caps = ptp_ixp_caps;
299
300 ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps);
301
302 if (IS_ERR(ixp_clock.ptp_clock))
303 return PTR_ERR(ixp_clock.ptp_clock);
304
305 __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
306 __raw_writel(1, &ixp_clock.regs->trgt_lo);
307 __raw_writel(0, &ixp_clock.regs->trgt_hi);
308 __raw_writel(TTIPEND, &ixp_clock.regs->event);
309
310 if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
311 pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
312 goto no_master;
313 }
314 if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
315 pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
316 goto no_slave;
317 }
318
319 return 0;
320no_slave:
321 free_irq(MASTER_IRQ, &ixp_clock);
322no_master:
323 ptp_clock_unregister(ixp_clock.ptp_clock);
324 return -ENODEV;
325}
326
327module_init(ptp_ixp_init);
328module_exit(ptp_ixp_exit);
329
330MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
331MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
332MODULE_LICENSE("GPL");