/** * struct ksz_desc_info - Descriptor information data structure * @ring: First descriptor in the ring. * @cur: Current descriptor being manipulated. * @ring_virt: First hardware descriptor in the ring. * @ring_phys: The physical address of the first descriptor of the ring. * @size: Size of hardware descriptor. * @alloc: Number of descriptors allocated. * @avail: Number of descriptors available for use. * @last: Index for last descriptor released to hardware. * @next: Index for next descriptor available for use. * @mask: Mask for index wrapping.
*/ struct ksz_desc_info { struct ksz_desc *ring; struct ksz_desc *cur; struct ksz_hw_desc *ring_virt;
u32 ring_phys; int size; int alloc; int avail; int last; int next; int mask;
};
/** * struct ksz_port_mib - Port MIB data structure * @cnt_ptr: Current pointer to MIB counter index. * @link_down: Indication the link has just gone down. * @state: Connection status of the port. * @mib_start: The starting counter index. Some ports do not start at 0. * @counter: 64-bit MIB counter value. * @dropped: Temporary buffer to remember last read packet dropped values. * * MIB counters needs to be read periodically so that counters do not get * overflowed and give incorrect values. A right balance is needed to * satisfy this condition and not waste too much CPU time. * * It is pointless to read MIB counters when the port is disconnected. The * @state provides the connection status so that MIB counters are read only * when the port is connected. The @link_down indicates the port is just * disconnected so that all MIB counters are read one last time to update the * information.
*/ struct ksz_port_mib {
u8 cnt_ptr;
u8 link_down;
u8 state;
u8 mib_start;
/** * struct ksz_port_cfg - Port configuration data structure * @vid: VID value. * @member: Port membership. * @port_prio: Port priority. * @rx_rate: Receive priority rate. * @tx_rate: Transmit priority rate. * @stp_state: Current Spanning Tree Protocol state.
*/ struct ksz_port_cfg {
u16 vid;
u8 member;
u8 port_prio;
u32 rx_rate[PRIO_QUEUES];
u32 tx_rate[PRIO_QUEUES]; int stp_state;
};
/** * struct ksz_switch - KSZ8842 switch data structure * @mac_table: MAC table entries information. * @vlan_table: VLAN table entries information. * @port_cfg: Port configuration information. * @diffserv: DiffServ priority settings. Possible values from 6-bit of ToS * (bit7 ~ bit2) field. * @p_802_1p: 802.1P priority settings. Possible values from 3-bit of 802.1p * Tag priority field. * @br_addr: Bridge address. Used for STP. * @other_addr: Other MAC address. Used for multiple network device mode. * @broad_per: Broadcast storm percentage. * @member: Current port membership. Used for STP.
*/ struct ksz_switch { struct ksz_mac_table mac_table[STATIC_MAC_TABLE_ENTRIES]; struct ksz_vlan_table vlan_table[VLAN_TABLE_ENTRIES]; struct ksz_port_cfg port_cfg[TOTAL_PORT_NUM];
/** * struct ksz_port_info - Port information data structure * @state: Connection status of the port. * @tx_rate: Transmit rate divided by 10000 to get Mbit. * @duplex: Duplex mode. * @advertised: Advertised auto-negotiation setting. Used to determine link. * @partner: Auto-negotiation partner setting. Used to determine link. * @port_id: Port index to access actual hardware register. * @pdev: Pointer to OS dependent network device.
*/ struct ksz_port_info {
uint state;
uint tx_rate;
u8 duplex;
u8 advertised;
u8 partner;
u8 port_id; void *pdev;
};
/** * struct ksz_hw - KSZ884X hardware data structure * @io: Virtual address assigned. * @ksz_switch: Pointer to KSZ8842 switch. * @port_info: Port information. * @port_mib: Port MIB information. * @dev_count: Number of network devices this hardware supports. * @dst_ports: Destination ports in switch for transmission. * @id: Hardware ID. Used for display only. * @mib_cnt: Number of MIB counters this hardware has. * @mib_port_cnt: Number of ports with MIB counters. * @tx_cfg: Cached transmit control settings. * @rx_cfg: Cached receive control settings. * @intr_mask: Current interrupt mask. * @intr_set: Current interrup set. * @intr_blocked: Interrupt blocked. * @rx_desc_info: Receive descriptor information. * @tx_desc_info: Transmit descriptor information. * @tx_int_cnt: Transmit interrupt count. Used for TX optimization. * @tx_int_mask: Transmit interrupt mask. Used for TX optimization. * @tx_size: Transmit data size. Used for TX optimization. * The maximum is defined by MAX_TX_HELD_SIZE. * @perm_addr: Permanent MAC address. * @override_addr: Overridden MAC address. * @address: Additional MAC address entries. * @addr_list_size: Additional MAC address list size. * @mac_override: Indication of MAC address overridden. * @promiscuous: Counter to keep track of promiscuous mode set. * @all_multi: Counter to keep track of all multicast mode set. * @multi_list: Multicast address entries. * @multi_bits: Cached multicast hash table settings. * @multi_list_size: Multicast address list size. * @enabled: Indication of hardware enabled. * @rx_stop: Indication of receive process stop. * @reserved2: none * @features: Hardware features to enable. * @overrides: Hardware features to override. * @parent: Pointer to parent, network device private structure.
*/ struct ksz_hw { void __iomem *io;
struct ksz_switch *ksz_switch; struct ksz_port_info port_info[SWITCH_PORT_NUM]; struct ksz_port_mib port_mib[TOTAL_PORT_NUM]; int dev_count; int dst_ports; int id; int mib_cnt; int mib_port_cnt;
/** * struct ksz_port - Virtual port data structure * @duplex: Duplex mode setting. 1 for half duplex, 2 for full * duplex, and 0 for auto, which normally results in full * duplex. * @speed: Speed setting. 10 for 10 Mbit, 100 for 100 Mbit, and * 0 for auto, which normally results in 100 Mbit. * @force_link: Force link setting. 0 for auto-negotiation, and 1 for * force. * @flow_ctrl: Flow control setting. PHY_NO_FLOW_CTRL for no flow * control, and PHY_FLOW_CTRL for flow control. * PHY_TX_ONLY and PHY_RX_ONLY are not supported for 100 * Mbit PHY. * @first_port: Index of first port this port supports. * @mib_port_cnt: Number of ports with MIB counters. * @port_cnt: Number of ports this port supports. * @counter: Port statistics counter. * @hw: Pointer to hardware structure. * @linked: Pointer to port information linked to this port.
*/ struct ksz_port {
u8 duplex;
u8 speed;
u8 force_link;
u8 flow_ctrl;
int first_port; int mib_port_cnt; int port_cnt;
u64 counter[OID_COUNTER_LAST];
/** * struct ksz_timer_info - Timer information data structure * @timer: Kernel timer. * @cnt: Running timer counter. * @max: Number of times to run timer; -1 for infinity. * @period: Timer period in jiffies.
*/ struct ksz_timer_info { struct timer_list timer; int cnt; int max; int period;
};
/** * struct ksz_counter_info - OS dependent counter information data structure * @counter: Wait queue to wakeup after counters are read. * @time: Next time in jiffies to read counter. * @read: Indication of counters read in full or not.
*/ struct ksz_counter_info {
wait_queue_head_t counter; unsignedlong time; int read;
};
/** * struct dev_info - Network device information data structure * @dev: Pointer to network device. * @pdev: Pointer to PCI device. * @hw: Hardware structure. * @desc_pool: Physical memory used for descriptor pool. * @hwlock: Spinlock to prevent hardware from accessing. * @lock: Mutex lock to prevent device from accessing. * @dev_rcv: Receive process function used. * @last_skb: Socket buffer allocated for descriptor rx fragments. * @skb_index: Buffer index for receiving fragments. * @skb_len: Buffer length for receiving fragments. * @mib_read: Workqueue to read MIB counters. * @mib_timer_info: Timer to read MIB counters. * @counter: Used for MIB reading. * @mtu: Current MTU used. The default is REGULAR_RX_BUF_SIZE; * the maximum is MAX_RX_BUF_SIZE. * @opened: Counter to keep track of device open. * @rx_tasklet: Receive processing tasklet. * @tx_tasklet: Transmit processing tasklet. * @wol_enable: Wake-on-LAN enable set by ethtool. * @wol_support: Wake-on-LAN support used by ethtool. * @pme_wait: Used for KSZ8841 power management.
*/ struct dev_info { struct net_device *dev; struct pci_dev *pdev;
int wol_enable; int wol_support; unsignedlong pme_wait;
};
/** * struct dev_priv - Network device private data structure * @adapter: Adapter device information. * @port: Port information. * @monitor_timer_info: Timer to monitor ports. * @proc_sem: Semaphore for proc accessing. * @id: Device ID. * @mii_if: MII interface information. * @advertising: Temporary variable to store advertised settings. * @msg_enable: The message flags controlling driver output. * @media_state: The connection status of the device. * @multicast: The all multicast state of the device. * @promiscuous: The promiscuous state of the device.
*/ struct dev_priv { struct dev_info *adapter; struct ksz_port port; struct ksz_timer_info monitor_timer_info;
struct semaphore proc_sem; int id;
struct mii_if_info mii_if;
u32 advertising;
u32 msg_enable; int media_state; int multicast; int promiscuous;
};
/** * hw_turn_on_intr - turn on specified interrupts * @hw: The hardware instance. * @bit: The interrupt bits to be on. * * This routine turns on the specified interrupts in the interrupt mask so that * those interrupts will be enabled.
*/ staticvoid hw_turn_on_intr(struct ksz_hw *hw, u32 bit)
{
hw->intr_mask |= bit;
if (!hw->intr_blocked)
hw_set_intr(hw, hw->intr_mask);
}
/** * hw_block_intr - block hardware interrupts * @hw: The hardware instance. * * This function blocks all interrupts of the hardware and returns the current * interrupt enable mask so that interrupts can be restored later. * * Return the current interrupt enable mask.
*/ static uint hw_block_intr(struct ksz_hw *hw)
{
uint interrupt = 0;
#define HW_DELAY(hw, reg) \ do { \
readw(hw->io + reg); \
} while (0)
/** * sw_r_table - read 4 bytes of data from switch table * @hw: The hardware instance. * @table: The table selector. * @addr: The address of the table entry. * @data: Buffer to store the read data. * * This routine reads 4 bytes of data from the table of the switch. * Hardware interrupts are disabled to minimize corruption of read data.
*/ staticvoid sw_r_table(struct ksz_hw *hw, int table, u16 addr, u32 *data)
{
u16 ctrl_addr;
uint interrupt;
/** * sw_w_table_64 - write 8 bytes of data to the switch table * @hw: The hardware instance. * @table: The table selector. * @addr: The address of the table entry. * @data_hi: The high part of data to be written (bit63 ~ bit32). * @data_lo: The low part of data to be written (bit31 ~ bit0). * * This routine writes 8 bytes of data to the table of the switch. * Hardware interrupts are disabled to minimize corruption of written data.
*/ staticvoid sw_w_table_64(struct ksz_hw *hw, int table, u16 addr, u32 data_hi,
u32 data_lo)
{
u16 ctrl_addr;
uint interrupt;
/** * sw_w_sta_mac_table - write to the static MAC table * @hw: The hardware instance. * @addr: The address of the table entry. * @mac_addr: The MAC address. * @ports: The port members. * @override: The flag to override the port receive/transmit settings. * @valid: The flag to indicate entry is valid. * @use_fid: The flag to indicate the FID is valid. * @fid: The FID value. * * This routine writes an entry of the static MAC table of the switch. It * calls sw_w_table_64() to write the data.
*/ staticvoid sw_w_sta_mac_table(struct ksz_hw *hw, u16 addr, u8 *mac_addr,
u8 ports, int override, int valid, int use_fid, u8 fid)
{
u32 data_hi;
u32 data_lo;
/** * sw_r_vlan_table - read from the VLAN table * @hw: The hardware instance. * @addr: The address of the table entry. * @vid: Buffer to store the VID. * @fid: Buffer to store the VID. * @member: Buffer to store the port membership. * * This function reads an entry of the VLAN table of the switch. It calls * sw_r_table() to get the data. * * Return 0 if the entry is valid; otherwise -1.
*/ staticint sw_r_vlan_table(struct ksz_hw *hw, u16 addr, u16 *vid, u8 *fid,
u8 *member)
{
u32 data;
/** * port_r_mib_cnt - read MIB counter * @hw: The hardware instance. * @port: The port index. * @addr: The address of the counter. * @cnt: Buffer to store the counter. * * This routine reads a MIB counter of the port. * Hardware interrupts are disabled to minimize corruption of read data.
*/ staticvoid port_r_mib_cnt(struct ksz_hw *hw, int port, u16 addr, u64 *cnt)
{
u32 data;
u16 ctrl_addr;
uint interrupt; int timeout;
for (timeout = 100; timeout > 0; timeout--) {
data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
if (data & MIB_COUNTER_VALID) { if (data & MIB_COUNTER_OVERFLOW)
*cnt += MIB_COUNTER_VALUE + 1;
*cnt += data & MIB_COUNTER_VALUE; break;
}
}
hw_restore_intr(hw, interrupt);
}
/** * port_r_mib_pkt - read dropped packet counts * @hw: The hardware instance. * @port: The port index. * @last: last one * @cnt: Buffer to store the receive and transmit dropped packet counts. * * This routine reads the dropped packet counts of the port. * Hardware interrupts are disabled to minimize corruption of read data.
*/ staticvoid port_r_mib_pkt(struct ksz_hw *hw, int port, u32 *last, u64 *cnt)
{
u32 cur;
u32 data;
u16 ctrl_addr;
uint interrupt; int index;
index = KS_MIB_PACKET_DROPPED_RX_0 + port; do {
interrupt = hw_block_intr(hw);
data &= MIB_PACKET_DROPPED;
cur = *last; if (data != cur) {
*last = data; if (data < cur)
data += MIB_PACKET_DROPPED + 1;
data -= cur;
*cnt += data;
}
++last;
++cnt;
index -= KS_MIB_PACKET_DROPPED_TX -
KS_MIB_PACKET_DROPPED_TX_0 + 1;
} while (index >= KS_MIB_PACKET_DROPPED_TX_0 + port);
}
/** * port_r_cnt - read MIB counters periodically * @hw: The hardware instance. * @port: The port index. * * This routine is used to read the counters of the port periodically to avoid * counter overflow. The hardware should be acquired first before calling this * routine. * * Return non-zero when not all counters not read.
*/ staticint port_r_cnt(struct ksz_hw *hw, int port)
{ struct ksz_port_mib *mib = &hw->port_mib[port];
if (mib->mib_start < PORT_COUNTER_NUM) while (mib->cnt_ptr < PORT_COUNTER_NUM) {
port_r_mib_cnt(hw, port, mib->cnt_ptr,
&mib->counter[mib->cnt_ptr]);
++mib->cnt_ptr;
} if (hw->mib_cnt > PORT_COUNTER_NUM)
port_r_mib_pkt(hw, port, mib->dropped,
&mib->counter[PORT_COUNTER_NUM]);
mib->cnt_ptr = 0; return 0;
}
/** * port_init_cnt - initialize MIB counter values * @hw: The hardware instance. * @port: The port index. * * This routine is used to initialize all counters to zero if the hardware * cannot do it after reset.
*/ staticvoid port_init_cnt(struct ksz_hw *hw, int port)
{ struct ksz_port_mib *mib = &hw->port_mib[port];
mib->cnt_ptr = 0; if (mib->mib_start < PORT_COUNTER_NUM) do {
port_r_mib_cnt(hw, port, mib->cnt_ptr,
&mib->counter[mib->cnt_ptr]);
++mib->cnt_ptr;
} while (mib->cnt_ptr < PORT_COUNTER_NUM); if (hw->mib_cnt > PORT_COUNTER_NUM)
port_r_mib_pkt(hw, port, mib->dropped,
&mib->counter[PORT_COUNTER_NUM]);
memset((void *) mib->counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
mib->cnt_ptr = 0;
}
/* * Port functions
*/
/** * port_cfg - set port register bits * @hw: The hardware instance. * @port: The port index. * @offset: The offset of the port register. * @bits: The data bits to set. * @set: The flag indicating whether the bits are to be set or not. * * This routine sets or resets the specified bits of the port register.
*/ staticvoid port_cfg(struct ksz_hw *hw, int port, int offset, u16 bits, int set)
{
u32 addr;
u16 data;
PORT_CTRL_ADDR(port, addr);
addr += offset;
data = readw(hw->io + addr); if (set)
data |= bits; else
data &= ~bits;
writew(data, hw->io + addr);
}
/** * port_r8 - read byte from port register * @hw: The hardware instance. * @port: The port index. * @offset: The offset of the port register. * @data: Buffer to store the data. * * This routine reads a byte from the port register.
*/ staticvoid port_r8(struct ksz_hw *hw, int port, int offset, u8 *data)
{
u32 addr;
/** * port_r16 - read word from port register. * @hw: The hardware instance. * @port: The port index. * @offset: The offset of the port register. * @data: Buffer to store the data. * * This routine reads a word from the port register.
*/ staticvoid port_r16(struct ksz_hw *hw, int port, int offset, u16 *data)
{
u32 addr;
/** * port_w16 - write word to port register. * @hw: The hardware instance. * @port: The port index. * @offset: The offset of the port register. * @data: Data to write. * * This routine writes a word to the port register.
*/ staticvoid port_w16(struct ksz_hw *hw, int port, int offset, u16 data)
{
u32 addr;
/** * sw_chk - check switch register bits * @hw: The hardware instance. * @addr: The address of the switch register. * @bits: The data bits to check. * * This function checks whether the specified bits of the switch register are * set or not. * * Return 0 if the bits are not set.
*/ staticint sw_chk(struct ksz_hw *hw, u32 addr, u16 bits)
{
u16 data;
/** * sw_cfg - set switch register bits * @hw: The hardware instance. * @addr: The address of the switch register. * @bits: The data bits to set. * @set: The flag indicating whether the bits are to be set or not. * * This function sets or resets the specified bits of the switch register.
*/ staticvoid sw_cfg(struct ksz_hw *hw, u32 addr, u16 bits, int set)
{
u16 data;
data = readw(hw->io + addr); if (set)
data |= bits; else
data &= ~bits;
writew(data, hw->io + addr);
}
/* Bandwidth */
staticinlinevoid port_cfg_broad_storm(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM, set);
}
/* Driver set switch broadcast storm protection at 10% rate. */ #define BROADCAST_STORM_PROTECTION_RATE 10
/** * sw_cfg_broad_storm - configure broadcast storm threshold * @hw: The hardware instance. * @percent: Broadcast storm threshold in percent of transmit rate. * * This routine configures the broadcast storm threshold of the switch.
*/ staticvoid sw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
{
u16 data;
u32 value = ((u32) BROADCAST_STORM_VALUE * (u32) percent / 100);
if (value > BROADCAST_STORM_RATE)
value = BROADCAST_STORM_RATE;
data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
data &= ~(BROADCAST_STORM_RATE_LO | BROADCAST_STORM_RATE_HI);
data |= ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
}
/** * sw_get_broad_storm - get broadcast storm threshold * @hw: The hardware instance. * @percent: Buffer to store the broadcast storm threshold percentage. * * This routine retrieves the broadcast storm threshold of the switch.
*/ staticvoid sw_get_broad_storm(struct ksz_hw *hw, u8 *percent)
{ int num;
u16 data;
data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
num = (data & BROADCAST_STORM_RATE_HI);
num <<= 8;
num |= (data & BROADCAST_STORM_RATE_LO) >> 8;
num = DIV_ROUND_CLOSEST(num * 100, BROADCAST_STORM_VALUE);
*percent = (u8) num;
}
/** * sw_dis_broad_storm - disable broadstorm * @hw: The hardware instance. * @port: The port index. * * This routine disables the broadcast storm limit function of the switch.
*/ staticvoid sw_dis_broad_storm(struct ksz_hw *hw, int port)
{
port_cfg_broad_storm(hw, port, 0);
}
/** * sw_ena_broad_storm - enable broadcast storm * @hw: The hardware instance. * @port: The port index. * * This routine enables the broadcast storm limit function of the switch.
*/ staticvoid sw_ena_broad_storm(struct ksz_hw *hw, int port)
{
sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
port_cfg_broad_storm(hw, port, 1);
}
/** * sw_init_broad_storm - initialize broadcast storm * @hw: The hardware instance. * * This routine initializes the broadcast storm limit function of the switch.
*/ staticvoid sw_init_broad_storm(struct ksz_hw *hw)
{ int port;
hw->ksz_switch->broad_per = 1;
sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per); for (port = 0; port < TOTAL_PORT_NUM; port++)
sw_dis_broad_storm(hw, port);
sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, MULTICAST_STORM_DISABLE, 1);
}
/** * hw_cfg_broad_storm - configure broadcast storm * @hw: The hardware instance. * @percent: Broadcast storm threshold in percent of transmit rate. * * This routine configures the broadcast storm threshold of the switch. * It is called by user functions. The hardware should be acquired first.
*/ staticvoid hw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
{ if (percent > 100)
percent = 100;
/** * sw_dis_prio_rate - disable switch priority rate * @hw: The hardware instance. * @port: The port index. * * This routine disables the priority rate function of the switch.
*/ staticvoid sw_dis_prio_rate(struct ksz_hw *hw, int port)
{
u32 addr;
/** * sw_init_prio_rate - initialize switch prioirty rate * @hw: The hardware instance. * * This routine initializes the priority rate function of the switch.
*/ staticvoid sw_init_prio_rate(struct ksz_hw *hw)
{ int port; int prio; struct ksz_switch *sw = hw->ksz_switch;
for (port = 0; port < TOTAL_PORT_NUM; port++) { for (prio = 0; prio < PRIO_QUEUES; prio++) {
sw->port_cfg[port].rx_rate[prio] =
sw->port_cfg[port].tx_rate[prio] = 0;
}
sw_dis_prio_rate(hw, port);
}
}
/* Communication */
staticinlinevoid port_cfg_back_pressure(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE, set);
}
/* Mirroring */
staticinlinevoid port_cfg_mirror_sniffer(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_SNIFFER, set);
}
staticinlinevoid port_cfg_mirror_rx(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_RX, set);
}
staticinlinevoid port_cfg_mirror_tx(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_TX, set);
}
staticinlinevoid port_cfg_diffserv(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE, set);
}
staticinlinevoid port_cfg_802_1p(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE, set);
}
staticinlinevoid port_cfg_replace_vid(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING, set);
}
staticinlinevoid port_cfg_prio(struct ksz_hw *hw, int p, int set)
{
port_cfg(hw, p,
KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE, set);
}
/** * sw_dis_diffserv - disable switch DiffServ priority * @hw: The hardware instance. * @port: The port index. * * This routine disables the DiffServ priority function of the switch.
*/ staticvoid sw_dis_diffserv(struct ksz_hw *hw, int port)
{
port_cfg_diffserv(hw, port, 0);
}
/** * sw_dis_802_1p - disable switch 802.1p priority * @hw: The hardware instance. * @port: The port index. * * This routine disables the 802.1p priority function of the switch.
*/ staticvoid sw_dis_802_1p(struct ksz_hw *hw, int port)
{
port_cfg_802_1p(hw, port, 0);
}
/** * sw_cfg_replace_null_vid - * @hw: The hardware instance. * @set: The flag to disable or enable. *
*/ staticvoid sw_cfg_replace_null_vid(struct ksz_hw *hw, int set)
{
sw_cfg(hw, KS8842_SWITCH_CTRL_3_OFFSET, SWITCH_REPLACE_NULL_VID, set);
}
/** * sw_cfg_replace_vid - enable switch 802.10 priority re-mapping * @hw: The hardware instance. * @port: The port index. * @set: The flag to disable or enable. * * This routine enables the 802.1p priority re-mapping function of the switch. * That allows 802.1p priority field to be replaced with the port's default * tag's priority value if the ingress packet's 802.1p priority has a higher * priority than port's default tag's priority.
*/ staticvoid sw_cfg_replace_vid(struct ksz_hw *hw, int port, int set)
{
port_cfg_replace_vid(hw, port, set);
}
/** * sw_cfg_port_based - configure switch port based priority * @hw: The hardware instance. * @port: The port index. * @prio: The priority to set. * * This routine configures the port based priority of the switch.
*/ staticvoid sw_cfg_port_based(struct ksz_hw *hw, int port, u8 prio)
{
u16 data;
if (prio > PORT_BASED_PRIORITY_BASE)
prio = PORT_BASED_PRIORITY_BASE;
hw->ksz_switch->port_cfg[port].port_prio = prio;
port_r16(hw, port, KS8842_PORT_CTRL_1_OFFSET, &data);
data &= ~PORT_BASED_PRIORITY_MASK;
data |= prio << PORT_BASED_PRIORITY_SHIFT;
port_w16(hw, port, KS8842_PORT_CTRL_1_OFFSET, data);
}
/** * sw_dis_multi_queue - disable transmit multiple queues * @hw: The hardware instance. * @port: The port index. * * This routine disables the transmit multiple queues selection of the switch * port. Only single transmit queue on the port.
*/ staticvoid sw_dis_multi_queue(struct ksz_hw *hw, int port)
{
port_cfg_prio(hw, port, 0);
}
/** * sw_init_prio - initialize switch priority * @hw: The hardware instance. * * This routine initializes the switch QoS priority functions.
*/ staticvoid sw_init_prio(struct ksz_hw *hw)
{ int port; int tos; struct ksz_switch *sw = hw->ksz_switch;
/* * Init all the 802.1p tag priority value to be assigned to different * priority queue.
*/
sw->p_802_1p[0] = 0;
sw->p_802_1p[1] = 0;
sw->p_802_1p[2] = 1;
sw->p_802_1p[3] = 1;
sw->p_802_1p[4] = 2;
sw->p_802_1p[5] = 2;
sw->p_802_1p[6] = 3;
sw->p_802_1p[7] = 3;
/* * Init all the DiffServ priority value to be assigned to priority * queue 0.
*/ for (tos = 0; tos < DIFFSERV_ENTRIES; tos++)
sw->diffserv[tos] = 0;
/* All QoS functions disabled. */ for (port = 0; port < TOTAL_PORT_NUM; port++) {
sw_dis_multi_queue(hw, port);
sw_dis_diffserv(hw, port);
sw_dis_802_1p(hw, port);
sw_cfg_replace_vid(hw, port, 0);
/** * port_get_def_vid - get port default VID. * @hw: The hardware instance. * @port: The port index. * @vid: Buffer to store the VID. * * This routine retrieves the default VID of the port.
*/ staticvoid port_get_def_vid(struct ksz_hw *hw, int port, u16 *vid)
{
u32 addr;
/** * sw_init_vlan - initialize switch VLAN * @hw: The hardware instance. * * This routine initializes the VLAN function of the switch.
*/ staticvoid sw_init_vlan(struct ksz_hw *hw)
{ int port; int entry; struct ksz_switch *sw = hw->ksz_switch;
data = readb(hw->io + addr);
data &= ~PORT_VLAN_MEMBERSHIP;
data |= (member & PORT_MASK);
writeb(data, hw->io + addr);
hw->ksz_switch->port_cfg[port].member = member;
}
/** * sw_set_addr - configure switch MAC address * @hw: The hardware instance. * @mac_addr: The MAC address. * * This function configures the MAC address of the switch.
*/ staticvoid sw_set_addr(struct ksz_hw *hw, u8 *mac_addr)
{ int i;
for (i = 0; i < 6; i += 2) {
writeb(mac_addr[i], hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
writeb(mac_addr[1 + i], hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
}
}
/** * sw_set_global_ctrl - set switch global control * @hw: The hardware instance. * * This routine sets the global control of the switch function.
*/ staticvoid sw_set_global_ctrl(struct ksz_hw *hw)
{
u16 data;
/* Enable switch MII flow control. */
data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
data |= SWITCH_FLOW_CTRL;
writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
data = readw(hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
/* Enable aggressive back off algorithm in half duplex mode. */
data |= SWITCH_AGGR_BACKOFF;
/* Enable automatic fast aging when link changed detected. */
data |= SWITCH_AGING_ENABLE;
data |= SWITCH_LINK_AUTO_AGING;
if (hw->overrides & FAST_AGING)
data |= SWITCH_FAST_AGING; else
data &= ~SWITCH_FAST_AGING;
writew(data, hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
/* Enable no excessive collision drop. */
data |= NO_EXC_COLLISION_DROP;
writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
}
/** * port_set_stp_state - configure port spanning tree state * @hw: The hardware instance. * @port: The port index. * @state: The spanning tree state. * * This routine configures the spanning tree state of the port.
*/ staticvoid port_set_stp_state(struct ksz_hw *hw, int port, int state)
{
u16 data;
port_r16(hw, port, KS8842_PORT_CTRL_2_OFFSET, &data); switch (state) { case STP_STATE_DISABLED:
data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
data |= PORT_LEARN_DISABLE; break; case STP_STATE_LISTENING: /* * No need to turn on transmit because of port direct mode. * Turning on receive is required if static MAC table is not setup.
*/
data &= ~PORT_TX_ENABLE;
data |= PORT_RX_ENABLE;
data |= PORT_LEARN_DISABLE; break; case STP_STATE_LEARNING:
data &= ~PORT_TX_ENABLE;
data |= PORT_RX_ENABLE;
data &= ~PORT_LEARN_DISABLE; break; case STP_STATE_FORWARDING:
data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
data &= ~PORT_LEARN_DISABLE; break; case STP_STATE_BLOCKED: /* * Need to setup static MAC table with override to keep receiving BPDU * messages. See sw_init_stp routine.
*/
data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
data |= PORT_LEARN_DISABLE; break; case STP_STATE_SIMPLE:
data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
data |= PORT_LEARN_DISABLE; break;
}
port_w16(hw, port, KS8842_PORT_CTRL_2_OFFSET, data);
hw->ksz_switch->port_cfg[port].stp_state = state;
}
/** * sw_clr_sta_mac_table - clear static MAC table * @hw: The hardware instance. * * This routine clears the static MAC table.
*/ staticvoid sw_clr_sta_mac_table(struct ksz_hw *hw)
{ struct ksz_mac_table *entry; int i;
for (i = 0; i < STATIC_MAC_TABLE_ENTRIES; i++) {
entry = &hw->ksz_switch->mac_table[i];
sw_w_sta_mac_table(hw, i,
entry->mac_addr, entry->ports,
entry->override, 0,
entry->use_fid, entry->fid);
}
}
/** * sw_init_stp - initialize switch spanning tree support * @hw: The hardware instance. * * This routine initializes the spanning tree support of the switch.
*/ staticvoid sw_init_stp(struct ksz_hw *hw)
{ struct ksz_mac_table *entry;
/** * sw_block_addr - block certain packets from the host port * @hw: The hardware instance. * * This routine blocks certain packets from reaching to the host port.
*/ staticvoid sw_block_addr(struct ksz_hw *hw)
{ struct ksz_mac_table *entry; int i;
for (i = BROADCAST_ENTRY; i <= IPV6_ADDR_ENTRY; i++) {
entry = &hw->ksz_switch->mac_table[i];
entry->valid = 0;
sw_w_sta_mac_table(hw, i,
entry->mac_addr, entry->ports,
entry->override, entry->valid,
entry->use_fid, entry->fid);
}
}
/** * hw_r_phy - read data from PHY register * @hw: The hardware instance. * @port: Port to read. * @reg: PHY register to read. * @val: Buffer to store the read data. * * This routine reads data from the PHY register.
*/ staticvoid hw_r_phy(struct ksz_hw *hw, int port, u16 reg, u16 *val)
{ int phy;
/** * hw_w_phy - write data to PHY register * @hw: The hardware instance. * @port: Port to write. * @reg: PHY register to write. * @val: Word data to write. * * This routine writes data to the PHY register.
*/ staticvoid hw_w_phy(struct ksz_hw *hw, int port, u16 reg, u16 val)
{ int phy;
/* User defined EEPROM data */ #define EEPROM_DATA_OTHER_MAC_ADDR 9
/** * eeprom_read - read from AT93C46 EEPROM * @hw: The hardware instance. * @reg: The register offset. * * This function reads a word from the AT93C46 EEPROM. * * Return the data value.
*/ static u16 eeprom_read(struct ksz_hw *hw, u8 reg)
{
u16 data;
/** * eeprom_write - write to AT93C46 EEPROM * @hw: The hardware instance. * @reg: The register offset. * @data: The data value. * * This procedure writes a word to the AT93C46 EEPROM.
*/ staticvoid eeprom_write(struct ksz_hw *hw, u8 reg, u16 data)
{ int timeout;
/* Disable flow control in the half duplex mode. */ if (1 == info->duplex)
hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE; if (hw->enabled && cfg != hw->tx_cfg)
writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
}
}
/** * port_get_link_speed - get current link status * @port: The port instance. * * This routine reads PHY registers to determine the current link status of the * switch ports.
*/ staticvoid port_get_link_speed(struct ksz_port *port)
{
uint interrupt; struct ksz_port_info *info; struct ksz_port_info *linked = NULL; struct ksz_hw *hw = port->hw;
u16 data;
u16 status;
u8 local;
u8 remote; int i; int p;
interrupt = hw_block_intr(hw);
for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
info = &hw->port_info[p];
port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
port_r16(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
/* * Link status is changing all the time even when there is no * cable connection!
*/
remote = status & (PORT_AUTO_NEG_COMPLETE |
PORT_STATUS_LINK_GOOD);
local = (u8) data;
/* No change to status. */ if (local == info->advertised && remote == info->partner) continue;
if (linked && media_disconnected == port->linked->state)
port->linked = linked;
hw_restore_intr(hw, interrupt);
}
#define PHY_RESET_TIMEOUT 10
/** * port_set_link_speed - set port speed * @port: The port instance. * * This routine sets the link speed of the switch ports.
*/ staticvoid port_set_link_speed(struct ksz_port *port)
{ struct ksz_hw *hw = port->hw;
u16 data;
u16 cfg;
u8 status; int i; int p;
for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
port_r8(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
cfg = 0; if (status & PORT_STATUS_LINK_GOOD)
cfg = data;
data |= PORT_AUTO_NEG_ENABLE;
data = advertised_flow_ctrl(port, data);
data |= PORT_AUTO_NEG_100BTX_FD | PORT_AUTO_NEG_100BTX |
PORT_AUTO_NEG_10BT_FD | PORT_AUTO_NEG_10BT;
/* Check if manual configuration is specified by the user. */ if (port->speed || port->duplex) { if (10 == port->speed)
data &= ~(PORT_AUTO_NEG_100BTX_FD |
PORT_AUTO_NEG_100BTX); elseif (100 == port->speed)
data &= ~(PORT_AUTO_NEG_10BT_FD |
PORT_AUTO_NEG_10BT); if (1 == port->duplex)
data &= ~(PORT_AUTO_NEG_100BTX_FD |
PORT_AUTO_NEG_10BT_FD); elseif (2 == port->duplex)
data &= ~(PORT_AUTO_NEG_100BTX |
PORT_AUTO_NEG_10BT);
} if (data != cfg) {
data |= PORT_AUTO_NEG_RESTART;
port_w16(hw, p, KS884X_PORT_CTRL_4_OFFSET, data);
}
}
}
/** * port_force_link_speed - force port speed * @port: The port instance. * * This routine forces the link speed of the switch ports.
*/ staticvoid port_force_link_speed(struct ksz_port *port)
{ struct ksz_hw *hw = port->hw;
u16 data; int i; int phy; int p;
for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
phy = KS884X_PHY_1_CTRL_OFFSET + p * PHY_CTRL_INTERVAL;
hw_r_phy_ctrl(hw, phy, &data);
data &= ~BMCR_ANENABLE;
if (10 == port->speed)
data &= ~BMCR_SPEED100; elseif (100 == port->speed)
data |= BMCR_SPEED100; if (1 == port->duplex)
data &= ~BMCR_FULLDPLX; elseif (2 == port->duplex)
data |= BMCR_FULLDPLX;
hw_w_phy_ctrl(hw, phy, data);
}
}
staticvoid port_set_power_saving(struct ksz_port *port, int enable)
{ struct ksz_hw *hw = port->hw; int i; int p;
for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++)
port_cfg(hw, p,
KS884X_PORT_CTRL_4_OFFSET, PORT_POWER_DOWN, enable);
}
/* * KSZ8841 power management functions
*/
/** * hw_chk_wol_pme_status - check PMEN pin * @hw: The hardware instance. * * This function is used to check PMEN pin is asserted. * * Return 1 if PMEN pin is asserted; otherwise, 0.
*/ staticint hw_chk_wol_pme_status(struct ksz_hw *hw)
{ struct dev_info *hw_priv = container_of(hw, struct dev_info, hw); struct pci_dev *pdev = hw_priv->pdev;
u16 data;
/** * hw_cfg_wol_pme - enable or disable Wake-on-LAN * @hw: The hardware instance. * @set: The flag indicating whether to enable or disable. * * This routine is used to enable or disable Wake-on-LAN.
*/ staticvoid hw_cfg_wol_pme(struct ksz_hw *hw, int set)
{ struct dev_info *hw_priv = container_of(hw, struct dev_info, hw); struct pci_dev *pdev = hw_priv->pdev;
u16 data;
if (!pdev->pm_cap) return;
pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
data &= ~PCI_PM_CTRL_STATE_MASK; if (set)
data |= PCI_PM_CTRL_PME_ENABLE | PCI_D3hot; else
data &= ~PCI_PM_CTRL_PME_ENABLE;
pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
}
/** * hw_cfg_wol - configure Wake-on-LAN features * @hw: The hardware instance. * @frame: The pattern frame bit. * @set: The flag indicating whether to enable or disable. * * This routine is used to enable or disable certain Wake-on-LAN features.
*/ staticvoid hw_cfg_wol(struct ksz_hw *hw, u16 frame, int set)
{
u16 data;
data = readw(hw->io + KS8841_WOL_CTRL_OFFSET); if (set)
data |= frame; else
data &= ~frame;
writew(data, hw->io + KS8841_WOL_CTRL_OFFSET);
}
/** * hw_set_wol_frame - program Wake-on-LAN pattern * @hw: The hardware instance. * @i: The frame index. * @mask_size: The size of the mask. * @mask: Mask to ignore certain bytes in the pattern. * @frame_size: The size of the frame. * @pattern: The frame data. * * This routine is used to program Wake-on-LAN pattern.
*/ staticvoid hw_set_wol_frame(struct ksz_hw *hw, int i, uint mask_size, const u8 *mask, uint frame_size, const u8 *pattern)
{ int bits; int from; int len; int to;
u32 crc;
u8 data[64];
u8 val = 0;
if (frame_size > mask_size * 8)
frame_size = mask_size * 8; if (frame_size > 64)
frame_size = 64;
/** * hw_add_wol_mcast - add multicast pattern * @hw: The hardware instance. * * This routine is used to add multicast pattern for waking up the host. * * It is assumed the multicast packet is the ICMPv6 neighbor solicitation used * by IPv6 ping command. Note that multicast packets are filtred through the * multicast hash table, so not all multicast packets can wake up the host.
*/ staticvoid hw_add_wol_mcast(struct ksz_hw *hw)
{ staticconst u8 mask[] = { 0x3F };
u8 pattern[] = { 0x33, 0x33, 0xFF, 0x00, 0x00, 0x00 };
/** * hw_add_wol_ucast - add unicast pattern * @hw: The hardware instance. * * This routine is used to add unicast pattern to wakeup the host. * * It is assumed the unicast packet is directed to the device, as the hardware * can only receive them in normal case.
*/ staticvoid hw_add_wol_ucast(struct ksz_hw *hw)
{ staticconst u8 mask[] = { 0x3F };
/** * hw_enable_wol - enable Wake-on-LAN * @hw: The hardware instance. * @wol_enable: The Wake-on-LAN settings. * @net_addr: The IPv4 address assigned to the device. * * This routine is used to enable Wake-on-LAN depending on driver settings.
*/ staticvoid hw_enable_wol(struct ksz_hw *hw, u32 wol_enable, const u8 *net_addr)
{
hw_cfg_wol(hw, KS8841_WOL_MAGIC_ENABLE, (wol_enable & WAKE_MAGIC));
hw_cfg_wol(hw, KS8841_WOL_FRAME0_ENABLE, (wol_enable & WAKE_UCAST));
hw_add_wol_ucast(hw);
hw_cfg_wol(hw, KS8841_WOL_FRAME1_ENABLE, (wol_enable & WAKE_MCAST));
hw_add_wol_mcast(hw);
hw_cfg_wol(hw, KS8841_WOL_FRAME2_ENABLE, (wol_enable & WAKE_BCAST));
hw_cfg_wol(hw, KS8841_WOL_FRAME3_ENABLE, (wol_enable & WAKE_ARP));
hw_add_wol_arp(hw, net_addr);
}
/** * hw_init - check driver is correct for the hardware * @hw: The hardware instance. * * This function checks the hardware is correct for this driver and sets the * hardware up for proper initialization. * * Return number of ports or 0 if not right.
*/ staticint hw_init(struct ksz_hw *hw)
{ int rc = 0;
u16 data;
u16 revision;
/* Set bus speed to 125MHz. */
writew(BUS_SPEED_125_MHZ, hw->io + KS884X_BUS_CTRL_OFFSET);
/** * hw_stop_rx - stop receiving * @hw: The hardware instance. * * This routine stops the receive function of the hardware.
*/ staticvoid hw_stop_rx(struct ksz_hw *hw)
{
hw->rx_stop = 0;
hw_turn_off_intr(hw, KS884X_INT_RX_STOPPED);
writel((hw->rx_cfg & ~DMA_RX_ENABLE), hw->io + KS_DMA_RX_CTRL);
}
/** * hw_start_tx - start transmitting * @hw: The hardware instance. * * This routine starts the transmit function of the hardware.
*/ staticvoid hw_start_tx(struct ksz_hw *hw)
{
writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
}
/** * hw_stop_tx - stop transmitting * @hw: The hardware instance. * * This routine stops the transmit function of the hardware.
*/ staticvoid hw_stop_tx(struct ksz_hw *hw)
{
writel((hw->tx_cfg & ~DMA_TX_ENABLE), hw->io + KS_DMA_TX_CTRL);
}
/** * hw_disable - disable hardware * @hw: The hardware instance. * * This routine disables the hardware.
*/ staticvoid hw_disable(struct ksz_hw *hw)
{
hw_stop_rx(hw);
hw_stop_tx(hw);
hw->enabled = 0;
}
/** * hw_enable - enable hardware * @hw: The hardware instance. * * This routine enables the hardware.
*/ staticvoid hw_enable(struct ksz_hw *hw)
{
hw_start_tx(hw);
hw_start_rx(hw);
hw->enabled = 1;
}
/** * hw_alloc_pkt - allocate enough descriptors for transmission * @hw: The hardware instance. * @length: The length of the packet. * @physical: Number of descriptors required. * * This function allocates descriptors for transmission. * * Return 0 if not successful; 1 for buffer copy; or number of descriptors.
*/ staticint hw_alloc_pkt(struct ksz_hw *hw, int length, int physical)
{ /* Always leave one descriptor free. */ if (hw->tx_desc_info.avail <= 1) return 0;
/* Allocate a descriptor for transmission and mark it current. */
get_tx_pkt(&hw->tx_desc_info, &hw->tx_desc_info.cur);
hw->tx_desc_info.cur->sw.buf.tx.first_seg = 1;
/* Keep track of number of transmit descriptors used so far. */
++hw->tx_int_cnt;
hw->tx_size += length;
/* Cannot hold on too much data. */ if (hw->tx_size >= MAX_TX_HELD_SIZE)
hw->tx_int_cnt = hw->tx_int_mask + 1;
if (physical > hw->tx_desc_info.avail) return 1;
return hw->tx_desc_info.avail;
}
/** * hw_send_pkt - mark packet for transmission * @hw: The hardware instance. * * This routine marks the packet for transmission in PCI version.
*/ staticvoid hw_send_pkt(struct ksz_hw *hw)
{ struct ksz_desc *cur = hw->tx_desc_info.cur;
cur->sw.buf.tx.last_seg = 1;
/* Interrupt only after specified number of descriptors used. */ if (hw->tx_int_cnt > hw->tx_int_mask) {
cur->sw.buf.tx.intr = 1;
hw->tx_int_cnt = 0;
hw->tx_size = 0;
}
/* KSZ8842 supports port directed transmission. */
cur->sw.buf.tx.dest_port = hw->dst_ports;
/** * hw_set_addr - set MAC address * @hw: The hardware instance. * * This routine programs the MAC address of the hardware when the address is * overridden.
*/ staticvoid hw_set_addr(struct ksz_hw *hw)
{ int i;
for (i = 0; i < ETH_ALEN; i++)
writeb(hw->override_addr[MAC_ADDR_ORDER(i)],
hw->io + KS884X_ADDR_0_OFFSET + i);
sw_set_addr(hw, hw->override_addr);
}
/** * hw_read_addr - read MAC address * @hw: The hardware instance. * * This routine retrieves the MAC address of the hardware.
*/ staticvoid hw_read_addr(struct ksz_hw *hw)
{ int i;
for (i = 0; i < ETH_ALEN; i++)
hw->perm_addr[MAC_ADDR_ORDER(i)] = readb(hw->io +
KS884X_ADDR_0_OFFSET + i);
staticvoid hw_ena_add_addr(struct ksz_hw *hw, int index, u8 *mac_addr)
{ int i;
u32 mac_addr_lo;
u32 mac_addr_hi;
mac_addr_hi = 0; for (i = 0; i < 2; i++) {
mac_addr_hi <<= 8;
mac_addr_hi |= mac_addr[i];
}
mac_addr_hi |= ADD_ADDR_ENABLE;
mac_addr_lo = 0; for (i = 2; i < 6; i++) {
mac_addr_lo <<= 8;
mac_addr_lo |= mac_addr[i];
}
index *= ADD_ADDR_INCR;
writel(mac_addr_lo, hw->io + index + KS_ADD_ADDR_0_LO);
writel(mac_addr_hi, hw->io + index + KS_ADD_ADDR_0_HI);
}
staticvoid hw_set_add_addr(struct ksz_hw *hw)
{ int i;
for (i = 0; i < ADDITIONAL_ENTRIES; i++) { if (empty_addr(hw->address[i]))
writel(0, hw->io + ADD_ADDR_INCR * i +
KS_ADD_ADDR_0_HI); else
hw_ena_add_addr(hw, i, hw->address[i]);
}
}
staticint hw_add_addr(struct ksz_hw *hw, const u8 *mac_addr)
{ int i; int j = ADDITIONAL_ENTRIES;
if (ether_addr_equal(hw->override_addr, mac_addr)) return 0; for (i = 0; i < hw->addr_list_size; i++) { if (ether_addr_equal(hw->address[i], mac_addr)) return 0; if (ADDITIONAL_ENTRIES == j && empty_addr(hw->address[i]))
j = i;
} if (j < ADDITIONAL_ENTRIES) {
memcpy(hw->address[j], mac_addr, ETH_ALEN);
hw_ena_add_addr(hw, j, hw->address[j]); return 0;
} return -1;
}
staticint hw_del_addr(struct ksz_hw *hw, const u8 *mac_addr)
{ int i;
for (i = 0; i < hw->addr_list_size; i++) { if (ether_addr_equal(hw->address[i], mac_addr)) {
eth_zero_addr(hw->address[i]);
writel(0, hw->io + ADD_ADDR_INCR * i +
KS_ADD_ADDR_0_HI); return 0;
}
} return -1;
}
/** * hw_clr_multicast - clear multicast addresses * @hw: The hardware instance. * * This routine removes all multicast addresses set in the hardware.
*/ staticvoid hw_clr_multicast(struct ksz_hw *hw)
{ int i;
for (i = 0; i < HW_MULTICAST_SIZE; i++) {
hw->multi_bits[i] = 0;
/** * hw_set_grp_addr - set multicast addresses * @hw: The hardware instance. * * This routine programs multicast addresses for the hardware to accept those * addresses.
*/ staticvoid hw_set_grp_addr(struct ksz_hw *hw)
{ int i; int index; int position; int value;
for (i = 0; i < hw->multi_list_size; i++) {
position = (ether_crc(6, hw->multi_list[i]) >> 26) & 0x3f;
index = position >> 3;
value = 1 << (position & 7);
hw->multi_bits[index] |= (u8) value;
}
for (i = 0; i < HW_MULTICAST_SIZE; i++)
writeb(hw->multi_bits[i], hw->io + KS884X_MULTICAST_0_OFFSET +
i);
}
/** * hw_set_multicast - enable or disable all multicast receiving * @hw: The hardware instance. * @multicast: To turn on or off the all multicast feature. * * This routine enables/disables the hardware to accept all multicast packets.
*/ staticvoid hw_set_multicast(struct ksz_hw *hw, u8 multicast)
{ /* Stop receiving for reconfiguration. */
hw_stop_rx(hw);
if (multicast)
hw->rx_cfg |= DMA_RX_ALL_MULTICAST; else
hw->rx_cfg &= ~DMA_RX_ALL_MULTICAST;
if (hw->enabled)
hw_start_rx(hw);
}
/** * hw_set_promiscuous - enable or disable promiscuous receiving * @hw: The hardware instance. * @prom: To turn on or off the promiscuous feature. * * This routine enables/disables the hardware to accept all packets.
*/ staticvoid hw_set_promiscuous(struct ksz_hw *hw, u8 prom)
{ /* Stop receiving for reconfiguration. */
hw_stop_rx(hw);
if (prom)
hw->rx_cfg |= DMA_RX_PROMISCUOUS; else
hw->rx_cfg &= ~DMA_RX_PROMISCUOUS;
if (hw->enabled)
hw_start_rx(hw);
}
/** * sw_enable - enable the switch * @hw: The hardware instance. * @enable: The flag to enable or disable the switch * * This routine is used to enable/disable the switch in KSZ8842.
*/ staticvoid sw_enable(struct ksz_hw *hw, int enable)
{ int port;
for (port = 0; port < SWITCH_PORT_NUM; port++) { if (hw->dev_count > 1) { /* Set port-base vlan membership with host port. */
sw_cfg_port_base_vlan(hw, port,
HOST_MASK | (1 << port));
port_set_stp_state(hw, port, STP_STATE_DISABLED);
} else {
sw_cfg_port_base_vlan(hw, port, PORT_MASK);
port_set_stp_state(hw, port, STP_STATE_FORWARDING);
}
} if (hw->dev_count > 1)
port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE); else
port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_FORWARDING);
if (enable)
enable = KS8842_START;
writew(enable, hw->io + KS884X_CHIP_ID_OFFSET);
}
/** * sw_setup - setup the switch * @hw: The hardware instance. * * This routine setup the hardware switch engine for default operation.
*/ staticvoid sw_setup(struct ksz_hw *hw)
{ int port;
sw_set_global_ctrl(hw);
/* Enable switch broadcast storm protection at 10% percent rate. */
sw_init_broad_storm(hw);
hw_cfg_broad_storm(hw, BROADCAST_STORM_PROTECTION_RATE); for (port = 0; port < SWITCH_PORT_NUM; port++)
sw_ena_broad_storm(hw, port);
sw_init_prio(hw);
sw_init_mirror(hw);
sw_init_prio_rate(hw);
sw_init_vlan(hw);
if (hw->features & STP_SUPPORT)
sw_init_stp(hw); if (!sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
SWITCH_TX_FLOW_CTRL | SWITCH_RX_FLOW_CTRL))
hw->overrides |= PAUSE_FLOW_CTRL;
sw_enable(hw, 1);
}
/** * ksz_start_timer - start kernel timer * @info: Kernel timer information. * @time: The time tick. * * This routine starts the kernel timer after the specified time tick.
*/ staticvoid ksz_start_timer(struct ksz_timer_info *info, int time)
{
info->cnt = 0;
info->timer.expires = jiffies + time;
add_timer(&info->timer);
if (ksz_alloc_soft_desc(&hw->rx_desc_info, 0)) return 1; if (ksz_alloc_soft_desc(&hw->tx_desc_info, 1)) return 1;
return 0;
}
/** * free_dma_buf - release DMA buffer resources * @adapter: Adapter information structure. * @dma_buf: pointer to buf * @direction: to or from device * * This routine is just a helper function to release the DMA buffer resources.
*/ staticvoid free_dma_buf(struct dev_info *adapter, struct ksz_dma_buf *dma_buf, int direction)
{
dma_unmap_single(&adapter->pdev->dev, dma_buf->dma, dma_buf->len,
direction);
dev_kfree_skb(dma_buf->skb);
dma_buf->skb = NULL;
dma_buf->dma = 0;
}
/** * ksz_alloc_mem - allocate memory for hardware descriptors * @adapter: Adapter information structure. * * This function allocates memory for use by hardware descriptors for receiving * and transmitting. * * Return 0 if successful.
*/ staticint ksz_alloc_mem(struct dev_info *adapter)
{ struct ksz_hw *hw = &adapter->hw;
/* Determine the number of receive and transmit descriptors. */
hw->rx_desc_info.alloc = NUM_OF_RX_DESC;
hw->tx_desc_info.alloc = NUM_OF_TX_DESC;
/* Determine how many descriptors to skip transmit interrupt. */
hw->tx_int_cnt = 0;
hw->tx_int_mask = NUM_OF_TX_DESC / 4; if (hw->tx_int_mask > 8)
hw->tx_int_mask = 8; while (hw->tx_int_mask) {
hw->tx_int_cnt++;
hw->tx_int_mask >>= 1;
} if (hw->tx_int_cnt) {
hw->tx_int_mask = (1 << (hw->tx_int_cnt - 1)) - 1;
hw->tx_int_cnt = 0;
}
/** * ksz_free_buffers - free buffers used in the descriptors * @adapter: Adapter information structure. * @desc_info: Descriptor information structure. * @direction: to or from device * * This local routine frees buffers used in the DMA buffers.
*/ staticvoid ksz_free_buffers(struct dev_info *adapter, struct ksz_desc_info *desc_info, int direction)
{ int i; struct ksz_dma_buf *dma_buf; struct ksz_desc *desc = desc_info->ring;
for (i = 0; i < desc_info->alloc; i++) {
dma_buf = DMA_BUFFER(desc); if (dma_buf->skb)
free_dma_buf(adapter, dma_buf, direction);
desc++;
}
}
/** * ksz_free_mem - free all resources used by descriptors * @adapter: Adapter information structure. * * This local routine frees all the resources allocated by ksz_alloc_mem().
*/ staticvoid ksz_free_mem(struct dev_info *adapter)
{ /* Free transmit buffers. */
ksz_free_buffers(adapter, &adapter->hw.tx_desc_info, DMA_TO_DEVICE);
staticvoid get_mib_counters(struct ksz_hw *hw, int first, int cnt,
u64 *counter)
{ int i; int mib; int port; struct ksz_port_mib *port_mib;
memset(counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM); for (i = 0, port = first; i < cnt; i++, port++) {
port_mib = &hw->port_mib[port]; for (mib = port_mib->mib_start; mib < hw->mib_cnt; mib++)
counter[mib] += port_mib->counter[mib];
}
}
/** * send_packet - send packet * @skb: Socket buffer. * @dev: Network device. * * This routine is used to send a packet out to the network.
*/ staticvoid send_packet(struct sk_buff *skb, struct net_device *dev)
{ struct ksz_desc *desc; struct ksz_desc *first; struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; struct ksz_desc_info *info = &hw->tx_desc_info; struct ksz_dma_buf *dma_buf; int len; int last_frag = skb_shinfo(skb)->nr_frags;
/* * KSZ8842 with multiple device interfaces needs to be told which port * to send.
*/ if (hw->dev_count > 1)
hw->dst_ports = 1 << priv->port.first_port;
/* Hardware will pad the length to 60. */
len = skb->len;
/* Remember the very first descriptor. */
first = info->cur;
desc = first;
dma_buf = DMA_BUFFER(desc); if (last_frag) { int frag;
skb_frag_t *this_frag;
/* * The last descriptor holds the packet so that it can be returned to * network subsystem after all descriptors are transmitted.
*/
dma_buf->skb = skb;
/** * transmit_cleanup - clean up transmit descriptors * @hw_priv: Network device. * @normal: break if owned * * This routine is called to clean up the transmitted buffers.
*/ staticvoid transmit_cleanup(struct dev_info *hw_priv, int normal)
{ int last; union desc_stat status; struct ksz_hw *hw = &hw_priv->hw; struct ksz_desc_info *info = &hw->tx_desc_info; struct ksz_desc *desc; struct ksz_dma_buf *dma_buf; struct net_device *dev = NULL;
spin_lock_irq(&hw_priv->hwlock);
last = info->last;
while (info->avail < info->alloc) { /* Get next descriptor which is not hardware owned. */
desc = &info->ring[last];
status.data = le32_to_cpu(desc->phw->ctrl.data); if (status.tx.hw_owned) { if (normal) break; else
reset_desc(desc, status);
}
/* This descriptor contains the last buffer in the packet. */ if (dma_buf->skb) {
dev = dma_buf->skb->dev;
/* Release the packet back to network subsystem. */
dev_kfree_skb_irq(dma_buf->skb);
dma_buf->skb = NULL;
}
/* Free the transmitted descriptor. */
last++;
last &= info->mask;
info->avail++;
}
info->last = last;
spin_unlock_irq(&hw_priv->hwlock);
/* Notify the network subsystem that the packet has been sent. */ if (dev)
netif_trans_update(dev);
}
/** * tx_done - transmit done processing * @hw_priv: Network device. * * This routine is called when the transmit interrupt is triggered, indicating * either a packet is sent successfully or there are transmit errors.
*/ staticvoid tx_done(struct dev_info *hw_priv)
{ struct ksz_hw *hw = &hw_priv->hw; int port;
transmit_cleanup(hw_priv, 1);
for (port = 0; port < hw->dev_count; port++) { struct net_device *dev = hw->port_info[port].pdev;
if (netif_running(dev) && netif_queue_stopped(dev))
netif_wake_queue(dev);
}
}
/** * netdev_tx - send out packet * @skb: Socket buffer. * @dev: Network device. * * This function is used by the upper network layer to send out a packet. * * Return 0 if successful; otherwise an error code indicating failure.
*/ static netdev_tx_t netdev_tx(struct sk_buff *skb, struct net_device *dev)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; int left; int num = 1; int rc = 0;
if (hw->features & SMALL_PACKET_TX_BUG) { struct sk_buff *org_skb = skb;
/** * netdev_tx_timeout - transmit timeout processing * @dev: Network device. * @txqueue: index of hanging queue * * This routine is called when the transmit timer expires. That indicates the * hardware is not running correctly because transmit interrupts are not * triggered to free up resources so that the transmit routine can continue * sending out packets. The hardware is reset to correct the problem.
*/ staticvoid netdev_tx_timeout(struct net_device *dev, unsignedint txqueue)
{ staticunsignedlong last_reset;
if (hw->dev_count > 1) { /* * Only reset the hardware if time between calls is long * enough.
*/ if (time_before_eq(jiffies, last_reset + dev->watchdog_timeo))
hw_priv = NULL;
}
last_reset = jiffies; if (hw_priv) {
hw_dis_intr(hw);
hw_disable(hw);
/* Notify upper layer for received packet. */
netif_rx(skb);
return 0;
}
staticint dev_rcv_packets(struct dev_info *hw_priv)
{ int next; union desc_stat status; struct ksz_hw *hw = &hw_priv->hw; struct net_device *dev = hw->port_info[0].pdev; struct ksz_desc_info *info = &hw->rx_desc_info; int left = info->alloc; struct ksz_desc *desc; int received = 0;
next = info->next; while (left--) { /* Get next descriptor which is not hardware owned. */
desc = &info->ring[next];
status.data = le32_to_cpu(desc->phw->ctrl.data); if (status.rx.hw_owned) break;
/* Status valid only when last descriptor bit is set. */ if (status.rx.last_desc && status.rx.first_desc) { if (rx_proc(dev, hw, desc, status)) goto release_packet;
received++;
}
release_packet:
release_desc(desc);
next++;
next &= info->mask;
}
info->next = next;
return received;
}
staticint port_rcv_packets(struct dev_info *hw_priv)
{ int next; union desc_stat status; struct ksz_hw *hw = &hw_priv->hw; struct net_device *dev = hw->port_info[0].pdev; struct ksz_desc_info *info = &hw->rx_desc_info; int left = info->alloc; struct ksz_desc *desc; int received = 0;
next = info->next; while (left--) { /* Get next descriptor which is not hardware owned. */
desc = &info->ring[next];
status.data = le32_to_cpu(desc->phw->ctrl.data); if (status.rx.hw_owned) break;
if (hw->dev_count > 1) { /* Get received port number. */ int p = HW_TO_DEV_PORT(status.rx.src_port);
dev = hw->port_info[p].pdev; if (!netif_running(dev)) goto release_packet;
}
/* Status valid only when last descriptor bit is set. */ if (status.rx.last_desc && status.rx.first_desc) { if (rx_proc(dev, hw, desc, status)) goto release_packet;
received++;
}
release_packet:
release_desc(desc);
next++;
next &= info->mask;
}
info->next = next;
return received;
}
staticint dev_rcv_special(struct dev_info *hw_priv)
{ int next; union desc_stat status; struct ksz_hw *hw = &hw_priv->hw; struct net_device *dev = hw->port_info[0].pdev; struct ksz_desc_info *info = &hw->rx_desc_info; int left = info->alloc; struct ksz_desc *desc; int received = 0;
next = info->next; while (left--) { /* Get next descriptor which is not hardware owned. */
desc = &info->ring[next];
status.data = le32_to_cpu(desc->phw->ctrl.data); if (status.rx.hw_owned) break;
if (hw->dev_count > 1) { /* Get received port number. */ int p = HW_TO_DEV_PORT(status.rx.src_port);
dev = hw->port_info[p].pdev; if (!netif_running(dev)) goto release_packet;
}
/* Status valid only when last descriptor bit is set. */ if (status.rx.last_desc && status.rx.first_desc) { /* * Receive without error. With receive errors * disabled, packets with receive errors will be * dropped, so no need to check the error bit.
*/ if (!status.rx.error || (status.data &
KS_DESC_RX_ERROR_COND) ==
KS_DESC_RX_ERROR_TOO_LONG) { if (rx_proc(dev, hw, desc, status)) goto release_packet;
received++;
} else { struct dev_priv *priv = netdev_priv(dev);
/* No ports in forwarding state. */ if (!sw->member) {
port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
sw_block_addr(hw);
} for (port = 0; port < SWITCH_PORT_NUM; port++) { if (STP_STATE_FORWARDING == sw->port_cfg[port].stp_state)
member = HOST_MASK | sw->member; else
member = HOST_MASK | (1 << port); if (member != sw->port_cfg[port].member)
sw_cfg_port_base_vlan(hw, port, member);
}
}
/** * netdev_close - close network device * @dev: Network device. * * This function process the close operation of network device. This is caused * by the user command "ifconfig ethX down." * * Return 0 if successful; otherwise an error code indicating failure.
*/ staticint netdev_close(struct net_device *dev)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_port *port = &priv->port; struct ksz_hw *hw = &hw_priv->hw; int pi;
netif_stop_queue(dev);
ksz_stop_timer(&priv->monitor_timer_info);
/* Need to shut the port manually in multiple device interfaces mode. */ if (hw->dev_count > 1) {
port_set_stp_state(hw, port->first_port, STP_STATE_DISABLED);
/* Port is closed. Need to change bridge setting. */ if (hw->features & STP_SUPPORT) {
pi = 1 << port->first_port; if (hw->ksz_switch->member & pi) {
hw->ksz_switch->member &= ~pi;
bridge_change(hw);
}
}
} if (port->first_port > 0)
hw_del_addr(hw, dev->dev_addr); if (!hw_priv->wol_enable)
port_set_power_saving(port, true);
if (priv->multicast)
--hw->all_multi; if (priv->promiscuous)
--hw->promiscuous;
hw_priv->opened--; if (!(hw_priv->opened)) {
ksz_stop_timer(&hw_priv->mib_timer_info);
flush_work(&hw_priv->mib_read);
/** * netdev_open - open network device * @dev: Network device. * * This function process the open operation of network device. This is caused * by the user command "ifconfig ethX up." * * Return 0 if successful; otherwise an error code indicating failure.
*/ staticint netdev_open(struct net_device *dev)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; struct ksz_port *port = &priv->port; unsignedlong next_jiffies; int i; int p; int rc = 0;
if (!(hw_priv->opened)) {
rc = prepare_hardware(dev); if (rc) return rc; for (i = 0; i < hw->mib_port_cnt; i++) {
next_jiffies += HZ * 1;
hw_priv->counter[i].time = next_jiffies;
hw->port_mib[i].state = media_disconnected;
port_init_cnt(hw, i);
} if (hw->ksz_switch)
hw->port_mib[HOST_PORT].state = media_connected; else {
hw_add_wol_bcast(hw);
hw_cfg_wol_pme(hw, 0);
hw_clr_wol_pme_status(&hw_priv->hw);
}
}
port_set_power_saving(port, false);
for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) { /* * Initialize to invalid value so that link detection * is done.
*/
hw->port_info[p].partner = 0xFF;
hw->port_info[p].state = media_disconnected;
}
/* Need to open the port in multiple device interfaces mode. */ if (hw->dev_count > 1) {
port_set_stp_state(hw, port->first_port, STP_STATE_SIMPLE); if (port->first_port > 0)
hw_add_addr(hw, dev->dev_addr);
}
port_get_link_speed(port); if (port->force_link)
port_force_link_speed(port); else
port_set_link_speed(port);
if (!(hw_priv->opened)) {
hw_setup_intr(hw);
hw_enable(hw);
hw_ena_intr(hw);
if (hw->mib_port_cnt)
ksz_start_timer(&hw_priv->mib_timer_info,
hw_priv->mib_timer_info.period);
}
/** * netdev_set_mac_address - set network device MAC address * @dev: Network device. * @addr: Buffer of MAC address. * * This function is used to set the MAC address of the network device. * * Return 0 to indicate success.
*/ staticint netdev_set_mac_address(struct net_device *dev, void *addr)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; struct sockaddr *mac = addr;
uint interrupt;
/* * Port is not in promiscuous mode, meaning it is released * from the bridge.
*/ if ((hw->features & STP_SUPPORT) && !promiscuous &&
netif_is_bridge_port(dev)) { struct ksz_switch *sw = hw->ksz_switch; int port = priv->port.first_port;
/* Cannot use different hashes in multiple device interfaces mode. */ if (hw_priv->hw.dev_count > 1) return;
if ((dev->flags & IFF_MULTICAST) && !netdev_mc_empty(dev)) { int i = 0;
/* List too big to support so turn on all multicast mode. */ if (netdev_mc_count(dev) > MAX_MULTICAST_LIST) { if (MAX_MULTICAST_LIST != hw->multi_list_size) {
hw->multi_list_size = MAX_MULTICAST_LIST;
++hw->all_multi;
hw_set_multicast(hw, hw->all_multi);
} return;
}
/* Save advertised settings for workaround in next function. */
ethtool_convert_link_mode_to_legacy_u32(&priv->advertising,
cmd->link_modes.advertising);
return 0;
}
/** * netdev_set_link_ksettings - set network device settings * @dev: Network device. * @cmd: Ethtool command. * * This function sets the PHY according to the ethtool command. * * Return 0 if successful; otherwise an error code.
*/ staticint netdev_set_link_ksettings(struct net_device *dev, conststruct ethtool_link_ksettings *cmd)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_port *port = &priv->port; struct ethtool_link_ksettings copy_cmd;
u32 speed = cmd->base.speed;
u32 advertising; int rc;
/** * netdev_get_link - get network device link status * @dev: Network device. * * This function gets the link status from the PHY. * * Return true if PHY is linked and false otherwise.
*/ static u32 netdev_get_link(struct net_device *dev)
{ struct dev_priv *priv = netdev_priv(dev); int rc;
rc = mii_link_ok(&priv->mii_if); return rc;
}
/** * netdev_get_drvinfo - get network driver information * @dev: Network device. * @info: Ethtool driver info data structure. * * This procedure returns the driver information.
*/ staticvoid netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter;
/** * netdev_get_regs_len - get length of register dump * @dev: Network device. * * This function returns the length of the register dump. * * Return length of the register dump.
*/ staticint netdev_get_regs_len(struct net_device *dev)
{ struct hw_regs *range = hw_regs_range; int regs_len = 0x10 * sizeof(u32);
/** * netdev_get_eeprom_len - get EEPROM length * @dev: Network device. * * This function returns the length of the EEPROM. * * Return length of the EEPROM.
*/ staticint netdev_get_eeprom_len(struct net_device *dev)
{ return EEPROM_SIZE * 2;
}
#define EEPROM_MAGIC 0x10A18842
/** * netdev_get_eeprom - get EEPROM data * @dev: Network device. * @eeprom: Ethtool EEPROM data structure. * @data: Buffer to store the EEPROM data. * * This function dumps the EEPROM data in the provided buffer. * * Return 0 if successful; otherwise an error code.
*/ staticint netdev_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter;
u8 *eeprom_byte = (u8 *) eeprom_data; int i; int len;
len = (eeprom->offset + eeprom->len + 1) / 2; for (i = eeprom->offset / 2; i < len; i++)
eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
eeprom->magic = EEPROM_MAGIC;
memcpy(data, &eeprom_byte[eeprom->offset], eeprom->len);
return 0;
}
/** * netdev_set_eeprom - write EEPROM data * @dev: Network device. * @eeprom: Ethtool EEPROM data structure. * @data: Data buffer. * * This function modifies the EEPROM data one byte at a time. * * Return 0 if successful; otherwise an error code.
*/ staticint netdev_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter;
u16 eeprom_word[EEPROM_SIZE];
u8 *eeprom_byte = (u8 *) eeprom_word; int i; int len;
if (eeprom->magic != EEPROM_MAGIC) return -EINVAL;
len = (eeprom->offset + eeprom->len + 1) / 2; for (i = eeprom->offset / 2; i < len; i++)
eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
memcpy(eeprom_word, eeprom_data, EEPROM_SIZE * 2);
memcpy(&eeprom_byte[eeprom->offset], data, eeprom->len); for (i = 0; i < EEPROM_SIZE; i++) if (eeprom_word[i] != eeprom_data[i]) {
eeprom_data[i] = eeprom_word[i];
eeprom_write(&hw_priv->hw, i, eeprom_data[i]);
}
return 0;
}
/** * netdev_get_pauseparam - get flow control parameters * @dev: Network device. * @pause: Ethtool PAUSE settings data structure. * * This procedure returns the PAUSE control flow settings.
*/ staticvoid netdev_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw;
/** * netdev_get_strings - get statistics identity strings * @dev: Network device. * @stringset: String set identifier. * @buf: Buffer to store the strings. * * This procedure returns the strings used to identify the statistics.
*/ staticvoid netdev_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw;
if (ETH_SS_STATS == stringset)
memcpy(buf, ðtool_stats_keys,
ETH_GSTRING_LEN * hw->mib_cnt);
}
/** * netdev_get_sset_count - get statistics size * @dev: Network device. * @sset: The statistics set number. * * This function returns the size of the statistics to be reported. * * Return size of the statistics to be reported.
*/ staticint netdev_get_sset_count(struct net_device *dev, int sset)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw;
/** * netdev_get_ethtool_stats - get network device statistics * @dev: Network device. * @stats: Ethtool statistics data structure. * @data: Buffer to store the statistics. * * This procedure returns the statistics.
*/ staticvoid netdev_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; struct ksz_port *port = &priv->port; int n_stats = stats->n_stats; int i; int n; int p;
u64 counter[TOTAL_PORT_COUNTER_NUM];
mutex_lock(&hw_priv->lock);
n = SWITCH_PORT_NUM; for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) { if (media_connected == hw->port_mib[p].state) {
hw_priv->counter[p].read = 1;
/* Remember first port that requests read. */ if (n == SWITCH_PORT_NUM)
n = p;
}
}
mutex_unlock(&hw_priv->lock);
if (n < SWITCH_PORT_NUM)
schedule_work(&hw_priv->mib_read);
if (1 == port->mib_port_cnt && n < SWITCH_PORT_NUM) {
p = n;
wait_event_interruptible_timeout(
hw_priv->counter[p].counter,
2 == hw_priv->counter[p].read,
HZ * 1);
} else for (i = 0, p = n; i < port->mib_port_cnt - n; i++, p++) { if (0 == i) {
wait_event_interruptible_timeout(
hw_priv->counter[p].counter,
2 == hw_priv->counter[p].read,
HZ * 2);
} elseif (hw->port_mib[p].cnt_ptr) {
wait_event_interruptible_timeout(
hw_priv->counter[p].counter,
2 == hw_priv->counter[p].read,
HZ * 1);
}
}
get_mib_counters(hw, port->first_port, port->mib_port_cnt, counter);
n = hw->mib_cnt; if (n > n_stats)
n = n_stats;
n_stats -= n; for (i = 0; i < n; i++)
*data++ = counter[i];
}
/** * netdev_set_features - set receive checksum support * @dev: Network device. * @features: New device features (offloads). * * This function sets receive checksum support setting. * * Return 0 if successful; otherwise an error code.
*/ staticint netdev_set_features(struct net_device *dev,
netdev_features_t features)
{ struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw;
mutex_lock(&hw_priv->lock);
/* see note in hw_setup() */ if (features & NETIF_F_RXCSUM)
hw->rx_cfg |= DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP; else
hw->rx_cfg &= ~(DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
if (hw->enabled)
writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
/* This is used to verify Wake-on-LAN is working. */ if (hw_priv->pme_wait) { if (time_is_before_eq_jiffies(hw_priv->pme_wait)) {
hw_clr_wol_pme_status(&hw_priv->hw);
hw_priv->pme_wait = 0;
}
} elseif (hw_chk_wol_pme_status(&hw_priv->hw)) {
/* PME is asserted. Wait 2 seconds to clear it. */
hw_priv->pme_wait = jiffies + HZ * 2;
}
ksz_update_timer(&hw_priv->mib_timer_info);
}
/** * dev_monitor - periodic monitoring * @t: timer list containing a network device pointer. * * This routine is run in a kernel timer to monitor the network device.
*/ staticvoid dev_monitor(struct timer_list *t)
{ struct dev_priv *priv = timer_container_of(priv, t,
monitor_timer_info.timer); struct net_device *dev = priv->mii_if.dev; struct dev_info *hw_priv = priv->adapter; struct ksz_hw *hw = &hw_priv->hw; struct ksz_port *port = &priv->port;
if (!(hw->features & LINK_INT_WORKING))
port_get_link_speed(port);
update_link(dev, priv, port);
/* * This enables multiple network device mode for KSZ8842, which contains a * switch with two physical ports. Some users like to take control of the * ports for running Spanning Tree Protocol. The driver will create an * additional eth? device for the other port. * * Some limitations are the network devices cannot have different MTU and * multicast hash tables.
*/ staticint multi_dev;
/* * As most users select multiple network device mode to use Spanning Tree * Protocol, this enables a feature in which most unicast and multicast packets * are forwarded inside the switch and not passed to the host. Only packets * that need the host's attention are passed to it. This prevents the host * wasting CPU time to examine each and every incoming packets and do the * forwarding itself. * * As the hack requires the private bridge header, the driver cannot compile * with just the kernel headers. * * Enabling STP support also turns on multiple network device mode.
*/ staticint stp;
/* * This enables fast aging in the KSZ8842 switch. Not sure what situation * needs that. However, fast aging is used to flush the dynamic MAC table when * STP support is enabled.
*/ staticint fast_aging;
/** * netdev_init - initialize network device. * @dev: Network device. * * This function initializes the network device. * * Return 0 if successful; otherwise an error code indicating failure.
*/ staticint __init netdev_init(struct net_device *dev)
{ struct dev_priv *priv = netdev_priv(dev);
/* * Hardware does not really support IPv6 checksum generation, but * driver actually runs faster with this on.
*/
dev->hw_features |= NETIF_F_IPV6_CSUM;
for (i = 0; i < hw->dev_count; i++) {
dev = alloc_etherdev(sizeof(struct dev_priv)); if (!dev) goto pcidev_init_reg_err;
SET_NETDEV_DEV(dev, &pdev->dev);
info->netdev[i] = dev;
release_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0)); for (i = 0; i < hw_priv->hw.dev_count; i++) { if (info->netdev[i])
netdev_free(info->netdev[i]);
} if (hw_priv->hw.io)
iounmap(hw_priv->hw.io);
ksz_free_mem(hw_priv);
kfree(hw_priv->hw.ksz_switch);
pci_dev_put(hw_priv->pdev);
kfree(info);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.116Bemerkung:
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.