#include <asm/qe.h>
#include <asm/ucc.h>
#include <asm/ucc_fast.h>
+#include <asm/machdep.h>
#include "ucc_geth.h"
#include "fsl_pq_mdio.h"
u16 __iomem *mrblr_register)
{
/* max_rx_buf_len value must be a multiple of 128 */
- if ((max_rx_buf_len == 0)
- || (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT))
+ if ((max_rx_buf_len == 0) ||
+ (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT))
return -EINVAL;
out_be16(mrblr_register, max_rx_buf_len);
struct ucc_geth __iomem *ug_regs;
struct ucc_fast __iomem *uf_regs;
int ret_val;
- u32 upsmr, maccfg2, tbiBaseAddress;
+ u32 upsmr, maccfg2;
u16 value;
ugeth_vdbg("%s: IN", __func__);
/* Note that this depends on proper setting in utbipar register. */
if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
- tbiBaseAddress = in_be32(&ug_regs->utbipar);
- tbiBaseAddress &= UTBIPAR_PHY_ADDRESS_MASK;
- tbiBaseAddress >>= UTBIPAR_PHY_ADDRESS_SHIFT;
- value = ugeth->phydev->bus->read(ugeth->phydev->bus,
- (u8) tbiBaseAddress, ENET_TBI_MII_CR);
+ struct ucc_geth_info *ug_info = ugeth->ug_info;
+ struct phy_device *tbiphy;
+
+ if (!ug_info->tbi_node)
+ ugeth_warn("TBI mode requires that the device "
+ "tree specify a tbi-handle\n");
+
+ tbiphy = of_phy_find_device(ug_info->tbi_node);
+ if (!tbiphy)
+ ugeth_warn("Could not get TBI device\n");
+
+ value = phy_read(tbiphy, ENET_TBI_MII_CR);
value &= ~0x1000; /* Turn off autonegotiation */
- ugeth->phydev->bus->write(ugeth->phydev->bus,
- (u8) tbiBaseAddress, ENET_TBI_MII_CR, value);
+ phy_write(tbiphy, ENET_TBI_MII_CR, value);
}
init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2);
return 0;
}
+static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
+{
+ struct ucc_fast_private *uccf;
+ u32 cecr_subblock;
+ u32 temp;
+ int i = 10;
+
+ uccf = ugeth->uccf;
+
+ /* Mask GRACEFUL STOP TX interrupt bit and clear it */
+ clrbits32(uccf->p_uccm, UCC_GETH_UCCE_GRA);
+ out_be32(uccf->p_ucce, UCC_GETH_UCCE_GRA); /* clear by writing 1 */
+
+ /* Issue host command */
+ cecr_subblock =
+ ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num);
+ qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock,
+ QE_CR_PROTOCOL_ETHERNET, 0);
+
+ /* Wait for command to complete */
+ do {
+ msleep(10);
+ temp = in_be32(uccf->p_ucce);
+ } while (!(temp & UCC_GETH_UCCE_GRA) && --i);
+
+ uccf->stopped_tx = 1;
+
+ return 0;
+}
+
+static int ugeth_graceful_stop_rx(struct ucc_geth_private *ugeth)
+{
+ struct ucc_fast_private *uccf;
+ u32 cecr_subblock;
+ u8 temp;
+ int i = 10;
+
+ uccf = ugeth->uccf;
+
+ /* Clear acknowledge bit */
+ temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack);
+ temp &= ~GRACEFUL_STOP_ACKNOWLEDGE_RX;
+ out_8(&ugeth->p_rx_glbl_pram->rxgstpack, temp);
+
+ /* Keep issuing command and checking acknowledge bit until
+ it is asserted, according to spec */
+ do {
+ /* Issue host command */
+ cecr_subblock =
+ ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.
+ ucc_num);
+ qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock,
+ QE_CR_PROTOCOL_ETHERNET, 0);
+ msleep(10);
+ temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack);
+ } while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX) && --i);
+
+ uccf->stopped_rx = 1;
+
+ return 0;
+}
+
+static int ugeth_restart_tx(struct ucc_geth_private *ugeth)
+{
+ struct ucc_fast_private *uccf;
+ u32 cecr_subblock;
+
+ uccf = ugeth->uccf;
+
+ cecr_subblock =
+ ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num);
+ qe_issue_cmd(QE_RESTART_TX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0);
+ uccf->stopped_tx = 0;
+
+ return 0;
+}
+
+static int ugeth_restart_rx(struct ucc_geth_private *ugeth)
+{
+ struct ucc_fast_private *uccf;
+ u32 cecr_subblock;
+
+ uccf = ugeth->uccf;
+
+ cecr_subblock =
+ ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num);
+ qe_issue_cmd(QE_RESTART_RX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET,
+ 0);
+ uccf->stopped_rx = 0;
+
+ return 0;
+}
+
+static int ugeth_enable(struct ucc_geth_private *ugeth, enum comm_dir mode)
+{
+ struct ucc_fast_private *uccf;
+ int enabled_tx, enabled_rx;
+
+ uccf = ugeth->uccf;
+
+ /* check if the UCC number is in range. */
+ if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) {
+ if (netif_msg_probe(ugeth))
+ ugeth_err("%s: ucc_num out of range.", __func__);
+ return -EINVAL;
+ }
+
+ enabled_tx = uccf->enabled_tx;
+ enabled_rx = uccf->enabled_rx;
+
+ /* Get Tx and Rx going again, in case this channel was actively
+ disabled. */
+ if ((mode & COMM_DIR_TX) && (!enabled_tx) && uccf->stopped_tx)
+ ugeth_restart_tx(ugeth);
+ if ((mode & COMM_DIR_RX) && (!enabled_rx) && uccf->stopped_rx)
+ ugeth_restart_rx(ugeth);
+
+ ucc_fast_enable(uccf, mode); /* OK to do even if not disabled */
+
+ return 0;
+
+}
+
+static int ugeth_disable(struct ucc_geth_private *ugeth, enum comm_dir mode)
+{
+ struct ucc_fast_private *uccf;
+
+ uccf = ugeth->uccf;
+
+ /* check if the UCC number is in range. */
+ if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) {
+ if (netif_msg_probe(ugeth))
+ ugeth_err("%s: ucc_num out of range.", __func__);
+ return -EINVAL;
+ }
+
+ /* Stop any transmissions */
+ if ((mode & COMM_DIR_TX) && uccf->enabled_tx && !uccf->stopped_tx)
+ ugeth_graceful_stop_tx(ugeth);
+
+ /* Stop any receptions */
+ if ((mode & COMM_DIR_RX) && uccf->enabled_rx && !uccf->stopped_rx)
+ ugeth_graceful_stop_rx(ugeth);
+
+ ucc_fast_disable(ugeth->uccf, mode); /* OK to do even if not enabled */
+
+ return 0;
+}
+
+static void ugeth_quiesce(struct ucc_geth_private *ugeth)
+{
+ /* Prevent any further xmits, plus detach the device. */
+ netif_device_detach(ugeth->ndev);
+
+ /* Wait for any current xmits to finish. */
+ netif_tx_disable(ugeth->ndev);
+
+ /* Disable the interrupt to avoid NAPI rescheduling. */
+ disable_irq(ugeth->ug_info->uf_info.irq);
+
+ /* Stop NAPI, and possibly wait for its completion. */
+ napi_disable(&ugeth->napi);
+}
+
+static void ugeth_activate(struct ucc_geth_private *ugeth)
+{
+ napi_enable(&ugeth->napi);
+ enable_irq(ugeth->ug_info->uf_info.irq);
+ netif_device_attach(ugeth->ndev);
+}
+
/* Called every time the controller might need to be made
* aware of new link state. The PHY code conveys this
* information through variables in the ugeth structure, and this
struct ucc_geth __iomem *ug_regs;
struct ucc_fast __iomem *uf_regs;
struct phy_device *phydev = ugeth->phydev;
- unsigned long flags;
int new_state = 0;
ug_regs = ugeth->ug_regs;
uf_regs = ugeth->uccf->uf_regs;
- spin_lock_irqsave(&ugeth->lock, flags);
-
if (phydev->link) {
u32 tempval = in_be32(&ug_regs->maccfg2);
u32 upsmr = in_be32(&uf_regs->upsmr);
ugeth->oldspeed = phydev->speed;
}
- out_be32(&ug_regs->maccfg2, tempval);
- out_be32(&uf_regs->upsmr, upsmr);
-
if (!ugeth->oldlink) {
new_state = 1;
ugeth->oldlink = 1;
}
+
+ if (new_state) {
+ /*
+ * To change the MAC configuration we need to disable
+ * the controller. To do so, we have to either grab
+ * ugeth->lock, which is a bad idea since 'graceful
+ * stop' commands might take quite a while, or we can
+ * quiesce driver's activity.
+ */
+ ugeth_quiesce(ugeth);
+ ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
+
+ out_be32(&ug_regs->maccfg2, tempval);
+ out_be32(&uf_regs->upsmr, upsmr);
+
+ ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
+ ugeth_activate(ugeth);
+ }
} else if (ugeth->oldlink) {
new_state = 1;
ugeth->oldlink = 0;
if (new_state && netif_msg_link(ugeth))
phy_print_status(phydev);
-
- spin_unlock_irqrestore(&ugeth->lock, flags);
}
/* Initialize TBI PHY interface for communicating with the
return 0;
}
-
-
-static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
-{
- struct ucc_fast_private *uccf;
- u32 cecr_subblock;
- u32 temp;
- int i = 10;
-
- uccf = ugeth->uccf;
-
- /* Mask GRACEFUL STOP TX interrupt bit and clear it */
- clrbits32(uccf->p_uccm, UCC_GETH_UCCE_GRA);
- out_be32(uccf->p_ucce, UCC_GETH_UCCE_GRA); /* clear by writing 1 */
-
- /* Issue host command */
- cecr_subblock =
- ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num);
- qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock,
- QE_CR_PROTOCOL_ETHERNET, 0);
-
- /* Wait for command to complete */
- do {
- msleep(10);
- temp = in_be32(uccf->p_ucce);
- } while (!(temp & UCC_GETH_UCCE_GRA) && --i);
-
- uccf->stopped_tx = 1;
-
- return 0;
-}
-
-static int ugeth_graceful_stop_rx(struct ucc_geth_private * ugeth)
-{
- struct ucc_fast_private *uccf;
- u32 cecr_subblock;
- u8 temp;
- int i = 10;
-
- uccf = ugeth->uccf;
-
- /* Clear acknowledge bit */
- temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack);
- temp &= ~GRACEFUL_STOP_ACKNOWLEDGE_RX;
- out_8(&ugeth->p_rx_glbl_pram->rxgstpack, temp);
-
- /* Keep issuing command and checking acknowledge bit until
- it is asserted, according to spec */
- do {
- /* Issue host command */
- cecr_subblock =
- ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.
- ucc_num);
- qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock,
- QE_CR_PROTOCOL_ETHERNET, 0);
- msleep(10);
- temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack);
- } while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX) && --i);
-
- uccf->stopped_rx = 1;
-
- return 0;
-}
-
-static int ugeth_restart_tx(struct ucc_geth_private *ugeth)
-{
- struct ucc_fast_private *uccf;
- u32 cecr_subblock;
-
- uccf = ugeth->uccf;
-
- cecr_subblock =
- ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num);
- qe_issue_cmd(QE_RESTART_TX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0);
- uccf->stopped_tx = 0;
-
- return 0;
-}
-
-static int ugeth_restart_rx(struct ucc_geth_private *ugeth)
-{
- struct ucc_fast_private *uccf;
- u32 cecr_subblock;
-
- uccf = ugeth->uccf;
-
- cecr_subblock =
- ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num);
- qe_issue_cmd(QE_RESTART_RX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET,
- 0);
- uccf->stopped_rx = 0;
-
- return 0;
-}
-
-static int ugeth_enable(struct ucc_geth_private *ugeth, enum comm_dir mode)
-{
- struct ucc_fast_private *uccf;
- int enabled_tx, enabled_rx;
-
- uccf = ugeth->uccf;
-
- /* check if the UCC number is in range. */
- if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) {
- if (netif_msg_probe(ugeth))
- ugeth_err("%s: ucc_num out of range.", __func__);
- return -EINVAL;
- }
-
- enabled_tx = uccf->enabled_tx;
- enabled_rx = uccf->enabled_rx;
-
- /* Get Tx and Rx going again, in case this channel was actively
- disabled. */
- if ((mode & COMM_DIR_TX) && (!enabled_tx) && uccf->stopped_tx)
- ugeth_restart_tx(ugeth);
- if ((mode & COMM_DIR_RX) && (!enabled_rx) && uccf->stopped_rx)
- ugeth_restart_rx(ugeth);
-
- ucc_fast_enable(uccf, mode); /* OK to do even if not disabled */
-
- return 0;
-
-}
-
-static int ugeth_disable(struct ucc_geth_private * ugeth, enum comm_dir mode)
-{
- struct ucc_fast_private *uccf;
-
- uccf = ugeth->uccf;
-
- /* check if the UCC number is in range. */
- if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) {
- if (netif_msg_probe(ugeth))
- ugeth_err("%s: ucc_num out of range.", __func__);
- return -EINVAL;
- }
-
- /* Stop any transmissions */
- if ((mode & COMM_DIR_TX) && uccf->enabled_tx && !uccf->stopped_tx)
- ugeth_graceful_stop_tx(ugeth);
-
- /* Stop any receptions */
- if ((mode & COMM_DIR_RX) && uccf->enabled_rx && !uccf->stopped_rx)
- ugeth_graceful_stop_rx(ugeth);
-
- ucc_fast_disable(ugeth->uccf, mode); /* OK to do even if not enabled */
-
- return 0;
-}
-
static void ugeth_dump_regs(struct ucc_geth_private *ugeth)
{
#ifdef DEBUG
dmi = dev->mc_list;
- for (i = 0; i < dev->mc_count; i++, dmi = dmi->next) {
+ for (i = 0; i < netdev_mc_count(dev);
+ i++, dmi = dmi->next) {
/* Only support group multicast for now.
*/
}
if ((ug_info->numStationAddresses !=
- UCC_GETH_NUM_OF_STATION_ADDRESSES_1)
- && ug_info->rxExtendedFiltering) {
+ UCC_GETH_NUM_OF_STATION_ADDRESSES_1) &&
+ ug_info->rxExtendedFiltering) {
if (netif_msg_probe(ugeth))
ugeth_err("%s: Number of station addresses greater than 1 "
"not allowed in extended parsing mode.",
UCC_GETH_NUM_OF_STATION_ADDRESSES_1);
ugeth->rx_extended_features = ugeth->rx_non_dynamic_extended_features ||
- (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP)
- || (ug_info->vlanOperationNonTagged !=
- UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP);
+ (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP) ||
+ (ug_info->vlanOperationNonTagged !=
+ UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP);
init_default_reg_vals(&uf_regs->upsmr,
&ug_regs->maccfg1, &ug_regs->maccfg2);
ugeth->p_init_enet_param_shadow->rgftgfrxglobal |=
ugeth->rx_glbl_pram_offset | ug_info->riscRx;
if ((ug_info->largestexternallookupkeysize !=
- QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE)
- && (ug_info->largestexternallookupkeysize !=
- QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES)
- && (ug_info->largestexternallookupkeysize !=
- QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) {
+ QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) &&
+ (ug_info->largestexternallookupkeysize !=
+ QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES) &&
+ (ug_info->largestexternallookupkeysize !=
+ QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) {
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Invalid largest External Lookup Key Size.",
__func__);
u8 __iomem *bd; /* BD pointer */
u32 bd_status;
u8 txQ = 0;
+ unsigned long flags;
ugeth_vdbg("%s: IN", __func__);
- spin_lock_irq(&ugeth->lock);
+ spin_lock_irqsave(&ugeth->lock, flags);
dev->stats.tx_bytes += skb->len;
uccf = ugeth->uccf;
out_be16(uccf->p_utodr, UCC_FAST_TOD);
#endif
- spin_unlock_irq(&ugeth->lock);
+ spin_unlock_irqrestore(&ugeth->lock, flags);
return NETDEV_TX_OK;
}
/* Handle the transmitted buffer and release */
/* the BD to be used with the current frame */
- if ((bd == ugeth->txBd[txQ]) && (netif_queue_stopped(dev) == 0))
+ skb = ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]];
+ if (!skb)
break;
dev->stats.tx_packets++;
- skb = ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]];
-
if (skb_queue_len(&ugeth->rx_recycle) < RX_BD_RING_LEN &&
skb_recycle_check(skb,
ugeth->ug_info->uf_info.max_rx_buf_length +
napi_enable(&ugeth->napi);
netif_start_queue(dev);
+ device_set_wakeup_capable(&dev->dev,
+ qe_alive_during_sleep() || ugeth->phydev->irq);
+ device_set_wakeup_enable(&dev->dev, ugeth->wol_en);
+
return err;
err:
schedule_work(&ugeth->timeout_work);
}
+
+#ifdef CONFIG_PM
+
+static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state)
+{
+ struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
+ struct ucc_geth_private *ugeth = netdev_priv(ndev);
+
+ if (!netif_running(ndev))
+ return 0;
+
+ netif_device_detach(ndev);
+ napi_disable(&ugeth->napi);
+
+ /*
+ * Disable the controller, otherwise we'll wakeup on any network
+ * activity.
+ */
+ ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
+
+ if (ugeth->wol_en & WAKE_MAGIC) {
+ setbits32(ugeth->uccf->p_uccm, UCC_GETH_UCCE_MPD);
+ setbits32(&ugeth->ug_regs->maccfg2, MACCFG2_MPE);
+ ucc_fast_enable(ugeth->uccf, COMM_DIR_RX_AND_TX);
+ } else if (!(ugeth->wol_en & WAKE_PHY)) {
+ phy_stop(ugeth->phydev);
+ }
+
+ return 0;
+}
+
+static int ucc_geth_resume(struct of_device *ofdev)
+{
+ struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
+ struct ucc_geth_private *ugeth = netdev_priv(ndev);
+ int err;
+
+ if (!netif_running(ndev))
+ return 0;
+
+ if (qe_alive_during_sleep()) {
+ if (ugeth->wol_en & WAKE_MAGIC) {
+ ucc_fast_disable(ugeth->uccf, COMM_DIR_RX_AND_TX);
+ clrbits32(&ugeth->ug_regs->maccfg2, MACCFG2_MPE);
+ clrbits32(ugeth->uccf->p_uccm, UCC_GETH_UCCE_MPD);
+ }
+ ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
+ } else {
+ /*
+ * Full reinitialization is required if QE shuts down
+ * during sleep.
+ */
+ ucc_geth_memclean(ugeth);
+
+ err = ucc_geth_init_mac(ugeth);
+ if (err) {
+ ugeth_err("%s: Cannot initialize MAC, aborting.",
+ ndev->name);
+ return err;
+ }
+ }
+
+ ugeth->oldlink = 0;
+ ugeth->oldspeed = 0;
+ ugeth->oldduplex = -1;
+
+ phy_stop(ugeth->phydev);
+ phy_start(ugeth->phydev);
+
+ napi_enable(&ugeth->napi);
+ netif_device_attach(ndev);
+
+ return 0;
+}
+
+#else
+#define ucc_geth_suspend NULL
+#define ucc_geth_resume NULL
+#endif
+
static phy_interface_t to_phy_interface(const char *phy_connection_type)
{
if (strcasecmp(phy_connection_type, "mii") == 0)
prop = of_get_property(np, "tx-clock", NULL);
if (!prop) {
printk(KERN_ERR
- "ucc_geth: mising tx-clock-name property\n");
+ "ucc_geth: missing tx-clock-name property\n");
return -EINVAL;
}
if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
.match_table = ucc_geth_match,
.probe = ucc_geth_probe,
.remove = ucc_geth_remove,
+ .suspend = ucc_geth_suspend,
+ .resume = ucc_geth_resume,
};
static int __init ucc_geth_init(void)