* [net-next 13/15] i40evf: don't store unnecessary array of strings
From: Aaron Brown @ 2014-02-13 11:48 UTC (permalink / raw)
To: davem
Cc: Mitch Williams, netdev, gospo, sassmann, Jesse Brandeburg,
Aaron Brown
In-Reply-To: <1392292133-32120-1-git-send-email-aaron.f.brown@intel.com>
From: Mitch Williams <mitch.a.williams@intel.com>
Since we store the traffic vector names in the queue vector struct, we
don't need to maintain an array of strings for these names in the
adapter structure. Replace this array with a single string and use it
when allocating the misc irq vector.
Also update copyrights.
Change-ID: I664f096c3c008210d6a04a487163e8aa934fee5b
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Tested-by: Sibai Li <sibai.li@intel.com>
Signed-off-by: Aaron Brown <aaron.f.brown@intel.com>
---
drivers/net/ethernet/intel/i40evf/i40evf.h | 2 +-
drivers/net/ethernet/intel/i40evf/i40evf_main.c | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 37f5877..696c9d1 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -190,7 +190,7 @@ struct i40evf_adapter {
struct delayed_work init_task;
struct i40e_q_vector *q_vector[MAX_MSIX_Q_VECTORS];
struct list_head vlan_filter_list;
- char name[MAX_MSIX_COUNT][IFNAMSIZ + 9];
+ char misc_vector_name[IFNAMSIZ + 9];
/* TX */
struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index f5caf44..d271d3a 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -511,9 +511,10 @@ static int i40evf_request_misc_irq(struct i40evf_adapter *adapter)
struct net_device *netdev = adapter->netdev;
int err;
- sprintf(adapter->name[0], "i40evf:mbx");
+ sprintf(adapter->misc_vector_name, "i40evf:mbx");
err = request_irq(adapter->msix_entries[0].vector,
- &i40evf_msix_aq, 0, adapter->name[0], netdev);
+ &i40evf_msix_aq, 0,
+ adapter->misc_vector_name, netdev);
if (err) {
dev_err(&adapter->pdev->dev,
"request_irq for msix_aq failed: %d\n", err);
--
1.8.5.GIT
^ permalink raw reply related
* [net-next 14/15] i40evf: change type of flags variable
From: Aaron Brown @ 2014-02-13 11:48 UTC (permalink / raw)
To: davem
Cc: Mitch Williams, netdev, gospo, sassmann, Jesse Brandeburg,
Dan Carpenter, Aaron Brown
In-Reply-To: <1392292133-32120-1-git-send-email-aaron.f.brown@intel.com>
From: Mitch Williams <mitch.a.williams@intel.com>
As pointed out by Dan Carpenter (from Oracle), the flags variable is
declared as a 64-bit long but all of the flags are defined as u32,
which may lead to unintended consequences. Fix this by declaring flags
as u32 (since we don't need any more than about a dozen flags right
now), and remove the volatile qualifier, since it's unnecessary and
just makes checkpatch cry.
Change-ID: I137d3bb1842bf7e9456b5929ca54e3b0ed45dcab
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
CC: Dan Carpenter <dan.carpenter@oracle.com>
Tested-by: Sibai Li <sibai.li@intel.com>
Signed-off-by: Aaron Brown <aaron.f.brown@intel.com>
---
drivers/net/ethernet/intel/i40evf/i40evf.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 696c9d1..5e0a344 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -205,7 +205,7 @@ struct i40evf_adapter {
int num_msix_vectors;
struct msix_entry *msix_entries;
- volatile unsigned long flags;
+ u32 flags;
#define I40EVF_FLAG_RX_CSUM_ENABLED (u32)(1)
#define I40EVF_FLAG_RX_1BUF_CAPABLE (u32)(1 << 1)
#define I40EVF_FLAG_RX_PS_CAPABLE (u32)(1 << 2)
--
1.8.5.GIT
^ permalink raw reply related
* [net-next 11/15] i40evf: clean up adapter struct
From: Aaron Brown @ 2014-02-13 11:48 UTC (permalink / raw)
To: davem
Cc: Mitch Williams, netdev, gospo, sassmann, Jesse Brandeburg,
Aaron Brown
In-Reply-To: <1392292133-32120-1-git-send-email-aaron.f.brown@intel.com>
From: Mitch Williams <mitch.a.williams@intel.com>
Remove a bunch of unused structure members that are just wasting
space. Remove a completely unused info structure definition as well.
Also update copyrights.
Change-ID: I028ab92d9b7bd13a832cf3363bd1dc6610d8a535
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Tested-by: Sibai Li <sibai.li@intel.com>
Signed-off-by: Aaron Brown <aaron.f.brown@intel.com>
---
drivers/net/ethernet/intel/i40evf/i40evf.h | 29 +----------------------------
1 file changed, 1 insertion(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index ff6529b..05969b3 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -185,7 +185,6 @@ enum i40evf_critical_section_t {
/* board specific private data structure */
struct i40evf_adapter {
struct timer_list watchdog_timer;
- struct vlan_group *vlgrp;
struct work_struct reset_task;
struct work_struct adminq_task;
struct delayed_work init_task;
@@ -193,38 +192,19 @@ struct i40evf_adapter {
struct list_head vlan_filter_list;
char name[MAX_MSIX_COUNT][IFNAMSIZ + 9];
- /* Interrupt Throttle Rate */
- u32 itr_setting;
- u16 eitr_low;
- u16 eitr_high;
-
/* TX */
struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
- u64 restart_queue;
- u64 hw_csum_tx_good;
- u64 lsc_int;
- u64 hw_tso_ctxt;
- u64 hw_tso6_ctxt;
u32 tx_timeout_count;
struct list_head mac_filter_list;
-#ifdef DEBUG
- bool detect_tx_hung;
-#endif /* DEBUG */
/* RX */
struct i40e_ring *rx_rings[I40E_MAX_VSI_QP];
int txd_count;
int rxd_count;
u64 hw_csum_rx_error;
- u64 hw_rx_no_dma_resources;
- u64 hw_csum_rx_good;
- u64 non_eop_descs;
int num_msix_vectors;
struct msix_entry *msix_entries;
- u64 rx_hdr_split;
-
- u32 init_state;
volatile unsigned long flags;
#define I40EVF_FLAG_RX_CSUM_ENABLED (u32)(1)
#define I40EVF_FLAG_RX_1BUF_CAPABLE (u32)(1 << 1)
@@ -261,11 +241,9 @@ struct i40evf_adapter {
enum i40evf_state_t state;
volatile unsigned long crit_section;
- u64 tx_busy;
struct work_struct watchdog_task;
bool netdev_registered;
- bool dev_closed;
bool link_up;
enum i40e_virtchnl_ops current_op;
struct i40e_virtchnl_vf_resource *vf_res; /* incl. all VSIs */
@@ -276,11 +254,6 @@ struct i40evf_adapter {
u32 aq_wait_count;
};
-struct i40evf_info {
- enum i40e_mac_type mac;
- unsigned int flags;
-};
-
/* needed by i40evf_ethtool.c */
extern char i40evf_driver_name[];
--
1.8.5.GIT
^ permalink raw reply related
* [net-next 15/15] i40evf: refactor reset handling
From: Aaron Brown @ 2014-02-13 11:48 UTC (permalink / raw)
To: davem
Cc: Mitch Williams, netdev, gospo, sassmann, Jesse Brandeburg,
Aaron Brown
In-Reply-To: <1392292133-32120-1-git-send-email-aaron.f.brown@intel.com>
From: Mitch Williams <mitch.a.williams@intel.com>
Respond better to a VF reset event. When a reset is signaled by the
PF, or detected by the watchdog task, prevent the watchdog from
processing admin queue requests, and schedule the reset task.
In the reset task, wait first for the reset to start, then for it to
complete, then reinit the driver.
If the reset never appears to complete after a long, long time (>10
seconds is possible depending on what's going on with the PF driver),
then set a flag to indicate that PF communications have failed.
If this flag is set, check for the reset to complete in the watchdog,
and attempt to do a full reinitialization of the driver from scratch.
With these changes the VF driver correctly handles a PF reset event
while running on bare metal, or in a VM.
Also update copyrights.
Change-ID: I93513efd0b50523a8345e7f6a33a5e4f8a2a5996
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Tested-by: Sibai Li <sibai.li@intel.com>
Signed-off-by: Aaron Brown <aaron.f.brown@intel.com>
---
drivers/net/ethernet/intel/i40evf/i40evf.h | 6 +-
drivers/net/ethernet/intel/i40evf/i40evf_main.c | 148 ++++++++++++++++-----
.../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 15 ++-
3 files changed, 130 insertions(+), 39 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 5e0a344..a30c4a9 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -164,15 +164,14 @@ struct i40evf_vlan_filter {
/* Driver state. The order of these is important! */
enum i40evf_state_t {
__I40EVF_STARTUP, /* driver loaded, probe complete */
- __I40EVF_FAILED, /* PF communication failed. Fatal. */
__I40EVF_REMOVE, /* driver is being unloaded */
__I40EVF_INIT_VERSION_CHECK, /* aq msg sent, awaiting reply */
__I40EVF_INIT_GET_RESOURCES, /* aq msg sent, awaiting reply */
__I40EVF_INIT_SW, /* got resources, setting up structs */
+ __I40EVF_RESETTING, /* in reset */
/* Below here, watchdog is running */
__I40EVF_DOWN, /* ready, can be opened */
__I40EVF_TESTING, /* in ethtool self-test */
- __I40EVF_RESETTING, /* in reset */
__I40EVF_RUNNING, /* opened, working */
};
@@ -214,6 +213,8 @@ struct i40evf_adapter {
#define I40EVF_FLAG_IMIR_ENABLED (u32)(1 << 5)
#define I40EVF_FLAG_MQ_CAPABLE (u32)(1 << 6)
#define I40EVF_FLAG_NEED_LINK_UPDATE (u32)(1 << 7)
+#define I40EVF_FLAG_PF_COMMS_FAILED (u32)(1 << 8)
+#define I40EVF_FLAG_RESET_PENDING (u32)(1 << 9)
/* duplcates for common code */
#define I40E_FLAG_FDIR_ATR_ENABLED 0
#define I40E_FLAG_DCB_ENABLED 0
@@ -231,6 +232,7 @@ struct i40evf_adapter {
#define I40EVF_FLAG_AQ_CONFIGURE_QUEUES (u32)(1 << 6)
#define I40EVF_FLAG_AQ_MAP_VECTORS (u32)(1 << 7)
#define I40EVF_FLAG_AQ_HANDLE_RESET (u32)(1 << 8)
+
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index d271d3a..fe2271e 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -964,16 +964,18 @@ void i40evf_down(struct i40evf_adapter *adapter)
struct net_device *netdev = adapter->netdev;
struct i40evf_mac_filter *f;
- /* remove all MAC filters from the VSI */
+ /* remove all MAC filters */
list_for_each_entry(f, &adapter->mac_filter_list, list) {
f->remove = true;
}
- adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
- /* disable receives */
- adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
- mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
- msleep(20);
-
+ if (!(adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) &&
+ adapter->state != __I40EVF_RESETTING) {
+ adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
+ /* disable receives */
+ adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
+ mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
+ msleep(20);
+ }
netif_tx_disable(netdev);
netif_tx_stop_all_queues(netdev);
@@ -1292,19 +1294,47 @@ static void i40evf_watchdog_task(struct work_struct *work)
watchdog_task);
struct i40e_hw *hw = &adapter->hw;
- if (adapter->state < __I40EVF_DOWN)
+ if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section))
+ goto restart_watchdog;
+
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
+ dev_info(&adapter->pdev->dev, "Checking for redemption\n");
+ if ((rd32(hw, I40E_VFGEN_RSTAT) & 0x3) == I40E_VFR_VFACTIVE) {
+ /* A chance for redemption! */
+ dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n");
+ adapter->state = __I40EVF_STARTUP;
+ adapter->flags &= ~I40EVF_FLAG_PF_COMMS_FAILED;
+ schedule_delayed_work(&adapter->init_task, 10);
+ clear_bit(__I40EVF_IN_CRITICAL_TASK,
+ &adapter->crit_section);
+ /* Don't reschedule the watchdog, since we've restarted
+ * the init task. When init_task contacts the PF and
+ * gets everything set up again, it'll restart the
+ * watchdog for us. Down, boy. Sit. Stay. Woof.
+ */
+ return;
+ }
+ adapter->aq_pending = 0;
+ adapter->aq_required = 0;
+ adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
goto watchdog_done;
+ }
- if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section))
+ if ((adapter->state < __I40EVF_DOWN) ||
+ (adapter->flags & I40EVF_FLAG_RESET_PENDING))
goto watchdog_done;
- /* check for unannounced reset */
- if ((adapter->state != __I40EVF_RESETTING) &&
+ /* check for reset */
+ if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) &&
(rd32(hw, I40E_VFGEN_RSTAT) & 0x3) != I40E_VFR_VFACTIVE) {
adapter->state = __I40EVF_RESETTING;
+ adapter->flags |= I40EVF_FLAG_RESET_PENDING;
+ dev_err(&adapter->pdev->dev, "Hardware reset detected.\n");
+ dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
schedule_work(&adapter->reset_task);
- dev_info(&adapter->pdev->dev, "%s: hardware reset detected\n",
- __func__);
+ adapter->aq_pending = 0;
+ adapter->aq_required = 0;
+ adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
goto watchdog_done;
}
@@ -1359,13 +1389,15 @@ static void i40evf_watchdog_task(struct work_struct *work)
i40evf_irq_enable(adapter, true);
i40evf_fire_sw_int(adapter, 0xFF);
+
watchdog_done:
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+restart_watchdog:
if (adapter->aq_required)
mod_timer(&adapter->watchdog_timer,
jiffies + msecs_to_jiffies(20));
else
mod_timer(&adapter->watchdog_timer, jiffies + (HZ * 2));
- clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
schedule_work(&adapter->adminq_task);
}
@@ -1412,6 +1444,8 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
i40e_flush(hw);
}
+#define I40EVF_RESET_WAIT_MS 100
+#define I40EVF_RESET_WAIT_COUNT 200
/**
* i40evf_reset_task - Call-back task to handle hardware reset
* @work: pointer to work_struct
@@ -1422,8 +1456,9 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
**/
static void i40evf_reset_task(struct work_struct *work)
{
- struct i40evf_adapter *adapter =
- container_of(work, struct i40evf_adapter, reset_task);
+ struct i40evf_adapter *adapter = container_of(work,
+ struct i40evf_adapter,
+ reset_task);
struct i40e_hw *hw = &adapter->hw;
int i = 0, err;
uint32_t rstat_val;
@@ -1431,22 +1466,56 @@ static void i40evf_reset_task(struct work_struct *work)
while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
&adapter->crit_section))
udelay(500);
+ /* poll until we see the reset actually happen */
+ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
+ rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
+ I40E_VFGEN_RSTAT_VFR_STATE_MASK;
+ if (rstat_val != I40E_VFR_VFACTIVE) {
+ dev_info(&adapter->pdev->dev, "Reset now occurring\n");
+ break;
+ } else {
+ msleep(I40EVF_RESET_WAIT_MS);
+ }
+ }
+ if (i == I40EVF_RESET_WAIT_COUNT) {
+ dev_err(&adapter->pdev->dev, "Reset was not detected\n");
+ adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
+ goto continue_reset; /* act like the reset happened */
+ }
- /* wait until the reset is complete */
- for (i = 0; i < 20; i++) {
+ /* wait until the reset is complete and the PF is responding to us */
+ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
I40E_VFGEN_RSTAT_VFR_STATE_MASK;
- if (rstat_val == I40E_VFR_COMPLETED)
+ if (rstat_val == I40E_VFR_VFACTIVE) {
+ dev_info(&adapter->pdev->dev, "Reset is complete. Reinitializing.\n");
break;
- else
- mdelay(100);
+ } else {
+ msleep(I40EVF_RESET_WAIT_MS);
+ }
}
- if (i == 20) {
+ if (i == I40EVF_RESET_WAIT_COUNT) {
/* reset never finished */
- dev_info(&adapter->pdev->dev, "%s: reset never finished: %x\n",
- __func__, rstat_val);
- /* carry on anyway */
+ dev_err(&adapter->pdev->dev, "Reset never finished (%x). PF driver is dead, and so am I.\n",
+ rstat_val);
+ adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
+
+ if (netif_running(adapter->netdev))
+ i40evf_close(adapter->netdev);
+
+ i40evf_free_misc_irq(adapter);
+ i40evf_reset_interrupt_capability(adapter);
+ i40evf_free_queues(adapter);
+ kfree(adapter->vf_res);
+ i40evf_shutdown_adminq(hw);
+ adapter->netdev->flags &= ~IFF_UP;
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+ return; /* Do not attempt to reinit. It's dead, Jim. */
}
+
+continue_reset:
+ adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
+
i40evf_down(adapter);
adapter->state = __I40EVF_RESETTING;
@@ -1506,6 +1575,9 @@ static void i40evf_adminq_task(struct work_struct *work)
i40e_status ret;
u16 pending;
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
+ return;
+
event.msg_size = I40EVF_MAX_AQ_BUF_SIZE;
event.msg_buf = kzalloc(event.msg_size, GFP_KERNEL);
if (!event.msg_buf) {
@@ -1637,6 +1709,10 @@ static int i40evf_open(struct net_device *netdev)
struct i40evf_adapter *adapter = netdev_priv(netdev);
int err;
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
+ dev_err(&adapter->pdev->dev, "Unable to open device due to PF driver failure.\n");
+ return -EIO;
+ }
if (adapter->state != __I40EVF_DOWN)
return -EBUSY;
@@ -1691,8 +1767,12 @@ static int i40evf_close(struct net_device *netdev)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
+ if (adapter->state <= __I40EVF_DOWN)
+ return 0;
+
/* signal that we are down to the interrupt handler */
adapter->state = __I40EVF_DOWN;
+
set_bit(__I40E_DOWN, &adapter->vsi.state);
i40evf_down(adapter);
@@ -1843,6 +1923,8 @@ static void i40evf_init_task(struct work_struct *work)
switch (adapter->state) {
case __I40EVF_STARTUP:
/* driver loaded, probe complete */
+ adapter->flags &= ~I40EVF_FLAG_PF_COMMS_FAILED;
+ adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
err = i40e_set_mac_type(hw);
if (err) {
dev_info(&pdev->dev, "%s: set_mac_type failed: %d\n",
@@ -2006,9 +2088,11 @@ static void i40evf_init_task(struct work_struct *work)
adapter->vsi.tx_itr_setting = I40E_ITR_DYNAMIC;
adapter->vsi.netdev = adapter->netdev;
- err = register_netdev(netdev);
- if (err)
- goto err_register;
+ if (!adapter->netdev_registered) {
+ err = register_netdev(netdev);
+ if (err)
+ goto err_register;
+ }
adapter->netdev_registered = true;
@@ -2032,17 +2116,16 @@ err_register:
i40evf_free_misc_irq(adapter);
err_sw_init:
i40evf_reset_interrupt_capability(adapter);
- adapter->state = __I40EVF_FAILED;
err_alloc:
kfree(adapter->vf_res);
adapter->vf_res = NULL;
err:
+ if (hw->aq.asq.count)
+ i40evf_shutdown_adminq(hw); /* ignore error */
/* Things went into the weeds, so try again later */
if (++adapter->aq_wait_count > I40EVF_AQ_MAX_ERR) {
dev_err(&pdev->dev, "Failed to communicate with PF; giving up.\n");
- if (hw->aq.asq.count)
- i40evf_shutdown_adminq(hw); /* ignore error */
- adapter->state = __I40EVF_FAILED;
+ adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
return; /* do not reschedule */
}
schedule_delayed_work(&adapter->init_task, HZ * 3);
@@ -2272,6 +2355,7 @@ static void i40evf_remove(struct pci_dev *pdev)
struct i40e_hw *hw = &adapter->hw;
cancel_delayed_work_sync(&adapter->init_task);
+ cancel_work_sync(&adapter->reset_task);
if (adapter->netdev_registered) {
unregister_netdev(netdev);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index e6978d7..93891a1 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -43,6 +43,9 @@ static int i40evf_send_pf_msg(struct i40evf_adapter *adapter,
struct i40e_hw *hw = &adapter->hw;
i40e_status err;
+ if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
+ return 0; /* nothing to see here, move along */
+
err = i40e_aq_send_msg_to_pf(hw, op, 0, msg, len, NULL);
if (err)
dev_err(&adapter->pdev->dev, "Unable to send opcode %d to PF, error %d, aq status %d\n",
@@ -689,10 +692,12 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
}
break;
case I40E_VIRTCHNL_EVENT_RESET_IMPENDING:
- adapter->state = __I40EVF_RESETTING;
- schedule_work(&adapter->reset_task);
- dev_info(&adapter->pdev->dev,
- "%s: hardware reset pending\n", __func__);
+ dev_info(&adapter->pdev->dev, "PF reset warning received\n");
+ if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) {
+ adapter->flags |= I40EVF_FLAG_RESET_PENDING;
+ dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
+ schedule_work(&adapter->reset_task);
+ }
break;
default:
dev_err(&adapter->pdev->dev,
--
1.8.5.GIT
^ permalink raw reply related
* [PATCH next resend] tcp: use zero-window when free_space is low
From: Florian Westphal @ 2014-02-13 11:52 UTC (permalink / raw)
To: netdev; +Cc: Florian Westphal, Neal Cardwell, Eric Dumazet, Yuchung Cheng
Currently the kernel tries to announce a zero window when free_space
is below the current receiver mss estimate.
When a sender is transmitting small packets and reader consumes data
slowly (or not at all), receiver might be unable to shrink the receive
win because
a) we cannot withdraw already-commited receive window, and,
b) we have to round the current rwin up to a multiple of the wscale
factor, else we would shrink the current window.
This causes the receive buffer to fill up until the rmem limit is hit.
When this happens, we start dropping packets.
Moreover, tcp_clamp_window may continue to grow sk_rcvbuf towards rmem[2]
even if socket is not being read from.
As we cannot avoid the "current_win is rounded up to multiple of mss"
issue [we would violate a) above] at least try to prevent the receive buf
growth towards tcp_rmem[2] limit by attempting to move to zero-window
announcement when free_space becomes less than 1/16 of the current
allowed receive buffer maximum. If tcp_rmem[2] is large, this will
increase our chances to get a zero-window announcement out in time.
Reproducer:
On server:
$ nc -l -p 12345
<suspend it: CTRL-Z>
Client:
#!/usr/bin/env python
import socket
import time
sock = socket.socket()
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
sock.connect(("192.168.4.1", 12345));
while True:
sock.send('A' * 23)
time.sleep(0.005)
socket buffer on server-side will grow until tcp_rmem[2] is hit,
at which point the client rexmits data until -EDTIMEOUT:
tcp_data_queue invokes tcp_try_rmem_schedule which will call
tcp_prune_queue which calls tcp_clamp_window(). And that function will
grow sk->sk_rcvbuf up until it eventually hits tcp_rmem[2].
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
V1 of this patch was deferred, resending to get discussion going again.
Changes since v1:
- add reproducer to commit message
Unfortunately I couldn't come up with something that has no magic
('allowed >> 4') value. I chose >>4 (1/16th) because it didn't cause
tput limitations in my 'full-mss-sized, steady state' netcat tests.
Maybe someone has better idea?
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 2a69f42..fd8d821 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2145,7 +2145,8 @@ u32 __tcp_select_window(struct sock *sk)
*/
int mss = icsk->icsk_ack.rcv_mss;
int free_space = tcp_space(sk);
- int full_space = min_t(int, tp->window_clamp, tcp_full_space(sk));
+ int allowed_space = tcp_full_space(sk);
+ int full_space = min_t(int, tp->window_clamp, allowed_space);
int window;
if (mss > full_space)
@@ -2158,7 +2159,19 @@ u32 __tcp_select_window(struct sock *sk)
tp->rcv_ssthresh = min(tp->rcv_ssthresh,
4U * tp->advmss);
- if (free_space < mss)
+ /* free_space might become our new window, make sure we don't
+ * increase it due to wscale.
+ */
+ free_space = round_down(free_space, 1 << tp->rx_opt.rcv_wscale);
+
+ /* if free space is less than mss estimate, or is below 1/16th
+ * of the maximum allowed, try to move to zero-window, else
+ * tcp_clamp_window() will grow rcv buf up to tcp_rmem[2], and
+ * new incoming data is dropped due to memory limits.
+ * With large window, mss test triggers way too late in order
+ * to announce zero window in time before rmem limit kicks in.
+ */
+ if (free_space < (allowed_space >> 4) || free_space < mss)
return 0;
}
--
1.8.1.5
^ permalink raw reply related
* Re: [PATCH ipsec-next v3] xfrm: avoid creating temporary SA when there are no listeners
From: Steffen Klassert @ 2014-02-13 12:08 UTC (permalink / raw)
To: Horia Geanta; +Cc: David S. Miller, netdev
In-Reply-To: <1392214806-21966-1-git-send-email-horia.geanta@freescale.com>
On Wed, Feb 12, 2014 at 04:20:06PM +0200, Horia Geanta wrote:
> In the case when KMs have no listeners, km_query() will fail and
> temporary SAs are garbage collected immediately after their allocation.
> This causes strain on memory allocation, leading even to OOM since
> temporary SA alloc/free cycle is performed for every packet
> and garbage collection does not keep up the pace.
>
> The sane thing to do is to make sure we have audience before
> temporary SA allocation.
>
> Signed-off-by: Horia Geanta <horia.geanta@freescale.com>
Applied to ipsec-next, thanks!
^ permalink raw reply
* Re: [PATCH v5] can: add Renesas R-Car CAN driver
From: Marc Kleine-Budde @ 2014-02-13 12:12 UTC (permalink / raw)
To: Sergei Shtylyov, netdev, wg, linux-can; +Cc: linux-sh, vksavl
In-Reply-To: <52E3148E.2010608@cogentembedded.com>
[-- Attachment #1: Type: text/plain, Size: 3825 bytes --]
On 01/25/2014 02:34 AM, Sergei Shtylyov wrote:
> Hello.
>
> On 01/20/2014 12:18 PM, Marc Kleine-Budde wrote:
>
>>> Add support for the CAN controller found in Renesas R-Car SoCs.
>
>>> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
>
>>> ---
>>> The patch is against the 'linux-can-next.git' repo.
>
> [...]
>>> Index: linux-can-next/drivers/net/can/rcar_can.c
>>> ===================================================================
>>> --- /dev/null
>>> +++ linux-can-next/drivers/net/can/rcar_can.c
>>> @@ -0,0 +1,857 @@
> [...]
>>> +/* Mailbox registers structure */
>>> +struct rcar_can_mbox_regs {
>>> + u32 id; /* IDE and RTR bits, SID and EID */
>>> + u8 stub; /* Not used */
>>> + u8 dlc; /* Data Length Code - bits [0..3] */
>>> + u8 data[8]; /* Data Bytes */
>>> + u8 tsh; /* Time Stamp Higher Byte */
>>> + u8 tsl; /* Time Stamp Lower Byte */
>>> +} __packed;
>
>> If you have contact to the hardware designer please blame him for
>
> Unfortunately, we don't.
>
>> placing the data register unaligned into the register space. :)
>
> It's not even the only one or worst example of questionable register
> design in this module IMO.
>
> [...]
>>> +static void rcar_can_tx_done(struct net_device *ndev)
>>> +{
>>> + struct rcar_can_priv *priv = netdev_priv(ndev);
>>> + struct net_device_stats *stats = &ndev->stats;
>>> + int i;
>>> +
>>> + spin_lock(&priv->skb_lock);
>>> + for (i = 0; i < priv->frames_queued; i++)
>>> + can_get_echo_skb(ndev, i);
>>> + stats->tx_bytes += priv->bytes_queued;
>>> + stats->tx_packets += priv->frames_queued;
>>> + priv->bytes_queued = 0;
>>> + priv->frames_queued = 0;
>>> + spin_unlock(&priv->skb_lock);
>
>> This looks broken. What happens if you send 2 CAN frames in a row, the
>> first one is send, a TX complete interrupt is issued and you handle it
>> here? You assume, that all CAN frames have been sent.
>
> TX interrupt will be issued only when TX FIFO gets empty (all 2 frames
> have been transmitted in this case). Please see the comment to the
> RCAR_CAN_MIER1_TXFIT bit.
Does the hardware have a TX complete interrupt? If you only have TX FIFO
empty, you have to limit the FIFO depth to 1.
>>> +static irqreturn_t rcar_can_interrupt(int irq, void *dev_id)
>>> +{
>>> + struct net_device *ndev = (struct net_device *)dev_id;
>
>> the cast is not needed
>
> Removed.
>
> [...]
>>> +static void rcar_can_set_bittiming(struct net_device *dev)
>>> +{
>>> + struct rcar_can_priv *priv = netdev_priv(dev);
>>> + struct can_bittiming *bt = &priv->can.bittiming;
>>> + u32 bcr;
>>> + u8 clkr;
>>> +
>>> + /* Don't overwrite CLKR with 32-bit BCR access */
>>> + /* CLKR has 8-bit access */
>
>> Can you explain the register layout here? Why do you access BCR with 32
>> bits when the register is defined as 3x8 bit? Can't you make it a
>> standard 32 bit register?
>
> 1. According to documentation BCR is the 24-bit register.
> Actually we can consider some 32-bit register that combines BCR and
> CLKR but according to documentation there are two separate registers.
> 2. BCR has 8- ,16-, and 32-bit access (according to documentation).
> 3. This is the algorithm that the documentation suggests.
> 4. We had a driver version with byte access but 32-bit access seems
> shorter.
Please use a normal read-modify-write 32 bit access.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 242 bytes --]
^ permalink raw reply
* Re: [PATCH v3 2/2] sctp: optimize the sctp_sysctl_net_register
From: Neil Horman @ 2014-02-13 12:44 UTC (permalink / raw)
To: Wang Weidong; +Cc: davem, vyasevich, dborkman, sergei.shtylyov, netdev
In-Reply-To: <52FC1D9D.3090500@huawei.com>
On Thu, Feb 13, 2014 at 09:19:25AM +0800, Wang Weidong wrote:
> On 2014/2/12 19:53, Neil Horman wrote:
> > On Wed, Feb 12, 2014 at 09:44:44AM +0800, Wang Weidong wrote:
> >> Here, when the net is init_net, we needn't to kmemdup the ctl_table
> >> again. So add a check for net. Also we can save some memory.
> >>
> >> Signed-off-by: Wang Weidong <wangweidong1@huawei.com>
> >> ---
> >> net/sctp/sysctl.c | 17 ++++++++++-------
> >> 1 file changed, 10 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
> >> index d354de5..35c8923 100644
> >> --- a/net/sctp/sysctl.c
> >> +++ b/net/sctp/sysctl.c
> >> @@ -402,15 +402,18 @@ static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write,
> >>
> >> int sctp_sysctl_net_register(struct net *net)
> >> {
> >> - struct ctl_table *table;
> >> - int i;
> >> + struct ctl_table *table = sctp_net_table;
> >> +
> >> + if (!net_eq(net, &init_net)) {
> >> + int i;
> >>
> >> - table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
> >> - if (!table)
> >> - return -ENOMEM;
> >> + table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
> >> + if (!table)
> >> + return -ENOMEM;
> >>
> >> - for (i = 0; table[i].data; i++)
> >> - table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
> >> + for (i = 0; table[i].data; i++)
> >> + table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
> >> + }
> >>
> > In the first version of this patch you complained about a lockdep issue. Did
> > you figure out what that was, and if it related to these changes?
> >
> > Neil
> >
> >
> Hi Neil,
>
> The lockdep issue doesn't relate to these changes. I should send it
> by the another email. Sorry for confusing you.
>
> Regards
> Wang
>
Then you probably don't need to mention it in the same note that you're
proposing changes.
Acked-by: Neil Horman <nhorman@tuxdriver.com>
^ permalink raw reply
* Re: RFC: bridge get fdb by bridge device
From: Jamal Hadi Salim @ 2014-02-13 12:50 UTC (permalink / raw)
To: John Fastabend
Cc: vyasevic, netdev@vger.kernel.org, Stephen Hemminger,
Scott Feldman
In-Reply-To: <52FBC282.6020301@intel.com>
On 02/12/14 13:50, John Fastabend wrote:
>
> Just to wrap things up in one email. Changing between VEB and VEPA
> modes already triggers an event. So management applications can listen
> for this.
>
Ok - reasonable.
> And I can send out a patch to add a flag to hardware bridge devices
> I'll likely get to it next week sometime unless someone beats me
> to it.
>
You understand this better - so i will wait.
I'll send an updated version of the patch this weekend
now that net-next is open.
>
> Sure, IEEE802.1Q would call these edge relays.
>
Ok, so what older kids used to call "repeaters".
The more i think about it, the more it looks like this is
still a bridge and we have a bridgeport mode as VEPA vs VEB.
IOW, as you said - you can have a bridge with mix and match of
VEB/VEPA. We can easily add such a feature to the software bridge
as well. It sounds simple and useful enough.
>
> Because it is not the same type of object as the software bridge.
> Most notably it doesn't do learning. If anything its more like a
> macvlan device and we could just as easily tag it IFF_MACVLAN. So
> because it doesn't really match 1:1 with either of those object I
> would just presume give its own flag. Userspace can always create
> a small macro call it is_bridge_like() and check for any of the
> handful of bridge like objects.
>
I think VEB/VEP may be somehow covering the "port" aspect.
The challenge is what to call "eth0 when running in SRIOV"
>>>
>>> # ip link set dev bridge0 master bridge1
>>> RTNETLINK answers: Too many levels of symbolic links
>>>
>>
>> pourquoi? If the original rationale was to limit the
>> broadcast domain scope it sounds strange that a bridge in
>> the form a macvlan is allowed.
>>
>
> Agreed. But there it is.
>
I am sure someone knows why - Stephen?
cheers,
jamal
^ permalink raw reply
* [PATCH] drivers: net: cpsw: fix buggy loop condition
From: Heiko Schocher @ 2014-02-13 13:47 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Heiko Schocher, Mugunthan V N, David S. Miller, Sebastian Siewior,
Daniel Mack, Felipe Balbi, Markus Pargmann, netdev, linux-kernel
commit:
>From 0cd8f9cc0654c06adde353c6532114c5f53a18e8 Mon Sep 17 00:00:00 2001
From: Mugunthan V N <mugunthanvnm@ti.com>
Date: Thu, 23 Jan 2014 00:03:12 +0530
Subject: [PATCH] drivers: net: cpsw: enable promiscuous mode support
Enable promiscuous mode support for CPSW.
Introduced a crash on an am335x based board (similiar to am335x-evm).
Reason is buggy end condition in for loop in cpsw_set_promiscious()
for (i = 0; i <= priv->data.slaves; i++)
should be
for (i = 0; i < priv->data.slaves; i++)
Fix this ...
Signed-off-by: Heiko Schocher <hs@denx.de>
Cc: Mugunthan V N <mugunthanvnm@ti.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Sebastian Siewior <bigeasy@linutronix.de>
Cc: Daniel Mack <zonque@gmail.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Markus Pargmann <mpa@pengutronix.de>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
complete crash dump
[ 4.986544] Unable to handle kernel NULL pointer dereference at virtual address 00000120
[ 4.995056] pgd = c0004000
[ 4.997906] [00000120] *pgd=00000000
[ 5.001723] Internal error: Oops: 5 [#1] SMP ARM
[ 5.006560] Modules linked in:
[ 5.009773] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.14.0-rc1-01621-ga10cd7e-dirty #9
[ 5.018243] task: c607b540 ti: c607c000 task.ti: c607c000
[ 5.023914] PC is at cpsw_set_promiscious+0x1d8/0x2a4
[ 5.029204] LR is at cpsw_set_promiscious+0x1c0/0x2a4
[ 5.034494] pc : [<c0407c18>] lr : [<c0407c00>] psr: 60000113
[ 5.034494] sp : c607dd88 ip : c0984ab8 fp : c085c7cc
[ 5.046506] r10: 00000024 r9 : c64b31d8 r8 : 00000003
[ 5.051974] r7 : 00000000 r6 : c64ad800 r5 : c64b3080 r4 : 00000000
[ 5.058806] r3 : 00000003 r2 : c64b3190 r1 : 00000002 r0 : 00000000
[ 5.065641] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 5.073291] Control: 10c5387d Table: 80004019 DAC: 00000017
[ 5.079303] Process swapper/0 (pid: 1, stack limit = 0xc607c248)
[ 5.085592] Stack: (0xc607dd88 to 0xc607e000)
[ 5.090158] dd80: c64ad800 c05ff710 00000000 00000000 00000000 00001002
[ 5.098722] dda0: 0000016b c0409c44 00000000 00000000 c64ad800 c05ff710 00000000 00000000
[ 5.107287] ddc0: 00000000 00001002 0000016b c04cd8cc c64ad964 c64ad800 c64ad82c c04cd920
[ 5.115850] dde0: 00001003 c64ad800 c05ff710 c04cd9f0 00000000 c64ad800 00001002 c64ad800
[ 5.124414] de00: 00001003 00000001 00001002 c04cdc58 c64ad800 00000128 00001002 c08fe840
[ 5.132979] de20: 00000000 c04cdd58 c64ad800 00000003 c085c7e0 c08fe840 c085c7cc c0847ce8
[ 5.141543] de40: 00000000 c058fbf4 c607b540 c5b17cd0 c5b131c8 00000000 12400000 c02a1558
[ 5.150105] de60: c5b17d0c 00000000 00000002 00000000 00000000 00000000 00000002 00000000
[ 5.158669] de80: c05185b4 c00873ac 00000002 00000000 00000000 c05185b4 00000000 c607c030
[ 5.167233] dea0: c607c008 60000113 c08f79c0 c0901610 00000007 c090bdc0 c090bdc0 c08459c8
[ 5.175798] dec0: c0901610 00000007 c090bdc0 c08615ac 00000007 c090bdc0 c090bdc0 c0847b58
[ 5.184362] dee0: 000000bd c607c030 00000000 c0008918 00000004 c08bfa30 c08bfa30 60000113
[ 5.192926] df00: c0089d00 00000000 c08bfa2c 00000000 00000000 c0590db0 00000002 c607c000
[ 5.201489] df20: c7eff36d c05dd514 000000bd c005df1c c07d1ed4 00000007 c7eff419 00000007
[ 5.210054] df40: 60000113 c08615ac 00000007 c090bdc0 c090bdc0 c080450c 000000bd c084d950
[ 5.218620] df60: c084d948 c0804c1c 00000007 00000007 c080450c dfdedeff fcdfffff c7dd9ec0
[ 5.227183] df80: c607c018 00000000 c058668c 00000000 00000000 00000000 00000000 00000000
[ 5.235747] dfa0: 00000000 c0586694 00000000 c000e548 00000000 00000000 00000000 00000000
[ 5.244310] dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 5.252873] dfe0: 00000000 00000000 00000000 00000000 00000013 00000000 fffff6f7 7ffeefbf
[ 5.261448] [<c0407c18>] (cpsw_set_promiscious) from [<c0409c44>] (cpsw_ndo_set_rx_mode+0x20/0xf8)
[ 5.270841] [<c0409c44>] (cpsw_ndo_set_rx_mode) from [<c04cd8cc>] (__dev_set_rx_mode+0x5c/0x94)
[ 5.279952] [<c04cd8cc>] (__dev_set_rx_mode) from [<c04cd920>] (dev_set_rx_mode+0x1c/0x28)
[ 5.288609] [<c04cd920>] (dev_set_rx_mode) from [<c04cd9f0>] (__dev_open+0xc4/0x110)
[ 5.296720] [<c04cd9f0>] (__dev_open) from [<c04cdc58>] (__dev_change_flags+0x88/0x170)
[ 5.305102] [<c04cdc58>] (__dev_change_flags) from [<c04cdd58>] (dev_change_flags+0x18/0x48)
[ 5.313945] [<c04cdd58>] (dev_change_flags) from [<c0847ce8>] (ip_auto_config+0x190/0x110c)
[ 5.322697] [<c0847ce8>] (ip_auto_config) from [<c0008918>] (do_one_initcall+0xe8/0x148)
[ 5.331190] [<c0008918>] (do_one_initcall) from [<c0804c1c>] (kernel_init_freeable+0x104/0x1c8)
[ 5.340316] [<c0804c1c>] (kernel_init_freeable) from [<c0586694>] (kernel_init+0x8/0x118)
[ 5.348890] [<c0586694>] (kernel_init) from [<c000e548>] (ret_from_fork+0x14/0x2c)
[ 5.356821] Code: e0829009 e2888001 e5990018 e1a03008 (e5900120)
[ 5.363291] ---[ end trace ba29586f1d312ca3 ]---
[ 5.369343] Kernel panic - not syncing: Fatal exception in interrupt
[ 5.376005] drm_kms_helper: panic occurred, switching back to text console
---
drivers/net/ethernet/ti/cpsw.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index bde63e3..34b4262 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -554,7 +554,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
* common for both the interface as the interface shares
* the same hardware resource.
*/
- for (i = 0; i <= priv->data.slaves; i++)
+ for (i = 0; i < priv->data.slaves; i++)
if (priv->slaves[i].ndev->flags & IFF_PROMISC)
flag = true;
@@ -578,7 +578,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
unsigned long timeout = jiffies + HZ;
/* Disable Learn for all ports */
- for (i = 0; i <= priv->data.slaves; i++) {
+ for (i = 0; i < priv->data.slaves; i++) {
cpsw_ale_control_set(ale, i,
ALE_PORT_NOLEARN, 1);
cpsw_ale_control_set(ale, i,
@@ -606,7 +606,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 0);
/* Enable Learn for all ports */
- for (i = 0; i <= priv->data.slaves; i++) {
+ for (i = 0; i < priv->data.slaves; i++) {
cpsw_ale_control_set(ale, i,
ALE_PORT_NOLEARN, 0);
cpsw_ale_control_set(ale, i,
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH] net: cpsw: catch of_get_phy_mode failing and propagate error
From: Mugunthan V N @ 2014-02-13 14:11 UTC (permalink / raw)
To: Uwe Kleine-König, netdev; +Cc: kernel, David S. Miller
In-Reply-To: <1392244402-24823-1-git-send-email-u.kleine-koenig@pengutronix.de>
On Thursday 13 February 2014 04:03 AM, Uwe Kleine-König wrote:
> It's wrong if the device tree doesn't provide a phy-mode property for
> the cpsw slaves as it is documented to be required in
> Documentation/devicetree/bindings/net/cpsw.txt.
>
> Anyhow it's nice to catch that problem, still more as it used to work
> without this property up to commit 388367a5a9fb (drivers: net: cpsw: use
> cpsw-phy-sel driver to configure phy mode) which is in v3.13-rc1.
>
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Good catch
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
Regards
Mugunthan V N
^ permalink raw reply
* Re: [PATCH] drivers: net: cpsw: fix buggy loop condition
From: Mugunthan V N @ 2014-02-13 14:14 UTC (permalink / raw)
To: Heiko Schocher, linux-arm-kernel
Cc: David S. Miller, Sebastian Siewior, Daniel Mack, Felipe Balbi,
Markus Pargmann, netdev, linux-kernel
In-Reply-To: <1392299247-16917-1-git-send-email-hs@denx.de>
On Thursday 13 February 2014 07:17 PM, Heiko Schocher wrote:
> commit:
> From 0cd8f9cc0654c06adde353c6532114c5f53a18e8 Mon Sep 17 00:00:00 2001
> From: Mugunthan V N <mugunthanvnm@ti.com>
> Date: Thu, 23 Jan 2014 00:03:12 +0530
> Subject: [PATCH] drivers: net: cpsw: enable promiscuous mode support
>
> Enable promiscuous mode support for CPSW.
>
> Introduced a crash on an am335x based board (similiar to am335x-evm).
> Reason is buggy end condition in for loop in cpsw_set_promiscious()
>
> for (i = 0; i <= priv->data.slaves; i++)
>
> should be
>
> for (i = 0; i < priv->data.slaves; i++)
>
> Fix this ...
>
> Signed-off-by: Heiko Schocher <hs@denx.de>
> Cc: Mugunthan V N <mugunthanvnm@ti.com>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: Sebastian Siewior <bigeasy@linutronix.de>
> Cc: Daniel Mack <zonque@gmail.com>
> Cc: Felipe Balbi <balbi@ti.com>
> Cc: Markus Pargmann <mpa@pengutronix.de>
> Cc: netdev@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
Ah, it's copy paste error, thanks for the fix.
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
Regards
Mugunthan V N
^ permalink raw reply
* Re: [PATCH v2 1/2] dp83640: Support a configurable number of periodic outputs
From: Stefan Sørensen @ 2014-02-13 14:21 UTC (permalink / raw)
To: Richard Cochran
Cc: grant.likely, robh+dt, mark.rutland, netdev, linux-kernel,
devicetree
In-Reply-To: <20140211200922.GA4254@netboy>
On Tue, 2014-02-11 at 21:09 +0100, Richard Cochran wrote:
> > -#define EXT_EVENT 1
>
> Regarding this EXT_EVENT thing ...
>
> > @@ -430,12 +419,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
> > switch (rq->type) {
> > case PTP_CLK_REQ_EXTTS:
> > index = rq->extts.index;
> > - if (index < 0 || index >= N_EXT_TS)
> > + if (index < 0 || index >= n_ext_ts)
> > return -EINVAL;
> > - event_num = EXT_EVENT + index;
> > + event_num = index;
>
> there was a mapping between the "event numbers" and the external time
> stamp channels. I don't remember off the top of my head why this these
> two differ by one, but there was a good reason.
I haven't seen anything in the documentation regarding this, output
triggers 0 and 1 are special, but the events should all behave the same.
Could be be a mixup between events and pins? Pin0 means disable the
event.
> Are you sure this is still working with this change?
It has been running with event 0 in one of our products for at least the
last 3 months....
> I am especially wondering about the event decoding here:
>
> > @@ -642,7 +631,7 @@ static void recalibrate(struct dp83640_clock *clock)
> >
> > static inline u16 exts_chan_to_edata(int ch)
> > {
> > - return 1 << ((ch + EXT_EVENT) * 2);
> > + return 1 << ((ch) * 2);
> > }
>
> Maybe I am just paranoid, but can you remind me how these event
> numbers are supposed to work, before and after the change?
The mapping was hardcoded to map events 0-5 to event channels 1-6, the
periodic output trigger at channel 6 and the calibration event+trigger
both at channel 7.
The patch will (at least in v3 that I will post shortly) change both the
event and trigger mapping to a direct mapping and keep the calibration
at channel 7.
Stefan
^ permalink raw reply
* Re: [PATCH v2 2/2] dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:23 UTC (permalink / raw)
To: Richard Cochran
Cc: grant.likely, robh+dt, mark.rutland, netdev, linux-kernel,
devicetree
In-Reply-To: <20140211201917.GB4254@netboy>
On Tue, 2014-02-11 at 21:19 +0100, Richard Cochran wrote:
> > +- dp83640,slave: If present, this phy will be slave to another dp83640
> > + on the same mdio bus.
>
> Wouldn't it be more natural to have one "dp83640,master" property
> rather than multiple slave properties?
I wanted to keep the common case of a single phy simple, i.e. no need to
specify any master/slave properties.
> Most of these pr_err lines are a bit _way_ too long for coding style.
I will fix that in the next version.
Stefan
^ permalink raw reply
* [PATCH v2] net:cpsw: Pass unhandled ioctl's on to generic phy ioctl
From: Stefan Sørensen @ 2014-02-13 14:26 UTC (permalink / raw)
To: mugunthanvnm, davem, netdev; +Cc: Stefan Sørensen
This patch allows the use of a generic timestamping phy connected
to the cpsw if CPTS support is not enabled. This also adds support
of the SIOCGMIIREG and SIOCSMIIREG, and moves handling of SIOCGMIIPHY
to the generic driver.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
Changes since v1:
- Remove SIOCGMIIPHY from cpsw
- Mention that SIOCGMIIREG and SIOCSMIIREG support is gained
drivers/net/ethernet/ti/cpsw.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 1d860ce..6ecea1d 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -1471,7 +1471,6 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
struct cpsw_priv *priv = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(req);
int slave_no = cpsw_slave_index(priv);
if (!netif_running(dev))
@@ -1484,14 +1483,11 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
case SIOCGHWTSTAMP:
return cpsw_hwtstamp_get(dev, req);
#endif
- case SIOCGMIIPHY:
- data->phy_id = priv->slaves[slave_no].phy->addr;
- break;
- default:
- return -ENOTSUPP;
}
- return 0;
+ if (!priv->slaves[slave_no].phy)
+ return -EINVAL;
+ return phy_mii_ioctl(priv->slaves[slave_no].phy, req, cmd);
}
static void cpsw_ndo_tx_timeout(struct net_device *ndev)
--
1.8.5.3
^ permalink raw reply related
* [PATCH] net:phy:dp83640: Move all HW initialization to dp83640_config_init
From: Stefan Sørensen @ 2014-02-13 14:26 UTC (permalink / raw)
To: richardcochran, netdev; +Cc: Stefan Sørensen
phy_init_hw not does a full PHY reset after the driver probe has
finished, so any hw initialization done in the probe will be lost.
Part of the timestamping functionality of the dp83640 is set up in the
probe and with that lost, enabling timestamping will cause a PHY
lockup, requiring a hard reset / power cycle to recover.
This patch moves all the HW initialization in dp83640_probe to
dp83640_config_init.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 0de863c..5ff221d 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -1017,11 +1017,6 @@ static int dp83640_probe(struct phy_device *phydev)
} else
list_add_tail(&dp83640->list, &clock->phylist);
- if (clock->chosen && !list_empty(&clock->phylist))
- recalibrate(clock);
- else
- enable_broadcast(dp83640->phydev, clock->page, 1);
-
dp83640_clock_put(clock);
return 0;
@@ -1074,6 +1069,14 @@ static void dp83640_remove(struct phy_device *phydev)
static int dp83640_config_init(struct phy_device *phydev)
{
+ struct dp83640_private *dp83640 = phydev->priv;
+ struct dp83640_clock *clock = dp83640->clock;
+
+ if (clock->chosen && !list_empty(&clock->phylist))
+ recalibrate(clock);
+ else
+ enable_broadcast(phydev, clock->page, 1);
+
enable_status_frames(phydev, true);
ext_write(0, phydev, PAGE4, PTP_CTL, PTP_ENABLE);
return 0;
--
1.8.5.3
^ permalink raw reply related
* Re: [PATCH net-next 1/2] bonding: remove the redundant judgements for bond_set_mac_address()
From: Jiri Pirko @ 2014-02-13 14:29 UTC (permalink / raw)
To: Ding Tianhong; +Cc: fubar, vfalico, andy, davem, netdev
In-Reply-To: <1392188330-17208-2-git-send-email-dingtianhong@huawei.com>
Wed, Feb 12, 2014 at 07:58:49AM CET, dingtianhong@huawei.com wrote:
>The dev_set_mac_address() will check the dev->netdev_ops->ndo_set_mac_address,
>so no need to check it in bond_set_mac_address().
>
>Cc: Jay Vosburgh <fubar@us.ibm.com>
>Cc: Veaceslav Falico <vfalico@redhat.com>
>Cc: Andy Gospodarek <andy@greyhouse.net>
>Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
Reviewed-by: Jiri Pirko <jiri@resnulli.us>
^ permalink raw reply
* Re: [PATCH net-next 2/2] bonding: remove the redundant judgements for bond_option_queue_id_set()
From: Jiri Pirko @ 2014-02-13 14:30 UTC (permalink / raw)
To: Ding Tianhong; +Cc: fubar, vfalico, andy, davem, netdev
In-Reply-To: <1392188330-17208-3-git-send-email-dingtianhong@huawei.com>
Wed, Feb 12, 2014 at 07:58:50AM CET, dingtianhong@huawei.com wrote:
>The dev_valid_name() will check the buffer length for input name, no need to
>check it twice.
>
>Cc: Jay Vosburgh <fubar@us.ibm.com>
>Cc: Veaceslav Falico <vfalico@redhat.com>
>Cc: Andy Gospodarek <andy@greyhouse.net>
>Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
Reviewed-by: Jiri Pirko <jiri@resnulli.us>
^ permalink raw reply
* [PATCH v3 1/3] net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
Periodic output triggers 0 and 1 of the dp83640 has a programmable
duty-cycle which is controlled by the Pulsewidth2 field of the trigger
data register. This field is not documented in the datasheet, but it
is described in the "PHYTER Software Development Guide" section
3.1.4.1. Failing to set the field can result in unstable/no trigger
output.
This patch add programming of the Pulsewidth2 field, setting it to the
same value as the Pulsewidth field for a 50% duty cycle.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 5ff221d..a370814 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -312,6 +312,11 @@ static void periodic_output(struct dp83640_clock *clock,
ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
+ /* Triggers 0 and 1 has programmable pulsewidth2 */
+ if (trigger == 0 || trigger == 1) {
+ ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff);
+ ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16);
+ }
/*enable trigger*/
val &= ~TRIG_LOAD;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 2/3] net:phy:dp83640: Support a configurable number of periodic outputs
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
The driver is currently limited to a single periodic output. This patch makes
the number of peridodic output dynamic by dropping the gpio_tab module
parameter and adding calibrate_pin, perout_pins, and extts_pins parameters.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 73 ++++++++++++++++++++---------------------------
1 file changed, 31 insertions(+), 42 deletions(-)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index a370814..28a6e1d 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -38,15 +38,12 @@
#define LAYER4 0x02
#define LAYER2 0x01
#define MAX_RXTS 64
-#define N_EXT_TS 6
+#define N_EXT 8
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
#define PSF_RX 0x2000
#define PSF_TX 0x1000
-#define EXT_EVENT 1
-#define CAL_EVENT 7
#define CAL_TRIGGER 7
-#define PER_TRIGGER 6
#define MII_DP83640_MICR 0x11
#define MII_DP83640_MISR 0x12
@@ -146,32 +143,24 @@ struct dp83640_clock {
struct ptp_clock *ptp_clock;
};
-/* globals */
-
-enum {
- CALIBRATE_GPIO,
- PEROUT_GPIO,
- EXTTS0_GPIO,
- EXTTS1_GPIO,
- EXTTS2_GPIO,
- EXTTS3_GPIO,
- EXTTS4_GPIO,
- EXTTS5_GPIO,
- GPIO_TABLE_SIZE
-};
static int chosen_phy = -1;
-static ushort gpio_tab[GPIO_TABLE_SIZE] = {
- 1, 2, 3, 4, 8, 9, 10, 11
-};
+static int calibrate_pin = 1;
+static int perout_pins[N_EXT] = {2};
+static int n_per_out = 1;
+static int extts_pins[N_EXT] = {3, 4, 8, 9, 10, 11};
+static int n_ext_ts = 6;
module_param(chosen_phy, int, 0444);
-module_param_array(gpio_tab, ushort, NULL, 0444);
+module_param(calibrate_pin, int, 0444);
+module_param_array(perout_pins, int, &n_per_out, 0444);
+module_param_array(extts_pins, int, &n_ext_ts, 0444);
MODULE_PARM_DESC(chosen_phy, \
"The address of the PHY to use for the ancillary clock features");
-MODULE_PARM_DESC(gpio_tab, \
- "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
+MODULE_PARM_DESC(calibrate_pin, "Which pin to use for calibration");
+MODULE_PARM_DESC(perout_pins, "Which pins to use for periodic output");
+MODULE_PARM_DESC(extts_pins, "Which pins to use for external timestamping");
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
@@ -267,15 +256,15 @@ static u64 phy2txts(struct phy_txts *p)
}
static void periodic_output(struct dp83640_clock *clock,
- struct ptp_clock_request *clkreq, bool on)
+ struct ptp_clock_request *clkreq, int trigger,
+ bool on)
{
struct dp83640_private *dp83640 = clock->chosen;
struct phy_device *phydev = dp83640->phydev;
u32 sec, nsec, period;
- u16 gpio, ptp_trig, trigger, val;
+ u16 gpio, ptp_trig, val;
- gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
- trigger = PER_TRIGGER;
+ gpio = on ? perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -446,12 +435,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= N_EXT_TS)
+ if (index < 0 || index >= n_ext_ts)
return -EINVAL;
- event_num = EXT_EVENT + index;
+ event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = gpio_tab[EXTTS0_GPIO + index];
+ gpio_num = extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -462,9 +451,10 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
return 0;
case PTP_CLK_REQ_PEROUT:
- if (rq->perout.index != 0)
+ index = rq->perout.index;
+ if (index < 0 || index >= n_per_out)
return -EINVAL;
- periodic_output(clock, rq, on);
+ periodic_output(clock, rq, index, on);
return 0;
default:
@@ -557,10 +547,9 @@ static void recalibrate(struct dp83640_clock *clock)
struct list_head *this;
struct dp83640_private *tmp;
struct phy_device *master = clock->chosen->phydev;
- u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val;
+ u16 cfg0, evnt, ptp_trig, trigger, val;
trigger = CAL_TRIGGER;
- cal_gpio = gpio_tab[CALIBRATE_GPIO];
mutex_lock(&clock->extreg_lock);
@@ -583,8 +572,8 @@ static void recalibrate(struct dp83640_clock *clock)
* enable an event timestamp
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
- evnt |= (CAL_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (cal_gpio & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+ evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -597,7 +586,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (cal_gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -661,7 +650,7 @@ static void recalibrate(struct dp83640_clock *clock)
static inline u16 exts_chan_to_edata(int ch)
{
- return 1 << ((ch + EXT_EVENT) * 2);
+ return 1 << ((ch) * 2);
}
static int decode_evnt(struct dp83640_private *dp83640,
@@ -695,14 +684,14 @@ static int decode_evnt(struct dp83640_private *dp83640,
parsed = words + 2;
} else {
parsed = words + 1;
- i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
+ i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK);
ext_status = exts_chan_to_edata(i);
}
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < N_EXT_TS; i++) {
+ for (i = 0; i < n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -908,8 +897,8 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = N_EXT_TS;
- clock->caps.n_per_out = 1;
+ clock->caps.n_ext_ts = n_ext_ts;
+ clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 3/3] net:phy:dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
This patch adds configuration of the periodic output and external timestamp
pins available on the dp83640 family of PHYs. It also configures the
master/slave relationship in a group of PHYs on the same MDIO bus and the pins
used for clock calibration in the group.
The configuration is retrieved from DT through the properties
dp83640,slave
dp83640,calibrate-pin
dp83640,perout-pins
dp83640,extts-pins
The configuration module parameters are retained as fallback for the non-DT
case.
Since the pin configuration is now stored for each clock device, groups of
devices on different mdio busses can now have different pin configurations.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
Documentation/devicetree/bindings/net/dp83640.txt | 29 +++++
drivers/net/phy/dp83640.c | 139 ++++++++++++++++++----
2 files changed, 143 insertions(+), 25 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
diff --git a/Documentation/devicetree/bindings/net/dp83640.txt b/Documentation/devicetree/bindings/net/dp83640.txt
new file mode 100644
index 0000000..b9a57c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dp83640.txt
@@ -0,0 +1,29 @@
+Required properties for the National DP83640 ethernet phy:
+
+- compatible : Must contain "national,dp83640"
+
+Optional properties:
+
+- dp83640,slave: If present, this phy will be slave to another dp83640
+ on the same mdio bus.
+- dp83640,perout-pins : List of the pin pins used for periodic output
+ triggers.
+- dp83640,extts-pins : List of the pin pins used for external event
+ timestamping.
+- dp83640,calibrate-pin : The pin used for master/slave calibration.
+
+Example:
+
+ ethernet-phy@1 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ dp83640,perout-pins = <2>;
+ dp83640,extts-pins = <3 4 8 9 10 11>;
+ dp83640,calibrate-pin = <1>;
+ };
+
+ ethernet-phy@2 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <2>;
+ dp83640,slave;
+ };
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 28a6e1d..077bdc2 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -30,6 +30,7 @@
#include <linux/phy.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
+#include <linux/of_device.h>
#include "dp83640_reg.h"
@@ -120,6 +121,8 @@ struct dp83640_private {
/* queues of incoming and outgoing packets */
struct sk_buff_head rx_queue;
struct sk_buff_head tx_queue;
+ /* is this phyter a slave */
+ bool slave;
};
struct dp83640_clock {
@@ -141,6 +144,7 @@ struct dp83640_clock {
struct list_head phylist;
/* reference to our PTP hardware clock */
struct ptp_clock *ptp_clock;
+ u32 perout_pins[N_EXT], extts_pins[N_EXT], calibrate_pin;
};
@@ -264,7 +268,7 @@ static void periodic_output(struct dp83640_clock *clock,
u32 sec, nsec, period;
u16 gpio, ptp_trig, val;
- gpio = on ? perout_pins[trigger] : 0;
+ gpio = on ? clock->perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -435,12 +439,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= n_ext_ts)
+ if (index < 0 || index >= clock->caps.n_ext_ts)
return -EINVAL;
event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = extts_pins[index];
+ gpio_num = clock->extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -452,7 +456,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
case PTP_CLK_REQ_PEROUT:
index = rq->perout.index;
- if (index < 0 || index >= n_per_out)
+ if (index < 0 || index >= clock->caps.n_per_out)
return -EINVAL;
periodic_output(clock, rq, index, on);
return 0;
@@ -573,7 +577,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (clock->calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -586,7 +590,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (clock->calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -691,7 +695,7 @@ static int decode_evnt(struct dp83640_private *dp83640,
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < n_ext_ts; i++) {
+ for (i = 0; i < dp83640->clock->caps.n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -893,12 +897,11 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
mutex_init(&clock->extreg_lock);
mutex_init(&clock->clock_lock);
INIT_LIST_HEAD(&clock->phylist);
+ clock->calibrate_pin = -1;
clock->caps.owner = THIS_MODULE;
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = n_ext_ts;
- clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
@@ -911,18 +914,6 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
get_device(&bus->dev);
}
-static int choose_this_phy(struct dp83640_clock *clock,
- struct phy_device *phydev)
-{
- if (chosen_phy == -1 && !clock->chosen)
- return 1;
-
- if (chosen_phy == phydev->addr)
- return 1;
-
- return 0;
-}
-
static struct dp83640_clock *dp83640_clock_get(struct dp83640_clock *clock)
{
if (clock)
@@ -968,6 +959,86 @@ static void dp83640_clock_put(struct dp83640_clock *clock)
mutex_unlock(&clock->clock_lock);
}
+#ifdef CONFIG_OF
+static int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ struct dp83640_clock *clock = dp83640->clock;
+ struct property *prop;
+ int err, proplen, n_cal = 0;
+
+ dp83640->slave = of_property_read_bool(node, "dp83640,slave");
+ if (!dp83640->slave && clock->chosen) {
+ pr_err("More then one dp83640 master on the same bus");
+ return -EINVAL;
+ }
+
+ prop = of_find_property(node, "dp83640,calibrate-pin", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have calibrate pin");
+ return -EINVAL;
+ }
+ of_property_read_u32(node, "dp83640,calibrate-pin",
+ &clock->calibrate_pin);
+ n_cal = 1;
+ }
+
+ prop = of_find_property(node, "dp83640,perout-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have perout pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_per_out = proplen / sizeof(u32);
+ if (clock->caps.n_per_out + n_cal > N_EXT) {
+ pr_err("Too many dp83640,perout-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,perout-pins",
+ clock->perout_pins,
+ clock->caps.n_per_out);
+ if (err < 0)
+ return err;
+ }
+
+ prop = of_find_property(node, "dp83640,extts-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have extts pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_ext_ts = proplen / sizeof(u32);
+ if (clock->caps.n_ext_ts + n_cal > N_EXT) {
+ pr_err("Too many dp83640,extts-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,extts-pins",
+ clock->extts_pins,
+ clock->caps.n_ext_ts);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id dp83640_of_match_table[] = {
+ { .compatible = "national,dp83640", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dp83640_of_match_table);
+#else
+
+static inline int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ return 0;
+}
+#endif
+
static int dp83640_probe(struct phy_device *phydev)
{
struct dp83640_clock *clock;
@@ -985,7 +1056,24 @@ static int dp83640_probe(struct phy_device *phydev)
if (!dp83640)
goto no_memory;
+ dp83640->clock = clock;
dp83640->phydev = phydev;
+
+ if (phydev->dev.of_node) {
+ err = dp83640_probe_dt(phydev->dev.of_node, dp83640);
+ if (err)
+ return err;
+ } else {
+ clock->calibrate_pin = calibrate_pin;
+ memcpy(clock->perout_pins, perout_pins,
+ sizeof(clock->perout_pins));
+ memcpy(clock->extts_pins, extts_pins,
+ sizeof(clock->extts_pins));
+ if (clock->chosen ||
+ (chosen_phy != -1 && phydev->addr != chosen_phy))
+ dp83640->slave = true;
+ }
+
INIT_WORK(&dp83640->ts_work, rx_timestamp_work);
INIT_LIST_HEAD(&dp83640->rxts);
@@ -999,9 +1087,7 @@ static int dp83640_probe(struct phy_device *phydev)
skb_queue_head_init(&dp83640->rx_queue);
skb_queue_head_init(&dp83640->tx_queue);
- dp83640->clock = clock;
-
- if (choose_this_phy(clock, phydev)) {
+ if (!dp83640->slave) {
clock->chosen = dp83640;
clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev);
if (IS_ERR(clock->ptp_clock)) {
@@ -1353,7 +1439,10 @@ static struct phy_driver dp83640_driver = {
.hwtstamp = dp83640_hwtstamp,
.rxtstamp = dp83640_rxtstamp,
.txtstamp = dp83640_txtstamp,
- .driver = {.owner = THIS_MODULE,}
+ .driver = {
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dp83640_of_match_table),
+ }
};
static int __init dp83640_init(void)
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 0/3] dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
This patch series add DT configuration to the DP83640 PHY driver and makes
the configuration of periodic output pins dynamic.
Changes since v2:
- Add patch to properly configure perout triggers 0+1
- Keep extts and perout numbers separate
- Shorten pr_err lines
Changes since v1:
- Add bindings documentation
- Keep module parameters
- Rename gpio->pin
- Split patch into DT part and dynamic periodic output support
Stefan Sørensen (3):
net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
net:phy:dp83640: Support a configurable number of periodic outputs
net:phy:dp83640: Get pin and master/slave configuration from DT
Documentation/devicetree/bindings/net/dp83640.txt | 29 ++++
drivers/net/phy/dp83640.c | 199 +++++++++++++++-------
2 files changed, 170 insertions(+), 58 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
--
1.8.5.3
^ permalink raw reply
* [PATCH v3 2/3] net:phy:dp83640: Support a configurable number of periodic outputs
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
The driver is currently limited to a single periodic output. This patch makes
the number of peridodic output dynamic by dropping the gpio_tab module
parameter and adding calibrate_pin, perout_pins, and extts_pins parameters.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
drivers/net/phy/dp83640.c | 73 ++++++++++++++++++++---------------------------
1 file changed, 31 insertions(+), 42 deletions(-)
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index a370814..28a6e1d 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -38,15 +38,12 @@
#define LAYER4 0x02
#define LAYER2 0x01
#define MAX_RXTS 64
-#define N_EXT_TS 6
+#define N_EXT 8
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
#define PSF_RX 0x2000
#define PSF_TX 0x1000
-#define EXT_EVENT 1
-#define CAL_EVENT 7
#define CAL_TRIGGER 7
-#define PER_TRIGGER 6
#define MII_DP83640_MICR 0x11
#define MII_DP83640_MISR 0x12
@@ -146,32 +143,24 @@ struct dp83640_clock {
struct ptp_clock *ptp_clock;
};
-/* globals */
-
-enum {
- CALIBRATE_GPIO,
- PEROUT_GPIO,
- EXTTS0_GPIO,
- EXTTS1_GPIO,
- EXTTS2_GPIO,
- EXTTS3_GPIO,
- EXTTS4_GPIO,
- EXTTS5_GPIO,
- GPIO_TABLE_SIZE
-};
static int chosen_phy = -1;
-static ushort gpio_tab[GPIO_TABLE_SIZE] = {
- 1, 2, 3, 4, 8, 9, 10, 11
-};
+static int calibrate_pin = 1;
+static int perout_pins[N_EXT] = {2};
+static int n_per_out = 1;
+static int extts_pins[N_EXT] = {3, 4, 8, 9, 10, 11};
+static int n_ext_ts = 6;
module_param(chosen_phy, int, 0444);
-module_param_array(gpio_tab, ushort, NULL, 0444);
+module_param(calibrate_pin, int, 0444);
+module_param_array(perout_pins, int, &n_per_out, 0444);
+module_param_array(extts_pins, int, &n_ext_ts, 0444);
MODULE_PARM_DESC(chosen_phy, \
"The address of the PHY to use for the ancillary clock features");
-MODULE_PARM_DESC(gpio_tab, \
- "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
+MODULE_PARM_DESC(calibrate_pin, "Which pin to use for calibration");
+MODULE_PARM_DESC(perout_pins, "Which pins to use for periodic output");
+MODULE_PARM_DESC(extts_pins, "Which pins to use for external timestamping");
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
@@ -267,15 +256,15 @@ static u64 phy2txts(struct phy_txts *p)
}
static void periodic_output(struct dp83640_clock *clock,
- struct ptp_clock_request *clkreq, bool on)
+ struct ptp_clock_request *clkreq, int trigger,
+ bool on)
{
struct dp83640_private *dp83640 = clock->chosen;
struct phy_device *phydev = dp83640->phydev;
u32 sec, nsec, period;
- u16 gpio, ptp_trig, trigger, val;
+ u16 gpio, ptp_trig, val;
- gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
- trigger = PER_TRIGGER;
+ gpio = on ? perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -446,12 +435,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= N_EXT_TS)
+ if (index < 0 || index >= n_ext_ts)
return -EINVAL;
- event_num = EXT_EVENT + index;
+ event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = gpio_tab[EXTTS0_GPIO + index];
+ gpio_num = extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -462,9 +451,10 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
return 0;
case PTP_CLK_REQ_PEROUT:
- if (rq->perout.index != 0)
+ index = rq->perout.index;
+ if (index < 0 || index >= n_per_out)
return -EINVAL;
- periodic_output(clock, rq, on);
+ periodic_output(clock, rq, index, on);
return 0;
default:
@@ -557,10 +547,9 @@ static void recalibrate(struct dp83640_clock *clock)
struct list_head *this;
struct dp83640_private *tmp;
struct phy_device *master = clock->chosen->phydev;
- u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val;
+ u16 cfg0, evnt, ptp_trig, trigger, val;
trigger = CAL_TRIGGER;
- cal_gpio = gpio_tab[CALIBRATE_GPIO];
mutex_lock(&clock->extreg_lock);
@@ -583,8 +572,8 @@ static void recalibrate(struct dp83640_clock *clock)
* enable an event timestamp
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
- evnt |= (CAL_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (cal_gpio & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+ evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -597,7 +586,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (cal_gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -661,7 +650,7 @@ static void recalibrate(struct dp83640_clock *clock)
static inline u16 exts_chan_to_edata(int ch)
{
- return 1 << ((ch + EXT_EVENT) * 2);
+ return 1 << ((ch) * 2);
}
static int decode_evnt(struct dp83640_private *dp83640,
@@ -695,14 +684,14 @@ static int decode_evnt(struct dp83640_private *dp83640,
parsed = words + 2;
} else {
parsed = words + 1;
- i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
+ i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK);
ext_status = exts_chan_to_edata(i);
}
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < N_EXT_TS; i++) {
+ for (i = 0; i < n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -908,8 +897,8 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = N_EXT_TS;
- clock->caps.n_per_out = 1;
+ clock->caps.n_ext_ts = n_ext_ts;
+ clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 3/3] net:phy:dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
In-Reply-To: <1392302129-24947-1-git-send-email-stefan.sorensen@spectralink.com>
This patch adds configuration of the periodic output and external timestamp
pins available on the dp83640 family of PHYs. It also configures the
master/slave relationship in a group of PHYs on the same MDIO bus and the pins
used for clock calibration in the group.
The configuration is retrieved from DT through the properties
dp83640,slave
dp83640,calibrate-pin
dp83640,perout-pins
dp83640,extts-pins
The configuration module parameters are retained as fallback for the non-DT
case.
Since the pin configuration is now stored for each clock device, groups of
devices on different mdio busses can now have different pin configurations.
Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
---
Documentation/devicetree/bindings/net/dp83640.txt | 29 +++++
drivers/net/phy/dp83640.c | 139 ++++++++++++++++++----
2 files changed, 143 insertions(+), 25 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
diff --git a/Documentation/devicetree/bindings/net/dp83640.txt b/Documentation/devicetree/bindings/net/dp83640.txt
new file mode 100644
index 0000000..b9a57c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dp83640.txt
@@ -0,0 +1,29 @@
+Required properties for the National DP83640 ethernet phy:
+
+- compatible : Must contain "national,dp83640"
+
+Optional properties:
+
+- dp83640,slave: If present, this phy will be slave to another dp83640
+ on the same mdio bus.
+- dp83640,perout-pins : List of the pin pins used for periodic output
+ triggers.
+- dp83640,extts-pins : List of the pin pins used for external event
+ timestamping.
+- dp83640,calibrate-pin : The pin used for master/slave calibration.
+
+Example:
+
+ ethernet-phy@1 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ dp83640,perout-pins = <2>;
+ dp83640,extts-pins = <3 4 8 9 10 11>;
+ dp83640,calibrate-pin = <1>;
+ };
+
+ ethernet-phy@2 {
+ compatible = "national,dp83640", "ethernet-phy-ieee802.3-c22";
+ reg = <2>;
+ dp83640,slave;
+ };
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 28a6e1d..077bdc2 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -30,6 +30,7 @@
#include <linux/phy.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
+#include <linux/of_device.h>
#include "dp83640_reg.h"
@@ -120,6 +121,8 @@ struct dp83640_private {
/* queues of incoming and outgoing packets */
struct sk_buff_head rx_queue;
struct sk_buff_head tx_queue;
+ /* is this phyter a slave */
+ bool slave;
};
struct dp83640_clock {
@@ -141,6 +144,7 @@ struct dp83640_clock {
struct list_head phylist;
/* reference to our PTP hardware clock */
struct ptp_clock *ptp_clock;
+ u32 perout_pins[N_EXT], extts_pins[N_EXT], calibrate_pin;
};
@@ -264,7 +268,7 @@ static void periodic_output(struct dp83640_clock *clock,
u32 sec, nsec, period;
u16 gpio, ptp_trig, val;
- gpio = on ? perout_pins[trigger] : 0;
+ gpio = on ? clock->perout_pins[trigger] : 0;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
@@ -435,12 +439,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
index = rq->extts.index;
- if (index < 0 || index >= n_ext_ts)
+ if (index < 0 || index >= clock->caps.n_ext_ts)
return -EINVAL;
event_num = index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) {
- gpio_num = extts_pins[index];
+ gpio_num = clock->extts_pins[index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
if (rq->extts.flags & PTP_FALLING_EDGE)
evnt |= EVNT_FALL;
@@ -452,7 +456,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
case PTP_CLK_REQ_PEROUT:
index = rq->perout.index;
- if (index < 0 || index >= n_per_out)
+ if (index < 0 || index >= clock->caps.n_per_out)
return -EINVAL;
periodic_output(clock, rq, index, on);
return 0;
@@ -573,7 +577,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE;
evnt |= (trigger & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
- evnt |= (calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+ evnt |= (clock->calibrate_pin & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
list_for_each(this, &clock->phylist) {
tmp = list_entry(this, struct dp83640_private, list);
@@ -586,7 +590,7 @@ static void recalibrate(struct dp83640_clock *clock)
*/
ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE;
ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT;
- ptp_trig |= (calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
+ ptp_trig |= (clock->calibrate_pin & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT;
ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig);
/* load trigger */
@@ -691,7 +695,7 @@ static int decode_evnt(struct dp83640_private *dp83640,
event.type = PTP_CLOCK_EXTTS;
event.timestamp = phy2txts(&dp83640->edata);
- for (i = 0; i < n_ext_ts; i++) {
+ for (i = 0; i < dp83640->clock->caps.n_ext_ts; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event);
@@ -893,12 +897,11 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
mutex_init(&clock->extreg_lock);
mutex_init(&clock->clock_lock);
INIT_LIST_HEAD(&clock->phylist);
+ clock->calibrate_pin = -1;
clock->caps.owner = THIS_MODULE;
sprintf(clock->caps.name, "dp83640 timer");
clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0;
- clock->caps.n_ext_ts = n_ext_ts;
- clock->caps.n_per_out = n_per_out;
clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime;
@@ -911,18 +914,6 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
get_device(&bus->dev);
}
-static int choose_this_phy(struct dp83640_clock *clock,
- struct phy_device *phydev)
-{
- if (chosen_phy == -1 && !clock->chosen)
- return 1;
-
- if (chosen_phy == phydev->addr)
- return 1;
-
- return 0;
-}
-
static struct dp83640_clock *dp83640_clock_get(struct dp83640_clock *clock)
{
if (clock)
@@ -968,6 +959,86 @@ static void dp83640_clock_put(struct dp83640_clock *clock)
mutex_unlock(&clock->clock_lock);
}
+#ifdef CONFIG_OF
+static int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ struct dp83640_clock *clock = dp83640->clock;
+ struct property *prop;
+ int err, proplen, n_cal = 0;
+
+ dp83640->slave = of_property_read_bool(node, "dp83640,slave");
+ if (!dp83640->slave && clock->chosen) {
+ pr_err("More then one dp83640 master on the same bus");
+ return -EINVAL;
+ }
+
+ prop = of_find_property(node, "dp83640,calibrate-pin", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have calibrate pin");
+ return -EINVAL;
+ }
+ of_property_read_u32(node, "dp83640,calibrate-pin",
+ &clock->calibrate_pin);
+ n_cal = 1;
+ }
+
+ prop = of_find_property(node, "dp83640,perout-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have perout pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_per_out = proplen / sizeof(u32);
+ if (clock->caps.n_per_out + n_cal > N_EXT) {
+ pr_err("Too many dp83640,perout-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,perout-pins",
+ clock->perout_pins,
+ clock->caps.n_per_out);
+ if (err < 0)
+ return err;
+ }
+
+ prop = of_find_property(node, "dp83640,extts-pins", &proplen);
+ if (prop) {
+ if (dp83640->slave) {
+ pr_err("dp83640 slave cannot have extts pins");
+ return -EINVAL;
+ }
+
+ clock->caps.n_ext_ts = proplen / sizeof(u32);
+ if (clock->caps.n_ext_ts + n_cal > N_EXT) {
+ pr_err("Too many dp83640,extts-pins");
+ return -EINVAL;
+ }
+ err = of_property_read_u32_array(node, "dp83640,extts-pins",
+ clock->extts_pins,
+ clock->caps.n_ext_ts);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id dp83640_of_match_table[] = {
+ { .compatible = "national,dp83640", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dp83640_of_match_table);
+#else
+
+static inline int dp83640_probe_dt(struct device_node *node,
+ struct dp83640_private *dp83640)
+{
+ return 0;
+}
+#endif
+
static int dp83640_probe(struct phy_device *phydev)
{
struct dp83640_clock *clock;
@@ -985,7 +1056,24 @@ static int dp83640_probe(struct phy_device *phydev)
if (!dp83640)
goto no_memory;
+ dp83640->clock = clock;
dp83640->phydev = phydev;
+
+ if (phydev->dev.of_node) {
+ err = dp83640_probe_dt(phydev->dev.of_node, dp83640);
+ if (err)
+ return err;
+ } else {
+ clock->calibrate_pin = calibrate_pin;
+ memcpy(clock->perout_pins, perout_pins,
+ sizeof(clock->perout_pins));
+ memcpy(clock->extts_pins, extts_pins,
+ sizeof(clock->extts_pins));
+ if (clock->chosen ||
+ (chosen_phy != -1 && phydev->addr != chosen_phy))
+ dp83640->slave = true;
+ }
+
INIT_WORK(&dp83640->ts_work, rx_timestamp_work);
INIT_LIST_HEAD(&dp83640->rxts);
@@ -999,9 +1087,7 @@ static int dp83640_probe(struct phy_device *phydev)
skb_queue_head_init(&dp83640->rx_queue);
skb_queue_head_init(&dp83640->tx_queue);
- dp83640->clock = clock;
-
- if (choose_this_phy(clock, phydev)) {
+ if (!dp83640->slave) {
clock->chosen = dp83640;
clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev);
if (IS_ERR(clock->ptp_clock)) {
@@ -1353,7 +1439,10 @@ static struct phy_driver dp83640_driver = {
.hwtstamp = dp83640_hwtstamp,
.rxtstamp = dp83640_rxtstamp,
.txtstamp = dp83640_txtstamp,
- .driver = {.owner = THIS_MODULE,}
+ .driver = {
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(dp83640_of_match_table),
+ }
};
static int __init dp83640_init(void)
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 0/3] dp83640: Get pin and master/slave configuration from DT
From: Stefan Sørensen @ 2014-02-13 14:35 UTC (permalink / raw)
To: richardcochran, grant.likely, robh+dt, mark.rutland, devicetree,
linux-kernel, netdev
Cc: Stefan Sørensen
This patch series add DT configuration to the DP83640 PHY driver and makes
the configuration of periodic output pins dynamic.
Changes since v2:
- Add patch to properly configure perout triggers 0+1
- Keep extts and perout numbers separate
- Shorten pr_err lines
Changes since v1:
- Add bindings documentation
- Keep module parameters
- Rename gpio->pin
- Split patch into DT part and dynamic periodic output support
Stefan Sørensen (3):
net:phy:dp83640: Program pulsewidth2 values of perout triggers 0 and 1
net:phy:dp83640: Support a configurable number of periodic outputs
net:phy:dp83640: Get pin and master/slave configuration from DT
Documentation/devicetree/bindings/net/dp83640.txt | 29 ++++
drivers/net/phy/dp83640.c | 199 +++++++++++++++-------
2 files changed, 170 insertions(+), 58 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dp83640.txt
--
1.8.5.3
^ permalink raw reply
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