📄 ag7100.c
字号:
#include <linux/stddef.h>#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <asm/byteorder.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/dma-mapping.h>#include <linux/bitops.h>#include <asm/irq.h>#include <asm/io.h>#include <net/sch_generic.h>#include "ag7100.h"#include "ag7100_phy.h"#include "ag7100_trc.h"static ag7100_mac_t *ag7100_macs[2];static void ag7100_hw_setup(ag7100_mac_t *mac);static void ag7100_hw_stop(ag7100_mac_t *mac);static void ag7100_oom_timer(unsigned long data);static int ag7100_check_link(ag7100_mac_t *mac);static int ag7100_tx_alloc(ag7100_mac_t *mac);static int ag7100_rx_alloc(ag7100_mac_t *mac);static void ag7100_rx_free(ag7100_mac_t *mac);static void ag7100_tx_free(ag7100_mac_t *mac);static int ag7100_ring_alloc(ag7100_ring_t *r, int count);static int ag7100_rx_replenish(ag7100_mac_t *mac);static int ag7100_tx_reap(ag7100_mac_t *mac);static void ag7100_ring_release(ag7100_mac_t *mac, ag7100_ring_t *r);static void ag7100_ring_free(ag7100_ring_t *r);static void ag7100_tx_timeout_task(ag7100_mac_t *mac);static int ag7100_poll(struct net_device *dev, int *budget);#ifdef CONFIG_AR9100void ag7100_dma_reset(ag7100_mac_t *mac);#endifstatic int ag7100_recv_packets(struct net_device *dev, ag7100_mac_t *mac, int max_work, int *work_done);static irqreturn_t ag7100_intr(int cpl, void *dev_id, struct pt_regs *regs);static struct sk_buff * ag7100_buffer_alloc(void);char *mii_str[2][4] = { {"GMii", "Mii", "RGMii", "RMii"}, {"RGMii", "RMii"}};char *spd_str[] = {"10Mbps", "100Mbps", "1000Mbps"};char *dup_str[] = {"half duplex", "full duplex"};#define MODULE_NAME "AG7100"/* if 0 compute in init */int tx_len_per_ds = 0;module_param(tx_len_per_ds, int, 0);MODULE_PARM_DESC(tx_len_per_ds, "Size of DMA chunk");/* if 0 compute in init */int tx_max_desc_per_ds_pkt=0;/* if 0 compute in init */#ifdef CONFIG_AR9100int fifo_3 = 0x780008;#elseint fifo_3 = 0;#endifmodule_param(fifo_3, int, 0);MODULE_PARM_DESC(fifo_3, "fifo cfg 3 settings");int mii0_if = AG7100_MII0_INTERFACE;module_param(mii0_if, int, 0);MODULE_PARM_DESC(mii0_if, "mii0 connect");int mii1_if = AG7100_MII1_INTERFACE;module_param(mii1_if, int, 0);MODULE_PARM_DESC(mii1_if, "mii1 connect");#ifndef CONFIG_AR9100int gige_pll = 0x0110000;#else#define SW_PLL 0x1f000000ulint gige_pll = 0x1a000000;#endifmodule_param(gige_pll, int, 0);MODULE_PARM_DESC(gige_pll, "Pll for (R)GMII if");/* * Cfg 5 settings* Weed out junk frames (CRC errored, short collision'ed frames etc.)*/int fifo_5 = 0x7ffef;module_param(fifo_5, int, 0);MODULE_PARM_DESC(fifo_5, "fifo cfg 5 settings");#define addr_to_words(addr, w1, w2) { \ w1 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; \ w2 = (addr[4] << 24) | (addr[5] << 16) | 0; \}/* * Defines specific to this implemention */#ifndef CONFIG_AG7100_LEN_PER_TX_DS#error Please run menuconfig and define CONFIG_AG7100_LEN_PER_TX_DS#endif#ifndef CONFIG_AG7100_NUMBER_TX_PKTS#error Please run menuconfig and define CONFIG_AG7100_NUMBER_TX_PKTS#endif#ifndef CONFIG_AG7100_NUMBER_RX_PKTS#error Please run menuconfig and define CONFIG_AG7100_NUMBER_RX_PKTS#endif#define AG7100_TX_FIFO_LEN 2048#define AG7100_TX_MIN_DS_LEN 128#define AG7100_TX_MAX_DS_LEN AG7100_TX_FIFO_LEN#define AG7100_TX_MTU_LEN 1536#define AG7100_TX_DESC_CNT CONFIG_AG7100_NUMBER_TX_PKTS*tx_max_desc_per_ds_pkt#define AG7100_TX_REAP_THRESH AG7100_TX_DESC_CNT/2#define AG7100_TX_QSTART_THRESH 4*tx_max_desc_per_ds_pkt#define AG7100_RX_DESC_CNT CONFIG_AG7100_NUMBER_RX_PKTS#define AG7100_NAPI_WEIGHT 64#define AG7100_PHY_POLL_SECONDS 2int dma_flag = 0;static inline int ag7100_tx_reap_thresh(ag7100_mac_t *mac){ ag7100_ring_t *r = &mac->mac_txring; return (ag7100_ndesc_unused(mac, r) < AG7100_TX_REAP_THRESH);}static inline int ag7100_tx_ring_full(ag7100_mac_t *mac){ ag7100_ring_t *r = &mac->mac_txring; ag7100_trc_new(ag7100_ndesc_unused(mac, r),"tx ring full"); return (ag7100_ndesc_unused(mac, r) < tx_max_desc_per_ds_pkt + 2);}static intag7100_open(struct net_device *dev){ unsigned int w1 = 0, w2 = 0; ag7100_mac_t *mac = (ag7100_mac_t *)dev->priv; int st;#if defined(CONFIG_AR9100) && defined(SWITCH_AHB_FREQ) u32 tmp_pll, pll;#endif assert(mac); st = request_irq(mac->mac_irq, ag7100_intr, 0, dev->name, dev); if (st < 0) { printk(MODULE_NAME ": request irq %d failed %d\n", mac->mac_irq, st); return 1; } if (ag7100_tx_alloc(mac)) goto tx_failed; if (ag7100_rx_alloc(mac)) goto rx_failed; ag7100_hw_setup(mac);#if defined(CONFIG_AR9100) && defined(SWITCH_AHB_FREQ) /* * Reduce the AHB frequency to 100MHz while setting up the * S26 phy. */ pll= ar7100_reg_rd(AR7100_PLL_CONFIG); tmp_pll = pll& ~((PLL_DIV_MASK << PLL_DIV_SHIFT) | (PLL_REF_DIV_MASK << PLL_REF_DIV_SHIFT)); tmp_pll = tmp_pll | (0x64 << PLL_DIV_SHIFT) | (0x5 << PLL_REF_DIV_SHIFT) | (1 << AHB_DIV_SHIFT); ar7100_reg_wr_nf(AR7100_PLL_CONFIG, tmp_pll); udelay(100*1000);#endif#if defined(CONFIG_ATHRS26_PHY) /* if using header for register configuration, we have to */ /* configure s26 register after frame transmission is enabled */ if (mac->mac_unit == 1) /* wan phy */ athrs26_reg_init();#endif ag7100_phy_setup(mac->mac_unit);#if defined(CONFIG_AR9100) && defined(SWITCH_AHB_FREQ) ar7100_reg_wr_nf(AR7100_PLL_CONFIG, pll); udelay(100*1000);#endif /* * set the mac addr */ addr_to_words(dev->dev_addr, w1, w2); ag7100_reg_wr(mac, AG7100_GE_MAC_ADDR1, w1); ag7100_reg_wr(mac, AG7100_GE_MAC_ADDR2, w2); /* * phy link mgmt */ init_timer(&mac->mac_phy_timer); mac->mac_phy_timer.data = (unsigned long)mac; mac->mac_phy_timer.function = ag7100_check_link; ag7100_check_link(mac); dev->trans_start = jiffies; ag7100_int_enable(mac); ag7100_rx_start(mac); ag7100_start_rx_count(mac); return 0;rx_failed: ag7100_tx_free(mac);tx_failed: free_irq(mac->mac_irq, dev); return 1;}static intag7100_stop(struct net_device *dev){ ag7100_mac_t *mac = (ag7100_mac_t *)dev->priv; int flags; spin_lock_irqsave(&mac->mac_lock, flags); netif_stop_queue(dev); netif_carrier_off(dev); ag7100_hw_stop(mac); free_irq(mac->mac_irq, dev); ag7100_tx_free(mac); ag7100_rx_free(mac); del_timer(&mac->mac_phy_timer); spin_unlock_irqrestore(&mac->mac_lock, flags); /*ag7100_trc_dump();*/ return 0;}static voidag7100_hw_setup(ag7100_mac_t *mac){ ag7100_ring_t *tx = &mac->mac_txring, *rx = &mac->mac_rxring; ag7100_desc_t *r0, *t0; ag7100_reg_wr(mac, AG7100_MAC_CFG1, (AG7100_MAC_CFG1_RX_EN | AG7100_MAC_CFG1_TX_EN)); ag7100_reg_rmw_set(mac, AG7100_MAC_CFG2, (AG7100_MAC_CFG2_PAD_CRC_EN | AG7100_MAC_CFG2_LEN_CHECK)); ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_0, 0x1f00); /* * set the mii if type - NB reg not in the gigE space */ ar7100_reg_wr(mii_reg(mac), mii_if(mac)); ag7100_reg_wr(mac, AG7100_MAC_MII_MGMT_CFG, AG7100_MGMT_CFG_CLK_DIV_20);#ifdef CONFIG_AR7100_EMULATION ag7100_reg_rmw_set(mac, AG7100_MAC_FIFO_CFG_4, 0x3ffff); ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_1, 0xfff0000); ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_2, 0x1fff);#else ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_1, 0xfff0000); ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_2, 0x1fff); /* * Weed out junk frames (CRC errored, short collision'ed frames etc.) */ ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_4, 0xffff); ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_5, 0x7ffef);#endif t0 = &tx->ring_desc[0]; r0 = &rx->ring_desc[0]; ag7100_reg_wr(mac, AG7100_DMA_TX_DESC, ag7100_desc_dma_addr(tx, t0)); ag7100_reg_wr(mac, AG7100_DMA_RX_DESC, ag7100_desc_dma_addr(rx, r0)); printk(MODULE_NAME ": cfg1 %#x cfg2 %#x\n", ag7100_reg_rd(mac, AG7100_MAC_CFG1), ag7100_reg_rd(mac, AG7100_MAC_CFG2));}static voidag7100_hw_stop(ag7100_mac_t *mac){ ag7100_rx_stop(mac); ag7100_tx_stop(mac); ag7100_int_disable(mac); /* * put everything into reset. */ ag7100_reg_rmw_set(mac, AG7100_MAC_CFG1, AG7100_MAC_CFG1_SOFT_RST);}/* * program the usb pll (misnomer) to genrate appropriate clock * Write 2 into control field * Write pll value * Write 3 into control field * Write 0 into control field */#ifdef CONFIG_AR9100#define ag7100_pll_shift(_mac) (((_mac)->mac_unit) ? 22: 20)#define ag7100_pll_offset(_mac) \ (((_mac)->mac_unit) ? AR9100_ETH_INT1_CLK : \ AR9100_ETH_INT0_CLK)#else#define ag7100_pll_shift(_mac) (((_mac)->mac_unit) ? 19: 17)#define ag7100_pll_offset(_mac) \ (((_mac)->mac_unit) ? AR7100_USB_PLL_GE1_OFFSET : \ AR7100_USB_PLL_GE0_OFFSET)#endifstatic voidag7100_set_pll(ag7100_mac_t *mac, unsigned int pll){#ifdef CONFIG_AR9100#define ETH_PLL_CONFIG AR9100_ETH_PLL_CONFIG#else#define ETH_PLL_CONFIG AR7100_USB_PLL_CONFIG#endif uint32_t shift, reg, val; shift = ag7100_pll_shift(mac); reg = ag7100_pll_offset(mac); val = ar7100_reg_rd(ETH_PLL_CONFIG); val &= ~(3 << shift); val |= (2 << shift); ar7100_reg_wr(ETH_PLL_CONFIG, val); udelay(100); ar7100_reg_wr(reg, pll); val |= (3 << shift); ar7100_reg_wr(ETH_PLL_CONFIG, val); udelay(100); val &= ~(3 << shift); ar7100_reg_wr(ETH_PLL_CONFIG, val); udelay(100); printk(MODULE_NAME ": pll reg %#x: %#x ", reg, ar7100_reg_rd(reg));}/* * Several fields need to be programmed based on what the PHY negotiated * Ideally we should quiesce everything before touching the pll, but: * 1. If its a linkup/linkdown, we dont care about quiescing the traffic. * 2. If its a single gigE PHY, this can only happen on lup/ldown. * 3. If its a 100Mpbs switch, the link will always remain at 100 (or nothing) * 4. If its a gigE switch then the speed should always be set at 1000Mpbs, * and the switch should provide buffering for slower devices. * * XXX Only gigE PLL can be changed as a parameter for now. 100/10 is hardcoded. * XXX Need defines for them - * XXX FIFO settings based on the mode */static voidag7100_set_mac_from_link(ag7100_mac_t *mac, ag7100_phy_speed_t speed, int fdx){#ifdef CONFIG_ATHRS26_PHY int change_flag = 0; if(mac->mac_speed != speed) change_flag = 1; if(change_flag) { athrs26_phy_off(mac); athrs26_mac_speed_set(mac, speed); }#endif mac->mac_speed = speed; mac->mac_fdx = fdx; ag7100_set_mii_ctrl_speed(mac, speed); ag7100_set_mac_duplex(mac, fdx); ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_3, fifo_3);#ifndef CONFIG_AR9100 ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_5, fifo_5);#endif switch (speed) { case AG7100_PHY_SPEED_1000T:#ifdef CONFIG_AR9100 ag7100_reg_wr(mac, AG7100_MAC_FIFO_CFG_3, 0x780fff);#endif ag7100_set_mac_if(mac, 1);#ifdef CONFIG_AR9100 if (mac->mac_unit == 0) { /* eth0 */ ag7100_set_pll(mac, gige_pll); } else { ag7100_set_pll(mac, SW_PLL); }#else ag7100_set_pll(mac, gige_pll);#endif ag7100_reg_rmw_set(mac, AG7100_MAC_FIFO_CFG_5, (1 << 19)); break; case AG7100_PHY_SPEED_100TX: ag7100_set_mac_if(mac, 0); ag7100_set_mac_speed(mac, 1);#ifndef CONFIG_AR7100_EMULATION#ifdef CONFIG_AR9100 if (mac->mac_unit == 0) { /* eth0 */ ag7100_set_pll(mac, 0x13000a44); } else { ag7100_set_pll(mac, SW_PLL); }#else ag7100_set_pll(mac, 0x0001099);#endif#endif ag7100_reg_rmw_clear(mac, AG7100_MAC_FIFO_CFG_5, (1 << 19)); break; case AG7100_PHY_SPEED_10T: ag7100_set_mac_if(mac, 0); ag7100_set_mac_speed(mac, 0);#ifdef CONFIG_AR9100 if (mac->mac_unit == 0) { /* eth0 */ ag7100_set_pll(mac, 0x00441099); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -