diff options
author | Takahiroi Shimizu <tshimizu818@gmail.com> | 2012-03-07 17:16:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-03-09 16:55:38 -0500 |
commit | 1a0bdadb4e36abac63b0a9787f372aac30c11a9e (patch) | |
tree | f66477ebd3542f16738995a049b0f341ee0ab82e | |
parent | 863d08ece9bf11043541e8017cfbdd16b800fbe5 (diff) |
net/pch_gbe: supports eg20t ptp clock
Supports EG20T ptp clock in the driver
Changes e-mail address.
Adds number.
Signed-off-by: Takahiro Shimizu <tshimizu818@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/oki-semi/pch_gbe/Kconfig | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 217 |
3 files changed, 240 insertions, 3 deletions
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig index 00bc4fc968c7..bce01641ee6b 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig +++ b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig | |||
@@ -20,3 +20,16 @@ config PCH_GBE | |||
20 | purpose use. | 20 | purpose use. |
21 | ML7223/ML7831 is companion chip for Intel Atom E6xx series. | 21 | ML7223/ML7831 is companion chip for Intel Atom E6xx series. |
22 | ML7223/ML7831 is completely compatible for Intel EG20T PCH. | 22 | ML7223/ML7831 is completely compatible for Intel EG20T PCH. |
23 | |||
24 | if PCH_GBE | ||
25 | |||
26 | config PCH_PTP | ||
27 | bool "PCH PTP clock support" | ||
28 | default n | ||
29 | depends on PTP_1588_CLOCK_PCH | ||
30 | ---help--- | ||
31 | Say Y here if you want to use Precision Time Protocol (PTP) in the | ||
32 | driver. PTP is a method to precisely synchronize distributed clocks | ||
33 | over Ethernet networks. | ||
34 | |||
35 | endif # PCH_GBE | ||
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h index a09a07197eb5..dd14915f54bb 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h | |||
@@ -630,6 +630,9 @@ struct pch_gbe_adapter { | |||
630 | unsigned long tx_queue_len; | 630 | unsigned long tx_queue_len; |
631 | bool have_msi; | 631 | bool have_msi; |
632 | bool rx_stop_flag; | 632 | bool rx_stop_flag; |
633 | int hwts_tx_en; | ||
634 | int hwts_rx_en; | ||
635 | struct pci_dev *ptp_pdev; | ||
633 | }; | 636 | }; |
634 | 637 | ||
635 | extern const char pch_driver_version[]; | 638 | extern const char pch_driver_version[]; |
@@ -648,6 +651,16 @@ extern void pch_gbe_free_tx_resources(struct pch_gbe_adapter *adapter, | |||
648 | extern void pch_gbe_free_rx_resources(struct pch_gbe_adapter *adapter, | 651 | extern void pch_gbe_free_rx_resources(struct pch_gbe_adapter *adapter, |
649 | struct pch_gbe_rx_ring *rx_ring); | 652 | struct pch_gbe_rx_ring *rx_ring); |
650 | extern void pch_gbe_update_stats(struct pch_gbe_adapter *adapter); | 653 | extern void pch_gbe_update_stats(struct pch_gbe_adapter *adapter); |
654 | #ifdef CONFIG_PCH_PTP | ||
655 | extern u32 pch_ch_control_read(struct pci_dev *pdev); | ||
656 | extern void pch_ch_control_write(struct pci_dev *pdev, u32 val); | ||
657 | extern u32 pch_ch_event_read(struct pci_dev *pdev); | ||
658 | extern void pch_ch_event_write(struct pci_dev *pdev, u32 val); | ||
659 | extern u32 pch_src_uuid_lo_read(struct pci_dev *pdev); | ||
660 | extern u32 pch_src_uuid_hi_read(struct pci_dev *pdev); | ||
661 | extern u64 pch_rx_snap_read(struct pci_dev *pdev); | ||
662 | extern u64 pch_tx_snap_read(struct pci_dev *pdev); | ||
663 | #endif | ||
651 | 664 | ||
652 | /* pch_gbe_param.c */ | 665 | /* pch_gbe_param.c */ |
653 | extern void pch_gbe_check_options(struct pch_gbe_adapter *adapter); | 666 | extern void pch_gbe_check_options(struct pch_gbe_adapter *adapter); |
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 69a66545c8ae..8035e5ff6e06 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 1999 - 2010 Intel Corporation. | 2 | * Copyright (C) 1999 - 2010 Intel Corporation. |
3 | * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. | 3 | * Copyright (C) 2010 - 2012 LAPIS SEMICONDUCTOR CO., LTD. |
4 | * | 4 | * |
5 | * This code was derived from the Intel e1000e Linux driver. | 5 | * This code was derived from the Intel e1000e Linux driver. |
6 | * | 6 | * |
@@ -21,6 +21,10 @@ | |||
21 | #include "pch_gbe.h" | 21 | #include "pch_gbe.h" |
22 | #include "pch_gbe_api.h" | 22 | #include "pch_gbe_api.h" |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #ifdef CONFIG_PCH_PTP | ||
25 | #include <linux/net_tstamp.h> | ||
26 | #include <linux/ptp_classify.h> | ||
27 | #endif | ||
24 | 28 | ||
25 | #define DRV_VERSION "1.00" | 29 | #define DRV_VERSION "1.00" |
26 | const char pch_driver_version[] = DRV_VERSION; | 30 | const char pch_driver_version[] = DRV_VERSION; |
@@ -95,12 +99,195 @@ const char pch_driver_version[] = DRV_VERSION; | |||
95 | 99 | ||
96 | #define PCH_GBE_INT_DISABLE_ALL 0 | 100 | #define PCH_GBE_INT_DISABLE_ALL 0 |
97 | 101 | ||
102 | #ifdef CONFIG_PCH_PTP | ||
103 | /* Macros for ieee1588 */ | ||
104 | #define TICKS_NS_SHIFT 5 | ||
105 | |||
106 | /* 0x40 Time Synchronization Channel Control Register Bits */ | ||
107 | #define MASTER_MODE (1<<0) | ||
108 | #define SLAVE_MODE (0<<0) | ||
109 | #define V2_MODE (1<<31) | ||
110 | #define CAP_MODE0 (0<<16) | ||
111 | #define CAP_MODE2 (1<<17) | ||
112 | |||
113 | /* 0x44 Time Synchronization Channel Event Register Bits */ | ||
114 | #define TX_SNAPSHOT_LOCKED (1<<0) | ||
115 | #define RX_SNAPSHOT_LOCKED (1<<1) | ||
116 | #endif | ||
117 | |||
98 | static unsigned int copybreak __read_mostly = PCH_GBE_COPYBREAK_DEFAULT; | 118 | static unsigned int copybreak __read_mostly = PCH_GBE_COPYBREAK_DEFAULT; |
99 | 119 | ||
100 | static int pch_gbe_mdio_read(struct net_device *netdev, int addr, int reg); | 120 | static int pch_gbe_mdio_read(struct net_device *netdev, int addr, int reg); |
101 | static void pch_gbe_mdio_write(struct net_device *netdev, int addr, int reg, | 121 | static void pch_gbe_mdio_write(struct net_device *netdev, int addr, int reg, |
102 | int data); | 122 | int data); |
103 | 123 | ||
124 | #ifdef CONFIG_PCH_PTP | ||
125 | static struct sock_filter ptp_filter[] = { | ||
126 | PTP_FILTER | ||
127 | }; | ||
128 | |||
129 | static int pch_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid) | ||
130 | { | ||
131 | u8 *data = skb->data; | ||
132 | unsigned int offset; | ||
133 | u16 *hi, *id; | ||
134 | u32 lo; | ||
135 | |||
136 | if ((sk_run_filter(skb, ptp_filter) != PTP_CLASS_V2_IPV4) && | ||
137 | (sk_run_filter(skb, ptp_filter) != PTP_CLASS_V1_IPV4)) { | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; | ||
142 | |||
143 | if (skb->len < offset + OFF_PTP_SEQUENCE_ID + sizeof(seqid)) | ||
144 | return 0; | ||
145 | |||
146 | hi = (u16 *)(data + offset + OFF_PTP_SOURCE_UUID); | ||
147 | id = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); | ||
148 | |||
149 | memcpy(&lo, &hi[1], sizeof(lo)); | ||
150 | |||
151 | return (uid_hi == *hi && | ||
152 | uid_lo == lo && | ||
153 | seqid == *id); | ||
154 | } | ||
155 | |||
156 | static void pch_rx_timestamp( | ||
157 | struct pch_gbe_adapter *adapter, struct sk_buff *skb) | ||
158 | { | ||
159 | struct skb_shared_hwtstamps *shhwtstamps; | ||
160 | struct pci_dev *pdev; | ||
161 | u64 ns; | ||
162 | u32 hi, lo, val; | ||
163 | u16 uid, seq; | ||
164 | |||
165 | if (!adapter->hwts_rx_en) | ||
166 | return; | ||
167 | |||
168 | /* Get ieee1588's dev information */ | ||
169 | pdev = adapter->ptp_pdev; | ||
170 | |||
171 | val = pch_ch_event_read(pdev); | ||
172 | |||
173 | if (!(val & RX_SNAPSHOT_LOCKED)) | ||
174 | return; | ||
175 | |||
176 | lo = pch_src_uuid_lo_read(pdev); | ||
177 | hi = pch_src_uuid_hi_read(pdev); | ||
178 | |||
179 | uid = hi & 0xffff; | ||
180 | seq = (hi >> 16) & 0xffff; | ||
181 | |||
182 | if (!pch_ptp_match(skb, htons(uid), htonl(lo), htons(seq))) | ||
183 | goto out; | ||
184 | |||
185 | ns = pch_rx_snap_read(pdev); | ||
186 | ns <<= TICKS_NS_SHIFT; | ||
187 | |||
188 | shhwtstamps = skb_hwtstamps(skb); | ||
189 | memset(shhwtstamps, 0, sizeof(*shhwtstamps)); | ||
190 | shhwtstamps->hwtstamp = ns_to_ktime(ns); | ||
191 | out: | ||
192 | pch_ch_event_write(pdev, RX_SNAPSHOT_LOCKED); | ||
193 | } | ||
194 | |||
195 | static void pch_tx_timestamp( | ||
196 | struct pch_gbe_adapter *adapter, struct sk_buff *skb) | ||
197 | { | ||
198 | struct skb_shared_hwtstamps shhwtstamps; | ||
199 | struct pci_dev *pdev; | ||
200 | struct skb_shared_info *shtx; | ||
201 | u64 ns; | ||
202 | u32 cnt, val; | ||
203 | |||
204 | shtx = skb_shinfo(skb); | ||
205 | if (unlikely(shtx->tx_flags & SKBTX_HW_TSTAMP && adapter->hwts_tx_en)) | ||
206 | shtx->tx_flags |= SKBTX_IN_PROGRESS; | ||
207 | else | ||
208 | return; | ||
209 | |||
210 | /* Get ieee1588's dev information */ | ||
211 | pdev = adapter->ptp_pdev; | ||
212 | |||
213 | /* | ||
214 | * This really stinks, but we have to poll for the Tx time stamp. | ||
215 | * Usually, the time stamp is ready after 4 to 6 microseconds. | ||
216 | */ | ||
217 | for (cnt = 0; cnt < 100; cnt++) { | ||
218 | val = pch_ch_event_read(pdev); | ||
219 | if (val & TX_SNAPSHOT_LOCKED) | ||
220 | break; | ||
221 | udelay(1); | ||
222 | } | ||
223 | if (!(val & TX_SNAPSHOT_LOCKED)) { | ||
224 | shtx->tx_flags &= ~SKBTX_IN_PROGRESS; | ||
225 | return; | ||
226 | } | ||
227 | |||
228 | ns = pch_tx_snap_read(pdev); | ||
229 | ns <<= TICKS_NS_SHIFT; | ||
230 | |||
231 | memset(&shhwtstamps, 0, sizeof(shhwtstamps)); | ||
232 | shhwtstamps.hwtstamp = ns_to_ktime(ns); | ||
233 | skb_tstamp_tx(skb, &shhwtstamps); | ||
234 | |||
235 | pch_ch_event_write(pdev, TX_SNAPSHOT_LOCKED); | ||
236 | } | ||
237 | |||
238 | static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) | ||
239 | { | ||
240 | struct hwtstamp_config cfg; | ||
241 | struct pch_gbe_adapter *adapter = netdev_priv(netdev); | ||
242 | struct pci_dev *pdev; | ||
243 | |||
244 | if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) | ||
245 | return -EFAULT; | ||
246 | |||
247 | if (cfg.flags) /* reserved for future extensions */ | ||
248 | return -EINVAL; | ||
249 | |||
250 | /* Get ieee1588's dev information */ | ||
251 | pdev = adapter->ptp_pdev; | ||
252 | |||
253 | switch (cfg.tx_type) { | ||
254 | case HWTSTAMP_TX_OFF: | ||
255 | adapter->hwts_tx_en = 0; | ||
256 | break; | ||
257 | case HWTSTAMP_TX_ON: | ||
258 | adapter->hwts_tx_en = 1; | ||
259 | break; | ||
260 | default: | ||
261 | return -ERANGE; | ||
262 | } | ||
263 | |||
264 | switch (cfg.rx_filter) { | ||
265 | case HWTSTAMP_FILTER_NONE: | ||
266 | adapter->hwts_rx_en = 0; | ||
267 | break; | ||
268 | case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | ||
269 | adapter->hwts_rx_en = 0; | ||
270 | pch_ch_control_write(pdev, (SLAVE_MODE | CAP_MODE0)); | ||
271 | break; | ||
272 | case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | ||
273 | adapter->hwts_rx_en = 1; | ||
274 | pch_ch_control_write(pdev, (MASTER_MODE | CAP_MODE0)); | ||
275 | break; | ||
276 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | ||
277 | adapter->hwts_rx_en = 1; | ||
278 | pch_ch_control_write(pdev, (V2_MODE | CAP_MODE2)); | ||
279 | break; | ||
280 | default: | ||
281 | return -ERANGE; | ||
282 | } | ||
283 | |||
284 | /* Clear out any old time stamps. */ | ||
285 | pch_ch_event_write(pdev, TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED); | ||
286 | |||
287 | return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; | ||
288 | } | ||
289 | #endif | ||
290 | |||
104 | inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) | 291 | inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) |
105 | { | 292 | { |
106 | iowrite32(0x01, &hw->reg->MAC_ADDR_LOAD); | 293 | iowrite32(0x01, &hw->reg->MAC_ADDR_LOAD); |
@@ -1072,6 +1259,11 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter, | |||
1072 | iowrite32(tx_ring->dma + | 1259 | iowrite32(tx_ring->dma + |
1073 | (int)sizeof(struct pch_gbe_tx_desc) * ring_num, | 1260 | (int)sizeof(struct pch_gbe_tx_desc) * ring_num, |
1074 | &hw->reg->TX_DSC_SW_P); | 1261 | &hw->reg->TX_DSC_SW_P); |
1262 | |||
1263 | #ifdef CONFIG_PCH_PTP | ||
1264 | pch_tx_timestamp(adapter, skb); | ||
1265 | #endif | ||
1266 | |||
1075 | dev_kfree_skb_any(skb); | 1267 | dev_kfree_skb_any(skb); |
1076 | } | 1268 | } |
1077 | 1269 | ||
@@ -1543,6 +1735,11 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, | |||
1543 | adapter->stats.multicast++; | 1735 | adapter->stats.multicast++; |
1544 | /* Write meta date of skb */ | 1736 | /* Write meta date of skb */ |
1545 | skb_put(skb, length); | 1737 | skb_put(skb, length); |
1738 | |||
1739 | #ifdef CONFIG_PCH_PTP | ||
1740 | pch_rx_timestamp(adapter, skb); | ||
1741 | #endif | ||
1742 | |||
1546 | skb->protocol = eth_type_trans(skb, netdev); | 1743 | skb->protocol = eth_type_trans(skb, netdev); |
1547 | if (tcp_ip_status & PCH_GBE_RXD_ACC_STAT_TCPIPOK) | 1744 | if (tcp_ip_status & PCH_GBE_RXD_ACC_STAT_TCPIPOK) |
1548 | skb->ip_summed = CHECKSUM_NONE; | 1745 | skb->ip_summed = CHECKSUM_NONE; |
@@ -2144,6 +2341,11 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) | |||
2144 | 2341 | ||
2145 | pr_debug("cmd : 0x%04x\n", cmd); | 2342 | pr_debug("cmd : 0x%04x\n", cmd); |
2146 | 2343 | ||
2344 | #ifdef CONFIG_PCH_PTP | ||
2345 | if (cmd == SIOCSHWTSTAMP) | ||
2346 | return hwtstamp_ioctl(netdev, ifr, cmd); | ||
2347 | #endif | ||
2348 | |||
2147 | return generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL); | 2349 | return generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL); |
2148 | } | 2350 | } |
2149 | 2351 | ||
@@ -2435,6 +2637,15 @@ static int pch_gbe_probe(struct pci_dev *pdev, | |||
2435 | goto err_free_netdev; | 2637 | goto err_free_netdev; |
2436 | } | 2638 | } |
2437 | 2639 | ||
2640 | #ifdef CONFIG_PCH_PTP | ||
2641 | adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number, | ||
2642 | PCI_DEVFN(12, 4)); | ||
2643 | if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) { | ||
2644 | pr_err("Bad ptp filter\n"); | ||
2645 | return -EINVAL; | ||
2646 | } | ||
2647 | #endif | ||
2648 | |||
2438 | netdev->netdev_ops = &pch_gbe_netdev_ops; | 2649 | netdev->netdev_ops = &pch_gbe_netdev_ops; |
2439 | netdev->watchdog_timeo = PCH_GBE_WATCHDOG_PERIOD; | 2650 | netdev->watchdog_timeo = PCH_GBE_WATCHDOG_PERIOD; |
2440 | netif_napi_add(netdev, &adapter->napi, | 2651 | netif_napi_add(netdev, &adapter->napi, |
@@ -2499,7 +2710,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, | |||
2499 | netif_carrier_off(netdev); | 2710 | netif_carrier_off(netdev); |
2500 | netif_stop_queue(netdev); | 2711 | netif_stop_queue(netdev); |
2501 | 2712 | ||
2502 | dev_dbg(&pdev->dev, "OKIsemi(R) PCH Network Connection\n"); | 2713 | dev_dbg(&pdev->dev, "PCH Network Connection\n"); |
2503 | 2714 | ||
2504 | device_set_wakeup_enable(&pdev->dev, 1); | 2715 | device_set_wakeup_enable(&pdev->dev, 1); |
2505 | return 0; | 2716 | return 0; |
@@ -2600,7 +2811,7 @@ module_init(pch_gbe_init_module); | |||
2600 | module_exit(pch_gbe_exit_module); | 2811 | module_exit(pch_gbe_exit_module); |
2601 | 2812 | ||
2602 | MODULE_DESCRIPTION("EG20T PCH Gigabit ethernet Driver"); | 2813 | MODULE_DESCRIPTION("EG20T PCH Gigabit ethernet Driver"); |
2603 | MODULE_AUTHOR("OKI SEMICONDUCTOR, <toshiharu-linux@dsn.okisemi.com>"); | 2814 | MODULE_AUTHOR("LAPIS SEMICONDUCTOR, <tshimizu818@gmail.com>"); |
2604 | MODULE_LICENSE("GPL"); | 2815 | MODULE_LICENSE("GPL"); |
2605 | MODULE_VERSION(DRV_VERSION); | 2816 | MODULE_VERSION(DRV_VERSION); |
2606 | MODULE_DEVICE_TABLE(pci, pch_gbe_pcidev_id); | 2817 | MODULE_DEVICE_TABLE(pci, pch_gbe_pcidev_id); |