Merge branch 'for-3.8/drivers' of git://git.kernel.dk/linux-block
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / net / ethernet / broadcom / tg3.c
index ac94939c03e6cae4a8ef94966afa5acbf5c3494b..78ea90c40e1902af09272a80b09010da52ae6c6b 100644 (file)
@@ -54,6 +54,9 @@
 #include <asm/byteorder.h>
 #include <linux/uaccess.h>
 
+#include <uapi/linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
+
 #ifdef CONFIG_SPARC
 #include <asm/idprom.h>
 #include <asm/prom.h>
@@ -90,10 +93,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
 
 #define DRV_MODULE_NAME                "tg3"
 #define TG3_MAJ_NUM                    3
-#define TG3_MIN_NUM                    127
+#define TG3_MIN_NUM                    128
 #define DRV_MODULE_VERSION     \
        __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE     "November 14, 2012"
+#define DRV_MODULE_RELDATE     "December 03, 2012"
 
 #define RESET_KIND_SHUTDOWN    0
 #define RESET_KIND_INIT                1
@@ -5516,6 +5519,190 @@ static int tg3_setup_phy(struct tg3 *tp, int force_reset)
        return err;
 }
 
+/* tp->lock must be held */
+static u64 tg3_refclk_read(struct tg3 *tp)
+{
+       u64 stamp = tr32(TG3_EAV_REF_CLCK_LSB);
+       return stamp | (u64)tr32(TG3_EAV_REF_CLCK_MSB) << 32;
+}
+
+/* tp->lock must be held */
+static void tg3_refclk_write(struct tg3 *tp, u64 newval)
+{
+       tw32(TG3_EAV_REF_CLCK_CTL, TG3_EAV_REF_CLCK_CTL_STOP);
+       tw32(TG3_EAV_REF_CLCK_LSB, newval & 0xffffffff);
+       tw32(TG3_EAV_REF_CLCK_MSB, newval >> 32);
+       tw32_f(TG3_EAV_REF_CLCK_CTL, TG3_EAV_REF_CLCK_CTL_RESUME);
+}
+
+static inline void tg3_full_lock(struct tg3 *tp, int irq_sync);
+static inline void tg3_full_unlock(struct tg3 *tp);
+static int tg3_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
+{
+       struct tg3 *tp = netdev_priv(dev);
+
+       info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+                               SOF_TIMESTAMPING_RX_SOFTWARE |
+                               SOF_TIMESTAMPING_SOFTWARE    |
+                               SOF_TIMESTAMPING_TX_HARDWARE |
+                               SOF_TIMESTAMPING_RX_HARDWARE |
+                               SOF_TIMESTAMPING_RAW_HARDWARE;
+
+       if (tp->ptp_clock)
+               info->phc_index = ptp_clock_index(tp->ptp_clock);
+       else
+               info->phc_index = -1;
+
+       info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
+
+       info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+                          (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
+                          (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+                          (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
+       return 0;
+}
+
+static int tg3_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+       struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);
+       bool neg_adj = false;
+       u32 correction = 0;
+
+       if (ppb < 0) {
+               neg_adj = true;
+               ppb = -ppb;
+       }
+
+       /* Frequency adjustment is performed using hardware with a 24 bit
+        * accumulator and a programmable correction value. On each clk, the
+        * correction value gets added to the accumulator and when it
+        * overflows, the time counter is incremented/decremented.
+        *
+        * So conversion from ppb to correction value is
+        *              ppb * (1 << 24) / 1000000000
+        */
+       correction = div_u64((u64)ppb * (1 << 24), 1000000000ULL) &
+                    TG3_EAV_REF_CLK_CORRECT_MASK;
+
+       tg3_full_lock(tp, 0);
+
+       if (correction)
+               tw32(TG3_EAV_REF_CLK_CORRECT_CTL,
+                    TG3_EAV_REF_CLK_CORRECT_EN |
+                    (neg_adj ? TG3_EAV_REF_CLK_CORRECT_NEG : 0) | correction);
+       else
+               tw32(TG3_EAV_REF_CLK_CORRECT_CTL, 0);
+
+       tg3_full_unlock(tp);
+
+       return 0;
+}
+
+static int tg3_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);
+
+       tg3_full_lock(tp, 0);
+       tp->ptp_adjust += delta;
+       tg3_full_unlock(tp);
+
+       return 0;
+}
+
+static int tg3_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+       u64 ns;
+       u32 remainder;
+       struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);
+
+       tg3_full_lock(tp, 0);
+       ns = tg3_refclk_read(tp);
+       ns += tp->ptp_adjust;
+       tg3_full_unlock(tp);
+
+       ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+       ts->tv_nsec = remainder;
+
+       return 0;
+}
+
+static int tg3_ptp_settime(struct ptp_clock_info *ptp,
+                          const struct timespec *ts)
+{
+       u64 ns;
+       struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);
+
+       ns = timespec_to_ns(ts);
+
+       tg3_full_lock(tp, 0);
+       tg3_refclk_write(tp, ns);
+       tp->ptp_adjust = 0;
+       tg3_full_unlock(tp);
+
+       return 0;
+}
+
+static int tg3_ptp_enable(struct ptp_clock_info *ptp,
+                         struct ptp_clock_request *rq, int on)
+{
+       return -EOPNOTSUPP;
+}
+
+static const struct ptp_clock_info tg3_ptp_caps = {
+       .owner          = THIS_MODULE,
+       .name           = "tg3 clock",
+       .max_adj        = 250000000,
+       .n_alarm        = 0,
+       .n_ext_ts       = 0,
+       .n_per_out      = 0,
+       .pps            = 0,
+       .adjfreq        = tg3_ptp_adjfreq,
+       .adjtime        = tg3_ptp_adjtime,
+       .gettime        = tg3_ptp_gettime,
+       .settime        = tg3_ptp_settime,
+       .enable         = tg3_ptp_enable,
+};
+
+static void tg3_hwclock_to_timestamp(struct tg3 *tp, u64 hwclock,
+                                    struct skb_shared_hwtstamps *timestamp)
+{
+       memset(timestamp, 0, sizeof(struct skb_shared_hwtstamps));
+       timestamp->hwtstamp  = ns_to_ktime((hwclock & TG3_TSTAMP_MASK) +
+                                          tp->ptp_adjust);
+}
+
+/* tp->lock must be held */
+static void tg3_ptp_init(struct tg3 *tp)
+{
+       if (!tg3_flag(tp, PTP_CAPABLE))
+               return;
+
+       /* Initialize the hardware clock to the system time. */
+       tg3_refclk_write(tp, ktime_to_ns(ktime_get_real()));
+       tp->ptp_adjust = 0;
+       tp->ptp_info = tg3_ptp_caps;
+}
+
+/* tp->lock must be held */
+static void tg3_ptp_resume(struct tg3 *tp)
+{
+       if (!tg3_flag(tp, PTP_CAPABLE))
+               return;
+
+       tg3_refclk_write(tp, ktime_to_ns(ktime_get_real()) + tp->ptp_adjust);
+       tp->ptp_adjust = 0;
+}
+
+static void tg3_ptp_fini(struct tg3 *tp)
+{
+       if (!tg3_flag(tp, PTP_CAPABLE) || !tp->ptp_clock)
+               return;
+
+       ptp_clock_unregister(tp->ptp_clock);
+       tp->ptp_clock = NULL;
+       tp->ptp_adjust = 0;
+}
+
 static inline int tg3_irq_sync(struct tg3 *tp)
 {
        return tp->irq_sync;
@@ -5696,6 +5883,16 @@ static void tg3_tx(struct tg3_napi *tnapi)
                        return;
                }
 
+               if (tnapi->tx_ring[sw_idx].len_flags & TXD_FLAG_HWTSTAMP) {
+                       struct skb_shared_hwtstamps timestamp;
+                       u64 hwclock = tr32(TG3_TX_TSTAMP_LSB);
+                       hwclock |= (u64)tr32(TG3_TX_TSTAMP_MSB) << 32;
+
+                       tg3_hwclock_to_timestamp(tp, hwclock, &timestamp);
+
+                       skb_tstamp_tx(skb, &timestamp);
+               }
+
                pci_unmap_single(tp->pdev,
                                 dma_unmap_addr(ri, mapping),
                                 skb_headlen(skb),
@@ -5963,6 +6160,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
                dma_addr_t dma_addr;
                u32 opaque_key, desc_idx, *post_ptr;
                u8 *data;
+               u64 tstamp = 0;
 
                desc_idx = desc->opaque & RXD_OPAQUE_INDEX_MASK;
                opaque_key = desc->opaque & RXD_OPAQUE_RING_MASK;
@@ -5997,6 +6195,14 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
                len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) -
                      ETH_FCS_LEN;
 
+               if ((desc->type_flags & RXD_FLAG_PTPSTAT_MASK) ==
+                    RXD_FLAG_PTPSTAT_PTPV1 ||
+                   (desc->type_flags & RXD_FLAG_PTPSTAT_MASK) ==
+                    RXD_FLAG_PTPSTAT_PTPV2) {
+                       tstamp = tr32(TG3_RX_TSTAMP_LSB);
+                       tstamp |= (u64)tr32(TG3_RX_TSTAMP_MSB) << 32;
+               }
+
                if (len > TG3_RX_COPY_THRESH(tp)) {
                        int skb_size;
                        unsigned int frag_size;
@@ -6040,6 +6246,10 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
                }
 
                skb_put(skb, len);
+               if (tstamp)
+                       tg3_hwclock_to_timestamp(tp, tstamp,
+                                                skb_hwtstamps(skb));
+
                if ((tp->dev->features & NETIF_F_RXCSUM) &&
                    (desc->type_flags & RXD_FLAG_TCPUDP_CSUM) &&
                    (((desc->ip_tcp_csum & RXD_TCPCSUM_MASK)
@@ -6525,8 +6735,11 @@ static inline void tg3_netif_stop(struct tg3 *tp)
        netif_tx_disable(tp->dev);
 }
 
+/* tp->lock must be held */
 static inline void tg3_netif_start(struct tg3 *tp)
 {
+       tg3_ptp_resume(tp);
+
        /* NOTE: unconditional netif_tx_wake_all_queues is only
         * appropriate so long as all callers are assured to
         * have free tx slots (such as after tg3_init_hw)
@@ -7094,6 +7307,12 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                vlan = vlan_tx_tag_get(skb);
        }
 
+       if ((unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) &&
+           tg3_flag(tp, TX_TSTAMP_EN)) {
+               skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+               base_flags |= TXD_FLAG_HWTSTAMP;
+       }
+
        len = skb_headlen(skb);
 
        mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE);
@@ -8962,9 +9181,15 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
         */
        tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM;
 
-       tw32(GRC_MODE,
-            tp->grc_mode |
-            (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP));
+       val = GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP;
+       if (tp->rxptpctl)
+               tw32(TG3_RX_PTP_CTL,
+                    tp->rxptpctl | TG3_RX_PTP_CTL_HWTS_INTERLOCK);
+
+       if (tg3_flag(tp, PTP_CAPABLE))
+               val |= GRC_MODE_TIME_SYNC_ENABLE;
+
+       tw32(GRC_MODE, tp->grc_mode | val);
 
        /* Setup the timer prescalar register.  Clock is always 66Mhz. */
        val = tr32(GRC_MISC_CFG);
@@ -10364,7 +10589,8 @@ static void tg3_ints_fini(struct tg3 *tp)
        tg3_flag_clear(tp, ENABLE_TSS);
 }
 
-static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq)
+static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq,
+                    bool init)
 {
        struct net_device *dev = tp->dev;
        int i, err;
@@ -10443,6 +10669,12 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq)
        tg3_flag_set(tp, INIT_COMPLETE);
        tg3_enable_ints(tp);
 
+       if (init)
+               tg3_ptp_init(tp);
+       else
+               tg3_ptp_resume(tp);
+
+
        tg3_full_unlock(tp);
 
        netif_tx_start_all_queues(dev);
@@ -10540,11 +10772,19 @@ static int tg3_open(struct net_device *dev)
 
        tg3_full_unlock(tp);
 
-       err = tg3_start(tp, true, true);
+       err = tg3_start(tp, true, true, true);
        if (err) {
                tg3_frob_aux_power(tp, false);
                pci_set_power_state(tp->pdev, PCI_D3hot);
        }
+
+       if (tg3_flag(tp, PTP_CAPABLE)) {
+               tp->ptp_clock = ptp_clock_register(&tp->ptp_info,
+                                                  &tp->pdev->dev);
+               if (IS_ERR(tp->ptp_clock))
+                       tp->ptp_clock = NULL;
+       }
+
        return err;
 }
 
@@ -10552,6 +10792,8 @@ static int tg3_close(struct net_device *dev)
 {
        struct tg3 *tp = netdev_priv(dev);
 
+       tg3_ptp_fini(tp);
+
        tg3_stop(tp);
 
        /* Clear stats across close / open calls */
@@ -11454,7 +11696,7 @@ static int tg3_set_channels(struct net_device *dev,
 
        tg3_carrier_off(tp);
 
-       tg3_start(tp, true, false);
+       tg3_start(tp, true, false, false);
 
        return 0;
 }
@@ -12507,7 +12749,6 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
                }
 
                tg3_full_lock(tp, irq_sync);
-
                tg3_halt(tp, RESET_KIND_SUSPEND, 1);
                err = tg3_nvram_lock(tp);
                tg3_halt_cpu(tp, RX_CPU_BASE);
@@ -12562,6 +12803,96 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
 
 }
 
+static int tg3_hwtstamp_ioctl(struct net_device *dev,
+                             struct ifreq *ifr, int cmd)
+{
+       struct tg3 *tp = netdev_priv(dev);
+       struct hwtstamp_config stmpconf;
+
+       if (!tg3_flag(tp, PTP_CAPABLE))
+               return -EINVAL;
+
+       if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf)))
+               return -EFAULT;
+
+       if (stmpconf.flags)
+               return -EINVAL;
+
+       switch (stmpconf.tx_type) {
+       case HWTSTAMP_TX_ON:
+               tg3_flag_set(tp, TX_TSTAMP_EN);
+               break;
+       case HWTSTAMP_TX_OFF:
+               tg3_flag_clear(tp, TX_TSTAMP_EN);
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       switch (stmpconf.rx_filter) {
+       case HWTSTAMP_FILTER_NONE:
+               tp->rxptpctl = 0;
+               break;
+       case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V1_EN |
+                              TG3_RX_PTP_CTL_ALL_V1_EVENTS;
+               break;
+       case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V1_EN |
+                              TG3_RX_PTP_CTL_SYNC_EVNT;
+               break;
+       case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V1_EN |
+                              TG3_RX_PTP_CTL_DELAY_REQ;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_EVENT:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_EN |
+                              TG3_RX_PTP_CTL_ALL_V2_EVENTS;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_L2_EN |
+                              TG3_RX_PTP_CTL_ALL_V2_EVENTS;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_L4_EN |
+                              TG3_RX_PTP_CTL_ALL_V2_EVENTS;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_SYNC:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_EN |
+                              TG3_RX_PTP_CTL_SYNC_EVNT;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_L2_EN |
+                              TG3_RX_PTP_CTL_SYNC_EVNT;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_L4_EN |
+                              TG3_RX_PTP_CTL_SYNC_EVNT;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_EN |
+                              TG3_RX_PTP_CTL_DELAY_REQ;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_L2_EN |
+                              TG3_RX_PTP_CTL_DELAY_REQ;
+               break;
+       case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+               tp->rxptpctl = TG3_RX_PTP_CTL_RX_PTP_V2_L4_EN |
+                              TG3_RX_PTP_CTL_DELAY_REQ;
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       if (netif_running(dev) && tp->rxptpctl)
+               tw32(TG3_RX_PTP_CTL,
+                    tp->rxptpctl | TG3_RX_PTP_CTL_HWTS_INTERLOCK);
+
+       return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ?
+               -EFAULT : 0;
+}
+
 static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct mii_ioctl_data *data = if_mii(ifr);
@@ -12612,6 +12943,9 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 
                return err;
 
+       case SIOCSHWTSTAMP:
+               return tg3_hwtstamp_ioctl(dev, ifr, cmd);
+
        default:
                /* do nothing */
                break;
@@ -12713,7 +13047,7 @@ static const struct ethtool_ops tg3_ethtool_ops = {
        .set_rxfh_indir         = tg3_set_rxfh_indir,
        .get_channels           = tg3_get_channels,
        .set_channels           = tg3_set_channels,
-       .get_ts_info            = ethtool_op_get_ts_info,
+       .get_ts_info            = tg3_get_ts_info,
 };
 
 static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
@@ -14539,8 +14873,7 @@ static bool tg3_10_100_only_device(struct tg3 *tp,
        return false;
 }
 
-static int tg3_get_invariants(struct tg3 *tp,
-                                       const struct pci_device_id *ent)
+static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
 {
        u32 misc_ctrl_reg;
        u32 pci_state_reg, grc_misc_cfg;
@@ -16274,6 +16607,10 @@ static int tg3_init_one(struct pci_dev *pdev,
 
        pci_set_drvdata(pdev, dev);
 
+       if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+           GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+               tg3_flag_set(tp, PTP_CAPABLE);
+
        if (tg3_flag(tp, 5717_PLUS)) {
                /* Resume a low-power mode */
                tg3_frob_aux_power(tp, false);
@@ -16599,8 +16936,8 @@ static void tg3_io_resume(struct pci_dev *pdev)
        tg3_full_lock(tp, 0);
        tg3_flag_set(tp, INIT_COMPLETE);
        err = tg3_restart_hw(tp, 1);
-       tg3_full_unlock(tp);
        if (err) {
+               tg3_full_unlock(tp);
                netdev_err(netdev, "Cannot restart hardware after reset.\n");
                goto done;
        }
@@ -16611,6 +16948,8 @@ static void tg3_io_resume(struct pci_dev *pdev)
 
        tg3_netif_start(tp);
 
+       tg3_full_unlock(tp);
+
        tg3_phy_start(tp);
 
 done: