diff options
Diffstat (limited to 'drivers/net')
631 files changed, 59414 insertions, 11654 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3835321b8cf..b45b240889f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -25,6 +25,9 @@ menuconfig NETDEVICES # that for each of the symbols. if NETDEVICES +config MII + tristate + config NET_CORE default y bool "Network core driver support" @@ -100,13 +103,6 @@ config NET_FC adaptor below. You also should have said Y to "SCSI support" and "SCSI generic support". -config MII - tristate "Generic Media Independent Interface device support" - help - Most ethernet controllers have MII transceiver either as an external - or internal device. It is safe to say Y or M here even if your - ethernet card lacks MII. - config IFB tristate "Intermediate Functional Block support" depends on NET_CLS_ACT @@ -244,6 +240,16 @@ config VIRTIO_NET This is the virtual network driver for virtio. It can be used with lguest or QEMU based VMMs (like KVM or Xen). Say Y or M. +config NLMON + tristate "Virtual netlink monitoring device" + ---help--- + This option enables a monitoring net device for netlink skbs. The + purpose of this is to analyze netlink messages with packet sockets. + Thus applications like tcpdump will be able to see local netlink + messages if they tap into the netlink device, record pcaps for further + diagnostics, etc. This is mostly intended for developers or support + to debug netlink issues. If unsure, say N. + endif # NET_CORE config SUNGEM_PHY diff --git a/drivers/net/Makefile b/drivers/net/Makefile index ef3d090efed..3fef8a81c0f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_TUN) += tun.o obj-$(CONFIG_VETH) += veth.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VXLAN) += vxlan.o +obj-$(CONFIG_NLMON) += nlmon.o # # Networking Drivers diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index e02cc265723..4ea8ed150d4 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1056,7 +1056,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) * */ -static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct slave *slave2) +static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2) { u8 tmp_mac_addr[ETH_ALEN]; @@ -1129,6 +1129,7 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla { int perm_curr_diff; int perm_bond_diff; + struct slave *found_slave; perm_curr_diff = !ether_addr_equal_64bits(slave->perm_hwaddr, slave->dev->dev_addr); @@ -1136,21 +1137,12 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla bond->dev->dev_addr); if (perm_curr_diff && perm_bond_diff) { - struct slave *tmp_slave; - int i, found = 0; - - bond_for_each_slave(bond, tmp_slave, i) { - if (ether_addr_equal_64bits(slave->perm_hwaddr, - tmp_slave->dev->dev_addr)) { - found = 1; - break; - } - } + found_slave = bond_slave_has_mac(bond, slave->perm_hwaddr); - if (found) { + if (found_slave) { /* locking: needs RTNL and nothing else */ - alb_swap_mac_addr(bond, slave, tmp_slave); - alb_fasten_mac_swap(bond, slave, tmp_slave); + alb_swap_mac_addr(slave, found_slave); + alb_fasten_mac_swap(bond, slave, found_slave); } } } @@ -1175,16 +1167,13 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla * @slave. * * assumption: this function is called before @slave is attached to the - * bond slave list. - * - * caller must hold the bond lock for write since the mac addresses are compared - * and may be swapped. + * bond slave list. */ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave) { - struct slave *tmp_slave1, *tmp_slave2, *free_mac_slave; + struct slave *tmp_slave1, *free_mac_slave = NULL; struct slave *has_bond_addr = bond->curr_active_slave; - int i, j, found = 0; + int i; if (bond->slave_cnt == 0) { /* this is the first slave */ @@ -1196,15 +1185,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav * slaves in the bond. */ if (!ether_addr_equal_64bits(slave->perm_hwaddr, bond->dev->dev_addr)) { - bond_for_each_slave(bond, tmp_slave1, i) { - if (ether_addr_equal_64bits(tmp_slave1->dev->dev_addr, - slave->dev->dev_addr)) { - found = 1; - break; - } - } - - if (!found) + if (!bond_slave_has_mac(bond, slave->dev->dev_addr)) return 0; /* Try setting slave mac to bond address and fall-through @@ -1215,19 +1196,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav /* The slave's address is equal to the address of the bond. * Search for a spare address in the bond for this slave. */ - free_mac_slave = NULL; - bond_for_each_slave(bond, tmp_slave1, i) { - found = 0; - bond_for_each_slave(bond, tmp_slave2, j) { - if (ether_addr_equal_64bits(tmp_slave1->perm_hwaddr, - tmp_slave2->dev->dev_addr)) { - found = 1; - break; - } - } - - if (!found) { + if (!bond_slave_has_mac(bond, tmp_slave1->perm_hwaddr)) { /* no slave has tmp_slave1's perm addr * as its curr addr */ @@ -1607,15 +1577,7 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave) return res; } - /* caller must hold the bond lock for write since the mac addresses - * are compared and may be swapped. - */ - read_lock(&bond->lock); - res = alb_handle_addr_collision_on_attach(bond, slave); - - read_unlock(&bond->lock); - if (res) { return res; } @@ -1698,7 +1660,6 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave __acquires(&bond->curr_slave_lock) { struct slave *swap_slave; - int i; if (bond->curr_active_slave == new_slave) { return; @@ -1720,17 +1681,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave /* set the new curr_active_slave to the bonds mac address * i.e. swap mac addresses of old curr_active_slave and new curr_active_slave */ - if (!swap_slave) { - struct slave *tmp_slave; - /* find slave that is holding the bond's mac address */ - bond_for_each_slave(bond, tmp_slave, i) { - if (ether_addr_equal_64bits(tmp_slave->dev->dev_addr, - bond->dev->dev_addr)) { - swap_slave = tmp_slave; - break; - } - } - } + if (!swap_slave) + swap_slave = bond_slave_has_mac(bond, bond->dev->dev_addr); /* * Arrange for swap_slave and new_slave to temporarily be @@ -1750,16 +1702,12 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave /* curr_active_slave must be set before calling alb_swap_mac_addr */ if (swap_slave) { /* swap mac address */ - alb_swap_mac_addr(bond, swap_slave, new_slave); - } else { - /* set the new_slave to the bond mac address */ - alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); - } - - if (swap_slave) { + alb_swap_mac_addr(swap_slave, new_slave); alb_fasten_mac_swap(bond, swap_slave, new_slave); read_lock(&bond->lock); } else { + /* set the new_slave to the bond mac address */ + alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); read_lock(&bond->lock); alb_send_learning_packets(new_slave, bond->dev->dev_addr); } @@ -1776,9 +1724,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) { struct bonding *bond = netdev_priv(bond_dev); struct sockaddr *sa = addr; - struct slave *slave, *swap_slave; + struct slave *swap_slave; int res; - int i; if (!is_valid_ether_addr(sa->sa_data)) { return -EADDRNOTAVAIL; @@ -1799,18 +1746,10 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) return 0; } - swap_slave = NULL; - - bond_for_each_slave(bond, slave, i) { - if (ether_addr_equal_64bits(slave->dev->dev_addr, - bond_dev->dev_addr)) { - swap_slave = slave; - break; - } - } + swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr); if (swap_slave) { - alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave); + alb_swap_mac_addr(swap_slave, bond->curr_active_slave); alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave); } else { alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f9756961352..07f257d44a1 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -104,6 +104,7 @@ static char *xmit_hash_policy; static int arp_interval = BOND_LINK_ARP_INTERV; static char *arp_ip_target[BOND_MAX_ARP_TARGETS]; static char *arp_validate; +static char *arp_all_targets; static char *fail_over_mac; static int all_slaves_active = 0; static struct bond_params bonding_defaults; @@ -166,6 +167,8 @@ module_param(arp_validate, charp, 0); MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes; " "0 for none (default), 1 for active, " "2 for backup, 3 for all"); +module_param(arp_all_targets, charp, 0); +MODULE_PARM_DESC(arp_all_targets, "fail on any/all arp targets timeout; 0 for any (default), 1 for all"); module_param(fail_over_mac, charp, 0); MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to " "the same MAC; 0 for none (default), " @@ -216,6 +219,12 @@ const struct bond_parm_tbl xmit_hashtype_tbl[] = { { NULL, -1}, }; +const struct bond_parm_tbl arp_all_targets_tbl[] = { +{ "any", BOND_ARP_TARGETS_ANY}, +{ "all", BOND_ARP_TARGETS_ALL}, +{ NULL, -1}, +}; + const struct bond_parm_tbl arp_validate_tbl[] = { { "none", BOND_ARP_VALIDATE_NONE}, { "active", BOND_ARP_VALIDATE_ACTIVE}, @@ -706,45 +715,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc) return err; } -/* - * Add a Multicast address to slaves - * according to mode - */ -static void bond_mc_add(struct bonding *bond, void *addr) -{ - if (USES_PRIMARY(bond->params.mode)) { - /* write lock already acquired */ - if (bond->curr_active_slave) - dev_mc_add(bond->curr_active_slave->dev, addr); - } else { - struct slave *slave; - int i; - - bond_for_each_slave(bond, slave, i) - dev_mc_add(slave->dev, addr); - } -} - -/* - * Remove a multicast address from slave - * according to mode - */ -static void bond_mc_del(struct bonding *bond, void *addr) -{ - if (USES_PRIMARY(bond->params.mode)) { - /* write lock already acquired */ - if (bond->curr_active_slave) - dev_mc_del(bond->curr_active_slave->dev, addr); - } else { - struct slave *slave; - int i; - bond_for_each_slave(bond, slave, i) { - dev_mc_del(slave->dev, addr); - } - } -} - - static void __bond_resend_igmp_join_requests(struct net_device *dev) { struct in_device *in_dev; @@ -810,17 +780,15 @@ static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) bond_resend_igmp_join_requests(bond); } -/* - * flush all members of flush->mc_list from device dev->mc_list +/* Flush bond's hardware addresses from slave */ -static void bond_mc_list_flush(struct net_device *bond_dev, +static void bond_hw_addr_flush(struct net_device *bond_dev, struct net_device *slave_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(ha, bond_dev) - dev_mc_del(slave_dev, ha->addr); + dev_uc_unsync(slave_dev, bond_dev); + dev_mc_unsync(slave_dev, bond_dev); if (bond->params.mode == BOND_MODE_8023AD) { /* del lacpdu mc addr from mc list */ @@ -832,22 +800,14 @@ static void bond_mc_list_flush(struct net_device *bond_dev, /*--------------------------- Active slave change ---------------------------*/ -/* - * Update the mc list and multicast-related flags for the new and - * old active slaves (if any) according to the multicast mode, and - * promiscuous flags unconditionally. +/* Update the hardware address list and promisc/allmulti for the new and + * old active slaves (if any). Modes that are !USES_PRIMARY keep all + * slaves up date at all times; only the USES_PRIMARY modes need to call + * this function to swap these settings during a failover. */ -static void bond_mc_swap(struct bonding *bond, struct slave *new_active, - struct slave *old_active) +static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active, + struct slave *old_active) { - struct netdev_hw_addr *ha; - - if (!USES_PRIMARY(bond->params.mode)) - /* nothing to do - mc list is already up-to-date on - * all slaves - */ - return; - if (old_active) { if (bond->dev->flags & IFF_PROMISC) dev_set_promiscuity(old_active->dev, -1); @@ -855,10 +815,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, if (bond->dev->flags & IFF_ALLMULTI) dev_set_allmulti(old_active->dev, -1); - netif_addr_lock_bh(bond->dev); - netdev_for_each_mc_addr(ha, bond->dev) - dev_mc_del(old_active->dev, ha->addr); - netif_addr_unlock_bh(bond->dev); + bond_hw_addr_flush(bond->dev, old_active->dev); } if (new_active) { @@ -870,12 +827,29 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, dev_set_allmulti(new_active->dev, 1); netif_addr_lock_bh(bond->dev); - netdev_for_each_mc_addr(ha, bond->dev) - dev_mc_add(new_active->dev, ha->addr); + dev_uc_sync(new_active->dev, bond->dev); + dev_mc_sync(new_active->dev, bond->dev); netif_addr_unlock_bh(bond->dev); } } +/** + * bond_set_dev_addr - clone slave's address to bond + * @bond_dev: bond net device + * @slave_dev: slave net device + * + * Should be called with RTNL held. + */ +static void bond_set_dev_addr(struct net_device *bond_dev, + struct net_device *slave_dev) +{ + pr_debug("bond_dev=%p slave_dev=%p slave_dev->addr_len=%d\n", + bond_dev, slave_dev, slave_dev->addr_len); + memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len); + bond_dev->addr_assign_type = NET_ADDR_STOLEN; + call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev); +} + /* * bond_do_fail_over_mac * @@ -898,11 +872,9 @@ static void bond_do_fail_over_mac(struct bonding *bond, switch (bond->params.fail_over_mac) { case BOND_FOM_ACTIVE: if (new_active) { - memcpy(bond->dev->dev_addr, new_active->dev->dev_addr, - new_active->dev->addr_len); write_unlock_bh(&bond->curr_slave_lock); read_unlock(&bond->lock); - call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); + bond_set_dev_addr(bond->dev, new_active->dev); read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); } @@ -1090,7 +1062,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } if (USES_PRIMARY(bond->params.mode)) - bond_mc_swap(bond, new_active, old_active); + bond_hw_addr_swap(bond, new_active, old_active); if (bond_is_lb(bond)) { bond_alb_handle_active_change(bond, new_active); @@ -1333,17 +1305,6 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev) /*---------------------------------- IOCTL ----------------------------------*/ -static void bond_set_dev_addr(struct net_device *bond_dev, - struct net_device *slave_dev) -{ - pr_debug("bond_dev=%p\n", bond_dev); - pr_debug("slave_dev=%p\n", slave_dev); - pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len); - memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len); - bond_dev->addr_assign_type = NET_ADDR_SET; - call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev); -} - static netdev_features_t bond_fix_features(struct net_device *dev, netdev_features_t features) { @@ -1425,8 +1386,6 @@ done: static void bond_setup_by_slave(struct net_device *bond_dev, struct net_device *slave_dev) { - struct bonding *bond = netdev_priv(bond_dev); - bond_dev->header_ops = slave_dev->header_ops; bond_dev->type = slave_dev->type; @@ -1435,7 +1394,6 @@ static void bond_setup_by_slave(struct net_device *bond_dev, memcpy(bond_dev->broadcast, slave_dev->broadcast, slave_dev->addr_len); - bond->setup_by_slave = 1; } /* On bonding slaves other than the currently active slave, suppress @@ -1533,10 +1491,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) struct bonding *bond = netdev_priv(bond_dev); const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct slave *new_slave = NULL; - struct netdev_hw_addr *ha; struct sockaddr addr; int link_reporting; - int res = 0; + int res = 0, i; if (!bond->params.use_carrier && slave_dev->ethtool_ops->get_link == NULL && @@ -1643,7 +1600,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* If this is the first slave, then we need to set the master's hardware * address to be the same as the slave's. */ - if (bond->slave_cnt == 0 && bond->dev_addr_from_first) + if (!bond->slave_cnt && bond->dev->addr_assign_type == NET_ADDR_RANDOM) bond_set_dev_addr(bond->dev, slave_dev); new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL); @@ -1713,10 +1670,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_close; } - /* If the mode USES_PRIMARY, then the new slave gets the - * master's promisc (and mc) settings only if it becomes the - * curr_active_slave, and that is taken care of later when calling - * bond_change_active() + /* If the mode USES_PRIMARY, then the following is handled by + * bond_change_active_slave(). */ if (!USES_PRIMARY(bond->params.mode)) { /* set promiscuity level to new slave */ @@ -1734,9 +1689,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } netif_addr_lock_bh(bond_dev); - /* upload master's mc_list to new slave */ - netdev_for_each_mc_addr(ha, bond_dev) - dev_mc_add(slave_dev, ha->addr); + + dev_mc_sync_multiple(slave_dev, bond_dev); + dev_uc_sync_multiple(slave_dev, bond_dev); + netif_addr_unlock_bh(bond_dev); } @@ -1766,6 +1722,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->last_arp_rx = jiffies - (msecs_to_jiffies(bond->params.arp_interval) + 1); + for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) + new_slave->target_last_arp_rx[i] = new_slave->last_arp_rx; if (bond->params.miimon && !bond->params.use_carrier) { link_reporting = bond_check_dev_link(bond, slave_dev, 1); @@ -1915,11 +1873,9 @@ err_dest_symlinks: bond_destroy_slave_symlinks(bond_dev, slave_dev); err_detach: - if (!USES_PRIMARY(bond->params.mode)) { - netif_addr_lock_bh(bond_dev); - bond_mc_list_flush(bond_dev, slave_dev); - netif_addr_unlock_bh(bond_dev); - } + if (!USES_PRIMARY(bond->params.mode)) + bond_hw_addr_flush(bond_dev, slave_dev); + bond_del_vlans_from_slave(bond, slave_dev); write_lock_bh(&bond->lock); bond_detach_slave(bond, new_slave); @@ -2089,7 +2045,6 @@ static int __bond_release_one(struct net_device *bond_dev, if (bond->slave_cnt == 0) { bond_set_carrier(bond); eth_hw_addr_random(bond_dev); - bond->dev_addr_from_first = true; if (bond_vlan_used(bond)) { pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n", @@ -2118,9 +2073,8 @@ static int __bond_release_one(struct net_device *bond_dev, bond_del_vlans_from_slave(bond, slave_dev); - /* If the mode USES_PRIMARY, then we should only remove its - * promisc and mc settings if it was the curr_active_slave, but that was - * already taken care of above when we detached the slave + /* If the mode USES_PRIMARY, then this cases was handled above by + * bond_change_active_slave(..., NULL) */ if (!USES_PRIMARY(bond->params.mode)) { /* unset promiscuity level from slave */ @@ -2131,10 +2085,7 @@ static int __bond_release_one(struct net_device *bond_dev, if (bond_dev->flags & IFF_ALLMULTI) dev_set_allmulti(slave_dev, -1); - /* flush master's mc_list from slave */ - netif_addr_lock_bh(bond_dev); - bond_mc_list_flush(bond_dev, slave_dev); - netif_addr_unlock_bh(bond_dev); + bond_hw_addr_flush(bond_dev, slave_dev); } bond_upper_dev_unlink(bond_dev, slave_dev); @@ -2672,18 +2623,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip) { int i; - __be32 *targets = bond->params.arp_targets; - for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) { - pr_debug("bva: sip %pI4 tip %pI4 t[%d] %pI4 bhti(tip) %d\n", - &sip, &tip, i, &targets[i], - bond_has_this_ip(bond, tip)); - if (sip == targets[i]) { - if (bond_has_this_ip(bond, tip)) - slave->last_arp_rx = jiffies; - return; - } + if (!sip || !bond_has_this_ip(bond, tip)) { + pr_debug("bva: sip %pI4 tip %pI4 not found\n", &sip, &tip); + return; + } + + i = bond_get_targets_ip(bond->params.arp_targets, sip); + if (i == -1) { + pr_debug("bva: sip %pI4 not found in targets\n", &sip); + return; } + slave->last_arp_rx = jiffies; + slave->target_last_arp_rx[i] = jiffies; } static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, @@ -2698,6 +2650,10 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, return RX_HANDLER_ANOTHER; read_lock(&bond->lock); + + if (!slave_do_arp_validate(bond, slave)) + goto out_unlock; + alen = arp_hdr_len(bond->dev); pr_debug("bond_arp_rcv: bond %s skb->dev %s\n", @@ -2737,10 +2693,17 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, * configuration, the ARP probe will (hopefully) travel from * the active, through one switch, the router, then the other * switch before reaching the backup. + * + * We 'trust' the arp requests if there is an active slave and + * it received valid arp reply(s) after it became active. This + * is done to avoid endless looping when we can't reach the + * arp_ip_target and fool ourselves with our own arp requests. */ if (bond_is_active_slave(slave)) bond_validate_arp(bond, slave, sip, tip); - else + else if (bond->curr_active_slave && + time_after(slave_last_rx(bond, bond->curr_active_slave), + bond->curr_active_slave->jiffies)) bond_validate_arp(bond, slave, tip, sip); out_unlock: @@ -3225,7 +3188,7 @@ static int bond_slave_netdev_event(unsigned long event, switch (event) { case NETDEV_UNREGISTER: - if (bond->setup_by_slave) + if (bond_dev->type != ARPHRD_ETHER) bond_release_and_destroy(bond_dev, slave_dev); else bond_release(bond_dev, slave_dev); @@ -3289,7 +3252,7 @@ static int bond_slave_netdev_event(unsigned long event, static int bond_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *event_dev = (struct net_device *)ptr; + struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); pr_debug("event_dev: %s, event: %lx\n", event_dev ? event_dev->name : "None", @@ -3672,19 +3635,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd return res; } -static bool bond_addr_in_mc_list(unsigned char *addr, - struct netdev_hw_addr_list *list, - int addrlen) -{ - struct netdev_hw_addr *ha; - - netdev_hw_addr_list_for_each(ha, list) - if (!memcmp(ha->addr, addr, addrlen)) - return true; - - return false; -} - static void bond_change_rx_flags(struct net_device *bond_dev, int change) { struct bonding *bond = netdev_priv(bond_dev); @@ -3698,35 +3648,29 @@ static void bond_change_rx_flags(struct net_device *bond_dev, int change) bond_dev->flags & IFF_ALLMULTI ? 1 : -1); } -static void bond_set_multicast_list(struct net_device *bond_dev) +static void bond_set_rx_mode(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct netdev_hw_addr *ha; - bool found; + struct slave *slave; + int i; read_lock(&bond->lock); - /* looking for addresses to add to slaves' mc list */ - netdev_for_each_mc_addr(ha, bond_dev) { - found = bond_addr_in_mc_list(ha->addr, &bond->mc_list, - bond_dev->addr_len); - if (!found) - bond_mc_add(bond, ha->addr); - } - - /* looking for addresses to delete from slaves' list */ - netdev_hw_addr_list_for_each(ha, &bond->mc_list) { - found = bond_addr_in_mc_list(ha->addr, &bond_dev->mc, - bond_dev->addr_len); - if (!found) - bond_mc_del(bond, ha->addr); + if (USES_PRIMARY(bond->params.mode)) { + read_lock(&bond->curr_slave_lock); + slave = bond->curr_active_slave; + if (slave) { + dev_uc_sync(slave->dev, bond_dev); + dev_mc_sync(slave->dev, bond_dev); + } + read_unlock(&bond->curr_slave_lock); + } else { + bond_for_each_slave(bond, slave, i) { + dev_uc_sync_multiple(slave->dev, bond_dev); + dev_mc_sync_multiple(slave->dev, bond_dev); + } } - /* save master's multicast list */ - __hw_addr_flush(&bond->mc_list); - __hw_addr_add_multiple(&bond->mc_list, &bond_dev->mc, - bond_dev->addr_len, NETDEV_HW_ADDR_T_MULTICAST); - read_unlock(&bond->lock); } @@ -3871,11 +3815,10 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) pr_debug("bond=%p, name=%s\n", bond, bond_dev ? bond_dev->name : "None"); - /* - * If fail_over_mac is set to active, do nothing and return - * success. Returning an error causes ifenslave to fail. + /* If fail_over_mac is enabled, do nothing and return success. + * Returning an error causes ifenslave to fail. */ - if (bond->params.fail_over_mac == BOND_FOM_ACTIVE) + if (bond->params.fail_over_mac) return 0; if (!is_valid_ether_addr(sa->sa_data)) @@ -4333,7 +4276,7 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_get_stats64 = bond_get_stats, .ndo_do_ioctl = bond_do_ioctl, .ndo_change_rx_flags = bond_change_rx_flags, - .ndo_set_rx_mode = bond_set_multicast_list, + .ndo_set_rx_mode = bond_set_rx_mode, .ndo_change_mtu = bond_change_mtu, .ndo_set_mac_address = bond_set_mac_address, .ndo_neigh_setup = bond_neigh_setup, @@ -4438,8 +4381,6 @@ static void bond_uninit(struct net_device *bond_dev) bond_debug_unregister(bond); - __hw_addr_flush(&bond->mc_list); - list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) { list_del(&vlan->vlan_list); kfree(vlan); @@ -4484,6 +4425,7 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl) static int bond_check_params(struct bond_params *params) { int arp_validate_value, fail_over_mac_value, primary_reselect_value, i; + int arp_all_targets_value; /* * Convert string parameters. @@ -4674,7 +4616,11 @@ static int bond_check_params(struct bond_params *params) arp_ip_target[i]); arp_interval = 0; } else { - arp_target[arp_ip_count++] = ip; + if (bond_get_targets_ip(arp_target, ip) == -1) + arp_target[arp_ip_count++] = ip; + else + pr_warning("Warning: duplicate address %pI4 in arp_ip_target, skipping\n", + &ip); } } @@ -4705,6 +4651,18 @@ static int bond_check_params(struct bond_params *params) } else arp_validate_value = 0; + arp_all_targets_value = 0; + if (arp_all_targets) { + arp_all_targets_value = bond_parse_parm(arp_all_targets, + arp_all_targets_tbl); + + if (arp_all_targets_value == -1) { + pr_err("Error: invalid arp_all_targets_value \"%s\"\n", + arp_all_targets); + arp_all_targets_value = 0; + } + } + if (miimon) { pr_info("MII link monitoring set to %d ms\n", miimon); } else if (arp_interval) { @@ -4769,6 +4727,7 @@ static int bond_check_params(struct bond_params *params) params->num_peer_notif = num_peer_notif; params->arp_interval = arp_interval; params->arp_validate = arp_validate_value; + params->arp_all_targets = arp_all_targets_value; params->updelay = updelay; params->downdelay = downdelay; params->use_carrier = use_carrier; @@ -4845,12 +4804,9 @@ static int bond_init(struct net_device *bond_dev) /* Ensure valid dev_addr */ if (is_zero_ether_addr(bond_dev->dev_addr) && - bond_dev->addr_assign_type == NET_ADDR_PERM) { + bond_dev->addr_assign_type == NET_ADDR_PERM) eth_hw_addr_random(bond_dev); - bond->dev_addr_from_first = true; - } - __hw_addr_init(&bond->mc_list); return 0; } @@ -4923,7 +4879,7 @@ static int __net_init bond_net_init(struct net *net) bond_create_proc_dir(bn); bond_create_sysfs(bn); - + return 0; } diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index d7434e0a610..dc36a3d7d9e 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -231,8 +231,7 @@ static ssize_t bonding_show_slaves(struct device *d, } /* - * Set the slaves in the current bond. The bond interface must be - * up for this to succeed. + * Set the slaves in the current bond. * This is supposed to be only thin wrapper for bond_enslave and bond_release. * All hard work should be done there. */ @@ -363,7 +362,6 @@ static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, /* * Show and set the bonding transmit hash method. - * The bond interface must be down to change the xmit hash policy. */ static ssize_t bonding_show_xmit_hash(struct device *d, struct device_attribute *attr, @@ -383,20 +381,12 @@ static ssize_t bonding_store_xmit_hash(struct device *d, int new_value, ret = count; struct bonding *bond = to_bond(d); - if (bond->dev->flags & IFF_UP) { - pr_err("%s: Interface is up. Unable to update xmit policy.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } - new_value = bond_parse_parm(buf, xmit_hashtype_tbl); if (new_value < 0) { pr_err("%s: Ignoring invalid xmit hash policy value %.*s.\n", bond->dev->name, (int)strlen(buf) - 1, buf); ret = -EINVAL; - goto out; } else { bond->params.xmit_policy = new_value; bond_set_mode_ops(bond, bond->params.mode); @@ -404,7 +394,7 @@ static ssize_t bonding_store_xmit_hash(struct device *d, bond->dev->name, xmit_hashtype_tbl[new_value].modename, new_value); } -out: + return ret; } static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR, @@ -453,6 +443,44 @@ static ssize_t bonding_store_arp_validate(struct device *d, static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate, bonding_store_arp_validate); +/* + * Show and set arp_all_targets. + */ +static ssize_t bonding_show_arp_all_targets(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct bonding *bond = to_bond(d); + int value = bond->params.arp_all_targets; + + return sprintf(buf, "%s %d\n", arp_all_targets_tbl[value].modename, + value); +} + +static ssize_t bonding_store_arp_all_targets(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bonding *bond = to_bond(d); + int new_value; + + new_value = bond_parse_parm(buf, arp_all_targets_tbl); + if (new_value < 0) { + pr_err("%s: Ignoring invalid arp_all_targets value %s\n", + bond->dev->name, buf); + return -EINVAL; + } + pr_info("%s: setting arp_all_targets to %s (%d).\n", + bond->dev->name, arp_all_targets_tbl[new_value].modename, + new_value); + + bond->params.arp_all_targets = new_value; + + return count; +} + +static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR, + bonding_show_arp_all_targets, bonding_store_arp_all_targets); /* * Show and store fail_over_mac. User only allowed to change the @@ -600,10 +628,11 @@ static ssize_t bonding_store_arp_targets(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - __be32 newtarget; - int i = 0, done = 0, ret = count; struct bonding *bond = to_bond(d); - __be32 *targets; + struct slave *slave; + __be32 newtarget, *targets; + unsigned long *targets_rx; + int ind, i, j, ret = -EINVAL; targets = bond->params.arp_targets; newtarget = in_aton(buf + 1); @@ -612,57 +641,63 @@ static ssize_t bonding_store_arp_targets(struct device *d, if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) { pr_err("%s: invalid ARP target %pI4 specified for addition\n", bond->dev->name, &newtarget); - ret = -EINVAL; goto out; } - /* look for an empty slot to put the target in, and check for dupes */ - for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) { - if (targets[i] == newtarget) { /* duplicate */ - pr_err("%s: ARP target %pI4 is already present\n", - bond->dev->name, &newtarget); - ret = -EINVAL; - goto out; - } - if (targets[i] == 0) { - pr_info("%s: adding ARP target %pI4.\n", - bond->dev->name, &newtarget); - done = 1; - targets[i] = newtarget; - } + + if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */ + pr_err("%s: ARP target %pI4 is already present\n", + bond->dev->name, &newtarget); + goto out; } - if (!done) { + + ind = bond_get_targets_ip(targets, 0); /* first free slot */ + if (ind == -1) { pr_err("%s: ARP target table is full!\n", bond->dev->name); - ret = -EINVAL; goto out; } + pr_info("%s: adding ARP target %pI4.\n", bond->dev->name, + &newtarget); + /* not to race with bond_arp_rcv */ + write_lock_bh(&bond->lock); + bond_for_each_slave(bond, slave, i) + slave->target_last_arp_rx[ind] = jiffies; + targets[ind] = newtarget; + write_unlock_bh(&bond->lock); } else if (buf[0] == '-') { if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) { pr_err("%s: invalid ARP target %pI4 specified for removal\n", bond->dev->name, &newtarget); - ret = -EINVAL; goto out; } - for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) { - if (targets[i] == newtarget) { - int j; - pr_info("%s: removing ARP target %pI4.\n", - bond->dev->name, &newtarget); - for (j = i; (j < (BOND_MAX_ARP_TARGETS-1)) && targets[j+1]; j++) - targets[j] = targets[j+1]; - - targets[j] = 0; - done = 1; - } - } - if (!done) { - pr_info("%s: unable to remove nonexistent ARP target %pI4.\n", + ind = bond_get_targets_ip(targets, newtarget); + if (ind == -1) { + pr_err("%s: unable to remove nonexistent ARP target %pI4.\n", bond->dev->name, &newtarget); - ret = -EINVAL; goto out; } + + if (ind == 0 && !targets[1] && bond->params.arp_interval) + pr_warn("%s: removing last arp target with arp_interval on\n", + bond->dev->name); + + pr_info("%s: removing ARP target %pI4.\n", bond->dev->name, + &newtarget); + + write_lock_bh(&bond->lock); + bond_for_each_slave(bond, slave, i) { + targets_rx = slave->target_last_arp_rx; + j = ind; + for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++) + targets_rx[j] = targets_rx[j+1]; + targets_rx[j] = 0; + } + for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++) + targets[i] = targets[i+1]; + targets[i] = 0; + write_unlock_bh(&bond->lock); } else { pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n", bond->dev->name); @@ -670,6 +705,7 @@ static ssize_t bonding_store_arp_targets(struct device *d, goto out; } + ret = count; out: return ret; } @@ -1645,6 +1681,7 @@ static struct attribute *per_bond_attrs[] = { &dev_attr_mode.attr, &dev_attr_fail_over_mac.attr, &dev_attr_arp_validate.attr, + &dev_attr_arp_all_targets.attr, &dev_attr_arp_interval.attr, &dev_attr_arp_ip_target.attr, &dev_attr_downdelay.attr, diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index f989e1529a2..42d1c6599cb 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -144,6 +144,7 @@ struct bond_params { u8 num_peer_notif; int arp_interval; int arp_validate; + int arp_all_targets; int use_carrier; int fail_over_mac; int updelay; @@ -179,6 +180,7 @@ struct slave { int delay; unsigned long jiffies; unsigned long last_arp_rx; + unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS]; s8 link; /* one of BOND_LINK_XXXX */ s8 new_link; u8 backup:1, /* indicates backup slave. Value corresponds with @@ -224,14 +226,12 @@ struct bonding { rwlock_t lock; rwlock_t curr_slave_lock; u8 send_peer_notif; - s8 setup_by_slave; u8 igmp_retrans; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; char proc_file_name[IFNAMSIZ]; #endif /* CONFIG_PROC_FS */ struct list_head bond_list; - struct netdev_hw_addr_list mc_list; int (*xmit_hash_policy)(struct sk_buff *, int); u16 rr_tx_counter; struct ad_bond_info ad_info; @@ -248,7 +248,6 @@ struct bonding { /* debugging support via debugfs */ struct dentry *debug_dir; #endif /* CONFIG_DEBUG_FS */ - bool dev_addr_from_first; }; static inline bool bond_vlan_used(struct bonding *bond) @@ -323,6 +322,9 @@ static inline bool bond_is_active_slave(struct slave *slave) #define BOND_FOM_ACTIVE 1 #define BOND_FOM_FOLLOW 2 +#define BOND_ARP_TARGETS_ANY 0 +#define BOND_ARP_TARGETS_ALL 1 + #define BOND_ARP_VALIDATE_NONE 0 #define BOND_ARP_VALIDATE_ACTIVE (1 << BOND_STATE_ACTIVE) #define BOND_ARP_VALIDATE_BACKUP (1 << BOND_STATE_BACKUP) @@ -335,11 +337,31 @@ static inline int slave_do_arp_validate(struct bonding *bond, return bond->params.arp_validate & (1 << bond_slave_state(slave)); } +/* Get the oldest arp which we've received on this slave for bond's + * arp_targets. + */ +static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond, + struct slave *slave) +{ + int i = 1; + unsigned long ret = slave->target_last_arp_rx[0]; + + for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++) + if (time_before(slave->target_last_arp_rx[i], ret)) + ret = slave->target_last_arp_rx[i]; + + return ret; +} + static inline unsigned long slave_last_rx(struct bonding *bond, struct slave *slave) { - if (slave_do_arp_validate(bond, slave)) - return slave->last_arp_rx; + if (slave_do_arp_validate(bond, slave)) { + if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL) + return slave_oldest_target_arp_rx(bond, slave); + else + return slave->last_arp_rx; + } return slave->dev->last_rx; } @@ -465,12 +487,29 @@ static inline struct slave *bond_slave_has_mac(struct bonding *bond, return NULL; } +/* Check if the ip is present in arp ip list, or first free slot if ip == 0 + * Returns -1 if not found, index if found + */ +static inline int bond_get_targets_ip(__be32 *targets, __be32 ip) +{ + int i; + + for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) + if (targets[i] == ip) + return i; + else if (targets[i] == 0) + break; + + return -1; +} + /* exported from bond_main.c */ extern int bond_net_id; extern const struct bond_parm_tbl bond_lacp_tbl[]; extern const struct bond_parm_tbl bond_mode_tbl[]; extern const struct bond_parm_tbl xmit_hashtype_tbl[]; extern const struct bond_parm_tbl arp_validate_tbl[]; +extern const struct bond_parm_tbl arp_all_targets_tbl[]; extern const struct bond_parm_tbl fail_over_mac_tbl[]; extern const struct bond_parm_tbl pri_reselect_tbl[]; extern struct bond_parm_tbl ad_select_tbl[]; diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 77be3cb0b5f..34dea95d58d 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -35,8 +35,9 @@ MODULE_ALIAS_LDISC(N_CAIF); #define OFF 0 #define CAIF_MAX_MTU 4096 -/*This list is protected by the rtnl lock. */ +static DEFINE_SPINLOCK(ser_lock); static LIST_HEAD(ser_list); +static LIST_HEAD(ser_release_list); static bool ser_loop; module_param(ser_loop, bool, S_IRUGO); @@ -308,6 +309,28 @@ static void ldisc_tx_wakeup(struct tty_struct *tty) } +static void ser_release(struct work_struct *work) +{ + struct list_head list; + struct ser_device *ser, *tmp; + + spin_lock(&ser_lock); + list_replace_init(&ser_release_list, &list); + spin_unlock(&ser_lock); + + if (!list_empty(&list)) { + rtnl_lock(); + list_for_each_entry_safe(ser, tmp, &list, node) { + dev_close(ser->dev); + unregister_netdevice(ser->dev); + debugfs_deinit(ser); + } + rtnl_unlock(); + } +} + +static DECLARE_WORK(ser_release_work, ser_release); + static int ldisc_open(struct tty_struct *tty) { struct ser_device *ser; @@ -321,6 +344,9 @@ static int ldisc_open(struct tty_struct *tty) if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) return -EPERM; + /* release devices to avoid name collision */ + ser_release(NULL); + sprintf(name, "cf%s", tty->name); dev = alloc_netdev(sizeof(*ser), name, caifdev_setup); if (!dev) @@ -341,7 +367,9 @@ static int ldisc_open(struct tty_struct *tty) return -ENODEV; } + spin_lock(&ser_lock); list_add(&ser->node, &ser_list); + spin_unlock(&ser_lock); rtnl_unlock(); netif_stop_queue(dev); update_tty_status(ser); @@ -351,19 +379,13 @@ static int ldisc_open(struct tty_struct *tty) static void ldisc_close(struct tty_struct *tty) { struct ser_device *ser = tty->disc_data; - /* Remove may be called inside or outside of rtnl_lock */ - int islocked = rtnl_is_locked(); - if (!islocked) - rtnl_lock(); - /* device is freed automagically by net-sysfs */ - dev_close(ser->dev); - unregister_netdevice(ser->dev); - list_del(&ser->node); - debugfs_deinit(ser); tty_kref_put(ser->tty); - if (!islocked) - rtnl_unlock(); + + spin_lock(&ser_lock); + list_move(&ser->node, &ser_release_list); + spin_unlock(&ser_lock); + schedule_work(&ser_release_work); } /* The line discipline structure. */ @@ -438,16 +460,11 @@ static int __init caif_ser_init(void) static void __exit caif_ser_exit(void) { - struct ser_device *ser = NULL; - struct list_head *node; - struct list_head *_tmp; - - list_for_each_safe(node, _tmp, &ser_list) { - ser = list_entry(node, struct ser_device, node); - dev_close(ser->dev); - unregister_netdevice(ser->dev); - list_del(node); - } + spin_lock(&ser_lock); + list_splice(&ser_list, &ser_release_list); + spin_unlock(&ser_lock); + ser_release(NULL); + cancel_work_sync(&ser_release_work); tty_unregister_ldisc(N_CAIF); debugfs_remove_recursive(debugfsdir); } diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index e456b70933c..3c069472eb8 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -102,12 +102,9 @@ config CAN_JANZ_ICAN3 This driver can also be built as a module. If so, the module will be called janz-ican3.ko. -config HAVE_CAN_FLEXCAN - bool - config CAN_FLEXCAN tristate "Support for Freescale FLEXCAN based chips" - depends on HAVE_CAN_FLEXCAN + depends on ARM || PPC ---help--- Say Y here if you want to support for Freescale FlexCAN. diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index db52f4414de..dbbe97ae121 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1220,7 +1220,7 @@ static ssize_t at91_sysfs_set_mb0_id(struct device *dev, goto out; } - err = strict_strtoul(buf, 0, &can_id); + err = kstrtoul(buf, 0, &can_id); if (err) { ret = err; goto out; @@ -1264,8 +1264,6 @@ static const struct of_device_id at91_can_dt_ids[] = { } }; MODULE_DEVICE_TABLE(of, at91_can_dt_ids); -#else -#define at91_can_dt_ids NULL #endif static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev) @@ -1393,8 +1391,6 @@ static int at91_can_remove(struct platform_device *pdev) unregister_netdev(dev); - platform_set_drvdata(pdev, NULL); - iounmap(priv->reg_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1426,7 +1422,7 @@ static struct platform_driver at91_can_driver = { .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, - .of_match_table = at91_can_dt_ids, + .of_match_table = of_match_ptr(at91_can_dt_ids), }, .id_table = at91_can_id_table, }; diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index d4a15e82bfc..a2700d25ff0 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -580,7 +580,7 @@ static int bfin_can_probe(struct platform_device *pdev) priv->pin_list = pdata; priv->can.clock.freq = get_sclk(); - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); dev->flags |= IFF_ECHO; /* we support local echo */ @@ -613,7 +613,7 @@ exit: static int bfin_can_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct bfin_can_priv *priv = netdev_priv(dev); struct resource *res; @@ -621,8 +621,6 @@ static int bfin_can_remove(struct platform_device *pdev) unregister_candev(dev); - dev_set_drvdata(&pdev->dev, NULL); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); @@ -635,7 +633,7 @@ static int bfin_can_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct bfin_can_priv *priv = netdev_priv(dev); struct bfin_can_regs __iomem *reg = priv->membase; int timeout = BFIN_CAN_TIMEOUT; @@ -658,7 +656,7 @@ static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg) static int bfin_can_resume(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct bfin_can_priv *priv = netdev_priv(dev); struct bfin_can_regs __iomem *reg = priv->membase; diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index d63b91904f8..b918c732942 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -201,8 +201,8 @@ static int c_can_plat_probe(struct platform_device *pdev) priv->instance = pdev->id; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - priv->raminit_ctrlreg = devm_request_and_ioremap(&pdev->dev, res); - if (!priv->raminit_ctrlreg || priv->instance < 0) + priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0) dev_info(&pdev->dev, "control memory is not used for raminit\n"); else priv->raminit = c_can_hw_raminit; @@ -234,7 +234,6 @@ static int c_can_plat_probe(struct platform_device *pdev) return 0; exit_free_device: - platform_set_drvdata(pdev, NULL); free_c_can_dev(dev); exit_iounmap: iounmap(addr); @@ -255,7 +254,6 @@ static int c_can_plat_remove(struct platform_device *pdev) struct resource *mem; unregister_c_can_dev(dev); - platform_set_drvdata(pdev, NULL); free_c_can_dev(dev); iounmap(priv->base); diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c index 8eaaac81f32..87a47c0cfd4 100644 --- a/drivers/net/can/cc770/cc770_isa.c +++ b/drivers/net/can/cc770/cc770_isa.c @@ -265,7 +265,7 @@ static int cc770_isa_probe(struct platform_device *pdev) else priv->clkout = COR_DEFAULT; - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); err = register_cc770dev(dev); @@ -293,12 +293,11 @@ static int cc770_isa_probe(struct platform_device *pdev) static int cc770_isa_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct cc770_priv *priv = netdev_priv(dev); int idx = pdev->id; unregister_cc770dev(dev); - dev_set_drvdata(&pdev->dev, NULL); if (mem[idx]) { iounmap(priv->reg_base); diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c index d0f6bfc45ae..034bdd816a6 100644 --- a/drivers/net/can/cc770/cc770_platform.c +++ b/drivers/net/can/cc770/cc770_platform.c @@ -216,7 +216,7 @@ static int cc770_platform_probe(struct platform_device *pdev) priv->reg_base, dev->irq, priv->can.clock.freq, priv->cpu_interface, priv->bus_config, priv->clkout); - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); err = register_cc770dev(dev); @@ -240,7 +240,7 @@ exit_release_mem: static int cc770_platform_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct cc770_priv *priv = netdev_priv(dev); struct resource *mem; diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 769d29ed106..7b0be0910f4 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -24,7 +24,6 @@ #include <linux/can/dev.h> #include <linux/can/error.h> #include <linux/can/led.h> -#include <linux/can/platform/flexcan.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/if_arp.h> @@ -37,7 +36,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> #define DRV_NAME "flexcan" @@ -212,6 +211,7 @@ struct flexcan_priv { struct clk *clk_per; struct flexcan_platform_data *pdata; const struct flexcan_devtype_data *devtype_data; + struct regulator *reg_xceiver; }; static struct flexcan_devtype_data fsl_p1010_devtype_data = { @@ -259,15 +259,6 @@ static inline void flexcan_write(u32 val, void __iomem *addr) } #endif -/* - * Swtich transceiver on or off - */ -static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on) -{ - if (priv->pdata && priv->pdata->transceiver_switch) - priv->pdata->transceiver_switch(on); -} - static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, u32 reg_esr) { @@ -800,7 +791,11 @@ static int flexcan_chip_start(struct net_device *dev) if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) flexcan_write(0x0, ®s->rxfgmask); - flexcan_transceiver_switch(priv, 1); + if (priv->reg_xceiver) { + err = regulator_enable(priv->reg_xceiver); + if (err) + goto out; + } /* synchronize with the can bus */ reg_mcr = flexcan_read(®s->mcr); @@ -843,7 +838,8 @@ static void flexcan_chip_stop(struct net_device *dev) reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT; flexcan_write(reg, ®s->mcr); - flexcan_transceiver_switch(priv, 0); + if (priv->reg_xceiver) + regulator_disable(priv->reg_xceiver); priv->can.state = CAN_STATE_STOPPED; return; @@ -1004,16 +1000,11 @@ static int flexcan_probe(struct platform_device *pdev) struct flexcan_priv *priv; struct resource *mem; struct clk *clk_ipg = NULL, *clk_per = NULL; - struct pinctrl *pinctrl; void __iomem *base; resource_size_t mem_size; int err, irq; u32 clock_freq = 0; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - return PTR_ERR(pinctrl); - if (pdev->dev.of_node) of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clock_freq); @@ -1090,6 +1081,10 @@ static int flexcan_probe(struct platform_device *pdev) priv->pdata = pdev->dev.platform_data; priv->devtype_data = devtype_data; + priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (IS_ERR(priv->reg_xceiver)) + priv->reg_xceiver = NULL; + netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); dev_set_drvdata(&pdev->dev, dev); @@ -1127,7 +1122,6 @@ static int flexcan_remove(struct platform_device *pdev) struct resource *mem; unregister_flexcandev(dev); - platform_set_drvdata(pdev, NULL); iounmap(priv->base); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1138,10 +1132,10 @@ static int flexcan_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int flexcan_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int flexcan_suspend(struct device *device) { - struct net_device *dev = platform_get_drvdata(pdev); + struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); flexcan_chip_disable(priv); @@ -1155,9 +1149,9 @@ static int flexcan_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int flexcan_resume(struct platform_device *pdev) +static int flexcan_resume(struct device *device) { - struct net_device *dev = platform_get_drvdata(pdev); + struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); priv->can.state = CAN_STATE_ERROR_ACTIVE; @@ -1169,21 +1163,19 @@ static int flexcan_resume(struct platform_device *pdev) return 0; } -#else -#define flexcan_suspend NULL -#define flexcan_resume NULL -#endif +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume); static struct platform_driver flexcan_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .pm = &flexcan_pm_ops, .of_match_table = flexcan_of_match, }, .probe = flexcan_probe, .remove = flexcan_remove, - .suspend = flexcan_suspend, - .resume = flexcan_resume, .id_table = flexcan_id_table, }; diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index 17fbc7a0922..6aa737a2439 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -1646,7 +1646,7 @@ static int grcan_setup_netdev(struct platform_device *ofdev, if (err) goto exit_free_candev; - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); /* Reset device to allow bit-timing to be set. No need to call * grcan_reset at this stage. That is done in grcan_open. @@ -1683,10 +1683,9 @@ static int grcan_probe(struct platform_device *ofdev) } res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - base = devm_request_and_ioremap(&ofdev->dev, res); - if (!base) { - dev_err(&ofdev->dev, "couldn't map IO resource\n"); - err = -EADDRNOTAVAIL; + base = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(base)) { + err = PTR_ERR(base); goto exit_error; } @@ -1716,13 +1715,12 @@ exit_error: static int grcan_remove(struct platform_device *ofdev) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct net_device *dev = platform_get_drvdata(ofdev); struct grcan_priv *priv = netdev_priv(dev); unregister_candev(dev); /* Will in turn call grcan_close */ irq_dispose_mapping(dev->irq); - dev_set_drvdata(&ofdev->dev, NULL); netif_napi_del(&priv->napi); free_candev(dev); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index c4bc1d2e203..36bd6fa1c7f 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -1734,7 +1734,7 @@ static ssize_t ican3_sysfs_set_term(struct device *dev, unsigned long enable; int ret; - if (strict_strtoul(buf, 0, &enable)) + if (kstrtoul(buf, 0, &enable)) return -EINVAL; ret = ican3_set_termination(mod, enable); diff --git a/drivers/net/can/led.c b/drivers/net/can/led.c index f27fca65dc4..a3d99a8fd2d 100644 --- a/drivers/net/can/led.c +++ b/drivers/net/can/led.c @@ -88,9 +88,9 @@ EXPORT_SYMBOL_GPL(devm_can_led_init); /* NETDEV rename notifier to rename the associated led triggers too */ static int can_led_notifier(struct notifier_block *nb, unsigned long msg, - void *data) + void *ptr) { - struct net_device *netdev = data; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct can_priv *priv = safe_candev_priv(netdev); char name[CAN_LED_NAME_SZ]; diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index 668850e441d..5b0ee8ef588 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -302,7 +302,7 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev) goto exit_free_mscan; } - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n", priv->reg_base, dev->irq, priv->can.clock.freq); @@ -321,11 +321,9 @@ exit_unmap_mem: static int mpc5xxx_can_remove(struct platform_device *ofdev) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct net_device *dev = platform_get_drvdata(ofdev); struct mscan_priv *priv = netdev_priv(dev); - dev_set_drvdata(&ofdev->dev, NULL); - unregister_mscandev(dev); iounmap(priv->reg_base); irq_dispose_mapping(dev->irq); @@ -338,7 +336,7 @@ static int mpc5xxx_can_remove(struct platform_device *ofdev) static struct mscan_regs saved_regs; static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct net_device *dev = platform_get_drvdata(ofdev); struct mscan_priv *priv = netdev_priv(dev); struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; @@ -349,7 +347,7 @@ static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state static int mpc5xxx_can_resume(struct platform_device *ofdev) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct net_device *dev = platform_get_drvdata(ofdev); struct mscan_priv *priv = netdev_priv(dev); struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c index 5c8da466148..06a282397ff 100644 --- a/drivers/net/can/sja1000/sja1000_isa.c +++ b/drivers/net/can/sja1000/sja1000_isa.c @@ -197,7 +197,7 @@ static int sja1000_isa_probe(struct platform_device *pdev) else priv->cdr = CDR_DEFAULT; - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); err = register_sja1000dev(dev); @@ -225,12 +225,11 @@ static int sja1000_isa_probe(struct platform_device *pdev) static int sja1000_isa_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct sja1000_priv *priv = netdev_priv(dev); int idx = pdev->id; unregister_sja1000dev(dev); - dev_set_drvdata(&pdev->dev, NULL); if (mem[idx]) { iounmap(priv->reg_base); diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index 8e0c4a00193..31ad3391116 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -72,13 +72,11 @@ static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, static int sja1000_ofp_remove(struct platform_device *ofdev) { - struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct net_device *dev = platform_get_drvdata(ofdev); struct sja1000_priv *priv = netdev_priv(dev); struct device_node *np = ofdev->dev.of_node; struct resource res; - dev_set_drvdata(&ofdev->dev, NULL); - unregister_sja1000dev(dev); free_sja1000dev(dev); iounmap(priv->reg_base); @@ -181,7 +179,7 @@ static int sja1000_ofp_probe(struct platform_device *ofdev) priv->reg_base, dev->irq, priv->can.clock.freq, priv->ocr, priv->cdr); - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); SET_NETDEV_DEV(dev, &ofdev->dev); err = register_sja1000dev(dev); diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index 21619bb5b86..8e259c54103 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -135,7 +135,7 @@ static int sp_probe(struct platform_device *pdev) break; } - dev_set_drvdata(&pdev->dev, dev); + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); err = register_sja1000dev(dev); @@ -161,12 +161,11 @@ static int sp_probe(struct platform_device *pdev) static int sp_remove(struct platform_device *pdev) { - struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct net_device *dev = platform_get_drvdata(pdev); struct sja1000_priv *priv = netdev_priv(dev); struct resource *res; unregister_sja1000dev(dev); - dev_set_drvdata(&pdev->dev, NULL); if (priv->reg_base) iounmap(priv->reg_base); diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 06b7e097d36..874188ba06f 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -161,7 +161,7 @@ static void slc_bump(struct slcan *sl) sl->rbuff[dlc_pos] = 0; /* terminate can_id string */ - if (strict_strtoul(sl->rbuff+1, 16, &ultmp)) + if (kstrtoul(sl->rbuff+1, 16, &ultmp)) return; cf.can_id = ultmp; diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 3a2b45601ec..65eef1eea2e 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -594,7 +594,7 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr, unsigned long val; int ret; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; val &= 0xFF; diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index f21fc37ec57..3a349a22d5b 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -1001,7 +1001,6 @@ static int ti_hecc_remove(struct platform_device *pdev) iounmap(priv->base); release_mem_region(res->start, resource_size(res)); free_candev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index adb4bf5eb4b..ede8daa6827 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -723,25 +723,6 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev) pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS)); } -#if 0 -#ifndef final_version - { /* Error-checking code, delete someday. */ - ushort status = inw(ioaddr + EL3_STATUS); - if (status & 0x0001 && /* IRQ line active, missed one. */ - inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */ - pr_debug("%s: Missed interrupt, status then %04x now %04x" - " Tx %2.2x Rx %4.4x.\n", dev->name, status, - inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS), - inw(ioaddr + RX_STATUS)); - /* Fake interrupt trigger by masking, acknowledge interrupts. */ - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - } - } -#endif -#endif /* * We lock the driver against other processors. Note * we don't need to lock versus the IRQ as we suspended diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 072c6f14e8f..ad5272b348f 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1012,10 +1012,8 @@ static int vortex_init_one(struct pci_dev *pdev, goto out; rc = pci_request_regions(pdev, DRV_NAME); - if (rc < 0) { - pci_disable_device(pdev); - goto out; - } + if (rc < 0) + goto out_disable; unit = vortex_cards_found; @@ -1032,23 +1030,24 @@ static int vortex_init_one(struct pci_dev *pdev, if (!ioaddr) /* If mapping fails, fall-back to BAR 0... */ ioaddr = pci_iomap(pdev, 0, 0); if (!ioaddr) { - pci_release_regions(pdev); - pci_disable_device(pdev); rc = -ENOMEM; - goto out; + goto out_release; } rc = vortex_probe1(&pdev->dev, ioaddr, pdev->irq, ent->driver_data, unit); - if (rc < 0) { - pci_iounmap(pdev, ioaddr); - pci_release_regions(pdev); - pci_disable_device(pdev); - goto out; - } + if (rc < 0) + goto out_iounmap; vortex_cards_found++; + goto out; +out_iounmap: + pci_iounmap(pdev, ioaddr); +out_release: + pci_release_regions(pdev); +out_disable: + pci_disable_device(pdev); out: return rc; } @@ -1473,7 +1472,7 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq, if (pdev) { vp->pm_state_valid = 1; - pci_save_state(VORTEX_PCI(vp)); + pci_save_state(pdev); acpi_set_WOL(dev); } retval = register_netdev(dev); @@ -3233,21 +3232,20 @@ static void vortex_remove_one(struct pci_dev *pdev) vp = netdev_priv(dev); if (vp->cb_fn_base) - pci_iounmap(VORTEX_PCI(vp), vp->cb_fn_base); + pci_iounmap(pdev, vp->cb_fn_base); unregister_netdev(dev); - if (VORTEX_PCI(vp)) { - pci_set_power_state(VORTEX_PCI(vp), PCI_D0); /* Go active */ - if (vp->pm_state_valid) - pci_restore_state(VORTEX_PCI(vp)); - pci_disable_device(VORTEX_PCI(vp)); - } + pci_set_power_state(pdev, PCI_D0); /* Go active */ + if (vp->pm_state_valid) + pci_restore_state(pdev); + pci_disable_device(pdev); + /* Should really use issue_and_wait() here */ iowrite16(TotalReset | ((vp->drv_flags & EEPROM_RESET) ? 0x04 : 0x14), vp->ioaddr + EL3_CMD); - pci_iounmap(VORTEX_PCI(vp), vp->ioaddr); + pci_iounmap(pdev, vp->ioaddr); pci_free_consistent(pdev, sizeof(struct boom_rx_desc) * RX_RING_SIZE diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig index 1c71c763f68..f00c76377b4 100644 --- a/drivers/net/ethernet/3com/Kconfig +++ b/drivers/net/ethernet/3com/Kconfig @@ -67,7 +67,6 @@ config PCMCIA_3C589 config VORTEX tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support" depends on (PCI || EISA) && HAS_IOPORT - select NET_CORE select MII ---help--- This option enables driver support for a large number of 10Mbps and diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c index 47618e50535..b2e84051373 100644 --- a/drivers/net/ethernet/8390/ne.c +++ b/drivers/net/ethernet/8390/ne.c @@ -849,7 +849,6 @@ static int ne_drv_remove(struct platform_device *pdev) free_irq(dev->irq, dev); release_region(dev->base_addr, NE_IO_EXTENT); free_netdev(dev); - platform_set_drvdata(pdev, NULL); } return 0; } diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 587a885de25..92201080e07 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -676,7 +676,7 @@ static int ne2k_pci_resume (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata (pdev); int rc; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); rc = pci_enable_device(pdev); diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index ed956e08d38..2037080c504 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -20,9 +20,11 @@ config SUNGEM_PHY source "drivers/net/ethernet/3com/Kconfig" source "drivers/net/ethernet/adaptec/Kconfig" source "drivers/net/ethernet/aeroflex/Kconfig" +source "drivers/net/ethernet/allwinner/Kconfig" source "drivers/net/ethernet/alteon/Kconfig" source "drivers/net/ethernet/amd/Kconfig" source "drivers/net/ethernet/apple/Kconfig" +source "drivers/net/ethernet/arc/Kconfig" source "drivers/net/ethernet/atheros/Kconfig" source "drivers/net/ethernet/cadence/Kconfig" source "drivers/net/ethernet/adi/Kconfig" @@ -63,7 +65,6 @@ config JME tristate "JMicron(R) PCI-Express Gigabit Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver supports the PCI-Express gigabit ethernet adapters @@ -95,7 +96,6 @@ config FEALNX tristate "Myson MTD-8xx PCI Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- Say Y here to support the Myson MTD-800 family of PCI-based Ethernet @@ -106,7 +106,6 @@ source "drivers/net/ethernet/8390/Kconfig" config NET_NETX tristate "NetX Ethernet support" - select NET_CORE select MII depends on ARCH_NETX ---help--- @@ -124,7 +123,6 @@ source "drivers/net/ethernet/oki-semi/Kconfig" config ETHOC tristate "OpenCores 10/100 Mbps Ethernet MAC support" depends on HAS_IOMEM && HAS_DMA - select NET_CORE select MII select PHYLIB select CRC32 diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 8268d85f944..390bd0bfaa2 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -6,9 +6,11 @@ obj-$(CONFIG_NET_VENDOR_3COM) += 3com/ obj-$(CONFIG_NET_VENDOR_8390) += 8390/ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/ obj-$(CONFIG_GRETH) += aeroflex/ +obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/ obj-$(CONFIG_NET_VENDOR_AMD) += amd/ obj-$(CONFIG_NET_VENDOR_APPLE) += apple/ +obj-$(CONFIG_NET_VENDOR_ARC) += arc/ obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/ obj-$(CONFIG_NET_CADENCE) += cadence/ obj-$(CONFIG_NET_BFIN) += adi/ diff --git a/drivers/net/ethernet/adaptec/Kconfig b/drivers/net/ethernet/adaptec/Kconfig index 0bff571b1bb..5c804bbe3da 100644 --- a/drivers/net/ethernet/adaptec/Kconfig +++ b/drivers/net/ethernet/adaptec/Kconfig @@ -22,7 +22,6 @@ config ADAPTEC_STARFIRE tristate "Adaptec Starfire/DuraLAN support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index a9481606bbc..f952fff6a9a 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig @@ -23,7 +23,6 @@ config BFIN_MAC tristate "Blackfin on-chip MAC support" depends on (BF516 || BF518 || BF526 || BF527 || BF536 || BF537) select CRC32 - select NET_CORE select MII select PHYLIB select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index dada66bfe0d..e904b3838dc 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1719,7 +1719,6 @@ out_err_mii_probe: mdiobus_unregister(lp->mii_bus); mdiobus_free(lp->mii_bus); out_err_probe_mac: - platform_set_drvdata(pdev, NULL); free_netdev(ndev); return rc; @@ -1732,8 +1731,6 @@ static int bfin_mac_remove(struct platform_device *pdev) bfin_phc_release(lp); - platform_set_drvdata(pdev, NULL); - lp->mii_bus->priv = NULL; unregister_netdev(ndev); @@ -1868,7 +1865,6 @@ static int bfin_mii_bus_remove(struct platform_device *pdev) struct bfin_mii_bus_platform_data *mii_bus_pd = dev_get_platdata(&pdev->dev); - platform_set_drvdata(pdev, NULL); mdiobus_unregister(miibus); kfree(miibus->irq); mdiobus_free(miibus); diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 269295403fc..7ff4b30d55e 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1565,7 +1565,7 @@ error1: static int greth_of_remove(struct platform_device *of_dev) { - struct net_device *ndev = dev_get_drvdata(&of_dev->dev); + struct net_device *ndev = platform_get_drvdata(of_dev); struct greth_private *greth = netdev_priv(ndev); /* Free descriptor areas */ @@ -1573,8 +1573,6 @@ static int greth_of_remove(struct platform_device *of_dev) dma_free_coherent(&of_dev->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys); - dev_set_drvdata(&of_dev->dev, NULL); - if (greth->phy) phy_stop(greth->phy); mdiobus_unregister(greth->mdio); diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig new file mode 100644 index 00000000000..53ad213e865 --- /dev/null +++ b/drivers/net/ethernet/allwinner/Kconfig @@ -0,0 +1,35 @@ +# +# Allwinner device configuration +# + +config NET_VENDOR_ALLWINNER + bool "Allwinner devices" + default y + depends on ARCH_SUNXI + ---help--- + If you have a network (Ethernet) card belonging to this + class, say Y and read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + Note that the answer to this question doesn't directly + affect the kernel: saying N will just cause the configurator + to skip all the questions about Allwinner cards. If you say Y, + you will be asked for your specific card in the following + questions. + +if NET_VENDOR_ALLWINNER + +config SUN4I_EMAC + tristate "Allwinner A10 EMAC support" + depends on ARCH_SUNXI + depends on OF + select CRC32 + select MII + select PHYLIB + ---help--- + Support for Allwinner A10 EMAC ethernet driver. + + To compile this driver as a module, choose M here. The module + will be called sun4i-emac. + +endif # NET_VENDOR_ALLWINNER diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile new file mode 100644 index 00000000000..03129f79651 --- /dev/null +++ b/drivers/net/ethernet/allwinner/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Allwinner device drivers. +# + +obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c new file mode 100644 index 00000000000..50b853a79d7 --- /dev/null +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -0,0 +1,954 @@ +/* + * Allwinner EMAC Fast Ethernet driver for Linux. + * + * Copyright 2012-2013 Stefan Roese <sr@denx.de> + * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * Based on the Linux driver provided by Allwinner: + * Copyright (C) 1997 Sten Wang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/phy.h> + +#include "sun4i-emac.h" + +#define DRV_NAME "sun4i-emac" +#define DRV_VERSION "1.02" + +#define EMAC_MAX_FRAME_LEN 0x0600 + +/* Transmit timeout, default 5 seconds. */ +static int watchdog = 5000; +module_param(watchdog, int, 0400); +MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); + +/* EMAC register address locking. + * + * The EMAC uses an address register to control where data written + * to the data register goes. This means that the address register + * must be preserved over interrupts or similar calls. + * + * During interrupt and other critical calls, a spinlock is used to + * protect the system, but the calls themselves save the address + * in the address register in case they are interrupting another + * access to the device. + * + * For general accesses a lock is provided so that calls which are + * allowed to sleep are serialised so that the address register does + * not need to be saved. This lock also serves to serialise access + * to the EEPROM and PHY access registers which are shared between + * these two devices. + */ + +/* The driver supports the original EMACE, and now the two newer + * devices, EMACA and EMACB. + */ + +struct emac_board_info { + struct clk *clk; + struct device *dev; + struct platform_device *pdev; + spinlock_t lock; + void __iomem *membase; + u32 msg_enable; + struct net_device *ndev; + struct sk_buff *skb_last; + u16 tx_fifo_stat; + + int emacrx_completed_flag; + + struct phy_device *phy_dev; + struct device_node *phy_node; + unsigned int link; + unsigned int speed; + unsigned int duplex; + + phy_interface_t phy_interface; +}; + +static void emac_update_speed(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + unsigned int reg_val; + + /* set EMAC SPEED, depend on PHY */ + reg_val = readl(db->membase + EMAC_MAC_SUPP_REG); + reg_val &= ~(0x1 << 8); + if (db->speed == SPEED_100) + reg_val |= 1 << 8; + writel(reg_val, db->membase + EMAC_MAC_SUPP_REG); +} + +static void emac_update_duplex(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + unsigned int reg_val; + + /* set duplex depend on phy */ + reg_val = readl(db->membase + EMAC_MAC_CTL1_REG); + reg_val &= ~EMAC_MAC_CTL1_DUPLEX_EN; + if (db->duplex) + reg_val |= EMAC_MAC_CTL1_DUPLEX_EN; + writel(reg_val, db->membase + EMAC_MAC_CTL1_REG); +} + +static void emac_handle_link_change(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + struct phy_device *phydev = db->phy_dev; + unsigned long flags; + int status_change = 0; + + if (phydev->link) { + if (db->speed != phydev->speed) { + spin_lock_irqsave(&db->lock, flags); + db->speed = phydev->speed; + emac_update_speed(dev); + spin_unlock_irqrestore(&db->lock, flags); + status_change = 1; + } + + if (db->duplex != phydev->duplex) { + spin_lock_irqsave(&db->lock, flags); + db->duplex = phydev->duplex; + emac_update_duplex(dev); + spin_unlock_irqrestore(&db->lock, flags); + status_change = 1; + } + } + + if (phydev->link != db->link) { + if (!phydev->link) { + db->speed = 0; + db->duplex = -1; + } + db->link = phydev->link; + + status_change = 1; + } + + if (status_change) + phy_print_status(phydev); +} + +static int emac_mdio_probe(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + + /* to-do: PHY interrupts are currently not supported */ + + /* attach the mac to the phy */ + db->phy_dev = of_phy_connect(db->ndev, db->phy_node, + &emac_handle_link_change, 0, + db->phy_interface); + if (!db->phy_dev) { + netdev_err(db->ndev, "could not find the PHY\n"); + return -ENODEV; + } + + /* mask with MAC supported features */ + db->phy_dev->supported &= PHY_BASIC_FEATURES; + db->phy_dev->advertising = db->phy_dev->supported; + + db->link = 0; + db->speed = 0; + db->duplex = -1; + + return 0; +} + +static void emac_mdio_remove(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + + phy_disconnect(db->phy_dev); + db->phy_dev = NULL; +} + +static void emac_reset(struct emac_board_info *db) +{ + dev_dbg(db->dev, "resetting device\n"); + + /* RESET device */ + writel(0, db->membase + EMAC_CTL_REG); + udelay(200); + writel(EMAC_CTL_RESET, db->membase + EMAC_CTL_REG); + udelay(200); +} + +static void emac_outblk_32bit(void __iomem *reg, void *data, int count) +{ + writesl(reg, data, round_up(count, 4) / 4); +} + +static void emac_inblk_32bit(void __iomem *reg, void *data, int count) +{ + readsl(reg, data, round_up(count, 4) / 4); +} + +static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct emac_board_info *dm = netdev_priv(dev); + struct phy_device *phydev = dm->phy_dev; + + if (!netif_running(dev)) + return -EINVAL; + + if (!phydev) + return -ENODEV; + + return phy_mii_ioctl(phydev, rq, cmd); +} + +/* ethtool ops */ +static void emac_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(DRV_NAME)); + strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION)); + strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); +} + +static int emac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct emac_board_info *dm = netdev_priv(dev); + struct phy_device *phydev = dm->phy_dev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_gset(phydev, cmd); +} + +static int emac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct emac_board_info *dm = netdev_priv(dev); + struct phy_device *phydev = dm->phy_dev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_sset(phydev, cmd); +} + +static const struct ethtool_ops emac_ethtool_ops = { + .get_drvinfo = emac_get_drvinfo, + .get_settings = emac_get_settings, + .set_settings = emac_set_settings, + .get_link = ethtool_op_get_link, +}; + +static unsigned int emac_setup(struct net_device *ndev) +{ + struct emac_board_info *db = netdev_priv(ndev); + unsigned int reg_val; + + /* set up TX */ + reg_val = readl(db->membase + EMAC_TX_MODE_REG); + + writel(reg_val | EMAC_TX_MODE_ABORTED_FRAME_EN, + db->membase + EMAC_TX_MODE_REG); + + /* set up RX */ + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + + writel(reg_val | EMAC_RX_CTL_PASS_LEN_OOR_EN | + EMAC_RX_CTL_ACCEPT_UNICAST_EN | EMAC_RX_CTL_DA_FILTER_EN | + EMAC_RX_CTL_ACCEPT_MULTICAST_EN | + EMAC_RX_CTL_ACCEPT_BROADCAST_EN, + db->membase + EMAC_RX_CTL_REG); + + /* set MAC */ + /* set MAC CTL0 */ + reg_val = readl(db->membase + EMAC_MAC_CTL0_REG); + writel(reg_val | EMAC_MAC_CTL0_RX_FLOW_CTL_EN | + EMAC_MAC_CTL0_TX_FLOW_CTL_EN, + db->membase + EMAC_MAC_CTL0_REG); + + /* set MAC CTL1 */ + reg_val = readl(db->membase + EMAC_MAC_CTL1_REG); + reg_val |= EMAC_MAC_CTL1_LEN_CHECK_EN; + reg_val |= EMAC_MAC_CTL1_CRC_EN; + reg_val |= EMAC_MAC_CTL1_PAD_EN; + writel(reg_val, db->membase + EMAC_MAC_CTL1_REG); + + /* set up IPGT */ + writel(EMAC_MAC_IPGT_FULL_DUPLEX, db->membase + EMAC_MAC_IPGT_REG); + + /* set up IPGR */ + writel((EMAC_MAC_IPGR_IPG1 << 8) | EMAC_MAC_IPGR_IPG2, + db->membase + EMAC_MAC_IPGR_REG); + + /* set up Collison window */ + writel((EMAC_MAC_CLRT_COLLISION_WINDOW << 8) | EMAC_MAC_CLRT_RM, + db->membase + EMAC_MAC_CLRT_REG); + + /* set up Max Frame Length */ + writel(EMAC_MAX_FRAME_LEN, + db->membase + EMAC_MAC_MAXF_REG); + + return 0; +} + +static unsigned int emac_powerup(struct net_device *ndev) +{ + struct emac_board_info *db = netdev_priv(ndev); + unsigned int reg_val; + + /* initial EMAC */ + /* flush RX FIFO */ + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + reg_val |= 0x8; + writel(reg_val, db->membase + EMAC_RX_CTL_REG); + udelay(1); + + /* initial MAC */ + /* soft reset MAC */ + reg_val = readl(db->membase + EMAC_MAC_CTL0_REG); + reg_val &= ~EMAC_MAC_CTL0_SOFT_RESET; + writel(reg_val, db->membase + EMAC_MAC_CTL0_REG); + + /* set MII clock */ + reg_val = readl(db->membase + EMAC_MAC_MCFG_REG); + reg_val &= (~(0xf << 2)); + reg_val |= (0xD << 2); + writel(reg_val, db->membase + EMAC_MAC_MCFG_REG); + + /* clear RX counter */ + writel(0x0, db->membase + EMAC_RX_FBC_REG); + + /* disable all interrupt and clear interrupt status */ + writel(0, db->membase + EMAC_INT_CTL_REG); + reg_val = readl(db->membase + EMAC_INT_STA_REG); + writel(reg_val, db->membase + EMAC_INT_STA_REG); + + udelay(1); + + /* set up EMAC */ + emac_setup(ndev); + + /* set mac_address to chip */ + writel(ndev->dev_addr[0] << 16 | ndev->dev_addr[1] << 8 | ndev-> + dev_addr[2], db->membase + EMAC_MAC_A1_REG); + writel(ndev->dev_addr[3] << 16 | ndev->dev_addr[4] << 8 | ndev-> + dev_addr[5], db->membase + EMAC_MAC_A0_REG); + + mdelay(1); + + return 0; +} + +static int emac_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + struct emac_board_info *db = netdev_priv(dev); + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + + writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev-> + dev_addr[2], db->membase + EMAC_MAC_A1_REG); + writel(dev->dev_addr[3] << 16 | dev->dev_addr[4] << 8 | dev-> + dev_addr[5], db->membase + EMAC_MAC_A0_REG); + + return 0; +} + +/* Initialize emac board */ +static void emac_init_device(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + unsigned long flags; + unsigned int reg_val; + + spin_lock_irqsave(&db->lock, flags); + + emac_update_speed(dev); + emac_update_duplex(dev); + + /* enable RX/TX */ + reg_val = readl(db->membase + EMAC_CTL_REG); + writel(reg_val | EMAC_CTL_RESET | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN, + db->membase + EMAC_CTL_REG); + + /* enable RX/TX0/RX Hlevel interrup */ + reg_val = readl(db->membase + EMAC_INT_CTL_REG); + reg_val |= (0xf << 0) | (0x01 << 8); + writel(reg_val, db->membase + EMAC_INT_CTL_REG); + + spin_unlock_irqrestore(&db->lock, flags); +} + +/* Our watchdog timed out. Called by the networking layer */ +static void emac_timeout(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + unsigned long flags; + + if (netif_msg_timer(db)) + dev_err(db->dev, "tx time out.\n"); + + /* Save previous register address */ + spin_lock_irqsave(&db->lock, flags); + + netif_stop_queue(dev); + emac_reset(db); + emac_init_device(dev); + /* We can accept TX packets again */ + dev->trans_start = jiffies; + netif_wake_queue(dev); + + /* Restore previous register address */ + spin_unlock_irqrestore(&db->lock, flags); +} + +/* Hardware start transmission. + * Send a packet to media from the upper layer. + */ +static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + unsigned long channel; + unsigned long flags; + + channel = db->tx_fifo_stat & 3; + if (channel == 3) + return 1; + + channel = (channel == 1 ? 1 : 0); + + spin_lock_irqsave(&db->lock, flags); + + writel(channel, db->membase + EMAC_TX_INS_REG); + + emac_outblk_32bit(db->membase + EMAC_TX_IO_DATA_REG, + skb->data, skb->len); + dev->stats.tx_bytes += skb->len; + + db->tx_fifo_stat |= 1 << channel; + /* TX control: First packet immediately send, second packet queue */ + if (channel == 0) { + /* set TX len */ + writel(skb->len, db->membase + EMAC_TX_PL0_REG); + /* start translate from fifo to phy */ + writel(readl(db->membase + EMAC_TX_CTL0_REG) | 1, + db->membase + EMAC_TX_CTL0_REG); + + /* save the time stamp */ + dev->trans_start = jiffies; + } else if (channel == 1) { + /* set TX len */ + writel(skb->len, db->membase + EMAC_TX_PL1_REG); + /* start translate from fifo to phy */ + writel(readl(db->membase + EMAC_TX_CTL1_REG) | 1, + db->membase + EMAC_TX_CTL1_REG); + + /* save the time stamp */ + dev->trans_start = jiffies; + } + + if ((db->tx_fifo_stat & 3) == 3) { + /* Second packet */ + netif_stop_queue(dev); + } + + spin_unlock_irqrestore(&db->lock, flags); + + /* free this SKB */ + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/* EMAC interrupt handler + * receive the packet to upper layer, free the transmitted packet + */ +static void emac_tx_done(struct net_device *dev, struct emac_board_info *db, + unsigned int tx_status) +{ + /* One packet sent complete */ + db->tx_fifo_stat &= ~(tx_status & 3); + if (3 == (tx_status & 3)) + dev->stats.tx_packets += 2; + else + dev->stats.tx_packets++; + + if (netif_msg_tx_done(db)) + dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); + + netif_wake_queue(dev); +} + +/* Received a packet and pass to upper layer + */ +static void emac_rx(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + struct sk_buff *skb; + u8 *rdptr; + bool good_packet; + static int rxlen_last; + unsigned int reg_val; + u32 rxhdr, rxstatus, rxcount, rxlen; + + /* Check packet ready or not */ + while (1) { + /* race warning: the first packet might arrive with + * the interrupts disabled, but the second will fix + * it + */ + rxcount = readl(db->membase + EMAC_RX_FBC_REG); + + if (netif_msg_rx_status(db)) + dev_dbg(db->dev, "RXCount: %x\n", rxcount); + + if ((db->skb_last != NULL) && (rxlen_last > 0)) { + dev->stats.rx_bytes += rxlen_last; + + /* Pass to upper layer */ + db->skb_last->protocol = eth_type_trans(db->skb_last, + dev); + netif_rx(db->skb_last); + dev->stats.rx_packets++; + db->skb_last = NULL; + rxlen_last = 0; + + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + reg_val &= ~EMAC_RX_CTL_DMA_EN; + writel(reg_val, db->membase + EMAC_RX_CTL_REG); + } + + if (!rxcount) { + db->emacrx_completed_flag = 1; + reg_val = readl(db->membase + EMAC_INT_CTL_REG); + reg_val |= (0xf << 0) | (0x01 << 8); + writel(reg_val, db->membase + EMAC_INT_CTL_REG); + + /* had one stuck? */ + rxcount = readl(db->membase + EMAC_RX_FBC_REG); + if (!rxcount) + return; + } + + reg_val = readl(db->membase + EMAC_RX_IO_DATA_REG); + if (netif_msg_rx_status(db)) + dev_dbg(db->dev, "receive header: %x\n", reg_val); + if (reg_val != EMAC_UNDOCUMENTED_MAGIC) { + /* disable RX */ + reg_val = readl(db->membase + EMAC_CTL_REG); + writel(reg_val & ~EMAC_CTL_RX_EN, + db->membase + EMAC_CTL_REG); + + /* Flush RX FIFO */ + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + writel(reg_val | (1 << 3), + db->membase + EMAC_RX_CTL_REG); + + do { + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + } while (reg_val & (1 << 3)); + + /* enable RX */ + reg_val = readl(db->membase + EMAC_CTL_REG); + writel(reg_val | EMAC_CTL_RX_EN, + db->membase + EMAC_CTL_REG); + reg_val = readl(db->membase + EMAC_INT_CTL_REG); + reg_val |= (0xf << 0) | (0x01 << 8); + writel(reg_val, db->membase + EMAC_INT_CTL_REG); + + db->emacrx_completed_flag = 1; + + return; + } + + /* A packet ready now & Get status/length */ + good_packet = true; + + emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, + &rxhdr, sizeof(rxhdr)); + + if (netif_msg_rx_status(db)) + dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr))); + + rxlen = EMAC_RX_IO_DATA_LEN(rxhdr); + rxstatus = EMAC_RX_IO_DATA_STATUS(rxhdr); + + if (netif_msg_rx_status(db)) + dev_dbg(db->dev, "RX: status %02x, length %04x\n", + rxstatus, rxlen); + + /* Packet Status check */ + if (rxlen < 0x40) { + good_packet = false; + if (netif_msg_rx_err(db)) + dev_dbg(db->dev, "RX: Bad Packet (runt)\n"); + } + + if (unlikely(!(rxstatus & EMAC_RX_IO_DATA_STATUS_OK))) { + good_packet = false; + + if (rxstatus & EMAC_RX_IO_DATA_STATUS_CRC_ERR) { + if (netif_msg_rx_err(db)) + dev_dbg(db->dev, "crc error\n"); + dev->stats.rx_crc_errors++; + } + + if (rxstatus & EMAC_RX_IO_DATA_STATUS_LEN_ERR) { + if (netif_msg_rx_err(db)) + dev_dbg(db->dev, "length error\n"); + dev->stats.rx_length_errors++; + } + } + + /* Move data from EMAC */ + skb = dev_alloc_skb(rxlen + 4); + if (good_packet && skb) { + skb_reserve(skb, 2); + rdptr = (u8 *) skb_put(skb, rxlen - 4); + + /* Read received packet from RX SRAM */ + if (netif_msg_rx_status(db)) + dev_dbg(db->dev, "RxLen %x\n", rxlen); + + emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, + rdptr, rxlen); + dev->stats.rx_bytes += rxlen; + + /* Pass to upper layer */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + } + } +} + +static irqreturn_t emac_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct emac_board_info *db = netdev_priv(dev); + int int_status; + unsigned long flags; + unsigned int reg_val; + + /* A real interrupt coming */ + + /* holders of db->lock must always block IRQs */ + spin_lock_irqsave(&db->lock, flags); + + /* Disable all interrupts */ + writel(0, db->membase + EMAC_INT_CTL_REG); + + /* Got EMAC interrupt status */ + /* Got ISR */ + int_status = readl(db->membase + EMAC_INT_STA_REG); + /* Clear ISR status */ + writel(int_status, db->membase + EMAC_INT_STA_REG); + + if (netif_msg_intr(db)) + dev_dbg(db->dev, "emac interrupt %02x\n", int_status); + + /* Received the coming packet */ + if ((int_status & 0x100) && (db->emacrx_completed_flag == 1)) { + /* carrier lost */ + db->emacrx_completed_flag = 0; + emac_rx(dev); + } + + /* Transmit Interrupt check */ + if (int_status & (0x01 | 0x02)) + emac_tx_done(dev, db, int_status); + + if (int_status & (0x04 | 0x08)) + netdev_info(dev, " ab : %x\n", int_status); + + /* Re-enable interrupt mask */ + if (db->emacrx_completed_flag == 1) { + reg_val = readl(db->membase + EMAC_INT_CTL_REG); + reg_val |= (0xf << 0) | (0x01 << 8); + writel(reg_val, db->membase + EMAC_INT_CTL_REG); + } + spin_unlock_irqrestore(&db->lock, flags); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Used by netconsole + */ +static void emac_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + emac_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +/* Open the interface. + * The interface is opened whenever "ifconfig" actives it. + */ +static int emac_open(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + int ret; + + if (netif_msg_ifup(db)) + dev_dbg(db->dev, "enabling %s\n", dev->name); + + if (devm_request_irq(db->dev, dev->irq, &emac_interrupt, + 0, dev->name, dev)) + return -EAGAIN; + + /* Initialize EMAC board */ + emac_reset(db); + emac_init_device(dev); + + ret = emac_mdio_probe(dev); + if (ret < 0) { + netdev_err(dev, "cannot probe MDIO bus\n"); + return ret; + } + + phy_start(db->phy_dev); + netif_start_queue(dev); + + return 0; +} + +static void emac_shutdown(struct net_device *dev) +{ + unsigned int reg_val; + struct emac_board_info *db = netdev_priv(dev); + + /* Disable all interrupt */ + writel(0, db->membase + EMAC_INT_CTL_REG); + + /* clear interupt status */ + reg_val = readl(db->membase + EMAC_INT_STA_REG); + writel(reg_val, db->membase + EMAC_INT_STA_REG); + + /* Disable RX/TX */ + reg_val = readl(db->membase + EMAC_CTL_REG); + reg_val &= ~(EMAC_CTL_TX_EN | EMAC_CTL_RX_EN | EMAC_CTL_RESET); + writel(reg_val, db->membase + EMAC_CTL_REG); +} + +/* Stop the interface. + * The interface is stopped when it is brought. + */ +static int emac_stop(struct net_device *ndev) +{ + struct emac_board_info *db = netdev_priv(ndev); + + if (netif_msg_ifdown(db)) + dev_dbg(db->dev, "shutting down %s\n", ndev->name); + + netif_stop_queue(ndev); + netif_carrier_off(ndev); + + phy_stop(db->phy_dev); + + emac_mdio_remove(ndev); + + emac_shutdown(ndev); + + return 0; +} + +static const struct net_device_ops emac_netdev_ops = { + .ndo_open = emac_open, + .ndo_stop = emac_stop, + .ndo_start_xmit = emac_start_xmit, + .ndo_tx_timeout = emac_timeout, + .ndo_do_ioctl = emac_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = emac_set_mac_address, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = emac_poll_controller, +#endif +}; + +/* Search EMAC board, allocate space and register it + */ +static int emac_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct emac_board_info *db; + struct net_device *ndev; + int ret = 0; + const char *mac_addr; + + ndev = alloc_etherdev(sizeof(struct emac_board_info)); + if (!ndev) { + dev_err(&pdev->dev, "could not allocate device.\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(ndev, &pdev->dev); + + db = netdev_priv(ndev); + memset(db, 0, sizeof(*db)); + + db->dev = &pdev->dev; + db->ndev = ndev; + db->pdev = pdev; + + spin_lock_init(&db->lock); + + db->membase = of_iomap(np, 0); + if (!db->membase) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; + goto out; + } + + /* fill in parameters for net-dev structure */ + ndev->base_addr = (unsigned long)db->membase; + ndev->irq = irq_of_parse_and_map(np, 0); + if (ndev->irq == -ENXIO) { + netdev_err(ndev, "No irq resource\n"); + ret = ndev->irq; + goto out; + } + + db->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(db->clk)) + goto out; + + clk_prepare_enable(db->clk); + + db->phy_node = of_parse_phandle(np, "phy", 0); + if (!db->phy_node) { + dev_err(&pdev->dev, "no associated PHY\n"); + ret = -ENODEV; + goto out; + } + + /* Read MAC-address from DT */ + mac_addr = of_get_mac_address(np); + if (mac_addr) + memcpy(ndev->dev_addr, mac_addr, ETH_ALEN); + + /* Check if the MAC address is valid, if not get a random one */ + if (!is_valid_ether_addr(ndev->dev_addr)) { + eth_hw_addr_random(ndev); + dev_warn(&pdev->dev, "using random MAC address %pM\n", + ndev->dev_addr); + } + + db->emacrx_completed_flag = 1; + emac_powerup(ndev); + emac_reset(db); + + ether_setup(ndev); + + ndev->netdev_ops = &emac_netdev_ops; + ndev->watchdog_timeo = msecs_to_jiffies(watchdog); + ndev->ethtool_ops = &emac_ethtool_ops; + + platform_set_drvdata(pdev, ndev); + + /* Carrier starts down, phylib will bring it up */ + netif_carrier_off(ndev); + + ret = register_netdev(ndev); + if (ret) { + dev_err(&pdev->dev, "Registering netdev failed!\n"); + ret = -ENODEV; + goto out; + } + + dev_info(&pdev->dev, "%s: at %p, IRQ %d MAC: %pM\n", + ndev->name, db->membase, ndev->irq, ndev->dev_addr); + + return 0; + +out: + dev_err(db->dev, "not found (%d).\n", ret); + + free_netdev(ndev); + + return ret; +} + +static int emac_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_netdev(ndev); + free_netdev(ndev); + + dev_dbg(&pdev->dev, "released and freed device\n"); + return 0; +} + +static int emac_suspend(struct platform_device *dev, pm_message_t state) +{ + struct net_device *ndev = platform_get_drvdata(dev); + + netif_carrier_off(ndev); + netif_device_detach(ndev); + emac_shutdown(ndev); + + return 0; +} + +static int emac_resume(struct platform_device *dev) +{ + struct net_device *ndev = platform_get_drvdata(dev); + struct emac_board_info *db = netdev_priv(ndev); + + emac_reset(db); + emac_init_device(ndev); + netif_device_attach(ndev); + + return 0; +} + +static const struct of_device_id emac_of_match[] = { + {.compatible = "allwinner,sun4i-emac",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, emac_of_match); + +static struct platform_driver emac_driver = { + .driver = { + .name = "sun4i-emac", + .of_match_table = emac_of_match, + }, + .probe = emac_probe, + .remove = emac_remove, + .suspend = emac_suspend, + .resume = emac_resume, +}; + +module_platform_driver(emac_driver); + +MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Allwinner A10 emac network driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.h b/drivers/net/ethernet/allwinner/sun4i-emac.h new file mode 100644 index 00000000000..38c72d9ec60 --- /dev/null +++ b/drivers/net/ethernet/allwinner/sun4i-emac.h @@ -0,0 +1,108 @@ +/* + * Allwinner EMAC Fast Ethernet driver for Linux. + * + * Copyright 2012 Stefan Roese <sr@denx.de> + * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * Based on the Linux driver provided by Allwinner: + * Copyright (C) 1997 Sten Wang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _SUN4I_EMAC_H_ +#define _SUN4I_EMAC_H_ + +#define EMAC_CTL_REG (0x00) +#define EMAC_CTL_RESET (1 << 0) +#define EMAC_CTL_TX_EN (1 << 1) +#define EMAC_CTL_RX_EN (1 << 2) +#define EMAC_TX_MODE_REG (0x04) +#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0) +#define EMAC_TX_MODE_DMA_EN (1 << 1) +#define EMAC_TX_FLOW_REG (0x08) +#define EMAC_TX_CTL0_REG (0x0c) +#define EMAC_TX_CTL1_REG (0x10) +#define EMAC_TX_INS_REG (0x14) +#define EMAC_TX_PL0_REG (0x18) +#define EMAC_TX_PL1_REG (0x1c) +#define EMAC_TX_STA_REG (0x20) +#define EMAC_TX_IO_DATA_REG (0x24) +#define EMAC_TX_IO_DATA1_REG (0x28) +#define EMAC_TX_TSVL0_REG (0x2c) +#define EMAC_TX_TSVH0_REG (0x30) +#define EMAC_TX_TSVL1_REG (0x34) +#define EMAC_TX_TSVH1_REG (0x38) +#define EMAC_RX_CTL_REG (0x3c) +#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1) +#define EMAC_RX_CTL_DMA_EN (1 << 2) +#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4) +#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5) +#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6) +#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7) +#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8) +#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16) +#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17) +#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20) +#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21) +#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22) +#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24) +#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25) +#define EMAC_RX_HASH0_REG (0x40) +#define EMAC_RX_HASH1_REG (0x44) +#define EMAC_RX_STA_REG (0x48) +#define EMAC_RX_IO_DATA_REG (0x4c) +#define EMAC_RX_IO_DATA_LEN(x) (x & 0xffff) +#define EMAC_RX_IO_DATA_STATUS(x) ((x >> 16) & 0xffff) +#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4) +#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5) +#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7) +#define EMAC_RX_FBC_REG (0x50) +#define EMAC_INT_CTL_REG (0x54) +#define EMAC_INT_STA_REG (0x58) +#define EMAC_MAC_CTL0_REG (0x5c) +#define EMAC_MAC_CTL0_RX_FLOW_CTL_EN (1 << 2) +#define EMAC_MAC_CTL0_TX_FLOW_CTL_EN (1 << 3) +#define EMAC_MAC_CTL0_SOFT_RESET (1 << 15) +#define EMAC_MAC_CTL1_REG (0x60) +#define EMAC_MAC_CTL1_DUPLEX_EN (1 << 0) +#define EMAC_MAC_CTL1_LEN_CHECK_EN (1 << 1) +#define EMAC_MAC_CTL1_HUGE_FRAME_EN (1 << 2) +#define EMAC_MAC_CTL1_DELAYED_CRC_EN (1 << 3) +#define EMAC_MAC_CTL1_CRC_EN (1 << 4) +#define EMAC_MAC_CTL1_PAD_EN (1 << 5) +#define EMAC_MAC_CTL1_PAD_CRC_EN (1 << 6) +#define EMAC_MAC_CTL1_AD_SHORT_FRAME_EN (1 << 7) +#define EMAC_MAC_CTL1_BACKOFF_DIS (1 << 12) +#define EMAC_MAC_IPGT_REG (0x64) +#define EMAC_MAC_IPGT_HALF_DUPLEX (0x12) +#define EMAC_MAC_IPGT_FULL_DUPLEX (0x15) +#define EMAC_MAC_IPGR_REG (0x68) +#define EMAC_MAC_IPGR_IPG1 (0x0c) +#define EMAC_MAC_IPGR_IPG2 (0x12) +#define EMAC_MAC_CLRT_REG (0x6c) +#define EMAC_MAC_CLRT_COLLISION_WINDOW (0x37) +#define EMAC_MAC_CLRT_RM (0x0f) +#define EMAC_MAC_MAXF_REG (0x70) +#define EMAC_MAC_SUPP_REG (0x74) +#define EMAC_MAC_TEST_REG (0x78) +#define EMAC_MAC_MCFG_REG (0x7c) +#define EMAC_MAC_A0_REG (0x98) +#define EMAC_MAC_A1_REG (0x9c) +#define EMAC_MAC_A2_REG (0xa0) +#define EMAC_SAFX_L_REG0 (0xa4) +#define EMAC_SAFX_H_REG0 (0xa8) +#define EMAC_SAFX_L_REG1 (0xac) +#define EMAC_SAFX_H_REG1 (0xb0) +#define EMAC_SAFX_L_REG2 (0xb4) +#define EMAC_SAFX_H_REG2 (0xb8) +#define EMAC_SAFX_L_REG3 (0xbc) +#define EMAC_SAFX_H_REG3 (0xc0) + +#define EMAC_PHY_DUPLEX (1 << 8) + +#define EMAC_EEPROM_MAGIC (0x444d394b) +#define EMAC_UNDOCUMENTED_MAGIC (0x0143414d) +#endif /* _SUN4I_EMAC_H_ */ diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index b7894f8af9d..219be1bf3cf 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -702,19 +702,6 @@ static struct pci_driver acenic_pci_driver = { .remove = acenic_remove_one, }; -static int __init acenic_init(void) -{ - return pci_register_driver(&acenic_pci_driver); -} - -static void __exit acenic_exit(void) -{ - pci_unregister_driver(&acenic_pci_driver); -} - -module_init(acenic_init); -module_exit(acenic_exit); - static void ace_free_descriptors(struct net_device *dev) { struct ace_private *ap = netdev_priv(dev); @@ -3199,3 +3186,5 @@ static int read_eeprom_byte(struct net_device *dev, unsigned long offset) ap->name, offset); goto out; } + +module_pci_driver(acenic_pci_driver); diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 13d74aa4033..562df46e0a8 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -34,7 +34,6 @@ config AMD8111_ETH tristate "AMD 8111 (new PCI LANCE) support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- If you have an AMD 8111-based PCI LANCE ethernet card, @@ -60,7 +59,6 @@ config PCNET32 tristate "AMD PCnet32 PCI support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- If you have a PCnet32 or PCnetPCI based network (Ethernet) card, diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 8e6b665a672..1b1429d5d5c 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1813,7 +1813,7 @@ static const struct net_device_ops amd8111e_netdev_ops = { static int amd8111e_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - int err,i,pm_cap; + int err, i; unsigned long reg_addr,reg_len; struct amd8111e_priv* lp; struct net_device* dev; @@ -1842,7 +1842,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev, pci_set_master(pdev); /* Find power-management capability. */ - if((pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM))==0){ + if (!pdev->pm_cap) { printk(KERN_ERR "amd8111e: No Power Management capability, " "exiting.\n"); err = -ENODEV; @@ -1875,7 +1875,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev, lp = netdev_priv(dev); lp->pci_dev = pdev; lp->amd8111e_net_dev = dev; - lp->pm_cap = pm_cap; + lp->pm_cap = pdev->pm_cap; spin_lock_init(&lp->lock); @@ -1981,15 +1981,4 @@ static struct pci_driver amd8111e_driver = { .resume = amd8111e_resume }; -static int __init amd8111e_init(void) -{ - return pci_register_driver(&amd8111e_driver); -} - -static void __exit amd8111e_cleanup(void) -{ - pci_unregister_driver(&amd8111e_driver); -} - -module_init(amd8111e_init); -module_exit(amd8111e_cleanup); +module_pci_driver(amd8111e_driver); diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 688aede742c..ceb45bc963a 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1301,8 +1301,6 @@ static int au1000_remove(struct platform_device *pdev) int i; struct resource *base, *macen; - platform_set_drvdata(pdev, NULL); - unregister_netdev(dev); mdiobus_unregister(aup->mii_bus); mdiobus_free(aup->mii_bus); diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index f47b780892e..ece56831a64 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -1470,7 +1470,7 @@ no_link_test: goto fail; } - dev_set_drvdata(&op->dev, lp); + platform_set_drvdata(op, lp); printk(KERN_INFO "%s: LANCE %pM\n", dev->name, dev->dev_addr); @@ -1501,7 +1501,7 @@ static int sunlance_sbus_probe(struct platform_device *op) static int sunlance_sbus_remove(struct platform_device *op) { - struct lance_private *lp = dev_get_drvdata(&op->dev); + struct lance_private *lp = platform_get_drvdata(op); struct net_device *net_dev = lp->dev; unregister_netdev(net_dev); @@ -1510,8 +1510,6 @@ static int sunlance_sbus_remove(struct platform_device *op) free_netdev(net_dev); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/net/ethernet/apple/bmac.c b/drivers/net/ethernet/apple/bmac.c index f36bbd6d508..a597b766f08 100644 --- a/drivers/net/ethernet/apple/bmac.c +++ b/drivers/net/ethernet/apple/bmac.c @@ -1016,7 +1016,6 @@ static void bmac_set_multicast(struct net_device *dev) static void bmac_set_multicast(struct net_device *dev) { struct netdev_hw_addr *ha; - int i; unsigned short rx_cfg; u32 crc; @@ -1030,14 +1029,12 @@ static void bmac_set_multicast(struct net_device *dev) rx_cfg |= RxPromiscEnable; bmwrite(dev, RXCFG, rx_cfg); } else { - u16 hash_table[4]; + u16 hash_table[4] = { 0 }; rx_cfg = bmread(dev, RXCFG); rx_cfg &= ~RxPromiscEnable; bmwrite(dev, RXCFG, rx_cfg); - for(i = 0; i < 4; i++) hash_table[i] = 0; - netdev_for_each_mc_addr(ha, dev) { crc = ether_crc_le(6, ha->addr); crc >>= 26; diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig new file mode 100644 index 00000000000..514c57fd26f --- /dev/null +++ b/drivers/net/ethernet/arc/Kconfig @@ -0,0 +1,31 @@ +# +# ARC EMAC network device configuration +# + +config NET_VENDOR_ARC + bool "ARC devices" + default y + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about ARC cards. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_ARC + +config ARC_EMAC + tristate "ARC EMAC support" + select MII + select PHYLIB + depends on OF_IRQ + depends on OF_NET + ---help--- + On some legacy ARC (Synopsys) FPGA boards such as ARCAngel4/ML50x + non-standard on-chip ethernet device ARC EMAC 10/100 is used. + Say Y here if you have such a board. If unsure, say N. + +endif # NET_VENDOR_ARC diff --git a/drivers/net/ethernet/arc/Makefile b/drivers/net/ethernet/arc/Makefile new file mode 100644 index 00000000000..00c8657637d --- /dev/null +++ b/drivers/net/ethernet/arc/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the ARC network device drivers. +# + +arc_emac-objs := emac_main.o emac_mdio.o +obj-$(CONFIG_ARC_EMAC) += arc_emac.o diff --git a/drivers/net/ethernet/arc/emac.h b/drivers/net/ethernet/arc/emac.h new file mode 100644 index 00000000000..dc08678bf9a --- /dev/null +++ b/drivers/net/ethernet/arc/emac.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com) + * + * Registers and bits definitions of ARC EMAC + */ + +#ifndef ARC_EMAC_H +#define ARC_EMAC_H + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/netdevice.h> +#include <linux/phy.h> + +/* STATUS and ENABLE Register bit masks */ +#define TXINT_MASK (1<<0) /* Transmit interrupt */ +#define RXINT_MASK (1<<1) /* Receive interrupt */ +#define ERR_MASK (1<<2) /* Error interrupt */ +#define TXCH_MASK (1<<3) /* Transmit chaining error interrupt */ +#define MSER_MASK (1<<4) /* Missed packet counter error */ +#define RXCR_MASK (1<<8) /* RXCRCERR counter rolled over */ +#define RXFR_MASK (1<<9) /* RXFRAMEERR counter rolled over */ +#define RXFL_MASK (1<<10) /* RXOFLOWERR counter rolled over */ +#define MDIO_MASK (1<<12) /* MDIO complete interrupt */ +#define TXPL_MASK (1<<31) /* Force polling of BD by EMAC */ + +/* CONTROL Register bit masks */ +#define EN_MASK (1<<0) /* VMAC enable */ +#define TXRN_MASK (1<<3) /* TX enable */ +#define RXRN_MASK (1<<4) /* RX enable */ +#define DSBC_MASK (1<<8) /* Disable receive broadcast */ +#define ENFL_MASK (1<<10) /* Enable Full-duplex */ +#define PROM_MASK (1<<11) /* Promiscuous mode */ + +/* Buffer descriptor INFO bit masks */ +#define OWN_MASK (1<<31) /* 0-CPU owns buffer, 1-EMAC owns buffer */ +#define FIRST_MASK (1<<16) /* First buffer in chain */ +#define LAST_MASK (1<<17) /* Last buffer in chain */ +#define LEN_MASK 0x000007FF /* last 11 bits */ +#define CRLS (1<<21) +#define DEFR (1<<22) +#define DROP (1<<23) +#define RTRY (1<<24) +#define LTCL (1<<28) +#define UFLO (1<<29) + +#define FOR_EMAC OWN_MASK +#define FOR_CPU 0 + +/* ARC EMAC register set combines entries for MAC and MDIO */ +enum { + R_ID = 0, + R_STATUS, + R_ENABLE, + R_CTRL, + R_POLLRATE, + R_RXERR, + R_MISS, + R_TX_RING, + R_RX_RING, + R_ADDRL, + R_ADDRH, + R_LAFL, + R_LAFH, + R_MDIO, +}; + +#define TX_TIMEOUT (400*HZ/1000) /* Transmission timeout */ + +#define ARC_EMAC_NAPI_WEIGHT 40 /* Workload for NAPI */ + +#define EMAC_BUFFER_SIZE 1536 /* EMAC buffer size */ + +/** + * struct arc_emac_bd - EMAC buffer descriptor (BD). + * + * @info: Contains status information on the buffer itself. + * @data: 32-bit byte addressable pointer to the packet data. + */ +struct arc_emac_bd { + __le32 info; + dma_addr_t data; +}; + +/* Number of Rx/Tx BD's */ +#define RX_BD_NUM 128 +#define TX_BD_NUM 128 + +#define RX_RING_SZ (RX_BD_NUM * sizeof(struct arc_emac_bd)) +#define TX_RING_SZ (TX_BD_NUM * sizeof(struct arc_emac_bd)) + +/** + * struct buffer_state - Stores Rx/Tx buffer state. + * @sk_buff: Pointer to socket buffer. + * @addr: Start address of DMA-mapped memory region. + * @len: Length of DMA-mapped memory region. + */ +struct buffer_state { + struct sk_buff *skb; + DEFINE_DMA_UNMAP_ADDR(addr); + DEFINE_DMA_UNMAP_LEN(len); +}; + +/** + * struct arc_emac_priv - Storage of EMAC's private information. + * @dev: Pointer to the current device. + * @ndev: Pointer to the current network device. + * @phy_dev: Pointer to attached PHY device. + * @bus: Pointer to the current MII bus. + * @regs: Base address of EMAC memory-mapped control registers. + * @napi: Structure for NAPI. + * @stats: Network device statistics. + * @rxbd: Pointer to Rx BD ring. + * @txbd: Pointer to Tx BD ring. + * @rxbd_dma: DMA handle for Rx BD ring. + * @txbd_dma: DMA handle for Tx BD ring. + * @rx_buff: Storage for Rx buffers states. + * @tx_buff: Storage for Tx buffers states. + * @txbd_curr: Index of Tx BD to use on the next "ndo_start_xmit". + * @txbd_dirty: Index of Tx BD to free on the next Tx interrupt. + * @last_rx_bd: Index of the last Rx BD we've got from EMAC. + * @link: PHY's last seen link state. + * @duplex: PHY's last set duplex mode. + * @speed: PHY's last set speed. + * @max_speed: Maximum supported by current system network data-rate. + */ +struct arc_emac_priv { + /* Devices */ + struct device *dev; + struct net_device *ndev; + struct phy_device *phy_dev; + struct mii_bus *bus; + + void __iomem *regs; + + struct napi_struct napi; + struct net_device_stats stats; + + struct arc_emac_bd *rxbd; + struct arc_emac_bd *txbd; + + dma_addr_t rxbd_dma; + dma_addr_t txbd_dma; + + struct buffer_state rx_buff[RX_BD_NUM]; + struct buffer_state tx_buff[TX_BD_NUM]; + unsigned int txbd_curr; + unsigned int txbd_dirty; + + unsigned int last_rx_bd; + + unsigned int link; + unsigned int duplex; + unsigned int speed; + unsigned int max_speed; +}; + +/** + * arc_reg_set - Sets EMAC register with provided value. + * @priv: Pointer to ARC EMAC private data structure. + * @reg: Register offset from base address. + * @value: Value to set in register. + */ +static inline void arc_reg_set(struct arc_emac_priv *priv, int reg, int value) +{ + iowrite32(value, priv->regs + reg * sizeof(int)); +} + +/** + * arc_reg_get - Gets value of specified EMAC register. + * @priv: Pointer to ARC EMAC private data structure. + * @reg: Register offset from base address. + * + * returns: Value of requested register. + */ +static inline unsigned int arc_reg_get(struct arc_emac_priv *priv, int reg) +{ + return ioread32(priv->regs + reg * sizeof(int)); +} + +/** + * arc_reg_or - Applies mask to specified EMAC register - ("reg" | "mask"). + * @priv: Pointer to ARC EMAC private data structure. + * @reg: Register offset from base address. + * @mask: Mask to apply to specified register. + * + * This function reads initial register value, then applies provided mask + * to it and then writes register back. + */ +static inline void arc_reg_or(struct arc_emac_priv *priv, int reg, int mask) +{ + unsigned int value = arc_reg_get(priv, reg); + arc_reg_set(priv, reg, value | mask); +} + +/** + * arc_reg_clr - Applies mask to specified EMAC register - ("reg" & ~"mask"). + * @priv: Pointer to ARC EMAC private data structure. + * @reg: Register offset from base address. + * @mask: Mask to apply to specified register. + * + * This function reads initial register value, then applies provided mask + * to it and then writes register back. + */ +static inline void arc_reg_clr(struct arc_emac_priv *priv, int reg, int mask) +{ + unsigned int value = arc_reg_get(priv, reg); + arc_reg_set(priv, reg, value & ~mask); +} + +int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv); +int arc_mdio_remove(struct arc_emac_priv *priv); + +#endif /* ARC_EMAC_H */ diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c new file mode 100644 index 00000000000..f1b121ee552 --- /dev/null +++ b/drivers/net/ethernet/arc/emac_main.c @@ -0,0 +1,819 @@ +/* + * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Driver for the ARC EMAC 10100 (hardware revision 5) + * + * Contributors: + * Amit Bhor + * Sameer Dhavale + * Vineet Gupta + */ + +#include <linux/etherdevice.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/of_platform.h> + +#include "emac.h" + +#define DRV_NAME "arc_emac" +#define DRV_VERSION "1.0" + +/** + * arc_emac_adjust_link - Adjust the PHY link duplex. + * @ndev: Pointer to the net_device structure. + * + * This function is called to change the duplex setting after auto negotiation + * is done by the PHY. + */ +static void arc_emac_adjust_link(struct net_device *ndev) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = priv->phy_dev; + unsigned int reg, state_changed = 0; + + if (priv->link != phy_dev->link) { + priv->link = phy_dev->link; + state_changed = 1; + } + + if (priv->speed != phy_dev->speed) { + priv->speed = phy_dev->speed; + state_changed = 1; + } + + if (priv->duplex != phy_dev->duplex) { + reg = arc_reg_get(priv, R_CTRL); + + if (DUPLEX_FULL == phy_dev->duplex) + reg |= ENFL_MASK; + else + reg &= ~ENFL_MASK; + + arc_reg_set(priv, R_CTRL, reg); + priv->duplex = phy_dev->duplex; + state_changed = 1; + } + + if (state_changed) + phy_print_status(phy_dev); +} + +/** + * arc_emac_get_settings - Get PHY settings. + * @ndev: Pointer to net_device structure. + * @cmd: Pointer to ethtool_cmd structure. + * + * This implements ethtool command for getting PHY settings. If PHY could + * not be found, the function returns -ENODEV. This function calls the + * relevant PHY ethtool API to get the PHY settings. + * Issue "ethtool ethX" under linux prompt to execute this function. + */ +static int arc_emac_get_settings(struct net_device *ndev, + struct ethtool_cmd *cmd) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + + return phy_ethtool_gset(priv->phy_dev, cmd); +} + +/** + * arc_emac_set_settings - Set PHY settings as passed in the argument. + * @ndev: Pointer to net_device structure. + * @cmd: Pointer to ethtool_cmd structure. + * + * This implements ethtool command for setting various PHY settings. If PHY + * could not be found, the function returns -ENODEV. This function calls the + * relevant PHY ethtool API to set the PHY. + * Issue e.g. "ethtool -s ethX speed 1000" under linux prompt to execute this + * function. + */ +static int arc_emac_set_settings(struct net_device *ndev, + struct ethtool_cmd *cmd) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + return phy_ethtool_sset(priv->phy_dev, cmd); +} + +/** + * arc_emac_get_drvinfo - Get EMAC driver information. + * @ndev: Pointer to net_device structure. + * @info: Pointer to ethtool_drvinfo structure. + * + * This implements ethtool command for getting the driver information. + * Issue "ethtool -i ethX" under linux prompt to execute this function. + */ +static void arc_emac_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); +} + +static const struct ethtool_ops arc_emac_ethtool_ops = { + .get_settings = arc_emac_get_settings, + .set_settings = arc_emac_set_settings, + .get_drvinfo = arc_emac_get_drvinfo, + .get_link = ethtool_op_get_link, +}; + +#define FIRST_OR_LAST_MASK (FIRST_MASK | LAST_MASK) + +/** + * arc_emac_tx_clean - clears processed by EMAC Tx BDs. + * @ndev: Pointer to the network device. + */ +static void arc_emac_tx_clean(struct net_device *ndev) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &priv->stats; + unsigned int i; + + for (i = 0; i < TX_BD_NUM; i++) { + unsigned int *txbd_dirty = &priv->txbd_dirty; + struct arc_emac_bd *txbd = &priv->txbd[*txbd_dirty]; + struct buffer_state *tx_buff = &priv->tx_buff[*txbd_dirty]; + struct sk_buff *skb = tx_buff->skb; + unsigned int info = le32_to_cpu(txbd->info); + + *txbd_dirty = (*txbd_dirty + 1) % TX_BD_NUM; + + if ((info & FOR_EMAC) || !txbd->data) + break; + + if (unlikely(info & (DROP | DEFR | LTCL | UFLO))) { + stats->tx_errors++; + stats->tx_dropped++; + + if (info & DEFR) + stats->tx_carrier_errors++; + + if (info & LTCL) + stats->collisions++; + + if (info & UFLO) + stats->tx_fifo_errors++; + } else if (likely(info & FIRST_OR_LAST_MASK)) { + stats->tx_packets++; + stats->tx_bytes += skb->len; + } + + dma_unmap_single(&ndev->dev, dma_unmap_addr(tx_buff, addr), + dma_unmap_len(tx_buff, len), DMA_TO_DEVICE); + + /* return the sk_buff to system */ + dev_kfree_skb_irq(skb); + + txbd->data = 0; + txbd->info = 0; + + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } +} + +/** + * arc_emac_rx - processing of Rx packets. + * @ndev: Pointer to the network device. + * @budget: How many BDs to process on 1 call. + * + * returns: Number of processed BDs + * + * Iterate through Rx BDs and deliver received packages to upper layer. + */ +static int arc_emac_rx(struct net_device *ndev, int budget) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + unsigned int work_done; + + for (work_done = 0; work_done <= budget; work_done++) { + unsigned int *last_rx_bd = &priv->last_rx_bd; + struct net_device_stats *stats = &priv->stats; + struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd]; + struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd]; + unsigned int pktlen, info = le32_to_cpu(rxbd->info); + struct sk_buff *skb; + dma_addr_t addr; + + if (unlikely((info & OWN_MASK) == FOR_EMAC)) + break; + + /* Make a note that we saw a packet at this BD. + * So next time, driver starts from this + 1 + */ + *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM; + + if (unlikely((info & FIRST_OR_LAST_MASK) != + FIRST_OR_LAST_MASK)) { + /* We pre-allocate buffers of MTU size so incoming + * packets won't be split/chained. + */ + if (net_ratelimit()) + netdev_err(ndev, "incomplete packet received\n"); + + /* Return ownership to EMAC */ + rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE); + stats->rx_errors++; + stats->rx_length_errors++; + continue; + } + + pktlen = info & LEN_MASK; + stats->rx_packets++; + stats->rx_bytes += pktlen; + skb = rx_buff->skb; + skb_put(skb, pktlen); + skb->dev = ndev; + skb->protocol = eth_type_trans(skb, ndev); + + dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr), + dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE); + + /* Prepare the BD for next cycle */ + rx_buff->skb = netdev_alloc_skb_ip_align(ndev, + EMAC_BUFFER_SIZE); + if (unlikely(!rx_buff->skb)) { + stats->rx_errors++; + /* Because receive_skb is below, increment rx_dropped */ + stats->rx_dropped++; + continue; + } + + /* receive_skb only if new skb was allocated to avoid holes */ + netif_receive_skb(skb); + + addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data, + EMAC_BUFFER_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&ndev->dev, addr)) { + if (net_ratelimit()) + netdev_err(ndev, "cannot dma map\n"); + dev_kfree_skb(rx_buff->skb); + stats->rx_errors++; + continue; + } + dma_unmap_addr_set(rx_buff, addr, addr); + dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE); + + rxbd->data = cpu_to_le32(addr); + + /* Make sure pointer to data buffer is set */ + wmb(); + + /* Return ownership to EMAC */ + rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE); + } + + return work_done; +} + +/** + * arc_emac_poll - NAPI poll handler. + * @napi: Pointer to napi_struct structure. + * @budget: How many BDs to process on 1 call. + * + * returns: Number of processed BDs + */ +static int arc_emac_poll(struct napi_struct *napi, int budget) +{ + struct net_device *ndev = napi->dev; + struct arc_emac_priv *priv = netdev_priv(ndev); + unsigned int work_done; + + arc_emac_tx_clean(ndev); + + work_done = arc_emac_rx(ndev, budget); + if (work_done < budget) { + napi_complete(napi); + arc_reg_or(priv, R_ENABLE, RXINT_MASK); + } + + return work_done; +} + +/** + * arc_emac_intr - Global interrupt handler for EMAC. + * @irq: irq number. + * @dev_instance: device instance. + * + * returns: IRQ_HANDLED for all cases. + * + * ARC EMAC has only 1 interrupt line, and depending on bits raised in + * STATUS register we may tell what is a reason for interrupt to fire. + */ +static irqreturn_t arc_emac_intr(int irq, void *dev_instance) +{ + struct net_device *ndev = dev_instance; + struct arc_emac_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &priv->stats; + unsigned int status; + + status = arc_reg_get(priv, R_STATUS); + status &= ~MDIO_MASK; + + /* Reset all flags except "MDIO complete" */ + arc_reg_set(priv, R_STATUS, status); + + if (status & RXINT_MASK) { + if (likely(napi_schedule_prep(&priv->napi))) { + arc_reg_clr(priv, R_ENABLE, RXINT_MASK); + __napi_schedule(&priv->napi); + } + } + + if (status & ERR_MASK) { + /* MSER/RXCR/RXFR/RXFL interrupt fires on corresponding + * 8-bit error counter overrun. + */ + + if (status & MSER_MASK) { + stats->rx_missed_errors += 0x100; + stats->rx_errors += 0x100; + } + + if (status & RXCR_MASK) { + stats->rx_crc_errors += 0x100; + stats->rx_errors += 0x100; + } + + if (status & RXFR_MASK) { + stats->rx_frame_errors += 0x100; + stats->rx_errors += 0x100; + } + + if (status & RXFL_MASK) { + stats->rx_over_errors += 0x100; + stats->rx_errors += 0x100; + } + } + + return IRQ_HANDLED; +} + +/** + * arc_emac_open - Open the network device. + * @ndev: Pointer to the network device. + * + * returns: 0, on success or non-zero error value on failure. + * + * This function sets the MAC address, requests and enables an IRQ + * for the EMAC device and starts the Tx queue. + * It also connects to the phy device. + */ +static int arc_emac_open(struct net_device *ndev) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = priv->phy_dev; + int i; + + phy_dev->autoneg = AUTONEG_ENABLE; + phy_dev->speed = 0; + phy_dev->duplex = 0; + phy_dev->advertising = phy_dev->supported; + + if (priv->max_speed > 100) { + phy_dev->advertising &= PHY_GBIT_FEATURES; + } else if (priv->max_speed <= 100) { + phy_dev->advertising &= PHY_BASIC_FEATURES; + if (priv->max_speed <= 10) { + phy_dev->advertising &= ~SUPPORTED_100baseT_Half; + phy_dev->advertising &= ~SUPPORTED_100baseT_Full; + } + } + + priv->last_rx_bd = 0; + + /* Allocate and set buffers for Rx BD's */ + for (i = 0; i < RX_BD_NUM; i++) { + dma_addr_t addr; + unsigned int *last_rx_bd = &priv->last_rx_bd; + struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd]; + struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd]; + + rx_buff->skb = netdev_alloc_skb_ip_align(ndev, + EMAC_BUFFER_SIZE); + if (unlikely(!rx_buff->skb)) + return -ENOMEM; + + addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data, + EMAC_BUFFER_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&ndev->dev, addr)) { + netdev_err(ndev, "cannot dma map\n"); + dev_kfree_skb(rx_buff->skb); + return -ENOMEM; + } + dma_unmap_addr_set(rx_buff, addr, addr); + dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE); + + rxbd->data = cpu_to_le32(addr); + + /* Make sure pointer to data buffer is set */ + wmb(); + + /* Return ownership to EMAC */ + rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE); + + *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM; + } + + /* Clean Tx BD's */ + memset(priv->txbd, 0, TX_RING_SZ); + + /* Initialize logical address filter */ + arc_reg_set(priv, R_LAFL, 0); + arc_reg_set(priv, R_LAFH, 0); + + /* Set BD ring pointers for device side */ + arc_reg_set(priv, R_RX_RING, (unsigned int)priv->rxbd_dma); + arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma); + + /* Enable interrupts */ + arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK); + + /* Set CONTROL */ + arc_reg_set(priv, R_CTRL, + (RX_BD_NUM << 24) | /* RX BD table length */ + (TX_BD_NUM << 16) | /* TX BD table length */ + TXRN_MASK | RXRN_MASK); + + napi_enable(&priv->napi); + + /* Enable EMAC */ + arc_reg_or(priv, R_CTRL, EN_MASK); + + phy_start_aneg(priv->phy_dev); + + netif_start_queue(ndev); + + return 0; +} + +/** + * arc_emac_stop - Close the network device. + * @ndev: Pointer to the network device. + * + * This function stops the Tx queue, disables interrupts and frees the IRQ for + * the EMAC device. + * It also disconnects the PHY device associated with the EMAC device. + */ +static int arc_emac_stop(struct net_device *ndev) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + + napi_disable(&priv->napi); + netif_stop_queue(ndev); + + /* Disable interrupts */ + arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK); + + /* Disable EMAC */ + arc_reg_clr(priv, R_CTRL, EN_MASK); + + return 0; +} + +/** + * arc_emac_stats - Get system network statistics. + * @ndev: Pointer to net_device structure. + * + * Returns the address of the device statistics structure. + * Statistics are updated in interrupt handler. + */ +static struct net_device_stats *arc_emac_stats(struct net_device *ndev) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &priv->stats; + unsigned long miss, rxerr; + u8 rxcrc, rxfram, rxoflow; + + rxerr = arc_reg_get(priv, R_RXERR); + miss = arc_reg_get(priv, R_MISS); + + rxcrc = rxerr; + rxfram = rxerr >> 8; + rxoflow = rxerr >> 16; + + stats->rx_errors += miss; + stats->rx_errors += rxcrc + rxfram + rxoflow; + + stats->rx_over_errors += rxoflow; + stats->rx_frame_errors += rxfram; + stats->rx_crc_errors += rxcrc; + stats->rx_missed_errors += miss; + + return stats; +} + +/** + * arc_emac_tx - Starts the data transmission. + * @skb: sk_buff pointer that contains data to be Transmitted. + * @ndev: Pointer to net_device structure. + * + * returns: NETDEV_TX_OK, on success + * NETDEV_TX_BUSY, if any of the descriptors are not free. + * + * This function is invoked from upper layers to initiate transmission. + */ +static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + unsigned int len, *txbd_curr = &priv->txbd_curr; + struct net_device_stats *stats = &priv->stats; + __le32 *info = &priv->txbd[*txbd_curr].info; + dma_addr_t addr; + + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + + len = max_t(unsigned int, ETH_ZLEN, skb->len); + + /* EMAC still holds this buffer in its possession. + * CPU must not modify this buffer descriptor + */ + if (unlikely((le32_to_cpu(*info) & OWN_MASK) == FOR_EMAC)) { + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + addr = dma_map_single(&ndev->dev, (void *)skb->data, len, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(&ndev->dev, addr))) { + stats->tx_dropped++; + stats->tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + dma_unmap_addr_set(&priv->tx_buff[*txbd_curr], addr, addr); + dma_unmap_len_set(&priv->tx_buff[*txbd_curr], len, len); + + priv->tx_buff[*txbd_curr].skb = skb; + priv->txbd[*txbd_curr].data = cpu_to_le32(addr); + + /* Make sure pointer to data buffer is set */ + wmb(); + + *info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | len); + + /* Increment index to point to the next BD */ + *txbd_curr = (*txbd_curr + 1) % TX_BD_NUM; + + /* Get "info" of the next BD */ + info = &priv->txbd[*txbd_curr].info; + + /* Check if if Tx BD ring is full - next BD is still owned by EMAC */ + if (unlikely((le32_to_cpu(*info) & OWN_MASK) == FOR_EMAC)) + netif_stop_queue(ndev); + + arc_reg_set(priv, R_STATUS, TXPL_MASK); + + skb_tx_timestamp(skb); + + return NETDEV_TX_OK; +} + +/** + * arc_emac_set_address - Set the MAC address for this device. + * @ndev: Pointer to net_device structure. + * @p: 6 byte Address to be written as MAC address. + * + * This function copies the HW address from the sockaddr structure to the + * net_device structure and updates the address in HW. + * + * returns: -EBUSY if the net device is busy or 0 if the address is set + * successfully. + */ +static int arc_emac_set_address(struct net_device *ndev, void *p) +{ + struct arc_emac_priv *priv = netdev_priv(ndev); + struct sockaddr *addr = p; + unsigned int addr_low, addr_hi; + + if (netif_running(ndev)) + return -EBUSY; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); + + addr_low = le32_to_cpu(*(__le32 *) &ndev->dev_addr[0]); + addr_hi = le16_to_cpu(*(__le16 *) &ndev->dev_addr[4]); + + arc_reg_set(priv, R_ADDRL, addr_low); + arc_reg_set(priv, R_ADDRH, addr_hi); + + return 0; +} + +static const struct net_device_ops arc_emac_netdev_ops = { + .ndo_open = arc_emac_open, + .ndo_stop = arc_emac_stop, + .ndo_start_xmit = arc_emac_tx, + .ndo_set_mac_address = arc_emac_set_address, + .ndo_get_stats = arc_emac_stats, +}; + +static int arc_emac_probe(struct platform_device *pdev) +{ + struct resource res_regs, res_irq; + struct device_node *phy_node; + struct arc_emac_priv *priv; + struct net_device *ndev; + const char *mac_addr; + unsigned int id, clock_frequency; + int err; + + if (!pdev->dev.of_node) + return -ENODEV; + + /* Get PHY from device tree */ + phy_node = of_parse_phandle(pdev->dev.of_node, "phy", 0); + if (!phy_node) { + dev_err(&pdev->dev, "failed to retrieve phy description from device tree\n"); + return -ENODEV; + } + + /* Get EMAC registers base address from device tree */ + err = of_address_to_resource(pdev->dev.of_node, 0, &res_regs); + if (err) { + dev_err(&pdev->dev, "failed to retrieve registers base from device tree\n"); + return -ENODEV; + } + + /* Get CPU clock frequency from device tree */ + if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &clock_frequency)) { + dev_err(&pdev->dev, "failed to retrieve <clock-frequency> from device tree\n"); + return -EINVAL; + } + + /* Get IRQ from device tree */ + err = of_irq_to_resource(pdev->dev.of_node, 0, &res_irq); + if (!err) { + dev_err(&pdev->dev, "failed to retrieve <irq> value from device tree\n"); + return -ENODEV; + } + + ndev = alloc_etherdev(sizeof(struct arc_emac_priv)); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, &pdev->dev); + + ndev->netdev_ops = &arc_emac_netdev_ops; + ndev->ethtool_ops = &arc_emac_ethtool_ops; + ndev->watchdog_timeo = TX_TIMEOUT; + /* FIXME :: no multicast support yet */ + ndev->flags &= ~IFF_MULTICAST; + + priv = netdev_priv(ndev); + priv->dev = &pdev->dev; + priv->ndev = ndev; + + priv->regs = devm_ioremap_resource(&pdev->dev, &res_regs); + if (IS_ERR(priv->regs)) { + err = PTR_ERR(priv->regs); + goto out; + } + dev_dbg(&pdev->dev, "Registers base address is 0x%p\n", priv->regs); + + id = arc_reg_get(priv, R_ID); + + /* Check for EMAC revision 5 or 7, magic number */ + if (!(id == 0x0005fd02 || id == 0x0007fd02)) { + dev_err(&pdev->dev, "ARC EMAC not detected, id=0x%x\n", id); + err = -ENODEV; + goto out; + } + dev_info(&pdev->dev, "ARC EMAC detected with id: 0x%x\n", id); + + /* Set poll rate so that it polls every 1 ms */ + arc_reg_set(priv, R_POLLRATE, clock_frequency / 1000000); + + /* Get max speed of operation from device tree */ + if (of_property_read_u32(pdev->dev.of_node, "max-speed", + &priv->max_speed)) { + dev_err(&pdev->dev, "failed to retrieve <max-speed> from device tree\n"); + err = -EINVAL; + goto out; + } + + ndev->irq = res_irq.start; + dev_info(&pdev->dev, "IRQ is %d\n", ndev->irq); + + /* Register interrupt handler for device */ + err = devm_request_irq(&pdev->dev, ndev->irq, arc_emac_intr, 0, + ndev->name, ndev); + if (err) { + dev_err(&pdev->dev, "could not allocate IRQ\n"); + goto out; + } + + /* Get MAC address from device tree */ + mac_addr = of_get_mac_address(pdev->dev.of_node); + + if (!mac_addr || !is_valid_ether_addr(mac_addr)) + eth_hw_addr_random(ndev); + else + memcpy(ndev->dev_addr, mac_addr, ETH_ALEN); + + dev_info(&pdev->dev, "MAC address is now %pM\n", ndev->dev_addr); + + /* Do 1 allocation instead of 2 separate ones for Rx and Tx BD rings */ + priv->rxbd = dmam_alloc_coherent(&pdev->dev, RX_RING_SZ + TX_RING_SZ, + &priv->rxbd_dma, GFP_KERNEL); + + if (!priv->rxbd) { + dev_err(&pdev->dev, "failed to allocate data buffers\n"); + err = -ENOMEM; + goto out; + } + + priv->txbd = priv->rxbd + RX_BD_NUM; + + priv->txbd_dma = priv->rxbd_dma + RX_RING_SZ; + dev_dbg(&pdev->dev, "EMAC Device addr: Rx Ring [0x%x], Tx Ring[%x]\n", + (unsigned int)priv->rxbd_dma, (unsigned int)priv->txbd_dma); + + err = arc_mdio_probe(pdev, priv); + if (err) { + dev_err(&pdev->dev, "failed to probe MII bus\n"); + goto out; + } + + priv->phy_dev = of_phy_connect(ndev, phy_node, arc_emac_adjust_link, 0, + PHY_INTERFACE_MODE_MII); + if (!priv->phy_dev) { + dev_err(&pdev->dev, "of_phy_connect() failed\n"); + err = -ENODEV; + goto out; + } + + dev_info(&pdev->dev, "connected to %s phy with id 0x%x\n", + priv->phy_dev->drv->name, priv->phy_dev->phy_id); + + netif_napi_add(ndev, &priv->napi, arc_emac_poll, ARC_EMAC_NAPI_WEIGHT); + + err = register_netdev(ndev); + if (err) { + netif_napi_del(&priv->napi); + dev_err(&pdev->dev, "failed to register network device\n"); + goto out; + } + + return 0; + +out: + free_netdev(ndev); + return err; +} + +static int arc_emac_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct arc_emac_priv *priv = netdev_priv(ndev); + + phy_disconnect(priv->phy_dev); + priv->phy_dev = NULL; + arc_mdio_remove(priv); + unregister_netdev(ndev); + netif_napi_del(&priv->napi); + free_netdev(ndev); + + return 0; +} + +static const struct of_device_id arc_emac_dt_ids[] = { + { .compatible = "snps,arc-emac" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, arc_emac_dt_ids); + +static struct platform_driver arc_emac_driver = { + .probe = arc_emac_probe, + .remove = arc_emac_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = arc_emac_dt_ids, + }, +}; + +module_platform_driver(arc_emac_driver); + +MODULE_AUTHOR("Alexey Brodkin <abrodkin@synopsys.com>"); +MODULE_DESCRIPTION("ARC EMAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/arc/emac_mdio.c b/drivers/net/ethernet/arc/emac_mdio.c new file mode 100644 index 00000000000..26ba2423f33 --- /dev/null +++ b/drivers/net/ethernet/arc/emac_mdio.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com) + * + * MDIO implementation for ARC EMAC + */ + +#include <linux/delay.h> +#include <linux/of_mdio.h> +#include <linux/platform_device.h> + +#include "emac.h" + +/* Number of seconds we wait for "MDIO complete" flag to appear */ +#define ARC_MDIO_COMPLETE_POLL_COUNT 1 + +/** + * arc_mdio_complete_wait - Waits until MDIO transaction is completed. + * @priv: Pointer to ARC EMAC private data structure. + * + * returns: 0 on success, -ETIMEDOUT on a timeout. + */ +static int arc_mdio_complete_wait(struct arc_emac_priv *priv) +{ + unsigned int i; + + for (i = 0; i < ARC_MDIO_COMPLETE_POLL_COUNT * 40; i++) { + unsigned int status = arc_reg_get(priv, R_STATUS); + + status &= MDIO_MASK; + + if (status) { + /* Reset "MDIO complete" flag */ + arc_reg_set(priv, R_STATUS, status); + return 0; + } + + msleep(25); + } + + return -ETIMEDOUT; +} + +/** + * arc_mdio_read - MDIO interface read function. + * @bus: Pointer to MII bus structure. + * @phy_addr: Address of the PHY device. + * @reg_num: PHY register to read. + * + * returns: The register contents on success, -ETIMEDOUT on a timeout. + * + * Reads the contents of the requested register from the requested PHY + * address. + */ +static int arc_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) +{ + struct arc_emac_priv *priv = bus->priv; + unsigned int value; + int error; + + arc_reg_set(priv, R_MDIO, + 0x60020000 | (phy_addr << 23) | (reg_num << 18)); + + error = arc_mdio_complete_wait(priv); + if (error < 0) + return error; + + value = arc_reg_get(priv, R_MDIO) & 0xffff; + + dev_dbg(priv->dev, "arc_mdio_read(phy_addr=%i, reg_num=%x) = %x\n", + phy_addr, reg_num, value); + + return value; +} + +/** + * arc_mdio_write - MDIO interface write function. + * @bus: Pointer to MII bus structure. + * @phy_addr: Address of the PHY device. + * @reg_num: PHY register to write to. + * @value: Value to be written into the register. + * + * returns: 0 on success, -ETIMEDOUT on a timeout. + * + * Writes the value to the requested register. + */ +static int arc_mdio_write(struct mii_bus *bus, int phy_addr, + int reg_num, u16 value) +{ + struct arc_emac_priv *priv = bus->priv; + + dev_dbg(priv->dev, + "arc_mdio_write(phy_addr=%i, reg_num=%x, value=%x)\n", + phy_addr, reg_num, value); + + arc_reg_set(priv, R_MDIO, + 0x50020000 | (phy_addr << 23) | (reg_num << 18) | value); + + return arc_mdio_complete_wait(priv); +} + +/** + * arc_mdio_probe - MDIO probe function. + * @pdev: Pointer to platform device. + * @priv: Pointer to ARC EMAC private data structure. + * + * returns: 0 on success, -ENOMEM when mdiobus_alloc + * (to allocate memory for MII bus structure) fails. + * + * Sets up and registers the MDIO interface. + */ +int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv) +{ + struct mii_bus *bus; + int error; + + bus = mdiobus_alloc(); + if (!bus) + return -ENOMEM; + + priv->bus = bus; + bus->priv = priv; + bus->parent = priv->dev; + bus->name = "Synopsys MII Bus", + bus->read = &arc_mdio_read; + bus->write = &arc_mdio_write; + + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); + + error = of_mdiobus_register(bus, pdev->dev.of_node); + if (error) { + dev_err(priv->dev, "cannot register MDIO bus %s\n", bus->name); + mdiobus_free(bus); + return error; + } + + return 0; +} + +/** + * arc_mdio_remove - MDIO remove function. + * @priv: Pointer to ARC EMAC private data structure. + * + * Unregisters the MDIO and frees any associate memory for MII bus. + */ +int arc_mdio_remove(struct arc_emac_priv *priv) +{ + mdiobus_unregister(priv->bus); + mdiobus_free(priv->bus); + priv->bus = NULL; + + return 0; +} diff --git a/drivers/net/ethernet/atheros/Kconfig b/drivers/net/ethernet/atheros/Kconfig index ad6aa1e9834..58ad37c733b 100644 --- a/drivers/net/ethernet/atheros/Kconfig +++ b/drivers/net/ethernet/atheros/Kconfig @@ -22,7 +22,6 @@ config ATL2 tristate "Atheros L2 Fast Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver supports the Atheros L2 fast ethernet adapter. @@ -34,7 +33,6 @@ config ATL1 tristate "Atheros/Attansic L1 Gigabit Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver supports the Atheros/Attansic L1 gigabit ethernet @@ -47,7 +45,6 @@ config ATL1E tristate "Atheros L1E Gigabit Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver supports the Atheros L1E gigabit ethernet adapter. @@ -59,7 +56,6 @@ config ATL1C tristate "Atheros L1C Gigabit Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver supports the Atheros L1C gigabit ethernet adapter. @@ -71,7 +67,6 @@ config ALX tristate "Qualcomm Atheros AR816x/AR817x support" depends on PCI select CRC32 - select NET_CORE select MDIO help This driver supports the Qualcomm Atheros L1F ethernet adapter, diff --git a/drivers/net/ethernet/atheros/alx/alx.h b/drivers/net/ethernet/atheros/alx/alx.h index 50b3ae2b143..d71103dbf2c 100644 --- a/drivers/net/ethernet/atheros/alx/alx.h +++ b/drivers/net/ethernet/atheros/alx/alx.h @@ -85,16 +85,16 @@ struct alx_priv { struct { dma_addr_t dma; void *virt; - int size; + unsigned int size; } descmem; /* protect int_mask updates */ spinlock_t irq_lock; u32 int_mask; - int tx_ringsz; - int rx_ringsz; - int rxbuf_size; + unsigned int tx_ringsz; + unsigned int rx_ringsz; + unsigned int rxbuf_size; struct napi_struct napi; struct alx_tx_queue txq; diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 6fa2aec2bc8..45b36507abc 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -46,21 +46,37 @@ #include "reg.h" #include "hw.h" +static u32 alx_get_supported_speeds(struct alx_hw *hw) +{ + u32 supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full; + + if (alx_hw_giga(hw)) + supported |= SUPPORTED_1000baseT_Full; + + BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half); + BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full); + BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half); + BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full); + BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full); + + return supported; +} static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; - ecmd->supported = SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | + ecmd->supported = SUPPORTED_Autoneg | SUPPORTED_TP | - SUPPORTED_Pause; + SUPPORTED_Pause | + SUPPORTED_Asym_Pause; if (alx_hw_giga(hw)) ecmd->supported |= SUPPORTED_1000baseT_Full; + ecmd->supported |= alx_get_supported_speeds(hw); ecmd->advertising = ADVERTISED_TP; if (hw->adv_cfg & ADVERTISED_Autoneg) @@ -68,6 +84,7 @@ static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->port = PORT_TP; ecmd->phy_address = 0; + if (hw->adv_cfg & ADVERTISED_Autoneg) ecmd->autoneg = AUTONEG_ENABLE; else @@ -85,14 +102,8 @@ static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) } } - if (hw->link_speed != SPEED_UNKNOWN) { - ethtool_cmd_speed_set(ecmd, - hw->link_speed - hw->link_speed % 10); - ecmd->duplex = hw->link_speed % 10; - } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; - } + ethtool_cmd_speed_set(ecmd, hw->link_speed); + ecmd->duplex = hw->duplex; return 0; } @@ -106,28 +117,15 @@ static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ASSERT_RTNL(); if (ecmd->autoneg == AUTONEG_ENABLE) { - if (ecmd->advertising & ADVERTISED_1000baseT_Half) + if (ecmd->advertising & ~alx_get_supported_speeds(hw)) return -EINVAL; adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; } else { - int speed = ethtool_cmd_speed(ecmd); - - switch (speed + ecmd->duplex) { - case SPEED_10 + DUPLEX_HALF: - adv_cfg = ADVERTISED_10baseT_Half; - break; - case SPEED_10 + DUPLEX_FULL: - adv_cfg = ADVERTISED_10baseT_Full; - break; - case SPEED_100 + DUPLEX_HALF: - adv_cfg = ADVERTISED_100baseT_Half; - break; - case SPEED_100 + DUPLEX_FULL: - adv_cfg = ADVERTISED_100baseT_Full; - break; - default: + adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd), + ecmd->duplex); + + if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) return -EINVAL; - } } hw->adv_cfg = adv_cfg; @@ -140,21 +138,10 @@ static void alx_get_pauseparam(struct net_device *netdev, struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; - if (hw->flowctrl & ALX_FC_ANEG && - hw->adv_cfg & ADVERTISED_Autoneg) - pause->autoneg = AUTONEG_ENABLE; - else - pause->autoneg = AUTONEG_DISABLE; - - if (hw->flowctrl & ALX_FC_TX) - pause->tx_pause = 1; - else - pause->tx_pause = 0; - - if (hw->flowctrl & ALX_FC_RX) - pause->rx_pause = 1; - else - pause->rx_pause = 0; + pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG && + hw->adv_cfg & ADVERTISED_Autoneg); + pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX); + pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX); } @@ -187,7 +174,8 @@ static int alx_set_pauseparam(struct net_device *netdev, if (reconfig_phy) { err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); - return err; + if (err) + return err; } /* flow control on mac */ @@ -213,60 +201,12 @@ static void alx_set_msglevel(struct net_device *netdev, u32 data) alx->msg_enable = data; } -static void alx_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) -{ - struct alx_priv *alx = netdev_priv(netdev); - struct alx_hw *hw = &alx->hw; - - wol->supported = WAKE_MAGIC | WAKE_PHY; - wol->wolopts = 0; - - if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) - wol->wolopts |= WAKE_MAGIC; - if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) - wol->wolopts |= WAKE_PHY; -} - -static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) -{ - struct alx_priv *alx = netdev_priv(netdev); - struct alx_hw *hw = &alx->hw; - - if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | - WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) - return -EOPNOTSUPP; - - hw->sleep_ctrl = 0; - - if (wol->wolopts & WAKE_MAGIC) - hw->sleep_ctrl |= ALX_SLEEP_WOL_MAGIC; - if (wol->wolopts & WAKE_PHY) - hw->sleep_ctrl |= ALX_SLEEP_WOL_PHY; - - device_set_wakeup_enable(&alx->hw.pdev->dev, hw->sleep_ctrl); - - return 0; -} - -static void alx_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) -{ - struct alx_priv *alx = netdev_priv(netdev); - - strlcpy(drvinfo->driver, alx_drv_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(alx->hw.pdev), - sizeof(drvinfo->bus_info)); -} - const struct ethtool_ops alx_ethtool_ops = { .get_settings = alx_get_settings, .set_settings = alx_set_settings, .get_pauseparam = alx_get_pauseparam, .set_pauseparam = alx_set_pauseparam, - .get_drvinfo = alx_get_drvinfo, .get_msglevel = alx_get_msglevel, .set_msglevel = alx_set_msglevel, - .get_wol = alx_get_wol, - .set_wol = alx_set_wol, .get_link = ethtool_op_get_link, }; diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index 220a16ad0e4..1e8c24a3cb4 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -282,8 +282,8 @@ static bool alx_read_macaddr(struct alx_hw *hw, u8 *addr) mac1 = alx_read_mem32(hw, ALX_STAD1); /* addr should be big-endian */ - *(__be32 *)(addr + 2) = cpu_to_be32(mac0); - *(__be16 *)addr = cpu_to_be16(mac1); + put_unaligned(cpu_to_be32(mac0), (__be32 *)(addr + 2)); + put_unaligned(cpu_to_be16(mac1), (__be16 *)addr); return is_valid_ether_addr(addr); } @@ -326,22 +326,12 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr) u32 val; /* for example: 00-0B-6A-F6-00-DC * STAD0=6AF600DC, STAD1=000B */ - val = be32_to_cpu(*(__be32 *)(addr + 2)); + val = be32_to_cpu(get_unaligned((__be32 *)(addr + 2))); alx_write_mem32(hw, ALX_STAD0, val); - val = be16_to_cpu(*(__be16 *)addr); + val = be16_to_cpu(get_unaligned((__be16 *)addr)); alx_write_mem32(hw, ALX_STAD1, val); } -static void alx_enable_osc(struct alx_hw *hw) -{ - u32 val; - - /* rising edge */ - val = alx_read_mem32(hw, ALX_MISC); - alx_write_mem32(hw, ALX_MISC, val & ~ALX_MISC_INTNLOSC_OPEN); - alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN); -} - static void alx_reset_osc(struct alx_hw *hw, u8 rev) { u32 val, val2; @@ -624,12 +614,12 @@ void alx_start_mac(struct alx_hw *hw) alx_write_mem32(hw, ALX_TXQ0, txq | ALX_TXQ0_EN); mac = hw->rx_ctrl; - if (hw->link_speed % 10 == DUPLEX_FULL) + if (hw->duplex == DUPLEX_FULL) mac |= ALX_MAC_CTRL_FULLD; else mac &= ~ALX_MAC_CTRL_FULLD; ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, - hw->link_speed >= SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 : + hw->link_speed == SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 : ALX_MAC_CTRL_SPEED_10_100); mac |= ALX_MAC_CTRL_TX_EN | ALX_MAC_CTRL_RX_EN; hw->rx_ctrl = mac; @@ -790,28 +780,22 @@ void alx_post_phy_link(struct alx_hw *hw) u16 phy_val, len, agc; u8 revid = alx_hw_revision(hw); bool adj_th = revid == ALX_REV_B0; - int speed; - - if (hw->link_speed == SPEED_UNKNOWN) - speed = SPEED_UNKNOWN; - else - speed = hw->link_speed - hw->link_speed % 10; if (revid != ALX_REV_B0 && !alx_is_rev_a(revid)) return; /* 1000BT/AZ, wrong cable length */ - if (speed != SPEED_UNKNOWN) { + if (hw->link_speed != SPEED_UNKNOWN) { alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL6, &phy_val); len = ALX_GET_FIELD(phy_val, ALX_CLDCTRL6_CAB_LEN); alx_read_phy_dbg(hw, ALX_MIIDBG_AGC, &phy_val); agc = ALX_GET_FIELD(phy_val, ALX_AGC_2_VGA); - if ((speed == SPEED_1000 && + if ((hw->link_speed == SPEED_1000 && (len > ALX_CLDCTRL6_CAB_LEN_SHORT1G || (len == 0 && agc > ALX_AGC_LONG1G_LIMT))) || - (speed == SPEED_100 && + (hw->link_speed == SPEED_100 && (len > ALX_CLDCTRL6_CAB_LEN_SHORT100M || (len == 0 && agc > ALX_AGC_LONG100M_LIMT)))) { alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT, @@ -831,10 +815,10 @@ void alx_post_phy_link(struct alx_hw *hw) /* threshold adjust */ if (adj_th && hw->lnk_patch) { - if (speed == SPEED_100) { + if (hw->link_speed == SPEED_100) { alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB, ALX_MSE16DB_UP); - } else if (speed == SPEED_1000) { + } else if (hw->link_speed == SPEED_1000) { /* * Giga link threshold, raise the tolerance of * noise 50% @@ -864,66 +848,6 @@ void alx_post_phy_link(struct alx_hw *hw) } } - -/* NOTE: - * 1. phy link must be established before calling this function - * 2. wol option (pattern,magic,link,etc.) is configed before call it. - */ -int alx_pre_suspend(struct alx_hw *hw, int speed) -{ - u32 master, mac, phy, val; - int err = 0; - - master = alx_read_mem32(hw, ALX_MASTER); - master &= ~ALX_MASTER_PCLKSEL_SRDS; - mac = hw->rx_ctrl; - /* 10/100 half */ - ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, ALX_MAC_CTRL_SPEED_10_100); - mac &= ~(ALX_MAC_CTRL_FULLD | ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_TX_EN); - - phy = alx_read_mem32(hw, ALX_PHY_CTRL); - phy &= ~(ALX_PHY_CTRL_DSPRST_OUT | ALX_PHY_CTRL_CLS); - phy |= ALX_PHY_CTRL_RST_ANALOG | ALX_PHY_CTRL_HIB_PULSE | - ALX_PHY_CTRL_HIB_EN; - - /* without any activity */ - if (!(hw->sleep_ctrl & ALX_SLEEP_ACTIVE)) { - err = alx_write_phy_reg(hw, ALX_MII_IER, 0); - if (err) - return err; - phy |= ALX_PHY_CTRL_IDDQ | ALX_PHY_CTRL_POWER_DOWN; - } else { - if (hw->sleep_ctrl & (ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_CIFS)) - mac |= ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_BRD_EN; - if (hw->sleep_ctrl & ALX_SLEEP_CIFS) - mac |= ALX_MAC_CTRL_TX_EN; - if (speed % 10 == DUPLEX_FULL) - mac |= ALX_MAC_CTRL_FULLD; - if (speed >= SPEED_1000) - ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, - ALX_MAC_CTRL_SPEED_1000); - phy |= ALX_PHY_CTRL_DSPRST_OUT; - err = alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, - ALX_MIIEXT_S3DIG10, - ALX_MIIEXT_S3DIG10_SL); - if (err) - return err; - } - - alx_enable_osc(hw); - hw->rx_ctrl = mac; - alx_write_mem32(hw, ALX_MASTER, master); - alx_write_mem32(hw, ALX_MAC_CTRL, mac); - alx_write_mem32(hw, ALX_PHY_CTRL, phy); - - /* set val of PDLL D3PLLOFF */ - val = alx_read_mem32(hw, ALX_PDLL_TRNS1); - val |= ALX_PDLL_TRNS1_D3PLLOFF_EN; - alx_write_mem32(hw, ALX_PDLL_TRNS1, val); - - return 0; -} - bool alx_phy_configured(struct alx_hw *hw) { u32 cfg, hw_cfg; @@ -938,7 +862,7 @@ bool alx_phy_configured(struct alx_hw *hw) return cfg == hw_cfg; } -int alx_get_phy_link(struct alx_hw *hw, int *speed) +int alx_read_phy_link(struct alx_hw *hw) { struct pci_dev *pdev = hw->pdev; u16 bmsr, giga; @@ -953,7 +877,8 @@ int alx_get_phy_link(struct alx_hw *hw, int *speed) return err; if (!(bmsr & BMSR_LSTATUS)) { - *speed = SPEED_UNKNOWN; + hw->link_speed = SPEED_UNKNOWN; + hw->duplex = DUPLEX_UNKNOWN; return 0; } @@ -967,20 +892,20 @@ int alx_get_phy_link(struct alx_hw *hw, int *speed) switch (giga & ALX_GIGA_PSSR_SPEED) { case ALX_GIGA_PSSR_1000MBS: - *speed = SPEED_1000; + hw->link_speed = SPEED_1000; break; case ALX_GIGA_PSSR_100MBS: - *speed = SPEED_100; + hw->link_speed = SPEED_100; break; case ALX_GIGA_PSSR_10MBS: - *speed = SPEED_10; + hw->link_speed = SPEED_10; break; default: goto wrong_speed; } - *speed += (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF; - return 1; + hw->duplex = (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF; + return 0; wrong_speed: dev_err(&pdev->dev, "invalid PHY speed/duplex: 0x%x\n", giga); @@ -995,26 +920,6 @@ int alx_clear_phy_intr(struct alx_hw *hw) return alx_read_phy_reg(hw, ALX_MII_ISR, &isr); } -int alx_config_wol(struct alx_hw *hw) -{ - u32 wol = 0; - int err = 0; - - /* turn on magic packet event */ - if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) - wol |= ALX_WOL0_MAGIC_EN | ALX_WOL0_PME_MAGIC_EN; - - /* turn on link up event */ - if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) { - wol |= ALX_WOL0_LINK_EN | ALX_WOL0_PME_LINK; - /* only link up can wake up */ - err = alx_write_phy_reg(hw, ALX_MII_IER, ALX_IER_LINK_UP); - } - alx_write_mem32(hw, ALX_WOL0, wol); - - return err; -} - void alx_disable_rss(struct alx_hw *hw) { u32 ctrl = alx_read_mem32(hw, ALX_RXQ0); @@ -1126,85 +1031,6 @@ void alx_configure_basic(struct alx_hw *hw) alx_write_mem32(hw, ALX_WRR, val); } -static inline u32 alx_speed_to_ethadv(int speed) -{ - switch (speed) { - case SPEED_1000 + DUPLEX_FULL: - return ADVERTISED_1000baseT_Full; - case SPEED_100 + DUPLEX_FULL: - return ADVERTISED_100baseT_Full; - case SPEED_100 + DUPLEX_HALF: - return ADVERTISED_10baseT_Half; - case SPEED_10 + DUPLEX_FULL: - return ADVERTISED_10baseT_Full; - case SPEED_10 + DUPLEX_HALF: - return ADVERTISED_10baseT_Half; - default: - return 0; - } -} - -int alx_select_powersaving_speed(struct alx_hw *hw, int *speed) -{ - int i, err, spd; - u16 lpa; - - err = alx_get_phy_link(hw, &spd); - if (err < 0) - return err; - - if (spd == SPEED_UNKNOWN) - return 0; - - err = alx_read_phy_reg(hw, MII_LPA, &lpa); - if (err) - return err; - - if (!(lpa & LPA_LPACK)) { - *speed = spd; - return 0; - } - - if (lpa & LPA_10FULL) - *speed = SPEED_10 + DUPLEX_FULL; - else if (lpa & LPA_10HALF) - *speed = SPEED_10 + DUPLEX_HALF; - else if (lpa & LPA_100FULL) - *speed = SPEED_100 + DUPLEX_FULL; - else - *speed = SPEED_100 + DUPLEX_HALF; - - if (*speed != spd) { - err = alx_write_phy_reg(hw, ALX_MII_IER, 0); - if (err) - return err; - err = alx_setup_speed_duplex(hw, - alx_speed_to_ethadv(*speed) | - ADVERTISED_Autoneg, - ALX_FC_ANEG | ALX_FC_RX | - ALX_FC_TX); - if (err) - return err; - - /* wait for linkup */ - for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { - int speed2; - - msleep(100); - - err = alx_get_phy_link(hw, &speed2); - if (err < 0) - return err; - if (speed2 != SPEED_UNKNOWN) - break; - } - if (i == ALX_MAX_SETUP_LNK_CYCLE) - return -ETIMEDOUT; - } - - return 0; -} - bool alx_get_phy_info(struct alx_hw *hw) { u16 devs1, devs2; diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h index 65e723d2172..96f3b4381e1 100644 --- a/drivers/net/ethernet/atheros/alx/hw.h +++ b/drivers/net/ethernet/atheros/alx/hw.h @@ -412,12 +412,11 @@ struct alx_hw { u32 smb_timer; /* SPEED_* + DUPLEX_*, SPEED_UNKNOWN if link is down */ int link_speed; + u8 duplex; /* auto-neg advertisement or force mode config */ - u32 adv_cfg; u8 flowctrl; - - u32 sleep_ctrl; + u32 adv_cfg; spinlock_t mdio_lock; struct mdio_if_info mdio; @@ -478,14 +477,12 @@ void alx_reset_pcie(struct alx_hw *hw); void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en); int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl); void alx_post_phy_link(struct alx_hw *hw); -int alx_pre_suspend(struct alx_hw *hw, int speed); int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data); int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data); int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata); int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data); -int alx_get_phy_link(struct alx_hw *hw, int *speed); +int alx_read_phy_link(struct alx_hw *hw); int alx_clear_phy_intr(struct alx_hw *hw); -int alx_config_wol(struct alx_hw *hw); void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc); void alx_start_mac(struct alx_hw *hw); int alx_reset_mac(struct alx_hw *hw); @@ -493,7 +490,21 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr); bool alx_phy_configured(struct alx_hw *hw); void alx_configure_basic(struct alx_hw *hw); void alx_disable_rss(struct alx_hw *hw); -int alx_select_powersaving_speed(struct alx_hw *hw, int *speed); bool alx_get_phy_info(struct alx_hw *hw); +static inline u32 alx_speed_to_ethadv(int speed, u8 duplex) +{ + if (speed == SPEED_1000 && duplex == DUPLEX_FULL) + return ADVERTISED_1000baseT_Full; + if (speed == SPEED_100 && duplex == DUPLEX_FULL) + return ADVERTISED_100baseT_Full; + if (speed == SPEED_100 && duplex== DUPLEX_HALF) + return ADVERTISED_100baseT_Half; + if (speed == SPEED_10 && duplex == DUPLEX_FULL) + return ADVERTISED_10baseT_Full; + if (speed == SPEED_10 && duplex == DUPLEX_HALF) + return ADVERTISED_10baseT_Half; + return 0; +} + #endif diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 418de8b1316..0e0b242a9dd 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -706,12 +706,12 @@ static int alx_init_sw(struct alx_priv *alx) alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8); alx->tx_ringsz = 256; alx->rx_ringsz = 512; - hw->sleep_ctrl = ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_WOL_PHY; hw->imt = 200; alx->int_mask = ALX_ISR_MISC; hw->dma_chnl = hw->max_dma_chnl; hw->ith_tpd = alx->tx_ringsz / 3; hw->link_speed = SPEED_UNKNOWN; + hw->duplex = DUPLEX_UNKNOWN; hw->adv_cfg = ADVERTISED_Autoneg | ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | @@ -758,6 +758,7 @@ static void alx_halt(struct alx_priv *alx) alx_netif_stop(alx); hw->link_speed = SPEED_UNKNOWN; + hw->duplex = DUPLEX_UNKNOWN; alx_reset_mac(hw); @@ -869,18 +870,18 @@ static void __alx_stop(struct alx_priv *alx) alx_free_rings(alx); } -static const char *alx_speed_desc(u16 speed) +static const char *alx_speed_desc(struct alx_hw *hw) { - switch (speed) { - case SPEED_1000 + DUPLEX_FULL: + switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) { + case ADVERTISED_1000baseT_Full: return "1 Gbps Full"; - case SPEED_100 + DUPLEX_FULL: + case ADVERTISED_100baseT_Full: return "100 Mbps Full"; - case SPEED_100 + DUPLEX_HALF: + case ADVERTISED_100baseT_Half: return "100 Mbps Half"; - case SPEED_10 + DUPLEX_FULL: + case ADVERTISED_10baseT_Full: return "10 Mbps Full"; - case SPEED_10 + DUPLEX_HALF: + case ADVERTISED_10baseT_Half: return "10 Mbps Half"; default: return "Unknown speed"; @@ -891,7 +892,8 @@ static void alx_check_link(struct alx_priv *alx) { struct alx_hw *hw = &alx->hw; unsigned long flags; - int speed, old_speed; + int old_speed; + u8 old_duplex; int err; /* clear PHY internal interrupt status, otherwise the main @@ -899,7 +901,9 @@ static void alx_check_link(struct alx_priv *alx) */ alx_clear_phy_intr(hw); - err = alx_get_phy_link(hw, &speed); + old_speed = hw->link_speed; + old_duplex = hw->duplex; + err = alx_read_phy_link(hw); if (err < 0) goto reset; @@ -908,15 +912,12 @@ static void alx_check_link(struct alx_priv *alx) alx_write_mem32(hw, ALX_IMR, alx->int_mask); spin_unlock_irqrestore(&alx->irq_lock, flags); - old_speed = hw->link_speed; - - if (old_speed == speed) + if (old_speed == hw->link_speed) return; - hw->link_speed = speed; - if (speed != SPEED_UNKNOWN) { + if (hw->link_speed != SPEED_UNKNOWN) { netif_info(alx, link, alx->dev, - "NIC Up: %s\n", alx_speed_desc(speed)); + "NIC Up: %s\n", alx_speed_desc(hw)); alx_post_phy_link(hw); alx_enable_aspm(hw, true, true); alx_start_mac(hw); @@ -959,65 +960,6 @@ static int alx_stop(struct net_device *netdev) return 0; } -static int __alx_shutdown(struct pci_dev *pdev, bool *wol_en) -{ - struct alx_priv *alx = pci_get_drvdata(pdev); - struct net_device *netdev = alx->dev; - struct alx_hw *hw = &alx->hw; - int err, speed; - - netif_device_detach(netdev); - - if (netif_running(netdev)) - __alx_stop(alx); - -#ifdef CONFIG_PM_SLEEP - err = pci_save_state(pdev); - if (err) - return err; -#endif - - err = alx_select_powersaving_speed(hw, &speed); - if (err) - return err; - err = alx_clear_phy_intr(hw); - if (err) - return err; - err = alx_pre_suspend(hw, speed); - if (err) - return err; - err = alx_config_wol(hw); - if (err) - return err; - - *wol_en = false; - if (hw->sleep_ctrl & ALX_SLEEP_ACTIVE) { - netif_info(alx, wol, netdev, - "wol: ctrl=%X, speed=%X\n", - hw->sleep_ctrl, speed); - device_set_wakeup_enable(&pdev->dev, true); - *wol_en = true; - } - - pci_disable_device(pdev); - - return 0; -} - -static void alx_shutdown(struct pci_dev *pdev) -{ - int err; - bool wol_en; - - err = __alx_shutdown(pdev, &wol_en); - if (!err) { - pci_wake_from_d3(pdev, wol_en); - pci_set_power_state(pdev, PCI_D3hot); - } else { - dev_err(&pdev->dev, "shutdown fail %d\n", err); - } -} - static void alx_link_check(struct work_struct *work) { struct alx_priv *alx; @@ -1396,8 +1338,6 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_unmap; } - device_set_wakeup_enable(&pdev->dev, hw->sleep_ctrl); - netdev_info(netdev, "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n", netdev->dev_addr); @@ -1442,22 +1382,12 @@ static void alx_remove(struct pci_dev *pdev) static int alx_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); - int err; - bool wol_en; - - err = __alx_shutdown(pdev, &wol_en); - if (err) { - dev_err(&pdev->dev, "shutdown fail in suspend %d\n", err); - return err; - } - - if (wol_en) { - pci_prepare_to_sleep(pdev); - } else { - pci_wake_from_d3(pdev, false); - pci_set_power_state(pdev, PCI_D3hot); - } + struct alx_priv *alx = pci_get_drvdata(pdev); + if (!netif_running(alx->dev)) + return 0; + netif_device_detach(alx->dev); + __alx_stop(alx); return 0; } @@ -1465,49 +1395,20 @@ static int alx_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct alx_priv *alx = pci_get_drvdata(pdev); - struct net_device *netdev = alx->dev; - struct alx_hw *hw = &alx->hw; - int err; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_save_state(pdev); - - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); - hw->link_speed = SPEED_UNKNOWN; - alx->int_mask = ALX_ISR_MISC; - - alx_reset_pcie(hw); - alx_reset_phy(hw); - - err = alx_reset_mac(hw); - if (err) { - netif_err(alx, hw, alx->dev, - "resume:reset_mac fail %d\n", err); - return -EIO; - } - - err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl); - if (err) { - netif_err(alx, hw, alx->dev, - "resume:setup_speed_duplex fail %d\n", err); - return -EIO; - } - - if (netif_running(netdev)) { - err = __alx_open(alx, true); - if (err) - return err; - } - - netif_device_attach(netdev); - - return err; + if (!netif_running(alx->dev)) + return 0; + netif_device_attach(alx->dev); + return __alx_open(alx, true); } + +static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); +#define ALX_PM_OPS (&alx_pm_ops) +#else +#define ALX_PM_OPS NULL #endif + static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { @@ -1550,8 +1451,6 @@ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) } pci_set_master(pdev); - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); alx_reset_pcie(hw); if (!alx_reset_mac(hw)) @@ -1587,13 +1486,6 @@ static const struct pci_error_handlers alx_err_handlers = { .resume = alx_pci_error_resume, }; -#ifdef CONFIG_PM_SLEEP -static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); -#define ALX_PM_OPS (&alx_pm_ops) -#else -#define ALX_PM_OPS NULL -#endif - static DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = { { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161), .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, @@ -1611,7 +1503,6 @@ static struct pci_driver alx_driver = { .id_table = alx_pci_tbl, .probe = alx_probe, .remove = alx_remove, - .shutdown = alx_shutdown, .err_handler = &alx_err_handlers, .driver.pm = ALX_PM_OPS, }; diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index 0ba900762b1..786a8748329 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -2755,27 +2755,4 @@ static struct pci_driver atl1c_driver = { .driver.pm = &atl1c_pm_ops, }; -/** - * atl1c_init_module - Driver Registration Routine - * - * atl1c_init_module is the first routine called when the driver is - * loaded. All it does is register with the PCI subsystem. - */ -static int __init atl1c_init_module(void) -{ - return pci_register_driver(&atl1c_driver); -} - -/** - * atl1c_exit_module - Driver Exit Cleanup Routine - * - * atl1c_exit_module is called just before the driver is removed - * from memory. - */ -static void __exit atl1c_exit_module(void) -{ - pci_unregister_driver(&atl1c_driver); -} - -module_init(atl1c_init_module); -module_exit(atl1c_exit_module); +module_pci_driver(atl1c_driver); diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 0688bb82b44..895f5377ad1 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -2489,27 +2489,4 @@ static struct pci_driver atl1e_driver = { .err_handler = &atl1e_err_handler }; -/** - * atl1e_init_module - Driver Registration Routine - * - * atl1e_init_module is the first routine called when the driver is - * loaded. All it does is register with the PCI subsystem. - */ -static int __init atl1e_init_module(void) -{ - return pci_register_driver(&atl1e_driver); -} - -/** - * atl1e_exit_module - Driver Exit Cleanup Routine - * - * atl1e_exit_module is called just before the driver is removed - * from memory. - */ -static void __exit atl1e_exit_module(void) -{ - pci_unregister_driver(&atl1e_driver); -} - -module_init(atl1e_init_module); -module_exit(atl1e_exit_module); +module_pci_driver(atl1e_driver); diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index fa0915f3999..538211d6f7d 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3145,31 +3145,6 @@ static struct pci_driver atl1_driver = { .driver.pm = &atl1_pm_ops, }; -/** - * atl1_exit_module - Driver Exit Cleanup Routine - * - * atl1_exit_module is called just before the driver is removed - * from memory. - */ -static void __exit atl1_exit_module(void) -{ - pci_unregister_driver(&atl1_driver); -} - -/** - * atl1_init_module - Driver Registration Routine - * - * atl1_init_module is the first routine called when the driver is - * loaded. All it does is register with the PCI subsystem. - */ -static int __init atl1_init_module(void) -{ - return pci_register_driver(&atl1_driver); -} - -module_init(atl1_init_module); -module_exit(atl1_exit_module); - struct atl1_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -3705,3 +3680,5 @@ static const struct ethtool_ops atl1_ethtool_ops = { .get_ethtool_stats = atl1_get_ethtool_stats, .get_sset_count = atl1_get_sset_count, }; + +module_pci_driver(atl1_driver); diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 3e69b3f8809..1d680baf43d 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -22,7 +22,6 @@ config B44 tristate "Broadcom 440x/47xx ethernet support" depends on SSB_POSSIBLE && HAS_DMA select SSB - select NET_CORE select MII ---help--- If you have a network (Ethernet) controller of this type, say Y @@ -54,7 +53,6 @@ config B44_PCI config BCM63XX_ENET tristate "Broadcom 63xx internal mac support" depends on BCM63XX - select NET_CORE select MII select PHYLIB help diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 0b3e23ec37f..b1bcd4ba474 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -41,8 +41,8 @@ static int copybreak __read_mostly = 128; module_param(copybreak, int, 0); MODULE_PARM_DESC(copybreak, "Receive copy threshold"); -/* io memory shared between all devices */ -static void __iomem *bcm_enet_shared_base; +/* io registers memory shared between all devices */ +static void __iomem *bcm_enet_shared_base[3]; /* * io helpers to access mac registers @@ -59,17 +59,76 @@ static inline void enet_writel(struct bcm_enet_priv *priv, } /* - * io helpers to access shared registers + * io helpers to access switch registers */ +static inline u32 enetsw_readl(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readl(priv->base + off); +} + +static inline void enetsw_writel(struct bcm_enet_priv *priv, + u32 val, u32 off) +{ + bcm_writel(val, priv->base + off); +} + +static inline u16 enetsw_readw(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readw(priv->base + off); +} + +static inline void enetsw_writew(struct bcm_enet_priv *priv, + u16 val, u32 off) +{ + bcm_writew(val, priv->base + off); +} + +static inline u8 enetsw_readb(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readb(priv->base + off); +} + +static inline void enetsw_writeb(struct bcm_enet_priv *priv, + u8 val, u32 off) +{ + bcm_writeb(val, priv->base + off); +} + + +/* io helpers to access shared registers */ static inline u32 enet_dma_readl(struct bcm_enet_priv *priv, u32 off) { - return bcm_readl(bcm_enet_shared_base + off); + return bcm_readl(bcm_enet_shared_base[0] + off); } static inline void enet_dma_writel(struct bcm_enet_priv *priv, u32 val, u32 off) { - bcm_writel(val, bcm_enet_shared_base + off); + bcm_writel(val, bcm_enet_shared_base[0] + off); +} + +static inline u32 enet_dmac_readl(struct bcm_enet_priv *priv, u32 off, int chan) +{ + return bcm_readl(bcm_enet_shared_base[1] + + bcm63xx_enetdmacreg(off) + chan * priv->dma_chan_width); +} + +static inline void enet_dmac_writel(struct bcm_enet_priv *priv, + u32 val, u32 off, int chan) +{ + bcm_writel(val, bcm_enet_shared_base[1] + + bcm63xx_enetdmacreg(off) + chan * priv->dma_chan_width); +} + +static inline u32 enet_dmas_readl(struct bcm_enet_priv *priv, u32 off, int chan) +{ + return bcm_readl(bcm_enet_shared_base[2] + off + chan * priv->dma_chan_width); +} + +static inline void enet_dmas_writel(struct bcm_enet_priv *priv, + u32 val, u32 off, int chan) +{ + bcm_writel(val, bcm_enet_shared_base[2] + off + chan * priv->dma_chan_width); } /* @@ -196,7 +255,6 @@ static int bcm_enet_refill_rx(struct net_device *dev) if (!skb) break; priv->rx_skb[desc_idx] = skb; - p = dma_map_single(&priv->pdev->dev, skb->data, priv->rx_skb_size, DMA_FROM_DEVICE); @@ -206,7 +264,7 @@ static int bcm_enet_refill_rx(struct net_device *dev) len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT; len_stat |= DMADESC_OWNER_MASK; if (priv->rx_dirty_desc == priv->rx_ring_size - 1) { - len_stat |= DMADESC_WRAP_MASK; + len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift); priv->rx_dirty_desc = 0; } else { priv->rx_dirty_desc++; @@ -217,7 +275,10 @@ static int bcm_enet_refill_rx(struct net_device *dev) priv->rx_desc_count++; /* tell dma engine we allocated one buffer */ - enet_dma_writel(priv, 1, ENETDMA_BUFALLOC_REG(priv->rx_chan)); + if (priv->dma_has_sram) + enet_dma_writel(priv, 1, ENETDMA_BUFALLOC_REG(priv->rx_chan)); + else + enet_dmac_writel(priv, 1, ENETDMAC_BUFALLOC, priv->rx_chan); } /* If rx ring is still empty, set a timer to try allocating @@ -293,13 +354,15 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) /* if the packet does not have start of packet _and_ * end of packet flag set, then just recycle it */ - if ((len_stat & DMADESC_ESOP_MASK) != DMADESC_ESOP_MASK) { + if ((len_stat & (DMADESC_ESOP_MASK >> priv->dma_desc_shift)) != + (DMADESC_ESOP_MASK >> priv->dma_desc_shift)) { dev->stats.rx_dropped++; continue; } /* recycle packet if it's marked as bad */ - if (unlikely(len_stat & DMADESC_ERR_MASK)) { + if (!priv->enet_is_sw && + unlikely(len_stat & DMADESC_ERR_MASK)) { dev->stats.rx_errors++; if (len_stat & DMADESC_OVSIZE_MASK) @@ -353,8 +416,8 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) bcm_enet_refill_rx(dev); /* kick rx dma */ - enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK, - ENETDMA_CHANCFG_REG(priv->rx_chan)); + enet_dmac_writel(priv, priv->dma_chan_en_mask, + ENETDMAC_CHANCFG, priv->rx_chan); } return processed; @@ -429,10 +492,10 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget) dev = priv->net_dev; /* ack interrupts */ - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->tx_chan)); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IR, priv->rx_chan); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IR, priv->tx_chan); /* reclaim sent skb */ tx_work_done = bcm_enet_tx_reclaim(dev, 0); @@ -451,10 +514,10 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget) napi_complete(napi); /* restore rx/tx interrupt */ - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IRMASK, priv->tx_chan); return rx_work_done; } @@ -497,8 +560,8 @@ static irqreturn_t bcm_enet_isr_dma(int irq, void *dev_id) priv = netdev_priv(dev); /* mask rx/tx interrupts */ - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); napi_schedule(&priv->napi); @@ -530,6 +593,26 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) goto out_unlock; } + /* pad small packets sent on a switch device */ + if (priv->enet_is_sw && skb->len < 64) { + int needed = 64 - skb->len; + char *data; + + if (unlikely(skb_tailroom(skb) < needed)) { + struct sk_buff *nskb; + + nskb = skb_copy_expand(skb, 0, needed, GFP_ATOMIC); + if (!nskb) { + ret = NETDEV_TX_BUSY; + goto out_unlock; + } + dev_kfree_skb(skb); + skb = nskb; + } + data = skb_put(skb, needed); + memset(data, 0, needed); + } + /* point to the next available desc */ desc = &priv->tx_desc_cpu[priv->tx_curr_desc]; priv->tx_skb[priv->tx_curr_desc] = skb; @@ -539,14 +622,14 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) DMA_TO_DEVICE); len_stat = (skb->len << DMADESC_LENGTH_SHIFT) & DMADESC_LENGTH_MASK; - len_stat |= DMADESC_ESOP_MASK | + len_stat |= (DMADESC_ESOP_MASK >> priv->dma_desc_shift) | DMADESC_APPEND_CRC | DMADESC_OWNER_MASK; priv->tx_curr_desc++; if (priv->tx_curr_desc == priv->tx_ring_size) { priv->tx_curr_desc = 0; - len_stat |= DMADESC_WRAP_MASK; + len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift); } priv->tx_desc_count--; @@ -557,8 +640,8 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) wmb(); /* kick tx dma */ - enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK, - ENETDMA_CHANCFG_REG(priv->tx_chan)); + enet_dmac_writel(priv, priv->dma_chan_en_mask, + ENETDMAC_CHANCFG, priv->tx_chan); /* stop queue if no more desc available */ if (!priv->tx_desc_count) @@ -686,6 +769,9 @@ static void bcm_enet_set_flow(struct bcm_enet_priv *priv, int rx_en, int tx_en) val &= ~ENET_RXCFG_ENFLOW_MASK; enet_writel(priv, val, ENET_RXCFG_REG); + if (!priv->dma_has_sram) + return; + /* tx flow control (pause frame generation) */ val = enet_dma_readl(priv, ENETDMA_CFG_REG); if (tx_en) @@ -833,8 +919,8 @@ static int bcm_enet_open(struct net_device *dev) /* mask all interrupts and request them */ enet_writel(priv, 0, ENET_IRMASK_REG); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); ret = request_irq(dev->irq, bcm_enet_isr_mac, 0, dev->name, dev); if (ret) @@ -909,8 +995,12 @@ static int bcm_enet_open(struct net_device *dev) priv->rx_curr_desc = 0; /* initialize flow control buffer allocation */ - enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, - ENETDMA_BUFALLOC_REG(priv->rx_chan)); + if (priv->dma_has_sram) + enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, + ENETDMA_BUFALLOC_REG(priv->rx_chan)); + else + enet_dmac_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, + ENETDMAC_BUFALLOC, priv->rx_chan); if (bcm_enet_refill_rx(dev)) { dev_err(kdev, "cannot allocate rx skb queue\n"); @@ -919,37 +1009,55 @@ static int bcm_enet_open(struct net_device *dev) } /* write rx & tx ring addresses */ - enet_dma_writel(priv, priv->rx_desc_dma, - ENETDMA_RSTART_REG(priv->rx_chan)); - enet_dma_writel(priv, priv->tx_desc_dma, - ENETDMA_RSTART_REG(priv->tx_chan)); + if (priv->dma_has_sram) { + enet_dmas_writel(priv, priv->rx_desc_dma, + ENETDMAS_RSTART_REG, priv->rx_chan); + enet_dmas_writel(priv, priv->tx_desc_dma, + ENETDMAS_RSTART_REG, priv->tx_chan); + } else { + enet_dmac_writel(priv, priv->rx_desc_dma, + ENETDMAC_RSTART, priv->rx_chan); + enet_dmac_writel(priv, priv->tx_desc_dma, + ENETDMAC_RSTART, priv->tx_chan); + } /* clear remaining state ram for rx & tx channel */ - enet_dma_writel(priv, 0, ENETDMA_SRAM2_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM2_REG(priv->tx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM3_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM3_REG(priv->tx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->tx_chan)); + if (priv->dma_has_sram) { + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->rx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->tx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->rx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->tx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->rx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->tx_chan); + } else { + enet_dmac_writel(priv, 0, ENETDMAC_FC, priv->rx_chan); + enet_dmac_writel(priv, 0, ENETDMAC_FC, priv->tx_chan); + } /* set max rx/tx length */ enet_writel(priv, priv->hw_mtu, ENET_RXMAXLEN_REG); enet_writel(priv, priv->hw_mtu, ENET_TXMAXLEN_REG); /* set dma maximum burst len */ - enet_dma_writel(priv, BCMENET_DMA_MAXBURST, - ENETDMA_MAXBURST_REG(priv->rx_chan)); - enet_dma_writel(priv, BCMENET_DMA_MAXBURST, - ENETDMA_MAXBURST_REG(priv->tx_chan)); + enet_dmac_writel(priv, priv->dma_maxburst, + ENETDMAC_MAXBURST, priv->rx_chan); + enet_dmac_writel(priv, priv->dma_maxburst, + ENETDMAC_MAXBURST, priv->tx_chan); /* set correct transmit fifo watermark */ enet_writel(priv, BCMENET_TX_FIFO_TRESH, ENET_TXWMARK_REG); /* set flow control low/high threshold to 1/3 / 2/3 */ - val = priv->rx_ring_size / 3; - enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan)); - val = (priv->rx_ring_size * 2) / 3; - enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan)); + if (priv->dma_has_sram) { + val = priv->rx_ring_size / 3; + enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan)); + val = (priv->rx_ring_size * 2) / 3; + enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan)); + } else { + enet_dmac_writel(priv, 5, ENETDMAC_FC, priv->rx_chan); + enet_dmac_writel(priv, priv->rx_ring_size, ENETDMAC_LEN, priv->rx_chan); + enet_dmac_writel(priv, priv->tx_ring_size, ENETDMAC_LEN, priv->tx_chan); + } /* all set, enable mac and interrupts, start dma engine and * kick rx dma channel */ @@ -958,26 +1066,26 @@ static int bcm_enet_open(struct net_device *dev) val |= ENET_CTL_ENABLE_MASK; enet_writel(priv, val, ENET_CTL_REG); enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); - enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK, - ENETDMA_CHANCFG_REG(priv->rx_chan)); + enet_dmac_writel(priv, priv->dma_chan_en_mask, + ENETDMAC_CHANCFG, priv->rx_chan); /* watch "mib counters about to overflow" interrupt */ enet_writel(priv, ENET_IR_MIB, ENET_IR_REG); enet_writel(priv, ENET_IR_MIB, ENET_IRMASK_REG); /* watch "packet transferred" interrupt in rx and tx */ - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->tx_chan)); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IR, priv->rx_chan); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IR, priv->tx_chan); /* make sure we enable napi before rx interrupt */ napi_enable(&priv->napi); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, priv->dma_chan_int_mask, + ENETDMAC_IRMASK, priv->tx_chan); if (priv->has_phy) phy_start(priv->phydev); @@ -1057,14 +1165,14 @@ static void bcm_enet_disable_dma(struct bcm_enet_priv *priv, int chan) { int limit; - enet_dma_writel(priv, 0, ENETDMA_CHANCFG_REG(chan)); + enet_dmac_writel(priv, 0, ENETDMAC_CHANCFG, chan); limit = 1000; do { u32 val; - val = enet_dma_readl(priv, ENETDMA_CHANCFG_REG(chan)); - if (!(val & ENETDMA_CHANCFG_EN_MASK)) + val = enet_dmac_readl(priv, ENETDMAC_CHANCFG, chan); + if (!(val & ENETDMAC_CHANCFG_EN_MASK)) break; udelay(1); } while (limit--); @@ -1090,8 +1198,8 @@ static int bcm_enet_stop(struct net_device *dev) /* mask all interrupts */ enet_writel(priv, 0, ENET_IRMASK_REG); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); /* make sure no mib update is scheduled */ cancel_work_sync(&priv->mib_update_task); @@ -1328,6 +1436,20 @@ static void bcm_enet_get_ethtool_stats(struct net_device *netdev, mutex_unlock(&priv->mib_update_lock); } +static int bcm_enet_nway_reset(struct net_device *dev) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + if (priv->has_phy) { + if (!priv->phydev) + return -ENODEV; + return genphy_restart_aneg(priv->phydev); + } + + return -EOPNOTSUPP; +} + static int bcm_enet_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { @@ -1470,6 +1592,7 @@ static const struct ethtool_ops bcm_enet_ethtool_ops = { .get_strings = bcm_enet_get_strings, .get_sset_count = bcm_enet_get_sset_count, .get_ethtool_stats = bcm_enet_get_ethtool_stats, + .nway_reset = bcm_enet_nway_reset, .get_settings = bcm_enet_get_settings, .set_settings = bcm_enet_set_settings, .get_drvinfo = bcm_enet_get_drvinfo, @@ -1530,7 +1653,7 @@ static int compute_hw_mtu(struct bcm_enet_priv *priv, int mtu) * it's appended */ priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN, - BCMENET_DMA_MAXBURST * 4); + priv->dma_maxburst * 4); return 0; } @@ -1621,7 +1744,7 @@ static int bcm_enet_probe(struct platform_device *pdev) /* stop if shared driver failed, assume driver->probe will be * called in the same order we register devices (correct ?) */ - if (!bcm_enet_shared_base) + if (!bcm_enet_shared_base[0]) return -ENODEV; res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1637,6 +1760,9 @@ static int bcm_enet_probe(struct platform_device *pdev) return -ENOMEM; priv = netdev_priv(dev); + priv->enet_is_sw = false; + priv->dma_maxburst = BCMENET_DMA_MAXBURST; + ret = compute_hw_mtu(priv, dev->mtu); if (ret) goto out; @@ -1687,6 +1813,11 @@ static int bcm_enet_probe(struct platform_device *pdev) priv->pause_tx = pd->pause_tx; priv->force_duplex_full = pd->force_duplex_full; priv->force_speed_100 = pd->force_speed_100; + priv->dma_chan_en_mask = pd->dma_chan_en_mask; + priv->dma_chan_int_mask = pd->dma_chan_int_mask; + priv->dma_chan_width = pd->dma_chan_width; + priv->dma_has_sram = pd->dma_has_sram; + priv->dma_desc_shift = pd->dma_desc_shift; } if (priv->mac_id == 0 && priv->has_phy && !priv->use_external_mii) { @@ -1847,7 +1978,6 @@ static int bcm_enet_remove(struct platform_device *pdev) clk_disable_unprepare(priv->mac_clk); clk_put(priv->mac_clk); - platform_set_drvdata(pdev, NULL); free_netdev(dev); return 0; } @@ -1862,19 +1992,881 @@ struct platform_driver bcm63xx_enet_driver = { }; /* - * reserve & remap memory space shared between all macs + * switch mii access callbacks */ -static int bcm_enet_shared_probe(struct platform_device *pdev) +static int bcmenet_sw_mdio_read(struct bcm_enet_priv *priv, + int ext, int phy_id, int location) { - struct resource *res; + u32 reg; + int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) + spin_lock_bh(&priv->enetsw_mdio_lock); + enetsw_writel(priv, 0, ENETSW_MDIOC_REG); + + reg = ENETSW_MDIOC_RD_MASK | + (phy_id << ENETSW_MDIOC_PHYID_SHIFT) | + (location << ENETSW_MDIOC_REG_SHIFT); + + if (ext) + reg |= ENETSW_MDIOC_EXT_MASK; + + enetsw_writel(priv, reg, ENETSW_MDIOC_REG); + udelay(50); + ret = enetsw_readw(priv, ENETSW_MDIOD_REG); + spin_unlock_bh(&priv->enetsw_mdio_lock); + return ret; +} + +static void bcmenet_sw_mdio_write(struct bcm_enet_priv *priv, + int ext, int phy_id, int location, + uint16_t data) +{ + u32 reg; + + spin_lock_bh(&priv->enetsw_mdio_lock); + enetsw_writel(priv, 0, ENETSW_MDIOC_REG); + + reg = ENETSW_MDIOC_WR_MASK | + (phy_id << ENETSW_MDIOC_PHYID_SHIFT) | + (location << ENETSW_MDIOC_REG_SHIFT); + + if (ext) + reg |= ENETSW_MDIOC_EXT_MASK; + + reg |= data; + + enetsw_writel(priv, reg, ENETSW_MDIOC_REG); + udelay(50); + spin_unlock_bh(&priv->enetsw_mdio_lock); +} + +static inline int bcm_enet_port_is_rgmii(int portid) +{ + return portid >= ENETSW_RGMII_PORT0; +} + +/* + * enet sw PHY polling + */ +static void swphy_poll_timer(unsigned long data) +{ + struct bcm_enet_priv *priv = (struct bcm_enet_priv *)data; + unsigned int i; + + for (i = 0; i < priv->num_ports; i++) { + struct bcm63xx_enetsw_port *port; + int val, j, up, advertise, lpa, lpa2, speed, duplex, media; + int external_phy = bcm_enet_port_is_rgmii(i); + u8 override; + + port = &priv->used_ports[i]; + if (!port->used) + continue; + + if (port->bypass_link) + continue; + + /* dummy read to clear */ + for (j = 0; j < 2; j++) + val = bcmenet_sw_mdio_read(priv, external_phy, + port->phy_id, MII_BMSR); + + if (val == 0xffff) + continue; + + up = (val & BMSR_LSTATUS) ? 1 : 0; + if (!(up ^ priv->sw_port_link[i])) + continue; + + priv->sw_port_link[i] = up; + + /* link changed */ + if (!up) { + dev_info(&priv->pdev->dev, "link DOWN on %s\n", + port->name); + enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK, + ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK | + ENETSW_PTCTRL_TXDIS_MASK, + ENETSW_PTCTRL_REG(i)); + continue; + } + + advertise = bcmenet_sw_mdio_read(priv, external_phy, + port->phy_id, MII_ADVERTISE); + + lpa = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id, + MII_LPA); + + lpa2 = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id, + MII_STAT1000); + + /* figure out media and duplex from advertise and LPA values */ + media = mii_nway_result(lpa & advertise); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + if (lpa2 & LPA_1000FULL) + duplex = 1; + + if (lpa2 & (LPA_1000FULL | LPA_1000HALF)) + speed = 1000; + else { + if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) + speed = 100; + else + speed = 10; + } + + dev_info(&priv->pdev->dev, + "link UP on %s, %dMbps, %s-duplex\n", + port->name, speed, duplex ? "full" : "half"); + + override = ENETSW_PORTOV_ENABLE_MASK | + ENETSW_PORTOV_LINKUP_MASK; + + if (speed == 1000) + override |= ENETSW_IMPOV_1000_MASK; + else if (speed == 100) + override |= ENETSW_IMPOV_100_MASK; + if (duplex) + override |= ENETSW_IMPOV_FDX_MASK; + + enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i)); + } + + priv->swphy_poll.expires = jiffies + HZ; + add_timer(&priv->swphy_poll); +} + +/* + * open callback, allocate dma rings & buffers and start rx operation + */ +static int bcm_enetsw_open(struct net_device *dev) +{ + struct bcm_enet_priv *priv; + struct device *kdev; + int i, ret; + unsigned int size; + void *p; + u32 val; + + priv = netdev_priv(dev); + kdev = &priv->pdev->dev; + + /* mask all interrupts and request them */ + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); + + ret = request_irq(priv->irq_rx, bcm_enet_isr_dma, + IRQF_DISABLED, dev->name, dev); + if (ret) + goto out_freeirq; + + if (priv->irq_tx != -1) { + ret = request_irq(priv->irq_tx, bcm_enet_isr_dma, + IRQF_DISABLED, dev->name, dev); + if (ret) + goto out_freeirq_rx; + } + + /* allocate rx dma ring */ + size = priv->rx_ring_size * sizeof(struct bcm_enet_desc); + p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL); + if (!p) { + dev_err(kdev, "cannot allocate rx ring %u\n", size); + ret = -ENOMEM; + goto out_freeirq_tx; + } + + memset(p, 0, size); + priv->rx_desc_alloc_size = size; + priv->rx_desc_cpu = p; + + /* allocate tx dma ring */ + size = priv->tx_ring_size * sizeof(struct bcm_enet_desc); + p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL); + if (!p) { + dev_err(kdev, "cannot allocate tx ring\n"); + ret = -ENOMEM; + goto out_free_rx_ring; + } + + memset(p, 0, size); + priv->tx_desc_alloc_size = size; + priv->tx_desc_cpu = p; + + priv->tx_skb = kzalloc(sizeof(struct sk_buff *) * priv->tx_ring_size, + GFP_KERNEL); + if (!priv->tx_skb) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out_free_tx_ring; + } + + priv->tx_desc_count = priv->tx_ring_size; + priv->tx_dirty_desc = 0; + priv->tx_curr_desc = 0; + spin_lock_init(&priv->tx_lock); + + /* init & fill rx ring with skbs */ + priv->rx_skb = kzalloc(sizeof(struct sk_buff *) * priv->rx_ring_size, + GFP_KERNEL); + if (!priv->rx_skb) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out_free_tx_skb; + } + + priv->rx_desc_count = 0; + priv->rx_dirty_desc = 0; + priv->rx_curr_desc = 0; + + /* disable all ports */ + for (i = 0; i < priv->num_ports; i++) { + enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK, + ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK | + ENETSW_PTCTRL_TXDIS_MASK, + ENETSW_PTCTRL_REG(i)); + + priv->sw_port_link[i] = 0; + } + + /* reset mib */ + val = enetsw_readb(priv, ENETSW_GMCR_REG); + val |= ENETSW_GMCR_RST_MIB_MASK; + enetsw_writeb(priv, val, ENETSW_GMCR_REG); + mdelay(1); + val &= ~ENETSW_GMCR_RST_MIB_MASK; + enetsw_writeb(priv, val, ENETSW_GMCR_REG); + mdelay(1); + + /* force CPU port state */ + val = enetsw_readb(priv, ENETSW_IMPOV_REG); + val |= ENETSW_IMPOV_FORCE_MASK | ENETSW_IMPOV_LINKUP_MASK; + enetsw_writeb(priv, val, ENETSW_IMPOV_REG); + + /* enable switch forward engine */ + val = enetsw_readb(priv, ENETSW_SWMODE_REG); + val |= ENETSW_SWMODE_FWD_EN_MASK; + enetsw_writeb(priv, val, ENETSW_SWMODE_REG); + + /* enable jumbo on all ports */ + enetsw_writel(priv, 0x1ff, ENETSW_JMBCTL_PORT_REG); + enetsw_writew(priv, 9728, ENETSW_JMBCTL_MAXSIZE_REG); + + /* initialize flow control buffer allocation */ + enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, + ENETDMA_BUFALLOC_REG(priv->rx_chan)); + + if (bcm_enet_refill_rx(dev)) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out; + } + + /* write rx & tx ring addresses */ + enet_dmas_writel(priv, priv->rx_desc_dma, + ENETDMAS_RSTART_REG, priv->rx_chan); + enet_dmas_writel(priv, priv->tx_desc_dma, + ENETDMAS_RSTART_REG, priv->tx_chan); + + /* clear remaining state ram for rx & tx channel */ + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->rx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->tx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->rx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->tx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->rx_chan); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->tx_chan); + + /* set dma maximum burst len */ + enet_dmac_writel(priv, priv->dma_maxburst, + ENETDMAC_MAXBURST, priv->rx_chan); + enet_dmac_writel(priv, priv->dma_maxburst, + ENETDMAC_MAXBURST, priv->tx_chan); + + /* set flow control low/high threshold to 1/3 / 2/3 */ + val = priv->rx_ring_size / 3; + enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan)); + val = (priv->rx_ring_size * 2) / 3; + enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan)); + + /* all set, enable mac and interrupts, start dma engine and + * kick rx dma channel + */ + wmb(); + enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); + enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK, + ENETDMAC_CHANCFG, priv->rx_chan); + + /* watch "packet transferred" interrupt in rx and tx */ + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR, priv->rx_chan); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR, priv->tx_chan); + + /* make sure we enable napi before rx interrupt */ + napi_enable(&priv->napi); + + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK, priv->tx_chan); + + netif_carrier_on(dev); + netif_start_queue(dev); + + /* apply override config for bypass_link ports here. */ + for (i = 0; i < priv->num_ports; i++) { + struct bcm63xx_enetsw_port *port; + u8 override; + port = &priv->used_ports[i]; + if (!port->used) + continue; + + if (!port->bypass_link) + continue; + + override = ENETSW_PORTOV_ENABLE_MASK | + ENETSW_PORTOV_LINKUP_MASK; + + switch (port->force_speed) { + case 1000: + override |= ENETSW_IMPOV_1000_MASK; + break; + case 100: + override |= ENETSW_IMPOV_100_MASK; + break; + case 10: + break; + default: + pr_warn("invalid forced speed on port %s: assume 10\n", + port->name); + break; + } + + if (port->force_duplex_full) + override |= ENETSW_IMPOV_FDX_MASK; + + + enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i)); + } + + /* start phy polling timer */ + init_timer(&priv->swphy_poll); + priv->swphy_poll.function = swphy_poll_timer; + priv->swphy_poll.data = (unsigned long)priv; + priv->swphy_poll.expires = jiffies; + add_timer(&priv->swphy_poll); + return 0; + +out: + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm_enet_desc *desc; + + if (!priv->rx_skb[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single(kdev, desc->address, priv->rx_skb_size, + DMA_FROM_DEVICE); + kfree_skb(priv->rx_skb[i]); + } + kfree(priv->rx_skb); + +out_free_tx_skb: + kfree(priv->tx_skb); + +out_free_tx_ring: + dma_free_coherent(kdev, priv->tx_desc_alloc_size, + priv->tx_desc_cpu, priv->tx_desc_dma); + +out_free_rx_ring: + dma_free_coherent(kdev, priv->rx_desc_alloc_size, + priv->rx_desc_cpu, priv->rx_desc_dma); + +out_freeirq_tx: + if (priv->irq_tx != -1) + free_irq(priv->irq_tx, dev); + +out_freeirq_rx: + free_irq(priv->irq_rx, dev); + +out_freeirq: + return ret; +} + +/* stop callback */ +static int bcm_enetsw_stop(struct net_device *dev) +{ + struct bcm_enet_priv *priv; + struct device *kdev; + int i; + + priv = netdev_priv(dev); + kdev = &priv->pdev->dev; + + del_timer_sync(&priv->swphy_poll); + netif_stop_queue(dev); + napi_disable(&priv->napi); + del_timer_sync(&priv->rx_timeout); + + /* mask all interrupts */ + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan); + + /* disable dma & mac */ + bcm_enet_disable_dma(priv, priv->tx_chan); + bcm_enet_disable_dma(priv, priv->rx_chan); + + /* force reclaim of all tx buffers */ + bcm_enet_tx_reclaim(dev, 1); + + /* free the rx skb ring */ + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm_enet_desc *desc; + + if (!priv->rx_skb[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single(kdev, desc->address, priv->rx_skb_size, + DMA_FROM_DEVICE); + kfree_skb(priv->rx_skb[i]); + } + + /* free remaining allocated memory */ + kfree(priv->rx_skb); + kfree(priv->tx_skb); + dma_free_coherent(kdev, priv->rx_desc_alloc_size, + priv->rx_desc_cpu, priv->rx_desc_dma); + dma_free_coherent(kdev, priv->tx_desc_alloc_size, + priv->tx_desc_cpu, priv->tx_desc_dma); + if (priv->irq_tx != -1) + free_irq(priv->irq_tx, dev); + free_irq(priv->irq_rx, dev); + + return 0; +} + +/* try to sort out phy external status by walking the used_port field + * in the bcm_enet_priv structure. in case the phy address is not + * assigned to any physical port on the switch, assume it is external + * (and yell at the user). + */ +static int bcm_enetsw_phy_is_external(struct bcm_enet_priv *priv, int phy_id) +{ + int i; + + for (i = 0; i < priv->num_ports; ++i) { + if (!priv->used_ports[i].used) + continue; + if (priv->used_ports[i].phy_id == phy_id) + return bcm_enet_port_is_rgmii(i); + } + + printk_once(KERN_WARNING "bcm63xx_enet: could not find a used port with phy_id %i, assuming phy is external\n", + phy_id); + return 1; +} + +/* can't use bcmenet_sw_mdio_read directly as we need to sort out + * external/internal status of the given phy_id first. + */ +static int bcm_enetsw_mii_mdio_read(struct net_device *dev, int phy_id, + int location) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + return bcmenet_sw_mdio_read(priv, + bcm_enetsw_phy_is_external(priv, phy_id), + phy_id, location); +} + +/* can't use bcmenet_sw_mdio_write directly as we need to sort out + * external/internal status of the given phy_id first. + */ +static void bcm_enetsw_mii_mdio_write(struct net_device *dev, int phy_id, + int location, + int val) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + bcmenet_sw_mdio_write(priv, bcm_enetsw_phy_is_external(priv, phy_id), + phy_id, location, val); +} + +static int bcm_enetsw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct mii_if_info mii; + + mii.dev = dev; + mii.mdio_read = bcm_enetsw_mii_mdio_read; + mii.mdio_write = bcm_enetsw_mii_mdio_write; + mii.phy_id = 0; + mii.phy_id_mask = 0x3f; + mii.reg_num_mask = 0x1f; + return generic_mii_ioctl(&mii, if_mii(rq), cmd, NULL); + +} + +static const struct net_device_ops bcm_enetsw_ops = { + .ndo_open = bcm_enetsw_open, + .ndo_stop = bcm_enetsw_stop, + .ndo_start_xmit = bcm_enet_start_xmit, + .ndo_change_mtu = bcm_enet_change_mtu, + .ndo_do_ioctl = bcm_enetsw_ioctl, +}; + + +static const struct bcm_enet_stats bcm_enetsw_gstrings_stats[] = { + { "rx_packets", DEV_STAT(rx_packets), -1 }, + { "tx_packets", DEV_STAT(tx_packets), -1 }, + { "rx_bytes", DEV_STAT(rx_bytes), -1 }, + { "tx_bytes", DEV_STAT(tx_bytes), -1 }, + { "rx_errors", DEV_STAT(rx_errors), -1 }, + { "tx_errors", DEV_STAT(tx_errors), -1 }, + { "rx_dropped", DEV_STAT(rx_dropped), -1 }, + { "tx_dropped", DEV_STAT(tx_dropped), -1 }, + + { "tx_good_octets", GEN_STAT(mib.tx_gd_octets), ETHSW_MIB_RX_GD_OCT }, + { "tx_unicast", GEN_STAT(mib.tx_unicast), ETHSW_MIB_RX_BRDCAST }, + { "tx_broadcast", GEN_STAT(mib.tx_brdcast), ETHSW_MIB_RX_BRDCAST }, + { "tx_multicast", GEN_STAT(mib.tx_mult), ETHSW_MIB_RX_MULT }, + { "tx_64_octets", GEN_STAT(mib.tx_64), ETHSW_MIB_RX_64 }, + { "tx_65_127_oct", GEN_STAT(mib.tx_65_127), ETHSW_MIB_RX_65_127 }, + { "tx_128_255_oct", GEN_STAT(mib.tx_128_255), ETHSW_MIB_RX_128_255 }, + { "tx_256_511_oct", GEN_STAT(mib.tx_256_511), ETHSW_MIB_RX_256_511 }, + { "tx_512_1023_oct", GEN_STAT(mib.tx_512_1023), ETHSW_MIB_RX_512_1023}, + { "tx_1024_1522_oct", GEN_STAT(mib.tx_1024_max), + ETHSW_MIB_RX_1024_1522 }, + { "tx_1523_2047_oct", GEN_STAT(mib.tx_1523_2047), + ETHSW_MIB_RX_1523_2047 }, + { "tx_2048_4095_oct", GEN_STAT(mib.tx_2048_4095), + ETHSW_MIB_RX_2048_4095 }, + { "tx_4096_8191_oct", GEN_STAT(mib.tx_4096_8191), + ETHSW_MIB_RX_4096_8191 }, + { "tx_8192_9728_oct", GEN_STAT(mib.tx_8192_9728), + ETHSW_MIB_RX_8192_9728 }, + { "tx_oversize", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR }, + { "tx_oversize_drop", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR_DISC }, + { "tx_dropped", GEN_STAT(mib.tx_drop), ETHSW_MIB_RX_DROP }, + { "tx_undersize", GEN_STAT(mib.tx_underrun), ETHSW_MIB_RX_UND }, + { "tx_pause", GEN_STAT(mib.tx_pause), ETHSW_MIB_RX_PAUSE }, + + { "rx_good_octets", GEN_STAT(mib.rx_gd_octets), ETHSW_MIB_TX_ALL_OCT }, + { "rx_broadcast", GEN_STAT(mib.rx_brdcast), ETHSW_MIB_TX_BRDCAST }, + { "rx_multicast", GEN_STAT(mib.rx_mult), ETHSW_MIB_TX_MULT }, + { "rx_unicast", GEN_STAT(mib.rx_unicast), ETHSW_MIB_TX_MULT }, + { "rx_pause", GEN_STAT(mib.rx_pause), ETHSW_MIB_TX_PAUSE }, + { "rx_dropped", GEN_STAT(mib.rx_drop), ETHSW_MIB_TX_DROP_PKTS }, + +}; + +#define BCM_ENETSW_STATS_LEN \ + (sizeof(bcm_enetsw_gstrings_stats) / sizeof(struct bcm_enet_stats)) + +static void bcm_enetsw_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { + memcpy(data + i * ETH_GSTRING_LEN, + bcm_enetsw_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); + } + break; + } +} + +static int bcm_enetsw_get_sset_count(struct net_device *netdev, + int string_set) +{ + switch (string_set) { + case ETH_SS_STATS: + return BCM_ENETSW_STATS_LEN; + default: + return -EINVAL; + } +} + +static void bcm_enetsw_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + strncpy(drvinfo->driver, bcm_enet_driver_name, 32); + strncpy(drvinfo->version, bcm_enet_driver_version, 32); + strncpy(drvinfo->fw_version, "N/A", 32); + strncpy(drvinfo->bus_info, "bcm63xx", 32); + drvinfo->n_stats = BCM_ENETSW_STATS_LEN; +} + +static void bcm_enetsw_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct bcm_enet_priv *priv; + int i; + + priv = netdev_priv(netdev); + + for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { + const struct bcm_enet_stats *s; + u32 lo, hi; + char *p; + int reg; + + s = &bcm_enetsw_gstrings_stats[i]; + + reg = s->mib_reg; + if (reg == -1) + continue; + + lo = enetsw_readl(priv, ENETSW_MIB_REG(reg)); + p = (char *)priv + s->stat_offset; + + if (s->sizeof_stat == sizeof(u64)) { + hi = enetsw_readl(priv, ENETSW_MIB_REG(reg + 1)); + *(u64 *)p = ((u64)hi << 32 | lo); + } else { + *(u32 *)p = lo; + } + } + + for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { + const struct bcm_enet_stats *s; + char *p; + + s = &bcm_enetsw_gstrings_stats[i]; + + if (s->mib_reg == -1) + p = (char *)&netdev->stats + s->stat_offset; + else + p = (char *)priv + s->stat_offset; + + data[i] = (s->sizeof_stat == sizeof(u64)) ? + *(u64 *)p : *(u32 *)p; + } +} + +static void bcm_enetsw_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + + /* rx/tx ring is actually only limited by memory */ + ering->rx_max_pending = 8192; + ering->tx_max_pending = 8192; + ering->rx_mini_max_pending = 0; + ering->rx_jumbo_max_pending = 0; + ering->rx_pending = priv->rx_ring_size; + ering->tx_pending = priv->tx_ring_size; +} + +static int bcm_enetsw_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct bcm_enet_priv *priv; + int was_running; + + priv = netdev_priv(dev); + + was_running = 0; + if (netif_running(dev)) { + bcm_enetsw_stop(dev); + was_running = 1; + } + + priv->rx_ring_size = ering->rx_pending; + priv->tx_ring_size = ering->tx_pending; + + if (was_running) { + int err; + + err = bcm_enetsw_open(dev); + if (err) + dev_close(dev); + } + return 0; +} + +static struct ethtool_ops bcm_enetsw_ethtool_ops = { + .get_strings = bcm_enetsw_get_strings, + .get_sset_count = bcm_enetsw_get_sset_count, + .get_ethtool_stats = bcm_enetsw_get_ethtool_stats, + .get_drvinfo = bcm_enetsw_get_drvinfo, + .get_ringparam = bcm_enetsw_get_ringparam, + .set_ringparam = bcm_enetsw_set_ringparam, +}; + +/* allocate netdevice, request register memory and register device. */ +static int bcm_enetsw_probe(struct platform_device *pdev) +{ + struct bcm_enet_priv *priv; + struct net_device *dev; + struct bcm63xx_enetsw_platform_data *pd; + struct resource *res_mem; + int ret, irq_rx, irq_tx; + + /* stop if shared driver failed, assume driver->probe will be + * called in the same order we register devices (correct ?) + */ + if (!bcm_enet_shared_base[0]) + return -ENODEV; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_rx = platform_get_irq(pdev, 0); + irq_tx = platform_get_irq(pdev, 1); + if (!res_mem || irq_rx < 0) return -ENODEV; - bcm_enet_shared_base = devm_request_and_ioremap(&pdev->dev, res); - if (!bcm_enet_shared_base) + ret = 0; + dev = alloc_etherdev(sizeof(*priv)); + if (!dev) return -ENOMEM; + priv = netdev_priv(dev); + memset(priv, 0, sizeof(*priv)); + + /* initialize default and fetch platform data */ + priv->enet_is_sw = true; + priv->irq_rx = irq_rx; + priv->irq_tx = irq_tx; + priv->rx_ring_size = BCMENET_DEF_RX_DESC; + priv->tx_ring_size = BCMENET_DEF_TX_DESC; + priv->dma_maxburst = BCMENETSW_DMA_MAXBURST; + + pd = pdev->dev.platform_data; + if (pd) { + memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); + memcpy(priv->used_ports, pd->used_ports, + sizeof(pd->used_ports)); + priv->num_ports = pd->num_ports; + priv->dma_has_sram = pd->dma_has_sram; + priv->dma_chan_en_mask = pd->dma_chan_en_mask; + priv->dma_chan_int_mask = pd->dma_chan_int_mask; + priv->dma_chan_width = pd->dma_chan_width; + } + + ret = compute_hw_mtu(priv, dev->mtu); + if (ret) + goto out; + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + "bcm63xx_enetsw")) { + ret = -EBUSY; + goto out; + } + + priv->base = ioremap(res_mem->start, resource_size(res_mem)); + if (priv->base == NULL) { + ret = -ENOMEM; + goto out_release_mem; + } + + priv->mac_clk = clk_get(&pdev->dev, "enetsw"); + if (IS_ERR(priv->mac_clk)) { + ret = PTR_ERR(priv->mac_clk); + goto out_unmap; + } + clk_enable(priv->mac_clk); + + priv->rx_chan = 0; + priv->tx_chan = 1; + spin_lock_init(&priv->rx_lock); + + /* init rx timeout (used for oom) */ + init_timer(&priv->rx_timeout); + priv->rx_timeout.function = bcm_enet_refill_rx_timer; + priv->rx_timeout.data = (unsigned long)dev; + + /* register netdevice */ + dev->netdev_ops = &bcm_enetsw_ops; + netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); + SET_ETHTOOL_OPS(dev, &bcm_enetsw_ethtool_ops); + SET_NETDEV_DEV(dev, &pdev->dev); + + spin_lock_init(&priv->enetsw_mdio_lock); + + ret = register_netdev(dev); + if (ret) + goto out_put_clk; + + netif_carrier_off(dev); + platform_set_drvdata(pdev, dev); + priv->pdev = pdev; + priv->net_dev = dev; + + return 0; + +out_put_clk: + clk_put(priv->mac_clk); + +out_unmap: + iounmap(priv->base); + +out_release_mem: + release_mem_region(res_mem->start, resource_size(res_mem)); +out: + free_netdev(dev); + return ret; +} + + +/* exit func, stops hardware and unregisters netdevice */ +static int bcm_enetsw_remove(struct platform_device *pdev) +{ + struct bcm_enet_priv *priv; + struct net_device *dev; + struct resource *res; + + /* stop netdevice */ + dev = platform_get_drvdata(pdev); + priv = netdev_priv(dev); + unregister_netdev(dev); + + /* release device resources */ + iounmap(priv->base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(pdev, NULL); + free_netdev(dev); + return 0; +} + +struct platform_driver bcm63xx_enetsw_driver = { + .probe = bcm_enetsw_probe, + .remove = bcm_enetsw_remove, + .driver = { + .name = "bcm63xx_enetsw", + .owner = THIS_MODULE, + }, +}; + +/* reserve & remap memory space shared between all macs */ +static int bcm_enet_shared_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *p[3]; + unsigned int i; + + memset(bcm_enet_shared_base, 0, sizeof(bcm_enet_shared_base)); + + for (i = 0; i < 3; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + p[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(p[i])) + return PTR_ERR(p[i]); + } + + memcpy(bcm_enet_shared_base, p, sizeof(bcm_enet_shared_base)); return 0; } @@ -1884,8 +2876,7 @@ static int bcm_enet_shared_remove(struct platform_device *pdev) return 0; } -/* - * this "shared" driver is needed because both macs share a single +/* this "shared" driver is needed because both macs share a single * address space */ struct platform_driver bcm63xx_enet_shared_driver = { @@ -1897,9 +2888,7 @@ struct platform_driver bcm63xx_enet_shared_driver = { }, }; -/* - * entry point - */ +/* entry point */ static int __init bcm_enet_init(void) { int ret; @@ -1912,12 +2901,19 @@ static int __init bcm_enet_init(void) if (ret) platform_driver_unregister(&bcm63xx_enet_shared_driver); + ret = platform_driver_register(&bcm63xx_enetsw_driver); + if (ret) { + platform_driver_unregister(&bcm63xx_enet_driver); + platform_driver_unregister(&bcm63xx_enet_shared_driver); + } + return ret; } static void __exit bcm_enet_exit(void) { platform_driver_unregister(&bcm63xx_enet_driver); + platform_driver_unregister(&bcm63xx_enetsw_driver); platform_driver_unregister(&bcm63xx_enet_shared_driver); } diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.h b/drivers/net/ethernet/broadcom/bcm63xx_enet.h index 133d5857b9e..f55af431008 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.h +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.h @@ -18,6 +18,7 @@ /* maximum burst len for dma (4 bytes unit) */ #define BCMENET_DMA_MAXBURST 16 +#define BCMENETSW_DMA_MAXBURST 8 /* tx transmit threshold (4 bytes unit), fifo is 256 bytes, the value * must be low enough so that a DMA transfer of above burst length can @@ -84,11 +85,60 @@ #define ETH_MIB_RX_CNTRL 54 +/* + * SW MIB Counters register definitions +*/ +#define ETHSW_MIB_TX_ALL_OCT 0 +#define ETHSW_MIB_TX_DROP_PKTS 2 +#define ETHSW_MIB_TX_QOS_PKTS 3 +#define ETHSW_MIB_TX_BRDCAST 4 +#define ETHSW_MIB_TX_MULT 5 +#define ETHSW_MIB_TX_UNI 6 +#define ETHSW_MIB_TX_COL 7 +#define ETHSW_MIB_TX_1_COL 8 +#define ETHSW_MIB_TX_M_COL 9 +#define ETHSW_MIB_TX_DEF 10 +#define ETHSW_MIB_TX_LATE 11 +#define ETHSW_MIB_TX_EX_COL 12 +#define ETHSW_MIB_TX_PAUSE 14 +#define ETHSW_MIB_TX_QOS_OCT 15 + +#define ETHSW_MIB_RX_ALL_OCT 17 +#define ETHSW_MIB_RX_UND 19 +#define ETHSW_MIB_RX_PAUSE 20 +#define ETHSW_MIB_RX_64 21 +#define ETHSW_MIB_RX_65_127 22 +#define ETHSW_MIB_RX_128_255 23 +#define ETHSW_MIB_RX_256_511 24 +#define ETHSW_MIB_RX_512_1023 25 +#define ETHSW_MIB_RX_1024_1522 26 +#define ETHSW_MIB_RX_OVR 27 +#define ETHSW_MIB_RX_JAB 28 +#define ETHSW_MIB_RX_ALIGN 29 +#define ETHSW_MIB_RX_CRC 30 +#define ETHSW_MIB_RX_GD_OCT 31 +#define ETHSW_MIB_RX_DROP 33 +#define ETHSW_MIB_RX_UNI 34 +#define ETHSW_MIB_RX_MULT 35 +#define ETHSW_MIB_RX_BRDCAST 36 +#define ETHSW_MIB_RX_SA_CHANGE 37 +#define ETHSW_MIB_RX_FRAG 38 +#define ETHSW_MIB_RX_OVR_DISC 39 +#define ETHSW_MIB_RX_SYM 40 +#define ETHSW_MIB_RX_QOS_PKTS 41 +#define ETHSW_MIB_RX_QOS_OCT 42 +#define ETHSW_MIB_RX_1523_2047 44 +#define ETHSW_MIB_RX_2048_4095 45 +#define ETHSW_MIB_RX_4096_8191 46 +#define ETHSW_MIB_RX_8192_9728 47 + + struct bcm_enet_mib_counters { u64 tx_gd_octets; u32 tx_gd_pkts; u32 tx_all_octets; u32 tx_all_pkts; + u32 tx_unicast; u32 tx_brdcast; u32 tx_mult; u32 tx_64; @@ -97,7 +147,12 @@ struct bcm_enet_mib_counters { u32 tx_256_511; u32 tx_512_1023; u32 tx_1024_max; + u32 tx_1523_2047; + u32 tx_2048_4095; + u32 tx_4096_8191; + u32 tx_8192_9728; u32 tx_jab; + u32 tx_drop; u32 tx_ovr; u32 tx_frag; u32 tx_underrun; @@ -114,6 +169,7 @@ struct bcm_enet_mib_counters { u32 rx_all_octets; u32 rx_all_pkts; u32 rx_brdcast; + u32 rx_unicast; u32 rx_mult; u32 rx_64; u32 rx_65_127; @@ -197,6 +253,9 @@ struct bcm_enet_priv { /* number of dma desc in tx ring */ int tx_ring_size; + /* maximum dma burst size */ + int dma_maxburst; + /* cpu view of rx dma ring */ struct bcm_enet_desc *tx_desc_cpu; @@ -269,6 +328,33 @@ struct bcm_enet_priv { /* maximum hardware transmit/receive size */ unsigned int hw_mtu; + + bool enet_is_sw; + + /* port mapping for switch devices */ + int num_ports; + struct bcm63xx_enetsw_port used_ports[ENETSW_MAX_PORT]; + int sw_port_link[ENETSW_MAX_PORT]; + + /* used to poll switch port state */ + struct timer_list swphy_poll; + spinlock_t enetsw_mdio_lock; + + /* dma channel enable mask */ + u32 dma_chan_en_mask; + + /* dma channel interrupt mask */ + u32 dma_chan_int_mask; + + /* DMA engine has internal SRAM */ + bool dma_has_sram; + + /* dma channel width */ + unsigned int dma_chan_width; + + /* dma descriptor shift value */ + unsigned int dma_desc_shift; }; + #endif /* ! BCM63XX_ENET_H_ */ diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 5d204492c60..6a2de1d79ff 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -8104,7 +8104,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) pci_set_master(pdev); - bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + bp->pm_cap = pdev->pm_cap; if (bp->pm_cap == 0) { dev_err(&pdev->dev, "Cannot find power management capability, aborting\n"); @@ -8764,18 +8764,4 @@ static struct pci_driver bnx2_pci_driver = { .err_handler = &bnx2_err_handler, }; -static int __init bnx2_init(void) -{ - return pci_register_driver(&bnx2_pci_driver); -} - -static void __exit bnx2_cleanup(void) -{ - pci_unregister_driver(&bnx2_pci_driver); -} - -module_init(bnx2_init); -module_exit(bnx2_cleanup); - - - +module_pci_driver(bnx2_pci_driver); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 3dba2a70a00..dedbd76c033 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -34,12 +34,10 @@ #define BCM_DCBNL #endif - #include "bnx2x_hsi.h" #include "../cnic_if.h" - #define BNX2X_MIN_MSIX_VEC_CNT(bp) ((bp)->min_msix_vec_cnt) #include <linux/mdio.h> @@ -114,7 +112,6 @@ do { \ #define BNX2X_ERROR(fmt, ...) \ pr_err("[%s:%d]" fmt, __func__, __LINE__, ##__VA_ARGS__) - /* before we have a dev->name use dev_info() */ #define BNX2X_DEV_INFO(fmt, ...) \ do { \ @@ -147,7 +144,6 @@ do { \ #define U64_HI(x) ((u32)(((u64)(x)) >> 32)) #define HILO_U64(hi, lo) ((((u64)(hi)) << 32) + (lo)) - #define REG_ADDR(bp, offset) ((bp->regview) + (offset)) #define REG_RD(bp, offset) readl(REG_ADDR(bp, offset)) @@ -366,7 +362,7 @@ union db_prod { /* * Number of required SGEs is the sum of two: * 1. Number of possible opened aggregations (next packet for - * these aggregations will probably consume SGE immidiatelly) + * these aggregations will probably consume SGE immediately) * 2. Rest of BRB blocks divided by 2 (block will consume new SGE only * after placement on BD for new TPA aggregation) * @@ -387,7 +383,6 @@ union db_prod { #define BIT_VEC64_ELEM_SHIFT 6 #define BIT_VEC64_ELEM_MASK ((u64)BIT_VEC64_ELEM_SZ - 1) - #define __BIT_VEC64_SET_BIT(el, bit) \ do { \ el = ((el) | ((u64)0x1 << (bit))); \ @@ -398,7 +393,6 @@ union db_prod { el = ((el) & (~((u64)0x1 << (bit)))); \ } while (0) - #define BIT_VEC64_SET_BIT(vec64, idx) \ __BIT_VEC64_SET_BIT((vec64)[(idx) >> BIT_VEC64_ELEM_SHIFT], \ (idx) & BIT_VEC64_ELEM_MASK) @@ -419,8 +413,6 @@ union db_prod { /*******************************************************/ - - /* Number of u64 elements in SGE mask array */ #define RX_SGE_MASK_LEN (NUM_RX_SGE / BIT_VEC64_ELEM_SZ) #define RX_SGE_MASK_LEN_MASK (RX_SGE_MASK_LEN - 1) @@ -493,11 +485,26 @@ struct bnx2x_fastpath { struct bnx2x *bp; /* parent */ struct napi_struct napi; + +#ifdef CONFIG_NET_LL_RX_POLL + unsigned int state; +#define BNX2X_FP_STATE_IDLE 0 +#define BNX2X_FP_STATE_NAPI (1 << 0) /* NAPI owns this FP */ +#define BNX2X_FP_STATE_POLL (1 << 1) /* poll owns this FP */ +#define BNX2X_FP_STATE_NAPI_YIELD (1 << 2) /* NAPI yielded this FP */ +#define BNX2X_FP_STATE_POLL_YIELD (1 << 3) /* poll yielded this FP */ +#define BNX2X_FP_YIELD (BNX2X_FP_STATE_NAPI_YIELD | BNX2X_FP_STATE_POLL_YIELD) +#define BNX2X_FP_LOCKED (BNX2X_FP_STATE_NAPI | BNX2X_FP_STATE_POLL) +#define BNX2X_FP_USER_PEND (BNX2X_FP_STATE_POLL | BNX2X_FP_STATE_POLL_YIELD) + /* protect state */ + spinlock_t lock; +#endif /* CONFIG_NET_LL_RX_POLL */ + union host_hc_status_block status_blk; - /* chip independed shortcuts into sb structure */ + /* chip independent shortcuts into sb structure */ __le16 *sb_index_values; __le16 *sb_running_index; - /* chip independed shortcut into rx_prods_offset memory */ + /* chip independent shortcut into rx_prods_offset memory */ u32 ustorm_rx_prods_offset; u32 rx_buf_size; @@ -565,6 +572,116 @@ struct bnx2x_fastpath { #define bnx2x_fp_stats(bp, fp) (&((bp)->fp_stats[(fp)->index])) #define bnx2x_fp_qstats(bp, fp) (&((bp)->fp_stats[(fp)->index].eth_q_stats)) +#ifdef CONFIG_NET_LL_RX_POLL +static inline void bnx2x_fp_init_lock(struct bnx2x_fastpath *fp) +{ + spin_lock_init(&fp->lock); + fp->state = BNX2X_FP_STATE_IDLE; +} + +/* called from the device poll routine to get ownership of a FP */ +static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp) +{ + bool rc = true; + + spin_lock(&fp->lock); + if (fp->state & BNX2X_FP_LOCKED) { + WARN_ON(fp->state & BNX2X_FP_STATE_NAPI); + fp->state |= BNX2X_FP_STATE_NAPI_YIELD; + rc = false; + } else { + /* we don't care if someone yielded */ + fp->state = BNX2X_FP_STATE_NAPI; + } + spin_unlock(&fp->lock); + return rc; +} + +/* returns true is someone tried to get the FP while napi had it */ +static inline bool bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp) +{ + bool rc = false; + + spin_lock(&fp->lock); + WARN_ON(fp->state & + (BNX2X_FP_STATE_POLL | BNX2X_FP_STATE_NAPI_YIELD)); + + if (fp->state & BNX2X_FP_STATE_POLL_YIELD) + rc = true; + fp->state = BNX2X_FP_STATE_IDLE; + spin_unlock(&fp->lock); + return rc; +} + +/* called from bnx2x_low_latency_poll() */ +static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp) +{ + bool rc = true; + + spin_lock_bh(&fp->lock); + if ((fp->state & BNX2X_FP_LOCKED)) { + fp->state |= BNX2X_FP_STATE_POLL_YIELD; + rc = false; + } else { + /* preserve yield marks */ + fp->state |= BNX2X_FP_STATE_POLL; + } + spin_unlock_bh(&fp->lock); + return rc; +} + +/* returns true if someone tried to get the FP while it was locked */ +static inline bool bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp) +{ + bool rc = false; + + spin_lock_bh(&fp->lock); + WARN_ON(fp->state & BNX2X_FP_STATE_NAPI); + + if (fp->state & BNX2X_FP_STATE_POLL_YIELD) + rc = true; + fp->state = BNX2X_FP_STATE_IDLE; + spin_unlock_bh(&fp->lock); + return rc; +} + +/* true if a socket is polling, even if it did not get the lock */ +static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp) +{ + WARN_ON(!(fp->state & BNX2X_FP_LOCKED)); + return fp->state & BNX2X_FP_USER_PEND; +} +#else +static inline void bnx2x_fp_init_lock(struct bnx2x_fastpath *fp) +{ +} + +static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp) +{ + return true; +} + +static inline bool bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp) +{ + return false; +} + +static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp) +{ + return false; +} + +static inline bool bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp) +{ + return false; +} + +static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp) +{ + return false; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + /* Use 2500 as a mini-jumbo MTU for FCoE */ #define BNX2X_FCOE_MINI_JUMBO_MTU 2500 @@ -580,12 +697,10 @@ struct bnx2x_fastpath { txdata_ptr[FIRST_TX_COS_INDEX] \ ->var) - #define IS_ETH_FP(fp) ((fp)->index < BNX2X_NUM_ETH_QUEUES((fp)->bp)) #define IS_FCOE_FP(fp) ((fp)->index == FCOE_IDX((fp)->bp)) #define IS_FCOE_IDX(idx) ((idx) == FCOE_IDX(bp)) - /* MC hsi */ #define MAX_FETCH_BD 13 /* HW max BDs per packet */ #define RX_COPY_THRESH 92 @@ -613,7 +728,7 @@ struct bnx2x_fastpath { * START_BD(splitted) - includes unpaged data segment for GSO * PARSING_BD - for TSO and CSUM data * PARSING_BD2 - for encapsulation data - * Frag BDs - decribes pages for frags + * Frag BDs - describes pages for frags */ #define BDS_PER_TX_PKT 4 #define MAX_BDS_PER_TX_PKT (MAX_SKB_FRAGS + BDS_PER_TX_PKT) @@ -693,12 +808,10 @@ struct bnx2x_fastpath { FW_DROP_LEVEL(bp)) #define RCQ_TH_HI(bp) (RCQ_TH_LO(bp) + DROPLESS_FC_HEADROOM) - /* This is needed for determining of last_max */ #define SUB_S16(a, b) (s16)((s16)(a) - (s16)(b)) #define SUB_S32(a, b) (s32)((s32)(a) - (s32)(b)) - #define BNX2X_SWCID_SHIFT 17 #define BNX2X_SWCID_MASK ((0x1 << BNX2X_SWCID_SHIFT) - 1) @@ -723,7 +836,6 @@ struct bnx2x_fastpath { DPM_TRIGER_TYPE); \ } while (0) - /* TX CSUM helpers */ #define SKB_CS_OFF(skb) (offsetof(struct tcphdr, check) - \ skb->csum_offset) @@ -766,7 +878,6 @@ struct bnx2x_fastpath { #define BNX2X_RX_SUM_FIX(cqe) \ BNX2X_PRS_FLAG_OVERETH_IPV4(cqe->fast_path_cqe.pars_flags.flags) - #define FP_USB_FUNC_OFF \ offsetof(struct cstorm_status_block_u, func) #define FP_CSB_FUNC_OFF \ @@ -900,14 +1011,14 @@ struct bnx2x_common { #define CHIP_IS_E3A0(bp) (CHIP_IS_E3(bp) && \ (CHIP_REV(bp) == CHIP_REV_Ax)) /* This define is used in two main places: - * 1. In the early stages of nic_load, to know if to configrue Parser / Searcher + * 1. In the early stages of nic_load, to know if to configure Parser / Searcher * to nic-only mode or to offload mode. Offload mode is configured if either the * chip is E1x (where MIC_MODE register is not applicable), or if cnic already * registered for this port (which means that the user wants storage services). * 2. During cnic-related load, to know if offload mode is already configured in - * the HW or needs to be configrued. + * the HW or needs to be configured. * Since the transition from nic-mode to offload-mode in HW causes traffic - * coruption, nic-mode is configured only in ports on which storage services + * corruption, nic-mode is configured only in ports on which storage services * where never requested. */ #define CONFIGURE_NIC_MODE(bp) (!CHIP_IS_E1x(bp) && !CNIC_ENABLED(bp)) @@ -1008,14 +1119,14 @@ extern struct workqueue_struct *bnx2x_wq; * If the maximum number of FP-SB available is X then: * a. If CNIC is supported it consumes 1 FP-SB thus the max number of * regular L2 queues is Y=X-1 - * b. in MF mode the actual number of L2 queues is Y= (X-1/MF_factor) + * b. In MF mode the actual number of L2 queues is Y= (X-1/MF_factor) * c. If the FCoE L2 queue is supported the actual number of L2 queues * is Y+1 * d. The number of irqs (MSIX vectors) is either Y+1 (one extra for * slow-path interrupts) or Y+2 if CNIC is supported (one additional * FP interrupt context for the CNIC). * e. The number of HW context (CID count) is always X or X+1 if FCoE - * L2 queue is supported. the cid for the FCoE L2 queue is always X. + * L2 queue is supported. The cid for the FCoE L2 queue is always X. */ /* fast-path interrupt contexts E1x */ @@ -1068,7 +1179,6 @@ struct bnx2x_slowpath { struct eth_classify_rules_ramrod_data e2; } mac_rdata; - union { struct tstorm_eth_mac_filter_config e1x; struct eth_filter_rules_ramrod_data e2; @@ -1119,7 +1229,6 @@ struct bnx2x_slowpath { #define bnx2x_sp_mapping(bp, var) \ (bp->slowpath_mapping + offsetof(struct bnx2x_slowpath, var)) - /* attn group wiring */ #define MAX_DYNAMIC_ATTN_GRPS 8 @@ -1221,11 +1330,11 @@ enum { BNX2X_SP_RTNL_AFEX_F_UPDATE, BNX2X_SP_RTNL_ENABLE_SRIOV, BNX2X_SP_RTNL_VFPF_MCAST, + BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN, BNX2X_SP_RTNL_VFPF_STORM_RX_MODE, BNX2X_SP_RTNL_HYPERVISOR_VLAN, }; - struct bnx2x_prev_path_list { struct list_head list; u8 bus; @@ -1392,6 +1501,7 @@ struct bnx2x { #define USING_SINGLE_MSIX_FLAG (1 << 20) #define BC_SUPPORTS_DCBX_MSG_NON_PMF (1 << 21) #define IS_VF_FLAG (1 << 22) +#define INTERRUPTS_ENABLED_FLAG (1 << 23) #define BP_NOMCP(bp) ((bp)->flags & NO_MCP_FLAG) @@ -1585,7 +1695,7 @@ struct bnx2x { struct mutex cnic_mutex; struct bnx2x_vlan_mac_obj iscsi_l2_mac_obj; - /* Start index of the "special" (CNIC related) L2 cleints */ + /* Start index of the "special" (CNIC related) L2 clients */ u8 cnic_base_cl_id; int dmae_ready; @@ -1699,7 +1809,7 @@ struct bnx2x { /* operation indication for the sp_rtnl task */ unsigned long sp_rtnl_state; - /* DCBX Negotation results */ + /* DCBX Negotiation results */ struct dcbx_features dcbx_local_feat; u32 dcbx_error; @@ -1755,7 +1865,6 @@ extern int num_queues; #define FUNC_FLG_SPQ 0x0010 #define FUNC_FLG_LEADING 0x0020 /* PF only */ - struct bnx2x_func_init_params { /* dma */ dma_addr_t fw_stat_map; /* valid iff FUNC_FLG_STATS */ @@ -1853,9 +1962,6 @@ struct bnx2x_func_init_params { #define skip_queue(bp, idx) (NO_FCOE(bp) && IS_FCOE_IDX(idx)) - - - /** * bnx2x_set_mac_one - configure a single MAC address * @@ -1921,7 +2027,6 @@ u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type, void bnx2x_prep_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae, u8 src_type, u8 dst_type); int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae); -void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae, int msglvl); /* FLR related routines */ u32 bnx2x_flr_clnup_poll_count(struct bnx2x *bp); @@ -1937,6 +2042,8 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, void bnx2x_update_coalesce(struct bnx2x *bp); int bnx2x_get_cur_phy_idx(struct bnx2x *bp); +bool bnx2x_port_after_undi(struct bnx2x *bp); + static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, int wait) { @@ -1998,7 +2105,6 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define UNLOAD_CLOSE 1 #define UNLOAD_RECOVERY 2 - /* DMAE command defines */ #define DMAE_TIMEOUT -1 #define DMAE_PCI_ERROR -2 /* E2 and onward */ @@ -2062,7 +2168,8 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define DMAE_LEN32_WR_MAX(bp) (CHIP_IS_E1(bp) ? 0x400 : 0x2000) #define DMAE_COMP_VAL 0x60d0d0ae /* E2 and on - upper bit - indicates eror */ + * indicates error + */ #define MAX_DMAE_C_PER_PORT 8 #define INIT_DMAE_C(bp) (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \ @@ -2100,7 +2207,6 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define SP_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_spe)) #define MAX_SP_DESC_CNT (SP_DESC_CNT - 1) - #define BNX2X_BTR 4 #define MAX_SPQ_PENDING 8 @@ -2137,6 +2243,8 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define ATTN_HARD_WIRED_MASK 0xff00 #define ATTENTION_ID 4 +#define IS_MF_STORAGE_ONLY(bp) (IS_MF_STORAGE_SD(bp) || \ + IS_MF_FCOE_AFEX(bp)) /* stuff added to make the code fit 80Col */ @@ -2338,4 +2446,9 @@ enum { #define NUM_MACS 8 +enum bnx2x_pci_bus_speed { + BNX2X_PCI_LINK_SPEED_2500 = 2500, + BNX2X_PCI_LINK_SPEED_5000 = 5000, + BNX2X_PCI_LINK_SPEED_8000 = 8000 +}; #endif /* bnx2x.h */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 638e55435b0..ec3aa1d451e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -24,6 +24,7 @@ #include <net/tcp.h> #include <net/ipv6.h> #include <net/ip6_checksum.h> +#include <net/ll_poll.h> #include <linux/prefetch.h> #include "bnx2x_cmn.h" #include "bnx2x_init.h" @@ -124,7 +125,7 @@ static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta) int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp); /* Queue pointer cannot be re-set on an fp-basis, as moving pointer - * backward along the array could cause memory to be overriden + * backward along the array could cause memory to be overridden */ for (cos = 1; cos < bp->max_cos; cos++) { for (i = 0; i < old_eth_num - delta; i++) { @@ -165,7 +166,6 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata, dma_unmap_single(&bp->pdev->dev, BD_UNMAP_ADDR(tx_start_bd), BD_UNMAP_LEN(tx_start_bd), DMA_TO_DEVICE); - nbd = le16_to_cpu(tx_start_bd->nbd) - 1; #ifdef BNX2X_STOP_ON_ERROR if ((nbd - 1) > (MAX_SKB_FRAGS + 2)) { @@ -259,7 +259,7 @@ int bnx2x_tx_int(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata) smp_mb(); if (unlikely(netif_tx_queue_stopped(txq))) { - /* Taking tx_lock() is needed to prevent reenabling the queue + /* Taking tx_lock() is needed to prevent re-enabling the queue * while it's empty. This could have happen if rx_action() gets * suspended in bnx2x_tx_int() after the condition before * netif_tx_wake_queue(), while tx_action (bnx2x_start_xmit()): @@ -572,7 +572,7 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp, return err; } - /* Unmap the page as we r going to pass it to the stack */ + /* Unmap the page as we're going to pass it to the stack */ dma_unmap_page(&bp->pdev->dev, dma_unmap_addr(&old_rx_pg, mapping), SGE_PAGES, DMA_FROM_DEVICE); @@ -733,7 +733,6 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, dev_kfree_skb_any(skb); } - /* put new data in bin */ rx_buf->data = new_data; @@ -805,40 +804,32 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) { struct bnx2x *bp = fp->bp; u16 bd_cons, bd_prod, bd_prod_fw, comp_ring_cons; - u16 hw_comp_cons, sw_comp_cons, sw_comp_prod; + u16 sw_comp_cons, sw_comp_prod; int rx_pkt = 0; + union eth_rx_cqe *cqe; + struct eth_fast_path_rx_cqe *cqe_fp; #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) return 0; #endif - /* CQ "next element" is of the size of the regular element, - that's why it's ok here */ - hw_comp_cons = le16_to_cpu(*fp->rx_cons_sb); - if ((hw_comp_cons & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) - hw_comp_cons++; - bd_cons = fp->rx_bd_cons; bd_prod = fp->rx_bd_prod; bd_prod_fw = bd_prod; sw_comp_cons = fp->rx_comp_cons; sw_comp_prod = fp->rx_comp_prod; - /* Memory barrier necessary as speculative reads of the rx - * buffer can be ahead of the index in the status block - */ - rmb(); + comp_ring_cons = RCQ_BD(sw_comp_cons); + cqe = &fp->rx_comp_ring[comp_ring_cons]; + cqe_fp = &cqe->fast_path_cqe; DP(NETIF_MSG_RX_STATUS, - "queue[%d]: hw_comp_cons %u sw_comp_cons %u\n", - fp->index, hw_comp_cons, sw_comp_cons); + "queue[%d]: sw_comp_cons %u\n", fp->index, sw_comp_cons); - while (sw_comp_cons != hw_comp_cons) { + while (BNX2X_IS_CQE_COMPLETED(cqe_fp)) { struct sw_rx_bd *rx_buf = NULL; struct sk_buff *skb; - union eth_rx_cqe *cqe; - struct eth_fast_path_rx_cqe *cqe_fp; u8 cqe_fp_flags; enum eth_rx_cqe_type cqe_fp_type; u16 len, pad, queue; @@ -850,12 +841,9 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) return 0; #endif - comp_ring_cons = RCQ_BD(sw_comp_cons); bd_prod = RX_BD(bd_prod); bd_cons = RX_BD(bd_cons); - cqe = &fp->rx_comp_ring[comp_ring_cons]; - cqe_fp = &cqe->fast_path_cqe; cqe_fp_flags = cqe_fp->type_error_flags; cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE; @@ -899,7 +887,6 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) cqe_fp); goto next_rx; - } queue = cqe->end_agg_cqe.queue_index; tpa_info = &fp->tpa_info[queue]; @@ -1002,9 +989,13 @@ reuse_rx: PARSING_FLAGS_VLAN) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), le16_to_cpu(cqe_fp->vlan_tag)); - napi_gro_receive(&fp->napi, skb); + skb_mark_ll(skb, &fp->napi); + if (bnx2x_fp_ll_polling(fp)) + netif_receive_skb(skb); + else + napi_gro_receive(&fp->napi, skb); next_rx: rx_buf->data = NULL; @@ -1016,8 +1007,15 @@ next_cqe: sw_comp_prod = NEXT_RCQ_IDX(sw_comp_prod); sw_comp_cons = NEXT_RCQ_IDX(sw_comp_cons); + /* mark CQE as free */ + BNX2X_SEED_CQE(cqe_fp); + if (rx_pkt == budget) break; + + comp_ring_cons = RCQ_BD(sw_comp_cons); + cqe = &fp->rx_comp_ring[comp_ring_cons]; + cqe_fp = &cqe->fast_path_cqe; } /* while */ fp->rx_bd_cons = bd_cons; @@ -1053,8 +1051,6 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) #endif /* Handle Rx and Tx according to MSI-X vector */ - prefetch(fp->rx_cons_sb); - for_each_cos_in_tx_queue(fp, cos) prefetch(fp->txdata_ptr[cos]->tx_cons_sb); @@ -1118,7 +1114,7 @@ static void bnx2x_fill_report_data(struct bnx2x *bp, memset(data, 0, sizeof(*data)); - /* Fill the report data: efective line speed */ + /* Fill the report data: effective line speed */ data->line_speed = line_speed; /* Link is down */ @@ -1161,7 +1157,7 @@ void bnx2x_link_report(struct bnx2x *bp) * * @bp: driver handle * - * None atomic inmlementation. + * None atomic implementation. * Should be called under the phy_lock. */ void __bnx2x_link_report(struct bnx2x *bp) @@ -1304,7 +1300,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp) "mtu %d rx_buf_size %d\n", bp->dev->mtu, fp->rx_buf_size); if (!fp->disable_tpa) { - /* Fill the per-aggregtion pool */ + /* Fill the per-aggregation pool */ for (i = 0; i < MAX_AGG_QS(bp); i++) { struct bnx2x_agg_info *tpa_info = &fp->tpa_info[i]; @@ -1726,7 +1722,7 @@ static int bnx2x_req_irq(struct bnx2x *bp) return request_irq(irq, bnx2x_interrupt, flags, bp->dev->name, bp->dev); } -int bnx2x_setup_irqs(struct bnx2x *bp) +static int bnx2x_setup_irqs(struct bnx2x *bp) { int rc = 0; if (bp->flags & USING_MSIX_FLAG && @@ -1759,32 +1755,46 @@ static void bnx2x_napi_enable_cnic(struct bnx2x *bp) { int i; - for_each_rx_queue_cnic(bp, i) + for_each_rx_queue_cnic(bp, i) { + bnx2x_fp_init_lock(&bp->fp[i]); napi_enable(&bnx2x_fp(bp, i, napi)); + } } static void bnx2x_napi_enable(struct bnx2x *bp) { int i; - for_each_eth_queue(bp, i) + for_each_eth_queue(bp, i) { + bnx2x_fp_init_lock(&bp->fp[i]); napi_enable(&bnx2x_fp(bp, i, napi)); + } } static void bnx2x_napi_disable_cnic(struct bnx2x *bp) { int i; - for_each_rx_queue_cnic(bp, i) + local_bh_disable(); + for_each_rx_queue_cnic(bp, i) { napi_disable(&bnx2x_fp(bp, i, napi)); + while (!bnx2x_fp_lock_napi(&bp->fp[i])) + mdelay(1); + } + local_bh_enable(); } static void bnx2x_napi_disable(struct bnx2x *bp) { int i; - for_each_eth_queue(bp, i) + local_bh_disable(); + for_each_eth_queue(bp, i) { napi_disable(&bnx2x_fp(bp, i, napi)); + while (!bnx2x_fp_lock_napi(&bp->fp[i])) + mdelay(1); + } + local_bh_enable(); } void bnx2x_netif_start(struct bnx2x *bp) @@ -1829,7 +1839,7 @@ u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb) } /* select a non-FCoE queue */ - return __skb_tx_hash(dev, skb, BNX2X_NUM_ETH_QUEUES(bp)); + return __netdev_pick_tx(dev, skb) % BNX2X_NUM_ETH_QUEUES(bp); } void bnx2x_set_num_queues(struct bnx2x *bp) @@ -1862,7 +1872,7 @@ void bnx2x_set_num_queues(struct bnx2x *bp) * * If the actual number of Tx queues (for each CoS) is less than 16 then there * will be the holes at the end of each group of 16 ETh L2 indices (0..15, - * 16..31,...) with indicies that are not coupled with any real Tx queue. + * 16..31,...) with indices that are not coupled with any real Tx queue. * * The proper configuration of skb->queue_mapping is handled by * bnx2x_select_queue() and __skb_tx_hash(). @@ -1924,7 +1934,7 @@ static void bnx2x_set_rx_buf_size(struct bnx2x *bp) ETH_OVREHEAD + mtu + BNX2X_FW_RX_ALIGN_END; - /* Note : rx_buf_size doesnt take into account NET_SKB_PAD */ + /* Note : rx_buf_size doesn't take into account NET_SKB_PAD */ if (fp->rx_buf_size + NET_SKB_PAD <= PAGE_SIZE) fp->rx_frag_size = fp->rx_buf_size + NET_SKB_PAD; else @@ -1937,7 +1947,7 @@ static int bnx2x_init_rss_pf(struct bnx2x *bp) int i; u8 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp); - /* Prepare the initial contents fo the indirection table if RSS is + /* Prepare the initial contents for the indirection table if RSS is * enabled */ for (i = 0; i < sizeof(bp->rss_conf_obj.ind_table); i++) @@ -2015,7 +2025,7 @@ static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code) /* * Cleans the object that have internal lists without sending - * ramrods. Should be run when interrutps are disabled. + * ramrods. Should be run when interrupts are disabled. */ void bnx2x_squeeze_objects(struct bnx2x *bp) { @@ -2166,10 +2176,10 @@ static int bnx2x_alloc_fw_stats_mem(struct bnx2x *bp) bp->fw_stats_data_mapping = bp->fw_stats_mapping + bp->fw_stats_req_sz; - DP(BNX2X_MSG_SP, "statistics request base address set to %x %x", + DP(BNX2X_MSG_SP, "statistics request base address set to %x %x\n", U64_HI(bp->fw_stats_req_mapping), U64_LO(bp->fw_stats_req_mapping)); - DP(BNX2X_MSG_SP, "statistics data base address set to %x %x", + DP(BNX2X_MSG_SP, "statistics data base address set to %x %x\n", U64_HI(bp->fw_stats_data_mapping), U64_LO(bp->fw_stats_data_mapping)); return 0; @@ -2183,6 +2193,8 @@ alloc_mem_err: /* send load request to mcp and analyze response */ static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code) { + u32 param; + /* init fw_seq */ bp->fw_seq = (SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) & @@ -2195,9 +2207,13 @@ static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code) DRV_PULSE_SEQ_MASK); BNX2X_DEV_INFO("drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq); + param = DRV_MSG_CODE_LOAD_REQ_WITH_LFA; + + if (IS_MF_SD(bp) && bnx2x_port_after_undi(bp)) + param |= DRV_MSG_CODE_LOAD_REQ_FORCE_LFA; + /* load request */ - (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, - DRV_MSG_CODE_LOAD_REQ_WITH_LFA); + (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, param); /* if mcp fails to respond we must abort */ if (!(*load_code)) { @@ -2238,7 +2254,7 @@ int bnx2x_nic_load_analyze_req(struct bnx2x *bp, u32 load_code) /* abort nic load if version mismatch */ if (my_fw != loaded_fw) { - BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. aborting\n", + BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. Aborting\n", loaded_fw, my_fw); return -EBUSY; } @@ -2316,10 +2332,10 @@ static void bnx2x_nic_load_afex_dcc(struct bnx2x *bp, int load_code) static void bnx2x_bz_fp(struct bnx2x *bp, int index) { struct bnx2x_fastpath *fp = &bp->fp[index]; - int cos; struct napi_struct orig_napi = fp->napi; struct bnx2x_agg_info *orig_tpa_info = fp->tpa_info; + /* bzero bnx2x_fastpath contents */ if (fp->tpa_info) memset(fp->tpa_info, 0, ETH_MAX_AGGREGATION_QUEUES_E1H_E2 * @@ -2345,8 +2361,7 @@ static void bnx2x_bz_fp(struct bnx2x *bp, int index) fp->txdata_ptr[cos] = &bp->bnx2x_txq[cos * BNX2X_NUM_ETH_QUEUES(bp) + index]; - /* - * set the tpa flag for each queue. The tpa flag determines the queue + /* set the tpa flag for each queue. The tpa flag determines the queue * minimal size so it must be set prior to queue memory allocation */ fp->disable_tpa = !(bp->flags & TPA_ENABLE_FLAG || @@ -2429,7 +2444,6 @@ int bnx2x_load_cnic(struct bnx2x *bp) if (bp->state == BNX2X_STATE_OPEN) bnx2x_cnic_notify(bp, CNIC_CTL_START_CMD); - DP(NETIF_MSG_IFUP, "Ending successfully CNIC-related load\n"); return 0; @@ -2472,6 +2486,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD; + /* zero the structure w/o any lock, before SP handler is initialized */ memset(&bp->last_reported_link, 0, sizeof(bp->last_reported_link)); __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, &bp->last_reported_link.link_report_flags); @@ -2536,8 +2551,8 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) } /* configure multi cos mappings in kernel. - * this configuration may be overriden by a multi class queue discipline - * or by a dcbx negotiation result. + * this configuration may be overridden by a multi class queue + * discipline or by a dcbx negotiation result. */ bnx2x_setup_tc(bp->dev, bp->max_cos); @@ -2696,7 +2711,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) /* Start the Tx */ switch (load_mode) { case LOAD_NORMAL: - /* Tx queue should be only reenabled */ + /* Tx queue should be only re-enabled */ netif_tx_wake_all_queues(bp->dev); break; @@ -2841,7 +2856,7 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) } /* Nothing to do during unload if previous bnx2x_nic_load() - * have not completed succesfully - all resourses are released. + * have not completed successfully - all resources are released. * * we can get here only after unsuccessful ndo_* callback, during which * dev->IFF_UP flag is still on. @@ -2856,6 +2871,9 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; smp_mb(); + /* indicate to VFs that the PF is going down */ + bnx2x_iov_channel_down(bp); + if (CNIC_LOADED(bp)) bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD); @@ -2890,10 +2908,9 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) /* Send the UNLOAD_REQUEST to the MCP */ bnx2x_send_unload_req(bp, unload_mode); - /* - * Prevent transactions to host from the functions on the + /* Prevent transactions to host from the functions on the * engine that doesn't reset global blocks in case of global - * attention once gloabl blocks are reset and gates are opened + * attention once global blocks are reset and gates are opened * (the engine which leader will perform the recovery * last). */ @@ -2914,7 +2931,7 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) } /* - * At this stage no more interrupts will arrive so we may safly clean + * At this stage no more interrupts will arrive so we may safely clean * the queueable objects here in case they failed to get cleaned so far. */ if (IS_PF(bp)) @@ -2955,7 +2972,6 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) bnx2x_set_reset_global(bp); } - /* The last driver must disable a "close the gate" if there is no * parity attention or "process kill" pending. */ @@ -3040,6 +3056,8 @@ int bnx2x_poll(struct napi_struct *napi, int budget) return 0; } #endif + if (!bnx2x_fp_lock_napi(fp)) + return work_done; for_each_cos_in_tx_queue(fp, cos) if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) @@ -3049,12 +3067,15 @@ int bnx2x_poll(struct napi_struct *napi, int budget) work_done += bnx2x_rx_int(fp, budget - work_done); /* must not complete if we consumed full budget */ - if (work_done >= budget) + if (work_done >= budget) { + bnx2x_fp_unlock_napi(fp); break; + } } /* Fall out from the NAPI loop if needed */ - if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { + if (!bnx2x_fp_unlock_napi(fp) && + !(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { /* No need to update SB for FCoE L2 ring as long as * it's connected to the default SB and the SB @@ -3096,6 +3117,32 @@ int bnx2x_poll(struct napi_struct *napi, int budget) return work_done; } +#ifdef CONFIG_NET_LL_RX_POLL +/* must be called with local_bh_disable()d */ +int bnx2x_low_latency_recv(struct napi_struct *napi) +{ + struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath, + napi); + struct bnx2x *bp = fp->bp; + int found = 0; + + if ((bp->state == BNX2X_STATE_CLOSED) || + (bp->state == BNX2X_STATE_ERROR) || + (bp->flags & (TPA_ENABLE_FLAG | GRO_ENABLE_FLAG))) + return LL_FLUSH_FAILED; + + if (!bnx2x_fp_lock_poll(fp)) + return LL_FLUSH_BUSY; + + if (bnx2x_has_rx_work(fp)) + found = bnx2x_rx_int(fp, 4); + + bnx2x_fp_unlock_poll(fp); + + return found; +} +#endif + /* we split the first BD into headers and data BDs * to ease the pain of our fellow microcode engineers * we use one mapping for both BDs @@ -3496,9 +3543,12 @@ static void bnx2x_update_pbds_gso_enc(struct sk_buff *skb, /* outer IP header info */ if (xmit_type & XMIT_CSUM_V4) { struct iphdr *iph = ip_hdr(skb); + u16 csum = (__force u16)(~iph->check) - + (__force u16)iph->tot_len - + (__force u16)iph->frag_off; + pbd2->fw_ip_csum_wo_len_flags_frag = - bswab16(csum_fold((~iph->check) - - iph->tot_len - iph->frag_off)); + bswab16(csum_fold((__force __wsum)csum)); } else { pbd2->fw_ip_hdr_to_payload_w = hlen_w - ((sizeof(struct ipv6hdr)) >> 1); @@ -3586,7 +3636,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) DP(NETIF_MSG_TX_QUEUED, "indices: txq %d, fp %d, txdata %d\n", txq_index, fp_index, txdata_index); */ - /* enable this debug print to view the tranmission details + /* enable this debug print to view the transmission details DP(NETIF_MSG_TX_QUEUED, "transmitting packet cid %d fp index %d txdata_index %d tx_data ptr %p fp pointer %p\n", txdata->cid, fp_index, txdata_index, txdata, fp); */ @@ -3968,7 +4018,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) /* setup tc must be called under rtnl lock */ ASSERT_RTNL(); - /* no traffic classes requested. aborting */ + /* no traffic classes requested. Aborting */ if (!num_tc) { netdev_reset_tc(dev); return 0; @@ -3976,7 +4026,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) /* requested to support too many traffic classes */ if (num_tc > bp->max_cos) { - BNX2X_ERR("support for too many traffic classes requested: %d. max supported is %d\n", + BNX2X_ERR("support for too many traffic classes requested: %d. Max supported is %d\n", num_tc, bp->max_cos); return -EINVAL; } @@ -3995,8 +4045,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) prio, bp->prio_to_cos[prio]); } - - /* Use this configuration to diffrentiate tc0 from other COSes + /* Use this configuration to differentiate tc0 from other COSes This can be used for ets or pfc, and save the effort of setting up a multio class queue disc or negotiating DCBX with a switch netdev_set_prio_tc_map(dev, 0, 0); @@ -4288,10 +4337,11 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index) &bnx2x_fp(bp, index, rx_desc_mapping), sizeof(struct eth_rx_bd) * NUM_RX_BD); - BNX2X_PCI_ALLOC(bnx2x_fp(bp, index, rx_comp_ring), - &bnx2x_fp(bp, index, rx_comp_mapping), - sizeof(struct eth_fast_path_rx_cqe) * - NUM_RCQ_BD); + /* Seed all CQEs by 1s */ + BNX2X_PCI_FALLOC(bnx2x_fp(bp, index, rx_comp_ring), + &bnx2x_fp(bp, index, rx_comp_mapping), + sizeof(struct eth_fast_path_rx_cqe) * + NUM_RCQ_BD); /* SGE ring */ BNX2X_ALLOC(bnx2x_fp(bp, index, rx_page_ring), @@ -4472,7 +4522,6 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) alloc_err: bnx2x_free_mem_bp(bp); return -ENOMEM; - } int bnx2x_reload_if_running(struct net_device *dev) @@ -4514,7 +4563,6 @@ int bnx2x_get_cur_phy_idx(struct bnx2x *bp) } return sel_phy_idx; - } int bnx2x_get_link_cfg_idx(struct bnx2x *bp) { @@ -4602,6 +4650,7 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features) { struct bnx2x *bp = netdev_priv(dev); u32 flags = bp->flags; + u32 changes; bool bnx2x_reload = false; if (features & NETIF_F_LRO) @@ -4626,10 +4675,16 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features) } } - if (flags ^ bp->flags) { - bp->flags = flags; + changes = flags ^ bp->flags; + + /* if GRO is changed while LRO is enabled, don't force a reload */ + if ((changes & GRO_ENABLE_FLAG) && (flags & TPA_ENABLE_FLAG)) + changes &= ~GRO_ENABLE_FLAG; + + if (changes) bnx2x_reload = true; - } + + bp->flags = flags; if (bnx2x_reload) { if (bp->recovery_state == BNX2X_RECOVERY_DONE) @@ -4724,7 +4779,6 @@ int bnx2x_resume(struct pci_dev *pdev) return rc; } - void bnx2x_set_ctx_validation(struct bnx2x *bp, struct eth_context *cxt, u32 cid) { @@ -4742,7 +4796,6 @@ static void storm_memset_hc_timeout(struct bnx2x *bp, u8 port, u8 fw_sb_id, u8 sb_index, u8 ticks) { - u32 addr = BAR_CSTRORM_INTMEM + CSTORM_STATUS_BLOCK_DATA_TIMEOUT_OFFSET(fw_sb_id, sb_index); REG_WR8(bp, addr, ticks); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 151675d66b0..c07a6d054cf 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -22,7 +22,6 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> - #include "bnx2x.h" #include "bnx2x_sriov.h" @@ -50,13 +49,25 @@ extern int int_mode; } \ } while (0) -#define BNX2X_PCI_ALLOC(x, y, size) \ -do { \ - x = dma_alloc_coherent(&bp->pdev->dev, size, y, \ - GFP_KERNEL | __GFP_ZERO); \ - if (x == NULL) \ - goto alloc_mem_err; \ -} while (0) +#define BNX2X_PCI_ALLOC(x, y, size) \ + do { \ + x = dma_alloc_coherent(&bp->pdev->dev, size, y, \ + GFP_KERNEL | __GFP_ZERO); \ + if (x == NULL) \ + goto alloc_mem_err; \ + DP(NETIF_MSG_HW, "BNX2X_PCI_ALLOC: Physical %Lx Virtual %p\n", \ + (unsigned long long)(*y), x); \ + } while (0) + +#define BNX2X_PCI_FALLOC(x, y, size) \ + do { \ + x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \ + if (x == NULL) \ + goto alloc_mem_err; \ + memset((void *)x, 0xFFFFFFFF, size); \ + DP(NETIF_MSG_HW, "BNX2X_PCI_FALLOC: Physical %Lx Virtual %p\n",\ + (unsigned long long)(*y), x); \ + } while (0) #define BNX2X_ALLOC(x, size) \ do { \ @@ -494,9 +505,6 @@ void bnx2x_update_max_mf_config(struct bnx2x *bp, u32 value); /* Error handling */ void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl); -/* validate currect fw is loaded */ -bool bnx2x_test_firmware_version(struct bnx2x *bp, bool is_err); - /* dev_close main block */ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link); @@ -607,6 +615,13 @@ int bnx2x_enable_msi(struct bnx2x *bp); int bnx2x_poll(struct napi_struct *napi, int budget); /** + * bnx2x_low_latency_recv - LL callback + * + * @napi: napi structure + */ +int bnx2x_low_latency_recv(struct napi_struct *napi); + +/** * bnx2x_alloc_mem_bp - allocate memories outsize main driver structure * * @bp: driver handle @@ -800,16 +815,18 @@ static inline bool bnx2x_has_tx_work(struct bnx2x_fastpath *fp) return false; } +#define BNX2X_IS_CQE_COMPLETED(cqe_fp) (cqe_fp->marker == 0x0) +#define BNX2X_SEED_CQE(cqe_fp) (cqe_fp->marker = 0xFFFFFFFF) static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp) { - u16 rx_cons_sb; + u16 cons; + union eth_rx_cqe *cqe; + struct eth_fast_path_rx_cqe *cqe_fp; - /* Tell compiler that status block fields can change */ - barrier(); - rx_cons_sb = le16_to_cpu(*fp->rx_cons_sb); - if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) - rx_cons_sb++; - return (fp->rx_comp_cons != rx_cons_sb); + cons = RCQ_BD(fp->rx_comp_cons); + cqe = &fp->rx_comp_ring[cons]; + cqe_fp = &cqe->fast_path_cqe; + return BNX2X_IS_CQE_COMPLETED(cqe_fp); } /** @@ -848,9 +865,11 @@ static inline void bnx2x_add_all_napi_cnic(struct bnx2x *bp) int i; /* Add NAPI objects */ - for_each_rx_queue_cnic(bp, i) + for_each_rx_queue_cnic(bp, i) { netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, NAPI_POLL_WEIGHT); + napi_hash_add(&bnx2x_fp(bp, i, napi)); + } } static inline void bnx2x_add_all_napi(struct bnx2x *bp) @@ -858,25 +877,31 @@ static inline void bnx2x_add_all_napi(struct bnx2x *bp) int i; /* Add NAPI objects */ - for_each_eth_queue(bp, i) + for_each_eth_queue(bp, i) { netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, NAPI_POLL_WEIGHT); + napi_hash_add(&bnx2x_fp(bp, i, napi)); + } } static inline void bnx2x_del_all_napi_cnic(struct bnx2x *bp) { int i; - for_each_rx_queue_cnic(bp, i) + for_each_rx_queue_cnic(bp, i) { + napi_hash_del(&bnx2x_fp(bp, i, napi)); netif_napi_del(&bnx2x_fp(bp, i, napi)); + } } static inline void bnx2x_del_all_napi(struct bnx2x *bp) { int i; - for_each_eth_queue(bp, i) + for_each_eth_queue(bp, i) { + napi_hash_del(&bnx2x_fp(bp, i, napi)); netif_napi_del(&bnx2x_fp(bp, i, napi)); + } } int bnx2x_set_int_mode(struct bnx2x *bp); @@ -1171,7 +1196,6 @@ static inline u8 bnx2x_cnic_eth_cl_id(struct bnx2x *bp, u8 cl_idx) static inline u8 bnx2x_cnic_fw_sb_id(struct bnx2x *bp) { - /* the 'first' id is allocated for the cnic */ return bp->base_fw_ndsb; } @@ -1181,7 +1205,6 @@ static inline u8 bnx2x_cnic_igu_sb_id(struct bnx2x *bp) return bp->igu_base_sb; } - static inline void bnx2x_init_fcoe_fp(struct bnx2x *bp) { struct bnx2x_fastpath *fp = bnx2x_fcoe_fp(bp); @@ -1334,8 +1357,8 @@ static inline bool bnx2x_mtu_allows_gro(int mtu) int fpp = SGE_PAGE_SIZE / (mtu - ETH_MAX_TPA_HEADER_SIZE); /* - * 1. number of frags should not grow above MAX_SKB_FRAGS - * 2. frag must fit the page + * 1. Number of frags should not grow above MAX_SKB_FRAGS + * 2. Frag must fit the page */ return mtu <= SGE_PAGE_SIZE && (U_ETH_SGL_SIZE * fpp) <= MAX_SKB_FRAGS; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 4b077a7f16a..0c94df47e0e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -253,7 +253,6 @@ static void bnx2x_dcbx_get_ets_feature(struct bnx2x *bp, memset(&pg_help_data, 0, sizeof(struct pg_help_data)); - if (GET_FLAGS(error, DCBX_LOCAL_ETS_ERROR)) DP(BNX2X_MSG_DCB, "DCBX_LOCAL_ETS_ERROR\n"); @@ -298,7 +297,6 @@ static void bnx2x_dcbx_get_ets_feature(struct bnx2x *bp, static void bnx2x_dcbx_get_pfc_feature(struct bnx2x *bp, struct dcbx_pfc_feature *pfc, u32 error) { - if (GET_FLAGS(error, DCBX_LOCAL_PFC_ERROR)) DP(BNX2X_MSG_DCB, "DCBX_LOCAL_PFC_ERROR\n"); @@ -367,7 +365,6 @@ static int bnx2x_dcbx_read_mib(struct bnx2x *bp, struct lldp_remote_mib *remote_mib ; struct lldp_local_mib *local_mib; - switch (read_mib_type) { case DCBX_READ_LOCAL_MIB: mib_size = sizeof(struct lldp_local_mib); @@ -629,7 +626,6 @@ static int bnx2x_dcbx_read_shmem_neg_results(struct bnx2x *bp) return 0; } - #ifdef BCM_DCBNL static inline u8 bnx2x_dcbx_dcbnl_app_up(struct dcbx_app_priority_entry *ent) @@ -691,7 +687,7 @@ static inline void bnx2x_dcbx_update_tc_mapping(struct bnx2x *bp) } /* setup tc must be called under rtnl lock, but we can't take it here - * as we are handling an attetntion on a work queue which must be + * as we are handling an attention on a work queue which must be * flushed at some rtnl-locked contexts (e.g. if down) */ if (!test_and_set_bit(BNX2X_SP_RTNL_SETUP_TC, &bp->sp_rtnl_state)) @@ -711,7 +707,7 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state) */ bnx2x_dcbnl_update_applist(bp, true); - /* Read rmeote mib if dcbx is in the FW */ + /* Read remote mib if dcbx is in the FW */ if (bnx2x_dcbx_read_shmem_remote_mib(bp)) return; #endif @@ -742,7 +738,7 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state) bnx2x_dcbx_update_tc_mapping(bp); /* - * allow other funtions to update their netdevices + * allow other functions to update their netdevices * accordingly */ if (IS_MF(bp)) @@ -864,7 +860,7 @@ static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp, i, DCBX_PRI_PG_GET(af->ets.pri_pg_tbl, i)); } - /*For IEEE admin_recommendation_bw_precentage + /*For IEEE admin_recommendation_bw_percentage *For IEEE admin_recommendation_ets_pg */ af->pfc.pri_en_bitmap = (u8)dp->admin_pfc_bitmap; for (i = 0; i < DCBX_CONFIG_MAX_APP_PROTOCOL; i++) { @@ -896,13 +892,11 @@ static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp, } af->app.default_pri = (u8)dp->admin_default_priority; - } /* Write the data. */ bnx2x_write_data(bp, (u32 *)&admin_mib, offset, sizeof(struct lldp_admin_mib)); - } void bnx2x_dcbx_set_state(struct bnx2x *bp, bool dcb_on, u32 dcbx_enabled) @@ -1076,7 +1070,7 @@ static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp, bool pg_found = false; u32 i, traf_type, add_traf_type, add_pg; u32 *ttp = bp->dcbx_port_params.app.traffic_type_priority; - struct pg_entry_help_data *data = help_data->data; /*shotcut*/ + struct pg_entry_help_data *data = help_data->data; /*shortcut*/ /* Set to invalid */ for (i = 0; i < LLFC_DRIVER_TRAFFIC_TYPE_MAX; i++) @@ -1172,7 +1166,8 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp, DCBX_PG_BW_GET(ets->pg_bw_tbl, pg_entry)); else /* If we join a group and one is strict - * than the bw rulls */ + * than the bw rules + */ cos_data->data[entry].strict = BNX2X_DCBX_STRICT_COS_HIGHEST; } @@ -1181,7 +1176,6 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp, BNX2X_ERR("dcbx error: Both groups must have priorities\n"); } - #ifndef POWER_OF_2 #define POWER_OF_2(x) ((0 != x) && (0 == (x & (x-1)))) #endif @@ -1284,7 +1278,7 @@ static void bnx2x_dcbx_2cos_limit_cee_single_pg_to_cos_params(struct bnx2x *bp, } else { /* If there are only pauseable priorities or * only non-pauseable,* the lower priorities go - * to the first queue and the higherpriorities go + * to the first queue and the higher priorities go * to the second queue. */ cos_data->data[0].pausable = @@ -1484,7 +1478,7 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params( * queue and one priority goes to the second queue. * * We will join this two cases: - * if one is BW limited it will go to the secoend queue + * if one is BW limited it will go to the second queue * otherwise the last priority will get it */ @@ -1504,7 +1498,8 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params( false == b_found_strict) /* last entry will be handled separately * If no priority is strict than last - * enty goes to last queue.*/ + * entry goes to last queue. + */ entry = 1; cos_data->data[entry].pri_join_mask |= pri_tested; @@ -1516,7 +1511,8 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params( b_found_strict = true; cos_data->data[1].pri_join_mask |= pri_tested; /* If we join a group and one is strict - * than the bw rulls */ + * than the bw rules + */ cos_data->data[1].strict = BNX2X_DCBX_STRICT_COS_HIGHEST; } @@ -1524,7 +1520,6 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params( } } - static void bnx2x_dcbx_2cos_limit_cee_fill_cos_params(struct bnx2x *bp, struct pg_help_data *help_data, struct dcbx_ets_feature *ets, @@ -1533,7 +1528,6 @@ static void bnx2x_dcbx_2cos_limit_cee_fill_cos_params(struct bnx2x *bp, u32 pri_join_mask, u8 num_of_dif_pri) { - /* default E2 settings */ cos_data->num_of_cos = DCBX_COS_MAX_NUM_E2; @@ -1629,7 +1623,6 @@ static u8 bnx2x_dcbx_cee_fill_strict_pri(struct bnx2x *bp, u8 num_spread_of_entries, u8 strict_app_pris) { - if (bnx2x_dcbx_spread_strict_pri(bp, cos_data, entry, num_spread_of_entries, strict_app_pris)) { @@ -1848,7 +1841,7 @@ static void bnx2x_dcbx_fw_struct(struct bnx2x *bp, void bnx2x_dcbx_pmf_update(struct bnx2x *bp) { - /* if we need to syncronize DCBX result from prev PMF + /* if we need to synchronize DCBX result from prev PMF * read it from shmem and update bp and netdev accordingly */ if (SHMEM2_HAS(bp, drv_flags) && @@ -1876,7 +1869,6 @@ void bnx2x_dcbx_pmf_update(struct bnx2x *bp) * dcbx negotiation. */ bnx2x_dcbx_update_tc_mapping(bp); - } } @@ -1943,14 +1935,14 @@ static void bnx2x_dcbnl_set_pg_tccfg_tx(struct net_device *netdev, int prio, return; /** - * bw_pct ingnored - band-width percentage devision between user + * bw_pct ignored - band-width percentage devision between user * priorities within the same group is not * standard and hence not supported * - * prio_type igonred - priority levels within the same group are not + * prio_type ignored - priority levels within the same group are not * standard and hence are not supported. According * to the standard pgid 15 is dedicated to strict - * prioirty traffic (on the port level). + * priority traffic (on the port level). * * up_map ignored */ @@ -1995,14 +1987,14 @@ static void bnx2x_dcbnl_get_pg_tccfg_tx(struct net_device *netdev, int prio, DP(BNX2X_MSG_DCB, "prio = %d\n", prio); /** - * bw_pct ingnored - band-width percentage devision between user + * bw_pct ignored - band-width percentage devision between user * priorities within the same group is not * standard and hence not supported * - * prio_type igonred - priority levels within the same group are not + * prio_type ignored - priority levels within the same group are not * standard and hence are not supported. According * to the standard pgid 15 is dedicated to strict - * prioirty traffic (on the port level). + * priority traffic (on the port level). * * up_map ignored */ @@ -2389,7 +2381,7 @@ static u8 bnx2x_dcbnl_get_featcfg(struct net_device *netdev, int featid, *flags |= DCB_FEATCFG_ERROR; break; default: - BNX2X_ERR("Non valid featrue-ID\n"); + BNX2X_ERR("Non valid feature-ID\n"); rval = 1; break; } @@ -2430,7 +2422,7 @@ static u8 bnx2x_dcbnl_set_featcfg(struct net_device *netdev, int featid, flags & DCB_FEATCFG_WILLING ? 1 : 0; break; default: - BNX2X_ERR("Non valid featrue-ID\n"); + BNX2X_ERR("Non valid feature-ID\n"); rval = 1; break; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h index d153f44cf8f..125bd1b6586 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h @@ -134,8 +134,6 @@ enum { #define PFC_BRB1_REG_HIGH_LLFC_LOW_THRESHOLD 130 #define PFC_BRB1_REG_HIGH_LLFC_HIGH_THRESHOLD 170 - - struct cos_entry_help_data { u32 pri_join_mask; u32 cos_bw; @@ -170,7 +168,6 @@ struct cos_help_data { (!(IS_DCBX_PFC_PRI_ONLY_NON_PAUSE((bp), (pg_pri)) || \ IS_DCBX_PFC_PRI_ONLY_PAUSE((bp), (pg_pri)))) - struct pg_entry_help_data { u8 num_of_dif_pri; u8 pg; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h index bff5e33eaa1..12eb4baee9f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h @@ -13,12 +13,6 @@ * consent. */ - -/* This struct holds a signature to ensure the dump returned from the driver - * match the meta data file inserted to grc_dump.tcl - * The signature is time stamp, diag version and grc_dump version - */ - #ifndef BNX2X_DUMP_H #define BNX2X_DUMP_H @@ -28,7 +22,6 @@ #define DRV_DUMP_USTORM_WAITP_ADDRESS 0x338a80 #define DRV_DUMP_CSTORM_WAITP_ADDRESS 0x238a80 - /* Possible Chips */ #define DUMP_CHIP_E1 1 #define DUMP_CHIP_E1H 2 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index ce1a9161867..c5f22510168 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -320,7 +320,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) speed = ethtool_cmd_speed(cmd); - /* If recieved a request for an unknown duplex, assume full*/ + /* If received a request for an unknown duplex, assume full*/ if (cmd->duplex == DUPLEX_UNKNOWN) cmd->duplex = DUPLEX_FULL; @@ -733,7 +733,6 @@ static bool bnx2x_is_reg_in_chip(struct bnx2x *bp, return false; } - static bool bnx2x_is_wreg_in_chip(struct bnx2x *bp, const struct wreg_addr *wreg_info) { @@ -850,7 +849,7 @@ static int __bnx2x_get_preset_regs(struct bnx2x *bp, u32 *p, u32 preset) /* Paged registers are supported in E2 & E3 only */ if (CHIP_IS_E2(bp) || CHIP_IS_E3(bp)) { - /* Read "paged" registes */ + /* Read "paged" registers */ bnx2x_read_pages_regs(bp, p, preset); } @@ -960,6 +959,9 @@ static int bnx2x_set_dump(struct net_device *dev, struct ethtool_dump *val) struct bnx2x *bp = netdev_priv(dev); /* Use the ethtool_dump "flag" field as the dump preset index */ + if (val->flag < 1 || val->flag > DUMP_MAX_PRESETS) + return -EINVAL; + bp->dump_preset_idx = val->flag; return 0; } @@ -969,12 +971,12 @@ static int bnx2x_get_dump_flag(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); + dump->version = BNX2X_DUMP_VERSION; + dump->flag = bp->dump_preset_idx; /* Calculate the requested preset idx length */ dump->len = bnx2x_get_preset_regs_len(dev, bp->dump_preset_idx); DP(BNX2X_MSG_ETHTOOL, "Get dump preset %d length=%d\n", bp->dump_preset_idx, dump->len); - - dump->flag = ETHTOOL_GET_DUMP_DATA; return 0; } @@ -986,8 +988,6 @@ static int bnx2x_get_dump_data(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); struct dump_header dump_hdr = {0}; - memset(p, 0, dump->len); - /* Disable parity attentions as long as following dump may * cause false alarms by reading never written registers. We * will re-enable parity attentions right after the dump. @@ -1155,8 +1155,8 @@ static int bnx2x_get_eeprom_len(struct net_device *dev) return bp->common.flash_size; } -/* Per pf misc lock must be aquired before the per port mcp lock. Otherwise, had - * we done things the other way around, if two pfs from the same port would +/* Per pf misc lock must be acquired before the per port mcp lock. Otherwise, + * had we done things the other way around, if two pfs from the same port would * attempt to access nvram at the same time, we could run into a scenario such * as: * pf A takes the port lock. @@ -1381,12 +1381,29 @@ static int bnx2x_nvram_read32(struct bnx2x *bp, u32 offset, u32 *buf, return rc; } +static bool bnx2x_is_nvm_accessible(struct bnx2x *bp) +{ + int rc = 1; + u16 pm = 0; + struct net_device *dev = pci_get_drvdata(bp->pdev); + + if (bp->pm_cap) + rc = pci_read_config_word(bp->pdev, + bp->pm_cap + PCI_PM_CTRL, &pm); + + if ((rc && !netif_running(dev)) || + (!rc && ((pm & PCI_PM_CTRL_STATE_MASK) != (__force u16)PCI_D0))) + return false; + + return true; +} + static int bnx2x_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *eebuf) { struct bnx2x *bp = netdev_priv(dev); - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1411,7 +1428,7 @@ static int bnx2x_get_module_eeprom(struct net_device *dev, u8 *user_data = data; unsigned int start_addr = ee->offset, xfer_size = 0; - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1474,7 +1491,7 @@ static int bnx2x_get_module_info(struct net_device *dev, int phy_idx, rc; u8 sff8472_comp, diag_type; - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1594,8 +1611,10 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf, */ val = be32_to_cpu(val_be); - val &= ~le32_to_cpu(0xff << BYTE_OFFSET(offset)); - val |= le32_to_cpu(*data_buf << BYTE_OFFSET(offset)); + val &= ~le32_to_cpu((__force __le32) + (0xff << BYTE_OFFSET(offset))); + val |= le32_to_cpu((__force __le32) + (*data_buf << BYTE_OFFSET(offset))); rc = bnx2x_nvram_write_dword(bp, align_offset, val, cmd_flags); @@ -1676,7 +1695,8 @@ static int bnx2x_set_eeprom(struct net_device *dev, int port = BP_PORT(bp); int rc = 0; u32 ext_phy_config; - if (!netif_running(dev)) { + + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1921,6 +1941,19 @@ static const char bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF][ETH_GSTRING_LEN] = { "link_test (online) " }; +enum { + BNX2X_PRI_FLAG_ISCSI, + BNX2X_PRI_FLAG_FCOE, + BNX2X_PRI_FLAG_STORAGE, + BNX2X_PRI_FLAG_LEN, +}; + +static const char bnx2x_private_arr[BNX2X_PRI_FLAG_LEN][ETH_GSTRING_LEN] = { + "iSCSI offload support", + "FCoE offload support", + "Storage only interface" +}; + static u32 bnx2x_eee_to_adv(u32 eee_adv) { u32 modes = 0; @@ -2041,7 +2074,7 @@ static int bnx2x_set_eee(struct net_device *dev, struct ethtool_eee *edata) EEE_MODE_OVERRIDE_NVRAM | EEE_MODE_OUTPUT_TIME; - /* Restart link to propogate changes */ + /* Restart link to propagate changes */ if (netif_running(dev)) { bnx2x_stats_handle(bp, STATS_EVENT_STOP); bnx2x_force_link_reset(bp); @@ -2160,7 +2193,7 @@ static int bnx2x_test_registers(struct bnx2x *bp) { BNX2X_CHIP_MASK_ALL, 0xffffffff, 0, 0x00000000 } }; - if (!netif_running(bp->dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return rc; @@ -2264,7 +2297,7 @@ static int bnx2x_test_memory(struct bnx2x *bp) { NULL, 0xffffffff, {0, 0, 0, 0} } }; - if (!netif_running(bp->dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return rc; @@ -2978,32 +3011,47 @@ static int bnx2x_num_stat_queues(struct bnx2x *bp) static int bnx2x_get_sset_count(struct net_device *dev, int stringset) { struct bnx2x *bp = netdev_priv(dev); - int i, num_stats; + int i, num_strings = 0; switch (stringset) { case ETH_SS_STATS: if (is_multi(bp)) { - num_stats = bnx2x_num_stat_queues(bp) * - BNX2X_NUM_Q_STATS; + num_strings = bnx2x_num_stat_queues(bp) * + BNX2X_NUM_Q_STATS; } else - num_stats = 0; + num_strings = 0; if (IS_MF_MODE_STAT(bp)) { for (i = 0; i < BNX2X_NUM_STATS; i++) if (IS_FUNC_STAT(i)) - num_stats++; + num_strings++; } else - num_stats += BNX2X_NUM_STATS; + num_strings += BNX2X_NUM_STATS; - return num_stats; + return num_strings; case ETH_SS_TEST: return BNX2X_NUM_TESTS(bp); + case ETH_SS_PRIV_FLAGS: + return BNX2X_PRI_FLAG_LEN; + default: return -EINVAL; } } +static u32 bnx2x_get_private_flags(struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + u32 flags = 0; + + flags |= (!(bp->flags & NO_ISCSI_FLAG) ? 1 : 0) << BNX2X_PRI_FLAG_ISCSI; + flags |= (!(bp->flags & NO_FCOE_FLAG) ? 1 : 0) << BNX2X_PRI_FLAG_FCOE; + flags |= (!!IS_MF_STORAGE_ONLY(bp)) << BNX2X_PRI_FLAG_STORAGE; + + return flags; +} + static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { struct bnx2x *bp = netdev_priv(dev); @@ -3026,7 +3074,6 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) } } - for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) { if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i)) continue; @@ -3045,6 +3092,12 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) start = 4; memcpy(buf, bnx2x_tests_str_arr + start, ETH_GSTRING_LEN * BNX2X_NUM_TESTS(bp)); + break; + + case ETH_SS_PRIV_FLAGS: + memcpy(buf, bnx2x_private_arr, + ETH_GSTRING_LEN * BNX2X_PRI_FLAG_LEN); + break; } } @@ -3106,17 +3159,12 @@ static int bnx2x_set_phys_id(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; } - if (!bp->port.pmf) { - DP(BNX2X_MSG_ETHTOOL, "Interface is not pmf\n"); - return -EOPNOTSUPP; - } - switch (state) { case ETHTOOL_ID_ACTIVE: return 1; /* cycle on/off once per second */ @@ -3148,7 +3196,6 @@ static int bnx2x_set_phys_id(struct net_device *dev, static int bnx2x_get_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) { - switch (info->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: @@ -3384,7 +3431,6 @@ static int bnx2x_set_channels(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); - DP(BNX2X_MSG_ETHTOOL, "set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n", channels->rx_count, channels->tx_count, channels->other_count, @@ -3445,6 +3491,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .set_pauseparam = bnx2x_set_pauseparam, .self_test = bnx2x_self_test, .get_sset_count = bnx2x_get_sset_count, + .get_priv_flags = bnx2x_get_private_flags, .get_strings = bnx2x_get_strings, .set_phys_id = bnx2x_set_phys_id, .get_ethtool_stats = bnx2x_get_ethtool_stats, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 12f00a40cdf..5018e52ae2a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1323,6 +1323,8 @@ struct drv_func_mb { #define DRV_MSG_CODE_UNLOAD_SKIP_LINK_RESET 0x00000002 #define DRV_MSG_CODE_LOAD_REQ_WITH_LFA 0x0000100a + #define DRV_MSG_CODE_LOAD_REQ_FORCE_LFA 0x00002000 + u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 #define FW_MSG_CODE_DRV_LOAD_COMMON 0x10100000 @@ -3816,7 +3818,8 @@ struct eth_fast_path_rx_cqe { __le16 len_on_bd; struct parsing_flags pars_flags; union eth_sgl_or_raw_data sgl_or_raw_data; - __le32 reserved1[8]; + __le32 reserved1[7]; + u32 marker; }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index b4c9dea93a5..15a528bda87 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -93,7 +93,6 @@ MODULE_FIRMWARE(FW_FILE_NAME_E1); MODULE_FIRMWARE(FW_FILE_NAME_E1H); MODULE_FIRMWARE(FW_FILE_NAME_E2); - int num_queues; module_param(num_queues, int, 0); MODULE_PARM_DESC(num_queues, @@ -103,8 +102,6 @@ static int disable_tpa; module_param(disable_tpa, int, 0); MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature"); -#define INT_MODE_INTx 1 -#define INT_MODE_MSI 2 int int_mode; module_param(int_mode, int, 0); MODULE_PARM_DESC(int_mode, " Force interrupt mode other than MSI-X " @@ -122,8 +119,6 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, " Default debug msglevel"); - - struct workqueue_struct *bnx2x_wq; struct bnx2x_mac_vals { @@ -376,9 +371,11 @@ static u32 bnx2x_reg_rd_ind(struct bnx2x *bp, u32 addr) #define DMAE_DP_DST_PCI "pci dst_addr [%x:%08x]" #define DMAE_DP_DST_NONE "dst_addr [none]" -void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae, int msglvl) +static void bnx2x_dp_dmae(struct bnx2x *bp, + struct dmae_command *dmae, int msglvl) { u32 src_type = dmae->opcode & DMAE_COMMAND_SRC; + int i; switch (dmae->opcode & DMAE_COMMAND_DST) { case DMAE_CMD_DST_PCI: @@ -434,6 +431,10 @@ void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae, int msglvl) dmae->comp_val); break; } + + for (i = 0; i < (sizeof(struct dmae_command)/4); i++) + DP(msglvl, "DMAE RAW [%02d]: 0x%08x\n", + i, *(((u32 *)dmae) + i)); } /* copy command into DMAE command memory and set DMAE command go */ @@ -508,8 +509,9 @@ int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae) int cnt = CHIP_REV_IS_SLOW(bp) ? (400000) : 4000; int rc = 0; - /* - * Lock the dmae channel. Disable BHs to prevent a dead-lock + bnx2x_dp_dmae(bp, dmae, BNX2X_MSG_DMAE); + + /* Lock the dmae channel. Disable BHs to prevent a dead-lock * as long as this code is called both from syscall context and * from ndo_set_rx_mode() flow that may be called from BH. */ @@ -548,6 +550,7 @@ unlock: void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr, u32 len32) { + int rc; struct dmae_command dmae; if (!bp->dmae_ready) { @@ -571,11 +574,16 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr, dmae.len = len32; /* issue the command and wait for completion */ - bnx2x_issue_dmae_with_comp(bp, &dmae); + rc = bnx2x_issue_dmae_with_comp(bp, &dmae); + if (rc) { + BNX2X_ERR("DMAE returned failure %d\n", rc); + bnx2x_panic(); + } } void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32) { + int rc; struct dmae_command dmae; if (!bp->dmae_ready) { @@ -603,7 +611,11 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32) dmae.len = len32; /* issue the command and wait for completion */ - bnx2x_issue_dmae_with_comp(bp, &dmae); + rc = bnx2x_issue_dmae_with_comp(bp, &dmae); + if (rc) { + BNX2X_ERR("DMAE returned failure %d\n", rc); + bnx2x_panic(); + } } static void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr, @@ -811,8 +823,8 @@ static void bnx2x_hc_int_disable(struct bnx2x *bp) u32 val = REG_RD(bp, addr); /* in E1 we must use only PCI configuration space to disable - * MSI/MSIX capablility - * It's forbitten to disable IGU_PF_CONF_MSI_MSIX_EN in HC block + * MSI/MSIX capability + * It's forbidden to disable IGU_PF_CONF_MSI_MSIX_EN in HC block */ if (CHIP_IS_E1(bp)) { /* Since IGU_PF_CONF_MSI_MSIX_EN still always on @@ -839,7 +851,7 @@ static void bnx2x_hc_int_disable(struct bnx2x *bp) REG_WR(bp, addr, val); if (REG_RD(bp, addr) != val) - BNX2X_ERR("BUG! proper val not read from IGU!\n"); + BNX2X_ERR("BUG! Proper val not read from IGU!\n"); } static void bnx2x_igu_int_disable(struct bnx2x *bp) @@ -857,7 +869,7 @@ static void bnx2x_igu_int_disable(struct bnx2x *bp) REG_WR(bp, IGU_REG_PF_CONFIGURATION, val); if (REG_RD(bp, IGU_REG_PF_CONFIGURATION) != val) - BNX2X_ERR("BUG! proper val not read from IGU!\n"); + BNX2X_ERR("BUG! Proper val not read from IGU!\n"); } static void bnx2x_int_disable(struct bnx2x *bp) @@ -917,7 +929,6 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int) sp_sb_data.p_func.vf_valid, sp_sb_data.state); - for_each_eth_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; int loop; @@ -1016,7 +1027,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int) hc_sm_p[j].timer_value); } - /* Indecies data */ + /* Indices data */ for (j = 0; j < loop; j++) { pr_cont("INDEX[%d] flags (0x%x) timeout (0x%x)\n", j, hc_index_p[j].flags, @@ -1027,6 +1038,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int) #ifdef BNX2X_STOP_ON_ERROR /* event queue */ + BNX2X_ERR("eq cons %x prod %x\n", bp->eq_cons, bp->eq_prod); for (i = 0; i < NUM_EQ_DESC; i++) { u32 *data = (u32 *)&bp->eq_ring[i].message.data; @@ -1111,7 +1123,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int) * bnx2x_pf_flr_clnup() is called during nic_load in the per function HW * initialization. */ -#define FLR_WAIT_USEC 10000 /* 10 miliseconds */ +#define FLR_WAIT_USEC 10000 /* 10 milliseconds */ #define FLR_WAIT_INTERVAL 50 /* usec */ #define FLR_POLL_CNT (FLR_WAIT_USEC/FLR_WAIT_INTERVAL) /* 200 */ @@ -1290,7 +1302,6 @@ void bnx2x_tx_hw_flushed(struct bnx2x *bp, u32 poll_count) for (i = 0; i < ARRAY_SIZE(cmd_regs); i++) bnx2x_pbf_pN_cmd_flushed(bp, &cmd_regs[i], poll_count); - /* Verify the transmission buffers are flushed P0, P1, P4 */ for (i = 0; i < ARRAY_SIZE(buf_regs); i++) bnx2x_pbf_pN_buf_flushed(bp, &buf_regs[i], poll_count); @@ -1305,11 +1316,9 @@ void bnx2x_tx_hw_flushed(struct bnx2x *bp, u32 poll_count) #define OP_GEN_AGG_VECT(index) \ (((index) << SDM_OP_GEN_AGG_VECT_IDX_SHIFT) & SDM_OP_GEN_AGG_VECT_IDX) - int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func, u32 poll_cnt) { u32 op_gen_command = 0; - u32 comp_addr = BAR_CSTRORM_INTMEM + CSTORM_FINAL_CLEANUP_COMPLETE_OFFSET(clnup_func); int ret = 0; @@ -1334,7 +1343,7 @@ int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func, u32 poll_cnt) bnx2x_panic(); return 1; } - /* Zero completion for nxt FLR */ + /* Zero completion for next FLR */ REG_WR(bp, comp_addr, 0); return ret; @@ -1352,7 +1361,6 @@ u8 bnx2x_is_pcie_pending(struct pci_dev *dev) */ static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt) { - /* wait for CFC PF usage-counter to zero (includes all the VFs) */ if (bnx2x_flr_clnup_poll_hw_counter(bp, CFC_REG_NUM_LCIDS_INSIDE_PF, @@ -1360,7 +1368,6 @@ static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt) poll_cnt)) return 1; - /* Wait for DQ PF usage-counter to zero (until DQ cleanup) */ if (bnx2x_flr_clnup_poll_hw_counter(bp, DORQ_REG_PF_USAGE_CNT, @@ -1390,7 +1397,7 @@ static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt) /* Wait DMAE PF usage counter to zero */ if (bnx2x_flr_clnup_poll_hw_counter(bp, dmae_reg_go_c[INIT_DMAE_C(bp)], - "DMAE dommand register timed out", + "DMAE command register timed out", poll_cnt)) return 1; @@ -1770,7 +1777,7 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe) break; case (RAMROD_CMD_ID_ETH_TERMINATE): - DP(BNX2X_MSG_SP, "got MULTI[%d] teminate ramrod\n", cid); + DP(BNX2X_MSG_SP, "got MULTI[%d] terminate ramrod\n", cid); drv_cmd = BNX2X_Q_CMD_TERMINATE; break; @@ -1859,7 +1866,6 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) mask = 0x2 << (fp->index + CNIC_SUPPORT(bp)); if (status & mask) { /* Handle Rx or Tx according to SB id */ - prefetch(fp->rx_cons_sb); for_each_cos_in_tx_queue(fp, cos) prefetch(fp->txdata_ptr[cos]->tx_cons_sb); prefetch(&fp->sb_running_index[SM_RX_ID]); @@ -1947,7 +1953,7 @@ int bnx2x_acquire_hw_lock(struct bnx2x *bp, u32 resource) if (lock_status & resource_bit) return 0; - msleep(5); + usleep_range(5000, 10000); } BNX2X_ERR("Timeout\n"); return -EAGAIN; @@ -1982,8 +1988,8 @@ int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource) /* Validating that the resource is currently taken */ lock_status = REG_RD(bp, hw_lock_control_reg); if (!(lock_status & resource_bit)) { - BNX2X_ERR("lock_status 0x%x resource_bit 0x%x. unlock was called but lock wasn't taken!\n", - lock_status, resource_bit); + BNX2X_ERR("lock_status 0x%x resource_bit 0x%x. Unlock was called but lock wasn't taken!\n", + lock_status, resource_bit); return -EFAULT; } @@ -1991,7 +1997,6 @@ int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource) return 0; } - int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port) { /* The GPIO should be swapped if swap register is set and active */ @@ -2347,14 +2352,13 @@ u8 bnx2x_link_test(struct bnx2x *bp, u8 is_serdes) return rc; } - /* Calculates the sum of vn_min_rates. It's needed for further normalizing of the min_rates. Returns: sum of vn_min_rates. or 0 - if all the min_rates are 0. - In the later case fainess algorithm should be deactivated. + In the later case fairness algorithm should be deactivated. If not all min_rates are zero then those that are zeroes will be set to 1. */ static void bnx2x_calc_vn_min(struct bnx2x *bp, @@ -2419,7 +2423,6 @@ static void bnx2x_calc_vn_max(struct bnx2x *bp, int vn, input->vnic_max_rate[vn] = vn_max_rate; } - static int bnx2x_get_cmng_fns_mode(struct bnx2x *bp) { if (CHIP_REV_IS_SLOW(bp)) @@ -2435,7 +2438,7 @@ void bnx2x_read_mf_cfg(struct bnx2x *bp) int vn, n = (CHIP_MODE_IS_4_PORT(bp) ? 2 : 1); if (BP_NOMCP(bp)) - return; /* what should be the default bvalue in this case */ + return; /* what should be the default value in this case */ /* For 2 port configuration the absolute function number formula * is: @@ -2901,7 +2904,6 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param) return rc; } - static void storm_memset_func_cfg(struct bnx2x *bp, struct tstorm_eth_function_common_config *tcfg, u16 abs_fid) @@ -2935,7 +2937,7 @@ void bnx2x_func_init(struct bnx2x *bp, struct bnx2x_func_init_params *p) } /** - * bnx2x_get_tx_only_flags - Return common flags + * bnx2x_get_common_flags - Return common flags * * @bp device handle * @fp queue handle @@ -3006,7 +3008,6 @@ static unsigned long bnx2x_get_q_flags(struct bnx2x *bp, if (IS_MF_AFEX(bp)) __set_bit(BNX2X_Q_FLG_SILENT_VLAN_REM, &flags); - return flags | bnx2x_get_common_flags(bp, fp, true); } @@ -3082,7 +3083,7 @@ static void bnx2x_pf_rx_q_prep(struct bnx2x *bp, * placed on the BD (not including paddings). */ rxq_init->buf_sz = fp->rx_buf_size - BNX2X_FW_RX_ALIGN_START - - BNX2X_FW_RX_ALIGN_END - IP_HEADER_ALIGNMENT_PADDING; + BNX2X_FW_RX_ALIGN_END - IP_HEADER_ALIGNMENT_PADDING; rxq_init->cl_qzone_id = fp->cl_qzone_id; rxq_init->tpa_agg_sz = tpa_agg_size; @@ -3124,7 +3125,7 @@ static void bnx2x_pf_tx_q_prep(struct bnx2x *bp, txq_init->fw_sb_id = fp->fw_sb_id; /* - * set the tss leading client id for TX classfication == + * set the tss leading client id for TX classification == * leading RSS client id */ txq_init->tss_leading_cl_id = bnx2x_fp(bp, 0, cl_id); @@ -3196,7 +3197,6 @@ static void bnx2x_pf_init(struct bnx2x *bp) storm_memset_eq_data(bp, &eq_data, BP_FUNC(bp)); } - static void bnx2x_e1h_disable(struct bnx2x *bp) { int port = BP_PORT(bp); @@ -3212,7 +3212,7 @@ static void bnx2x_e1h_enable(struct bnx2x *bp) REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1); - /* Tx queue should be only reenabled */ + /* Tx queue should be only re-enabled */ netif_tx_wake_all_queues(bp->dev); /* @@ -3540,10 +3540,8 @@ static bool bnx2x_is_contextless_ramrod(int cmd, int cmd_type) return true; else return false; - } - /** * bnx2x_sp_post - place a single command on an SP ring * @@ -3608,14 +3606,13 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, /* * It's ok if the actual decrement is issued towards the memory * somewhere between the spin_lock and spin_unlock. Thus no - * more explict memory barrier is needed. + * more explicit memory barrier is needed. */ if (common) atomic_dec(&bp->eq_spq_left); else atomic_dec(&bp->cq_spq_left); - DP(BNX2X_MSG_SP, "SPQE[%x] (%x:%x) (cmd, common?) (%d,%d) hw_cid %x data (%x:%x) type(0x%x) left (CQ, EQ) (%x,%x)\n", bp->spq_prod_idx, (u32)U64_HI(bp->spq_mapping), @@ -3637,15 +3634,14 @@ static int bnx2x_acquire_alr(struct bnx2x *bp) might_sleep(); for (j = 0; j < 1000; j++) { - val = (1UL << 31); - REG_WR(bp, GRCBASE_MCP + 0x9c, val); - val = REG_RD(bp, GRCBASE_MCP + 0x9c); - if (val & (1L << 31)) + REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, MCPR_ACCESS_LOCK_LOCK); + val = REG_RD(bp, MCP_REG_MCPR_ACCESS_LOCK); + if (val & MCPR_ACCESS_LOCK_LOCK) break; - msleep(5); + usleep_range(5000, 10000); } - if (!(val & (1L << 31))) { + if (!(val & MCPR_ACCESS_LOCK_LOCK)) { BNX2X_ERR("Cannot acquire MCP access lock register\n"); rc = -EBUSY; } @@ -3656,7 +3652,7 @@ static int bnx2x_acquire_alr(struct bnx2x *bp) /* release split MCP access lock register */ static void bnx2x_release_alr(struct bnx2x *bp) { - REG_WR(bp, GRCBASE_MCP + 0x9c, 0); + REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, 0); } #define BNX2X_DEF_SB_ATT_IDX 0x0001 @@ -3678,7 +3674,7 @@ static u16 bnx2x_update_dsb_idx(struct bnx2x *bp) rc |= BNX2X_DEF_SB_IDX; } - /* Do not reorder: indecies reading should complete before handling */ + /* Do not reorder: indices reading should complete before handling */ barrier(); return rc; } @@ -3827,8 +3823,7 @@ static void bnx2x_fan_failure(struct bnx2x *bp) netdev_err(bp->dev, "Fan Failure on Network Controller has caused the driver to shutdown the card to prevent permanent damage.\n" "Please contact OEM Support for assistance\n"); - /* - * Schedule device reset (unload) + /* Schedule device reset (unload) * This is due to some boards consuming sufficient power when driver is * up to overheat if fan fails. */ @@ -3836,7 +3831,6 @@ static void bnx2x_fan_failure(struct bnx2x *bp) set_bit(BNX2X_SP_RTNL_FAN_FAILURE, &bp->sp_rtnl_state); smp_mb__after_clear_bit(); schedule_delayed_work(&bp->sp_rtnl_task, 0); - } static void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) @@ -4106,7 +4100,7 @@ static void bnx2x_clear_reset_global(struct bnx2x *bp) */ static bool bnx2x_reset_is_global(struct bnx2x *bp) { - u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG); + u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG); DP(NETIF_MSG_HW, "GEN_REG_VAL=0x%08x\n", val); return (val & BNX2X_GLOBAL_RESET_BIT) ? true : false; @@ -4157,7 +4151,7 @@ void bnx2x_set_reset_in_progress(struct bnx2x *bp) */ bool bnx2x_reset_is_done(struct bnx2x *bp, int engine) { - u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG); + u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG); u32 bit = engine ? BNX2X_PATH1_RST_IN_PROG_BIT : BNX2X_PATH0_RST_IN_PROG_BIT; @@ -4260,13 +4254,18 @@ static bool bnx2x_get_load_status(struct bnx2x *bp, int engine) return val != 0; } +static void _print_parity(struct bnx2x *bp, u32 reg) +{ + pr_cont(" [0x%08x] ", REG_RD(bp, reg)); +} + static void _print_next_block(int idx, const char *blk) { pr_cont("%s%s", idx ? ", " : "", blk); } -static int bnx2x_check_blocks_with_parity0(u32 sig, int par_num, - bool print) +static int bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig, + int par_num, bool print) { int i = 0; u32 cur_bit = 0; @@ -4275,33 +4274,54 @@ static int bnx2x_check_blocks_with_parity0(u32 sig, int par_num, if (sig & cur_bit) { switch (cur_bit) { case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "BRB"); + _print_parity(bp, + BRB1_REG_BRB1_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "PARSER"); + _print_parity(bp, PRS_REG_PRS_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "TSDM"); + _print_parity(bp, + TSDM_REG_TSDM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "SEARCHER"); + _print_parity(bp, SRC_REG_SRC_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "TCM"); + _print_parity(bp, + TCM_REG_TCM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "TSEMI"); + _print_parity(bp, + TSEM_REG_TSEM_PRTY_STS_0); + _print_parity(bp, + TSEM_REG_TSEM_PRTY_STS_1); + } break; case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "XPB"); + _print_parity(bp, GRCBASE_XPB + + PB_REG_PB_PRTY_STS); + } break; } @@ -4313,8 +4333,9 @@ static int bnx2x_check_blocks_with_parity0(u32 sig, int par_num, return par_num; } -static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num, - bool *global, bool print) +static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig, + int par_num, bool *global, + bool print) { int i = 0; u32 cur_bit = 0; @@ -4323,37 +4344,66 @@ static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num, if (sig & cur_bit) { switch (cur_bit) { case AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "PBF"); + _print_parity(bp, PBF_REG_PBF_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "QM"); + _print_parity(bp, QM_REG_QM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "TM"); + _print_parity(bp, TM_REG_TM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "XSDM"); + _print_parity(bp, + XSDM_REG_XSDM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "XCM"); + _print_parity(bp, XCM_REG_XCM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "XSEMI"); + _print_parity(bp, + XSEM_REG_XSEM_PRTY_STS_0); + _print_parity(bp, + XSEM_REG_XSEM_PRTY_STS_1); + } break; case AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "DOORBELLQ"); + _print_parity(bp, + DORQ_REG_DORQ_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "NIG"); + if (CHIP_IS_E1x(bp)) { + _print_parity(bp, + NIG_REG_NIG_PRTY_STS); + } else { + _print_parity(bp, + NIG_REG_NIG_PRTY_STS_0); + _print_parity(bp, + NIG_REG_NIG_PRTY_STS_1); + } + } break; case AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR: if (print) @@ -4362,32 +4412,52 @@ static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num, *global = true; break; case AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "DEBUG"); + _print_parity(bp, DBG_REG_DBG_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "USDM"); + _print_parity(bp, + USDM_REG_USDM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "UCM"); + _print_parity(bp, UCM_REG_UCM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "USEMI"); + _print_parity(bp, + USEM_REG_USEM_PRTY_STS_0); + _print_parity(bp, + USEM_REG_USEM_PRTY_STS_1); + } break; case AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "UPB"); + _print_parity(bp, GRCBASE_UPB + + PB_REG_PB_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "CSDM"); + _print_parity(bp, + CSDM_REG_CSDM_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "CCM"); + _print_parity(bp, CCM_REG_CCM_PRTY_STS); + } break; } @@ -4399,8 +4469,8 @@ static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num, return par_num; } -static int bnx2x_check_blocks_with_parity2(u32 sig, int par_num, - bool print) +static int bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig, + int par_num, bool print) { int i = 0; u32 cur_bit = 0; @@ -4409,12 +4479,23 @@ static int bnx2x_check_blocks_with_parity2(u32 sig, int par_num, if (sig & cur_bit) { switch (cur_bit) { case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "CSEMI"); + _print_parity(bp, + CSEM_REG_CSEM_PRTY_STS_0); + _print_parity(bp, + CSEM_REG_CSEM_PRTY_STS_1); + } break; case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "PXP"); + _print_parity(bp, PXP_REG_PXP_PRTY_STS); + _print_parity(bp, + PXP2_REG_PXP2_PRTY_STS_0); + _print_parity(bp, + PXP2_REG_PXP2_PRTY_STS_1); + } break; case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR: if (print) @@ -4422,24 +4503,42 @@ static int bnx2x_check_blocks_with_parity2(u32 sig, int par_num, "PXPPCICLOCKCLIENT"); break; case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "CFC"); + _print_parity(bp, + CFC_REG_CFC_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "CDU"); + _print_parity(bp, CDU_REG_CDU_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "DMAE"); + _print_parity(bp, + DMAE_REG_DMAE_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "IGU"); + if (CHIP_IS_E1x(bp)) + _print_parity(bp, + HC_REG_HC_PRTY_STS); + else + _print_parity(bp, + IGU_REG_IGU_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "MISC"); + _print_parity(bp, + MISC_REG_MISC_PRTY_STS); + } break; } @@ -4493,8 +4592,8 @@ static int bnx2x_check_blocks_with_parity3(u32 sig, int par_num, return par_num; } -static int bnx2x_check_blocks_with_parity4(u32 sig, int par_num, - bool print) +static int bnx2x_check_blocks_with_parity4(struct bnx2x *bp, u32 sig, + int par_num, bool print) { int i = 0; u32 cur_bit = 0; @@ -4503,12 +4602,18 @@ static int bnx2x_check_blocks_with_parity4(u32 sig, int par_num, if (sig & cur_bit) { switch (cur_bit) { case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "PGLUE_B"); + _print_parity(bp, + PGLUE_B_REG_PGLUE_B_PRTY_STS); + } break; case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR: - if (print) + if (print) { _print_next_block(par_num++, "ATC"); + _print_parity(bp, + ATC_REG_ATC_PRTY_STS); + } break; } @@ -4539,15 +4644,15 @@ static bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print, if (print) netdev_err(bp->dev, "Parity errors detected in blocks: "); - par_num = bnx2x_check_blocks_with_parity0( + par_num = bnx2x_check_blocks_with_parity0(bp, sig[0] & HW_PRTY_ASSERT_SET_0, par_num, print); - par_num = bnx2x_check_blocks_with_parity1( + par_num = bnx2x_check_blocks_with_parity1(bp, sig[1] & HW_PRTY_ASSERT_SET_1, par_num, global, print); - par_num = bnx2x_check_blocks_with_parity2( + par_num = bnx2x_check_blocks_with_parity2(bp, sig[2] & HW_PRTY_ASSERT_SET_2, par_num, print); par_num = bnx2x_check_blocks_with_parity3( sig[3] & HW_PRTY_ASSERT_SET_3, par_num, global, print); - par_num = bnx2x_check_blocks_with_parity4( + par_num = bnx2x_check_blocks_with_parity4(bp, sig[4] & HW_PRTY_ASSERT_SET_4, par_num, print); if (print) @@ -4591,7 +4696,6 @@ bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print) return bnx2x_parity_attn(bp, global, print, attn.sig); } - static void bnx2x_attn_int_deasserted4(struct bnx2x *bp, u32 attn) { u32 val; @@ -4643,7 +4747,6 @@ static void bnx2x_attn_int_deasserted4(struct bnx2x *bp, u32 attn) (u32)(attn & (AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR | AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR))); } - } static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) @@ -4878,7 +4981,6 @@ static void bnx2x_handle_classification_eqe(struct bnx2x *bp, BNX2X_ERR("Failed to schedule new commands: %d\n", rc); else if (rc > 0) DP(BNX2X_MSG_SP, "Scheduled next pending commands...\n"); - } static void bnx2x_set_iscsi_eth_rx_mode(struct bnx2x *bp, bool start); @@ -5009,7 +5111,7 @@ static void bnx2x_eq_int(struct bnx2x *bp) hw_cons = le16_to_cpu(*bp->eq_cons_sb); /* The hw_cos range is 1-255, 257 - the sw_cons range is 0-254, 256. - * when we get the the next-page we nned to adjust so the loop + * when we get the next-page we need to adjust so the loop * condition below will be met. The next element is the size of a * regular element and hence incrementing by 1 */ @@ -5075,8 +5177,6 @@ static void bnx2x_eq_int(struct bnx2x *bp) if (q_obj->complete_cmd(bp, q_obj, BNX2X_Q_CMD_CFC_DEL)) break; - - goto next_spqe; case EVENT_RING_OPCODE_STOP_TRAFFIC: @@ -5218,7 +5318,7 @@ static void bnx2x_sp_task(struct work_struct *work) DP(BNX2X_MSG_SP, "sp task invoked\n"); - /* make sure the atomic interupt_occurred has been written */ + /* make sure the atomic interrupt_occurred has been written */ smp_rmb(); if (atomic_read(&bp->interrupt_occurred)) { @@ -5265,7 +5365,6 @@ static void bnx2x_sp_task(struct work_struct *work) /* ack status block only if something was actually handled */ bnx2x_ack_sb(bp, bp->igu_dsb_id, ATTENTION_ID, le16_to_cpu(bp->def_att_idx), IGU_INT_ENABLE, 1); - } /* must be called after the EQ processing (since eq leads to sriov @@ -5316,7 +5415,6 @@ irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance) /* end of slow path */ - void bnx2x_drv_pulse(struct bnx2x *bp) { SHMEM_WR(bp, func_mb[BP_FW_MB_IDX(bp)].drv_pulse_mb, @@ -5360,7 +5458,7 @@ static void bnx2x_timer(unsigned long data) /* sample pf vf bulletin board for new posts from pf */ if (IS_VF(bp)) - bnx2x_sample_bulletin(bp); + bnx2x_timer_sriov(bp); mod_timer(&bp->timer, jiffies + bp->current_interval); } @@ -5382,7 +5480,6 @@ static void bnx2x_fill(struct bnx2x *bp, u32 addr, int fill, u32 len) else for (i = 0; i < len; i++) REG_WR8(bp, addr + i, fill); - } /* helper: writes FP SP data to FW - data_size in dwords */ @@ -5461,10 +5558,8 @@ static void bnx2x_zero_sp_sb(struct bnx2x *bp) bnx2x_fill(bp, BAR_CSTRORM_INTMEM + CSTORM_SP_SYNC_BLOCK_OFFSET(func), 0, CSTORM_SP_SYNC_BLOCK_SIZE); - } - static void bnx2x_setup_ndsb_state_machine(struct hc_status_block_sm *hc_sm, int igu_sb_id, int igu_seg_id) { @@ -5474,7 +5569,6 @@ static void bnx2x_setup_ndsb_state_machine(struct hc_status_block_sm *hc_sm, hc_sm->time_to_expire = 0xFFFFFFFF; } - /* allocates state machine ids. */ static void bnx2x_map_sb_state_machines(struct hc_index_data *index_data) { @@ -5700,7 +5794,7 @@ static void bnx2x_init_eq_ring(struct bnx2x *bp) bp->eq_cons = 0; bp->eq_prod = NUM_EQ_DESC; bp->eq_cons_sb = BNX2X_EQ_INDEX; - /* we want a warning message before it gets rought... */ + /* we want a warning message before it gets wrought... */ atomic_set(&bp->eq_spq_left, min_t(int, MAX_SP_DESC_CNT - MAX_SPQ_PENDING, NUM_EQ_DESC) - 1); } @@ -5784,7 +5878,7 @@ static int bnx2x_fill_accept_flags(struct bnx2x *bp, u32 rx_mode, break; case BNX2X_RX_MODE_PROMISC: - /* According to deffinition of SI mode, iface in promisc mode + /* According to definition of SI mode, iface in promisc mode * should receive matched and unmatched (in resolution of port) * unicast packets. */ @@ -5927,7 +6021,7 @@ static void bnx2x_init_eth_fp(struct bnx2x *bp, int fp_idx) /* init shortcut */ fp->ustorm_rx_prods_offset = bnx2x_rx_ustorm_prods_offset(fp); - /* Setup SB indicies */ + /* Setup SB indices */ fp->rx_cons_sb = BNX2X_RX_SB_INDEX; /* Configure Queue State object */ @@ -5983,6 +6077,8 @@ static void bnx2x_init_tx_ring_one(struct bnx2x_fp_txdata *txdata) BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); } + *txdata->tx_cons_sb = cpu_to_le16(0); + SET_FLAG(txdata->tx_db.data.header.header, DOORBELL_HDR_DB_TYPE, 1); txdata->tx_db.data.zero_fill1 = 0; txdata->tx_db.data.prod = 0; @@ -6001,6 +6097,7 @@ static void bnx2x_init_tx_rings_cnic(struct bnx2x *bp) for_each_tx_queue_cnic(bp, i) bnx2x_init_tx_ring_one(bp->fp[i].txdata_ptr[0]); } + static void bnx2x_init_tx_rings(struct bnx2x *bp) { int i; @@ -6043,11 +6140,6 @@ void bnx2x_pre_irq_nic_init(struct bnx2x *bp) bnx2x_init_rx_rings(bp); bnx2x_init_tx_rings(bp); - if (IS_VF(bp)) { - bnx2x_memset_stats(bp); - return; - } - if (IS_PF(bp)) { /* Initialize MOD_ABS interrupts */ bnx2x_init_mod_abs_int(bp, &bp->link_vars, bp->common.chip_id, @@ -6058,6 +6150,8 @@ void bnx2x_pre_irq_nic_init(struct bnx2x *bp) bnx2x_init_def_sb(bp); bnx2x_update_dsb_idx(bp); bnx2x_init_sp_ring(bp); + } else { + bnx2x_memset_stats(bp); } } @@ -6236,7 +6330,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) if (val == 0x10) break; - msleep(10); + usleep_range(10000, 20000); count--; } if (val != 0x10) { @@ -6251,7 +6345,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) if (val == 1) break; - msleep(10); + usleep_range(10000, 20000); count--; } if (val != 0x1) { @@ -6292,7 +6386,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) if (val == 0xb0) break; - msleep(10); + usleep_range(10000, 20000); count--; } if (val != 0xb0) { @@ -6681,7 +6775,7 @@ static int bnx2x_init_hw_common(struct bnx2x *bp) * stay set) * f. If this is VNIC 3 of a port then also init * first_timers_ilt_entry to zero and last_timers_ilt_entry - * to the last enrty in the ILT. + * to the last entry in the ILT. * * Notes: * Currently the PF error in the PGLC is non recoverable. @@ -6772,7 +6866,6 @@ static int bnx2x_init_hw_common(struct bnx2x *bp) bnx2x_init_block(bp, BLOCK_QM, PHASE_COMMON); - /* QM queues pointers table */ bnx2x_qm_init_ptr_table(bp, bp->qm_cid_count, INITOP_SET); @@ -7013,7 +7106,6 @@ static int bnx2x_init_hw_port(struct bnx2x *bp) u32 low, high; u32 val; - DP(NETIF_MSG_HW, "starting port init port %d\n", port); REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0); @@ -7078,7 +7170,6 @@ static int bnx2x_init_hw_port(struct bnx2x *bp) BRB1_REG_MAC_GUARANTIED_1 : BRB1_REG_MAC_GUARANTIED_0), 40); - bnx2x_init_block(bp, BLOCK_PRS, init_phase); if (CHIP_IS_E3B0(bp)) { if (IS_MF_AFEX(bp)) { @@ -7150,8 +7241,8 @@ static int bnx2x_init_hw_port(struct bnx2x *bp) bnx2x_init_block(bp, BLOCK_MISC_AEU, init_phase); /* init aeu_mask_attn_func_0/1: - * - SF mode: bits 3-7 are masked. only bits 0-2 are in use - * - MF mode: bit 3 is masked. bits 0-2 are in use as in SF + * - SF mode: bits 3-7 are masked. Only bits 0-2 are in use + * - MF mode: bit 3 is masked. Bits 0-2 are in use as in SF * bits 4-7 are used for "per vn group attention" */ val = IS_MF(bp) ? 0xF7 : 0x7; /* Enable DCBX attention for all but E1 */ @@ -7275,7 +7366,6 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, bool is_pf) while (!(REG_RD(bp, igu_addr_ack) & sb_bit) && --cnt) msleep(20); - if (!(REG_RD(bp, igu_addr_ack) & sb_bit)) { DP(NETIF_MSG_HW, "Unable to finish IGU cleanup: idu_sb_id %d offset %d bit %d (cnt %d)\n", @@ -7295,7 +7385,6 @@ static void bnx2x_clear_func_ilt(struct bnx2x *bp, u32 func) bnx2x_ilt_wr(bp, i, 0); } - static void bnx2x_init_searcher(struct bnx2x *bp) { int port = BP_PORT(bp); @@ -7331,7 +7420,6 @@ static int bnx2x_reset_nic_mode(struct bnx2x *bp) int rc, i, port = BP_PORT(bp); int vlan_en = 0, mac_en[NUM_MACS]; - /* Close input from network */ if (bp->mf_mode == SINGLE_FUNCTION) { bnx2x_set_rx_filter(&bp->link_params, 0); @@ -7406,7 +7494,7 @@ int bnx2x_init_hw_func_cnic(struct bnx2x *bp) bnx2x_ilt_init_op_cnic(bp, INITOP_SET); if (CONFIGURE_NIC_MODE(bp)) { - /* Configrue searcher as part of function hw init */ + /* Configure searcher as part of function hw init */ bnx2x_init_searcher(bp); /* Reset NIC mode */ @@ -7479,8 +7567,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) } else { /* Set NIC mode */ REG_WR(bp, PRS_REG_NIC_MODE, 1); - DP(NETIF_MSG_IFUP, "NIC MODE configrued\n"); - + DP(NETIF_MSG_IFUP, "NIC MODE configured\n"); } if (!CHIP_IS_E1x(bp)) { @@ -7677,7 +7764,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) } bnx2x_igu_clear_sb(bp, bp->igu_dsb_id); - /* !!! these should become driver const once + /* !!! These should become driver const once rf-tool supports split-68 const */ REG_WR(bp, IGU_REG_SB_INT_BEFORE_MASK_LSB, 0); REG_WR(bp, IGU_REG_SB_INT_BEFORE_MASK_MSB, 0); @@ -7734,7 +7821,6 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) return 0; } - void bnx2x_free_mem_cnic(struct bnx2x *bp) { bnx2x_ilt_mem_op_cnic(bp, ILT_MEMOP_FREE); @@ -7779,7 +7865,6 @@ void bnx2x_free_mem(struct bnx2x *bp) bnx2x_iov_free_mem(bp); } - int bnx2x_alloc_mem_cnic(struct bnx2x *bp) { if (!CHIP_IS_E1x(bp)) @@ -7793,7 +7878,7 @@ int bnx2x_alloc_mem_cnic(struct bnx2x *bp) host_hc_status_block_e1x)); if (CONFIGURE_NIC_MODE(bp) && !bp->t2) - /* allocate searcher T2 table, as it wan't allocated before */ + /* allocate searcher T2 table, as it wasn't allocated before */ BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, SRC_T2_SZ); /* write address to which L5 should insert its values */ @@ -8068,7 +8153,6 @@ void bnx2x_ilt_set_info(struct bnx2x *bp) ilt_client->page_size, ilt_client->flags, ilog2(ilt_client->page_size >> 12)); - } if (CNIC_SUPPORT(bp)) { @@ -8124,7 +8208,6 @@ void bnx2x_ilt_set_info(struct bnx2x *bp) static void bnx2x_pf_q_prep_init(struct bnx2x *bp, struct bnx2x_fastpath *fp, struct bnx2x_queue_init_params *init_params) { - u8 cos; int cxt_index, cxt_offset; @@ -8133,7 +8216,7 @@ static void bnx2x_pf_q_prep_init(struct bnx2x *bp, __set_bit(BNX2X_Q_FLG_HC, &init_params->rx.flags); __set_bit(BNX2X_Q_FLG_HC, &init_params->tx.flags); - /* If HC is supporterd, enable host coalescing in the transition + /* If HC is supported, enable host coalescing in the transition * to INIT state. */ __set_bit(BNX2X_Q_FLG_HC_EN, &init_params->rx.flags); @@ -8205,7 +8288,6 @@ static int bnx2x_setup_tx_only(struct bnx2x *bp, struct bnx2x_fastpath *fp, return bnx2x_queue_state_change(bp, q_params); } - /** * bnx2x_setup_queue - setup queue * @@ -8254,7 +8336,6 @@ int bnx2x_setup_queue(struct bnx2x *bp, struct bnx2x_fastpath *fp, DP(NETIF_MSG_IFUP, "init complete\n"); - /* Now move the Queue to the SETUP state... */ memset(setup_params, 0, sizeof(*setup_params)); @@ -8315,7 +8396,6 @@ static int bnx2x_stop_queue(struct bnx2x *bp, int index) /* We want to wait for completion in this context */ __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags); - /* close tx-only connections */ for (tx_index = FIRST_TX_ONLY_COS_INDEX; tx_index < fp->max_cos; @@ -8369,7 +8449,6 @@ static int bnx2x_stop_queue(struct bnx2x *bp, int index) return bnx2x_queue_state_change(bp, &q_params); } - static void bnx2x_reset_func(struct bnx2x *bp) { int port = BP_PORT(bp); @@ -8422,7 +8501,7 @@ static void bnx2x_reset_func(struct bnx2x *bp) * scan to complete */ for (i = 0; i < 200; i++) { - msleep(10); + usleep_range(10000, 20000); if (!REG_RD(bp, TM_REG_LIN0_SCAN_ON + port*4)) break; } @@ -8623,14 +8702,14 @@ static int bnx2x_func_wait_started(struct bnx2x *bp) /* * (assumption: No Attention from MCP at this stage) - * PMF probably in the middle of TXdisable/enable transaction + * PMF probably in the middle of TX disable/enable transaction * 1. Sync IRS for default SB - * 2. Sync SP queue - this guarantes us that attention handling started - * 3. Wait, that TXdisable/enable transaction completes + * 2. Sync SP queue - this guarantees us that attention handling started + * 3. Wait, that TX disable/enable transaction completes * - * 1+2 guranty that if DCBx attention was scheduled it already changed - * pending bit of transaction from STARTED-->TX_STOPPED, if we alredy - * received complettion for the transaction the state is TX_STOPPED. + * 1+2 guarantee that if DCBx attention was scheduled it already changed + * pending bit of transaction from STARTED-->TX_STOPPED, if we already + * received completion for the transaction the state is TX_STOPPED. * State will return to STARTED after completion of TX_STOPPED-->STARTED * transaction. */ @@ -8660,7 +8739,7 @@ static int bnx2x_func_wait_started(struct bnx2x *bp) struct bnx2x_func_state_params func_params = {NULL}; DP(NETIF_MSG_IFDOWN, - "Hmmm... unexpected function state! Forcing STARTED-->TX_ST0PPED-->STARTED\n"); + "Hmmm... Unexpected function state! Forcing STARTED-->TX_ST0PPED-->STARTED\n"); func_params.f_obj = &bp->func_obj; __set_bit(RAMROD_DRV_CLR_ONLY, @@ -8740,7 +8819,6 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode, bool keep_link) bnx2x_iov_chip_cleanup(bp); - /* * Send the UNLOAD_REQUEST to the MCP. This will return if * this function should perform FUNC, PORT or COMMON HW @@ -8750,7 +8828,7 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode, bool keep_link) /* * (assumption: No Attention from MCP at this stage) - * PMF probably in the middle of TXdisable/enable transaction + * PMF probably in the middle of TX disable/enable transaction */ rc = bnx2x_func_wait_started(bp); if (rc) { @@ -8813,7 +8891,6 @@ unload_error: if (rc) BNX2X_ERR("HW_RESET failed\n"); - /* Report UNLOAD_DONE to MCP */ bnx2x_send_unload_done(bp, keep_link); } @@ -9179,7 +9256,6 @@ static int bnx2x_process_kill(struct bnx2x *bp, bool global) if (!CHIP_IS_E1x(bp) && bnx2x_er_poll_igu_vq(bp)) return -EAGAIN; - /* TBD: Indicate that "process kill" is in progress to MCP */ /* Clear "unprepared" bit */ @@ -9367,7 +9443,7 @@ static void bnx2x_parity_recover(struct bnx2x *bp) * the first leader that performs a * leader_reset() reset the global blocks in * order to clear global attentions. Otherwise - * the the gates will remain closed for that + * the gates will remain closed for that * engine. */ if (load_status || @@ -9480,14 +9556,12 @@ static void bnx2x_sp_rtnl_task(struct work_struct *work) return; } - /* if stop on error is defined no recovery flows should be executed */ + if (unlikely(bp->recovery_state != BNX2X_RECOVERY_DONE)) { #ifdef BNX2X_STOP_ON_ERROR - BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined so reset not done to allow debug dump,\n" - "you will need to reboot when done\n"); - goto sp_rtnl_not_reset; + BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined so reset not done to allow debug dump,\n" + "you will need to reboot when done\n"); + goto sp_rtnl_not_reset; #endif - - if (unlikely(bp->recovery_state != BNX2X_RECOVERY_DONE)) { /* * Clear all pending SP commands as we are going to reset the * function anyway. @@ -9502,6 +9576,12 @@ static void bnx2x_sp_rtnl_task(struct work_struct *work) } if (test_and_clear_bit(BNX2X_SP_RTNL_TX_TIMEOUT, &bp->sp_rtnl_state)) { +#ifdef BNX2X_STOP_ON_ERROR + BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined so reset not done to allow debug dump,\n" + "you will need to reboot when done\n"); + goto sp_rtnl_not_reset; +#endif + /* * Clear all pending SP commands as we are going to reset the * function anyway. @@ -9540,6 +9620,13 @@ sp_rtnl_not_reset: "sending set mcast vf pf channel message from rtnl sp-task\n"); bnx2x_vfpf_set_mcast(bp->dev); } + if (test_and_clear_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN, + &bp->sp_rtnl_state)){ + if (!test_bit(__LINK_STATE_NOCARRIER, &bp->dev->state)) { + bnx2x_tx_disable(bp); + BNX2X_ERR("PF indicated channel is not servicable anymore. This means this VF device is no longer operational\n"); + } + } if (test_and_clear_bit(BNX2X_SP_RTNL_VFPF_STORM_RX_MODE, &bp->sp_rtnl_state)) { @@ -9647,7 +9734,6 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; REG_WR(bp, vals->bmac_addr, wb_data[0]); REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]); - } BNX2X_DEV_INFO("Disable emac Rx\n"); vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4; @@ -9681,7 +9767,6 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, if (mac_stopped) msleep(20); - } #define BNX2X_PREV_UNDI_PROD_ADDR(p) (BAR_TSTRORM_INTMEM + 0x1508 + ((p) << 4)) @@ -9780,6 +9865,21 @@ static bool bnx2x_prev_is_path_marked(struct bnx2x *bp) return rc; } +bool bnx2x_port_after_undi(struct bnx2x *bp) +{ + struct bnx2x_prev_path_list *entry; + bool val; + + down(&bnx2x_prev_sem); + + entry = bnx2x_prev_path_get_entry(bp); + val = !!(entry && (entry->undi & (1 << BP_PORT(bp)))); + + up(&bnx2x_prev_sem); + + return val; +} + static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi) { struct bnx2x_prev_path_list *tmp_list; @@ -9839,7 +9939,6 @@ static int bnx2x_do_flr(struct bnx2x *bp) u16 status; struct pci_dev *dev = bp->pdev; - if (CHIP_IS_E1x(bp)) { BNX2X_DEV_INFO("FLR not supported in E1/E1H\n"); return -EINVAL; @@ -9986,7 +10085,6 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) if (!timer_count) BNX2X_ERR("Failed to empty BRB, hope for the best\n"); - } /* No packets are in the pipeline, path is ready for reset */ @@ -10036,7 +10134,6 @@ static int bnx2x_prev_unload(struct bnx2x *bp) { int time_counter = 10; u32 rc, fw, hw_lock_reg, hw_lock_val; - struct bnx2x_prev_path_list *prev_list; BNX2X_DEV_INFO("Entering Previous Unload Flow\n"); /* clear hw from errors which may have resulted from an interrupted @@ -10049,7 +10146,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) (MISC_REG_DRIVER_CONTROL_1 + BP_FUNC(bp) * 8) : (MISC_REG_DRIVER_CONTROL_7 + (BP_FUNC(bp) - 6) * 8); - hw_lock_val = (REG_RD(bp, hw_lock_reg)); + hw_lock_val = REG_RD(bp, hw_lock_reg); if (hw_lock_val) { if (hw_lock_val & HW_LOCK_RESOURCE_NVRAM) { BNX2X_DEV_INFO("Release Previously held NVRAM lock\n"); @@ -10064,7 +10161,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) if (MCPR_ACCESS_LOCK_LOCK & REG_RD(bp, MCP_REG_MCPR_ACCESS_LOCK)) { BNX2X_DEV_INFO("Release previously held alr\n"); - REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, 0); + bnx2x_release_alr(bp); } do { @@ -10093,7 +10190,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) break; } - /* non-common reply from MCP night require looping */ + /* non-common reply from MCP might require looping */ rc = bnx2x_prev_unload_uncommon(bp); if (rc != BNX2X_PREV_WAIT_NEEDED) break; @@ -10107,8 +10204,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) } /* Mark function if its port was used to boot from SAN */ - prev_list = bnx2x_prev_path_get_entry(bp); - if (prev_list && (prev_list->undi & (1 << BP_PORT(bp)))) + if (bnx2x_port_after_undi(bp)) bp->link_params.feature_config_flags |= FEATURE_CONFIG_BOOT_FROM_SAN; @@ -10192,8 +10288,6 @@ static void bnx2x_get_common_hwinfo(struct bnx2x *bp) bnx2x_init_shmem(bp); - - bp->common.shmem2_base = REG_RD(bp, (BP_PATH(bp) ? MISC_REG_GENERIC_CR_1 : MISC_REG_GENERIC_CR_0)); @@ -10455,6 +10549,9 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) bp->port.supported[idx] &= ~SUPPORTED_10000baseT_Full; + if (!(bp->link_params.speed_cap_mask[idx] & + PORT_HW_CFG_SPEED_CAPABILITY_D0_20G)) + bp->port.supported[idx] &= ~SUPPORTED_20000baseKR2_Full; } BNX2X_DEV_INFO("supported 0x%x 0x%x\n", bp->port.supported[0], @@ -10765,7 +10862,6 @@ void bnx2x_get_iscsi_info(struct bnx2x *bp) */ if (!bp->cnic_eth_dev.max_iscsi_conn) bp->flags |= no_flags; - } static void bnx2x_get_ext_wwn_info(struct bnx2x *bp, int func) @@ -10782,12 +10878,56 @@ static void bnx2x_get_ext_wwn_info(struct bnx2x *bp, int func) bp->cnic_eth_dev.fcoe_wwn_node_name_lo = MF_CFG_RD(bp, func_ext_config[func].fcoe_wwn_node_name_lower); } + +static int bnx2x_shared_fcoe_funcs(struct bnx2x *bp) +{ + u8 count = 0; + + if (IS_MF(bp)) { + u8 fid; + + /* iterate over absolute function ids for this path: */ + for (fid = BP_PATH(bp); fid < E2_FUNC_MAX * 2; fid += 2) { + if (IS_MF_SD(bp)) { + u32 cfg = MF_CFG_RD(bp, + func_mf_config[fid].config); + + if (!(cfg & FUNC_MF_CFG_FUNC_HIDE) && + ((cfg & FUNC_MF_CFG_PROTOCOL_MASK) == + FUNC_MF_CFG_PROTOCOL_FCOE)) + count++; + } else { + u32 cfg = MF_CFG_RD(bp, + func_ext_config[fid]. + func_cfg); + + if ((cfg & MACP_FUNC_CFG_FLAGS_ENABLED) && + (cfg & MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD)) + count++; + } + } + } else { /* SF */ + int port, port_cnt = CHIP_MODE_IS_4_PORT(bp) ? 2 : 1; + + for (port = 0; port < port_cnt; port++) { + u32 lic = SHMEM_RD(bp, + drv_lic_key[port].max_fcoe_conn) ^ + FW_ENCODE_32BIT_PATTERN; + if (lic) + count++; + } + } + + return count; +} + static void bnx2x_get_fcoe_info(struct bnx2x *bp) { int port = BP_PORT(bp); int func = BP_ABS_FUNC(bp); u32 max_fcoe_conn = FW_ENCODE_32BIT_PATTERN ^ SHMEM_RD(bp, drv_lic_key[port].max_fcoe_conn); + u8 num_fcoe_func = bnx2x_shared_fcoe_funcs(bp); if (!CNIC_SUPPORT(bp)) { bp->flags |= NO_FCOE_FLAG; @@ -10801,9 +10941,10 @@ static void bnx2x_get_fcoe_info(struct bnx2x *bp) /* Calculate the number of maximum allowed FCoE tasks */ bp->cnic_eth_dev.max_fcoe_exchanges = MAX_NUM_FCOE_TASKS_PER_ENGINE; - if (IS_MF(bp) || CHIP_MODE_IS_4_PORT(bp)) - bp->cnic_eth_dev.max_fcoe_exchanges /= - MAX_FCOE_FUNCS_PER_ENGINE; + + /* check if FCoE resources must be shared between different functions */ + if (num_fcoe_func) + bp->cnic_eth_dev.max_fcoe_exchanges /= num_fcoe_func; /* Read the WWN: */ if (!IS_MF(bp)) { @@ -11031,7 +11172,7 @@ static int bnx2x_get_hwinfo(struct bnx2x *bp) } else { bp->common.int_block = INT_BLOCK_IGU; - /* do not allow device reset during IGU info preocessing */ + /* do not allow device reset during IGU info processing */ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RESET); val = REG_RD(bp, IGU_REG_BLOCK_CONFIGURATION); @@ -11110,7 +11251,7 @@ static int bnx2x_get_hwinfo(struct bnx2x *bp) E1H_FUNC_MAX * sizeof(struct drv_func_mb); /* * get mf configuration: - * 1. existence of MF configuration + * 1. Existence of MF configuration * 2. MAC address must be legal (check only upper bytes) * for Switch-Independent mode; * OVLAN must be legal for Switch-Dependent mode @@ -11384,7 +11525,6 @@ static int bnx2x_init_bp(struct bnx2x *bp) mutex_init(&bp->fw_mb_mutex); spin_lock_init(&bp->stats_lock); - INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task); INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task); INIT_DELAYED_WORK(&bp->period_task, bnx2x_period_task); @@ -11393,7 +11533,7 @@ static int bnx2x_init_bp(struct bnx2x *bp) if (rc) return rc; } else { - random_ether_addr(bp->dev->dev_addr); + eth_zero_addr(bp->dev->dev_addr); } bnx2x_set_modes_bitmap(bp); @@ -11417,7 +11557,6 @@ static int bnx2x_init_bp(struct bnx2x *bp) bnx2x_prev_unload(bp); } - if (CHIP_REV_IS_FPGA(bp)) dev_err(&bp->pdev->dev, "FPGA detected\n"); @@ -11489,7 +11628,7 @@ static int bnx2x_init_bp(struct bnx2x *bp) /* We need at least one default status block for slow-path events, * second status block for the L2 queue, and a third status block for - * CNIC if supproted. + * CNIC if supported. */ if (CNIC_SUPPORT(bp)) bp->min_msix_vec_cnt = 3; @@ -11497,10 +11636,11 @@ static int bnx2x_init_bp(struct bnx2x *bp) bp->min_msix_vec_cnt = 2; BNX2X_DEV_INFO("bp->min_msix_vec_cnt %d", bp->min_msix_vec_cnt); + bp->dump_preset_idx = 1; + return rc; } - /**************************************************************************** * General service functions ****************************************************************************/ @@ -11585,9 +11725,6 @@ static int bnx2x_close(struct net_device *dev) /* Unload the driver, release IRQs */ bnx2x_nic_unload(bp, UNLOAD_CLOSE, false); - /* Power off */ - bnx2x_set_power_state(bp, PCI_D3hot); - return 0; } @@ -11852,6 +11989,10 @@ static int bnx2x_validate_addr(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); + /* query the bulletin board for mac address configured by the PF */ + if (IS_VF(bp)) + bnx2x_sample_bulletin(bp); + if (!bnx2x_is_valid_ether_addr(bp, dev->dev_addr)) { BNX2X_ERR("Non-valid Ethernet address\n"); return -EADDRNOTAVAIL; @@ -11878,12 +12019,16 @@ static const struct net_device_ops bnx2x_netdev_ops = { .ndo_setup_tc = bnx2x_setup_tc, #ifdef CONFIG_BNX2X_SRIOV .ndo_set_vf_mac = bnx2x_set_vf_mac, - .ndo_set_vf_vlan = bnx2x_set_vf_vlan, + .ndo_set_vf_vlan = bnx2x_set_vf_vlan, .ndo_get_vf_config = bnx2x_get_vf_config, #endif #ifdef NETDEV_FCOE_WWNN .ndo_fcoe_get_wwn = bnx2x_fcoe_get_wwn, #endif + +#ifdef CONFIG_NET_LL_RX_POLL + .ndo_ll_poll = bnx2x_low_latency_recv, +#endif }; static int bnx2x_set_coherency_mask(struct bnx2x *bp) @@ -11959,7 +12104,7 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, } if (IS_PF(bp)) { - bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + bp->pm_cap = pdev->pm_cap; if (bp->pm_cap == 0) { dev_err(&bp->pdev->dev, "Cannot find power management capability, aborting\n"); @@ -12008,8 +12153,6 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, } BNX2X_DEV_INFO("me reg PF num: %d\n", bp->pf_num); - bnx2x_set_power_state(bp, PCI_D0); - /* clean indirect addresses */ pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET); @@ -12094,15 +12237,26 @@ err_out: return rc; } -static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width, int *speed) +static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width, + enum bnx2x_pci_bus_speed *speed) { - u32 val = 0; + u32 link_speed, val = 0; pci_read_config_dword(bp->pdev, PCICFG_LINK_CONTROL, &val); *width = (val & PCICFG_LINK_WIDTH) >> PCICFG_LINK_WIDTH_SHIFT; - /* return value of 1=2.5GHz 2=5GHz */ - *speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT; + link_speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT; + + switch (link_speed) { + case 3: + *speed = BNX2X_PCI_LINK_SPEED_8000; + break; + case 2: + *speed = BNX2X_PCI_LINK_SPEED_5000; + break; + default: + *speed = BNX2X_PCI_LINK_SPEED_2500; + } } static int bnx2x_check_firmware(struct bnx2x *bp) @@ -12327,7 +12481,6 @@ static void bnx2x_release_firmware(struct bnx2x *bp) bp->firmware = NULL; } - static struct bnx2x_func_sp_drv_ops bnx2x_func_sp_drv = { .init_hw_cmn_chip = bnx2x_init_hw_common_chip, .init_hw_cmn = bnx2x_init_hw_common, @@ -12465,7 +12618,8 @@ static int bnx2x_init_one(struct pci_dev *pdev, { struct net_device *dev = NULL; struct bnx2x *bp; - int pcie_width, pcie_speed; + int pcie_width; + enum bnx2x_pci_bus_speed pcie_speed; int rc, max_non_def_sbs; int rx_count, tx_count, rss_count, doorbell_size; int max_cos_est; @@ -12605,7 +12759,6 @@ static int bnx2x_init_one(struct pci_dev *pdev, } BNX2X_DEV_INFO("device name after netdev register %s\n", dev->name); - if (!NO_FCOE(bp)) { /* Add storage MAC address */ rtnl_lock(); @@ -12617,15 +12770,15 @@ static int bnx2x_init_one(struct pci_dev *pdev, BNX2X_DEV_INFO("got pcie width %d and speed %d\n", pcie_width, pcie_speed); - BNX2X_DEV_INFO( - "%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n", - board_info[ent->driver_data].name, - (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), - pcie_width, - ((!CHIP_IS_E2(bp) && pcie_speed == 2) || - (CHIP_IS_E2(bp) && pcie_speed == 1)) ? - "5GHz (Gen2)" : "2.5GHz", - dev->base_addr, bp->pdev->irq, dev->dev_addr); + BNX2X_DEV_INFO("%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n", + board_info[ent->driver_data].name, + (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), + pcie_width, + pcie_speed == BNX2X_PCI_LINK_SPEED_2500 ? "2.5GHz" : + pcie_speed == BNX2X_PCI_LINK_SPEED_5000 ? "5.0GHz" : + pcie_speed == BNX2X_PCI_LINK_SPEED_8000 ? "8.0GHz" : + "Unknown", + dev->base_addr, bp->pdev->irq, dev->dev_addr); return 0; @@ -12647,17 +12800,11 @@ init_one_exit: return rc; } -static void bnx2x_remove_one(struct pci_dev *pdev) +static void __bnx2x_remove(struct pci_dev *pdev, + struct net_device *dev, + struct bnx2x *bp, + bool remove_netdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct bnx2x *bp; - - if (!dev) { - dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n"); - return; - } - bp = netdev_priv(dev); - /* Delete storage MAC address */ if (!NO_FCOE(bp)) { rtnl_lock(); @@ -12670,7 +12817,17 @@ static void bnx2x_remove_one(struct pci_dev *pdev) bnx2x_dcbnl_update_applist(bp, true); #endif - unregister_netdev(dev); + /* Close the interface - either directly or implicitly */ + if (remove_netdev) { + unregister_netdev(dev); + } else { + rtnl_lock(); + if (netif_running(dev)) + bnx2x_close(dev); + rtnl_unlock(); + } + + bnx2x_iov_remove_one(bp); /* Power on: we can't let PCI layer write to us while we are in D3 */ if (IS_PF(bp)) @@ -12686,12 +12843,16 @@ static void bnx2x_remove_one(struct pci_dev *pdev) /* Make sure RESET task is not scheduled before continuing */ cancel_delayed_work_sync(&bp->sp_rtnl_task); - bnx2x_iov_remove_one(bp); - /* send message via vfpf channel to release the resources of this vf */ if (IS_VF(bp)) bnx2x_vfpf_release(bp); + /* Assumes no further PCIe PM changes will occur */ + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, bp->wol); + pci_set_power_state(pdev, PCI_D3hot); + } + if (bp->regview) iounmap(bp->regview); @@ -12706,7 +12867,8 @@ static void bnx2x_remove_one(struct pci_dev *pdev) } bnx2x_free_mem_bp(bp); - free_netdev(dev); + if (remove_netdev) + free_netdev(dev); if (atomic_read(&pdev->enable_cnt) == 1) pci_release_regions(pdev); @@ -12715,6 +12877,20 @@ static void bnx2x_remove_one(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } +static void bnx2x_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct bnx2x *bp; + + if (!dev) { + dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n"); + return; + } + bp = netdev_priv(dev); + + __bnx2x_remove(pdev, dev, bp, true); +} + static int bnx2x_eeh_nic_unload(struct bnx2x *bp) { bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; @@ -12747,19 +12923,6 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) return 0; } -static void bnx2x_eeh_recover(struct bnx2x *bp) -{ - u32 val; - - mutex_init(&bp->port.phy_mutex); - - - val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]); - if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - BNX2X_ERR("BAD MCP validity signature\n"); -} - /** * bnx2x_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device @@ -12828,6 +12991,10 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev) if (netif_running(dev)) { BNX2X_ERR("IO slot reset --> driver unload\n"); + + /* MCP should have been reset; Need to wait for validity */ + bnx2x_init_shmem(bp); + if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) { u32 v; @@ -12849,7 +13016,7 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev) bnx2x_prev_unload(bp); - /* We should have resetted the engine, so It's fair to + /* We should have reseted the engine, so It's fair to * assume the FW will no longer write to the bnx2x driver. */ bnx2x_squeeze_objects(bp); @@ -12886,8 +13053,6 @@ static void bnx2x_io_resume(struct pci_dev *pdev) rtnl_lock(); - bnx2x_eeh_recover(bp); - bp->fw_seq = SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK; @@ -12905,6 +13070,29 @@ static const struct pci_error_handlers bnx2x_err_handler = { .resume = bnx2x_io_resume, }; +static void bnx2x_shutdown(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct bnx2x *bp; + + if (!dev) + return; + + bp = netdev_priv(dev); + if (!bp) + return; + + rtnl_lock(); + netif_device_detach(dev); + rtnl_unlock(); + + /* Don't remove the netdevice, as there are scenarios which will cause + * the kernel to hang, e.g., when trying to remove bnx2i while the + * rootfs is mounted from SAN. + */ + __bnx2x_remove(pdev, dev, bp, false); +} + static struct pci_driver bnx2x_pci_driver = { .name = DRV_MODULE_NAME, .id_table = bnx2x_pci_tbl, @@ -12916,6 +13104,7 @@ static struct pci_driver bnx2x_pci_driver = { #ifdef CONFIG_BNX2X_SRIOV .sriov_configure = bnx2x_sriov_configure, #endif + .shutdown = bnx2x_shutdown, }; static int __init bnx2x_init(void) @@ -12941,11 +13130,12 @@ static int __init bnx2x_init(void) static void __exit bnx2x_cleanup(void) { struct list_head *pos, *q; + pci_unregister_driver(&bnx2x_pci_driver); destroy_workqueue(bnx2x_wq); - /* Free globablly allocated resources */ + /* Free globally allocated resources */ list_for_each_safe(pos, q, &bnx2x_prev_list) { struct bnx2x_prev_path_list *tmp = list_entry(pos, struct bnx2x_prev_path_list, list); @@ -12968,7 +13158,7 @@ module_exit(bnx2x_cleanup); * @bp: driver handle * @set: set or clear the CAM entry * - * This function will wait until the ramdord completion returns. + * This function will wait until the ramrod completion returns. * Return 0 if success, -ENODEV if ramrod doesn't return. */ static int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp) @@ -12996,7 +13186,6 @@ static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count) BUG_ON(bp->cnic_spq_pending < count); bp->cnic_spq_pending -= count; - for (; bp->cnic_kwq_pending; bp->cnic_kwq_pending--) { u16 type = (le16_to_cpu(bp->cnic_kwq_cons->hdr.type) & SPE_HDR_CONN_TYPE) >> @@ -13169,7 +13358,6 @@ static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid, u8 err) bnx2x_cnic_sp_post(bp, 0); } - /* Called with netif_addr_lock_bh() taken. * Sets an rx_mode config for an iSCSI ETH client. * Doesn't block. @@ -13210,7 +13398,6 @@ static void bnx2x_set_iscsi_eth_rx_mode(struct bnx2x *bp, bool start) } } - static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl) { struct bnx2x *bp = netdev_priv(dev); @@ -13398,7 +13585,6 @@ void bnx2x_setup_cnic_info(struct bnx2x *bp) { struct cnic_eth_dev *cp = &bp->cnic_eth_dev; - cp->ctx_tbl_offset = FUNC_ILT_BASE(BP_FUNC(bp)) + bnx2x_cid_ilt_lines(bp); cp->starting_cid = bnx2x_cid_ilt_lines(bp) * ILT_PAGE_CIDS; @@ -13434,7 +13620,6 @@ static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops, BNX2X_ERR("CNIC-related load failed\n"); return rc; } - } bp->cnic_enabled = true; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index d22bc40091e..8e627b886d7 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -35,6 +35,8 @@ #define ATC_REG_ATC_INT_STS_CLR 0x1101c0 /* [RW 5] Parity mask register #0 read/write */ #define ATC_REG_ATC_PRTY_MASK 0x1101d8 +/* [R 5] Parity register #0 read */ +#define ATC_REG_ATC_PRTY_STS 0x1101cc /* [RC 5] Parity register #0 read clear */ #define ATC_REG_ATC_PRTY_STS_CLR 0x1101d0 /* [RW 19] Interrupt mask register #0 read/write */ @@ -2750,6 +2752,8 @@ #define PBF_REG_PBF_INT_STS 0x1401c8 /* [RW 20] Parity mask register #0 read/write */ #define PBF_REG_PBF_PRTY_MASK 0x1401e4 +/* [R 28] Parity register #0 read */ +#define PBF_REG_PBF_PRTY_STS 0x1401d8 /* [RC 20] Parity register #0 read clear */ #define PBF_REG_PBF_PRTY_STS_CLR 0x1401dc /* [RW 16] The Ethernet type value for L2 tag 0 */ @@ -4517,6 +4521,8 @@ #define TM_REG_TM_INT_STS 0x1640f0 /* [RW 7] Parity mask register #0 read/write */ #define TM_REG_TM_PRTY_MASK 0x16410c +/* [R 7] Parity register #0 read */ +#define TM_REG_TM_PRTY_STS 0x164100 /* [RC 7] Parity register #0 read clear */ #define TM_REG_TM_PRTY_STS_CLR 0x164104 /* [RW 8] The event id for aggregated interrupt 0 */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 32a9609cc98..8f03c984550 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -35,9 +35,9 @@ /** * bnx2x_exe_queue_init - init the Exe Queue object * - * @o: poiter to the object + * @o: pointer to the object * @exe_len: length - * @owner: poiter to the owner + * @owner: pointer to the owner * @validate: validate function pointer * @optimize: optimize function pointer * @exec: execute function pointer @@ -142,7 +142,6 @@ free_and_exit: spin_unlock_bh(&o->lock); return rc; - } static inline void __bnx2x_exe_queue_reset_pending( @@ -163,13 +162,11 @@ static inline void __bnx2x_exe_queue_reset_pending( static inline void bnx2x_exe_queue_reset_pending(struct bnx2x *bp, struct bnx2x_exe_queue_obj *o) { - spin_lock_bh(&o->lock); __bnx2x_exe_queue_reset_pending(bp, o); spin_unlock_bh(&o->lock); - } /** @@ -179,7 +176,7 @@ static inline void bnx2x_exe_queue_reset_pending(struct bnx2x *bp, * @o: queue * @ramrod_flags: flags * - * (Atomicy is ensured using the exe_queue->lock). + * (Atomicity is ensured using the exe_queue->lock). */ static inline int bnx2x_exe_queue_step(struct bnx2x *bp, struct bnx2x_exe_queue_obj *o, @@ -192,8 +189,7 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp, spin_lock_bh(&o->lock); - /* - * Next step should not be performed until the current is finished, + /* Next step should not be performed until the current is finished, * unless a DRV_CLEAR_ONLY bit is set. In this case we just want to * properly clear object internals without sending any command to the FW * which also implies there won't be any completion to clear the @@ -209,8 +205,7 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp, } } - /* - * Run through the pending commands list and create a next + /* Run through the pending commands list and create a next * execution chunk. */ while (!list_empty(&o->exe_queue)) { @@ -220,8 +215,7 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp, if (cur_len + elem->cmd_len <= o->exe_chunk_len) { cur_len += elem->cmd_len; - /* - * Prevent from both lists being empty when moving an + /* Prevent from both lists being empty when moving an * element. This will allow the call of * bnx2x_exe_queue_empty() without locking. */ @@ -241,14 +235,12 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp, rc = o->execute(bp, o->owner, &o->pending_comp, ramrod_flags); if (rc < 0) - /* - * In case of an error return the commands back to the queue - * and reset the pending_comp. + /* In case of an error return the commands back to the queue + * and reset the pending_comp. */ list_splice_init(&o->pending_comp, &o->exe_queue); else if (!rc) - /* - * If zero is returned, means there are no outstanding pending + /* If zero is returned, means there are no outstanding pending * completions and we may dismiss the pending list. */ __bnx2x_exe_queue_reset_pending(bp, o); @@ -308,7 +300,6 @@ static inline int bnx2x_state_wait(struct bnx2x *bp, int state, /* can take a while if any port is running */ int cnt = 5000; - if (CHIP_REV_IS_EMUL(bp)) cnt *= 20; @@ -456,7 +447,6 @@ static int bnx2x_get_n_elements(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o, DP(BNX2X_MSG_SP, "copied element number %d to address %p element was:\n", counter, next); next += stride + size; - } } return counter * ETH_ALEN; @@ -518,7 +508,6 @@ static int bnx2x_check_vlan_mac_add(struct bnx2x *bp, return 0; } - /* check_del() callbacks */ static struct bnx2x_vlan_mac_registry_elem * bnx2x_check_mac_del(struct bnx2x *bp, @@ -609,7 +598,6 @@ static bool bnx2x_check_move_always_err( return false; } - static inline u8 bnx2x_vlan_mac_get_rx_tx_flag(struct bnx2x_vlan_mac_obj *o) { struct bnx2x_raw_obj *raw = &o->raw; @@ -626,7 +614,6 @@ static inline u8 bnx2x_vlan_mac_get_rx_tx_flag(struct bnx2x_vlan_mac_obj *o) return rx_tx_flag; } - void bnx2x_set_mac_in_nig(struct bnx2x *bp, bool add, unsigned char *dev_addr, int index) { @@ -693,7 +680,7 @@ static inline void bnx2x_vlan_mac_set_cmd_hdr_e2(struct bnx2x *bp, * * @cid: connection id * @type: BNX2X_FILTER_XXX_PENDING - * @hdr: poiter to header to setup + * @hdr: pointer to header to setup * @rule_cnt: * * currently we always configure one rule and echo field to contain a CID and an @@ -707,7 +694,6 @@ static inline void bnx2x_vlan_mac_set_rdata_hdr_e2(u32 cid, int type, hdr->rule_cnt = (u8)rule_cnt; } - /* hw_config() callbacks */ static void bnx2x_set_one_mac_e2(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o, @@ -723,8 +709,7 @@ static void bnx2x_set_one_mac_e2(struct bnx2x *bp, unsigned long *vlan_mac_flags = &elem->cmd_data.vlan_mac.vlan_mac_flags; u8 *mac = elem->cmd_data.vlan_mac.u.mac.mac; - /* - * Set LLH CAM entry: currently only iSCSI and ETH macs are + /* Set LLH CAM entry: currently only iSCSI and ETH macs are * relevant. In addition, current implementation is tuned for a * single ETH MAC. * @@ -879,8 +864,7 @@ static void bnx2x_set_one_mac_e1x(struct bnx2x *bp, struct bnx2x_raw_obj *raw = &o->raw; struct mac_configuration_cmd *config = (struct mac_configuration_cmd *)(raw->rdata); - /* - * 57710 and 57711 do not support MOVE command, + /* 57710 and 57711 do not support MOVE command, * so it's either ADD or DEL */ bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ? @@ -960,7 +944,6 @@ static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp, u16 vlan = elem->cmd_data.vlan_mac.u.vlan_mac.vlan; u8 *mac = elem->cmd_data.vlan_mac.u.vlan_mac.mac; - /* Reset the ramrod data buffer for the first rule */ if (rule_idx == 0) memset(data, 0, sizeof(*data)); @@ -969,7 +952,7 @@ static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp, bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_PAIR, &rule_entry->pair.header); - /* Set VLAN and MAC themselvs */ + /* Set VLAN and MAC themselves */ rule_entry->pair.vlan = cpu_to_le16(vlan); bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb, &rule_entry->pair.mac_mid, @@ -1021,8 +1004,7 @@ static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp, struct bnx2x_raw_obj *raw = &o->raw; struct mac_configuration_cmd *config = (struct mac_configuration_cmd *)(raw->rdata); - /* - * 57710 and 57711 do not support MOVE command, + /* 57710 and 57711 do not support MOVE command, * so it's either ADD or DEL */ bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ? @@ -1046,7 +1028,7 @@ static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp, * * @bp: device handle * @p: command parameters - * @ppos: pointer to the cooky + * @ppos: pointer to the cookie * * reconfigure next MAC/VLAN/VLAN-MAC element from the * previously configured elements list. @@ -1054,7 +1036,7 @@ static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp, * from command parameters only RAMROD_COMP_WAIT bit in ramrod_flags is taken * into an account * - * pointer to the cooky - that should be given back in the next call to make + * pointer to the cookie - that should be given back in the next call to make * function handle the next element. If *ppos is set to NULL it will restart the * iterator. If returned *ppos == NULL this means that the last element has been * handled. @@ -1102,8 +1084,7 @@ static int bnx2x_vlan_mac_restore(struct bnx2x *bp, return bnx2x_config_vlan_mac(bp, p); } -/* - * bnx2x_exeq_get_mac/bnx2x_exeq_get_vlan/bnx2x_exeq_get_vlan_mac return a +/* bnx2x_exeq_get_mac/bnx2x_exeq_get_vlan/bnx2x_exeq_get_vlan_mac return a * pointer to an element with a specific criteria and NULL if such an element * hasn't been found. */ @@ -1187,8 +1168,7 @@ static inline int bnx2x_validate_vlan_mac_add(struct bnx2x *bp, return rc; } - /* - * Check if there is a pending ADD command for this + /* Check if there is a pending ADD command for this * MAC/VLAN/VLAN-MAC. Return an error if there is. */ if (exeq->get(exeq, elem)) { @@ -1196,8 +1176,7 @@ static inline int bnx2x_validate_vlan_mac_add(struct bnx2x *bp, return -EEXIST; } - /* - * TODO: Check the pending MOVE from other objects where this + /* TODO: Check the pending MOVE from other objects where this * object is a destination object. */ @@ -1240,8 +1219,7 @@ static inline int bnx2x_validate_vlan_mac_del(struct bnx2x *bp, return -EEXIST; } - /* - * Check if there are pending DEL or MOVE commands for this + /* Check if there are pending DEL or MOVE commands for this * MAC/VLAN/VLAN-MAC. Return an error if so. */ memcpy(&query_elem, elem, sizeof(query_elem)); @@ -1292,8 +1270,7 @@ static inline int bnx2x_validate_vlan_mac_move(struct bnx2x *bp, struct bnx2x_exe_queue_obj *src_exeq = &src_o->exe_queue; struct bnx2x_exe_queue_obj *dest_exeq = &dest_o->exe_queue; - /* - * Check if we can perform this operation based on the current registry + /* Check if we can perform this operation based on the current registry * state. */ if (!src_o->check_move(bp, src_o, dest_o, @@ -1302,8 +1279,7 @@ static inline int bnx2x_validate_vlan_mac_move(struct bnx2x *bp, return -EINVAL; } - /* - * Check if there is an already pending DEL or MOVE command for the + /* Check if there is an already pending DEL or MOVE command for the * source object or ADD command for a destination object. Return an * error if so. */ @@ -1392,7 +1368,7 @@ static int bnx2x_remove_vlan_mac(struct bnx2x *bp, } /** - * bnx2x_wait_vlan_mac - passivly wait for 5 seconds until all work completes. + * bnx2x_wait_vlan_mac - passively wait for 5 seconds until all work completes. * * @bp: device handle * @o: bnx2x_vlan_mac_obj @@ -1550,9 +1526,8 @@ static inline int bnx2x_vlan_mac_get_registry_elem( /* Get a new CAM offset */ if (!o->get_cam_offset(o, ®_elem->cam_offset)) { - /* - * This shell never happen, because we have checked the - * CAM availiability in the 'validate'. + /* This shall never happen, because we have checked the + * CAM availability in the 'validate'. */ WARN_ON(1); kfree(reg_elem); @@ -1599,8 +1574,7 @@ static int bnx2x_execute_vlan_mac(struct bnx2x *bp, struct bnx2x_vlan_mac_registry_elem *reg_elem; enum bnx2x_vlan_mac_cmd cmd; - /* - * If DRIVER_ONLY execution is requested, cleanup a registry + /* If DRIVER_ONLY execution is requested, cleanup a registry * and exit. Otherwise send a ramrod to FW. */ if (!drv_only) { @@ -1609,11 +1583,10 @@ static int bnx2x_execute_vlan_mac(struct bnx2x *bp, /* Set pending */ r->set_pending(r); - /* Fill tha ramrod data */ + /* Fill the ramrod data */ list_for_each_entry(elem, exe_chunk, link) { cmd = elem->cmd_data.vlan_mac.cmd; - /* - * We will add to the target object in MOVE command, so + /* We will add to the target object in MOVE command, so * change the object for a CAM search. */ if (cmd == BNX2X_VLAN_MAC_MOVE) @@ -1646,12 +1619,11 @@ static int bnx2x_execute_vlan_mac(struct bnx2x *bp, idx++; } - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ rc = bnx2x_sp_post(bp, o->ramrod_cmd, r->cid, @@ -1766,8 +1738,7 @@ int bnx2x_config_vlan_mac( return rc; } - /* - * If nothing will be executed further in this iteration we want to + /* If nothing will be executed further in this iteration we want to * return PENDING if there are pending commands */ if (!bnx2x_exe_queue_empty(&o->exe_queue)) @@ -1786,13 +1757,11 @@ int bnx2x_config_vlan_mac( return rc; } - /* - * RAMROD_COMP_WAIT is a superset of RAMROD_EXEC. If it was set + /* RAMROD_COMP_WAIT is a superset of RAMROD_EXEC. If it was set * then user want to wait until the last command is done. */ if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags)) { - /* - * Wait maximum for the current exe_queue length iterations plus + /* Wait maximum for the current exe_queue length iterations plus * one (for the current pending command). */ int max_iterations = bnx2x_exe_queue_length(&o->exe_queue) + 1; @@ -1818,8 +1787,6 @@ int bnx2x_config_vlan_mac( return rc; } - - /** * bnx2x_vlan_mac_del_all - delete elements with given vlan_mac_flags spec * @@ -1829,7 +1796,7 @@ int bnx2x_config_vlan_mac( * @ramrod_flags: execution flags to be used for this deletion * * if the last operation has completed successfully and there are no - * moreelements left, positive value if the last operation has completed + * more elements left, positive value if the last operation has completed * successfully and there are more previously configured elements, negative * value is current operation has failed. */ @@ -1870,8 +1837,7 @@ static int bnx2x_vlan_mac_del_all(struct bnx2x *bp, p.ramrod_flags = *ramrod_flags; p.user_req.cmd = BNX2X_VLAN_MAC_DEL; - /* - * Add all but the last VLAN-MAC to the execution queue without actually + /* Add all but the last VLAN-MAC to the execution queue without actually * execution anything. */ __clear_bit(RAMROD_COMP_WAIT, &p.ramrod_flags); @@ -1934,7 +1900,6 @@ static inline void bnx2x_init_vlan_mac_common(struct bnx2x_vlan_mac_obj *o, state, pstate, type); } - void bnx2x_init_mac_obj(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *mac_obj, u8 cl_id, u32 cid, u8 func_id, void *rdata, @@ -2048,8 +2013,7 @@ void bnx2x_init_vlan_mac_obj(struct bnx2x *bp, /* CAM pool handling */ vlan_mac_obj->get_credit = bnx2x_get_credit_vlan_mac; vlan_mac_obj->put_credit = bnx2x_put_credit_vlan_mac; - /* - * CAM offset is relevant for 57710 and 57711 chips only which have a + /* CAM offset is relevant for 57710 and 57711 chips only which have a * single CAM for both MACs and VLAN-MAC pairs. So the offset * will be taken from MACs' pool object only. */ @@ -2092,7 +2056,6 @@ void bnx2x_init_vlan_mac_obj(struct bnx2x *bp, bnx2x_execute_vlan_mac, bnx2x_exeq_get_vlan_mac); } - } /* RX_MODE verbs: DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */ @@ -2117,12 +2080,12 @@ static int bnx2x_set_rx_mode_e1x(struct bnx2x *bp, struct tstorm_eth_mac_filter_config *mac_filters = (struct tstorm_eth_mac_filter_config *)p->rdata; - /* initial seeting is drop-all */ + /* initial setting is drop-all */ u8 drop_all_ucast = 1, drop_all_mcast = 1; u8 accp_all_ucast = 0, accp_all_bcast = 0, accp_all_mcast = 0; u8 unmatched_unicast = 0; - /* In e1x there we only take into account rx acceot flag since tx switching + /* In e1x there we only take into account rx accept flag since tx switching * isn't enabled. */ if (test_bit(BNX2X_ACCEPT_UNICAST, &p->rx_accept_flags)) /* accept matched ucast */ @@ -2245,7 +2208,6 @@ static inline void bnx2x_rx_mode_set_cmd_state_e2(struct bnx2x *bp, } cmd->state = cpu_to_le16(state); - } static int bnx2x_set_rx_mode_e2(struct bnx2x *bp, @@ -2286,9 +2248,7 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp, false); } - - /* - * If FCoE Queue configuration has been requested configure the Rx and + /* If FCoE Queue configuration has been requested configure the Rx and * internal switching modes for this queue in separate rules. * * FCoE queue shell never be set to ACCEPT_ALL packets of any sort: @@ -2324,8 +2284,7 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp, } } - /* - * Set the ramrod header (most importantly - number of rules to + /* Set the ramrod header (most importantly - number of rules to * configure). */ bnx2x_rx_mode_set_rdata_hdr_e2(p->cid, &data->header, rule_idx); @@ -2334,12 +2293,11 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp, data->header.rule_cnt, p->rx_accept_flags, p->tx_accept_flags); - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ /* Send a ramrod */ @@ -2476,7 +2434,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp, cur_mac = (struct bnx2x_mcast_mac_elem *) ((u8 *)new_cmd + sizeof(*new_cmd)); - /* Push the MACs of the current command into the pendig command + /* Push the MACs of the current command into the pending command * MACs list: FIFO */ list_for_each_entry(pos, &p->mcast_list, link) { @@ -2909,7 +2867,6 @@ static int bnx2x_mcast_validate_e2(struct bnx2x *bp, default: BNX2X_ERR("Unknown command: %d\n", cmd); return -EINVAL; - } /* Increase the total number of MACs pending to be configured */ @@ -3034,20 +2991,18 @@ static int bnx2x_mcast_setup_e2(struct bnx2x *bp, if (!o->total_pending_num) bnx2x_mcast_refresh_registry_e2(bp, o); - /* - * If CLEAR_ONLY was requested - don't send a ramrod and clear + /* If CLEAR_ONLY was requested - don't send a ramrod and clear * RAMROD_PENDING status immediately. */ if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) { raw->clear_pending(raw); return 0; } else { - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ /* Send a ramrod */ @@ -3121,7 +3076,7 @@ static inline void bnx2x_mcast_hdl_restore_e1h(struct bnx2x *bp, } } -/* On 57711 we write the multicast MACs' aproximate match +/* On 57711 we write the multicast MACs' approximate match * table by directly into the TSTORM's internal RAM. So we don't * really need to handle any tricks to make it work. */ @@ -3223,7 +3178,6 @@ static int bnx2x_mcast_validate_e1(struct bnx2x *bp, default: BNX2X_ERR("Unknown command: %d\n", cmd); return -EINVAL; - } /* We want to ensure that commands are executed one by one for 57710. @@ -3245,7 +3199,7 @@ static void bnx2x_mcast_revert_e1(struct bnx2x *bp, /* If current command hasn't been handled yet and we are * here means that it's meant to be dropped and we have to - * update the number of outstandling MACs accordingly. + * update the number of outstanding MACs accordingly. */ if (p->mcast_list_len) o->total_pending_num -= o->max_cmd_len; @@ -3342,7 +3296,6 @@ static inline int bnx2x_mcast_handle_restore_cmd_e1( return -1; } - static inline int bnx2x_mcast_handle_pending_cmds_e1( struct bnx2x *bp, struct bnx2x_mcast_ramrod_params *p) { @@ -3352,7 +3305,6 @@ static inline int bnx2x_mcast_handle_pending_cmds_e1( union bnx2x_mcast_config_data cfg_data = {NULL}; int cnt = 0; - /* If nothing to be done - return */ if (list_empty(&o->pending_cmds_head)) return 0; @@ -3523,20 +3475,18 @@ static int bnx2x_mcast_setup_e1(struct bnx2x *bp, if (rc) return rc; - /* - * If CLEAR_ONLY was requested - don't send a ramrod and clear + /* If CLEAR_ONLY was requested - don't send a ramrod and clear * RAMROD_PENDING status immediately. */ if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) { raw->clear_pending(raw); return 0; } else { - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ /* Send a ramrod */ @@ -3550,7 +3500,6 @@ static int bnx2x_mcast_setup_e1(struct bnx2x *bp, /* Ramrod completion is pending */ return 1; } - } static int bnx2x_mcast_get_registry_size_exact(struct bnx2x_mcast_obj *o) @@ -3848,7 +3797,6 @@ static bool bnx2x_credit_pool_always_true(struct bnx2x_credit_pool_obj *o, return true; } - static bool bnx2x_credit_pool_get_entry( struct bnx2x_credit_pool_obj *o, int *offset) @@ -3999,8 +3947,7 @@ void bnx2x_init_mac_credit_pool(struct bnx2x *bp, } else { - /* - * CAM credit is equaly divided between all active functions + /* CAM credit is equaly divided between all active functions * on the PATH. */ if ((func_num > 0)) { @@ -4009,8 +3956,7 @@ void bnx2x_init_mac_credit_pool(struct bnx2x *bp, else cam_sz = BNX2X_CAM_SIZE_EMUL; - /* - * No need for CAM entries handling for 57712 and + /* No need for CAM entries handling for 57712 and * newer. */ bnx2x_init_credit_pool(p, -1, cam_sz); @@ -4018,7 +3964,6 @@ void bnx2x_init_mac_credit_pool(struct bnx2x *bp, /* this should never happen! Block MAC operations. */ bnx2x_init_credit_pool(p, 0, 0); } - } } @@ -4028,14 +3973,12 @@ void bnx2x_init_vlan_credit_pool(struct bnx2x *bp, u8 func_num) { if (CHIP_IS_E1x(bp)) { - /* - * There is no VLAN credit in HW on 57710 and 57711 only + /* There is no VLAN credit in HW on 57710 and 57711 only * MAC / MAC-VLAN can be set */ bnx2x_init_credit_pool(p, 0, -1); } else { - /* - * CAM credit is equaly divided between all active functions + /* CAM credit is equally divided between all active functions * on the PATH. */ if (func_num > 0) { @@ -4051,7 +3994,7 @@ void bnx2x_init_vlan_credit_pool(struct bnx2x *bp, /** * bnx2x_debug_print_ind_table - prints the indirection table configuration. * - * @bp: driver hanlde + * @bp: driver handle * @p: pointer to rss configuration * * Prints it when NETIF_MSG_IFUP debug level is configured. @@ -4164,12 +4107,11 @@ static int bnx2x_setup_rss(struct bnx2x *bp, data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY; } - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ /* Send a ramrod */ @@ -4215,7 +4157,6 @@ int bnx2x_config_rss(struct bnx2x *bp, return rc; } - void bnx2x_init_rss_config_obj(struct bnx2x *bp, struct bnx2x_rss_config_obj *rss_obj, u8 cl_id, u32 cid, u8 func_id, u8 engine_id, @@ -4288,7 +4229,6 @@ int bnx2x_queue_state_change(struct bnx2x *bp, return !!test_bit(pending_bit, pending); } - static int bnx2x_queue_set_pending(struct bnx2x_queue_sp_obj *obj, struct bnx2x_queue_state_params *params) { @@ -4337,7 +4277,7 @@ static int bnx2x_queue_comp_cmd(struct bnx2x *bp, } if (o->next_tx_only >= o->max_cos) - /* >= becuase tx only must always be smaller than cos since the + /* >= because tx only must always be smaller than cos since the * primary connection supports COS 0 */ BNX2X_ERR("illegal value for next tx_only: %d. max cos was %d", @@ -4403,7 +4343,6 @@ static void bnx2x_q_fill_init_general_data(struct bnx2x *bp, gen_data->mtu = cpu_to_le16(params->mtu); gen_data->func_id = o->func_id; - gen_data->cos = params->cos; gen_data->traffic_type = @@ -4530,7 +4469,6 @@ static void bnx2x_q_fill_init_rx_data(struct bnx2x_queue_sp_obj *o, cpu_to_le16(params->silent_removal_value); rx_data->silent_vlan_mask = cpu_to_le16(params->silent_removal_mask); - } /* initialize the general, tx and rx parts of a queue object */ @@ -4652,12 +4590,11 @@ static inline int bnx2x_q_send_setup_e1x(struct bnx2x *bp, /* Fill the ramrod data */ bnx2x_q_fill_setup_data_cmn(bp, params, rdata); - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX], @@ -4681,12 +4618,11 @@ static inline int bnx2x_q_send_setup_e2(struct bnx2x *bp, bnx2x_q_fill_setup_data_cmn(bp, params, rdata); bnx2x_q_fill_setup_data_e2(bp, params, rdata); - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX], @@ -4706,7 +4642,6 @@ static inline int bnx2x_q_send_setup_tx_only(struct bnx2x *bp, ¶ms->params.tx_only; u8 cid_index = tx_only_params->cid_index; - if (cid_index >= o->max_cos) { BNX2X_ERR("queue[%d]: cid_index (%d) is out of range\n", o->cl_id, cid_index); @@ -4727,12 +4662,11 @@ static inline int bnx2x_q_send_setup_tx_only(struct bnx2x *bp, o->cids[cid_index], rdata->general.client_id, rdata->general.sp_client_id, rdata->general.cos); - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ return bnx2x_sp_post(bp, ramrod, o->cids[cid_index], @@ -4761,7 +4695,7 @@ static void bnx2x_q_fill_update_data(struct bnx2x *bp, test_bit(BNX2X_Q_UPDATE_IN_VLAN_REM_CHNG, ¶ms->update_flags); - /* Outer VLAN sripping */ + /* Outer VLAN stripping */ data->outer_vlan_removal_enable_flg = test_bit(BNX2X_Q_UPDATE_OUT_VLAN_REM, ¶ms->update_flags); data->outer_vlan_removal_change_flg = @@ -4816,19 +4750,17 @@ static inline int bnx2x_q_send_update(struct bnx2x *bp, return -EINVAL; } - /* Clear the ramrod data */ memset(rdata, 0, sizeof(*rdata)); /* Fill the ramrod data */ bnx2x_q_fill_update_data(bp, o, update_params, rdata); - /* - * No need for an explicit memory barrier here as long we would - * need to ensure the ordering of writing to the SPQ element - * and updating of the SPQ producer which involves a memory - * read and we will have to put a full memory barrier there - * (inside bnx2x_sp_post()). + /* No need for an explicit memory barrier here as long we would + * need to ensure the ordering of writing to the SPQ element + * and updating of the SPQ producer which involves a memory + * read and we will have to put a full memory barrier there + * (inside bnx2x_sp_post()). */ return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_CLIENT_UPDATE, @@ -5038,8 +4970,7 @@ static int bnx2x_queue_chk_transition(struct bnx2x *bp, ¶ms->params.update; u8 next_tx_only = o->num_tx_only; - /* - * Forget all pending for completion commands if a driver only state + /* Forget all pending for completion commands if a driver only state * transition has been requested. */ if (test_bit(RAMROD_DRV_CLR_ONLY, ¶ms->ramrod_flags)) { @@ -5047,8 +4978,7 @@ static int bnx2x_queue_chk_transition(struct bnx2x *bp, o->next_state = BNX2X_Q_STATE_MAX; } - /* - * Don't allow a next state transition if we are in the middle of + /* Don't allow a next state transition if we are in the middle of * the previous one. */ if (o->pending) { @@ -5257,8 +5187,7 @@ enum bnx2x_func_state bnx2x_func_get_state(struct bnx2x *bp, if (o->pending) return BNX2X_F_STATE_MAX; - /* - * unsure the order of reading of o->pending and o->state + /* unsure the order of reading of o->pending and o->state * o->pending should be read first */ rmb(); @@ -5356,8 +5285,7 @@ static int bnx2x_func_chk_transition(struct bnx2x *bp, enum bnx2x_func_state state = o->state, next_state = BNX2X_F_STATE_MAX; enum bnx2x_func_cmd cmd = params->cmd; - /* - * Forget all pending for completion commands if a driver only state + /* Forget all pending for completion commands if a driver only state * transition has been requested. */ if (test_bit(RAMROD_DRV_CLR_ONLY, ¶ms->ramrod_flags)) { @@ -5365,8 +5293,7 @@ static int bnx2x_func_chk_transition(struct bnx2x *bp, o->next_state = BNX2X_F_STATE_MAX; } - /* - * Don't allow a next state transition if we are in the middle of + /* Don't allow a next state transition if we are in the middle of * the previous one. */ if (o->pending) @@ -5539,7 +5466,7 @@ static int bnx2x_func_hw_init(struct bnx2x *bp, goto init_err; } - /* Handle the beginning of COMMON_XXX pases separatelly... */ + /* Handle the beginning of COMMON_XXX pases separately... */ switch (load_code) { case FW_MSG_CODE_DRV_LOAD_COMMON_CHIP: rc = bnx2x_func_init_cmn_chip(bp, drv); @@ -5573,7 +5500,7 @@ static int bnx2x_func_hw_init(struct bnx2x *bp, init_err: drv->gunzip_end(bp); - /* In case of success, complete the comand immediatelly: no ramrods + /* In case of success, complete the command immediately: no ramrods * have been sent. */ if (!rc) @@ -5598,7 +5525,7 @@ static inline void bnx2x_func_reset_func(struct bnx2x *bp, } /** - * bnx2x_func_reset_port - reser HW at port stage + * bnx2x_func_reset_port - reset HW at port stage * * @bp: device handle * @drv: @@ -5620,7 +5547,7 @@ static inline void bnx2x_func_reset_port(struct bnx2x *bp, } /** - * bnx2x_func_reset_cmn - reser HW at common stage + * bnx2x_func_reset_cmn - reset HW at common stage * * @bp: device handle * @drv: @@ -5636,7 +5563,6 @@ static inline void bnx2x_func_reset_cmn(struct bnx2x *bp, drv->reset_hw_cmn(bp); } - static inline int bnx2x_func_hw_reset(struct bnx2x *bp, struct bnx2x_func_state_params *params) { @@ -5663,7 +5589,7 @@ static inline int bnx2x_func_hw_reset(struct bnx2x *bp, break; } - /* Complete the comand immediatelly: no ramrods have been sent. */ + /* Complete the command immediately: no ramrods have been sent. */ o->complete_cmd(bp, o, BNX2X_F_CMD_HW_RESET); return 0; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index 43c00bc84a0..798dfe99673 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -34,8 +34,7 @@ enum { RAMROD_RESTORE, /* Execute the next command now */ RAMROD_EXEC, - /* - * Don't add a new command and continue execution of posponed + /* Don't add a new command and continue execution of postponed * commands. If not set a new command will be added to the * pending commands list. */ @@ -129,8 +128,7 @@ enum bnx2x_vlan_mac_cmd { struct bnx2x_vlan_mac_data { /* Requested command: BNX2X_VLAN_MAC_XX */ enum bnx2x_vlan_mac_cmd cmd; - /* - * used to contain the data related vlan_mac_flags bits from + /* used to contain the data related vlan_mac_flags bits from * ramrod parameters. */ unsigned long vlan_mac_flags; @@ -190,14 +188,10 @@ typedef struct bnx2x_exeq_elem * struct bnx2x_exeq_elem *elem); struct bnx2x_exe_queue_obj { - /* - * Commands pending for an execution. - */ + /* Commands pending for an execution. */ struct list_head exe_queue; - /* - * Commands pending for an completion. - */ + /* Commands pending for an completion. */ struct list_head pending_comp; spinlock_t lock; @@ -245,14 +239,13 @@ struct bnx2x_exe_queue_obj { }; /***************** Classification verbs: Set/Del MAC/VLAN/VLAN-MAC ************/ /* - * Element in the VLAN_MAC registry list having all currenty configured + * Element in the VLAN_MAC registry list having all currently configured * rules. */ struct bnx2x_vlan_mac_registry_elem { struct list_head link; - /* - * Used to store the cam offset used for the mac/vlan/vlan-mac. + /* Used to store the cam offset used for the mac/vlan/vlan-mac. * Relevant for 57710 and 57711 only. VLANs and MACs share the * same CAM for these chips. */ @@ -310,7 +303,7 @@ struct bnx2x_vlan_mac_obj { * @param n number of elements to get * @param buf buffer preallocated by caller into which elements * will be copied. Note elements are 4-byte aligned - * so buffer size must be able to accomodate the + * so buffer size must be able to accommodate the * aligned elements. * * @return number of copied bytes @@ -395,7 +388,7 @@ struct bnx2x_vlan_mac_obj { * @param bp * @param p Command parameters (RAMROD_COMP_WAIT bit in * ramrod_flags is only taken into an account) - * @param ppos a pointer to the cooky that should be given back in the + * @param ppos a pointer to the cookie that should be given back in the * next call to make function handle the next element. If * *ppos is set to NULL it will restart the iterator. * If returned *ppos == NULL this means that the last @@ -408,7 +401,7 @@ struct bnx2x_vlan_mac_obj { struct bnx2x_vlan_mac_registry_elem **ppos); /** - * Should be called on a completion arival. + * Should be called on a completion arrival. * * @param bp * @param o @@ -447,7 +440,7 @@ void bnx2x_set_mac_in_nig(struct bnx2x *bp, /** RX_MODE verbs:DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */ -/* RX_MODE ramrod spesial flags: set in rx_mode_flags field in +/* RX_MODE ramrod special flags: set in rx_mode_flags field in * a bnx2x_rx_mode_ramrod_params. */ enum { @@ -475,8 +468,7 @@ struct bnx2x_rx_mode_ramrod_params { unsigned long ramrod_flags; unsigned long rx_mode_flags; - /* - * rdata is either a pointer to eth_filter_rules_ramrod_data(e2) or to + /* rdata is either a pointer to eth_filter_rules_ramrod_data(e2) or to * a tstorm_eth_mac_filter_config (e1x). */ void *rdata; @@ -646,12 +638,11 @@ struct bnx2x_credit_pool_obj { /* Maximum allowed credit. put() will check against it. */ int pool_sz; - /* - * Allocate a pool table statically. + /* Allocate a pool table statically. * - * Currently the mamimum allowed size is MAX_MAC_CREDIT_E2(272) + * Currently the maximum allowed size is MAX_MAC_CREDIT_E2(272) * - * The set bit in the table will mean that the entry is available. + * The set bit in the table will mean that the entry is available. */ #define BNX2X_POOL_VEC_SIZE (MAX_MAC_CREDIT_E2 / 64) u64 pool_mirror[BNX2X_POOL_VEC_SIZE]; @@ -832,7 +823,7 @@ enum { BNX2X_Q_FLG_TUN_INC_INNER_IP_ID }; -/* Queue type options: queue type may be a compination of below. */ +/* Queue type options: queue type may be a combination of below. */ enum bnx2x_q_type { /** TODO: Consider moving both these flags into the init() * ramrod params. @@ -1002,10 +993,9 @@ struct bnx2x_queue_sp_obj { u8 cl_id; u8 func_id; - /* - * number of traffic classes supported by queue. - * The primary connection of the queue suppotrs the first traffic - * class. Any further traffic class is suppoted by a tx-only + /* number of traffic classes supported by queue. + * The primary connection of the queue supports the first traffic + * class. Any further traffic class is supported by a tx-only * connection. * * Therefore max_cos is also a number of valid entries in the cids @@ -1021,7 +1011,7 @@ struct bnx2x_queue_sp_obj { /* BNX2X_Q_CMD_XX bits. This object implements "one * pending" paradigm but for debug and tracing purposes it's - * more convinient to have different bits for different + * more convenient to have different bits for different * commands. */ unsigned long pending; @@ -1210,7 +1200,7 @@ struct bnx2x_func_sp_obj { /* BNX2X_FUNC_CMD_XX bits. This object implements "one * pending" paradigm but for debug and tracing purposes it's - * more convinient to have different bits for different + * more convenient to have different bits for different * commands. */ unsigned long pending; @@ -1329,7 +1319,7 @@ void bnx2x_init_rx_mode_obj(struct bnx2x *bp, * * @p: Command parameters * - * Return: 0 - if operation was successfull and there is no pending completions, + * Return: 0 - if operation was successful and there is no pending completions, * positive number - if there are pending completions, * negative - if there were errors */ @@ -1361,7 +1351,7 @@ void bnx2x_init_mcast_obj(struct bnx2x *bp, * the current command will be enqueued to the tail of the * pending commands list. * - * Return: 0 is operation was successfull and there are no pending completions, + * Return: 0 is operation was successful and there are no pending completions, * negative if there were errors, positive if there are pending * completions. */ @@ -1377,7 +1367,6 @@ void bnx2x_init_vlan_credit_pool(struct bnx2x *bp, struct bnx2x_credit_pool_obj *p, u8 func_id, u8 func_num); - /****************** RSS CONFIGURATION ****************/ void bnx2x_init_rss_config_obj(struct bnx2x *bp, struct bnx2x_rss_config_obj *rss_obj, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 2ce7c747136..95861efb505 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -1341,7 +1341,7 @@ int bnx2x_vfop_qdown_cmd(struct bnx2x *bp, */ /* internal vf enable - until vf is enabled internally all transactions - * are blocked. this routine should always be called last with pretend. + * are blocked. This routine should always be called last with pretend. */ static void bnx2x_vf_enable_internal(struct bnx2x *bp, u8 enable) { @@ -1459,21 +1459,16 @@ static u8 bnx2x_vf_is_pcie_pending(struct bnx2x *bp, u8 abs_vfid) struct bnx2x_virtf *vf = bnx2x_vf_by_abs_fid(bp, abs_vfid); if (!vf) - goto unknown_dev; + return false; dev = pci_get_bus_and_slot(vf->bus, vf->devfn); if (dev) return bnx2x_is_pcie_pending(dev); - -unknown_dev: return false; } int bnx2x_vf_flr_clnup_epilog(struct bnx2x *bp, u8 abs_vfid) { - /* Wait 100ms */ - msleep(100); - /* Verify no pending pci transactions */ if (bnx2x_vf_is_pcie_pending(bp, abs_vfid)) BNX2X_ERR("PCIE Transactions still pending\n"); @@ -1620,7 +1615,7 @@ next_vf_to_clean: i++) ; - DP(BNX2X_MSG_IOV, "next vf to cleanup: %d. num of vfs: %d\n", i, + DP(BNX2X_MSG_IOV, "next vf to cleanup: %d. Num of vfs: %d\n", i, BNX2X_NR_VIRTFN(bp)); if (i < BNX2X_NR_VIRTFN(bp)) { @@ -1743,7 +1738,7 @@ void bnx2x_iov_init_dq(struct bnx2x *bp) REG_WR(bp, DORQ_REG_VF_TYPE_MIN_MCID_0, 0); REG_WR(bp, DORQ_REG_VF_TYPE_MAX_MCID_0, 0x1ffff); - /* set the number of VF alllowed doorbells to the full DQ range */ + /* set the number of VF allowed doorbells to the full DQ range */ REG_WR(bp, DORQ_REG_VF_NORM_MAX_CID_COUNT, 0x20000); /* set the VF doorbell threshold */ @@ -2176,6 +2171,9 @@ int bnx2x_iov_nic_init(struct bnx2x *bp) DP(BNX2X_MSG_IOV, "num of vfs: %d\n", (bp)->vfdb->sriov.nr_virtfn); + /* let FLR complete ... */ + msleep(100); + /* initialize vf database */ for_each_vf(bp, vfid) { struct bnx2x_virtf *vf = BP_VF(bp, vfid); @@ -2403,7 +2401,7 @@ int bnx2x_iov_eq_sp_event(struct bnx2x *bp, union event_ring_elem *elem) /* extract vf and rxq index from vf_cid - relies on the following: * 1. vfid on cid reflects the true abs_vfid - * 2. the max number of VFs (per path) is 64 + * 2. The max number of VFs (per path) is 64 */ qidx = cid & ((1 << BNX2X_VF_CID_WND)-1); abs_vfid = (cid >> BNX2X_VF_CID_WND) & (BNX2X_MAX_NUM_OF_VFS-1); @@ -2461,7 +2459,7 @@ static struct bnx2x_virtf *bnx2x_vf_by_cid(struct bnx2x *bp, int vf_cid) { /* extract the vf from vf_cid - relies on the following: * 1. vfid on cid reflects the true abs_vfid - * 2. the max number of VFs (per path) is 64 + * 2. The max number of VFs (per path) is 64 */ int abs_vfid = (vf_cid >> BNX2X_VF_CID_WND) & (BNX2X_MAX_NUM_OF_VFS-1); return bnx2x_vf_by_abs_fid(bp, abs_vfid); @@ -2480,7 +2478,7 @@ void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid, if (vf) { /* extract queue index from vf_cid - relies on the following: * 1. vfid on cid reflects the true abs_vfid - * 2. the max number of VFs (per path) is 64 + * 2. The max number of VFs (per path) is 64 */ int q_index = vf_cid & ((1 << BNX2X_VF_CID_WND)-1); *q_obj = &bnx2x_vfq(vf, q_index, sp_obj); @@ -2705,7 +2703,7 @@ int bnx2x_vf_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf, } /* static allocation: - * the global maximum number are fixed per VF. fail the request if + * the global maximum number are fixed per VF. Fail the request if * requested number exceed these globals */ if (!bnx2x_vf_chk_avail_resc(bp, vf, resc)) { @@ -2777,6 +2775,10 @@ int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf, dma_addr_t *sb_map) vf->abs_vfid, vf->state); return -EINVAL; } + + /* let FLR complete ... */ + msleep(100); + /* FLR cleanup epilogue */ if (bnx2x_vf_flr_clnup_epilog(bp, vf->abs_vfid)) return -EBUSY; @@ -2890,7 +2892,7 @@ int bnx2x_vfop_close_cmd(struct bnx2x *bp, return -ENOMEM; } -/* VF release can be called either: 1. the VF was acquired but +/* VF release can be called either: 1. The VF was acquired but * not enabled 2. the vf was enabled or in the process of being * enabled */ @@ -3024,7 +3026,6 @@ void bnx2x_unlock_vf_pf_channel(struct bnx2x *bp, struct bnx2x_virtf *vf, int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs_param) { - struct bnx2x *bp = netdev_priv(pci_get_drvdata(dev)); DP(BNX2X_MSG_IOV, "bnx2x_sriov_configure called with %d, BNX2X_NR_VIRTFN(bp) was %d\n", @@ -3032,7 +3033,7 @@ int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs_param) /* HW channel is only operational when PF is up */ if (bp->state != BNX2X_STATE_OPEN) { - BNX2X_ERR("VF num configurtion via sysfs not supported while PF is down"); + BNX2X_ERR("VF num configuration via sysfs not supported while PF is down\n"); return -EINVAL; } @@ -3086,6 +3087,11 @@ void bnx2x_disable_sriov(struct bnx2x *bp) static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx, struct bnx2x_virtf *vf) { + if (bp->state != BNX2X_STATE_OPEN) { + BNX2X_ERR("vf ndo called though PF is down\n"); + return -EINVAL; + } + if (!IS_SRIOV(bp)) { BNX2X_ERR("vf ndo called though sriov is disabled\n"); return -EINVAL; @@ -3141,7 +3147,7 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, /* mac configured by ndo so its in bulletin board */ memcpy(&ivi->mac, bulletin->mac, ETH_ALEN); else - /* funtion has not been loaded yet. Show mac as 0s */ + /* function has not been loaded yet. Show mac as 0s */ memset(&ivi->mac, 0, ETH_ALEN); /* vlan */ @@ -3149,7 +3155,7 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, /* vlan configured by ndo so its in bulletin board */ memcpy(&ivi->vlan, &bulletin->vlan, VLAN_HLEN); else - /* funtion has not been loaded yet. Show vlans as 0s */ + /* function has not been loaded yet. Show vlans as 0s */ memset(&ivi->vlan, 0, VLAN_HLEN); } @@ -3189,7 +3195,7 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) return -EINVAL; } - /* update PF's copy of the VF's bulletin. will no longer accept mac + /* update PF's copy of the VF's bulletin. Will no longer accept mac * configuration requests from vf unless match this mac */ bulletin->valid_bitmap |= 1 << MAC_ADDR_VALID; @@ -3358,8 +3364,11 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) return 0; } -/* crc is the first field in the bulletin board. compute the crc over the - * entire bulletin board excluding the crc field itself +/* crc is the first field in the bulletin board. Compute the crc over the + * entire bulletin board excluding the crc field itself. Use the length field + * as the Bulletin Board was posted by a PF with possibly a different version + * from the vf which will sample it. Therefore, the length is computed by the + * PF and the used blindly by the VF. */ u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, struct pf_vf_bulletin_content *bulletin) @@ -3389,7 +3398,7 @@ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp) if (bulletin.crc == bnx2x_crc_vf_bulletin(bp, &bulletin)) break; - BNX2X_ERR("bad crc on bulletin board. contained %x computed %x\n", + BNX2X_ERR("bad crc on bulletin board. Contained %x computed %x\n", bulletin.crc, bnx2x_crc_vf_bulletin(bp, &bulletin)); } @@ -3417,6 +3426,20 @@ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp) return PFVF_BULLETIN_UPDATED; } +void bnx2x_timer_sriov(struct bnx2x *bp) +{ + bnx2x_sample_bulletin(bp); + + /* if channel is down we need to self destruct */ + if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) { + smp_mb__before_clear_bit(); + set_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN, + &bp->sp_rtnl_state); + smp_mb__after_clear_bit(); + schedule_delayed_work(&bp->sp_rtnl_task, 0); + } +} + void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp) { /* vf doorbells are embedded within the regview */ @@ -3452,7 +3475,7 @@ int bnx2x_open_epilog(struct bnx2x *bp) * register_netdevice which must have rtnl lock taken. As we are holding * the lock right now, that could only work if the probe would not take * the lock. However, as the probe of the vf may be called from other - * contexts as well (such as passthrough to vm failes) it can't assume + * contexts as well (such as passthrough to vm fails) it can't assume * the lock is being held for it. Using delayed work here allows the * probe code to simply take the lock (i.e. wait for it to be released * if it is being held). We only want to do this if the number of VFs @@ -3467,3 +3490,23 @@ int bnx2x_open_epilog(struct bnx2x *bp) return 0; } + +void bnx2x_iov_channel_down(struct bnx2x *bp) +{ + int vf_idx; + struct pf_vf_bulletin_content *bulletin; + + if (!IS_SRIOV(bp)) + return; + + for_each_vf(bp, vf_idx) { + /* locate this VFs bulletin board and update the channel down + * bit + */ + bulletin = BP_VF_BULLETIN(bp, vf_idx); + bulletin->valid_bitmap |= 1 << CHANNEL_DOWN; + + /* update vf bulletin board */ + bnx2x_post_vf_bulletin(bp, vf_idx); + } +} diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index d67ddc554c0..d143a7cdbbb 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -197,7 +197,7 @@ struct bnx2x_virtf { u8 state; #define VF_FREE 0 /* VF ready to be acquired holds no resc */ -#define VF_ACQUIRED 1 /* VF aquired, but not initalized */ +#define VF_ACQUIRED 1 /* VF acquired, but not initialized */ #define VF_ENABLED 2 /* VF Enabled */ #define VF_RESET 3 /* VF FLR'd, pending cleanup */ @@ -496,7 +496,7 @@ enum { else if ((next) == VFOP_VERIFY_PEND) \ BNX2X_ERR("expected pending\n"); \ else { \ - DP(BNX2X_MSG_IOV, "no ramrod. scheduling\n"); \ + DP(BNX2X_MSG_IOV, "no ramrod. Scheduling\n"); \ atomic_set(&vf->op_in_progress, 1); \ queue_delayed_work(bnx2x_wq, &bp->sp_task, 0); \ return; \ @@ -722,7 +722,6 @@ u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, struct pf_vf_bulletin_content *bulletin); int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf); - enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp); /* VF side vfpf channel functions */ @@ -752,6 +751,7 @@ static inline int bnx2x_vf_ustorm_prods_offset(struct bnx2x *bp, } enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp); +void bnx2x_timer_sriov(struct bnx2x *bp); void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp); int bnx2x_vf_pci_alloc(struct bnx2x *bp); int bnx2x_enable_sriov(struct bnx2x *bp); @@ -762,6 +762,7 @@ static inline int bnx2x_vf_headroom(struct bnx2x *bp) } void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp); int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs); +void bnx2x_iov_channel_down(struct bnx2x *bp); int bnx2x_open_epilog(struct bnx2x *bp); #else /* CONFIG_BNX2X_SRIOV */ @@ -809,6 +810,7 @@ static inline enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp { return PFVF_BULLETIN_UNCHANGED; } +static inline void bnx2x_timer_sriov(struct bnx2x *bp) {} static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp) { @@ -818,6 +820,7 @@ static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp) static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; } static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {} static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; } +static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {} static inline int bnx2x_open_epilog(struct bnx2x *bp) {return 0; } #endif /* CONFIG_BNX2X_SRIOV */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 2ca3d94fcec..98366abd02b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -1002,7 +1002,6 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp) qstats->valid_bytes_received_lo = qstats->total_bytes_received_lo; - UPDATE_EXTEND_TSTAT(rcv_ucast_pkts, total_unicast_packets_received); UPDATE_EXTEND_TSTAT(rcv_mcast_pkts, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index d117f472816..853824d258e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -40,7 +40,6 @@ struct nig_stats { u32 egress_mac_pkt1_hi; }; - enum bnx2x_stats_event { STATS_EVENT_PMF = 0, STATS_EVENT_LINK_UP, @@ -208,7 +207,6 @@ struct bnx2x_eth_stats { u32 eee_tx_lpi; }; - struct bnx2x_eth_q_stats { u32 total_unicast_bytes_received_hi; u32 total_unicast_bytes_received_lo; @@ -331,7 +329,6 @@ struct bnx2x_fw_port_stats_old { u32 mac_discard; }; - /**************************************************************************** * Macros ****************************************************************************/ @@ -536,7 +533,6 @@ struct bnx2x_fw_port_stats_old { SUB_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) - /* forward */ struct bnx2x; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index 928b074d7d8..2088063151d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -113,7 +113,7 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping) { struct cstorm_vf_zone_data __iomem *zone_data = REG_ADDR(bp, PXP_VF_ADDR_CSDM_GLOBAL_START); - int tout = 600, interval = 100; /* wait for 60 seconds */ + int tout = 100, interval = 100; /* wait for 10 seconds */ if (*done) { BNX2X_ERR("done was non zero before message to pf was sent\n"); @@ -121,6 +121,16 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping) return -EINVAL; } + /* if PF indicated channel is down avoid sending message. Return success + * so calling flow can continue + */ + bnx2x_sample_bulletin(bp); + if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) { + DP(BNX2X_MSG_IOV, "detecting channel down. Aborting message\n"); + *done = PFVF_STATUS_SUCCESS; + return 0; + } + /* Write message address */ writel(U64_LO(msg_mapping), &zone_data->non_trigger.vf_pf_channel.msg_addr_lo); @@ -233,7 +243,7 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) attempts++; - /* test whether the PF accepted our request. If not, humble the + /* test whether the PF accepted our request. If not, humble * the request and try again. */ if (bp->acquire_resp.hdr.status == PFVF_STATUS_SUCCESS) { @@ -333,7 +343,7 @@ int bnx2x_vfpf_release(struct bnx2x *bp) DP(BNX2X_MSG_SP, "vf released\n"); } else { /* PF reports error */ - BNX2X_ERR("PF failed our release request - are we out of sync? response status: %d\n", + BNX2X_ERR("PF failed our release request - are we out of sync? Response status: %d\n", resp->hdr.status); rc = -EAGAIN; goto out; @@ -787,7 +797,7 @@ static inline void bnx2x_set_vf_mbxs_valid(struct bnx2x *bp) storm_memset_vf_mbx_valid(bp, bnx2x_vf(bp, i, abs_vfid)); } -/* enable vf_pf mailbox (aka vf-pf-chanell) */ +/* enable vf_pf mailbox (aka vf-pf-channel) */ void bnx2x_vf_enable_mbx(struct bnx2x *bp, u8 abs_vfid) { bnx2x_vf_flr_clnup_epilog(bp, abs_vfid); @@ -844,7 +854,6 @@ static int bnx2x_copy32_vf_dmae(struct bnx2x *bp, u8 from_vf, dmae.dst_addr_hi = vf_addr_hi; } dmae.len = len32; - bnx2x_dp_dmae(bp, &dmae, BNX2X_MSG_DMAE); /* issue the command and wait for completion */ return bnx2x_issue_dmae_with_comp(bp, &dmae); @@ -1072,7 +1081,7 @@ static void bnx2x_vf_mbx_set_q_flags(struct bnx2x *bp, u32 mbx_q_flags, if (mbx_q_flags & VFPF_QUEUE_FLG_DHC) __set_bit(BNX2X_Q_FLG_DHC, sp_q_flags); - /* outer vlan removal is set according to the PF's multi fuction mode */ + /* outer vlan removal is set according to PF's multi function mode */ if (IS_MF_SD(bp)) __set_bit(BNX2X_Q_FLG_OV, sp_q_flags); } @@ -1104,7 +1113,7 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf, struct bnx2x_queue_init_params *init_p; struct bnx2x_queue_setup_params *setup_p; - /* reinit the VF operation context */ + /* re-init the VF operation context */ memset(&vf->op_params.qctor, 0 , sizeof(vf->op_params.qctor)); setup_p = &vf->op_params.qctor.prep_qsetup; init_p = &vf->op_params.qctor.qstate.params.init; @@ -1588,8 +1597,9 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf, * support them. Or this may be because someone wrote a crappy * VF driver and is sending garbage over the channel. */ - BNX2X_ERR("unknown TLV. type %d length %d. first 20 bytes of mailbox buffer:\n", - mbx->first_tlv.tl.type, mbx->first_tlv.tl.length); + BNX2X_ERR("unknown TLV. type %d length %d vf->state was %d. first 20 bytes of mailbox buffer:\n", + mbx->first_tlv.tl.type, mbx->first_tlv.tl.length, + vf->state); for (i = 0; i < 20; i++) DP_CONT(BNX2X_MSG_IOV, "%x ", mbx->msg->req.tlv_buf_size.tlv_buffer[i]); @@ -1605,8 +1615,11 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf, bnx2x_vf_mbx_resp(bp, vf); } else { /* can't send a response since this VF is unknown to us - * just unlock the channel and be done with. + * just ack the FW to release the mailbox and unlock + * the channel. */ + storm_memset_vf_mbx_ack(bp, vf->abs_vfid); + mmiowb(); bnx2x_unlock_vf_pf_channel(bp, vf, mbx->first_tlv.tl.type); } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h index 41708faab57..f3ad174a3a6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h @@ -331,7 +331,10 @@ struct pf_vf_bulletin_content { #define VLAN_VALID 1 /* when set, the vf should not access * the vfpf channel */ - +#define CHANNEL_DOWN 2 /* vfpf channel is disabled. VFs are not + * to attempt to send messages on the + * channel after this bit is set + */ u8 mac[ETH_ALEN]; u8 mac_padding[2]; diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 6b0dc131b20..d78d4cf140e 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -5622,7 +5622,7 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event, static int cnic_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *netdev = ptr; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct cnic_dev *dev; int new_dev = 0; diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index e80bfb60c3e..c2777712da9 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2197,7 +2197,7 @@ static const struct net_device_ops sbmac_netdev_ops = { static int sbmac_init(struct platform_device *pldev, long long base) { - struct net_device *dev = dev_get_drvdata(&pldev->dev); + struct net_device *dev = platform_get_drvdata(pldev); int idx = pldev->id; struct sbmac_softc *sc = netdev_priv(dev); unsigned char *eaddr; @@ -2275,7 +2275,7 @@ static int sbmac_init(struct platform_device *pldev, long long base) dev->name); goto free_mdio; } - dev_set_drvdata(&pldev->dev, sc->mii_bus); + platform_set_drvdata(pldev, sc->mii_bus); err = register_netdev(dev); if (err) { @@ -2300,7 +2300,6 @@ static int sbmac_init(struct platform_device *pldev, long long base) return 0; unreg_mdio: mdiobus_unregister(sc->mii_bus); - dev_set_drvdata(&pldev->dev, NULL); free_mdio: mdiobus_free(sc->mii_bus); uninit_ctx: @@ -2624,7 +2623,7 @@ static int sbmac_probe(struct platform_device *pldev) goto out_unmap; } - dev_set_drvdata(&pldev->dev, dev); + platform_set_drvdata(pldev, dev); SET_NETDEV_DEV(dev, &pldev->dev); sc = netdev_priv(dev); @@ -2649,7 +2648,7 @@ out_out: static int __exit sbmac_remove(struct platform_device *pldev) { - struct net_device *dev = dev_get_drvdata(&pldev->dev); + struct net_device *dev = platform_get_drvdata(pldev); struct sbmac_softc *sc = netdev_priv(dev); unregister_netdev(dev); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index a13463e8a2c..d964f302ac9 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -968,9 +968,6 @@ static void tg3_ape_driver_state_change(struct tg3 *tp, int kind) event = APE_EVENT_STATUS_STATE_UNLOAD; break; - case RESET_KIND_SUSPEND: - event = APE_EVENT_STATUS_STATE_SUSPEND; - break; default: return; } @@ -1317,8 +1314,8 @@ static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable) if (err) return err; - if (enable) + if (enable) val |= MII_TG3_AUXCTL_ACTL_SMDSP_ENA; else val &= ~MII_TG3_AUXCTL_ACTL_SMDSP_ENA; @@ -1745,10 +1742,6 @@ static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind) break; } } - - if (kind == RESET_KIND_INIT || - kind == RESET_KIND_SUSPEND) - tg3_ape_driver_state_change(tp, kind); } /* tp->lock is held. */ @@ -1770,9 +1763,6 @@ static void tg3_write_sig_post_reset(struct tg3 *tp, int kind) break; } } - - if (kind == RESET_KIND_SHUTDOWN) - tg3_ape_driver_state_change(tp, kind); } /* tp->lock is held. */ @@ -2341,6 +2331,46 @@ static void tg3_phy_apply_otp(struct tg3 *tp) tg3_phy_toggle_auxctl_smdsp(tp, false); } +static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_eee *eee) +{ + u32 val; + struct ethtool_eee *dest = &tp->eee; + + if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) + return; + + if (eee) + dest = eee; + + if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, TG3_CL45_D7_EEERES_STAT, &val)) + return; + + /* Pull eee_active */ + if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T || + val == TG3_CL45_D7_EEERES_STAT_LP_100TX) { + dest->eee_active = 1; + } else + dest->eee_active = 0; + + /* Pull lp advertised settings */ + if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, &val)) + return; + dest->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); + + /* Pull advertised and eee_enabled settings */ + if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val)) + return; + dest->eee_enabled = !!val; + dest->advertised = mmd_eee_adv_to_ethtool_adv_t(val); + + /* Pull tx_lpi_enabled */ + val = tr32(TG3_CPMU_EEE_MODE); + dest->tx_lpi_enabled = !!(val & TG3_CPMU_EEEMD_LPI_IN_TX); + + /* Pull lpi timer value */ + dest->tx_lpi_timer = tr32(TG3_CPMU_EEE_DBTMR1) & 0xffff; +} + static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up) { u32 val; @@ -2364,11 +2394,8 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up) tw32(TG3_CPMU_EEE_CTRL, eeectl); - tg3_phy_cl45_read(tp, MDIO_MMD_AN, - TG3_CL45_D7_EEERES_STAT, &val); - - if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T || - val == TG3_CL45_D7_EEERES_STAT_LP_100TX) + tg3_eee_pull_config(tp, NULL); + if (tp->eee.eee_active) tp->setlpicnt = 2; } @@ -4192,6 +4219,8 @@ static int tg3_power_down_prepare(struct tg3 *tp) tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN); + tg3_ape_driver_state_change(tp, RESET_KIND_SHUTDOWN); + return 0; } @@ -4292,6 +4321,16 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl) /* Advertise 1000-BaseT EEE ability */ if (advertise & ADVERTISED_1000baseT_Full) val |= MDIO_AN_EEE_ADV_1000T; + + if (!tp->eee.eee_enabled) { + val = 0; + tp->eee.advertised = 0; + } else { + tp->eee.advertised = advertise & + (ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full); + } + err = tg3_phy_cl45_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); if (err) val = 0; @@ -4536,26 +4575,23 @@ static int tg3_init_5401phy_dsp(struct tg3 *tp) static bool tg3_phy_eee_config_ok(struct tg3 *tp) { - u32 val; - u32 tgtadv = 0; - u32 advertising = tp->link_config.advertising; + struct ethtool_eee eee; if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) return true; - if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val)) - return false; - - val &= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); - - - if (advertising & ADVERTISED_100baseT_Full) - tgtadv |= MDIO_AN_EEE_ADV_100TX; - if (advertising & ADVERTISED_1000baseT_Full) - tgtadv |= MDIO_AN_EEE_ADV_1000T; + tg3_eee_pull_config(tp, &eee); - if (val != tgtadv) - return false; + if (tp->eee.eee_enabled) { + if (tp->eee.advertised != eee.advertised || + tp->eee.tx_lpi_timer != eee.tx_lpi_timer || + tp->eee.tx_lpi_enabled != eee.tx_lpi_enabled) + return false; + } else { + /* EEE is disabled but we're advertising */ + if (eee.advertised) + return false; + } return true; } @@ -4656,6 +4692,42 @@ static void tg3_clear_mac_status(struct tg3 *tp) udelay(40); } +static void tg3_setup_eee(struct tg3 *tp) +{ + u32 val; + + val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 | + TG3_CPMU_EEE_LNKIDL_UART_IDL; + if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0) + val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT; + + tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val); + + tw32_f(TG3_CPMU_EEE_CTRL, + TG3_CPMU_EEE_CTRL_EXIT_20_1_US); + + val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET | + (tp->eee.tx_lpi_enabled ? TG3_CPMU_EEEMD_LPI_IN_TX : 0) | + TG3_CPMU_EEEMD_LPI_IN_RX | + TG3_CPMU_EEEMD_EEE_ENABLE; + + if (tg3_asic_rev(tp) != ASIC_REV_5717) + val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN; + + if (tg3_flag(tp, ENABLE_APE)) + val |= TG3_CPMU_EEEMD_APE_TX_DET_EN; + + tw32_f(TG3_CPMU_EEE_MODE, tp->eee.eee_enabled ? val : 0); + + tw32_f(TG3_CPMU_EEE_DBTMR1, + TG3_CPMU_DBTMR1_PCIEXIT_2047US | + (tp->eee.tx_lpi_timer & 0xffff)); + + tw32_f(TG3_CPMU_EEE_DBTMR2, + TG3_CPMU_DBTMR2_APE_TX_2047US | + TG3_CPMU_DBTMR2_TXIDXEQ_2047US); +} + static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset) { bool current_link_up; @@ -4822,8 +4894,10 @@ static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset) */ if (!eee_config_ok && (tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && - !force_reset) + !force_reset) { + tg3_setup_eee(tp); tg3_phy_reset(tp); + } } else { if (!(bmcr & BMCR_ANENABLE) && tp->link_config.speed == current_speed && @@ -6335,9 +6409,7 @@ static void tg3_tx_recover(struct tg3 *tp) "Please report the problem to the driver maintainer " "and include system chipset information.\n"); - spin_lock(&tp->lock); tg3_flag_set(tp, TX_RECOVERY_PENDING); - spin_unlock(&tp->lock); } static inline u32 tg3_tx_avail(struct tg3_napi *tnapi) @@ -9205,11 +9277,9 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) } /* tp->lock is held. */ -static void tg3_rings_reset(struct tg3 *tp) +static void tg3_tx_rcbs_disable(struct tg3 *tp) { - int i; - u32 stblk, txrcb, rxrcb, limit; - struct tg3_napi *tnapi = &tp->napi[0]; + u32 txrcb, limit; /* Disable all transmit rings but the first. */ if (!tg3_flag(tp, 5705_PLUS)) @@ -9226,7 +9296,33 @@ static void tg3_rings_reset(struct tg3 *tp) txrcb < limit; txrcb += TG3_BDINFO_SIZE) tg3_write_mem(tp, txrcb + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); +} + +/* tp->lock is held. */ +static void tg3_tx_rcbs_init(struct tg3 *tp) +{ + int i = 0; + u32 txrcb = NIC_SRAM_SEND_RCB; + + if (tg3_flag(tp, ENABLE_TSS)) + i++; + + for (; i < tp->irq_max; i++, txrcb += TG3_BDINFO_SIZE) { + struct tg3_napi *tnapi = &tp->napi[i]; + + if (!tnapi->tx_ring) + continue; + + tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, + (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT), + NIC_SRAM_TX_BUFFER_DESC); + } +} +/* tp->lock is held. */ +static void tg3_rx_ret_rcbs_disable(struct tg3 *tp) +{ + u32 rxrcb, limit; /* Disable all receive return rings but the first. */ if (tg3_flag(tp, 5717_PLUS)) @@ -9244,6 +9340,39 @@ static void tg3_rings_reset(struct tg3 *tp) rxrcb < limit; rxrcb += TG3_BDINFO_SIZE) tg3_write_mem(tp, rxrcb + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); +} + +/* tp->lock is held. */ +static void tg3_rx_ret_rcbs_init(struct tg3 *tp) +{ + int i = 0; + u32 rxrcb = NIC_SRAM_RCV_RET_RCB; + + if (tg3_flag(tp, ENABLE_RSS)) + i++; + + for (; i < tp->irq_max; i++, rxrcb += TG3_BDINFO_SIZE) { + struct tg3_napi *tnapi = &tp->napi[i]; + + if (!tnapi->rx_rcb) + continue; + + tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, + (tp->rx_ret_ring_mask + 1) << + BDINFO_FLAGS_MAXLEN_SHIFT, 0); + } +} + +/* tp->lock is held. */ +static void tg3_rings_reset(struct tg3 *tp) +{ + int i; + u32 stblk; + struct tg3_napi *tnapi = &tp->napi[0]; + + tg3_tx_rcbs_disable(tp); + + tg3_rx_ret_rcbs_disable(tp); /* Disable interrupts */ tw32_mailbox_f(tp->napi[0].int_mbox, 1); @@ -9280,9 +9409,6 @@ static void tg3_rings_reset(struct tg3 *tp) tw32_tx_mbox(mbox + i * 8, 0); } - txrcb = NIC_SRAM_SEND_RCB; - rxrcb = NIC_SRAM_RCV_RET_RCB; - /* Clear status block in ram. */ memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); @@ -9292,46 +9418,20 @@ static void tg3_rings_reset(struct tg3 *tp) tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tnapi->status_mapping & 0xffffffff)); - if (tnapi->tx_ring) { - tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, - (TG3_TX_RING_SIZE << - BDINFO_FLAGS_MAXLEN_SHIFT), - NIC_SRAM_TX_BUFFER_DESC); - txrcb += TG3_BDINFO_SIZE; - } - - if (tnapi->rx_rcb) { - tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, - (tp->rx_ret_ring_mask + 1) << - BDINFO_FLAGS_MAXLEN_SHIFT, 0); - rxrcb += TG3_BDINFO_SIZE; - } - stblk = HOSTCC_STATBLCK_RING1; for (i = 1, tnapi++; i < tp->irq_cnt; i++, tnapi++) { u64 mapping = (u64)tnapi->status_mapping; tw32(stblk + TG3_64BIT_REG_HIGH, mapping >> 32); tw32(stblk + TG3_64BIT_REG_LOW, mapping & 0xffffffff); + stblk += 8; /* Clear status block in ram. */ memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); - - if (tnapi->tx_ring) { - tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, - (TG3_TX_RING_SIZE << - BDINFO_FLAGS_MAXLEN_SHIFT), - NIC_SRAM_TX_BUFFER_DESC); - txrcb += TG3_BDINFO_SIZE; - } - - tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, - ((tp->rx_ret_ring_mask + 1) << - BDINFO_FLAGS_MAXLEN_SHIFT), 0); - - stblk += 8; - rxrcb += TG3_BDINFO_SIZE; } + + tg3_tx_rcbs_init(tp); + tg3_rx_ret_rcbs_init(tp); } static void tg3_setup_rxbd_thresholds(struct tg3 *tp) @@ -9531,46 +9631,17 @@ static int tg3_reset_hw(struct tg3 *tp, bool reset_phy) if (tg3_flag(tp, INIT_COMPLETE)) tg3_abort_hw(tp, 1); - /* Enable MAC control of LPI */ - if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) { - val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 | - TG3_CPMU_EEE_LNKIDL_UART_IDL; - if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0) - val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT; - - tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val); - - tw32_f(TG3_CPMU_EEE_CTRL, - TG3_CPMU_EEE_CTRL_EXIT_20_1_US); - - val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET | - TG3_CPMU_EEEMD_LPI_IN_TX | - TG3_CPMU_EEEMD_LPI_IN_RX | - TG3_CPMU_EEEMD_EEE_ENABLE; - - if (tg3_asic_rev(tp) != ASIC_REV_5717) - val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN; - - if (tg3_flag(tp, ENABLE_APE)) - val |= TG3_CPMU_EEEMD_APE_TX_DET_EN; - - tw32_f(TG3_CPMU_EEE_MODE, val); - - tw32_f(TG3_CPMU_EEE_DBTMR1, - TG3_CPMU_DBTMR1_PCIEXIT_2047US | - TG3_CPMU_DBTMR1_LNKIDLE_2047US); - - tw32_f(TG3_CPMU_EEE_DBTMR2, - TG3_CPMU_DBTMR2_APE_TX_2047US | - TG3_CPMU_DBTMR2_TXIDXEQ_2047US); - } - if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && !(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) { tg3_phy_pull_config(tp); + tg3_eee_pull_config(tp, NULL); tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED; } + /* Enable MAC control of LPI */ + if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) + tg3_setup_eee(tp); + if (reset_phy) tg3_phy_reset(tp); @@ -11226,7 +11297,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, */ err = tg3_alloc_consistent(tp); if (err) - goto err_out1; + goto out_ints_fini; tg3_napi_init(tp); @@ -11240,12 +11311,15 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tnapi = &tp->napi[i]; free_irq(tnapi->irq_vec, tnapi); } - goto err_out2; + goto out_napi_fini; } } tg3_full_lock(tp, 0); + if (init) + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); + err = tg3_init_hw(tp, reset_phy); if (err) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); @@ -11255,7 +11329,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_full_unlock(tp); if (err) - goto err_out3; + goto out_free_irq; if (test_irq && tg3_flag(tp, USING_MSI)) { err = tg3_test_msi(tp); @@ -11266,7 +11340,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_free_rings(tp); tg3_full_unlock(tp); - goto err_out2; + goto out_napi_fini; } if (!tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, USING_MSI)) { @@ -11306,18 +11380,18 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, return 0; -err_out3: +out_free_irq: for (i = tp->irq_cnt - 1; i >= 0; i--) { struct tg3_napi *tnapi = &tp->napi[i]; free_irq(tnapi->irq_vec, tnapi); } -err_out2: +out_napi_fini: tg3_napi_disable(tp); tg3_napi_fini(tp); tg3_free_consistent(tp); -err_out1: +out_ints_fini: tg3_ints_fini(tp); return err; @@ -13362,11 +13436,13 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest, struct tg3 *tp = netdev_priv(dev); bool doextlpbk = etest->flags & ETH_TEST_FL_EXTERNAL_LB; - if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) && - tg3_power_up(tp)) { - etest->flags |= ETH_TEST_FL_FAILED; - memset(data, 1, sizeof(u64) * TG3_NUM_TEST); - return; + if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) { + if (tg3_power_up(tp)) { + etest->flags |= ETH_TEST_FL_FAILED; + memset(data, 1, sizeof(u64) * TG3_NUM_TEST); + return; + } + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); } memset(data, 0, sizeof(u64) * TG3_NUM_TEST); @@ -13657,6 +13733,57 @@ static int tg3_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) return 0; } +static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct tg3 *tp = netdev_priv(dev); + + if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) { + netdev_warn(tp->dev, "Board does not support EEE!\n"); + return -EOPNOTSUPP; + } + + if (edata->advertised != tp->eee.advertised) { + netdev_warn(tp->dev, + "Direct manipulation of EEE advertisement is not supported\n"); + return -EINVAL; + } + + if (edata->tx_lpi_timer > TG3_CPMU_DBTMR1_LNKIDLE_MAX) { + netdev_warn(tp->dev, + "Maximal Tx Lpi timer supported is %#x(u)\n", + TG3_CPMU_DBTMR1_LNKIDLE_MAX); + return -EINVAL; + } + + tp->eee = *edata; + + tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED; + tg3_warn_mgmt_link_flap(tp); + + if (netif_running(tp->dev)) { + tg3_full_lock(tp, 0); + tg3_setup_eee(tp); + tg3_phy_reset(tp); + tg3_full_unlock(tp); + } + + return 0; +} + +static int tg3_get_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct tg3 *tp = netdev_priv(dev); + + if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) { + netdev_warn(tp->dev, + "Board does not support EEE!\n"); + return -EOPNOTSUPP; + } + + *edata = tp->eee; + return 0; +} + static const struct ethtool_ops tg3_ethtool_ops = { .get_settings = tg3_get_settings, .set_settings = tg3_set_settings, @@ -13690,6 +13817,8 @@ static const struct ethtool_ops tg3_ethtool_ops = { .get_channels = tg3_get_channels, .set_channels = tg3_set_channels, .get_ts_info = tg3_get_ts_info, + .get_eee = tg3_get_eee, + .set_eee = tg3_set_eee, }; static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev, @@ -15038,9 +15167,18 @@ static int tg3_phy_probe(struct tg3 *tp) (tg3_asic_rev(tp) == ASIC_REV_5717 && tg3_chip_rev_id(tp) != CHIPREV_ID_5717_A0) || (tg3_asic_rev(tp) == ASIC_REV_57765 && - tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) + tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) { tp->phy_flags |= TG3_PHYFLG_EEE_CAP; + tp->eee.supported = SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full; + tp->eee.advertised = ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full; + tp->eee.eee_enabled = 1; + tp->eee.tx_lpi_enabled = 1; + tp->eee.tx_lpi_timer = TG3_CPMU_DBTMR1_LNKIDLE_2047US; + } + tg3_phy_init_link_config(tp); if (!(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && @@ -17112,7 +17250,7 @@ static int tg3_init_one(struct pci_dev *pdev, { struct net_device *dev; struct tg3 *tp; - int i, err, pm_cap; + int i, err; u32 sndmbx, rcvmbx, intmbx; char str[40]; u64 dma_mask, persist_dma_mask; @@ -17134,25 +17272,10 @@ static int tg3_init_one(struct pci_dev *pdev, pci_set_master(pdev); - /* Find power-management capability. */ - pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); - if (pm_cap == 0) { - dev_err(&pdev->dev, - "Cannot find Power Management capability, aborting\n"); - err = -EIO; - goto err_out_free_res; - } - - err = pci_set_power_state(pdev, PCI_D0); - if (err) { - dev_err(&pdev->dev, "Transition to D0 failed, aborting\n"); - goto err_out_free_res; - } - dev = alloc_etherdev_mq(sizeof(*tp), TG3_IRQ_MAX_VECS); if (!dev) { err = -ENOMEM; - goto err_out_power_down; + goto err_out_free_res; } SET_NETDEV_DEV(dev, &pdev->dev); @@ -17160,7 +17283,7 @@ static int tg3_init_one(struct pci_dev *pdev, tp = netdev_priv(dev); tp->pdev = pdev; tp->dev = dev; - tp->pm_cap = pm_cap; + tp->pm_cap = pdev->pm_cap; tp->rx_mode = TG3_DEF_RX_MODE; tp->tx_mode = TG3_DEF_TX_MODE; tp->irq_sync = 1; @@ -17498,9 +17621,6 @@ err_out_iounmap: err_out_free_dev: free_netdev(dev); -err_out_power_down: - pci_set_power_state(pdev, PCI_D3hot); - err_out_free_res: pci_release_regions(pdev); @@ -17610,6 +17730,8 @@ static int tg3_resume(struct device *device) tg3_full_lock(tp, 0); + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); + tg3_flag_set(tp, INIT_COMPLETE); err = tg3_restart_hw(tp, !(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN)); @@ -17671,10 +17793,13 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev, tg3_full_unlock(tp); done: - if (state == pci_channel_io_perm_failure) + if (state == pci_channel_io_perm_failure) { + tg3_napi_enable(tp); + dev_close(netdev); err = PCI_ERS_RESULT_DISCONNECT; - else + } else { pci_disable_device(pdev); + } rtnl_unlock(); @@ -17720,6 +17845,10 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev) rc = PCI_ERS_RESULT_RECOVERED; done: + if (rc != PCI_ERS_RESULT_RECOVERED && netif_running(netdev)) { + tg3_napi_enable(tp); + dev_close(netdev); + } rtnl_unlock(); return rc; @@ -17744,6 +17873,7 @@ static void tg3_io_resume(struct pci_dev *pdev) goto done; tg3_full_lock(tp, 0); + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); tg3_flag_set(tp, INIT_COMPLETE); err = tg3_restart_hw(tp, true); if (err) { @@ -17781,15 +17911,4 @@ static struct pci_driver tg3_driver = { .driver.pm = &tg3_pm_ops, }; -static int __init tg3_init(void) -{ - return pci_register_driver(&tg3_driver); -} - -static void __exit tg3_cleanup(void) -{ - pci_unregister_driver(&tg3_driver); -} - -module_init(tg3_init); -module_exit(tg3_cleanup); +module_pci_driver(tg3_driver); diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index ff6e30eeae3..cd63d1189aa 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -1175,6 +1175,7 @@ #define TG3_CPMU_EEE_DBTMR1 0x000036b4 #define TG3_CPMU_DBTMR1_PCIEXIT_2047US 0x07ff0000 #define TG3_CPMU_DBTMR1_LNKIDLE_2047US 0x000007ff +#define TG3_CPMU_DBTMR1_LNKIDLE_MAX 0x0000ffff #define TG3_CPMU_EEE_DBTMR2 0x000036b8 #define TG3_CPMU_DBTMR2_APE_TX_2047US 0x07ff0000 #define TG3_CPMU_DBTMR2_TXIDXEQ_2047US 0x000007ff @@ -3372,6 +3373,7 @@ struct tg3 { unsigned int irq_cnt; struct ethtool_coalesce coal; + struct ethtool_eee eee; /* firmware info */ const char *fw_needed; diff --git a/drivers/net/ethernet/brocade/bna/bfa_defs.h b/drivers/net/ethernet/brocade/bna/bfa_defs.h index e423f82da49..b7d8127c198 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_defs.h +++ b/drivers/net/ethernet/brocade/bna/bfa_defs.h @@ -164,7 +164,8 @@ struct bfa_ioc_attr { u8 port_mode; /*!< enum bfa_mode */ u8 cap_bm; /*!< capability */ u8 port_mode_cfg; /*!< enum bfa_mode */ - u8 rsvd[4]; /*!< 64bit align */ + u8 def_fn; /*!< 1 if default fn */ + u8 rsvd[3]; /*!< 64bit align */ }; /* Adapter capability mask definition */ diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index f2b73ffa912..6f3cac060f2 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -2371,7 +2371,7 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr) memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr)); ioc_attr->state = bfa_ioc_get_state(ioc); - ioc_attr->port_id = ioc->port_id; + ioc_attr->port_id = bfa_ioc_portid(ioc); ioc_attr->port_mode = ioc->port_mode; ioc_attr->port_mode_cfg = ioc->port_mode_cfg; @@ -2381,8 +2381,9 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr) bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr); - ioc_attr->pci_attr.device_id = ioc->pcidev.device_id; - ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func; + ioc_attr->pci_attr.device_id = bfa_ioc_devid(ioc); + ioc_attr->pci_attr.pcifn = bfa_ioc_pcifn(ioc); + ioc_attr->def_fn = bfa_ioc_is_default(ioc); bfa_ioc_get_pci_chip_rev(ioc, ioc_attr->pci_attr.chip_rev); } diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.h b/drivers/net/ethernet/brocade/bna/bfa_ioc.h index 63a85e555df..f04e0aab25b 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.h +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.h @@ -222,6 +222,8 @@ struct bfa_ioc_hwif { #define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva) #define bfa_ioc_portid(__ioc) ((__ioc)->port_id) #define bfa_ioc_asic_gen(__ioc) ((__ioc)->asic_gen) +#define bfa_ioc_is_default(__ioc) \ + (bfa_ioc_pcifn(__ioc) == bfa_ioc_portid(__ioc)) #define bfa_ioc_fetch_stats(__ioc, __stats) \ (((__stats)->drv_stats) = (__ioc)->stats) #define bfa_ioc_clr_stats(__ioc) \ diff --git a/drivers/net/ethernet/brocade/bna/bna.h b/drivers/net/ethernet/brocade/bna/bna.h index 25dae757e9c..f1eafc409bb 100644 --- a/drivers/net/ethernet/brocade/bna/bna.h +++ b/drivers/net/ethernet/brocade/bna/bna.h @@ -455,6 +455,8 @@ void bna_bfi_rx_enet_stop_rsp(struct bna_rx *rx, void bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr); void bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr); +void bna_bfi_rxf_ucast_set_rsp(struct bna_rxf *rxf, + struct bfi_msgq_mhdr *msghdr); /* APIs for BNA */ void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna, diff --git a/drivers/net/ethernet/brocade/bna/bna_enet.c b/drivers/net/ethernet/brocade/bna/bna_enet.c index db14f69d63b..3ca77fad485 100644 --- a/drivers/net/ethernet/brocade/bna/bna_enet.c +++ b/drivers/net/ethernet/brocade/bna/bna_enet.c @@ -298,7 +298,6 @@ bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr) case BFI_ENET_I2H_RSS_ENABLE_RSP: case BFI_ENET_I2H_RX_PROMISCUOUS_RSP: case BFI_ENET_I2H_RX_DEFAULT_RSP: - case BFI_ENET_I2H_MAC_UCAST_SET_RSP: case BFI_ENET_I2H_MAC_UCAST_CLR_RSP: case BFI_ENET_I2H_MAC_UCAST_ADD_RSP: case BFI_ENET_I2H_MAC_UCAST_DEL_RSP: @@ -311,6 +310,12 @@ bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr) bna_bfi_rxf_cfg_rsp(&rx->rxf, msghdr); break; + case BFI_ENET_I2H_MAC_UCAST_SET_RSP: + bna_rx_from_rid(bna, msghdr->enet_id, rx); + if (rx) + bna_bfi_rxf_ucast_set_rsp(&rx->rxf, msghdr); + break; + case BFI_ENET_I2H_MAC_MCAST_ADD_RSP: bna_rx_from_rid(bna, msghdr->enet_id, rx); if (rx) diff --git a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c index ea6f4a03640..57cd1bff59f 100644 --- a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c +++ b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c @@ -711,6 +711,21 @@ bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr) } void +bna_bfi_rxf_ucast_set_rsp(struct bna_rxf *rxf, + struct bfi_msgq_mhdr *msghdr) +{ + struct bfi_enet_rsp *rsp = + (struct bfi_enet_rsp *)msghdr; + + if (rsp->error) { + /* Clear ucast from cache */ + rxf->ucast_active_set = 0; + } + + bfa_fsm_send_event(rxf, RXF_E_FW_RESP); +} + +void bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr) { diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 07f7ef05c3f..b78e69e0e52 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -2624,6 +2624,9 @@ bnad_stop(struct net_device *netdev) bnad_destroy_tx(bnad, 0); bnad_destroy_rx(bnad, 0); + /* These config flags are cleared in the hardware */ + bnad->cfg_flags &= ~(BNAD_CF_ALLMULTI | BNAD_CF_PROMISC); + /* Synchronize mailbox IRQ */ bnad_mbox_irq_sync(bnad); diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h index c1d0bc059bf..aefee77523f 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.h +++ b/drivers/net/ethernet/brocade/bna/bnad.h @@ -71,7 +71,7 @@ struct bnad_rx_ctrl { #define BNAD_NAME "bna" #define BNAD_NAME_LEN 64 -#define BNAD_VERSION "3.1.2.1" +#define BNAD_VERSION "3.2.21.1" #define BNAD_MAILBOX_MSIX_INDEX 0 #define BNAD_MAILBOX_MSIX_VECTORS 1 diff --git a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c index 94d957d203a..7d6aa8c87df 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c +++ b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c @@ -230,32 +230,12 @@ bnad_debugfs_open_drvinfo(struct inode *inode, struct file *file) static loff_t bnad_debugfs_lseek(struct file *file, loff_t offset, int orig) { - loff_t pos = file->f_pos; struct bnad_debug_info *debug = file->private_data; if (!debug) return -EINVAL; - switch (orig) { - case 0: - file->f_pos = offset; - break; - case 1: - file->f_pos += offset; - break; - case 2: - file->f_pos = debug->buffer_len + offset; - break; - default: - return -EINVAL; - } - - if (file->f_pos < 0 || file->f_pos > debug->buffer_len) { - file->f_pos = pos; - return -EINVAL; - } - - return file->f_pos; + return fixed_size_llseek(file, offset, orig, debug->buffer_len); } static ssize_t diff --git a/drivers/net/ethernet/brocade/bna/cna.h b/drivers/net/ethernet/brocade/bna/cna.h index 14ca9317c91..c37f706d999 100644 --- a/drivers/net/ethernet/brocade/bna/cna.h +++ b/drivers/net/ethernet/brocade/bna/cna.h @@ -37,8 +37,8 @@ extern char bfa_version[]; -#define CNA_FW_FILE_CT "ctfw-3.1.0.0.bin" -#define CNA_FW_FILE_CT2 "ct2fw-3.1.0.0.bin" +#define CNA_FW_FILE_CT "ctfw-3.2.1.0.bin" +#define CNA_FW_FILE_CT2 "ct2fw-3.2.1.0.bin" #define FC_SYMNAME_MAX 256 /*!< max name server symbolic name size */ #pragma pack(1) diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index 768285ec10f..8030cc0396f 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -23,7 +23,6 @@ if NET_CADENCE config ARM_AT91_ETHER tristate "AT91RM9200 Ethernet support" depends on GENERIC_HARDIRQS && HAS_DMA - select NET_CORE select MACB ---help--- If you wish to compile a kernel for the AT91RM9200 and enable diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c index cc9a185f0ab..3f1957158a3 100644 --- a/drivers/net/ethernet/cadence/at91_ether.c +++ b/drivers/net/ethernet/cadence/at91_ether.c @@ -435,7 +435,6 @@ static int at91ether_remove(struct platform_device *pdev) unregister_netdev(dev); clk_disable(lp->pclk); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index c89aa41dd44..e866608d7d9 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -32,7 +32,8 @@ #include "macb.h" -#define RX_BUFFER_SIZE 128 +#define MACB_RX_BUFFER_SIZE 128 +#define RX_BUFFER_MULTIPLE 64 /* bytes */ #define RX_RING_SIZE 512 /* must be power of 2 */ #define RX_RING_BYTES (sizeof(struct macb_dma_desc) * RX_RING_SIZE) @@ -92,7 +93,7 @@ static struct macb_dma_desc *macb_rx_desc(struct macb *bp, unsigned int index) static void *macb_rx_buffer(struct macb *bp, unsigned int index) { - return bp->rx_buffers + RX_BUFFER_SIZE * macb_rx_ring_wrap(index); + return bp->rx_buffers + bp->rx_buffer_size * macb_rx_ring_wrap(index); } void macb_set_hwaddr(struct macb *bp) @@ -528,6 +529,155 @@ static void macb_tx_interrupt(struct macb *bp) netif_wake_queue(bp->dev); } +static void gem_rx_refill(struct macb *bp) +{ + unsigned int entry; + struct sk_buff *skb; + struct macb_dma_desc *desc; + dma_addr_t paddr; + + while (CIRC_SPACE(bp->rx_prepared_head, bp->rx_tail, RX_RING_SIZE) > 0) { + u32 addr, ctrl; + + entry = macb_rx_ring_wrap(bp->rx_prepared_head); + desc = &bp->rx_ring[entry]; + + /* Make hw descriptor updates visible to CPU */ + rmb(); + + addr = desc->addr; + ctrl = desc->ctrl; + bp->rx_prepared_head++; + + if ((addr & MACB_BIT(RX_USED))) + continue; + + if (bp->rx_skbuff[entry] == NULL) { + /* allocate sk_buff for this free entry in ring */ + skb = netdev_alloc_skb(bp->dev, bp->rx_buffer_size); + if (unlikely(skb == NULL)) { + netdev_err(bp->dev, + "Unable to allocate sk_buff\n"); + break; + } + bp->rx_skbuff[entry] = skb; + + /* now fill corresponding descriptor entry */ + paddr = dma_map_single(&bp->pdev->dev, skb->data, + bp->rx_buffer_size, DMA_FROM_DEVICE); + + if (entry == RX_RING_SIZE - 1) + paddr |= MACB_BIT(RX_WRAP); + bp->rx_ring[entry].addr = paddr; + bp->rx_ring[entry].ctrl = 0; + + /* properly align Ethernet header */ + skb_reserve(skb, NET_IP_ALIGN); + } + } + + /* Make descriptor updates visible to hardware */ + wmb(); + + netdev_vdbg(bp->dev, "rx ring: prepared head %d, tail %d\n", + bp->rx_prepared_head, bp->rx_tail); +} + +/* Mark DMA descriptors from begin up to and not including end as unused */ +static void discard_partial_frame(struct macb *bp, unsigned int begin, + unsigned int end) +{ + unsigned int frag; + + for (frag = begin; frag != end; frag++) { + struct macb_dma_desc *desc = macb_rx_desc(bp, frag); + desc->addr &= ~MACB_BIT(RX_USED); + } + + /* Make descriptor updates visible to hardware */ + wmb(); + + /* + * When this happens, the hardware stats registers for + * whatever caused this is updated, so we don't have to record + * anything. + */ +} + +static int gem_rx(struct macb *bp, int budget) +{ + unsigned int len; + unsigned int entry; + struct sk_buff *skb; + struct macb_dma_desc *desc; + int count = 0; + + while (count < budget) { + u32 addr, ctrl; + + entry = macb_rx_ring_wrap(bp->rx_tail); + desc = &bp->rx_ring[entry]; + + /* Make hw descriptor updates visible to CPU */ + rmb(); + + addr = desc->addr; + ctrl = desc->ctrl; + + if (!(addr & MACB_BIT(RX_USED))) + break; + + desc->addr &= ~MACB_BIT(RX_USED); + bp->rx_tail++; + count++; + + if (!(ctrl & MACB_BIT(RX_SOF) && ctrl & MACB_BIT(RX_EOF))) { + netdev_err(bp->dev, + "not whole frame pointed by descriptor\n"); + bp->stats.rx_dropped++; + break; + } + skb = bp->rx_skbuff[entry]; + if (unlikely(!skb)) { + netdev_err(bp->dev, + "inconsistent Rx descriptor chain\n"); + bp->stats.rx_dropped++; + break; + } + /* now everything is ready for receiving packet */ + bp->rx_skbuff[entry] = NULL; + len = MACB_BFEXT(RX_FRMLEN, ctrl); + + netdev_vdbg(bp->dev, "gem_rx %u (len %u)\n", entry, len); + + skb_put(skb, len); + addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, addr)); + dma_unmap_single(&bp->pdev->dev, addr, + len, DMA_FROM_DEVICE); + + skb->protocol = eth_type_trans(skb, bp->dev); + skb_checksum_none_assert(skb); + + bp->stats.rx_packets++; + bp->stats.rx_bytes += skb->len; + +#if defined(DEBUG) && defined(VERBOSE_DEBUG) + netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", + skb->len, skb->csum); + print_hex_dump(KERN_DEBUG, " mac: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb->mac_header, 16, true); + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb->data, 32, true); +#endif + + netif_receive_skb(skb); + } + + gem_rx_refill(bp); + + return count; +} + static int macb_rx_frame(struct macb *bp, unsigned int first_frag, unsigned int last_frag) { @@ -575,7 +725,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, skb_put(skb, len); for (frag = first_frag; ; frag++) { - unsigned int frag_len = RX_BUFFER_SIZE; + unsigned int frag_len = bp->rx_buffer_size; if (offset + frag_len > len) { BUG_ON(frag != last_frag); @@ -583,7 +733,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, } skb_copy_to_linear_data_offset(skb, offset, macb_rx_buffer(bp, frag), frag_len); - offset += RX_BUFFER_SIZE; + offset += bp->rx_buffer_size; desc = macb_rx_desc(bp, frag); desc->addr &= ~MACB_BIT(RX_USED); @@ -606,27 +756,6 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, return 0; } -/* Mark DMA descriptors from begin up to and not including end as unused */ -static void discard_partial_frame(struct macb *bp, unsigned int begin, - unsigned int end) -{ - unsigned int frag; - - for (frag = begin; frag != end; frag++) { - struct macb_dma_desc *desc = macb_rx_desc(bp, frag); - desc->addr &= ~MACB_BIT(RX_USED); - } - - /* Make descriptor updates visible to hardware */ - wmb(); - - /* - * When this happens, the hardware stats registers for - * whatever caused this is updated, so we don't have to record - * anything. - */ -} - static int macb_rx(struct macb *bp, int budget) { int received = 0; @@ -687,7 +816,7 @@ static int macb_poll(struct napi_struct *napi, int budget) netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n", (unsigned long)status, budget); - work_done = macb_rx(bp, budget); + work_done = bp->macbgem_ops.mog_rx(bp, budget); if (work_done < budget) { napi_complete(napi); @@ -870,12 +999,71 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static void macb_init_rx_buffer_size(struct macb *bp, size_t size) +{ + if (!macb_is_gem(bp)) { + bp->rx_buffer_size = MACB_RX_BUFFER_SIZE; + } else { + bp->rx_buffer_size = size; + + if (bp->rx_buffer_size % RX_BUFFER_MULTIPLE) { + netdev_dbg(bp->dev, + "RX buffer must be multiple of %d bytes, expanding\n", + RX_BUFFER_MULTIPLE); + bp->rx_buffer_size = + roundup(bp->rx_buffer_size, RX_BUFFER_MULTIPLE); + } + } + + netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%Zu]\n", + bp->dev->mtu, bp->rx_buffer_size); +} + +static void gem_free_rx_buffers(struct macb *bp) +{ + struct sk_buff *skb; + struct macb_dma_desc *desc; + dma_addr_t addr; + int i; + + if (!bp->rx_skbuff) + return; + + for (i = 0; i < RX_RING_SIZE; i++) { + skb = bp->rx_skbuff[i]; + + if (skb == NULL) + continue; + + desc = &bp->rx_ring[i]; + addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, desc->addr)); + dma_unmap_single(&bp->pdev->dev, addr, skb->len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + skb = NULL; + } + + kfree(bp->rx_skbuff); + bp->rx_skbuff = NULL; +} + +static void macb_free_rx_buffers(struct macb *bp) +{ + if (bp->rx_buffers) { + dma_free_coherent(&bp->pdev->dev, + RX_RING_SIZE * bp->rx_buffer_size, + bp->rx_buffers, bp->rx_buffers_dma); + bp->rx_buffers = NULL; + } +} + static void macb_free_consistent(struct macb *bp) { if (bp->tx_skb) { kfree(bp->tx_skb); bp->tx_skb = NULL; } + bp->macbgem_ops.mog_free_rx_buffers(bp); if (bp->rx_ring) { dma_free_coherent(&bp->pdev->dev, RX_RING_BYTES, bp->rx_ring, bp->rx_ring_dma); @@ -886,12 +1074,37 @@ static void macb_free_consistent(struct macb *bp) bp->tx_ring, bp->tx_ring_dma); bp->tx_ring = NULL; } - if (bp->rx_buffers) { - dma_free_coherent(&bp->pdev->dev, - RX_RING_SIZE * RX_BUFFER_SIZE, - bp->rx_buffers, bp->rx_buffers_dma); - bp->rx_buffers = NULL; - } +} + +static int gem_alloc_rx_buffers(struct macb *bp) +{ + int size; + + size = RX_RING_SIZE * sizeof(struct sk_buff *); + bp->rx_skbuff = kzalloc(size, GFP_KERNEL); + if (!bp->rx_skbuff) + return -ENOMEM; + else + netdev_dbg(bp->dev, + "Allocated %d RX struct sk_buff entries at %p\n", + RX_RING_SIZE, bp->rx_skbuff); + return 0; +} + +static int macb_alloc_rx_buffers(struct macb *bp) +{ + int size; + + size = RX_RING_SIZE * bp->rx_buffer_size; + bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, + &bp->rx_buffers_dma, GFP_KERNEL); + if (!bp->rx_buffers) + return -ENOMEM; + else + netdev_dbg(bp->dev, + "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n", + size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers); + return 0; } static int macb_alloc_consistent(struct macb *bp) @@ -921,14 +1134,8 @@ static int macb_alloc_consistent(struct macb *bp) "Allocated TX ring of %d bytes at %08lx (mapped %p)\n", size, (unsigned long)bp->tx_ring_dma, bp->tx_ring); - size = RX_RING_SIZE * RX_BUFFER_SIZE; - bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, - &bp->rx_buffers_dma, GFP_KERNEL); - if (!bp->rx_buffers) + if (bp->macbgem_ops.mog_alloc_rx_buffers(bp)) goto out_err; - netdev_dbg(bp->dev, - "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n", - size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers); return 0; @@ -937,6 +1144,21 @@ out_err: return -ENOMEM; } +static void gem_init_rings(struct macb *bp) +{ + int i; + + for (i = 0; i < TX_RING_SIZE; i++) { + bp->tx_ring[i].addr = 0; + bp->tx_ring[i].ctrl = MACB_BIT(TX_USED); + } + bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP); + + bp->rx_tail = bp->rx_prepared_head = bp->tx_head = bp->tx_tail = 0; + + gem_rx_refill(bp); +} + static void macb_init_rings(struct macb *bp) { int i; @@ -946,7 +1168,7 @@ static void macb_init_rings(struct macb *bp) for (i = 0; i < RX_RING_SIZE; i++) { bp->rx_ring[i].addr = addr; bp->rx_ring[i].ctrl = 0; - addr += RX_BUFFER_SIZE; + addr += bp->rx_buffer_size; } bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP); @@ -1056,7 +1278,7 @@ static void macb_configure_dma(struct macb *bp) if (macb_is_gem(bp)) { dmacfg = gem_readl(bp, DMACFG) & ~GEM_BF(RXBS, -1L); - dmacfg |= GEM_BF(RXBS, RX_BUFFER_SIZE / 64); + dmacfg |= GEM_BF(RXBS, bp->rx_buffer_size / RX_BUFFER_MULTIPLE); dmacfg |= GEM_BF(FBLDO, 16); dmacfg |= GEM_BIT(TXPBMS) | GEM_BF(RXBMS, -1L); dmacfg &= ~GEM_BIT(ENDIA); @@ -1070,7 +1292,7 @@ static void macb_configure_dma(struct macb *bp) static void macb_configure_caps(struct macb *bp) { if (macb_is_gem(bp)) { - if (GEM_BF(IRQCOR, gem_readl(bp, DCFG1)) == 0) + if (GEM_BFEXT(IRQCOR, gem_readl(bp, DCFG1)) == 0) bp->caps |= MACB_CAPS_ISR_CLEAR_ON_WRITE; } } @@ -1233,6 +1455,7 @@ EXPORT_SYMBOL_GPL(macb_set_rx_mode); static int macb_open(struct net_device *dev) { struct macb *bp = netdev_priv(dev); + size_t bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + NET_IP_ALIGN; int err; netdev_dbg(bp->dev, "open\n"); @@ -1244,6 +1467,9 @@ static int macb_open(struct net_device *dev) if (!bp->phy_dev) return -EAGAIN; + /* RX buffers initialization */ + macb_init_rx_buffer_size(bp, bufsz); + err = macb_alloc_consistent(bp); if (err) { netdev_err(dev, "Unable to allocate DMA memory (error %d)\n", @@ -1253,7 +1479,7 @@ static int macb_open(struct net_device *dev) napi_enable(&bp->napi); - macb_init_rings(bp); + bp->macbgem_ops.mog_init_rings(bp); macb_init_hw(bp); /* schedule a link state check */ @@ -1572,6 +1798,19 @@ static int __init macb_probe(struct platform_device *pdev) dev->base_addr = regs->start; + /* setup appropriated routines according to adapter type */ + if (macb_is_gem(bp)) { + bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers; + bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; + bp->macbgem_ops.mog_init_rings = gem_init_rings; + bp->macbgem_ops.mog_rx = gem_rx; + } else { + bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; + bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; + bp->macbgem_ops.mog_init_rings = macb_init_rings; + bp->macbgem_ops.mog_rx = macb_rx; + } + /* Set MII management clock divider */ config = macb_mdc_clk_div(bp); config |= macb_dbw(bp); @@ -1649,7 +1888,6 @@ err_out_put_pclk: err_out_free_dev: free_netdev(dev); err_out: - platform_set_drvdata(pdev, NULL); return err; } @@ -1675,7 +1913,6 @@ static int __exit macb_remove(struct platform_device *pdev) clk_disable_unprepare(bp->pclk); clk_put(bp->pclk); free_netdev(dev); - platform_set_drvdata(pdev, NULL); } return 0; diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 548c0ecae86..f4076155bed 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -545,12 +545,24 @@ struct gem_stats { u32 rx_udp_checksum_errors; }; +struct macb; + +struct macb_or_gem_ops { + int (*mog_alloc_rx_buffers)(struct macb *bp); + void (*mog_free_rx_buffers)(struct macb *bp); + void (*mog_init_rings)(struct macb *bp); + int (*mog_rx)(struct macb *bp, int budget); +}; + struct macb { void __iomem *regs; unsigned int rx_tail; + unsigned int rx_prepared_head; struct macb_dma_desc *rx_ring; + struct sk_buff **rx_skbuff; void *rx_buffers; + size_t rx_buffer_size; unsigned int tx_head, tx_tail; struct macb_dma_desc *tx_ring; @@ -573,6 +585,8 @@ struct macb { dma_addr_t tx_ring_dma; dma_addr_t rx_buffers_dma; + struct macb_or_gem_ops macbgem_ops; + struct mii_bus *mii_bus; struct phy_device *phy_dev; unsigned int link; diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 4a1f2fa812a..7cb148c495c 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1790,7 +1790,6 @@ err_io: free_netdev(ndev); err_alloc: release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); return ret; } @@ -1813,7 +1812,6 @@ static int xgmac_remove(struct platform_device *pdev) free_irq(ndev->irq, ndev); free_irq(priv->pmt_irq, ndev); - platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); netif_napi_del(&priv->napi); diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index 9624cfe7df5..d7048db9863 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -1351,22 +1351,11 @@ static void remove_one(struct pci_dev *pdev) t1_sw_reset(pdev); } -static struct pci_driver driver = { +static struct pci_driver cxgb_pci_driver = { .name = DRV_NAME, .id_table = t1_pci_tbl, .probe = init_one, .remove = remove_one, }; -static int __init t1_init_module(void) -{ - return pci_register_driver(&driver); -} - -static void __exit t1_cleanup_module(void) -{ - pci_unregister_driver(&driver); -} - -module_init(t1_init_module); -module_exit(t1_cleanup_module); +module_pci_driver(cxgb_pci_driver); diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 71497e835f4..b650951791d 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3037,7 +3037,9 @@ static void t3_io_resume(struct pci_dev *pdev) CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n", t3_read_reg(adapter, A_PCIE_PEX_ERR)); + rtnl_lock(); t3_resume_ports(adapter); + rtnl_unlock(); } static const struct pci_error_handlers t3_err_handler = { diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 0c96e5fe99c..4058b856eb7 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -1246,6 +1246,7 @@ int cxgb3_offload_activate(struct adapter *adapter) struct tid_range stid_range, tid_range; struct mtutab mtutab; unsigned int l2t_capacity; + struct l2t_data *l2td; t = kzalloc(sizeof(*t), GFP_KERNEL); if (!t) @@ -1261,8 +1262,8 @@ int cxgb3_offload_activate(struct adapter *adapter) goto out_free; err = -ENOMEM; - RCU_INIT_POINTER(dev->l2opt, t3_init_l2t(l2t_capacity)); - if (!L2DATA(dev)) + l2td = t3_init_l2t(l2t_capacity); + if (!l2td) goto out_free; natids = min(tid_range.num / 2, MAX_ATIDS); @@ -1279,6 +1280,7 @@ int cxgb3_offload_activate(struct adapter *adapter) INIT_LIST_HEAD(&t->list_node); t->dev = dev; + RCU_INIT_POINTER(dev->l2opt, l2td); T3C_DATA(dev) = t; dev->recv = process_rx; dev->neigh_update = t3_l2t_update; @@ -1294,8 +1296,7 @@ int cxgb3_offload_activate(struct adapter *adapter) return 0; out_free_l2t: - t3_free_l2t(L2DATA(dev)); - RCU_INIT_POINTER(dev->l2opt, NULL); + t3_free_l2t(l2td); out_free: kfree(t); return err; diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index f12e6b85a65..687ec4a8bb4 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -455,6 +455,11 @@ static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q, q->pg_chunk.offset = 0; mapping = pci_map_page(adapter->pdev, q->pg_chunk.page, 0, q->alloc_size, PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(adapter->pdev, mapping))) { + __free_pages(q->pg_chunk.page, order); + q->pg_chunk.page = NULL; + return -EIO; + } q->pg_chunk.mapping = mapping; } sd->pg_chunk = q->pg_chunk; @@ -949,40 +954,75 @@ static inline unsigned int calc_tx_descs(const struct sk_buff *skb) return flits_to_desc(flits); } + +/* map_skb - map a packet main body and its page fragments + * @pdev: the PCI device + * @skb: the packet + * @addr: placeholder to save the mapped addresses + * + * map the main body of an sk_buff and its page fragments, if any. + */ +static int map_skb(struct pci_dev *pdev, const struct sk_buff *skb, + dma_addr_t *addr) +{ + const skb_frag_t *fp, *end; + const struct skb_shared_info *si; + + *addr = pci_map_single(pdev, skb->data, skb_headlen(skb), + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, *addr)) + goto out_err; + + si = skb_shinfo(skb); + end = &si->frags[si->nr_frags]; + + for (fp = si->frags; fp < end; fp++) { + *++addr = skb_frag_dma_map(&pdev->dev, fp, 0, skb_frag_size(fp), + DMA_TO_DEVICE); + if (pci_dma_mapping_error(pdev, *addr)) + goto unwind; + } + return 0; + +unwind: + while (fp-- > si->frags) + dma_unmap_page(&pdev->dev, *--addr, skb_frag_size(fp), + DMA_TO_DEVICE); + + pci_unmap_single(pdev, addr[-1], skb_headlen(skb), PCI_DMA_TODEVICE); +out_err: + return -ENOMEM; +} + /** - * make_sgl - populate a scatter/gather list for a packet + * write_sgl - populate a scatter/gather list for a packet * @skb: the packet * @sgp: the SGL to populate * @start: start address of skb main body data to include in the SGL * @len: length of skb main body data to include in the SGL - * @pdev: the PCI device + * @addr: the list of the mapped addresses * - * Generates a scatter/gather list for the buffers that make up a packet + * Copies the scatter/gather list for the buffers that make up a packet * and returns the SGL size in 8-byte words. The caller must size the SGL * appropriately. */ -static inline unsigned int make_sgl(const struct sk_buff *skb, +static inline unsigned int write_sgl(const struct sk_buff *skb, struct sg_ent *sgp, unsigned char *start, - unsigned int len, struct pci_dev *pdev) + unsigned int len, const dma_addr_t *addr) { - dma_addr_t mapping; - unsigned int i, j = 0, nfrags; + unsigned int i, j = 0, k = 0, nfrags; if (len) { - mapping = pci_map_single(pdev, start, len, PCI_DMA_TODEVICE); sgp->len[0] = cpu_to_be32(len); - sgp->addr[0] = cpu_to_be64(mapping); - j = 1; + sgp->addr[j++] = cpu_to_be64(addr[k++]); } nfrags = skb_shinfo(skb)->nr_frags; for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - mapping = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag), - DMA_TO_DEVICE); sgp->len[j] = cpu_to_be32(skb_frag_size(frag)); - sgp->addr[j] = cpu_to_be64(mapping); + sgp->addr[j] = cpu_to_be64(addr[k++]); j ^= 1; if (j == 0) ++sgp; @@ -1138,7 +1178,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, const struct port_info *pi, unsigned int pidx, unsigned int gen, struct sge_txq *q, unsigned int ndesc, - unsigned int compl) + unsigned int compl, const dma_addr_t *addr) { unsigned int flits, sgl_flits, cntrl, tso_info; struct sg_ent *sgp, sgl[MAX_SKB_FRAGS / 2 + 1]; @@ -1196,7 +1236,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, } sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = make_sgl(skb, sgp, skb->data, skb_headlen(skb), adap->pdev); + sgl_flits = write_sgl(skb, sgp, skb->data, skb_headlen(skb), addr); write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, gen, htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | compl), @@ -1227,6 +1267,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) struct netdev_queue *txq; struct sge_qset *qs; struct sge_txq *q; + dma_addr_t addr[MAX_SKB_FRAGS + 1]; /* * The chip min packet length is 9 octets but play safe and reject @@ -1255,6 +1296,11 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } + if (unlikely(map_skb(adap->pdev, skb, addr) < 0)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + q->in_use += ndesc; if (unlikely(credits - ndesc < q->stop_thres)) { t3_stop_tx_queue(txq, qs, q); @@ -1312,7 +1358,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) if (likely(!skb_shared(skb))) skb_orphan(skb); - write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl); + write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl, addr); check_ring_tx_db(adap, q); return NETDEV_TX_OK; } @@ -1537,10 +1583,9 @@ static void deferred_unmap_destructor(struct sk_buff *skb) dui = (struct deferred_unmap_info *)skb->head; p = dui->addr; - if (skb->tail - skb->transport_header) - pci_unmap_single(dui->pdev, *p++, - skb->tail - skb->transport_header, - PCI_DMA_TODEVICE); + if (skb_tail_pointer(skb) - skb_transport_header(skb)) + pci_unmap_single(dui->pdev, *p++, skb_tail_pointer(skb) - + skb_transport_header(skb), PCI_DMA_TODEVICE); si = skb_shinfo(skb); for (i = 0; i < si->nr_frags; i++) @@ -1578,7 +1623,8 @@ static void setup_deferred_unmapping(struct sk_buff *skb, struct pci_dev *pdev, */ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, struct sge_txq *q, unsigned int pidx, - unsigned int gen, unsigned int ndesc) + unsigned int gen, unsigned int ndesc, + const dma_addr_t *addr) { unsigned int sgl_flits, flits; struct work_request_hdr *from; @@ -1599,9 +1645,9 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, flits = skb_transport_offset(skb) / 8; sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb), - skb->tail - skb->transport_header, - adap->pdev); + sgl_flits = write_sgl(skb, sgp, skb_transport_header(skb), + skb_tail_pointer(skb) - + skb_transport_header(skb), addr); if (need_skb_unmap()) { setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits); skb->destructor = deferred_unmap_destructor; @@ -1627,7 +1673,7 @@ static inline unsigned int calc_tx_descs_ofld(const struct sk_buff *skb) flits = skb_transport_offset(skb) / 8; /* headers */ cnt = skb_shinfo(skb)->nr_frags; - if (skb->tail != skb->transport_header) + if (skb_tail_pointer(skb) != skb_transport_header(skb)) cnt++; return flits_to_desc(flits + sgl_len(cnt)); } @@ -1659,6 +1705,11 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); goto again; } + if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) { + spin_unlock(&q->lock); + return NET_XMIT_SUCCESS; + } + gen = q->gen; q->in_use += ndesc; pidx = q->pidx; @@ -1669,7 +1720,7 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); } spin_unlock(&q->lock); - write_ofld_wr(adap, skb, q, pidx, gen, ndesc); + write_ofld_wr(adap, skb, q, pidx, gen, ndesc, (dma_addr_t *)skb->head); check_ring_tx_db(adap, q); return NET_XMIT_SUCCESS; } @@ -1687,6 +1738,7 @@ static void restart_offloadq(unsigned long data) struct sge_txq *q = &qs->txq[TXQ_OFLD]; const struct port_info *pi = netdev_priv(qs->netdev); struct adapter *adap = pi->adapter; + unsigned int written = 0; spin_lock(&q->lock); again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); @@ -1706,10 +1758,14 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); break; } + if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) + break; + gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; + written += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; @@ -1717,7 +1773,8 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); __skb_unlink(skb, &q->sendq); spin_unlock(&q->lock); - write_ofld_wr(adap, skb, q, pidx, gen, ndesc); + write_ofld_wr(adap, skb, q, pidx, gen, ndesc, + (dma_addr_t *)skb->head); spin_lock(&q->lock); } spin_unlock(&q->lock); @@ -1727,8 +1784,9 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); set_bit(TXQ_LAST_PKT_DB, &q->flags); #endif wmb(); - t3_write_reg(adap, A_SG_KDOORBELL, - F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); + if (likely(written)) + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /** diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 681804b30a3..2aafb809e06 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -51,7 +51,7 @@ #include "t4_hw.h" #define FW_VERSION_MAJOR 1 -#define FW_VERSION_MINOR 1 +#define FW_VERSION_MINOR 4 #define FW_VERSION_MICRO 0 #define FW_VERSION_MAJOR_T5 0 diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 3cd397d6043..5a3256b083f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4842,8 +4842,17 @@ static int adap_init0(struct adapter *adap) * is excessively mismatched relative to the driver.) */ ret = t4_check_fw_version(adap); + + /* The error code -EFAULT is returned by t4_check_fw_version() if + * firmware on adapter < supported firmware. If firmware on adapter + * is too old (not supported by driver) and we're the MASTER_PF set + * adapter state to DEV_STATE_UNINIT to force firmware upgrade + * and reinitialization. + */ + if ((adap->flags & MASTER_PF) && ret == -EFAULT) + state = DEV_STATE_UNINIT; if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) { - if (ret == -EINVAL || ret > 0) { + if (ret == -EINVAL || ret == -EFAULT || ret > 0) { if (upgrade_fw(adap) >= 0) { /* * Note that the chip was reset as part of the @@ -4852,7 +4861,21 @@ static int adap_init0(struct adapter *adap) */ reset = 0; ret = t4_check_fw_version(adap); - } + } else + if (ret == -EFAULT) { + /* + * Firmware is old but still might + * work if we force reinitialization + * of the adapter. Ignoring FW upgrade + * failure. + */ + dev_warn(adap->pdev_dev, + "Ignoring firmware upgrade " + "failure, and forcing driver " + "to reinitialize the " + "adapter.\n"); + ret = 0; + } } if (ret < 0) return ret; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 2bfbb206b35..ac311f5f3eb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1294,7 +1294,7 @@ static inline unsigned int calc_tx_flits_ofld(const struct sk_buff *skb) flits = skb_transport_offset(skb) / 8U; /* headers */ cnt = skb_shinfo(skb)->nr_frags; - if (skb->tail != skb->transport_header) + if (skb_tail_pointer(skb) != skb_transport_header(skb)) cnt++; return flits + sgl_len(cnt); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index d02d4e8c441..4cbb2f9850b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -938,6 +938,15 @@ int t4_check_fw_version(struct adapter *adapter) memcpy(adapter->params.api_vers, api_vers, sizeof(adapter->params.api_vers)); + if (major < exp_major || (major == exp_major && minor < exp_minor) || + (major == exp_major && minor == exp_minor && micro < exp_micro)) { + dev_err(adapter->pdev_dev, + "Card has firmware version %u.%u.%u, minimum " + "supported firmware is %u.%u.%u.\n", major, minor, + micro, exp_major, exp_minor, exp_micro); + return -EFAULT; + } + if (major != exp_major) { /* major mismatch - fail */ dev_err(adapter->pdev_dev, "card FW has major version %u, driver wants %u\n", @@ -3773,7 +3782,6 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf) p->lport = j; p->rss_size = rss_size; memcpy(adap->port[i]->dev_addr, addr, ETH_ALEN); - adap->port[i]->dev_id = j; ret = ntohl(c.u.info.lstatus_to_modtype); p->mdio_addr = (ret & FW_PORT_CMD_MDIOCAP) ? diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig index 8388e36cf08..7403dff8f14 100644 --- a/drivers/net/ethernet/cirrus/Kconfig +++ b/drivers/net/ethernet/cirrus/Kconfig @@ -44,7 +44,6 @@ config CS89x0_PLATFORM config EP93XX_ETH tristate "EP93xx Ethernet support" depends on ARM && ARCH_EP93XX - select NET_CORE select MII help This is a driver for the ethernet hardware included in EP93xx CPUs. diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index 67b0388b6e6..e3d4ec836f8 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -783,7 +783,6 @@ static int ep93xx_eth_remove(struct platform_device *pdev) dev = platform_get_drvdata(pdev); if (dev == NULL) return 0; - platform_set_drvdata(pdev, NULL); ep = netdev_priv(dev); diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 635f55992d7..992ec2ee64d 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1761,6 +1761,7 @@ static void enic_change_mtu_work(struct work_struct *work) enic_synchronize_irqs(enic); err = vnic_rq_disable(&enic->rq[0]); if (err) { + rtnl_unlock(); netdev_err(netdev, "Unable to disable RQ.\n"); return; } @@ -1773,6 +1774,7 @@ static void enic_change_mtu_work(struct work_struct *work) vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf); /* Need at least one buffer on ring to get going */ if (vnic_rq_desc_used(&enic->rq[0]) == 0) { + rtnl_unlock(); netdev_err(netdev, "Unable to alloc receive buffers.\n"); return; } diff --git a/drivers/net/ethernet/davicom/Kconfig b/drivers/net/ethernet/davicom/Kconfig index 9745fe5e803..316c5e5a92a 100644 --- a/drivers/net/ethernet/davicom/Kconfig +++ b/drivers/net/ethernet/davicom/Kconfig @@ -6,7 +6,6 @@ config DM9000 tristate "DM9000 support" depends on ARM || BLACKFIN || MIPS || COLDFIRE select CRC32 - select NET_CORE select MII ---help--- Support for DM9000 chipset. diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 9105465b2a1..a13b312b50f 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -29,6 +29,8 @@ #include <linux/spinlock.h> #include <linux/crc32.h> #include <linux/mii.h> +#include <linux/of.h> +#include <linux/of_net.h> #include <linux/ethtool.h> #include <linux/dm9000.h> #include <linux/delay.h> @@ -827,7 +829,7 @@ dm9000_hash_table_unlocked(struct net_device *dev) struct netdev_hw_addr *ha; int i, oft; u32 hash_val; - u16 hash_table[4]; + u16 hash_table[4] = { 0, 0, 0, 0x8000 }; /* broadcast address */ u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; dm9000_dbg(db, 1, "entering %s\n", __func__); @@ -835,13 +837,6 @@ dm9000_hash_table_unlocked(struct net_device *dev) for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++) iow(db, oft, dev->dev_addr[i]); - /* Clear Hash Table */ - for (i = 0; i < 4; i++) - hash_table[i] = 0x0; - - /* broadcast address */ - hash_table[3] = 0x8000; - if (dev->flags & IFF_PROMISC) rcr |= RCR_PRMSC; @@ -1358,6 +1353,31 @@ static const struct net_device_ops dm9000_netdev_ops = { #endif }; +static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev) +{ + struct dm9000_plat_data *pdata; + struct device_node *np = dev->of_node; + const void *mac_addr; + + if (!IS_ENABLED(CONFIG_OF) || !np) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (of_find_property(np, "davicom,ext-phy", NULL)) + pdata->flags |= DM9000_PLATF_EXT_PHY; + if (of_find_property(np, "davicom,no-eeprom", NULL)) + pdata->flags |= DM9000_PLATF_NO_EEPROM; + + mac_addr = of_get_mac_address(np); + if (mac_addr) + memcpy(pdata->dev_addr, mac_addr, sizeof(pdata->dev_addr)); + + return pdata; +} + /* * Search DM9000 board, allocate space and register it */ @@ -1373,6 +1393,12 @@ dm9000_probe(struct platform_device *pdev) int i; u32 id_val; + if (!pdata) { + pdata = dm9000_parse_dt(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + /* Init network device */ ndev = alloc_etherdev(sizeof(struct board_info)); if (!ndev) @@ -1673,8 +1699,6 @@ dm9000_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); dm9000_release_board(pdev, netdev_priv(ndev)); free_netdev(ndev); /* free device structure */ @@ -1683,11 +1707,20 @@ dm9000_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dm9000_of_matches[] = { + { .compatible = "davicom,dm9000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dm9000_of_matches); +#endif + static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", .owner = THIS_MODULE, .pm = &dm9000_drv_pm_ops, + .of_match_table = of_match_ptr(dm9000_of_matches), }, .probe = dm9000_probe, .remove = dm9000_drv_remove, diff --git a/drivers/net/ethernet/dec/tulip/Kconfig b/drivers/net/ethernet/dec/tulip/Kconfig index 1df33c799c0..eb9ba6e97d0 100644 --- a/drivers/net/ethernet/dec/tulip/Kconfig +++ b/drivers/net/ethernet/dec/tulip/Kconfig @@ -126,7 +126,6 @@ config WINBOND_840 tristate "Winbond W89c840 Ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver is for the Winbond W89c840 chip. It also works with diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 1e9443d9fb5..c94152f1c6b 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1410,12 +1410,6 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return i; } - /* The chip will fail to enter a low-power state later unless - * first explicitly commanded into D0 */ - if (pci_set_power_state(pdev, PCI_D0)) { - pr_notice("Failed to set power state to D0\n"); - } - irq = pdev->irq; /* alloc_etherdev ensures aligned and zeroed private structures */ diff --git a/drivers/net/ethernet/dec/tulip/xircom_cb.c b/drivers/net/ethernet/dec/tulip/xircom_cb.c index cdbcd164314..9b84cb04fe5 100644 --- a/drivers/net/ethernet/dec/tulip/xircom_cb.c +++ b/drivers/net/ethernet/dec/tulip/xircom_cb.c @@ -1171,16 +1171,4 @@ investigate_write_descriptor(struct net_device *dev, } } -static int __init xircom_init(void) -{ - return pci_register_driver(&xircom_ops); -} - -static void __exit xircom_exit(void) -{ - pci_unregister_driver(&xircom_ops); -} - -module_init(xircom_init) -module_exit(xircom_exit) - +module_pci_driver(xircom_ops); diff --git a/drivers/net/ethernet/dlink/Kconfig b/drivers/net/ethernet/dlink/Kconfig index ee26ce78e27..c543ac11ce0 100644 --- a/drivers/net/ethernet/dlink/Kconfig +++ b/drivers/net/ethernet/dlink/Kconfig @@ -36,7 +36,6 @@ config SUNDANCE tristate "Sundance Alta support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver is for the Sundance "Alta" chip. diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 0a510684e46..c827b1b6b1c 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -333,6 +333,9 @@ enum vf_state { #define BE_VF_UC_PMAC_COUNT 2 #define BE_FLAGS_QNQ_ASYNC_EVT_RCVD (1 << 11) +/* Ethtool set_dump flags */ +#define LANCER_INITIATE_FW_DUMP 0x1 + struct phy_info { u8 transceiver; u8 autoneg; @@ -398,6 +401,7 @@ struct be_adapter { u32 cmd_privileges; /* Ethtool knobs and info */ char fw_ver[FW_VER_LEN]; + char fw_on_flash[FW_VER_LEN]; int if_handle; /* Used to configure filtering */ u32 *pmac_id; /* MAC addr handle used by BE card */ u32 beacon_state; /* for set_phys_id */ diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 1db2df61b8a..6e6e0a117ee 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -3255,6 +3255,72 @@ err: return status; } +static int lancer_wait_idle(struct be_adapter *adapter) +{ +#define SLIPORT_IDLE_TIMEOUT 30 + u32 reg_val; + int status = 0, i; + + for (i = 0; i < SLIPORT_IDLE_TIMEOUT; i++) { + reg_val = ioread32(adapter->db + PHYSDEV_CONTROL_OFFSET); + if ((reg_val & PHYSDEV_CONTROL_INP_MASK) == 0) + break; + + ssleep(1); + } + + if (i == SLIPORT_IDLE_TIMEOUT) + status = -1; + + return status; +} + +int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask) +{ + int status = 0; + + status = lancer_wait_idle(adapter); + if (status) + return status; + + iowrite32(mask, adapter->db + PHYSDEV_CONTROL_OFFSET); + + return status; +} + +/* Routine to check whether dump image is present or not */ +bool dump_present(struct be_adapter *adapter) +{ + u32 sliport_status = 0; + + sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); + return !!(sliport_status & SLIPORT_STATUS_DIP_MASK); +} + +int lancer_initiate_dump(struct be_adapter *adapter) +{ + int status; + + /* give firmware reset and diagnostic dump */ + status = lancer_physdev_ctrl(adapter, PHYSDEV_CONTROL_FW_RESET_MASK | + PHYSDEV_CONTROL_DD_MASK); + if (status < 0) { + dev_err(&adapter->pdev->dev, "Firmware reset failed\n"); + return status; + } + + status = lancer_wait_idle(adapter); + if (status) + return status; + + if (!dump_present(adapter)) { + dev_err(&adapter->pdev->dev, "Dump image not present\n"); + return -1; + } + + return 0; +} + /* Uses sync mcc */ int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain) { diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 025bdb0d176..5228d88c5a0 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -1937,6 +1937,9 @@ extern int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter, struct be_dma_mem *cmd, struct be_fat_conf_params *cfgs); extern int lancer_wait_ready(struct be_adapter *adapter); +extern int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask); +extern int lancer_initiate_dump(struct be_adapter *adapter); +extern bool dump_present(struct be_adapter *adapter); extern int lancer_test_and_set_rdy_state(struct be_adapter *adapter); extern int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name); extern int be_cmd_get_func_config(struct be_adapter *adapter); diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 3d4461adb3b..4f8c941217c 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -177,19 +177,15 @@ static void be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct be_adapter *adapter = netdev_priv(netdev); - char fw_on_flash[FW_VER_LEN]; - - memset(fw_on_flash, 0 , sizeof(fw_on_flash)); - be_cmd_get_fw_ver(adapter, adapter->fw_ver, fw_on_flash); strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, DRV_VER, sizeof(drvinfo->version)); - if (!memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN)) + if (!memcmp(adapter->fw_ver, adapter->fw_on_flash, FW_VER_LEN)) strlcpy(drvinfo->fw_version, adapter->fw_ver, sizeof(drvinfo->fw_version)); else snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%s [%s]", adapter->fw_ver, fw_on_flash); + "%s [%s]", adapter->fw_ver, adapter->fw_on_flash); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); @@ -673,6 +669,34 @@ be_set_phys_id(struct net_device *netdev, return 0; } +static int be_set_dump(struct net_device *netdev, struct ethtool_dump *dump) +{ + struct be_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->pdev->dev; + int status; + + if (!lancer_chip(adapter)) { + dev_err(dev, "FW dump not supported\n"); + return -EOPNOTSUPP; + } + + if (dump_present(adapter)) { + dev_err(dev, "Previous dump not cleared, not forcing dump\n"); + return 0; + } + + switch (dump->flag) { + case LANCER_INITIATE_FW_DUMP: + status = lancer_initiate_dump(adapter); + if (!status) + dev_info(dev, "F/w dump initiated successfully\n"); + break; + default: + dev_err(dev, "Invalid dump level: 0x%x\n", dump->flag); + return -EINVAL; + } + return status; +} static void be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) @@ -1110,6 +1134,7 @@ const struct ethtool_ops be_ethtool_ops = { .set_pauseparam = be_set_pauseparam, .get_strings = be_get_stat_strings, .set_phys_id = be_set_phys_id, + .set_dump = be_set_dump, .get_msglevel = be_get_msg_level, .set_msglevel = be_set_msg_level, .get_sset_count = be_get_sset_count, diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index 8780183c6d1..3e216212160 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -53,10 +53,12 @@ #define PHYSDEV_CONTROL_OFFSET 0x414 #define SLIPORT_STATUS_ERR_MASK 0x80000000 +#define SLIPORT_STATUS_DIP_MASK 0x02000000 #define SLIPORT_STATUS_RN_MASK 0x01000000 #define SLIPORT_STATUS_RDY_MASK 0x00800000 #define SLI_PORT_CONTROL_IP_MASK 0x08000000 #define PHYSDEV_CONTROL_FW_RESET_MASK 0x00000002 +#define PHYSDEV_CONTROL_DD_MASK 0x00000004 #define PHYSDEV_CONTROL_INP_MASK 0x40000000 #define SLIPORT_ERROR_NO_RESOURCE1 0x2 diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a0b4be51f0d..2df48bb0f1c 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -834,32 +834,39 @@ static int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb) return vlan_tx_tag_present(skb) || adapter->pvid || adapter->qnq_vid; } -static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb) +static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, + struct sk_buff *skb) { - return BE3_chip(adapter) && - be_ipv6_exthdr_check(skb); + return BE3_chip(adapter) && be_ipv6_exthdr_check(skb); } -static netdev_tx_t be_xmit(struct sk_buff *skb, - struct net_device *netdev) +static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, + struct sk_buff *skb, + bool *skip_hw_vlan) { - struct be_adapter *adapter = netdev_priv(netdev); - struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; - struct be_queue_info *txq = &txo->q; - struct iphdr *ip = NULL; - u32 wrb_cnt = 0, copied = 0; - u32 start = txq->head, eth_hdr_len; - bool dummy_wrb, stopped = false; - bool skip_hw_vlan = false; struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; + unsigned int eth_hdr_len; + struct iphdr *ip; - eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? - VLAN_ETH_HLEN : ETH_HLEN; + /* Lancer ASIC has a bug wherein packets that are 32 bytes or less + * may cause a transmit stall on that port. So the work-around is to + * pad such packets to a 36-byte length. + */ + if (unlikely(lancer_chip(adapter) && skb->len <= 32)) { + if (skb_padto(skb, 36)) + goto tx_drop; + skb->len = 36; + } /* For padded packets, BE HW modifies tot_len field in IP header * incorrecly when VLAN tag is inserted by HW. + * For padded packets, Lancer computes incorrect checksum. */ - if (skb->len <= 60 && vlan_tx_tag_present(skb) && is_ipv4_pkt(skb)) { + eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? + VLAN_ETH_HLEN : ETH_HLEN; + if (skb->len <= 60 && + (lancer_chip(adapter) || vlan_tx_tag_present(skb)) && + is_ipv4_pkt(skb)) { ip = (struct iphdr *)ip_hdr(skb); pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); } @@ -869,15 +876,15 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, */ if ((adapter->function_mode & UMC_ENABLED) && veh->h_vlan_proto == htons(ETH_P_8021Q)) - skip_hw_vlan = true; + *skip_hw_vlan = true; /* HW has a bug wherein it will calculate CSUM for VLAN * pkts even though it is disabled. * Manually insert VLAN in pkt. */ if (skb->ip_summed != CHECKSUM_PARTIAL && - vlan_tx_tag_present(skb)) { - skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan); + vlan_tx_tag_present(skb)) { + skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) goto tx_drop; } @@ -887,8 +894,8 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, * skip HW tagging is not enabled by FW. */ if (unlikely(be_ipv6_tx_stall_chk(adapter, skb) && - (adapter->pvid || adapter->qnq_vid) && - !qnq_async_evt_rcvd(adapter))) + (adapter->pvid || adapter->qnq_vid) && + !qnq_async_evt_rcvd(adapter))) goto tx_drop; /* Manual VLAN tag insertion to prevent: @@ -899,11 +906,31 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, */ if (be_ipv6_tx_stall_chk(adapter, skb) && be_vlan_tag_tx_chk(adapter, skb)) { - skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan); + skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) goto tx_drop; } + return skb; +tx_drop: + dev_kfree_skb_any(skb); + return NULL; +} + +static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct be_adapter *adapter = netdev_priv(netdev); + struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; + struct be_queue_info *txq = &txo->q; + bool dummy_wrb, stopped = false; + u32 wrb_cnt = 0, copied = 0; + bool skip_hw_vlan = false; + u32 start = txq->head; + + skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan); + if (!skb) + return NETDEV_TX_OK; + wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb, @@ -933,7 +960,6 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, txq->head = start; dev_kfree_skb_any(skb); } -tx_drop: return NETDEV_TX_OK; } @@ -1236,30 +1262,6 @@ static int be_set_vf_tx_rate(struct net_device *netdev, return status; } -static int be_find_vfs(struct be_adapter *adapter, int vf_state) -{ - struct pci_dev *dev, *pdev = adapter->pdev; - int vfs = 0, assigned_vfs = 0, pos; - u16 offset, stride; - - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); - if (!pos) - return 0; - pci_read_config_word(pdev, pos + PCI_SRIOV_VF_OFFSET, &offset); - pci_read_config_word(pdev, pos + PCI_SRIOV_VF_STRIDE, &stride); - - dev = pci_get_device(pdev->vendor, PCI_ANY_ID, NULL); - while (dev) { - if (dev->is_virtfn && pci_physfn(dev) == pdev) { - vfs++; - if (dev->dev_flags & PCI_DEV_FLAGS_ASSIGNED) - assigned_vfs++; - } - dev = pci_get_device(pdev->vendor, PCI_ANY_ID, dev); - } - return (vf_state == ASSIGNED) ? assigned_vfs : vfs; -} - static void be_eqd_update(struct be_adapter *adapter, struct be_eq_obj *eqo) { struct be_rx_stats *stats = rx_stats(&adapter->rx_obj[eqo->idx]); @@ -2771,7 +2773,7 @@ static void be_vf_clear(struct be_adapter *adapter) struct be_vf_cfg *vf_cfg; u32 vf; - if (be_find_vfs(adapter, ASSIGNED)) { + if (pci_vfs_assigned(adapter->pdev)) { dev_warn(&adapter->pdev->dev, "VFs are assigned to VMs: not disabling VFs\n"); goto done; @@ -2873,7 +2875,7 @@ static int be_vf_setup(struct be_adapter *adapter) int status, old_vfs, vf; struct device *dev = &adapter->pdev->dev; - old_vfs = be_find_vfs(adapter, ENABLED); + old_vfs = pci_num_vf(adapter->pdev); if (old_vfs) { dev_info(dev, "%d VFs are already enabled\n", old_vfs); if (old_vfs != num_vfs) @@ -3184,7 +3186,7 @@ static int be_setup(struct be_adapter *adapter) if (status) goto err; - be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL); + be_cmd_get_fw_ver(adapter, adapter->fw_ver, adapter->fw_on_flash); if (adapter->vlans_added) be_vid_config(adapter); @@ -3530,40 +3532,6 @@ static int be_flash_skyhawk(struct be_adapter *adapter, return 0; } -static int lancer_wait_idle(struct be_adapter *adapter) -{ -#define SLIPORT_IDLE_TIMEOUT 30 - u32 reg_val; - int status = 0, i; - - for (i = 0; i < SLIPORT_IDLE_TIMEOUT; i++) { - reg_val = ioread32(adapter->db + PHYSDEV_CONTROL_OFFSET); - if ((reg_val & PHYSDEV_CONTROL_INP_MASK) == 0) - break; - - ssleep(1); - } - - if (i == SLIPORT_IDLE_TIMEOUT) - status = -1; - - return status; -} - -static int lancer_fw_reset(struct be_adapter *adapter) -{ - int status = 0; - - status = lancer_wait_idle(adapter); - if (status) - return status; - - iowrite32(PHYSDEV_CONTROL_FW_RESET_MASK, adapter->db + - PHYSDEV_CONTROL_OFFSET); - - return status; -} - static int lancer_fw_download(struct be_adapter *adapter, const struct firmware *fw) { @@ -3641,7 +3609,8 @@ static int lancer_fw_download(struct be_adapter *adapter, } if (change_status == LANCER_FW_RESET_NEEDED) { - status = lancer_fw_reset(adapter); + status = lancer_physdev_ctrl(adapter, + PHYSDEV_CONTROL_FW_RESET_MASK); if (status) { dev_err(&adapter->pdev->dev, "Adapter busy for FW reset.\n" @@ -3776,6 +3745,10 @@ int be_load_fw(struct be_adapter *adapter, u8 *fw_file) else status = be_fw_download(adapter, fw); + if (!status) + be_cmd_get_fw_ver(adapter, adapter->fw_ver, + adapter->fw_on_flash); + fw_exit: release_firmware(fw); return status; @@ -4203,9 +4176,10 @@ reschedule: schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000)); } +/* If any VFs are already enabled don't FLR the PF */ static bool be_reset_required(struct be_adapter *adapter) { - return be_find_vfs(adapter, ENABLED) > 0 ? false : true; + return pci_num_vf(adapter->pdev) ? false : true; } static char *mc_name(struct be_adapter *adapter) @@ -4390,7 +4364,7 @@ static int be_resume(struct pci_dev *pdev) if (status) return status; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* tell fw we're ready to fire cmds */ @@ -4486,7 +4460,7 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; pci_set_master(pdev); - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* Check if card is ok and fw is ready */ diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 5722bc61fa5..cf579fb39bc 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -1147,8 +1147,6 @@ static int ethoc_remove(struct platform_device *pdev) struct net_device *netdev = platform_get_drvdata(pdev); struct ethoc *priv = netdev_priv(netdev); - platform_set_drvdata(pdev, NULL); - if (netdev) { netif_napi_del(&priv->napi); phy_disconnect(priv->phy); diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig index b8974b9e3b4..5918c689169 100644 --- a/drivers/net/ethernet/faraday/Kconfig +++ b/drivers/net/ethernet/faraday/Kconfig @@ -21,7 +21,6 @@ if NET_VENDOR_FARADAY config FTMAC100 tristate "Faraday FTMAC100 10/100 Ethernet support" depends on ARM - select NET_CORE select MII ---help--- This driver supports the FTMAC100 10/100 Ethernet controller diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 21b85fb7d05..934e1ae279f 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1311,7 +1311,6 @@ err_ioremap: release_resource(priv->res); err_req_mem: netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); err_alloc_etherdev: return err; @@ -1335,7 +1334,6 @@ static int __exit ftgmac100_remove(struct platform_device *pdev) release_resource(priv->res); netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); return 0; } diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index a6eda8d8313..4658f4cc196 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -1149,7 +1149,6 @@ err_ioremap: release_resource(priv->res); err_req_mem: netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); err_alloc_etherdev: return err; @@ -1169,7 +1168,6 @@ static int __exit ftmac100_remove(struct platform_device *pdev) release_resource(priv->res); netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); return 0; } diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 9ce5b7185fd..2b0a0ea4f8e 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -60,6 +60,61 @@ #define BM_MIIGSK_CFGR_RMII 0x01 #define BM_MIIGSK_CFGR_FRCONT_10M 0x40 +#define RMON_T_DROP 0x200 /* Count of frames not cntd correctly */ +#define RMON_T_PACKETS 0x204 /* RMON TX packet count */ +#define RMON_T_BC_PKT 0x208 /* RMON TX broadcast pkts */ +#define RMON_T_MC_PKT 0x20C /* RMON TX multicast pkts */ +#define RMON_T_CRC_ALIGN 0x210 /* RMON TX pkts with CRC align err */ +#define RMON_T_UNDERSIZE 0x214 /* RMON TX pkts < 64 bytes, good CRC */ +#define RMON_T_OVERSIZE 0x218 /* RMON TX pkts > MAX_FL bytes good CRC */ +#define RMON_T_FRAG 0x21C /* RMON TX pkts < 64 bytes, bad CRC */ +#define RMON_T_JAB 0x220 /* RMON TX pkts > MAX_FL bytes, bad CRC */ +#define RMON_T_COL 0x224 /* RMON TX collision count */ +#define RMON_T_P64 0x228 /* RMON TX 64 byte pkts */ +#define RMON_T_P65TO127 0x22C /* RMON TX 65 to 127 byte pkts */ +#define RMON_T_P128TO255 0x230 /* RMON TX 128 to 255 byte pkts */ +#define RMON_T_P256TO511 0x234 /* RMON TX 256 to 511 byte pkts */ +#define RMON_T_P512TO1023 0x238 /* RMON TX 512 to 1023 byte pkts */ +#define RMON_T_P1024TO2047 0x23C /* RMON TX 1024 to 2047 byte pkts */ +#define RMON_T_P_GTE2048 0x240 /* RMON TX pkts > 2048 bytes */ +#define RMON_T_OCTETS 0x244 /* RMON TX octets */ +#define IEEE_T_DROP 0x248 /* Count of frames not counted crtly */ +#define IEEE_T_FRAME_OK 0x24C /* Frames tx'd OK */ +#define IEEE_T_1COL 0x250 /* Frames tx'd with single collision */ +#define IEEE_T_MCOL 0x254 /* Frames tx'd with multiple collision */ +#define IEEE_T_DEF 0x258 /* Frames tx'd after deferral delay */ +#define IEEE_T_LCOL 0x25C /* Frames tx'd with late collision */ +#define IEEE_T_EXCOL 0x260 /* Frames tx'd with excesv collisions */ +#define IEEE_T_MACERR 0x264 /* Frames tx'd with TX FIFO underrun */ +#define IEEE_T_CSERR 0x268 /* Frames tx'd with carrier sense err */ +#define IEEE_T_SQE 0x26C /* Frames tx'd with SQE err */ +#define IEEE_T_FDXFC 0x270 /* Flow control pause frames tx'd */ +#define IEEE_T_OCTETS_OK 0x274 /* Octet count for frames tx'd w/o err */ +#define RMON_R_PACKETS 0x284 /* RMON RX packet count */ +#define RMON_R_BC_PKT 0x288 /* RMON RX broadcast pkts */ +#define RMON_R_MC_PKT 0x28C /* RMON RX multicast pkts */ +#define RMON_R_CRC_ALIGN 0x290 /* RMON RX pkts with CRC alignment err */ +#define RMON_R_UNDERSIZE 0x294 /* RMON RX pkts < 64 bytes, good CRC */ +#define RMON_R_OVERSIZE 0x298 /* RMON RX pkts > MAX_FL bytes good CRC */ +#define RMON_R_FRAG 0x29C /* RMON RX pkts < 64 bytes, bad CRC */ +#define RMON_R_JAB 0x2A0 /* RMON RX pkts > MAX_FL bytes, bad CRC */ +#define RMON_R_RESVD_O 0x2A4 /* Reserved */ +#define RMON_R_P64 0x2A8 /* RMON RX 64 byte pkts */ +#define RMON_R_P65TO127 0x2AC /* RMON RX 65 to 127 byte pkts */ +#define RMON_R_P128TO255 0x2B0 /* RMON RX 128 to 255 byte pkts */ +#define RMON_R_P256TO511 0x2B4 /* RMON RX 256 to 511 byte pkts */ +#define RMON_R_P512TO1023 0x2B8 /* RMON RX 512 to 1023 byte pkts */ +#define RMON_R_P1024TO2047 0x2BC /* RMON RX 1024 to 2047 byte pkts */ +#define RMON_R_P_GTE2048 0x2C0 /* RMON RX pkts > 2048 bytes */ +#define RMON_R_OCTETS 0x2C4 /* RMON RX octets */ +#define IEEE_R_DROP 0x2C8 /* Count frames not counted correctly */ +#define IEEE_R_FRAME_OK 0x2CC /* Frames rx'd OK */ +#define IEEE_R_CRC 0x2D0 /* Frames rx'd with CRC err */ +#define IEEE_R_ALIGN 0x2D4 /* Frames rx'd with alignment err */ +#define IEEE_R_MACERR 0x2D8 /* Receive FIFO overflow count */ +#define IEEE_R_FDXFC 0x2DC /* Flow control pause frames rx'd */ +#define IEEE_R_OCTETS_OK 0x2E0 /* Octet cnt for frames rx'd w/o err */ + #else #define FEC_ECNTRL 0x000 /* Ethernet control reg */ @@ -148,6 +203,9 @@ struct bufdesc_ex { #define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ +/* Enhanced buffer descriptor control/status used by Ethernet receive */ +#define BD_ENET_RX_VLAN 0x00000004 + /* Buffer descriptor control/status used by Ethernet transmit. */ #define BD_ENET_TX_READY ((ushort)0x8000) @@ -272,9 +330,10 @@ struct fec_enet_private { int hwts_tx_en; struct timer_list time_keep; struct fec_enet_delayed_work delay_work; + struct regulator *reg_phy; }; -void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev); +void fec_ptp_init(struct platform_device *pdev); void fec_ptp_start_cyclecounter(struct net_device *ndev); int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index d48099f03b7..d3ad5ea711d 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -53,13 +53,15 @@ #include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/of_net.h> -#include <linux/pinctrl/consumer.h> #include <linux/regulator/consumer.h> +#include <linux/if_vlan.h> #include <asm/cacheflush.h> #include "fec.h" +static void set_multicast_list(struct net_device *ndev); + #if defined(CONFIG_ARM) #define FEC_ALIGNMENT 0xf #else @@ -89,6 +91,8 @@ #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) /* Controller has hardware checksum support */ #define FEC_QUIRK_HAS_CSUM (1 << 5) +/* Controller has hardware vlan support */ +#define FEC_QUIRK_HAS_VLAN (1 << 6) static struct platform_device_id fec_devtype[] = { { @@ -107,7 +111,8 @@ static struct platform_device_id fec_devtype[] = { }, { .name = "imx6q-fec", .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | - FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM, + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN, }, { .name = "mvf600-fec", .driver_data = FEC_QUIRK_ENET_MAC, @@ -178,11 +183,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) -/* The FEC stores dest/src/type, data, and checksum for receive packets. +/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. */ -#define PKT_MAXBUF_SIZE 1518 +#define PKT_MAXBUF_SIZE 1522 #define PKT_MINBUF_SIZE 64 -#define PKT_MAXBLR_SIZE 1520 +#define PKT_MAXBLR_SIZE 1536 /* FEC receive acceleration */ #define FEC_RACC_IPDIS (1 << 1) @@ -243,7 +248,7 @@ static void *swap_buffer(void *bufaddr, int len) int i; unsigned int *buf = bufaddr; - for (i = 0; i < (len + 3) / 4; i++, buf++) + for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++) *buf = cpu_to_be32(*buf); return bufaddr; @@ -471,9 +476,8 @@ fec_restart(struct net_device *ndev, int duplex) /* Clear any outstanding interrupt. */ writel(0xffc00000, fep->hwp + FEC_IEVENT); - /* Reset all multicast. */ - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); + /* Setup multicast filter. */ + set_multicast_list(ndev); #ifndef CONFIG_M5272 writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); writel(0, fep->hwp + FEC_HASH_TABLE_LOW); @@ -609,6 +613,11 @@ fec_restart(struct net_device *ndev, int duplex) if (fep->bufdesc_ex) ecntl |= (1 << 4); +#ifndef CONFIG_M5272 + /* Enable the MIB statistic event counters */ + writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); +#endif + /* And last, enable the transmit and receive processing */ writel(ecntl, fep->hwp + FEC_ECNTRL); writel(0, fep->hwp + FEC_R_DES_ACTIVE); @@ -735,6 +744,7 @@ fec_enet_tx(struct net_device *ndev) ndev->stats.tx_carrier_errors++; } else { ndev->stats.tx_packets++; + ndev->stats.tx_bytes += bdp->cbd_datlen; } if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && @@ -800,6 +810,9 @@ fec_enet_rx(struct net_device *ndev, int budget) ushort pkt_len; __u8 *data; int pkt_received = 0; + struct bufdesc_ex *ebdp = NULL; + bool vlan_packet_rcvd = false; + u16 vlan_tag; #ifdef CONFIG_M532x flush_cache_all(); @@ -863,6 +876,24 @@ fec_enet_rx(struct net_device *ndev, int budget) if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) swap_buffer(data, pkt_len); + /* Extract the enhanced buffer descriptor */ + ebdp = NULL; + if (fep->bufdesc_ex) + ebdp = (struct bufdesc_ex *)bdp; + + /* If this is a VLAN packet remove the VLAN Tag */ + vlan_packet_rcvd = false; + if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && + fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { + /* Push and remove the vlan tag */ + struct vlan_hdr *vlan_header = + (struct vlan_hdr *) (data + ETH_HLEN); + vlan_tag = ntohs(vlan_header->h_vlan_TCI); + pkt_len -= VLAN_HLEN; + + vlan_packet_rcvd = true; + } + /* This does 16 byte alignment, exactly what we need. * The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up @@ -873,9 +904,18 @@ fec_enet_rx(struct net_device *ndev, int budget) if (unlikely(!skb)) { ndev->stats.rx_dropped++; } else { + int payload_offset = (2 * ETH_ALEN); skb_reserve(skb, NET_IP_ALIGN); skb_put(skb, pkt_len - 4); /* Make room */ - skb_copy_to_linear_data(skb, data, pkt_len - 4); + + /* Extract the frame data without the VLAN header. */ + skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN)); + if (vlan_packet_rcvd) + payload_offset = (2 * ETH_ALEN) + VLAN_HLEN; + skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN), + data + payload_offset, + pkt_len - 4 - (2 * ETH_ALEN)); + skb->protocol = eth_type_trans(skb, ndev); /* Get receive timestamp from the skb */ @@ -883,8 +923,6 @@ fec_enet_rx(struct net_device *ndev, int budget) struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); unsigned long flags; - struct bufdesc_ex *ebdp = - (struct bufdesc_ex *)bdp; memset(shhwtstamps, 0, sizeof(*shhwtstamps)); @@ -895,9 +933,7 @@ fec_enet_rx(struct net_device *ndev, int budget) } if (fep->bufdesc_ex && - (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { - struct bufdesc_ex *ebdp = - (struct bufdesc_ex *)bdp; + (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { /* don't check it */ skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -906,6 +942,12 @@ fec_enet_rx(struct net_device *ndev, int budget) } } + /* Handle received VLAN packets */ + if (vlan_packet_rcvd) + __vlan_hwaccel_put_tag(skb, + htons(ETH_P_8021Q), + vlan_tag); + if (!skb_defer_rx_timestamp(skb)) napi_gro_receive(&fep->napi, skb); } @@ -1444,8 +1486,117 @@ static int fec_enet_set_pauseparam(struct net_device *ndev, return 0; } +static const struct fec_stat { + char name[ETH_GSTRING_LEN]; + u16 offset; +} fec_stats[] = { + /* RMON TX */ + { "tx_dropped", RMON_T_DROP }, + { "tx_packets", RMON_T_PACKETS }, + { "tx_broadcast", RMON_T_BC_PKT }, + { "tx_multicast", RMON_T_MC_PKT }, + { "tx_crc_errors", RMON_T_CRC_ALIGN }, + { "tx_undersize", RMON_T_UNDERSIZE }, + { "tx_oversize", RMON_T_OVERSIZE }, + { "tx_fragment", RMON_T_FRAG }, + { "tx_jabber", RMON_T_JAB }, + { "tx_collision", RMON_T_COL }, + { "tx_64byte", RMON_T_P64 }, + { "tx_65to127byte", RMON_T_P65TO127 }, + { "tx_128to255byte", RMON_T_P128TO255 }, + { "tx_256to511byte", RMON_T_P256TO511 }, + { "tx_512to1023byte", RMON_T_P512TO1023 }, + { "tx_1024to2047byte", RMON_T_P1024TO2047 }, + { "tx_GTE2048byte", RMON_T_P_GTE2048 }, + { "tx_octets", RMON_T_OCTETS }, + + /* IEEE TX */ + { "IEEE_tx_drop", IEEE_T_DROP }, + { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, + { "IEEE_tx_1col", IEEE_T_1COL }, + { "IEEE_tx_mcol", IEEE_T_MCOL }, + { "IEEE_tx_def", IEEE_T_DEF }, + { "IEEE_tx_lcol", IEEE_T_LCOL }, + { "IEEE_tx_excol", IEEE_T_EXCOL }, + { "IEEE_tx_macerr", IEEE_T_MACERR }, + { "IEEE_tx_cserr", IEEE_T_CSERR }, + { "IEEE_tx_sqe", IEEE_T_SQE }, + { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, + { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, + + /* RMON RX */ + { "rx_packets", RMON_R_PACKETS }, + { "rx_broadcast", RMON_R_BC_PKT }, + { "rx_multicast", RMON_R_MC_PKT }, + { "rx_crc_errors", RMON_R_CRC_ALIGN }, + { "rx_undersize", RMON_R_UNDERSIZE }, + { "rx_oversize", RMON_R_OVERSIZE }, + { "rx_fragment", RMON_R_FRAG }, + { "rx_jabber", RMON_R_JAB }, + { "rx_64byte", RMON_R_P64 }, + { "rx_65to127byte", RMON_R_P65TO127 }, + { "rx_128to255byte", RMON_R_P128TO255 }, + { "rx_256to511byte", RMON_R_P256TO511 }, + { "rx_512to1023byte", RMON_R_P512TO1023 }, + { "rx_1024to2047byte", RMON_R_P1024TO2047 }, + { "rx_GTE2048byte", RMON_R_P_GTE2048 }, + { "rx_octets", RMON_R_OCTETS }, + + /* IEEE RX */ + { "IEEE_rx_drop", IEEE_R_DROP }, + { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, + { "IEEE_rx_crc", IEEE_R_CRC }, + { "IEEE_rx_align", IEEE_R_ALIGN }, + { "IEEE_rx_macerr", IEEE_R_MACERR }, + { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, + { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, +}; + +static void fec_enet_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct fec_enet_private *fep = netdev_priv(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(fec_stats); i++) + data[i] = readl(fep->hwp + fec_stats[i].offset); +} + +static void fec_enet_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + int i; + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(fec_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + fec_stats[i].name, ETH_GSTRING_LEN); + break; + } +} + +static int fec_enet_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(fec_stats); + default: + return -EOPNOTSUPP; + } +} #endif /* !defined(CONFIG_M5272) */ +static int fec_enet_nway_reset(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); + struct phy_device *phydev = fep->phy_dev; + + if (!phydev) + return -ENODEV; + + return genphy_restart_aneg(phydev); +} + static const struct ethtool_ops fec_enet_ethtool_ops = { #if !defined(CONFIG_M5272) .get_pauseparam = fec_enet_get_pauseparam, @@ -1456,6 +1607,12 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .get_drvinfo = fec_enet_get_drvinfo, .get_link = ethtool_op_get_link, .get_ts_info = fec_enet_get_ts_info, + .nway_reset = fec_enet_nway_reset, +#ifndef CONFIG_M5272 + .get_ethtool_stats = fec_enet_get_ethtool_stats, + .get_strings = fec_enet_get_strings, + .get_sset_count = fec_enet_get_sset_count, +#endif }; static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) @@ -1803,6 +1960,12 @@ static int fec_enet_init(struct net_device *ndev) writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); + if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) { + /* enable hw VLAN support */ + ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; + } + if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { /* enable hw accelerator */ ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM @@ -1865,8 +2028,6 @@ fec_probe(struct platform_device *pdev) struct resource *r; const struct of_device_id *of_id; static int dev_id; - struct pinctrl *pinctrl; - struct regulator *reg_phy; of_id = of_match_device(fec_dt_ids, &pdev->dev); if (of_id) @@ -1893,17 +2054,17 @@ fec_probe(struct platform_device *pdev) fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; #endif - fep->hwp = devm_request_and_ioremap(&pdev->dev, r); + fep->hwp = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(fep->hwp)) { + ret = PTR_ERR(fep->hwp); + goto failed_ioremap; + } + fep->pdev = pdev; fep->dev_id = dev_id++; fep->bufdesc_ex = 0; - if (!fep->hwp) { - ret = -ENOMEM; - goto failed_ioremap; - } - platform_set_drvdata(pdev, ndev); ret = of_get_phy_mode(pdev->dev.of_node); @@ -1917,12 +2078,6 @@ fec_probe(struct platform_device *pdev) fep->phy_interface = ret; } - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto failed_pin; - } - fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(fep->clk_ipg)) { ret = PTR_ERR(fep->clk_ipg); @@ -1953,20 +2108,22 @@ fec_probe(struct platform_device *pdev) clk_prepare_enable(fep->clk_enet_out); clk_prepare_enable(fep->clk_ptp); - reg_phy = devm_regulator_get(&pdev->dev, "phy"); - if (!IS_ERR(reg_phy)) { - ret = regulator_enable(reg_phy); + fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); + if (!IS_ERR(fep->reg_phy)) { + ret = regulator_enable(fep->reg_phy); if (ret) { dev_err(&pdev->dev, "Failed to enable phy regulator: %d\n", ret); goto failed_regulator; } + } else { + fep->reg_phy = NULL; } fec_reset_phy(pdev); if (fep->bufdesc_ex) - fec_ptp_init(ndev, pdev); + fec_ptp_init(pdev); ret = fec_enet_init(ndev); if (ret) @@ -2010,19 +2167,20 @@ fec_probe(struct platform_device *pdev) failed_register: fec_enet_mii_remove(fep); failed_mii_init: -failed_init: +failed_irq: for (i = 0; i < FEC_IRQ_NUM; i++) { irq = platform_get_irq(pdev, i); if (irq > 0) free_irq(irq, ndev); } -failed_irq: +failed_init: + if (fep->reg_phy) + regulator_disable(fep->reg_phy); failed_regulator: clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); clk_disable_unprepare(fep->clk_enet_out); clk_disable_unprepare(fep->clk_ptp); -failed_pin: failed_clk: failed_ioremap: free_netdev(ndev); @@ -2041,21 +2199,21 @@ fec_drv_remove(struct platform_device *pdev) unregister_netdev(ndev); fec_enet_mii_remove(fep); del_timer_sync(&fep->time_keep); + for (i = 0; i < FEC_IRQ_NUM; i++) { + int irq = platform_get_irq(pdev, i); + if (irq > 0) + free_irq(irq, ndev); + } + if (fep->reg_phy) + regulator_disable(fep->reg_phy); clk_disable_unprepare(fep->clk_ptp); if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); clk_disable_unprepare(fep->clk_enet_out); clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); - for (i = 0; i < FEC_IRQ_NUM; i++) { - int irq = platform_get_irq(pdev, i); - if (irq > 0) - free_irq(irq, ndev); - } free_netdev(ndev); - platform_set_drvdata(pdev, NULL); - return 0; } @@ -2074,6 +2232,9 @@ fec_suspend(struct device *dev) clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); + if (fep->reg_phy) + regulator_disable(fep->reg_phy); + return 0; } @@ -2082,6 +2243,13 @@ fec_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + if (fep->reg_phy) { + ret = regulator_enable(fep->reg_phy); + if (ret) + return ret; + } clk_prepare_enable(fep->clk_enet_out); clk_prepare_enable(fep->clk_ahb); diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 9bc15e2365b..9947765e90c 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -981,7 +981,7 @@ static int mpc52xx_fec_probe(struct platform_device *op) goto err_node; /* We're done ! */ - dev_set_drvdata(&op->dev, ndev); + platform_set_drvdata(op, ndev); netdev_info(ndev, "%s MAC %pM\n", op->dev.of_node->full_name, ndev->dev_addr); @@ -1010,7 +1010,7 @@ mpc52xx_fec_remove(struct platform_device *op) struct net_device *ndev; struct mpc52xx_fec_priv *priv; - ndev = dev_get_drvdata(&op->dev); + ndev = platform_get_drvdata(op); priv = netdev_priv(ndev); unregister_netdev(ndev); @@ -1030,14 +1030,13 @@ mpc52xx_fec_remove(struct platform_device *op) free_netdev(ndev); - dev_set_drvdata(&op->dev, NULL); return 0; } #ifdef CONFIG_PM static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state) { - struct net_device *dev = dev_get_drvdata(&op->dev); + struct net_device *dev = platform_get_drvdata(op); if (netif_running(dev)) mpc52xx_fec_close(dev); @@ -1047,7 +1046,7 @@ static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state static int mpc52xx_fec_of_resume(struct platform_device *op) { - struct net_device *dev = dev_get_drvdata(&op->dev); + struct net_device *dev = platform_get_drvdata(op); mpc52xx_fec_hw_init(dev); mpc52xx_fec_reset_stats(dev); diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 25fc960cbf0..5007e4f9fff 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -347,8 +347,9 @@ static void fec_time_keep(unsigned long _data) * cyclecounter init routine and exits. */ -void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev) +void fec_ptp_init(struct platform_device *pdev) { + struct net_device *ndev = platform_get_drvdata(pdev); struct fec_enet_private *fep = netdev_priv(ndev); fep->ptp_caps.owner = THIS_MODULE; diff --git a/drivers/net/ethernet/freescale/fs_enet/Kconfig b/drivers/net/ethernet/freescale/fs_enet/Kconfig index 268414d9f2c..be92229f2c2 100644 --- a/drivers/net/ethernet/freescale/fs_enet/Kconfig +++ b/drivers/net/ethernet/freescale/fs_enet/Kconfig @@ -1,7 +1,6 @@ config FS_ENET tristate "Freescale Ethernet Driver" depends on NET_VENDOR_FREESCALE && (CPM1 || CPM2 || PPC_MPC512x) - select NET_CORE select MII select PHYLIB diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index edc120094c3..8de53a14a6f 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -1048,7 +1048,7 @@ static int fs_enet_probe(struct platform_device *ofdev) } SET_NETDEV_DEV(ndev, &ofdev->dev); - dev_set_drvdata(&ofdev->dev, ndev); + platform_set_drvdata(ofdev, ndev); fep = netdev_priv(ndev); fep->dev = &ofdev->dev; @@ -1106,7 +1106,6 @@ out_cleanup_data: fep->ops->cleanup_data(ndev); out_free_dev: free_netdev(ndev); - dev_set_drvdata(&ofdev->dev, NULL); out_put: of_node_put(fpi->phy_node); out_free_fpi: @@ -1116,7 +1115,7 @@ out_free_fpi: static int fs_enet_remove(struct platform_device *ofdev) { - struct net_device *ndev = dev_get_drvdata(&ofdev->dev); + struct net_device *ndev = platform_get_drvdata(ofdev); struct fs_enet_private *fep = netdev_priv(ndev); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c index 2bafbd37c24..844ecfa84d1 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c @@ -179,7 +179,7 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) } new_bus->parent = &ofdev->dev; - dev_set_drvdata(&ofdev->dev, new_bus); + platform_set_drvdata(ofdev, new_bus); ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); if (ret) @@ -188,7 +188,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) return 0; out_free_irqs: - dev_set_drvdata(&ofdev->dev, NULL); kfree(new_bus->irq); out_unmap_regs: iounmap(bitbang->dir); @@ -202,11 +201,10 @@ out: static int fs_enet_mdio_remove(struct platform_device *ofdev) { - struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); + struct mii_bus *bus = platform_get_drvdata(ofdev); struct bb_info *bitbang = bus->priv; mdiobus_unregister(bus); - dev_set_drvdata(&ofdev->dev, NULL); kfree(bus->irq); free_mdio_bitbang(bus); iounmap(bitbang->dir); diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c index 18e8ef20373..2f1c46a12f0 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c @@ -180,7 +180,7 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) } new_bus->parent = &ofdev->dev; - dev_set_drvdata(&ofdev->dev, new_bus); + platform_set_drvdata(ofdev, new_bus); ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); if (ret) @@ -189,7 +189,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) return 0; out_free_irqs: - dev_set_drvdata(&ofdev->dev, NULL); kfree(new_bus->irq); out_unmap_regs: iounmap(fec->fecp); @@ -204,11 +203,10 @@ out: static int fs_enet_mdio_remove(struct platform_device *ofdev) { - struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); + struct mii_bus *bus = platform_get_drvdata(ofdev); struct fec_info *fec = bus->priv; mdiobus_unregister(bus); - dev_set_drvdata(&ofdev->dev, NULL); kfree(bus->irq); iounmap(fec->fecp); kfree(fec); diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 2375a01715a..8d2db7b808b 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -128,6 +128,7 @@ static void gfar_set_multi(struct net_device *dev); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); static void gfar_configure_serdes(struct net_device *dev); static int gfar_poll(struct napi_struct *napi, int budget); +static int gfar_poll_sq(struct napi_struct *napi, int budget); #ifdef CONFIG_NET_POLL_CONTROLLER static void gfar_netpoll(struct net_device *dev); #endif @@ -1000,7 +1001,7 @@ static int gfar_probe(struct platform_device *ofdev) spin_lock_init(&priv->bflock); INIT_WORK(&priv->reset_task, gfar_reset_task); - dev_set_drvdata(&ofdev->dev, priv); + platform_set_drvdata(ofdev, priv); regs = priv->gfargrp[0].regs; gfar_detect_errata(priv); @@ -1038,9 +1039,13 @@ static int gfar_probe(struct platform_device *ofdev) dev->ethtool_ops = &gfar_ethtool_ops; /* Register for napi ...We are registering NAPI for each grp */ - for (i = 0; i < priv->num_grps; i++) - netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, + if (priv->mode == SQ_SG_MODE) + netif_napi_add(dev, &priv->gfargrp[0].napi, gfar_poll_sq, GFAR_DEV_WEIGHT); + else + for (i = 0; i < priv->num_grps; i++) + netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, + GFAR_DEV_WEIGHT); if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | @@ -1240,15 +1245,13 @@ register_fail: static int gfar_remove(struct platform_device *ofdev) { - struct gfar_private *priv = dev_get_drvdata(&ofdev->dev); + struct gfar_private *priv = platform_get_drvdata(ofdev); if (priv->phy_node) of_node_put(priv->phy_node); if (priv->tbi_node) of_node_put(priv->tbi_node); - dev_set_drvdata(&ofdev->dev, NULL); - unregister_netdev(priv->ndev); unmap_group_regs(priv); free_gfar_dev(priv); @@ -2825,6 +2828,48 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) return howmany; } +static int gfar_poll_sq(struct napi_struct *napi, int budget) +{ + struct gfar_priv_grp *gfargrp = + container_of(napi, struct gfar_priv_grp, napi); + struct gfar __iomem *regs = gfargrp->regs; + struct gfar_priv_tx_q *tx_queue = gfargrp->priv->tx_queue[0]; + struct gfar_priv_rx_q *rx_queue = gfargrp->priv->rx_queue[0]; + int work_done = 0; + + /* Clear IEVENT, so interrupts aren't called again + * because of the packets that have already arrived + */ + gfar_write(®s->ievent, IEVENT_RTX_MASK); + + /* run Tx cleanup to completion */ + if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) + gfar_clean_tx_ring(tx_queue); + + work_done = gfar_clean_rx_ring(rx_queue, budget); + + if (work_done < budget) { + napi_complete(napi); + /* Clear the halt bit in RSTAT */ + gfar_write(®s->rstat, gfargrp->rstat); + + gfar_write(®s->imask, IMASK_DEFAULT); + + /* If we are coalescing interrupts, update the timer + * Otherwise, clear it + */ + gfar_write(®s->txic, 0); + if (likely(tx_queue->txcoalescing)) + gfar_write(®s->txic, tx_queue->txic); + + gfar_write(®s->rxic, 0); + if (unlikely(rx_queue->rxcoalescing)) + gfar_write(®s->rxic, rx_queue->rxic); + } + + return work_done; +} + static int gfar_poll(struct napi_struct *napi, int budget) { struct gfar_priv_grp *gfargrp = diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 083ea2b4d20..098f133908a 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -519,7 +519,7 @@ static int gianfar_ptp_probe(struct platform_device *dev) } gfar_phc_index = ptp_clock_index(etsects->clock); - dev_set_drvdata(&dev->dev, etsects); + platform_set_drvdata(dev, etsects); return 0; @@ -537,7 +537,7 @@ no_memory: static int gianfar_ptp_remove(struct platform_device *dev) { - struct etsects *etsects = dev_get_drvdata(&dev->dev); + struct etsects *etsects = platform_get_drvdata(dev); gfar_write(&etsects->regs->tmr_temask, 0); gfar_write(&etsects->regs->tmr_ctrl, 0); diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index e04c59818f6..3c43dac894e 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3564,7 +3564,7 @@ static void ucc_geth_timeout(struct net_device *dev) static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state) { - struct net_device *ndev = dev_get_drvdata(&ofdev->dev); + struct net_device *ndev = platform_get_drvdata(ofdev); struct ucc_geth_private *ugeth = netdev_priv(ndev); if (!netif_running(ndev)) @@ -3592,7 +3592,7 @@ static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state) static int ucc_geth_resume(struct platform_device *ofdev) { - struct net_device *ndev = dev_get_drvdata(&ofdev->dev); + struct net_device *ndev = platform_get_drvdata(ofdev); struct ucc_geth_private *ugeth = netdev_priv(ndev); int err; diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 418068b941b..c1b6e7e31aa 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -227,7 +227,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev) goto err_registration; } - dev_set_drvdata(&pdev->dev, bus); + platform_set_drvdata(pdev, bus); return 0; @@ -242,7 +242,7 @@ err_ioremap: static int xgmac_mdio_remove(struct platform_device *pdev) { - struct mii_bus *bus = dev_get_drvdata(&pdev->dev); + struct mii_bus *bus = platform_get_drvdata(pdev); mdiobus_unregister(bus); iounmap(bus->priv); diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig index 6529d31595a..563a1ac71db 100644 --- a/drivers/net/ethernet/ibm/Kconfig +++ b/drivers/net/ethernet/ibm/Kconfig @@ -5,8 +5,7 @@ config NET_VENDOR_IBM bool "IBM devices" default y - depends on MCA || PPC_PSERIES || PPC_PSERIES || PPC_DCR || \ - (IBMEBUS && SPARSEMEM) + depends on PPC_PSERIES || PPC_DCR || (IBMEBUS && SPARSEMEM) ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 90ea0b1673c..35853b43d66 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -98,8 +98,7 @@ static struct ehea_fw_handle_array ehea_fw_handles; static struct ehea_bcmc_reg_array ehea_bcmc_regs; -static int ehea_probe_adapter(struct platform_device *dev, - const struct of_device_id *id); +static int ehea_probe_adapter(struct platform_device *dev); static int ehea_remove(struct platform_device *dev); @@ -112,7 +111,7 @@ static struct of_device_id ehea_device_table[] = { }; MODULE_DEVICE_TABLE(of, ehea_device_table); -static struct of_platform_driver ehea_driver = { +static struct platform_driver ehea_driver = { .driver = { .name = "ehea", .owner = THIS_MODULE, @@ -3251,8 +3250,7 @@ static void ehea_remove_device_sysfs(struct platform_device *dev) device_remove_file(&dev->dev, &dev_attr_remove_port); } -static int ehea_probe_adapter(struct platform_device *dev, - const struct of_device_id *id) +static int ehea_probe_adapter(struct platform_device *dev) { struct ehea_adapter *adapter; const u64 *adapter_handle; @@ -3289,7 +3287,7 @@ static int ehea_probe_adapter(struct platform_device *dev, adapter->pd = EHEA_PD_ID; - dev_set_drvdata(&dev->dev, adapter); + platform_set_drvdata(dev, adapter); /* initialize adapter and ports */ @@ -3360,7 +3358,7 @@ out: static int ehea_remove(struct platform_device *dev) { - struct ehea_adapter *adapter = dev_get_drvdata(&dev->dev); + struct ehea_adapter *adapter = platform_get_drvdata(dev); int i; for (i = 0; i < EHEA_MAX_PORTS; i++) diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 610ed223d1d..856ea66c922 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -696,7 +696,7 @@ static int mal_probe(struct platform_device *ofdev) /* Advertise this instance to the rest of the world */ wmb(); - dev_set_drvdata(&ofdev->dev, mal); + platform_set_drvdata(ofdev, mal); mal_dbg_register(mal); @@ -722,7 +722,7 @@ static int mal_probe(struct platform_device *ofdev) static int mal_remove(struct platform_device *ofdev) { - struct mal_instance *mal = dev_get_drvdata(&ofdev->dev); + struct mal_instance *mal = platform_get_drvdata(ofdev); MAL_DBG(mal, "remove" NL); @@ -735,8 +735,6 @@ static int mal_remove(struct platform_device *ofdev) "mal%d: commac list is not empty on remove!\n", mal->index); - dev_set_drvdata(&ofdev->dev, NULL); - free_irq(mal->serr_irq, mal); free_irq(mal->txde_irq, mal); free_irq(mal->txeob_irq, mal); diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c index 39251765b55..c47e23d6eea 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.c +++ b/drivers/net/ethernet/ibm/emac/rgmii.c @@ -95,7 +95,7 @@ static inline u32 rgmii_mode_mask(int mode, int input) int rgmii_attach(struct platform_device *ofdev, int input, int mode) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; RGMII_DBG(dev, "attach(%d)" NL, input); @@ -124,7 +124,7 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode) void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; u32 ssr; @@ -146,7 +146,7 @@ void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) void rgmii_get_mdio(struct platform_device *ofdev, int input) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; u32 fer; @@ -167,7 +167,7 @@ void rgmii_get_mdio(struct platform_device *ofdev, int input) void rgmii_put_mdio(struct platform_device *ofdev, int input) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; u32 fer; @@ -188,7 +188,7 @@ void rgmii_put_mdio(struct platform_device *ofdev, int input) void rgmii_detach(struct platform_device *ofdev, int input) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p; BUG_ON(!dev || dev->users == 0); @@ -214,7 +214,7 @@ int rgmii_get_regs_len(struct platform_device *ofdev) void *rgmii_dump_regs(struct platform_device *ofdev, void *buf) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct emac_ethtool_regs_subhdr *hdr = buf; struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1); @@ -279,7 +279,7 @@ static int rgmii_probe(struct platform_device *ofdev) (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); wmb(); - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); return 0; @@ -291,9 +291,7 @@ static int rgmii_probe(struct platform_device *ofdev) static int rgmii_remove(struct platform_device *ofdev) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); - - dev_set_drvdata(&ofdev->dev, NULL); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); WARN_ON(dev->users != 0); diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c index 795f1393e2b..c231a4a32c4 100644 --- a/drivers/net/ethernet/ibm/emac/tah.c +++ b/drivers/net/ethernet/ibm/emac/tah.c @@ -25,7 +25,7 @@ int tah_attach(struct platform_device *ofdev, int channel) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); mutex_lock(&dev->lock); /* Reset has been done at probe() time... nothing else to do for now */ @@ -37,7 +37,7 @@ int tah_attach(struct platform_device *ofdev, int channel) void tah_detach(struct platform_device *ofdev, int channel) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); mutex_lock(&dev->lock); --dev->users; @@ -46,7 +46,7 @@ void tah_detach(struct platform_device *ofdev, int channel) void tah_reset(struct platform_device *ofdev) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); struct tah_regs __iomem *p = dev->base; int n; @@ -74,7 +74,7 @@ int tah_get_regs_len(struct platform_device *ofdev) void *tah_dump_regs(struct platform_device *ofdev, void *buf) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); struct emac_ethtool_regs_subhdr *hdr = buf; struct tah_regs *regs = (struct tah_regs *)(hdr + 1); @@ -118,7 +118,7 @@ static int tah_probe(struct platform_device *ofdev) goto err_free; } - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ tah_reset(ofdev); @@ -137,9 +137,7 @@ static int tah_probe(struct platform_device *ofdev) static int tah_remove(struct platform_device *ofdev) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); - - dev_set_drvdata(&ofdev->dev, NULL); + struct tah_instance *dev = platform_get_drvdata(ofdev); WARN_ON(dev->users != 0); diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c index f91202f4212..4cdf286f7ee 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.c +++ b/drivers/net/ethernet/ibm/emac/zmii.c @@ -84,7 +84,7 @@ static inline u32 zmii_mode_mask(int mode, int input) int zmii_attach(struct platform_device *ofdev, int input, int *mode) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); struct zmii_regs __iomem *p = dev->base; ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode); @@ -150,7 +150,7 @@ int zmii_attach(struct platform_device *ofdev, int input, int *mode) void zmii_get_mdio(struct platform_device *ofdev, int input) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); u32 fer; ZMII_DBG2(dev, "get_mdio(%d)" NL, input); @@ -163,7 +163,7 @@ void zmii_get_mdio(struct platform_device *ofdev, int input) void zmii_put_mdio(struct platform_device *ofdev, int input) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); ZMII_DBG2(dev, "put_mdio(%d)" NL, input); mutex_unlock(&dev->lock); @@ -172,7 +172,7 @@ void zmii_put_mdio(struct platform_device *ofdev, int input) void zmii_set_speed(struct platform_device *ofdev, int input, int speed) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); u32 ssr; mutex_lock(&dev->lock); @@ -193,7 +193,7 @@ void zmii_set_speed(struct platform_device *ofdev, int input, int speed) void zmii_detach(struct platform_device *ofdev, int input) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); BUG_ON(!dev || dev->users == 0); @@ -218,7 +218,7 @@ int zmii_get_regs_len(struct platform_device *ofdev) void *zmii_dump_regs(struct platform_device *ofdev, void *buf) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); struct emac_ethtool_regs_subhdr *hdr = buf; struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1); @@ -272,7 +272,7 @@ static int zmii_probe(struct platform_device *ofdev) printk(KERN_INFO "ZMII %s initialized\n", ofdev->dev.of_node->full_name); wmb(); - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); return 0; @@ -284,9 +284,7 @@ static int zmii_probe(struct platform_device *ofdev) static int zmii_remove(struct platform_device *ofdev) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); - - dev_set_drvdata(&ofdev->dev, NULL); + struct zmii_instance *dev = platform_get_drvdata(ofdev); WARN_ON(dev->users != 0); diff --git a/drivers/net/ethernet/icplus/Kconfig b/drivers/net/ethernet/icplus/Kconfig index 5119ef18953..14a66e9d2e2 100644 --- a/drivers/net/ethernet/icplus/Kconfig +++ b/drivers/net/ethernet/icplus/Kconfig @@ -5,7 +5,6 @@ config IP1000 tristate "IP1000 Gigabit Ethernet support" depends on PCI - select NET_CORE select MII ---help--- This driver supports IP1000 gigabit Ethernet cards. diff --git a/drivers/net/ethernet/icplus/ipg.c b/drivers/net/ethernet/icplus/ipg.c index 068d7815165..1fde90b9668 100644 --- a/drivers/net/ethernet/icplus/ipg.c +++ b/drivers/net/ethernet/icplus/ipg.c @@ -2298,15 +2298,4 @@ static struct pci_driver ipg_pci_driver = { .remove = ipg_remove, }; -static int __init ipg_init_module(void) -{ - return pci_register_driver(&ipg_pci_driver); -} - -static void __exit ipg_exit_module(void) -{ - pci_unregister_driver(&ipg_pci_driver); -} - -module_init(ipg_init_module); -module_exit(ipg_exit_module); +module_pci_driver(ipg_pci_driver); diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index 05f7264c51f..f0e7ed20a75 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -20,7 +20,6 @@ if NET_VENDOR_INTEL config E100 tristate "Intel(R) PRO/100+ support" depends on PCI - select NET_CORE select MII ---help--- This driver supports Intel(R) PRO/100 family of adapters. diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index d2bea3f07c7..5115ae76a5d 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -3069,7 +3069,7 @@ static int e100_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* ack any pending wake events, disable PME */ - pci_enable_wake(pdev, 0, 0); + pci_enable_wake(pdev, PCI_D0, 0); /* disable reverse auto-negotiation */ if (nic->phy == phy_82552_v) { @@ -3160,7 +3160,7 @@ static void e100_io_resume(struct pci_dev *pdev) struct nic *nic = netdev_priv(netdev); /* ack any pending wake events, disable PME */ - pci_enable_wake(pdev, 0, 0); + pci_enable_wake(pdev, PCI_D0, 0); netif_device_attach(netdev); if (netif_running(netdev)) { diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c index b71c8502a2b..895450e9bb3 100644 --- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c +++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c @@ -66,17 +66,17 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) s32 ret_val; if (hw->phy.media_type != e1000_media_type_copper) { - phy->type = e1000_phy_none; + phy->type = e1000_phy_none; return 0; } else { phy->ops.power_up = e1000_power_up_phy_copper; phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan; } - phy->addr = 1; - phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; - phy->reset_delay_us = 100; - phy->type = e1000_phy_gg82563; + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + phy->type = e1000_phy_gg82563; /* This can only be done after all function pointers are setup. */ ret_val = e1000e_get_phy_id(hw); @@ -98,19 +98,19 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) u32 eecd = er32(EECD); u16 size; - nvm->opcode_bits = 8; - nvm->delay_usec = 1; + nvm->opcode_bits = 8; + nvm->delay_usec = 1; switch (nvm->override) { case e1000_nvm_override_spi_large: - nvm->page_size = 32; + nvm->page_size = 32; nvm->address_bits = 16; break; case e1000_nvm_override_spi_small: - nvm->page_size = 8; + nvm->page_size = 8; nvm->address_bits = 8; break; default: - nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8; break; } @@ -128,7 +128,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) /* EEPROM access above 16k is unsupported */ if (size > 14) size = 14; - nvm->word_size = 1 << size; + nvm->word_size = 1 << size; return 0; } @@ -859,7 +859,7 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw) /* Transmit Arbitration Control 0 */ reg = er32(TARC(0)); - reg &= ~(0xF << 27); /* 30:27 */ + reg &= ~(0xF << 27); /* 30:27 */ if (hw->phy.media_type != e1000_media_type_copper) reg &= ~(1 << 20); ew32(TARC(0), reg); diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index 7380442a382..4c303e2a7cb 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -77,24 +77,24 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw) return 0; } - phy->addr = 1; - phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; - phy->reset_delay_us = 100; + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; - phy->ops.power_up = e1000_power_up_phy_copper; - phy->ops.power_down = e1000_power_down_phy_copper_82571; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_82571; switch (hw->mac.type) { case e1000_82571: case e1000_82572: - phy->type = e1000_phy_igp_2; + phy->type = e1000_phy_igp_2; break; case e1000_82573: - phy->type = e1000_phy_m88; + phy->type = e1000_phy_m88; break; case e1000_82574: case e1000_82583: - phy->type = e1000_phy_bm; + phy->type = e1000_phy_bm; phy->ops.acquire = e1000_get_hw_semaphore_82574; phy->ops.release = e1000_put_hw_semaphore_82574; phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82574; @@ -193,7 +193,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) /* EEPROM access above 16k is unsupported */ if (size > 14) size = 14; - nvm->word_size = 1 << size; + nvm->word_size = 1 << size; break; } @@ -339,7 +339,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw) static s32 e1000_get_variants_82571(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; - static int global_quad_port_a; /* global port a indication */ + static int global_quad_port_a; /* global port a indication */ struct pci_dev *pdev = adapter->pdev; int is_port_b = er32(STATUS) & E1000_STATUS_FUNC_1; s32 rc; @@ -1003,8 +1003,6 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) default: break; } - if (ret_val) - e_dbg("Cannot acquire MDIO ownership\n"); ctrl = er32(CTRL); @@ -1015,7 +1013,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82574: case e1000_82583: - e1000_put_hw_semaphore_82574(hw); + /* Release mutex only if the hw semaphore is acquired */ + if (!ret_val) + e1000_put_hw_semaphore_82574(hw); break; default: break; @@ -1178,7 +1178,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) /* Transmit Arbitration Control 0 */ reg = er32(TARC(0)); - reg &= ~(0xF << 27); /* 30:27 */ + reg &= ~(0xF << 27); /* 30:27 */ switch (hw->mac.type) { case e1000_82571: case e1000_82572: @@ -1390,7 +1390,7 @@ bool e1000_check_phy_82574(struct e1000_hw *hw) ret_val = e1e_rphy(hw, E1000_RECEIVE_ERROR_COUNTER, &receive_errors); if (ret_val) return false; - if (receive_errors == E1000_RECEIVE_ERROR_MAX) { + if (receive_errors == E1000_RECEIVE_ERROR_MAX) { ret_val = e1e_rphy(hw, E1000_BASE1000T_STATUS, &status_1kbt); if (ret_val) return false; diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 7c8ca658d55..59c22bf1870 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -244,7 +244,7 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) mac->autoneg = 1; adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; break; - case SPEED_1000 + DUPLEX_HALF: /* not supported */ + case SPEED_1000 + DUPLEX_HALF: /* not supported */ default: goto err_inval; } @@ -416,7 +416,7 @@ static void e1000_set_msglevel(struct net_device *netdev, u32 data) static int e1000_get_regs_len(struct net_device __always_unused *netdev) { -#define E1000_REGS_LEN 32 /* overestimate */ +#define E1000_REGS_LEN 32 /* overestimate */ return E1000_REGS_LEN * sizeof(u32); } @@ -433,22 +433,22 @@ static void e1000_get_regs(struct net_device *netdev, regs->version = (1 << 24) | (adapter->pdev->revision << 16) | adapter->pdev->device; - regs_buff[0] = er32(CTRL); - regs_buff[1] = er32(STATUS); + regs_buff[0] = er32(CTRL); + regs_buff[1] = er32(STATUS); - regs_buff[2] = er32(RCTL); - regs_buff[3] = er32(RDLEN(0)); - regs_buff[4] = er32(RDH(0)); - regs_buff[5] = er32(RDT(0)); - regs_buff[6] = er32(RDTR); + regs_buff[2] = er32(RCTL); + regs_buff[3] = er32(RDLEN(0)); + regs_buff[4] = er32(RDH(0)); + regs_buff[5] = er32(RDT(0)); + regs_buff[6] = er32(RDTR); - regs_buff[7] = er32(TCTL); - regs_buff[8] = er32(TDLEN(0)); - regs_buff[9] = er32(TDH(0)); + regs_buff[7] = er32(TCTL); + regs_buff[8] = er32(TDLEN(0)); + regs_buff[9] = er32(TDH(0)); regs_buff[10] = er32(TDT(0)); regs_buff[11] = er32(TIDV); - regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */ + regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */ /* ethtool doesn't use anything past this point, so all this * code is likely legacy junk for apps that may or may not exist @@ -1379,7 +1379,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) if (hw->phy.media_type == e1000_media_type_copper && hw->phy.type == e1000_phy_m88) { - ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */ + ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */ } else { /* Set the ILOS bit on the fiber Nic if half duplex link is * detected. @@ -1613,7 +1613,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) ew32(TDT(0), k); e1e_flush(); msleep(200); - time = jiffies; /* set the start time for the receive */ + time = jiffies; /* set the start time for the receive */ good_cnt = 0; /* receive the sent packets */ do { @@ -1636,11 +1636,11 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) */ } while ((good_cnt < 64) && !time_after(jiffies, time + 20)); if (good_cnt != 64) { - ret_val = 13; /* ret_val is the same as mis-compare */ + ret_val = 13; /* ret_val is the same as mis-compare */ break; } if (jiffies >= (time + 20)) { - ret_val = 14; /* error code for time out error */ + ret_val = 14; /* error code for time out error */ break; } } diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index 84850f7a23e..a6f903a9b77 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -402,13 +402,13 @@ struct e1000_phy_stats { struct e1000_host_mng_dhcp_cookie { u32 signature; - u8 status; - u8 reserved0; + u8 status; + u8 reserved0; u16 vlan_id; u32 reserved1; u16 reserved2; - u8 reserved3; - u8 checksum; + u8 reserved3; + u8 checksum; }; /* Host Interface "Rev 1" */ @@ -427,8 +427,8 @@ struct e1000_host_command_info { /* Host Interface "Rev 2" */ struct e1000_host_mng_command_header { - u8 command_id; - u8 checksum; + u8 command_id; + u8 checksum; u16 reserved1; u16 reserved2; u16 command_length; @@ -549,7 +549,7 @@ struct e1000_mac_info { u32 mta_shadow[MAX_MTA_REG]; u16 rar_entry_count; - u8 forced_speed_duplex; + u8 forced_speed_duplex; bool adaptive_ifs; bool has_fwsm; @@ -577,7 +577,7 @@ struct e1000_phy_info { u32 addr; u32 id; - u32 reset_delay_us; /* in usec */ + u32 reset_delay_us; /* in usec */ u32 revision; enum e1000_media_type media_type; @@ -636,11 +636,11 @@ struct e1000_dev_spec_82571 { }; struct e1000_dev_spec_80003es2lan { - bool mdic_wa_enable; + bool mdic_wa_enable; }; struct e1000_shadow_ram { - u16 value; + u16 value; bool modified; }; @@ -660,17 +660,17 @@ struct e1000_hw { void __iomem *hw_addr; void __iomem *flash_address; - struct e1000_mac_info mac; - struct e1000_fc_info fc; - struct e1000_phy_info phy; - struct e1000_nvm_info nvm; - struct e1000_bus_info bus; + struct e1000_mac_info mac; + struct e1000_fc_info fc; + struct e1000_phy_info phy; + struct e1000_nvm_info nvm; + struct e1000_bus_info bus; struct e1000_host_mng_dhcp_cookie mng_cookie; union { - struct e1000_dev_spec_82571 e82571; + struct e1000_dev_spec_82571 e82571; struct e1000_dev_spec_80003es2lan e80003es2lan; - struct e1000_dev_spec_ich8lan ich8lan; + struct e1000_dev_spec_ich8lan ich8lan; } dev_spec; }; diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index ad9d8f2dd86..9dde390f7e7 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -101,12 +101,12 @@ union ich8_hws_flash_regacc { /* ICH Flash Protected Region */ union ich8_flash_protected_range { struct ich8_pr { - u32 base:13; /* 0:12 Protected Range Base */ - u32 reserved1:2; /* 13:14 Reserved */ - u32 rpe:1; /* 15 Read Protection Enable */ - u32 limit:13; /* 16:28 Protected Range Limit */ - u32 reserved2:2; /* 29:30 Reserved */ - u32 wpe:1; /* 31 Write Protection Enable */ + u32 base:13; /* 0:12 Protected Range Base */ + u32 reserved1:2; /* 13:14 Reserved */ + u32 rpe:1; /* 15 Read Protection Enable */ + u32 limit:13; /* 16:28 Protected Range Limit */ + u32 reserved2:2; /* 29:30 Reserved */ + u32 wpe:1; /* 31 Write Protection Enable */ } range; u32 regval; }; @@ -362,21 +362,21 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) struct e1000_phy_info *phy = &hw->phy; s32 ret_val; - phy->addr = 1; - phy->reset_delay_us = 100; - - phy->ops.set_page = e1000_set_page_igp; - phy->ops.read_reg = e1000_read_phy_reg_hv; - phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked; - phy->ops.read_reg_page = e1000_read_phy_reg_page_hv; - phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan; - phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan; - phy->ops.write_reg = e1000_write_phy_reg_hv; - phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked; - phy->ops.write_reg_page = e1000_write_phy_reg_page_hv; - phy->ops.power_up = e1000_power_up_phy_copper; - phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; - phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->addr = 1; + phy->reset_delay_us = 100; + + phy->ops.set_page = e1000_set_page_igp; + phy->ops.read_reg = e1000_read_phy_reg_hv; + phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked; + phy->ops.read_reg_page = e1000_read_phy_reg_page_hv; + phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan; + phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan; + phy->ops.write_reg = e1000_write_phy_reg_hv; + phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked; + phy->ops.write_reg_page = e1000_write_phy_reg_page_hv; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; phy->id = e1000_phy_unknown; @@ -445,11 +445,11 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) s32 ret_val; u16 i = 0; - phy->addr = 1; - phy->reset_delay_us = 100; + phy->addr = 1; + phy->reset_delay_us = 100; - phy->ops.power_up = e1000_power_up_phy_copper; - phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; /* We may need to do this twice - once for IGP and if that fails, * we'll set BM func pointers and try again @@ -457,7 +457,7 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) ret_val = e1000e_determine_phy_address(hw); if (ret_val) { phy->ops.write_reg = e1000e_write_phy_reg_bm; - phy->ops.read_reg = e1000e_read_phy_reg_bm; + phy->ops.read_reg = e1000e_read_phy_reg_bm; ret_val = e1000e_determine_phy_address(hw); if (ret_val) { e_dbg("Cannot determine PHY addr. Erroring out\n"); @@ -560,7 +560,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* Clear shadow ram */ for (i = 0; i < nvm->word_size; i++) { dev_spec->shadow_ram[i].modified = false; - dev_spec->shadow_ram[i].value = 0xFFFF; + dev_spec->shadow_ram[i].value = 0xFFFF; } return 0; @@ -1012,7 +1012,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) hw->dev_spec.ich8lan.eee_lp_ability = 0; if (!link) - return 0; /* No link detected */ + return 0; /* No link detected */ mac->get_link_status = false; @@ -2816,7 +2816,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, s32 ret_val = -E1000_ERR_NVM; u8 count = 0; - if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK) + if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK) return -E1000_ERR_NVM; flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) + @@ -2939,7 +2939,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) * write to bank 0 etc. We also need to erase the segment that * is going to be written */ - ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); + ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); if (ret_val) { e_dbg("Could not detect valid bank, assuming bank 0\n"); bank = 0; @@ -4073,7 +4073,7 @@ void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw) { u32 reg; u16 data; - u8 retry = 0; + u8 retry = 0; if (hw->phy.type != e1000_phy_igp_3) return; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index a27e3bcc324..77f81cbb601 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1196,7 +1196,7 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) while ((eop_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) && (count < tx_ring->count)) { bool cleaned = false; - rmb(); /* read buffer_info after eop_desc */ + rmb(); /* read buffer_info after eop_desc */ for (; !cleaned; count++) { tx_desc = E1000_TX_DESC(*tx_ring, i); buffer_info = &tx_ring->buffer_info[i]; @@ -1385,7 +1385,7 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done, skb_put(skb, l1); goto copydone; - } /* if */ + } /* if */ } for (j = 0; j < PS_PAGE_BUFFERS; j++) { @@ -1800,7 +1800,7 @@ static irqreturn_t e1000_intr(int __always_unused irq, void *data) u32 rctl, icr = er32(ICR); if (!icr || test_bit(__E1000_DOWN, &adapter->state)) - return IRQ_NONE; /* Not our interrupt */ + return IRQ_NONE; /* Not our interrupt */ /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is * not set, then the adapter didn't send an interrupt @@ -2487,7 +2487,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes) else if ((packets < 5) && (bytes > 512)) retval = low_latency; break; - case low_latency: /* 50 usec aka 20000 ints/s */ + case low_latency: /* 50 usec aka 20000 ints/s */ if (bytes > 10000) { /* this if handles the TSO accounting */ if (bytes / packets > 8000) @@ -2502,7 +2502,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes) retval = lowest_latency; } break; - case bulk_latency: /* 250 usec aka 4000 ints/s */ + case bulk_latency: /* 250 usec aka 4000 ints/s */ if (bytes > 25000) { if (packets > 35) retval = low_latency; @@ -2554,7 +2554,7 @@ static void e1000_set_itr(struct e1000_adapter *adapter) new_itr = 70000; break; case low_latency: - new_itr = 20000; /* aka hwitr = ~200 */ + new_itr = 20000; /* aka hwitr = ~200 */ break; case bulk_latency: new_itr = 4000; @@ -2673,7 +2673,7 @@ static int e1000e_poll(struct napi_struct *napi, int weight) } static int e1000_vlan_rx_add_vid(struct net_device *netdev, - __be16 proto, u16 vid) + __always_unused __be16 proto, u16 vid) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -2699,7 +2699,7 @@ static int e1000_vlan_rx_add_vid(struct net_device *netdev, } static int e1000_vlan_rx_kill_vid(struct net_device *netdev, - __be16 proto, u16 vid) + __always_unused __be16 proto, u16 vid) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -3104,13 +3104,13 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) /* UPE and MPE will be handled by normal PROMISC logic * in e1000e_set_rx_mode */ - rctl |= (E1000_RCTL_SBP | /* Receive bad packets */ - E1000_RCTL_BAM | /* RX All Bcast Pkts */ - E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */ + rctl |= (E1000_RCTL_SBP | /* Receive bad packets */ + E1000_RCTL_BAM | /* RX All Bcast Pkts */ + E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */ - rctl &= ~(E1000_RCTL_VFE | /* Disable VLAN filter */ - E1000_RCTL_DPF | /* Allow filtered pause */ - E1000_RCTL_CFIEN); /* Dis VLAN CFIEN Filter */ + rctl &= ~(E1000_RCTL_VFE | /* Disable VLAN filter */ + E1000_RCTL_DPF | /* Allow filtered pause */ + E1000_RCTL_CFIEN); /* Dis VLAN CFIEN Filter */ /* Do not mess with E1000_CTRL_VME, it affects transmit as well, * and that breaks VLANs. */ @@ -3799,7 +3799,7 @@ void e1000e_reset(struct e1000_adapter *adapter) hwm = min(((pba << 10) * 9 / 10), ((pba << 10) - adapter->max_frame_size)); - fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */ + fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */ fc->low_water = fc->high_water - 8; break; case e1000_pchlan: @@ -3808,10 +3808,10 @@ void e1000e_reset(struct e1000_adapter *adapter) */ if (adapter->netdev->mtu > ETH_DATA_LEN) { fc->high_water = 0x3500; - fc->low_water = 0x1500; + fc->low_water = 0x1500; } else { fc->high_water = 0x5000; - fc->low_water = 0x3000; + fc->low_water = 0x3000; } fc->refresh_time = 0x1000; break; @@ -4581,7 +4581,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.crcerrs += er32(CRCERRS); adapter->stats.gprc += er32(GPRC); adapter->stats.gorc += er32(GORCL); - er32(GORCH); /* Clear gorc */ + er32(GORCH); /* Clear gorc */ adapter->stats.bprc += er32(BPRC); adapter->stats.mprc += er32(MPRC); adapter->stats.roc += er32(ROC); @@ -4614,7 +4614,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.xofftxc += er32(XOFFTXC); adapter->stats.gptc += er32(GPTC); adapter->stats.gotc += er32(GOTCL); - er32(GOTCH); /* Clear gotc */ + er32(GOTCH); /* Clear gotc */ adapter->stats.rnbc += er32(RNBC); adapter->stats.ruc += er32(RUC); @@ -5106,13 +5106,13 @@ static int e1000_tso(struct e1000_ring *tx_ring, struct sk_buff *skb) context_desc = E1000_CONTEXT_DESC(*tx_ring, i); buffer_info = &tx_ring->buffer_info[i]; - context_desc->lower_setup.ip_fields.ipcss = ipcss; - context_desc->lower_setup.ip_fields.ipcso = ipcso; - context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); + context_desc->lower_setup.ip_fields.ipcss = ipcss; + context_desc->lower_setup.ip_fields.ipcso = ipcso; + context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); context_desc->upper_setup.tcp_fields.tucss = tucss; context_desc->upper_setup.tcp_fields.tucso = tucso; context_desc->upper_setup.tcp_fields.tucse = 0; - context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); + context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); context_desc->tcp_seg_setup.fields.hdr_len = hdr_len; context_desc->cmd_and_length = cpu_to_le32(cmd_length); @@ -5363,7 +5363,7 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count) static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct sk_buff *skb) { - struct e1000_hw *hw = &adapter->hw; + struct e1000_hw *hw = &adapter->hw; u16 length, offset; if (vlan_tx_tag_present(skb) && @@ -6259,7 +6259,7 @@ static void e1000_netpoll(struct net_device *netdev) e1000_intr_msi(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); break; - default: /* E1000E_INT_MODE_LEGACY */ + default: /* E1000E_INT_MODE_LEGACY */ disable_irq(adapter->pdev->irq); e1000_intr(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); @@ -6589,9 +6589,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T; /* construct the net_device struct */ - netdev->netdev_ops = &e1000e_netdev_ops; + netdev->netdev_ops = &e1000e_netdev_ops; e1000e_set_ethtool_ops(netdev); - netdev->watchdog_timeo = 5 * HZ; + netdev->watchdog_timeo = 5 * HZ; netif_napi_add(netdev, &adapter->napi, e1000e_poll, 64); strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); @@ -7034,7 +7034,6 @@ static void __exit e1000_exit_module(void) } module_exit(e1000_exit_module); - MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>"); MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/intel/e1000e/nvm.c b/drivers/net/ethernet/intel/e1000e/nvm.c index 44ddc0a0ee0..d70a03906ac 100644 --- a/drivers/net/ethernet/intel/e1000e/nvm.c +++ b/drivers/net/ethernet/intel/e1000e/nvm.c @@ -117,7 +117,6 @@ static u16 e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count) u16 data; eecd = er32(EECD); - eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); data = 0; diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index 59c76a6815a..da2be59505c 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -1583,13 +1583,13 @@ s32 e1000e_check_downshift(struct e1000_hw *hw) case e1000_phy_gg82563: case e1000_phy_bm: case e1000_phy_82578: - offset = M88E1000_PHY_SPEC_STATUS; - mask = M88E1000_PSSR_DOWNSHIFT; + offset = M88E1000_PHY_SPEC_STATUS; + mask = M88E1000_PSSR_DOWNSHIFT; break; case e1000_phy_igp_2: case e1000_phy_igp_3: - offset = IGP01E1000_PHY_LINK_HEALTH; - mask = IGP01E1000_PLHR_SS_DOWNGRADE; + offset = IGP01E1000_PHY_LINK_HEALTH; + mask = IGP01E1000_PLHR_SS_DOWNGRADE; break; default: /* speed downshift not supported */ @@ -1653,14 +1653,14 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) if ((data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { - offset = IGP01E1000_PHY_PCS_INIT_REG; - mask = IGP01E1000_PHY_POLARITY_MASK; + offset = IGP01E1000_PHY_PCS_INIT_REG; + mask = IGP01E1000_PHY_POLARITY_MASK; } else { /* This really only applies to 10Mbps since * there is no polarity for 100Mbps (always 0). */ - offset = IGP01E1000_PHY_PORT_STATUS; - mask = IGP01E1000_PSSR_POLARITY_REVERSED; + offset = IGP01E1000_PHY_PORT_STATUS; + mask = IGP01E1000_PSSR_POLARITY_REVERSED; } ret_val = e1e_rphy(hw, offset, &data); @@ -1900,7 +1900,7 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw) s32 e1000e_get_phy_info_m88(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; + s32 ret_val; u16 phy_data; bool link; @@ -2253,7 +2253,7 @@ enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id) case M88E1011_I_PHY_ID: phy_type = e1000_phy_m88; break; - case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ + case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ phy_type = e1000_phy_igp_2; break; case GG82563_E_PHY_ID: @@ -2317,7 +2317,7 @@ s32 e1000e_determine_phy_address(struct e1000_hw *hw) /* If phy_type is valid, break - we found our * PHY address */ - if (phy_type != e1000_phy_unknown) + if (phy_type != e1000_phy_unknown) return 0; usleep_range(1000, 2000); diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index ff6a17cb136..f21a91a299a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -401,12 +401,82 @@ static s32 igb_init_mac_params_82575(struct e1000_hw *hw) return 0; } +/** + * igb_set_sfp_media_type_82575 - derives SFP module media type. + * @hw: pointer to the HW structure + * + * The media type is chosen based on SFP module. + * compatibility flags retrieved from SFP ID EEPROM. + **/ +static s32 igb_set_sfp_media_type_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_ERR_CONFIG; + u32 ctrl_ext = 0; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags; + u8 tranceiver_type = 0; + s32 timeout = 3; + + /* Turn I2C interface ON and power on sfp cage */ + ctrl_ext = rd32(E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + wr32(E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA); + + wrfl(); + + /* Read SFP module data */ + while (timeout) { + ret_val = igb_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET), + &tranceiver_type); + if (ret_val == 0) + break; + msleep(100); + timeout--; + } + if (ret_val != 0) + goto out; + + ret_val = igb_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET), + (u8 *)eth_flags); + if (ret_val != 0) + goto out; + + /* Check if there is some SFP module plugged and powered */ + if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) || + (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) { + dev_spec->module_plugged = true; + if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) { + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e100_base_fx) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e1000_base_t) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_copper; + } else { + hw->phy.media_type = e1000_media_type_unknown; + hw_dbg("PHY module has not been recognized\n"); + goto out; + } + } else { + hw->phy.media_type = e1000_media_type_unknown; + } + ret_val = 0; +out: + /* Restore I2C interface setting */ + wr32(E1000_CTRL_EXT, ctrl_ext); + return ret_val; +} + static s32 igb_get_invariants_82575(struct e1000_hw *hw) { struct e1000_mac_info *mac = &hw->mac; struct e1000_dev_spec_82575 * dev_spec = &hw->dev_spec._82575; s32 ret_val; u32 ctrl_ext = 0; + u32 link_mode = 0; switch (hw->device_id) { case E1000_DEV_ID_82575EB_COPPER: @@ -470,16 +540,56 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) */ hw->phy.media_type = e1000_media_type_copper; dev_spec->sgmii_active = false; + dev_spec->module_plugged = false; ctrl_ext = rd32(E1000_CTRL_EXT); - switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) { - case E1000_CTRL_EXT_LINK_MODE_SGMII: - dev_spec->sgmii_active = true; - break; + + link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK; + switch (link_mode) { case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: - case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: hw->phy.media_type = e1000_media_type_internal_serdes; break; + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* Get phy control interface type set (MDIO vs. I2C)*/ + if (igb_sgmii_uses_mdio_82575(hw)) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + break; + } + /* fall through for I2C based SGMII */ + case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: + /* read media type from SFP EEPROM */ + ret_val = igb_set_sfp_media_type_82575(hw); + if ((ret_val != 0) || + (hw->phy.media_type == e1000_media_type_unknown)) { + /* If media type was not identified then return media + * type defined by the CTRL_EXT settings. + */ + hw->phy.media_type = e1000_media_type_internal_serdes; + + if (link_mode == E1000_CTRL_EXT_LINK_MODE_SGMII) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + } + + break; + } + + /* do not change link mode for 100BaseFX */ + if (dev_spec->eth_flags.e100_base_fx) + break; + + /* change current link mode setting */ + ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK; + + if (hw->phy.media_type == e1000_media_type_copper) + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII; + else + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES; + + wr32(E1000_CTRL_EXT, ctrl_ext); + + break; default: break; } diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index 31a0f82cc65..aa201abb8ad 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -61,20 +61,22 @@ /* Clear Interrupt timers after IMS clear */ /* packet buffer parity error detection enabled */ /* descriptor FIFO parity error detection enable */ -#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ -#define E1000_I2CCMD_REG_ADDR_SHIFT 16 -#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 -#define E1000_I2CCMD_OPCODE_READ 0x08000000 -#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 -#define E1000_I2CCMD_READY 0x20000000 -#define E1000_I2CCMD_ERROR 0x80000000 -#define E1000_MAX_SGMII_PHY_REG_ADDR 255 -#define E1000_I2CCMD_PHY_TIMEOUT 200 -#define E1000_IVAR_VALID 0x80 -#define E1000_GPIE_NSICR 0x00000001 -#define E1000_GPIE_MSIX_MODE 0x00000010 -#define E1000_GPIE_EIAME 0x40000000 -#define E1000_GPIE_PBA 0x80000000 +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_I2CCMD_REG_ADDR_SHIFT 16 +#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 +#define E1000_I2CCMD_OPCODE_READ 0x08000000 +#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 +#define E1000_I2CCMD_READY 0x20000000 +#define E1000_I2CCMD_ERROR 0x80000000 +#define E1000_I2CCMD_SFP_DATA_ADDR(a) (0x0000 + (a)) +#define E1000_I2CCMD_SFP_DIAG_ADDR(a) (0x0100 + (a)) +#define E1000_MAX_SGMII_PHY_REG_ADDR 255 +#define E1000_I2CCMD_PHY_TIMEOUT 200 +#define E1000_IVAR_VALID 0x80 +#define E1000_GPIE_NSICR 0x00000001 +#define E1000_GPIE_MSIX_MODE 0x00000010 +#define E1000_GPIE_EIAME 0x40000000 +#define E1000_GPIE_PBA 0x80000000 /* Receive Descriptor bit definitions */ #define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ @@ -270,8 +272,10 @@ #define AUTONEG_ADVERTISE_SPEED_DEFAULT E1000_ALL_SPEED_DUPLEX /* LED Control */ -#define E1000_LEDCTL_LED0_MODE_SHIFT 0 -#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_IVRT 0x00000040 #define E1000_LEDCTL_MODE_LED_ON 0xE #define E1000_LEDCTL_MODE_LED_OFF 0xF diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 488abb24a54..94d7866b9c2 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -528,6 +528,8 @@ struct e1000_dev_spec_82575 { bool global_device_reset; bool eee_disable; bool clear_semaphore_once; + struct e1000_sfp_flags eth_flags; + bool module_plugged; }; struct e1000_hw { diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index bfc08e05c90..5caa332e755 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -82,11 +82,11 @@ enum E1000_INVM_STRUCTURE_TYPE { #define E1000_INVM_MAJOR_SHIFT 4 #define ID_LED_DEFAULT_I210 ((ID_LED_OFF1_ON2 << 8) | \ - (ID_LED_OFF1_OFF2 << 4) | \ - (ID_LED_DEF1_DEF2)) + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_OFF2)) #define ID_LED_DEFAULT_I210_SERDES ((ID_LED_DEF1_DEF2 << 8) | \ (ID_LED_DEF1_DEF2 << 4) | \ - (ID_LED_DEF1_DEF2)) + (ID_LED_OFF1_ON2)) /* NVM offset defaults for i211 device */ #define NVM_INIT_CTRL_2_DEFAULT_I211 0X7243 diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 2559d70a232..bab556a47fc 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -1332,7 +1332,13 @@ s32 igb_id_led_init(struct e1000_hw *hw) u16 data, i, temp; const u16 led_mask = 0x0F; - ret_val = igb_valid_led_default(hw, &data); + /* i210 and i211 devices have different LED mechanism */ + if ((hw->mac.type == e1000_i210) || + (hw->mac.type == e1000_i211)) + ret_val = igb_valid_led_default_i210(hw, &data); + else + ret_val = igb_valid_led_default(hw, &data); + if (ret_val) goto out; @@ -1406,15 +1412,34 @@ s32 igb_blink_led(struct e1000_hw *hw) u32 ledctl_blink = 0; u32 i; - /* set the blink bit for each LED that's "on" (0x0E) - * in ledctl_mode2 - */ - ledctl_blink = hw->mac.ledctl_mode2; - for (i = 0; i < 4; i++) - if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) == - E1000_LEDCTL_MODE_LED_ON) - ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << - (i * 8)); + if (hw->phy.media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* Set the blink bit for each LED that's "on" (0x0E) + * (or "off" if inverted) in ledctl_mode2. The blink + * logic in hardware only works when mode is set to "on" + * so it must be changed accordingly when the mode is + * "off" and inverted. + */ + ledctl_blink = hw->mac.ledctl_mode2; + for (i = 0; i < 32; i += 8) { + u32 mode = (hw->mac.ledctl_mode2 >> i) & + E1000_LEDCTL_LED0_MODE_MASK; + u32 led_default = hw->mac.ledctl_default >> i; + + if ((!(led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_ON)) || + ((led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_OFF))) { + ledctl_blink &= + ~(E1000_LEDCTL_LED0_MODE_MASK << i); + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_MODE_LED_ON) << i; + } + } + } wr32(E1000_LEDCTL, ledctl_blink); diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 115b0da6e01..60461946f98 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -341,6 +341,130 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) } /** + * igb_read_sfp_data_byte - Reads SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to be read + * @data: read data buffer pointer + * + * Reads one byte from SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters + * access + **/ +s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + hw_dbg("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing with the + * EEPROM to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + + wr32(E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + data_local = rd32(E1000_I2CCMD); + if (data_local & E1000_I2CCMD_READY) + break; + } + if (!(data_local & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (data_local & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + *data = (u8) data_local & 0xFF; + + return 0; +} + +/** + * e1000_write_sfp_data_byte - Writes SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to write to + * @data: data to write + * + * Writes one byte to SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters + * access + **/ +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + hw_dbg("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + /* The programming interface is 16 bits wide + * so we need to read the whole word first + * then update appropriate byte lane and write + * the updated word back. + */ + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing + * with an EEPROM to write the data given. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + /* Set a command to read single word */ + wr32(E1000_I2CCMD, i2ccmd); + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + /* Poll the ready bit to see if lastly + * launched I2C operation completed + */ + i2ccmd = rd32(E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) { + /* Check if this is READ or WRITE phase */ + if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) == + E1000_I2CCMD_OPCODE_READ) { + /* Write the selected byte + * lane and update whole word + */ + data_local = i2ccmd & 0xFF00; + data_local |= data; + i2ccmd = ((offset << + E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | data_local); + wr32(E1000_I2CCMD, i2ccmd); + } else { + break; + } + } + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + return 0; +} + +/** * igb_read_phy_reg_igp - Read igp PHY register * @hw: pointer to the HW structure * @offset: register offset to be read @@ -2014,7 +2138,7 @@ out: * Verify the reset block is not blocking us from resetting. Acquire * semaphore (if necessary) and read/set/write the device control reset * bit in the PHY. Wait the appropriate delay time for the device to - * reset and relase the semaphore (if necessary). + * reset and release the semaphore (if necessary). **/ s32 igb_phy_hw_reset(struct e1000_hw *hw) { diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index 784fd1c4098..6a0873f2095 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -69,6 +69,8 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data); +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data); s32 igb_copper_link_setup_82580(struct e1000_hw *hw); s32 igb_get_phy_info_82580(struct e1000_hw *hw); s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw); @@ -157,4 +159,22 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw); #define GS40G_CS_POWER_DOWN 0x0002 #define GS40G_LINE_LB 0x4000 +/* SFP modules ID memory locations */ +#define E1000_SFF_IDENTIFIER_OFFSET 0x00 +#define E1000_SFF_IDENTIFIER_SFF 0x02 +#define E1000_SFF_IDENTIFIER_SFP 0x03 + +#define E1000_SFF_ETH_FLAGS_OFFSET 0x06 +/* Flags for SFP modules compatible with ETH up to 1Gb */ +struct e1000_sfp_flags { + u8 e1000_base_sx:1; + u8 e1000_base_lx:1; + u8 e1000_base_cx:1; + u8 e1000_base_t:1; + u8 e100_base_lx:1; + u8 e100_base_fx:1; + u8 e10_base_bx10:1; + u8 e10_base_px:1; +}; + #endif diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 9d6c075e232..15ea8dc9dad 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -322,11 +322,6 @@ static inline int igb_desc_unused(struct igb_ring *ring) return ring->count + ring->next_to_clean - ring->next_to_use - 1; } -struct igb_i2c_client_list { - struct i2c_client *client; - struct igb_i2c_client_list *next; -}; - #ifdef CONFIG_IGB_HWMON #define IGB_HWMON_TYPE_LOC 0 @@ -514,13 +509,18 @@ extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va, struct sk_buff *skb); -static inline void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector, +static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) { if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TS) && !igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) - igb_ptp_rx_rgtstamp(q_vector, skb); + igb_ptp_rx_rgtstamp(rx_ring->q_vector, skb); + + /* Update the last_rx_timestamp timer in order to enable watchdog check + * for error case of latched timestamp on a dropped packet. + */ + rx_ring->last_rx_timestamp = jiffies; } extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 7876240fa74..85fe7b52f43 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -142,6 +142,8 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags; u32 status; if (hw->phy.media_type == e1000_media_type_copper) { @@ -162,49 +164,26 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->advertising |= hw->phy.autoneg_advertised; } - if (hw->mac.autoneg != 1) - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); - - if (hw->fc.requested_mode == e1000_fc_full) - ecmd->advertising |= ADVERTISED_Pause; - else if (hw->fc.requested_mode == e1000_fc_rx_pause) - ecmd->advertising |= (ADVERTISED_Pause | - ADVERTISED_Asym_Pause); - else if (hw->fc.requested_mode == e1000_fc_tx_pause) - ecmd->advertising |= ADVERTISED_Asym_Pause; - else - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); - ecmd->port = PORT_TP; ecmd->phy_address = hw->phy.addr; ecmd->transceiver = XCVR_INTERNAL; } else { - ecmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full | - SUPPORTED_FIBRE | + ecmd->supported = (SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause); - if (hw->mac.type == e1000_i354) - ecmd->supported |= SUPPORTED_2500baseX_Full; - ecmd->advertising = ADVERTISED_FIBRE; - - switch (adapter->link_speed) { - case SPEED_2500: - ecmd->advertising = ADVERTISED_2500baseX_Full; - break; - case SPEED_1000: - ecmd->advertising = ADVERTISED_1000baseT_Full; - break; - case SPEED_100: - ecmd->advertising = ADVERTISED_100baseT_Full; - break; - default: - break; + if (hw->mac.type == e1000_i354) { + ecmd->supported |= SUPPORTED_2500baseX_Full; + ecmd->advertising |= ADVERTISED_2500baseX_Full; + } + if ((eth_flags->e1000_base_lx) || (eth_flags->e1000_base_sx)) { + ecmd->supported |= SUPPORTED_1000baseT_Full; + ecmd->advertising |= ADVERTISED_1000baseT_Full; + } + if (eth_flags->e100_base_fx) { + ecmd->supported |= SUPPORTED_100baseT_Full; + ecmd->advertising |= ADVERTISED_100baseT_Full; } - if (hw->mac.autoneg == 1) ecmd->advertising |= ADVERTISED_Autoneg; @@ -212,6 +191,21 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->transceiver = XCVR_EXTERNAL; } + if (hw->mac.autoneg != 1) + ecmd->advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + + if (hw->fc.requested_mode == e1000_fc_full) + ecmd->advertising |= ADVERTISED_Pause; + else if (hw->fc.requested_mode == e1000_fc_rx_pause) + ecmd->advertising |= (ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + else if (hw->fc.requested_mode == e1000_fc_tx_pause) + ecmd->advertising |= ADVERTISED_Asym_Pause; + else + ecmd->advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + status = rd32(E1000_STATUS); if (status & E1000_STATUS_LU) { @@ -392,6 +386,10 @@ static int igb_set_pauseparam(struct net_device *netdev, struct e1000_hw *hw = &adapter->hw; int retval = 0; + /* 100basefx does not support setting link flow control */ + if (hw->dev_spec._82575.eth_flags.e100_base_fx) + return -EINVAL; + adapter->fc_autoneg = pause->autoneg; while (test_and_set_bit(__IGB_RESETTING, &adapter->state)) @@ -813,10 +811,8 @@ static int igb_set_eeprom(struct net_device *netdev, ret_val = hw->nvm.ops.write(hw, first_word, last_word - first_word + 1, eeprom_buff); - /* Update the checksum over the first part of the EEPROM if needed - * and flush shadow RAM for 82573 controllers - */ - if ((ret_val == 0) && ((first_word <= NVM_CHECKSUM_REG))) + /* Update the checksum if nvm write succeeded */ + if (ret_val == 0) hw->nvm.ops.update(hw); igb_set_fw_version(adapter); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 64cbe0dfe04..6a0c1b66ce5 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1667,10 +1667,13 @@ void igb_down(struct igb_adapter *adapter) wrfl(); msleep(10); - for (i = 0; i < adapter->num_q_vectors; i++) + igb_irq_disable(adapter); + + for (i = 0; i < adapter->num_q_vectors; i++) { + napi_synchronize(&(adapter->q_vector[i]->napi)); napi_disable(&(adapter->q_vector[i]->napi)); + } - igb_irq_disable(adapter); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); @@ -6622,7 +6625,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring, igb_rx_checksum(rx_ring, rx_desc, skb); - igb_ptp_rx_hwtstamp(rx_ring->q_vector, rx_desc, skb); + igb_ptp_rx_hwtstamp(rx_ring, rx_desc, skb); if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) && igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index ca932387a80..fb098b46c6a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -52,6 +52,11 @@ #include <linux/dca.h> #endif +#include <net/ll_poll.h> + +#ifdef CONFIG_NET_LL_RX_POLL +#define LL_EXTENDED_STATS +#endif /* common prefix used by pr_<> macros */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -182,6 +187,11 @@ struct ixgbe_rx_buffer { struct ixgbe_queue_stats { u64 packets; u64 bytes; +#ifdef LL_EXTENDED_STATS + u64 yields; + u64 misses; + u64 cleaned; +#endif /* LL_EXTENDED_STATS */ }; struct ixgbe_tx_queue_stats { @@ -356,9 +366,133 @@ struct ixgbe_q_vector { struct rcu_head rcu; /* to avoid race with update stats on free */ char name[IFNAMSIZ + 9]; +#ifdef CONFIG_NET_LL_RX_POLL + unsigned int state; +#define IXGBE_QV_STATE_IDLE 0 +#define IXGBE_QV_STATE_NAPI 1 /* NAPI owns this QV */ +#define IXGBE_QV_STATE_POLL 2 /* poll owns this QV */ +#define IXGBE_QV_LOCKED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL) +#define IXGBE_QV_STATE_NAPI_YIELD 4 /* NAPI yielded this QV */ +#define IXGBE_QV_STATE_POLL_YIELD 8 /* poll yielded this QV */ +#define IXGBE_QV_YIELD (IXGBE_QV_STATE_NAPI_YIELD | IXGBE_QV_STATE_POLL_YIELD) +#define IXGBE_QV_USER_PEND (IXGBE_QV_STATE_POLL | IXGBE_QV_STATE_POLL_YIELD) + spinlock_t lock; +#endif /* CONFIG_NET_LL_RX_POLL */ + /* for dynamic allocation of rings associated with this q_vector */ struct ixgbe_ring ring[0] ____cacheline_internodealigned_in_smp; }; +#ifdef CONFIG_NET_LL_RX_POLL +static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector) +{ + + spin_lock_init(&q_vector->lock); + q_vector->state = IXGBE_QV_STATE_IDLE; +} + +/* called from the device poll routine to get ownership of a q_vector */ +static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) +{ + int rc = true; + spin_lock(&q_vector->lock); + if (q_vector->state & IXGBE_QV_LOCKED) { + WARN_ON(q_vector->state & IXGBE_QV_STATE_NAPI); + q_vector->state |= IXGBE_QV_STATE_NAPI_YIELD; + rc = false; +#ifdef LL_EXTENDED_STATS + q_vector->tx.ring->stats.yields++; +#endif + } else + /* we don't care if someone yielded */ + q_vector->state = IXGBE_QV_STATE_NAPI; + spin_unlock(&q_vector->lock); + return rc; +} + +/* returns true is someone tried to get the qv while napi had it */ +static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector) +{ + int rc = false; + spin_lock(&q_vector->lock); + WARN_ON(q_vector->state & (IXGBE_QV_STATE_POLL | + IXGBE_QV_STATE_NAPI_YIELD)); + + if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD) + rc = true; + q_vector->state = IXGBE_QV_STATE_IDLE; + spin_unlock(&q_vector->lock); + return rc; +} + +/* called from ixgbe_low_latency_poll() */ +static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector) +{ + int rc = true; + spin_lock_bh(&q_vector->lock); + if ((q_vector->state & IXGBE_QV_LOCKED)) { + q_vector->state |= IXGBE_QV_STATE_POLL_YIELD; + rc = false; +#ifdef LL_EXTENDED_STATS + q_vector->rx.ring->stats.yields++; +#endif + } else + /* preserve yield marks */ + q_vector->state |= IXGBE_QV_STATE_POLL; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* returns true if someone tried to get the qv while it was locked */ +static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector) +{ + int rc = false; + spin_lock_bh(&q_vector->lock); + WARN_ON(q_vector->state & (IXGBE_QV_STATE_NAPI)); + + if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD) + rc = true; + q_vector->state = IXGBE_QV_STATE_IDLE; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* true if a socket is polling, even if it did not get the lock */ +static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector) +{ + WARN_ON(!(q_vector->state & IXGBE_QV_LOCKED)); + return q_vector->state & IXGBE_QV_USER_PEND; +} +#else /* CONFIG_NET_LL_RX_POLL */ +static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector) +{ +} + +static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) +{ + return true; +} + +static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector) +{ + return false; +} + +static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector) +{ + return false; +} + +static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector) +{ + return false; +} + +static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector) +{ + return false; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + #ifdef CONFIG_IXGBE_HWMON #define IXGBE_HWMON_TYPE_LOC 0 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index 1f2c805684d..e055e000131 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -380,3 +380,26 @@ s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, } return 0; } + +static void ixgbe_dcb_read_rtrup2tc_82599(struct ixgbe_hw *hw, u8 *map) +{ + u32 reg, i; + + reg = IXGBE_READ_REG(hw, IXGBE_RTRUP2TC); + for (i = 0; i < MAX_USER_PRIORITY; i++) + map[i] = IXGBE_RTRUP2TC_UP_MASK & + (reg >> (i * IXGBE_RTRUP2TC_UP_SHIFT)); + return; +} + +void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map) +{ + switch (hw->mac.type) { + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + ixgbe_dcb_read_rtrup2tc_82599(hw, map); + break; + default: + break; + } +} diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h index 1634de8b627..fc0a2dd5249 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h @@ -159,6 +159,8 @@ s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max, s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *tc_prio); s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *); +void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map); + /* DCB definitions for credit calculation */ #define DCB_CREDIT_QUANTUM 64 /* DCB Quantum */ #define MAX_CREDIT_REFILL 511 /* 0x1FF * 64B = 32704B */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h index a4ef07631d1..d71d9ce3e39 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h @@ -45,6 +45,7 @@ /* Receive UP2TC mapping */ #define IXGBE_RTRUP2TC_UP_SHIFT 3 +#define IXGBE_RTRUP2TC_UP_MASK 7 /* Transmit UP2TC mapping */ #define IXGBE_RTTUP2TC_UP_SHIFT 3 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index f3d68f9696b..edd89a1ef27 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -554,6 +554,9 @@ static int ixgbe_dcbnl_ieee_setets(struct net_device *dev, for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) adapter->ixgbe_ieee_ets->prio_tc[i] = IEEE_8021QAZ_MAX_TCS; + /* if possible update UP2TC mappings from HW */ + ixgbe_dcb_read_rtrup2tc(&adapter->hw, + adapter->ixgbe_ieee_ets->prio_tc); } for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index d3754722adb..24e2e7aafda 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1054,6 +1054,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = 0; data[i+1] = 0; i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = 0; + data[i+1] = 0; + data[i+2] = 0; + i += 3; +#endif continue; } @@ -1063,6 +1069,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i+1] = ring->stats.bytes; } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = ring->stats.yields; + data[i+1] = ring->stats.misses; + data[i+2] = ring->stats.cleaned; + i += 3; +#endif } for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { ring = adapter->rx_ring[j]; @@ -1070,6 +1082,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = 0; data[i+1] = 0; i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = 0; + data[i+1] = 0; + data[i+2] = 0; + i += 3; +#endif continue; } @@ -1079,6 +1097,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i+1] = ring->stats.bytes; } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = ring->stats.yields; + data[i+1] = ring->stats.misses; + data[i+2] = ring->stats.cleaned; + i += 3; +#endif } for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) { @@ -1115,12 +1139,28 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, p += ETH_GSTRING_LEN; sprintf(p, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; +#ifdef LL_EXTENDED_STATS + sprintf(p, "tx_q_%u_napi_yield", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_q_%u_misses", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_q_%u_cleaned", i); + p += ETH_GSTRING_LEN; +#endif /* LL_EXTENDED_STATS */ } for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) { sprintf(p, "rx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "rx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; +#ifdef LL_EXTENDED_STATS + sprintf(p, "rx_q_%u_ll_poll_yield", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_q_%u_misses", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_q_%u_cleaned", i); + p += ETH_GSTRING_LEN; +#endif /* LL_EXTENDED_STATS */ } for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { sprintf(p, "tx_pb_%u_pxon", i); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index ef5f7a678ce..90b4e1089ec 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -811,6 +811,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, /* initialize NAPI */ netif_napi_add(adapter->netdev, &q_vector->napi, ixgbe_poll, 64); + napi_hash_add(&q_vector->napi); /* tie q_vector and adapter together */ adapter->q_vector[v_idx] = q_vector; @@ -931,6 +932,7 @@ static void ixgbe_free_q_vector(struct ixgbe_adapter *adapter, int v_idx) adapter->rx_ring[ring->queue_index] = NULL; adapter->q_vector[v_idx] = NULL; + napi_hash_del(&q_vector->napi); netif_napi_del(&q_vector->napi); /* diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d30fbdd81fc..047ebaaf014 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1504,7 +1504,9 @@ static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector, { struct ixgbe_adapter *adapter = q_vector->adapter; - if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) + if (ixgbe_qv_ll_polling(q_vector)) + netif_receive_skb(skb); + else if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) napi_gro_receive(&q_vector->napi, skb); else netif_rx(skb); @@ -1892,9 +1894,9 @@ dma_sync: * expensive overhead for IOMMU access this provides a means of avoiding * it by maintaining the mapping of the page to the syste. * - * Returns true if all work is completed without reaching budget + * Returns amount of work completed **/ -static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, +static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, struct ixgbe_ring *rx_ring, const int budget) { @@ -1976,6 +1978,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, } #endif /* IXGBE_FCOE */ + skb_mark_ll(skb, &q_vector->napi); ixgbe_rx_skb(q_vector, skb); /* update budget accounting */ @@ -1992,9 +1995,43 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, if (cleaned_count) ixgbe_alloc_rx_buffers(rx_ring, cleaned_count); - return (total_rx_packets < budget); + return total_rx_packets; } +#ifdef CONFIG_NET_LL_RX_POLL +/* must be called with local_bh_disable()d */ +static int ixgbe_low_latency_recv(struct napi_struct *napi) +{ + struct ixgbe_q_vector *q_vector = + container_of(napi, struct ixgbe_q_vector, napi); + struct ixgbe_adapter *adapter = q_vector->adapter; + struct ixgbe_ring *ring; + int found = 0; + + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return LL_FLUSH_FAILED; + + if (!ixgbe_qv_lock_poll(q_vector)) + return LL_FLUSH_BUSY; + + ixgbe_for_each_ring(ring, q_vector->rx) { + found = ixgbe_clean_rx_irq(q_vector, ring, 4); +#ifdef LL_EXTENDED_STATS + if (found) + ring->stats.cleaned += found; + else + ring->stats.misses++; +#endif + if (found) + break; + } + + ixgbe_qv_unlock_poll(q_vector); + + return found; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + /** * ixgbe_configure_msix - Configure MSI-X hardware * @adapter: board private structure @@ -2550,6 +2587,9 @@ int ixgbe_poll(struct napi_struct *napi, int budget) ixgbe_for_each_ring(ring, q_vector->tx) clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring); + if (!ixgbe_qv_lock_napi(q_vector)) + return budget; + /* attempt to distribute budget to each queue fairly, but don't allow * the budget to go below 1 because we'll exit polling */ if (q_vector->rx.count > 1) @@ -2558,9 +2598,10 @@ int ixgbe_poll(struct napi_struct *napi, int budget) per_ring_budget = budget; ixgbe_for_each_ring(ring, q_vector->rx) - clean_complete &= ixgbe_clean_rx_irq(q_vector, ring, - per_ring_budget); + clean_complete &= (ixgbe_clean_rx_irq(q_vector, ring, + per_ring_budget) < per_ring_budget); + ixgbe_qv_unlock_napi(q_vector); /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; @@ -3747,16 +3788,25 @@ static void ixgbe_napi_enable_all(struct ixgbe_adapter *adapter) { int q_idx; - for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { + ixgbe_qv_init_lock(adapter->q_vector[q_idx]); napi_enable(&adapter->q_vector[q_idx]->napi); + } } static void ixgbe_napi_disable_all(struct ixgbe_adapter *adapter) { int q_idx; - for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) + local_bh_disable(); /* for ixgbe_qv_lock_napi() */ + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { napi_disable(&adapter->q_vector[q_idx]->napi); + while (!ixgbe_qv_lock_napi(adapter->q_vector[q_idx])) { + pr_info("QV %d locked\n", q_idx); + mdelay(1); + } + } + local_bh_enable(); } #ifdef CONFIG_IXGBE_DCB @@ -7177,6 +7227,9 @@ static const struct net_device_ops ixgbe_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ixgbe_netpoll, #endif +#ifdef CONFIG_NET_LL_RX_POLL + .ndo_ll_poll = ixgbe_low_latency_recv, +#endif #ifdef IXGBE_FCOE .ndo_fcoe_ddp_setup = ixgbe_fcoe_ddp_get, .ndo_fcoe_ddp_target = ixgbe_fcoe_ddp_target, diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 070a6f1a057..7fbe6abf605 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -3148,7 +3148,6 @@ jme_init_one(struct pci_dev *pdev, jme->mii_if.mdio_write = jme_mdio_write; jme_clear_pm(jme); - pci_set_power_state(jme->pdev, PCI_D0); device_set_wakeup_enable(&pdev->dev, true); jme_set_phyfifo_5level(jme); diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index 5409fe876a4..270e65f2110 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -483,7 +483,6 @@ static void korina_multicast_list(struct net_device *dev) unsigned long flags; struct netdev_hw_addr *ha; u32 recognise = ETH_ARC_AB; /* always accept broadcasts */ - int i; /* Set promiscuous mode */ if (dev->flags & IFF_PROMISC) @@ -495,12 +494,9 @@ static void korina_multicast_list(struct net_device *dev) /* Build the hash table */ if (netdev_mc_count(dev) > 4) { - u16 hash_table[4]; + u16 hash_table[4] = { 0 }; u32 crc; - for (i = 0; i < 4; i++) - hash_table[i] = 0; - netdev_for_each_mc_addr(ha, dev) { crc = ether_crc_le(6, ha->addr); crc >>= 26; @@ -1214,7 +1210,6 @@ static int korina_remove(struct platform_device *pdev) iounmap(lp->rx_dma_regs); iounmap(lp->tx_dma_regs); - platform_set_drvdata(pdev, NULL); unregister_netdev(bif->dev); free_netdev(bif->dev); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index d1cbfb12c1c..c35db735958 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -60,6 +60,10 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_net.h> +#include <linux/of_mdio.h> static char mv643xx_eth_driver_name[] = "mv643xx_eth"; static char mv643xx_eth_driver_version[] = "1.4"; @@ -115,6 +119,8 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define LINK_UP 0x00000002 #define TXQ_COMMAND 0x0048 #define TXQ_FIX_PRIO_CONF 0x004c +#define PORT_SERIAL_CONTROL1 0x004c +#define CLK125_BYPASS_EN 0x00000010 #define TX_BW_RATE 0x0050 #define TX_BW_MTU 0x0058 #define TX_BW_BURST 0x005c @@ -615,7 +621,7 @@ static int rxq_refill(struct rx_queue *rxq, int budget) rx_desc = rxq->rx_desc_area + rx; - size = skb->end - skb->data; + size = skb_end_pointer(skb) - skb->data; rx_desc->buf_ptr = dma_map_single(mp->dev->dev.parent, skb->data, size, DMA_FROM_DEVICE); @@ -2450,13 +2456,159 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) } } +#if defined(CONFIG_OF) +static const struct of_device_id mv643xx_eth_shared_ids[] = { + { .compatible = "marvell,orion-eth", }, + { .compatible = "marvell,kirkwood-eth", }, + { } +}; +MODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids); +#endif + +#if defined(CONFIG_OF) && !defined(CONFIG_MV64X60) +#define mv643xx_eth_property(_np, _name, _v) \ + do { \ + u32 tmp; \ + if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \ + _v = tmp; \ + } while (0) + +static struct platform_device *port_platdev[3]; + +static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, + struct device_node *pnp) +{ + struct platform_device *ppdev; + struct mv643xx_eth_platform_data ppd; + struct resource res; + const char *mac_addr; + int ret; + int dev_num = 0; + + memset(&ppd, 0, sizeof(ppd)); + ppd.shared = pdev; + + memset(&res, 0, sizeof(res)); + if (!of_irq_to_resource(pnp, 0, &res)) { + dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name); + return -EINVAL; + } + + if (of_property_read_u32(pnp, "reg", &ppd.port_number)) { + dev_err(&pdev->dev, "missing reg property on %s\n", pnp->name); + return -EINVAL; + } + + if (ppd.port_number >= 3) { + dev_err(&pdev->dev, "invalid reg property on %s\n", pnp->name); + return -EINVAL; + } + + while (dev_num < 3 && port_platdev[dev_num]) + dev_num++; + + if (dev_num == 3) { + dev_err(&pdev->dev, "too many ports registered\n"); + return -EINVAL; + } + + mac_addr = of_get_mac_address(pnp); + if (mac_addr) + memcpy(ppd.mac_addr, mac_addr, 6); + + mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size); + mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr); + mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size); + mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size); + mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr); + mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size); + + ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0); + if (!ppd.phy_node) { + ppd.phy_addr = MV643XX_ETH_PHY_NONE; + of_property_read_u32(pnp, "speed", &ppd.speed); + of_property_read_u32(pnp, "duplex", &ppd.duplex); + } + + ppdev = platform_device_alloc(MV643XX_ETH_NAME, dev_num); + if (!ppdev) + return -ENOMEM; + ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + + ret = platform_device_add_resources(ppdev, &res, 1); + if (ret) + goto port_err; + + ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); + if (ret) + goto port_err; + + ret = platform_device_add(ppdev); + if (ret) + goto port_err; + + port_platdev[dev_num] = ppdev; + + return 0; + +port_err: + platform_device_put(ppdev); + return ret; +} + +static int mv643xx_eth_shared_of_probe(struct platform_device *pdev) +{ + struct mv643xx_eth_shared_platform_data *pd; + struct device_node *pnp, *np = pdev->dev.of_node; + int ret; + + /* bail out if not registered from DT */ + if (!np) + return 0; + + pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + pdev->dev.platform_data = pd; + + mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit); + + for_each_available_child_of_node(np, pnp) { + ret = mv643xx_eth_shared_of_add_port(pdev, pnp); + if (ret) + return ret; + } + return 0; +} + +static void mv643xx_eth_shared_of_remove(void) +{ + int n; + + for (n = 0; n < 3; n++) { + platform_device_del(port_platdev[n]); + port_platdev[n] = NULL; + } +} +#else +static inline int mv643xx_eth_shared_of_probe(struct platform_device *pdev) +{ + return 0; +} + +static inline void mv643xx_eth_shared_of_remove(void) +{ +} +#endif + static int mv643xx_eth_shared_probe(struct platform_device *pdev) { static int mv643xx_eth_version_printed; - struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; + struct mv643xx_eth_shared_platform_data *pd; struct mv643xx_eth_shared_private *msp; const struct mbus_dram_target_info *dram; struct resource *res; + int ret; if (!mv643xx_eth_version_printed++) pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n", @@ -2469,8 +2621,9 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL); if (msp == NULL) return -ENOMEM; + platform_set_drvdata(pdev, msp); - msp->base = ioremap(res->start, resource_size(res)); + msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (msp->base == NULL) return -ENOMEM; @@ -2485,12 +2638,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (dram) mv643xx_eth_conf_mbus_windows(msp, dram); + ret = mv643xx_eth_shared_of_probe(pdev); + if (ret) + return ret; + pd = pdev->dev.platform_data; + msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ? pd->tx_csum_limit : 9 * 1024; infer_hw_params(msp); - platform_set_drvdata(pdev, msp); - return 0; } @@ -2498,10 +2654,9 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) { struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); - iounmap(msp->base); + mv643xx_eth_shared_of_remove(); if (!IS_ERR(msp->clk)) clk_disable_unprepare(msp->clk); - return 0; } @@ -2511,6 +2666,7 @@ static struct platform_driver mv643xx_eth_shared_driver = { .driver = { .name = MV643XX_ETH_SHARED_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mv643xx_eth_shared_ids), }, }; @@ -2701,6 +2857,15 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mp->dev = dev; + /* Kirkwood resets some registers on gated clocks. Especially + * CLK125_BYPASS_EN must be cleared but is not available on + * all other SoCs/System Controllers using this driver. + */ + if (of_device_is_compatible(pdev->dev.of_node, + "marvell,kirkwood-eth-port")) + wrlp(mp, PORT_SERIAL_CONTROL1, + rdlp(mp, PORT_SERIAL_CONTROL1) & ~CLK125_BYPASS_EN); + /* * Start with a default rate, and if there is a clock, allow * it to override the default. @@ -2710,23 +2875,35 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (!IS_ERR(mp->clk)) { clk_prepare_enable(mp->clk); mp->t_clk = clk_get_rate(mp->clk); + } else if (!IS_ERR(mp->shared->clk)) { + mp->t_clk = clk_get_rate(mp->shared->clk); } set_params(mp, pd); netif_set_real_num_tx_queues(dev, mp->txq_count); netif_set_real_num_rx_queues(dev, mp->rxq_count); - if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { + err = 0; + if (pd->phy_node) { + mp->phy = of_phy_connect(mp->dev, pd->phy_node, + mv643xx_eth_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); + if (!mp->phy) + err = -ENODEV; + } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { mp->phy = phy_scan(mp, pd->phy_addr); - if (IS_ERR(mp->phy)) { + if (IS_ERR(mp->phy)) err = PTR_ERR(mp->phy); - if (err == -ENODEV) - err = -EPROBE_DEFER; - goto out; - } - phy_init(mp, pd->speed, pd->duplex); + else + phy_init(mp, pd->speed, pd->duplex); } + if (err == -ENODEV) { + err = -EPROBE_DEFER; + goto out; + } + if (err) + goto out; SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); @@ -2805,7 +2982,7 @@ static int mv643xx_eth_remove(struct platform_device *pdev) unregister_netdev(mp->dev); if (mp->phy != NULL) - phy_detach(mp->phy); + phy_disconnect(mp->phy); cancel_work_sync(&mp->tx_timeout_task); if (!IS_ERR(mp->clk)) @@ -2813,8 +2990,6 @@ static int mv643xx_eth_remove(struct platform_device *pdev) free_netdev(mp->dev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index c9667855523..712779fb12b 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2251,6 +2251,21 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu) return 0; } +/* Get mac address */ +static void mvneta_get_mac_addr(struct mvneta_port *pp, unsigned char *addr) +{ + u32 mac_addr_l, mac_addr_h; + + mac_addr_l = mvreg_read(pp, MVNETA_MAC_ADDR_LOW); + mac_addr_h = mvreg_read(pp, MVNETA_MAC_ADDR_HIGH); + addr[0] = (mac_addr_h >> 24) & 0xFF; + addr[1] = (mac_addr_h >> 16) & 0xFF; + addr[2] = (mac_addr_h >> 8) & 0xFF; + addr[3] = mac_addr_h & 0xFF; + addr[4] = (mac_addr_l >> 8) & 0xFF; + addr[5] = mac_addr_l & 0xFF; +} + /* Handle setting mac address */ static int mvneta_set_mac_addr(struct net_device *dev, void *addr) { @@ -2667,7 +2682,9 @@ static int mvneta_probe(struct platform_device *pdev) u32 phy_addr; struct mvneta_port *pp; struct net_device *dev; - const char *mac_addr; + const char *dt_mac_addr; + char hw_mac_addr[ETH_ALEN]; + const char *mac_from; int phy_mode; int err; @@ -2703,13 +2720,6 @@ static int mvneta_probe(struct platform_device *pdev) goto err_free_irq; } - mac_addr = of_get_mac_address(dn); - - if (!mac_addr || !is_valid_ether_addr(mac_addr)) - eth_hw_addr_random(dev); - else - memcpy(dev->dev_addr, mac_addr, ETH_ALEN); - dev->tx_queue_len = MVNETA_MAX_TXD; dev->watchdog_timeo = 5 * HZ; dev->netdev_ops = &mvneta_netdev_ops; @@ -2740,6 +2750,21 @@ static int mvneta_probe(struct platform_device *pdev) clk_prepare_enable(pp->clk); + dt_mac_addr = of_get_mac_address(dn); + if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { + mac_from = "device tree"; + memcpy(dev->dev_addr, dt_mac_addr, ETH_ALEN); + } else { + mvneta_get_mac_addr(pp, hw_mac_addr); + if (is_valid_ether_addr(hw_mac_addr)) { + mac_from = "hardware"; + memcpy(dev->dev_addr, hw_mac_addr, ETH_ALEN); + } else { + mac_from = "random"; + eth_hw_addr_random(dev); + } + } + pp->tx_done_timer.data = (unsigned long)dev; pp->tx_ring_size = MVNETA_MAX_TXD; @@ -2772,7 +2797,8 @@ static int mvneta_probe(struct platform_device *pdev) goto err_deinit; } - netdev_info(dev, "mac: %pM\n", dev->dev_addr); + netdev_info(dev, "Using %s mac address %pM\n", mac_from, + dev->dev_addr); platform_set_drvdata(pdev, pp->dev); @@ -2804,8 +2830,6 @@ static int mvneta_remove(struct platform_device *pdev) irq_dispose_mapping(dev->irq); free_netdev(dev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 1c8af8ba08d..db481477bcc 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -357,7 +357,7 @@ static void rxq_refill(struct net_device *dev) /* Get 'used' Rx descriptor */ used_rx_desc = pep->rx_used_desc_q; p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc]; - size = skb->end - skb->data; + size = skb_end_pointer(skb) - skb->data; p_used_rx_desc->buf_ptr = dma_map_single(NULL, skb->data, size, @@ -1602,7 +1602,6 @@ static int pxa168_eth_remove(struct platform_device *pdev) unregister_netdev(dev); cancel_work_sync(&pep->tx_timeout_task); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 171f4b3dda0..c896079728e 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -3706,7 +3706,7 @@ static const struct file_operations skge_debug_fops = { static int skge_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct skge_port *skge; struct dentry *d; diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index d175bbd3ffd..e09a8c6f853 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4642,7 +4642,7 @@ static const struct file_operations sky2_debug_fops = { static int sky2_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct sky2_port *sky2 = netdev_priv(dev); if (dev->netdev_ops->ndo_open != sky2_open || !sky2_debug) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 0e572a52715..299d0184f98 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -39,6 +39,7 @@ #include <linux/errno.h> #include <linux/mlx4/cmd.h> +#include <linux/mlx4/device.h> #include <linux/semaphore.h> #include <rdma/ib_smi.h> @@ -111,6 +112,14 @@ enum { GO_BIT_TIMEOUT_MSECS = 10000 }; +enum mlx4_vlan_transition { + MLX4_VLAN_TRANSITION_VST_VST = 0, + MLX4_VLAN_TRANSITION_VST_VGT = 1, + MLX4_VLAN_TRANSITION_VGT_VST = 2, + MLX4_VLAN_TRANSITION_VGT_VGT = 3, +}; + + struct mlx4_cmd_context { struct completion done; int result; @@ -256,6 +265,8 @@ static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op, if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { + mlx4_warn(dev, "communication channel command 0x%x timed out\n", + op); err = -EBUSY; goto out; } @@ -485,6 +496,8 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, } if (cmd_pending(dev)) { + mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n", + op); err = -ETIMEDOUT; goto out; } @@ -548,6 +561,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { + mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n", + op); err = -EBUSY; goto out; } @@ -785,6 +800,15 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave, vhcr->op, MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE); } +int MLX4_CMD_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return -EPERM; +} + int mlx4_DMA_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, @@ -1219,6 +1243,15 @@ static struct mlx4_cmd_info cmd_info[] = { .wrapper = mlx4_GEN_QP_wrapper }, { + .opcode = MLX4_CMD_UPDATE_QP, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = MLX4_CMD_UPDATE_QP_wrapper + }, + { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, .has_outbox = false, @@ -1488,6 +1521,102 @@ out: return ret; } +static int calculate_transition(u16 oper_vlan, u16 admin_vlan) +{ + return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT)); +} + +int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, + int slave, int port) +{ + struct mlx4_vport_oper_state *vp_oper; + struct mlx4_vport_state *vp_admin; + struct mlx4_vf_immed_vlan_work *work; + struct mlx4_dev *dev = &(priv->dev); + int err; + int admin_vlan_ix = NO_INDX; + enum mlx4_vlan_transition vlan_trans; + + vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; + vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + + if (vp_oper->state.default_vlan == vp_admin->default_vlan && + vp_oper->state.default_qos == vp_admin->default_qos && + vp_oper->state.link_state == vp_admin->link_state) + return 0; + + vlan_trans = calculate_transition(vp_oper->state.default_vlan, + vp_admin->default_vlan); + + if (!(priv->mfunc.master.slave_state[slave].active && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP && + vlan_trans == MLX4_VLAN_TRANSITION_VST_VST)) { + /* even if the UPDATE_QP command isn't supported, we still want + * to set this VF link according to the admin directive + */ + vp_oper->state.link_state = vp_admin->link_state; + return -1; + } + + mlx4_dbg(dev, "updating immediately admin params slave %d port %d\n", + slave, port); + mlx4_dbg(dev, "vlan %d QoS %d link down %d\n", vp_admin->default_vlan, + vp_admin->default_qos, vp_admin->link_state); + + work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!work) + return -ENOMEM; + + if (vp_oper->state.default_vlan != vp_admin->default_vlan) { + err = __mlx4_register_vlan(&priv->dev, port, + vp_admin->default_vlan, + &admin_vlan_ix); + if (err) { + kfree(work); + mlx4_warn((&priv->dev), + "No vlan resources slave %d, port %d\n", + slave, port); + return err; + } + work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN; + mlx4_dbg((&(priv->dev)), + "alloc vlan %d idx %d slave %d port %d\n", + (int)(vp_admin->default_vlan), + admin_vlan_ix, slave, port); + } + + /* save original vlan ix and vlan id */ + work->orig_vlan_id = vp_oper->state.default_vlan; + work->orig_vlan_ix = vp_oper->vlan_idx; + + /* handle new qos */ + if (vp_oper->state.default_qos != vp_admin->default_qos) + work->flags |= MLX4_VF_IMMED_VLAN_FLAG_QOS; + + if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN) + vp_oper->vlan_idx = admin_vlan_ix; + + vp_oper->state.default_vlan = vp_admin->default_vlan; + vp_oper->state.default_qos = vp_admin->default_qos; + vp_oper->state.link_state = vp_admin->link_state; + + if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE) + work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE; + + /* iterate over QPs owned by this slave, using UPDATE_QP */ + work->port = port; + work->slave = slave; + work->qos = vp_oper->state.default_qos; + work->vlan_id = vp_oper->state.default_vlan; + work->vlan_ix = vp_oper->vlan_idx; + work->priv = priv; + INIT_WORK(&work->work, mlx4_vf_immed_vlan_work_handler); + queue_work(priv->mfunc.master.comm_wq, &work->work); + + return 0; +} + + static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave) { int port, err; @@ -2102,10 +2231,12 @@ int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) } EXPORT_SYMBOL_GPL(mlx4_set_vf_mac); + int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_vport_state *s_info; + struct mlx4_vport_oper_state *vf_oper; + struct mlx4_vport_state *vf_admin; int slave; if ((!mlx4_is_master(dev)) || @@ -2119,12 +2250,19 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) if (slave < 0) return -EINVAL; - s_info = &priv->mfunc.master.vf_admin[slave].vport[port]; + vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + vf_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; + if ((0 == vlan) && (0 == qos)) - s_info->default_vlan = MLX4_VGT; + vf_admin->default_vlan = MLX4_VGT; else - s_info->default_vlan = vlan; - s_info->default_qos = qos; + vf_admin->default_vlan = vlan; + vf_admin->default_qos = qos; + + if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port)) + mlx4_info(dev, + "updating vf %d port %d config will take effect on next VF restart\n", + vf, port); return 0; } EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan); @@ -2178,7 +2316,55 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in ivf->qos = s_info->default_qos; ivf->tx_rate = s_info->tx_rate; ivf->spoofchk = s_info->spoofchk; + ivf->linkstate = s_info->link_state; return 0; } EXPORT_SYMBOL_GPL(mlx4_get_vf_config); + +int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_state) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_vport_state *s_info; + int slave; + u8 link_stat_event; + + slave = mlx4_get_slave_indx(dev, vf); + if (slave < 0) + return -EINVAL; + + switch (link_state) { + case IFLA_VF_LINK_STATE_AUTO: + /* get current link state */ + if (!priv->sense.do_sense_port[port]) + link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_ACTIVE; + else + link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_DOWN; + break; + + case IFLA_VF_LINK_STATE_ENABLE: + link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_ACTIVE; + break; + + case IFLA_VF_LINK_STATE_DISABLE: + link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_DOWN; + break; + + default: + mlx4_warn(dev, "unknown value for link_state %02x on slave %d port %d\n", + link_state, slave, port); + return -EINVAL; + }; + s_info = &priv->mfunc.master.vf_admin[slave].vport[port]; + s_info->link_state = link_state; + + /* send event */ + mlx4_gen_port_state_change_eqe(dev, slave, port, link_stat_event); + + if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port)) + mlx4_dbg(dev, + "updating vf %d port %d no link state HW enforcment\n", + vf, port); + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_set_vf_link_state); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 1e6c594d6d0..3e2d5047cdb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -139,6 +139,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, if (!cq->is_tx) { netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64); + napi_hash_add(&cq->napi); napi_enable(&cq->napi); } @@ -162,6 +163,8 @@ void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) { if (!cq->is_tx) { napi_disable(&cq->napi); + napi_hash_del(&cq->napi); + synchronize_rcu(); netif_napi_del(&cq->napi); } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index 0f91222ea3d..9d4a1ea030d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -207,9 +207,6 @@ static int mlx4_en_dcbnl_ieee_getmaxrate(struct net_device *dev, struct mlx4_en_priv *priv = netdev_priv(dev); int i; - if (!priv->maxrate) - return -EINVAL; - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) maxrate->tc_maxrate[i] = priv->maxrate[i] * MLX4_RATELIMIT_UNITS_IN_KB; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index c9e6b62dd00..727874f575c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -222,7 +222,12 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset) switch (sset) { case ETH_SS_STATS: return (priv->stats_bitmap ? bit_count : NUM_ALL_STATS) + - (priv->tx_ring_num + priv->rx_ring_num) * 2; + (priv->tx_ring_num * 2) + +#ifdef CONFIG_NET_LL_RX_POLL + (priv->rx_ring_num * 5); +#else + (priv->rx_ring_num * 2); +#endif case ETH_SS_TEST: return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2; @@ -271,6 +276,11 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, for (i = 0; i < priv->rx_ring_num; i++) { data[index++] = priv->rx_ring[i].packets; data[index++] = priv->rx_ring[i].bytes; +#ifdef CONFIG_NET_LL_RX_POLL + data[index++] = priv->rx_ring[i].yields; + data[index++] = priv->rx_ring[i].misses; + data[index++] = priv->rx_ring[i].cleaned; +#endif } spin_unlock_bh(&priv->stats_lock); @@ -334,6 +344,14 @@ static void mlx4_en_get_strings(struct net_device *dev, "rx%d_packets", i); sprintf(data + (index++) * ETH_GSTRING_LEN, "rx%d_bytes", i); +#ifdef CONFIG_NET_LL_RX_POLL + sprintf(data + (index++) * ETH_GSTRING_LEN, + "rx%d_napi_yield", i); + sprintf(data + (index++) * ETH_GSTRING_LEN, + "rx%d_misses", i); + sprintf(data + (index++) * ETH_GSTRING_LEN, + "rx%d_cleaned", i); +#endif } break; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index a5c9df07a7d..a071cda2dd0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -310,7 +310,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev) err_mr: (void) mlx4_mr_free(dev, &mdev->mr); err_map: - if (!mdev->uar_map) + if (mdev->uar_map) iounmap(mdev->uar_map); err_uar: mlx4_uar_free(dev, &mdev->priv_uar); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 89c47ea84b5..caf20477056 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -38,6 +38,7 @@ #include <linux/slab.h> #include <linux/hash.h> #include <net/ip.h> +#include <net/ll_poll.h> #include <linux/mlx4/driver.h> #include <linux/mlx4/device.h> @@ -67,6 +68,34 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up) return 0; } +#ifdef CONFIG_NET_LL_RX_POLL +/* must be called with local_bh_disable()d */ +static int mlx4_en_low_latency_recv(struct napi_struct *napi) +{ + struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); + struct net_device *dev = cq->dev; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring]; + int done; + + if (!priv->port_up) + return LL_FLUSH_FAILED; + + if (!mlx4_en_cq_lock_poll(cq)) + return LL_FLUSH_BUSY; + + done = mlx4_en_process_rx_cq(dev, cq, 4); + if (likely(done)) + rx_ring->cleaned += done; + else + rx_ring->misses++; + + mlx4_en_cq_unlock_poll(cq); + + return done; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + #ifdef CONFIG_RFS_ACCEL struct mlx4_en_filter { @@ -376,7 +405,7 @@ static int mlx4_en_vlan_rx_add_vid(struct net_device *dev, en_err(priv, "Failed configuring VLAN filter\n"); } if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx)) - en_err(priv, "failed adding vlan %d\n", vid); + en_dbg(HW, priv, "failed adding vlan %d\n", vid); mutex_unlock(&mdev->state_lock); return 0; @@ -399,7 +428,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev, if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx)) mlx4_unregister_vlan(mdev->dev, priv->port, idx); else - en_err(priv, "could not find vid %d in cache\n", vid); + en_dbg(HW, priv, "could not find vid %d in cache\n", vid); if (mdev->device_up && priv->port_up) { err = mlx4_SET_VLAN_FLTR(mdev->dev, priv); @@ -1207,10 +1236,19 @@ static void mlx4_en_tx_timeout(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; + int i; if (netif_msg_timer(priv)) en_warn(priv, "Tx timeout called on port:%d\n", priv->port); + for (i = 0; i < priv->tx_ring_num; i++) { + if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i))) + continue; + en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n", + i, priv->tx_ring[i].qpn, priv->tx_ring[i].cqn, + priv->tx_ring[i].cons, priv->tx_ring[i].prod); + } + priv->port_stats.tx_timeout++; en_dbg(DRV, priv, "Scheduling watchdog\n"); queue_work(mdev->workqueue, &priv->watchdog_task); @@ -1346,12 +1384,13 @@ static void mlx4_en_do_get_stats(struct work_struct *work) mutex_lock(&mdev->state_lock); if (mdev->device_up) { - err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0); - if (err) - en_dbg(HW, priv, "Could not update stats\n"); + if (priv->port_up) { + err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0); + if (err) + en_dbg(HW, priv, "Could not update stats\n"); - if (priv->port_up) mlx4_en_auto_moderation(priv); + } queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY); } @@ -1445,6 +1484,8 @@ int mlx4_en_start_port(struct net_device *dev) for (i = 0; i < priv->rx_ring_num; i++) { cq = &priv->rx_cq[i]; + mlx4_en_cq_init_lock(cq); + err = mlx4_en_activate_cq(priv, cq, i); if (err) { en_err(priv, "Failed activating Rx CQ\n"); @@ -1603,6 +1644,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) return; } + /* close port*/ + mlx4_CLOSE_PORT(mdev->dev, priv->port); + /* Synchronize with tx routine */ netif_tx_lock_bh(dev); if (detach) @@ -1694,14 +1738,20 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) /* Free RX Rings */ for (i = 0; i < priv->rx_ring_num; i++) { - mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); - while (test_bit(NAPI_STATE_SCHED, &priv->rx_cq[i].napi.state)) + struct mlx4_en_cq *cq = &priv->rx_cq[i]; + + local_bh_disable(); + while (!mlx4_en_cq_lock_napi(cq)) { + pr_info("CQ %d locked\n", i); + mdelay(1); + } + local_bh_enable(); + + while (test_bit(NAPI_STATE_SCHED, &cq->napi.state)) msleep(1); - mlx4_en_deactivate_cq(priv, &priv->rx_cq[i]); + mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); + mlx4_en_deactivate_cq(priv, cq); } - - /* close port*/ - mlx4_CLOSE_PORT(mdev->dev, priv->port); } static void mlx4_en_restart(struct work_struct *work) @@ -2061,6 +2111,13 @@ static int mlx4_en_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_ return mlx4_get_vf_config(mdev->dev, en_priv->port, vf, ivf); } +static int mlx4_en_set_vf_link_state(struct net_device *dev, int vf, int link_state) +{ + struct mlx4_en_priv *en_priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = en_priv->mdev; + + return mlx4_set_vf_link_state(mdev->dev, en_priv->port, vf, link_state); +} static const struct net_device_ops mlx4_netdev_ops = { .ndo_open = mlx4_en_open, .ndo_stop = mlx4_en_close, @@ -2083,6 +2140,9 @@ static const struct net_device_ops mlx4_netdev_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = mlx4_en_filter_rfs, #endif +#ifdef CONFIG_NET_LL_RX_POLL + .ndo_ll_poll = mlx4_en_low_latency_recv, +#endif }; static const struct net_device_ops mlx4_netdev_ops_master = { @@ -2101,6 +2161,7 @@ static const struct net_device_ops mlx4_netdev_ops_master = { .ndo_set_vf_mac = mlx4_en_set_vf_mac, .ndo_set_vf_vlan = mlx4_en_set_vf_vlan, .ndo_set_vf_spoofchk = mlx4_en_set_vf_spoofchk, + .ndo_set_vf_link_state = mlx4_en_set_vf_link_state, .ndo_get_vf_config = mlx4_en_get_vf_config, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = mlx4_en_netpoll, @@ -2271,6 +2332,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, mdev->pndev[port] = dev; netif_carrier_off(dev); + mlx4_en_set_default_moderation(priv); + err = register_netdev(dev); if (err) { en_err(priv, "Netdev registration failed for port %d\n", port); @@ -2302,7 +2365,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, en_err(priv, "Failed Initializing port\n"); goto out; } - mlx4_en_set_default_moderation(priv); queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY); if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 02aee1ebd20..76997b93fdf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -31,6 +31,7 @@ * */ +#include <net/ll_poll.h> #include <linux/mlx4/cq.h> #include <linux/slab.h> #include <linux/mlx4/qp.h> @@ -42,40 +43,64 @@ #include "mlx4_en.h" +static int mlx4_alloc_pages(struct mlx4_en_priv *priv, + struct mlx4_en_rx_alloc *page_alloc, + const struct mlx4_en_frag_info *frag_info, + gfp_t _gfp) +{ + int order; + struct page *page; + dma_addr_t dma; + + for (order = MLX4_EN_ALLOC_PREFER_ORDER; ;) { + gfp_t gfp = _gfp; + + if (order) + gfp |= __GFP_COMP | __GFP_NOWARN; + page = alloc_pages(gfp, order); + if (likely(page)) + break; + if (--order < 0 || + ((PAGE_SIZE << order) < frag_info->frag_size)) + return -ENOMEM; + } + dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE << order, + PCI_DMA_FROMDEVICE); + if (dma_mapping_error(priv->ddev, dma)) { + put_page(page); + return -ENOMEM; + } + page_alloc->size = PAGE_SIZE << order; + page_alloc->page = page; + page_alloc->dma = dma; + page_alloc->offset = frag_info->frag_align; + /* Not doing get_page() for each frag is a big win + * on asymetric workloads. + */ + atomic_set(&page->_count, page_alloc->size / frag_info->frag_stride); + return 0; +} + static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_desc *rx_desc, struct mlx4_en_rx_alloc *frags, - struct mlx4_en_rx_alloc *ring_alloc) + struct mlx4_en_rx_alloc *ring_alloc, + gfp_t gfp) { struct mlx4_en_rx_alloc page_alloc[MLX4_EN_MAX_RX_FRAGS]; - struct mlx4_en_frag_info *frag_info; + const struct mlx4_en_frag_info *frag_info; struct page *page; dma_addr_t dma; int i; for (i = 0; i < priv->num_frags; i++) { frag_info = &priv->frag_info[i]; - if (ring_alloc[i].offset == frag_info->last_offset) { - page = alloc_pages(GFP_ATOMIC | __GFP_COMP, - MLX4_EN_ALLOC_ORDER); - if (!page) - goto out; - dma = dma_map_page(priv->ddev, page, 0, - MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - if (dma_mapping_error(priv->ddev, dma)) { - put_page(page); - goto out; - } - page_alloc[i].page = page; - page_alloc[i].dma = dma; - page_alloc[i].offset = frag_info->frag_align; - } else { - page_alloc[i].page = ring_alloc[i].page; - get_page(ring_alloc[i].page); - page_alloc[i].dma = ring_alloc[i].dma; - page_alloc[i].offset = ring_alloc[i].offset + - frag_info->frag_stride; - } + page_alloc[i] = ring_alloc[i]; + page_alloc[i].offset += frag_info->frag_stride; + if (page_alloc[i].offset + frag_info->frag_stride <= ring_alloc[i].size) + continue; + if (mlx4_alloc_pages(priv, &page_alloc[i], frag_info, gfp)) + goto out; } for (i = 0; i < priv->num_frags; i++) { @@ -87,14 +112,16 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, return 0; - out: while (i--) { frag_info = &priv->frag_info[i]; - if (ring_alloc[i].offset == frag_info->last_offset) + if (page_alloc[i].page != ring_alloc[i].page) { dma_unmap_page(priv->ddev, page_alloc[i].dma, - MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - put_page(page_alloc[i].page); + page_alloc[i].size, PCI_DMA_FROMDEVICE); + page = page_alloc[i].page; + atomic_set(&page->_count, 1); + put_page(page); + } } return -ENOMEM; } @@ -103,12 +130,12 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv, struct mlx4_en_rx_alloc *frags, int i) { - struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; + const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; - if (frags[i].offset == frag_info->last_offset) { - dma_unmap_page(priv->ddev, frags[i].dma, MLX4_EN_ALLOC_SIZE, + if (frags[i].offset + frag_info->frag_stride > frags[i].size) + dma_unmap_page(priv->ddev, frags[i].dma, frags[i].size, PCI_DMA_FROMDEVICE); - } + if (frags[i].page) put_page(frags[i].page); } @@ -116,35 +143,28 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv, static int mlx4_en_init_allocator(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring) { - struct mlx4_en_rx_alloc *page_alloc; int i; + struct mlx4_en_rx_alloc *page_alloc; for (i = 0; i < priv->num_frags; i++) { - page_alloc = &ring->page_alloc[i]; - page_alloc->page = alloc_pages(GFP_ATOMIC | __GFP_COMP, - MLX4_EN_ALLOC_ORDER); - if (!page_alloc->page) - goto out; + const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; - page_alloc->dma = dma_map_page(priv->ddev, page_alloc->page, 0, - MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - if (dma_mapping_error(priv->ddev, page_alloc->dma)) { - put_page(page_alloc->page); - page_alloc->page = NULL; + if (mlx4_alloc_pages(priv, &ring->page_alloc[i], + frag_info, GFP_KERNEL)) goto out; - } - page_alloc->offset = priv->frag_info[i].frag_align; - en_dbg(DRV, priv, "Initialized allocator:%d with page:%p\n", - i, page_alloc->page); } return 0; out: while (i--) { + struct page *page; + page_alloc = &ring->page_alloc[i]; dma_unmap_page(priv->ddev, page_alloc->dma, - MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - put_page(page_alloc->page); + page_alloc->size, PCI_DMA_FROMDEVICE); + page = page_alloc->page; + atomic_set(&page->_count, 1); + put_page(page); page_alloc->page = NULL; } return -ENOMEM; @@ -157,13 +177,18 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv, int i; for (i = 0; i < priv->num_frags; i++) { + const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; + page_alloc = &ring->page_alloc[i]; en_dbg(DRV, priv, "Freeing allocator:%d count:%d\n", i, page_count(page_alloc->page)); dma_unmap_page(priv->ddev, page_alloc->dma, - MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE); - put_page(page_alloc->page); + page_alloc->size, PCI_DMA_FROMDEVICE); + while (page_alloc->offset + frag_info->frag_stride < page_alloc->size) { + put_page(page_alloc->page); + page_alloc->offset += frag_info->frag_stride; + } page_alloc->page = NULL; } } @@ -194,13 +219,14 @@ static void mlx4_en_init_rx_desc(struct mlx4_en_priv *priv, } static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, int index) + struct mlx4_en_rx_ring *ring, int index, + gfp_t gfp) { struct mlx4_en_rx_desc *rx_desc = ring->buf + (index * ring->stride); struct mlx4_en_rx_alloc *frags = ring->rx_info + (index << priv->log_rx_info); - return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc); + return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc, gfp); } static inline void mlx4_en_update_rx_prod_db(struct mlx4_en_rx_ring *ring) @@ -234,7 +260,8 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv) ring = &priv->rx_ring[ring_ind]; if (mlx4_en_prepare_rx_desc(priv, ring, - ring->actual_size)) { + ring->actual_size, + GFP_KERNEL)) { if (ring->actual_size < MLX4_EN_MIN_RX_SIZE) { en_err(priv, "Failed to allocate " "enough rx buffers\n"); @@ -449,11 +476,11 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, DMA_FROM_DEVICE); /* Save page reference in skb */ - get_page(frags[nr].page); __skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page); skb_frag_size_set(&skb_frags_rx[nr], frag_info->frag_size); skb_frags_rx[nr].page_offset = frags[nr].offset; skb->truesize += frag_info->frag_stride; + frags[nr].page = NULL; } /* Adjust size of last fragment to match actual length */ if (nr > 0) @@ -546,7 +573,7 @@ static void mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv, int index = ring->prod & ring->size_mask; while ((u32) (ring->prod - ring->cons) < ring->actual_size) { - if (mlx4_en_prepare_rx_desc(priv, ring, index)) + if (mlx4_en_prepare_rx_desc(priv, ring, index, GFP_ATOMIC)) break; ring->prod++; index = ring->prod & ring->size_mask; @@ -656,8 +683,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud * - DIX Ethernet (type interpretation) * - TCP/IP (v4) * - without IP options - * - not an IP fragment */ - if (dev->features & NETIF_F_GRO) { + * - not an IP fragment + * - no LLS polling in progress + */ + if (!mlx4_en_cq_ll_polling(cq) && + (dev->features & NETIF_F_GRO)) { struct sk_buff *gro_skb = napi_get_frags(&cq->napi); if (!gro_skb) goto next; @@ -737,6 +767,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud timestamp); } + skb_mark_ll(skb, &cq->napi); + /* Push it up the stack */ netif_receive_skb(skb); @@ -781,8 +813,13 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) struct mlx4_en_priv *priv = netdev_priv(dev); int done; + if (!mlx4_en_cq_lock_napi(cq)) + return budget; + done = mlx4_en_process_rx_cq(dev, cq, budget); + mlx4_en_cq_unlock_napi(cq); + /* If we used up all the quota - we're probably not done yet... */ if (done == budget) INC_PERF_COUNTER(priv->pstats.napi_quota); @@ -794,21 +831,7 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) return done; } - -/* Calculate the last offset position that accommodates a full fragment - * (assuming fagment size = stride-align) */ -static int mlx4_en_last_alloc_offset(struct mlx4_en_priv *priv, u16 stride, u16 align) -{ - u16 res = MLX4_EN_ALLOC_SIZE % stride; - u16 offset = MLX4_EN_ALLOC_SIZE - stride - res + align; - - en_dbg(DRV, priv, "Calculated last offset for stride:%d align:%d " - "res:%d offset:%d\n", stride, align, res, offset); - return offset; -} - - -static int frag_sizes[] = { +static const int frag_sizes[] = { FRAG_SZ0, FRAG_SZ1, FRAG_SZ2, @@ -836,9 +859,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) priv->frag_info[i].frag_stride = ALIGN(frag_sizes[i], SMP_CACHE_BYTES); } - priv->frag_info[i].last_offset = mlx4_en_last_alloc_offset( - priv, priv->frag_info[i].frag_stride, - priv->frag_info[i].frag_align); buf_size += priv->frag_info[i].frag_size; i++; } @@ -850,13 +870,13 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d " "num_frags:%d):\n", eff_mtu, priv->num_frags); for (i = 0; i < priv->num_frags; i++) { - en_dbg(DRV, priv, " frag:%d - size:%d prefix:%d align:%d " - "stride:%d last_offset:%d\n", i, - priv->frag_info[i].frag_size, - priv->frag_info[i].frag_prefix_size, - priv->frag_info[i].frag_align, - priv->frag_info[i].frag_stride, - priv->frag_info[i].last_offset); + en_err(priv, + " frag:%d - size:%d prefix:%d align:%d stride:%d\n", + i, + priv->frag_info[i].frag_size, + priv->frag_info[i].frag_prefix_size, + priv->frag_info[i].frag_align, + priv->frag_info[i].frag_stride); } } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 4e6877a032a..7c492382da0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -544,7 +544,7 @@ u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb) if (vlan_tx_tag_present(skb)) up = vlan_tx_tag_get(skb) >> VLAN_PRIO_SHIFT; - return __skb_tx_hash(dev, skb, rings_p_up) + up * rings_p_up; + return __netdev_pick_tx(dev, skb) % rings_p_up + up * rings_p_up; } static void mlx4_bf_copy(void __iomem *dst, unsigned long *src, unsigned bytecnt) diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 6000342f972..7e042869ef0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -448,6 +448,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) int i; enum slave_port_gen_event gen_event; unsigned long flags; + struct mlx4_vport_state *s_info; while ((eqe = next_eqe_sw(eq, dev->caps.eqe_factor))) { /* @@ -556,7 +557,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) mlx4_dbg(dev, "%s: Sending MLX4_PORT_CHANGE_SUBTYPE_DOWN" " to slave: %d, port:%d\n", __func__, i, port); - mlx4_slave_event(dev, i, eqe); + s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state; + if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state) + mlx4_slave_event(dev, i, eqe); } else { /* IB port */ set_and_calc_slave_port_state(dev, i, port, MLX4_PORT_STATE_DEV_EVENT_PORT_DOWN, @@ -580,7 +583,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) for (i = 0; i < dev->num_slaves; i++) { if (i == mlx4_master_func_num(dev)) continue; - mlx4_slave_event(dev, i, eqe); + s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state; + if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state) + mlx4_slave_event(dev, i, eqe); } else /* IB port */ /* port-up event will be sent to a slave when the diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 2c97901c6a6..8873d6802c8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -133,7 +133,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [4] = "Automatic MAC reassignment support", [5] = "Time stamping support", [6] = "VST (control vlan insertion/stripping) support", - [7] = "FSM (MAC anti-spoofing) support" + [7] = "FSM (MAC anti-spoofing) support", + [8] = "Dynamic QP updates support" }; int i; @@ -659,6 +660,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) QUERY_DEV_CAP_MAX_COUNTERS_OFFSET); MLX4_GET(field32, outbox, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET); + if (field32 & (1 << 16)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_UPDATE_QP; if (field32 & (1 << 26)) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_VLAN_CONTROL; if (field32 & (1 << 20)) @@ -830,8 +833,10 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, u8 port_type; u16 short_field; int err; + int admin_link_state; #define MLX4_VF_PORT_NO_LINK_SENSE_MASK 0xE0 +#define MLX4_PORT_LINK_UP_MASK 0x80 #define QUERY_PORT_CUR_MAX_PKEY_OFFSET 0x0c #define QUERY_PORT_CUR_MAX_GID_OFFSET 0x0e @@ -861,6 +866,12 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, /* set port type to currently operating port type */ port_type |= (dev->caps.port_type[vhcr->in_modifier] & 0x3); + admin_link_state = priv->mfunc.master.vf_oper[slave].vport[vhcr->in_modifier].state.link_state; + if (IFLA_VF_LINK_STATE_ENABLE == admin_link_state) + port_type |= MLX4_PORT_LINK_UP_MASK; + else if (IFLA_VF_LINK_STATE_DISABLE == admin_link_state) + port_type &= ~MLX4_PORT_LINK_UP_MASK; + MLX4_PUT(outbox->buf, port_type, QUERY_PORT_SUPPORTED_TYPE_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 8a434997a0d..e85af922dcd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -98,7 +98,7 @@ MODULE_PARM_DESC(log_num_mgm_entry_size, "log mgm size, that defines the num" static bool enable_64b_cqe_eqe; module_param(enable_64b_cqe_eqe, bool, 0444); MODULE_PARM_DESC(enable_64b_cqe_eqe, - "Enable 64 byte CQEs/EQEs when the the FW supports this"); + "Enable 64 byte CQEs/EQEs when the FW supports this"); #define HCA_GLOBAL_CAP_MASK 0 @@ -842,11 +842,11 @@ static ssize_t set_port_ib_mtu(struct device *dev, return -EINVAL; } - err = sscanf(buf, "%d", &mtu); - if (err > 0) + err = kstrtoint(buf, 0, &mtu); + if (!err) ibta_mtu = int_to_ibta_mtu(mtu); - if (err <= 0 || ibta_mtu < 0) { + if (err || ibta_mtu < 0) { mlx4_err(mdev, "%s is invalid IBTA mtu\n", buf); return -EINVAL; } @@ -2080,6 +2080,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data) num_vfs, MLX4_MAX_NUM_VF); return -EINVAL; } + + if (num_vfs < 0) { + pr_err("num_vfs module parameter cannot be negative\n"); + return -EINVAL; + } /* * Check for BARs. */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index df15bb6631c..17d9277e33e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -482,6 +482,7 @@ struct mlx4_vport_state { u8 default_qos; u32 tx_rate; bool spoofchk; + u32 link_state; }; struct mlx4_vf_admin_state { @@ -570,6 +571,25 @@ struct mlx4_cmd { u8 comm_toggle; }; +enum { + MLX4_VF_IMMED_VLAN_FLAG_VLAN = 1 << 0, + MLX4_VF_IMMED_VLAN_FLAG_QOS = 1 << 1, + MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE = 1 << 2, +}; +struct mlx4_vf_immed_vlan_work { + struct work_struct work; + struct mlx4_priv *priv; + int flags; + int slave; + int vlan_ix; + int orig_vlan_ix; + u8 port; + u8 qos; + u16 vlan_id; + u16 orig_vlan_id; +}; + + struct mlx4_uar_table { struct mlx4_bitmap bitmap; }; @@ -1217,4 +1237,6 @@ static inline spinlock_t *mlx4_tlock(struct mlx4_dev *dev) #define NOT_MASKED_PD_BITS 17 +void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work); + #endif /* MLX4_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index b1d7657b2bf..35fb60e2320 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -96,13 +96,14 @@ /* Use the maximum between 16384 and a single page */ #define MLX4_EN_ALLOC_SIZE PAGE_ALIGN(16384) -#define MLX4_EN_ALLOC_ORDER get_order(MLX4_EN_ALLOC_SIZE) -/* Receive fragment sizes; we use at most 4 fragments (for 9600 byte MTU +#define MLX4_EN_ALLOC_PREFER_ORDER PAGE_ALLOC_COSTLY_ORDER + +/* Receive fragment sizes; we use at most 3 fragments (for 9600 byte MTU * and 4K allocations) */ enum { - FRAG_SZ0 = 512 - NET_IP_ALIGN, - FRAG_SZ1 = 1024, + FRAG_SZ0 = 1536 - NET_IP_ALIGN, + FRAG_SZ1 = 4096, FRAG_SZ2 = 4096, FRAG_SZ3 = MLX4_EN_ALLOC_SIZE }; @@ -234,9 +235,10 @@ struct mlx4_en_tx_desc { #define MLX4_EN_CX3_HIGH_ID 0x1005 struct mlx4_en_rx_alloc { - struct page *page; - dma_addr_t dma; - u16 offset; + struct page *page; + dma_addr_t dma; + u32 offset; + u32 size; }; struct mlx4_en_tx_ring { @@ -290,6 +292,11 @@ struct mlx4_en_rx_ring { void *rx_info; unsigned long bytes; unsigned long packets; +#ifdef CONFIG_NET_LL_RX_POLL + unsigned long yields; + unsigned long misses; + unsigned long cleaned; +#endif unsigned long csum_ok; unsigned long csum_none; int hwtstamp_rx_filter; @@ -310,6 +317,19 @@ struct mlx4_en_cq { u16 moder_cnt; struct mlx4_cqe *buf; #define MLX4_EN_OPCODE_ERROR 0x1e + +#ifdef CONFIG_NET_LL_RX_POLL + unsigned int state; +#define MLX4_EN_CQ_STATE_IDLE 0 +#define MLX4_EN_CQ_STATE_NAPI 1 /* NAPI owns this CQ */ +#define MLX4_EN_CQ_STATE_POLL 2 /* poll owns this CQ */ +#define MLX4_CQ_LOCKED (MLX4_EN_CQ_STATE_NAPI | MLX4_EN_CQ_STATE_POLL) +#define MLX4_EN_CQ_STATE_NAPI_YIELD 4 /* NAPI yielded this CQ */ +#define MLX4_EN_CQ_STATE_POLL_YIELD 8 /* poll yielded this CQ */ +#define CQ_YIELD (MLX4_EN_CQ_STATE_NAPI_YIELD | MLX4_EN_CQ_STATE_POLL_YIELD) +#define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD) + spinlock_t poll_lock; /* protects from LLS/napi conflicts */ +#endif /* CONFIG_NET_LL_RX_POLL */ }; struct mlx4_en_port_profile { @@ -421,8 +441,6 @@ struct mlx4_en_frag_info { u16 frag_prefix_size; u16 frag_stride; u16 frag_align; - u16 last_offset; - }; #ifdef CONFIG_MLX4_EN_DCB @@ -562,6 +580,115 @@ struct mlx4_mac_entry { struct rcu_head rcu; }; +#ifdef CONFIG_NET_LL_RX_POLL +static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq) +{ + spin_lock_init(&cq->poll_lock); + cq->state = MLX4_EN_CQ_STATE_IDLE; +} + +/* called from the device poll rutine to get ownership of a cq */ +static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq) +{ + int rc = true; + spin_lock(&cq->poll_lock); + if (cq->state & MLX4_CQ_LOCKED) { + WARN_ON(cq->state & MLX4_EN_CQ_STATE_NAPI); + cq->state |= MLX4_EN_CQ_STATE_NAPI_YIELD; + rc = false; + } else + /* we don't care if someone yielded */ + cq->state = MLX4_EN_CQ_STATE_NAPI; + spin_unlock(&cq->poll_lock); + return rc; +} + +/* returns true is someone tried to get the cq while napi had it */ +static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) +{ + int rc = false; + spin_lock(&cq->poll_lock); + WARN_ON(cq->state & (MLX4_EN_CQ_STATE_POLL | + MLX4_EN_CQ_STATE_NAPI_YIELD)); + + if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) + rc = true; + cq->state = MLX4_EN_CQ_STATE_IDLE; + spin_unlock(&cq->poll_lock); + return rc; +} + +/* called from mlx4_en_low_latency_poll() */ +static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) +{ + int rc = true; + spin_lock_bh(&cq->poll_lock); + if ((cq->state & MLX4_CQ_LOCKED)) { + struct net_device *dev = cq->dev; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring]; + + cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD; + rc = false; + rx_ring->yields++; + } else + /* preserve yield marks */ + cq->state |= MLX4_EN_CQ_STATE_POLL; + spin_unlock_bh(&cq->poll_lock); + return rc; +} + +/* returns true if someone tried to get the cq while it was locked */ +static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) +{ + int rc = false; + spin_lock_bh(&cq->poll_lock); + WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI)); + + if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) + rc = true; + cq->state = MLX4_EN_CQ_STATE_IDLE; + spin_unlock_bh(&cq->poll_lock); + return rc; +} + +/* true if a socket is polling, even if it did not get the lock */ +static inline bool mlx4_en_cq_ll_polling(struct mlx4_en_cq *cq) +{ + WARN_ON(!(cq->state & MLX4_CQ_LOCKED)); + return cq->state & CQ_USER_PEND; +} +#else +static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq) +{ +} + +static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq) +{ + return true; +} + +static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) +{ + return false; +} + +static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) +{ + return false; +} + +static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) +{ + return false; +} + +static inline bool mlx4_en_cq_ll_polling(struct mlx4_en_cq *cq) +{ + return false; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + #define MLX4_EN_WOL_DO_MODIFY (1ULL << 63) void mlx4_en_update_loopback_state(struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 1157f028a90..f984a89c27d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -101,6 +101,8 @@ struct res_qp { spinlock_t mcg_spl; int local_qpn; atomic_t ref_count; + u32 qpc_flags; + u8 sched_queue; }; enum res_mtt_states { @@ -355,7 +357,7 @@ static void update_gid(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox, static int update_vport_qp_param(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox, - u8 slave) + u8 slave, u32 qpn) { struct mlx4_qp_context *qpc = inbox->buf + 8; struct mlx4_vport_oper_state *vp_oper; @@ -369,12 +371,30 @@ static int update_vport_qp_param(struct mlx4_dev *dev, if (MLX4_VGT != vp_oper->state.default_vlan) { qp_type = (be32_to_cpu(qpc->flags) >> 16) & 0xff; - if (MLX4_QP_ST_RC == qp_type) + if (MLX4_QP_ST_RC == qp_type || + (MLX4_QP_ST_UD == qp_type && + !mlx4_is_qp_reserved(dev, qpn))) return -EINVAL; + /* the reserved QPs (special, proxy, tunnel) + * do not operate over vlans + */ + if (mlx4_is_qp_reserved(dev, qpn)) + return 0; + /* force strip vlan by clear vsd */ qpc->param3 &= ~cpu_to_be32(MLX4_STRIP_VLAN); - if (0 != vp_oper->state.default_vlan) { + + if (vp_oper->state.link_state == IFLA_VF_LINK_STATE_DISABLE && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP) { + qpc->pri_path.vlan_control = + MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; + } else if (0 != vp_oper->state.default_vlan) { qpc->pri_path.vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | @@ -2114,6 +2134,8 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave, if (err) return err; qp->local_qpn = local_qpn; + qp->sched_queue = 0; + qp->qpc_flags = be32_to_cpu(qpc->flags); err = get_res(dev, slave, mtt_base, RES_MTT, &mtt); if (err) @@ -2836,6 +2858,9 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave, { int err; struct mlx4_qp_context *qpc = inbox->buf + 8; + int qpn = vhcr->in_modifier & 0x7fffff; + struct res_qp *qp; + u8 orig_sched_queue; err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave); if (err) @@ -2844,11 +2869,30 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave, update_pkey_index(dev, slave, inbox); update_gid(dev, inbox, (u8)slave); adjust_proxy_tun_qkey(dev, vhcr, qpc); - err = update_vport_qp_param(dev, inbox, slave); + orig_sched_queue = qpc->pri_path.sched_queue; + err = update_vport_qp_param(dev, inbox, slave, qpn); if (err) return err; - return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd); + err = get_res(dev, slave, qpn, RES_QP, &qp); + if (err) + return err; + if (qp->com.from_state != RES_QP_HW) { + err = -EBUSY; + goto out; + } + + err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd); +out: + /* if no error, save sched queue value passed in by VF. This is + * essentially the QOS value provided by the VF. This will be useful + * if we allow dynamic changes from VST back to VGT + */ + if (!err) + qp->sched_queue = orig_sched_queue; + + put_res(dev, slave, qpn, RES_QP); + return err; } int mlx4_RTR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave, @@ -3932,3 +3976,112 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave) rem_slave_xrcdns(dev, slave); mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex); } + +void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) +{ + struct mlx4_vf_immed_vlan_work *work = + container_of(_work, struct mlx4_vf_immed_vlan_work, work); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_update_qp_context *upd_context; + struct mlx4_dev *dev = &work->priv->dev; + struct mlx4_resource_tracker *tracker = + &work->priv->mfunc.master.res_tracker; + struct list_head *qp_list = + &tracker->slave_list[work->slave].res_list[RES_QP]; + struct res_qp *qp; + struct res_qp *tmp; + u64 qp_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) | + (1ULL << MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE)); + + int err; + int port, errors = 0; + u8 vlan_control; + + if (mlx4_is_slave(dev)) { + mlx4_warn(dev, "Trying to update-qp in slave %d\n", + work->slave); + goto out; + } + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + goto out; + if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE) /* block all */ + vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; + else if (!work->vlan_id) + vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; + else + vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED; + + upd_context = mailbox->buf; + upd_context->primary_addr_path_mask = cpu_to_be64(qp_mask); + upd_context->qp_context.pri_path.vlan_control = vlan_control; + upd_context->qp_context.pri_path.vlan_index = work->vlan_ix; + + spin_lock_irq(mlx4_tlock(dev)); + list_for_each_entry_safe(qp, tmp, qp_list, com.list) { + spin_unlock_irq(mlx4_tlock(dev)); + if (qp->com.owner == work->slave) { + if (qp->com.from_state != RES_QP_HW || + !qp->sched_queue || /* no INIT2RTR trans yet */ + mlx4_is_qp_reserved(dev, qp->local_qpn) || + qp->qpc_flags & (1 << MLX4_RSS_QPC_FLAG_OFFSET)) { + spin_lock_irq(mlx4_tlock(dev)); + continue; + } + port = (qp->sched_queue >> 6 & 1) + 1; + if (port != work->port) { + spin_lock_irq(mlx4_tlock(dev)); + continue; + } + upd_context->qp_context.pri_path.sched_queue = + qp->sched_queue & 0xC7; + upd_context->qp_context.pri_path.sched_queue |= + ((work->qos & 0x7) << 3); + + err = mlx4_cmd(dev, mailbox->dma, + qp->local_qpn & 0xffffff, + 0, MLX4_CMD_UPDATE_QP, + MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE); + if (err) { + mlx4_info(dev, "UPDATE_QP failed for slave %d, " + "port %d, qpn %d (%d)\n", + work->slave, port, qp->local_qpn, + err); + errors++; + } + } + spin_lock_irq(mlx4_tlock(dev)); + } + spin_unlock_irq(mlx4_tlock(dev)); + mlx4_free_cmd_mailbox(dev, mailbox); + + if (errors) + mlx4_err(dev, "%d UPDATE_QP failures for slave %d, port %d\n", + errors, work->slave, work->port); + + /* unregister previous vlan_id if needed and we had no errors + * while updating the QPs + */ + if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN && !errors && + NO_INDX != work->orig_vlan_ix) + __mlx4_unregister_vlan(&work->priv->dev, work->port, + work->orig_vlan_ix); +out: + kfree(work); + return; +} diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig index fe42fc00d8d..d16b11ed2e5 100644 --- a/drivers/net/ethernet/micrel/Kconfig +++ b/drivers/net/ethernet/micrel/Kconfig @@ -22,7 +22,6 @@ if NET_VENDOR_MICREL config ARM_KS8695_ETHER tristate "KS8695 Ethernet support" depends on ARM && ARCH_KS8695 - select NET_CORE select MII ---help--- If you wish to compile a kernel for the KS8695 and want to @@ -39,7 +38,6 @@ config KS8842 config KS8851 tristate "Micrel KS8851 SPI" depends on SPI - select NET_CORE select MII select CRC32 select EEPROM_93CX6 @@ -49,7 +47,6 @@ config KS8851 config KS8851_MLL tristate "Micrel KS8851 MLL" depends on HAS_IOMEM - select NET_CORE select MII ---help--- This platform driver is for Micrel KS8851 Address/data bus @@ -58,7 +55,6 @@ config KS8851_MLL config KSZ884X_PCI tristate "Micrel KSZ8841/2 PCI" depends on PCI - select NET_CORE select MII select CRC32 ---help--- diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c index b6c60fdef4f..106eb972f2a 100644 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ b/drivers/net/ethernet/micrel/ks8695net.c @@ -1600,7 +1600,6 @@ ks8695_drv_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ks8695_priv *ksp = netdev_priv(ndev); - platform_set_drvdata(pdev, NULL); netif_napi_del(&ksp->napi); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c index fbcb9e74d7f..e393d998be8 100644 --- a/drivers/net/ethernet/micrel/ks8842.c +++ b/drivers/net/ethernet/micrel/ks8842.c @@ -1250,7 +1250,6 @@ static int ks8842_remove(struct platform_device *pdev) iounmap(adapter->hw_addr); free_netdev(netdev); release_mem_region(iomem->start, resource_size(iomem)); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index ddaf138ce0d..ac20098b542 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -35,6 +35,9 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/ks8851_mll.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_net.h> #define DRV_NAME "ks8851_mll" @@ -1524,6 +1527,13 @@ static int ks_hw_init(struct ks_net *ks) return true; } +#if defined(CONFIG_OF) +static const struct of_device_id ks8851_ml_dt_ids[] = { + { .compatible = "micrel,ks8851-mll" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ks8851_ml_dt_ids); +#endif static int ks8851_probe(struct platform_device *pdev) { @@ -1532,7 +1542,7 @@ static int ks8851_probe(struct platform_device *pdev) struct net_device *netdev; struct ks_net *ks; u16 id, data; - struct ks8851_mll_platform_data *pdata; + const char *mac; io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -1619,13 +1629,21 @@ static int ks8851_probe(struct platform_device *pdev) ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA); /* overwriting the default MAC address */ - pdata = pdev->dev.platform_data; - if (!pdata) { - netdev_err(netdev, "No platform data\n"); - err = -ENODEV; - goto err_pdata; + if (pdev->dev.of_node) { + mac = of_get_mac_address(pdev->dev.of_node); + if (mac) + memcpy(ks->mac_addr, mac, ETH_ALEN); + } else { + struct ks8851_mll_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + netdev_err(netdev, "No platform data\n"); + err = -ENODEV; + goto err_pdata; + } + memcpy(ks->mac_addr, pdata->mac_addr, ETH_ALEN); } - memcpy(ks->mac_addr, pdata->mac_addr, 6); if (!is_valid_ether_addr(ks->mac_addr)) { /* Use random MAC address if none passed */ eth_random_addr(ks->mac_addr); @@ -1671,7 +1689,6 @@ static int ks8851_remove(struct platform_device *pdev) iounmap(ks->hw_addr); free_netdev(netdev); release_mem_region(iomem->start, resource_size(iomem)); - platform_set_drvdata(pdev, NULL); return 0; } @@ -1680,6 +1697,7 @@ static struct platform_driver ks8851_platform_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ks8851_ml_dt_ids), }, .probe = ks8851_probe, .remove = ks8851_remove, diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 7be9788ed0f..967bae8b85c 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3299,7 +3299,7 @@ static int myri10ge_resume(struct pci_dev *pdev) if (mgp == NULL) return -EINVAL; netdev = mgp->dev; - pci_set_power_state(pdev, 0); /* zeros conf space as a side effect */ + pci_set_power_state(pdev, PCI_D0); /* zeros conf space as a side effect */ msleep(5); /* give card time to respond */ pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor); if (vendor == 0xffff) { diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index cbfaed5f2f8..5a20eaf903d 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -3444,7 +3444,7 @@ static int vxge_device_register(struct __vxge_hw_device *hldev, } vxge_debug_init(vxge_hw_device_trace_level_get(hldev), - "%s : checksuming enabled", __func__); + "%s : checksumming enabled", __func__); if (high_dma) { ndev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/ethernet/netx-eth.c b/drivers/net/ethernet/netx-eth.c index cb9e6383150..dc2c6f561e9 100644 --- a/drivers/net/ethernet/netx-eth.c +++ b/drivers/net/ethernet/netx-eth.c @@ -422,7 +422,6 @@ exit_free_pfifo: exit_free_xc: free_xc(priv->xc); exit_free_netdev: - platform_set_drvdata(pdev, NULL); free_netdev(ndev); exit: return ret; @@ -430,11 +429,9 @@ exit: static int netx_eth_drv_remove(struct platform_device *pdev) { - struct net_device *ndev = dev_get_drvdata(&pdev->dev); + struct net_device *ndev = platform_get_drvdata(pdev); struct netx_eth_priv *priv = netdev_priv(ndev); - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); xc_stop(priv->xc); free_xc(priv->xc); diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig index 334c1718309..01182b55947 100644 --- a/drivers/net/ethernet/nuvoton/Kconfig +++ b/drivers/net/ethernet/nuvoton/Kconfig @@ -22,7 +22,6 @@ config W90P910_ETH tristate "Nuvoton w90p910 Ethernet support" depends on ARM && ARCH_W90X900 select PHYLIB - select NET_CORE select MII ---help--- Say Y here if you want to use built-in Ethernet ports diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c index 3df8287b745..e88bdb1aa66 100644 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c @@ -1051,7 +1051,6 @@ failed_put_clk: clk_put(ether->clk); failed_free_rxirq: free_irq(ether->rxirq, pdev); - platform_set_drvdata(pdev, NULL); failed_free_txirq: free_irq(ether->txirq, pdev); failed_free_io: @@ -1080,7 +1079,6 @@ static int w90p910_ether_remove(struct platform_device *pdev) free_irq(ether->rxirq, dev); del_timer_sync(ðer->check_timer); - platform_set_drvdata(pdev, NULL); free_netdev(dev); return 0; diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index b003fe53c8e..098b96dad66 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -6340,7 +6340,7 @@ static DEFINE_PCI_DEVICE_TABLE(pci_tbl) = { {0,}, }; -static struct pci_driver driver = { +static struct pci_driver forcedeth_pci_driver = { .name = DRV_NAME, .id_table = pci_tbl, .probe = nv_probe, @@ -6349,16 +6349,6 @@ static struct pci_driver driver = { .driver.pm = NV_PM_OPS, }; -static int __init init_nic(void) -{ - return pci_register_driver(&driver); -} - -static void __exit exit_nic(void) -{ - pci_unregister_driver(&driver); -} - module_param(max_interrupt_work, int, 0); MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt"); module_param(optimization_mode, int, 0); @@ -6379,11 +6369,8 @@ module_param(debug_tx_timeout, bool, 0); MODULE_PARM_DESC(debug_tx_timeout, "Dump tx related registers and ring when tx_timeout happens"); +module_pci_driver(forcedeth_pci_driver); MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>"); MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); MODULE_LICENSE("GPL"); - MODULE_DEVICE_TABLE(pci, pci_tbl); - -module_init(init_nic); -module_exit(exit_nic); diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 55a5548d6ad..a061b93efe6 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1483,7 +1483,6 @@ static int lpc_eth_drv_probe(struct platform_device *pdev) return 0; err_out_unregister_netdev: - platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); err_out_dma_unmap: if (!use_iram_for_net(&pldat->pdev->dev) || @@ -1511,7 +1510,6 @@ static int lpc_eth_drv_remove(struct platform_device *pdev) struct netdata_local *pldat = netdev_priv(ndev); unregister_netdev(ndev); - platform_set_drvdata(pdev, NULL); if (!use_iram_for_net(&pldat->pdev->dev) || pldat->dma_buff_size > lpc32xx_return_iram_size()) diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index 91a8a5d2803..622aa75904c 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1448,7 +1448,7 @@ static int octeon_mgmt_probe(struct platform_device *pdev) SET_NETDEV_DEV(netdev, &pdev->dev); - dev_set_drvdata(&pdev->dev, netdev); + platform_set_drvdata(pdev, netdev); p = netdev_priv(netdev); netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll, OCTEON_MGMT_NAPI_WEIGHT); @@ -1570,7 +1570,7 @@ err: static int octeon_mgmt_remove(struct platform_device *pdev) { - struct net_device *netdev = dev_get_drvdata(&pdev->dev); + struct net_device *netdev = platform_get_drvdata(pdev); unregister_netdev(netdev); free_netdev(netdev); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig index 34d05bf72b2..cb22341a14a 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig +++ b/drivers/net/ethernet/oki-semi/pch_gbe/Kconfig @@ -5,7 +5,6 @@ config PCH_GBE tristate "OKI SEMICONDUCTOR IOH(ML7223/ML7831) GbE" depends on PCI - select NET_CORE select MII select PTP_1588_CLOCK_PCH ---help--- diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h index 7fb7e178c74..7779036690c 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h @@ -633,6 +633,8 @@ struct pch_gbe_adapter { struct pci_dev *ptp_pdev; }; +#define pch_gbe_hw_to_adapter(hw) container_of(hw, struct pch_gbe_adapter, hw) + extern const char pch_driver_version[]; /* pch_gbe_main.c */ diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c index 5ae03e815ee..ff3ad70935a 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c @@ -19,6 +19,7 @@ */ #include "pch_gbe.h" #include "pch_gbe_phy.h" +#include "pch_gbe_api.h" /* bus type values */ #define pch_gbe_bus_type_unknown 0 @@ -70,7 +71,9 @@ static s32 pch_gbe_plat_init_hw(struct pch_gbe_hw *hw) ret_val = pch_gbe_phy_get_id(hw); if (ret_val) { - pr_err("pch_gbe_phy_get_id error\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "pch_gbe_phy_get_id error\n"); return ret_val; } pch_gbe_phy_init_setting(hw); @@ -112,10 +115,12 @@ static void pch_gbe_plat_init_function_pointers(struct pch_gbe_hw *hw) * 0: Successfully * ENOSYS: Function is not registered */ -inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) +s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) { if (!hw->reg) { - pr_err("ERROR: Registers not mapped\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: Registers not mapped\n"); return -ENOSYS; } pch_gbe_plat_init_function_pointers(hw); @@ -126,12 +131,15 @@ inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) * pch_gbe_hal_get_bus_info - Obtain bus information for adapter * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) +void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) { - if (!hw->func->get_bus_info) - pr_err("ERROR: configuration\n"); - else - hw->func->get_bus_info(hw); + if (!hw->func->get_bus_info) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); + return; + } + hw->func->get_bus_info(hw); } /** @@ -141,10 +149,12 @@ inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) * 0: Successfully * ENOSYS: Function is not registered */ -inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) +s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) { if (!hw->func->init_hw) { - pr_err("ERROR: configuration\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); return -ENOSYS; } return hw->func->init_hw(hw); @@ -159,7 +169,7 @@ inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) * 0: Successfully * Negative value: Failed */ -inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset, +s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset, u16 *data) { if (!hw->func->read_phy_reg) @@ -176,7 +186,7 @@ inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset, * 0: Successfully * Negative value: Failed */ -inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, +s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, u16 data) { if (!hw->func->write_phy_reg) @@ -188,24 +198,30 @@ inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, * pch_gbe_hal_phy_hw_reset - Hard PHY reset * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) +void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) { - if (!hw->func->reset_phy) - pr_err("ERROR: configuration\n"); - else - hw->func->reset_phy(hw); + if (!hw->func->reset_phy) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); + return; + } + hw->func->reset_phy(hw); } /** * pch_gbe_hal_phy_sw_reset - Soft PHY reset * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) +void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) { - if (!hw->func->sw_reset_phy) - pr_err("ERROR: configuration\n"); - else - hw->func->sw_reset_phy(hw); + if (!hw->func->sw_reset_phy) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); + return; + } + hw->func->sw_reset_phy(hw); } /** @@ -215,10 +231,12 @@ inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) * 0: Successfully * ENOSYS: Function is not registered */ -inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) +s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) { if (!hw->func->read_mac_addr) { - pr_err("ERROR: configuration\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); return -ENOSYS; } return hw->func->read_mac_addr(hw); @@ -228,7 +246,7 @@ inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) * pch_gbe_hal_power_up_phy - Power up PHY * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw) +void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw) { if (hw->func->power_up_phy) hw->func->power_up_phy(hw); @@ -238,7 +256,7 @@ inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw) * pch_gbe_hal_power_down_phy - Power down PHY * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw) +void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw) { if (hw->func->power_down_phy) hw->func->power_down_phy(hw); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index 24b787be606..1129db0cdf8 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -122,7 +122,7 @@ static int pch_gbe_set_settings(struct net_device *netdev, } ret = mii_ethtool_sset(&adapter->mii, ecmd); if (ret) { - pr_err("Error: mii_ethtool_sset\n"); + netdev_err(netdev, "Error: mii_ethtool_sset\n"); return ret; } hw->mac.link_speed = speed; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 0c1c65a9ce5..ab1039a95bf 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -287,7 +287,7 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } -inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) +static inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) { iowrite32(0x01, &hw->reg->MAC_ADDR_LOAD); } @@ -300,6 +300,7 @@ inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) */ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 adr1a, adr1b; adr1a = ioread32(&hw->reg->mac_adr[0].high); @@ -312,7 +313,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) hw->mac.addr[4] = (u8)(adr1b & 0xFF); hw->mac.addr[5] = (u8)((adr1b >> 8) & 0xFF); - pr_debug("hw->mac.addr : %pM\n", hw->mac.addr); + netdev_dbg(adapter->netdev, "hw->mac.addr : %pM\n", hw->mac.addr); return 0; } @@ -324,6 +325,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) static void pch_gbe_wait_clr_bit(void *reg, u32 bit) { u32 tmp; + /* wait busy */ tmp = 1000; while ((ioread32(reg) & bit) && --tmp) @@ -340,9 +342,10 @@ static void pch_gbe_wait_clr_bit(void *reg, u32 bit) */ static void pch_gbe_mac_mar_set(struct pch_gbe_hw *hw, u8 * addr, u32 index) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 mar_low, mar_high, adrmask; - pr_debug("index : 0x%x\n", index); + netdev_dbg(adapter->netdev, "index : 0x%x\n", index); /* * HW expects these in little endian so we reverse the byte order @@ -468,10 +471,11 @@ static void pch_gbe_mac_mc_addr_list_update(struct pch_gbe_hw *hw, */ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct pch_gbe_mac_info *mac = &hw->mac; u32 rx_fctrl; - pr_debug("mac->fc = %u\n", mac->fc); + netdev_dbg(adapter->netdev, "mac->fc = %u\n", mac->fc); rx_fctrl = ioread32(&hw->reg->RX_FCTRL); @@ -493,14 +497,16 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) mac->tx_fc_enable = true; break; default: - pr_err("Flow control param set incorrectly\n"); + netdev_err(adapter->netdev, + "Flow control param set incorrectly\n"); return -EINVAL; } if (mac->link_duplex == DUPLEX_HALF) rx_fctrl &= ~PCH_GBE_FL_CTRL_EN; iowrite32(rx_fctrl, &hw->reg->RX_FCTRL); - pr_debug("RX_FCTRL reg : 0x%08x mac->tx_fc_enable : %d\n", - ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable); + netdev_dbg(adapter->netdev, + "RX_FCTRL reg : 0x%08x mac->tx_fc_enable : %d\n", + ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable); return 0; } @@ -511,10 +517,11 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) */ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 addr_mask; - pr_debug("wu_evt : 0x%08x ADDR_MASK reg : 0x%08x\n", - wu_evt, ioread32(&hw->reg->ADDR_MASK)); + netdev_dbg(adapter->netdev, "wu_evt : 0x%08x ADDR_MASK reg : 0x%08x\n", + wu_evt, ioread32(&hw->reg->ADDR_MASK)); if (wu_evt) { /* Set Wake-On-Lan address mask */ @@ -546,6 +553,7 @@ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt) u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, u16 data) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 data_out = 0; unsigned int i; unsigned long flags; @@ -558,7 +566,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, udelay(20); } if (i == 0) { - pr_err("pch-gbe.miim won't go Ready\n"); + netdev_err(adapter->netdev, "pch-gbe.miim won't go Ready\n"); spin_unlock_irqrestore(&hw->miim_lock, flags); return 0; /* No way to indicate timeout error */ } @@ -573,9 +581,9 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, } spin_unlock_irqrestore(&hw->miim_lock, flags); - pr_debug("PHY %s: reg=%d, data=0x%04X\n", - dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg, - dir == PCH_GBE_MIIM_OPER_READ ? data_out : data); + netdev_dbg(adapter->netdev, "PHY %s: reg=%d, data=0x%04X\n", + dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg, + dir == PCH_GBE_MIIM_OPER_READ ? data_out : data); return (u16) data_out; } @@ -585,6 +593,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, */ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); unsigned long tmp2, tmp3; /* Set Pause packet */ @@ -606,10 +615,13 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) /* Transmit Pause Packet */ iowrite32(PCH_GBE_PS_PKT_RQ, &hw->reg->PAUSE_REQ); - pr_debug("PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", - ioread32(&hw->reg->PAUSE_PKT1), ioread32(&hw->reg->PAUSE_PKT2), - ioread32(&hw->reg->PAUSE_PKT3), ioread32(&hw->reg->PAUSE_PKT4), - ioread32(&hw->reg->PAUSE_PKT5)); + netdev_dbg(adapter->netdev, + "PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + ioread32(&hw->reg->PAUSE_PKT1), + ioread32(&hw->reg->PAUSE_PKT2), + ioread32(&hw->reg->PAUSE_PKT3), + ioread32(&hw->reg->PAUSE_PKT4), + ioread32(&hw->reg->PAUSE_PKT5)); return; } @@ -624,15 +636,15 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) */ static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter) { - adapter->tx_ring = kzalloc(sizeof(*adapter->tx_ring), GFP_KERNEL); + adapter->tx_ring = devm_kzalloc(&adapter->pdev->dev, + sizeof(*adapter->tx_ring), GFP_KERNEL); if (!adapter->tx_ring) return -ENOMEM; - adapter->rx_ring = kzalloc(sizeof(*adapter->rx_ring), GFP_KERNEL); - if (!adapter->rx_ring) { - kfree(adapter->tx_ring); + adapter->rx_ring = devm_kzalloc(&adapter->pdev->dev, + sizeof(*adapter->rx_ring), GFP_KERNEL); + if (!adapter->rx_ring) return -ENOMEM; - } return 0; } @@ -669,7 +681,7 @@ static int pch_gbe_init_phy(struct pch_gbe_adapter *adapter) break; } adapter->hw.phy.addr = adapter->mii.phy_id; - pr_debug("phy_addr = %d\n", adapter->mii.phy_id); + netdev_dbg(netdev, "phy_addr = %d\n", adapter->mii.phy_id); if (addr == 32) return -EAGAIN; /* Selected the phy and isolate the rest */ @@ -758,13 +770,15 @@ void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter) */ void pch_gbe_reset(struct pch_gbe_adapter *adapter) { + struct net_device *netdev = adapter->netdev; + pch_gbe_mac_reset_hw(&adapter->hw); /* reprogram multicast address register after reset */ - pch_gbe_set_multi(adapter->netdev); + pch_gbe_set_multi(netdev); /* Setup the receive address. */ pch_gbe_mac_init_rx_addrs(&adapter->hw, PCH_GBE_MAR_ENTRIES); if (pch_gbe_hal_init_hw(&adapter->hw)) - pr_err("Hardware Error\n"); + netdev_err(netdev, "Hardware Error\n"); } /** @@ -778,7 +792,7 @@ static void pch_gbe_free_irq(struct pch_gbe_adapter *adapter) free_irq(adapter->pdev->irq, netdev); if (adapter->have_msi) { pci_disable_msi(adapter->pdev); - pr_debug("call pci_disable_msi\n"); + netdev_dbg(netdev, "call pci_disable_msi\n"); } } @@ -795,7 +809,8 @@ static void pch_gbe_irq_disable(struct pch_gbe_adapter *adapter) ioread32(&hw->reg->INT_ST); synchronize_irq(adapter->pdev->irq); - pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN)); + netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n", + ioread32(&hw->reg->INT_EN)); } /** @@ -809,7 +824,8 @@ static void pch_gbe_irq_enable(struct pch_gbe_adapter *adapter) if (likely(atomic_dec_and_test(&adapter->irq_sem))) iowrite32(PCH_GBE_INT_ENABLE_MASK, &hw->reg->INT_EN); ioread32(&hw->reg->INT_ST); - pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN)); + netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n", + ioread32(&hw->reg->INT_EN)); } @@ -846,9 +862,9 @@ static void pch_gbe_configure_tx(struct pch_gbe_adapter *adapter) struct pch_gbe_hw *hw = &adapter->hw; u32 tdba, tdlen, dctrl; - pr_debug("dma addr = 0x%08llx size = 0x%08x\n", - (unsigned long long)adapter->tx_ring->dma, - adapter->tx_ring->size); + netdev_dbg(adapter->netdev, "dma addr = 0x%08llx size = 0x%08x\n", + (unsigned long long)adapter->tx_ring->dma, + adapter->tx_ring->size); /* Setup the HW Tx Head and Tail descriptor pointers */ tdba = adapter->tx_ring->dma; @@ -894,9 +910,9 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter) struct pch_gbe_hw *hw = &adapter->hw; u32 rdba, rdlen, rxdma; - pr_debug("dma adr = 0x%08llx size = 0x%08x\n", - (unsigned long long)adapter->rx_ring->dma, - adapter->rx_ring->size); + netdev_dbg(adapter->netdev, "dma adr = 0x%08llx size = 0x%08x\n", + (unsigned long long)adapter->rx_ring->dma, + adapter->rx_ring->size); pch_gbe_mac_force_mac_fc(hw); @@ -907,9 +923,10 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter) rxdma &= ~PCH_GBE_RX_DMA_EN; iowrite32(rxdma, &hw->reg->DMA_CTRL); - pr_debug("MAC_RX_EN reg = 0x%08x DMA_CTRL reg = 0x%08x\n", - ioread32(&hw->reg->MAC_RX_EN), - ioread32(&hw->reg->DMA_CTRL)); + netdev_dbg(adapter->netdev, + "MAC_RX_EN reg = 0x%08x DMA_CTRL reg = 0x%08x\n", + ioread32(&hw->reg->MAC_RX_EN), + ioread32(&hw->reg->DMA_CTRL)); /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ @@ -977,7 +994,8 @@ static void pch_gbe_clean_tx_ring(struct pch_gbe_adapter *adapter, buffer_info = &tx_ring->buffer_info[i]; pch_gbe_unmap_and_free_tx_resource(adapter, buffer_info); } - pr_debug("call pch_gbe_unmap_and_free_tx_resource() %d count\n", i); + netdev_dbg(adapter->netdev, + "call pch_gbe_unmap_and_free_tx_resource() %d count\n", i); size = (unsigned long)sizeof(struct pch_gbe_buffer) * tx_ring->count; memset(tx_ring->buffer_info, 0, size); @@ -1009,7 +1027,8 @@ pch_gbe_clean_rx_ring(struct pch_gbe_adapter *adapter, buffer_info = &rx_ring->buffer_info[i]; pch_gbe_unmap_and_free_rx_resource(adapter, buffer_info); } - pr_debug("call pch_gbe_unmap_and_free_rx_resource() %d count\n", i); + netdev_dbg(adapter->netdev, + "call pch_gbe_unmap_and_free_rx_resource() %d count\n", i); size = (unsigned long)sizeof(struct pch_gbe_buffer) * rx_ring->count; memset(rx_ring->buffer_info, 0, size); @@ -1087,7 +1106,7 @@ static void pch_gbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; struct pch_gbe_hw *hw = &adapter->hw; - pr_debug("right now = %ld\n", jiffies); + netdev_dbg(netdev, "right now = %ld\n", jiffies); pch_gbe_update_stats(adapter); if ((mii_link_ok(&adapter->mii)) && (!netif_carrier_ok(netdev))) { @@ -1095,7 +1114,7 @@ static void pch_gbe_watchdog(unsigned long data) netdev->tx_queue_len = adapter->tx_queue_len; /* mii library handles link maintenance tasks */ if (mii_ethtool_gset(&adapter->mii, &cmd)) { - pr_err("ethtool get setting Error\n"); + netdev_err(netdev, "ethtool get setting Error\n"); mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + PCH_GBE_WATCHDOG_PERIOD)); @@ -1213,7 +1232,7 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter, buffer_info->length, DMA_TO_DEVICE); if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) { - pr_err("TX DMA map failed\n"); + netdev_err(adapter->netdev, "TX DMA map failed\n"); buffer_info->dma = 0; buffer_info->time_stamp = 0; tx_ring->next_to_use = ring_num; @@ -1333,13 +1352,13 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) /* When request status is no interruption factor */ if (unlikely(!int_st)) return IRQ_NONE; /* Not our interrupt. End processing. */ - pr_debug("%s occur int_st = 0x%08x\n", __func__, int_st); + netdev_dbg(netdev, "%s occur int_st = 0x%08x\n", __func__, int_st); if (int_st & PCH_GBE_INT_RX_FRAME_ERR) adapter->stats.intr_rx_frame_err_count++; if (int_st & PCH_GBE_INT_RX_FIFO_ERR) if (!adapter->rx_stop_flag) { adapter->stats.intr_rx_fifo_err_count++; - pr_debug("Rx fifo over run\n"); + netdev_dbg(netdev, "Rx fifo over run\n"); adapter->rx_stop_flag = true; int_en = ioread32(&hw->reg->INT_EN); iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR), @@ -1359,7 +1378,7 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) /* When Rx descriptor is empty */ if ((int_st & PCH_GBE_INT_RX_DSC_EMP)) { adapter->stats.intr_rx_dsc_empty_count++; - pr_debug("Rx descriptor is empty\n"); + netdev_dbg(netdev, "Rx descriptor is empty\n"); int_en = ioread32(&hw->reg->INT_EN); iowrite32((int_en & ~PCH_GBE_INT_RX_DSC_EMP), &hw->reg->INT_EN); if (hw->mac.tx_fc_enable) { @@ -1382,8 +1401,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) __napi_schedule(&adapter->napi); } } - pr_debug("return = 0x%08x INT_EN reg = 0x%08x\n", - IRQ_HANDLED, ioread32(&hw->reg->INT_EN)); + netdev_dbg(netdev, "return = 0x%08x INT_EN reg = 0x%08x\n", + IRQ_HANDLED, ioread32(&hw->reg->INT_EN)); return IRQ_HANDLED; } @@ -1437,9 +1456,10 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter, rx_desc->buffer_addr = (buffer_info->dma); rx_desc->gbec_status = DSC_INIT16; - pr_debug("i = %d buffer_info->dma = 0x08%llx buffer_info->length = 0x%x\n", - i, (unsigned long long)buffer_info->dma, - buffer_info->length); + netdev_dbg(netdev, + "i = %d buffer_info->dma = 0x08%llx buffer_info->length = 0x%x\n", + i, (unsigned long long)buffer_info->dma, + buffer_info->length); if (unlikely(++i == rx_ring->count)) i = 0; @@ -1531,12 +1551,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, bool cleaned = false; int unused, thresh; - pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); + netdev_dbg(adapter->netdev, "next_to_clean : %d\n", + tx_ring->next_to_clean); i = tx_ring->next_to_clean; tx_desc = PCH_GBE_TX_DESC(*tx_ring, i); - pr_debug("gbec_status:0x%04x dma_status:0x%04x\n", - tx_desc->gbec_status, tx_desc->dma_status); + netdev_dbg(adapter->netdev, "gbec_status:0x%04x dma_status:0x%04x\n", + tx_desc->gbec_status, tx_desc->dma_status); unused = PCH_GBE_DESC_UNUSED(tx_ring); thresh = tx_ring->count - PCH_GBE_TX_WEIGHT; @@ -1544,8 +1565,10 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, { /* current marked clean, tx queue filling up, do extra clean */ int j, k; if (unused < 8) { /* tx queue nearly full */ - pr_debug("clean_tx: transmit queue warning (%x,%x) unused=%d\n", - tx_ring->next_to_clean,tx_ring->next_to_use,unused); + netdev_dbg(adapter->netdev, + "clean_tx: transmit queue warning (%x,%x) unused=%d\n", + tx_ring->next_to_clean, tx_ring->next_to_use, + unused); } /* current marked clean, scan for more that need cleaning. */ @@ -1557,49 +1580,56 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, if (++k >= tx_ring->count) k = 0; /*increment, wrap*/ } if (j < PCH_GBE_TX_WEIGHT) { - pr_debug("clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n", - unused,j, i,k, tx_ring->next_to_use, tx_desc->gbec_status); + netdev_dbg(adapter->netdev, + "clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n", + unused, j, i, k, tx_ring->next_to_use, + tx_desc->gbec_status); i = k; /*found one to clean, usu gbec_status==2000.*/ } } while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) { - pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status); + netdev_dbg(adapter->netdev, "gbec_status:0x%04x\n", + tx_desc->gbec_status); buffer_info = &tx_ring->buffer_info[i]; skb = buffer_info->skb; cleaned = true; if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_ABT)) { adapter->stats.tx_aborted_errors++; - pr_err("Transfer Abort Error\n"); + netdev_err(adapter->netdev, "Transfer Abort Error\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CRSER) ) { adapter->stats.tx_carrier_errors++; - pr_err("Transfer Carrier Sense Error\n"); + netdev_err(adapter->netdev, + "Transfer Carrier Sense Error\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_EXCOL) ) { adapter->stats.tx_aborted_errors++; - pr_err("Transfer Collision Abort Error\n"); + netdev_err(adapter->netdev, + "Transfer Collision Abort Error\n"); } else if ((tx_desc->gbec_status & (PCH_GBE_TXD_GMAC_STAT_SNGCOL | PCH_GBE_TXD_GMAC_STAT_MLTCOL))) { adapter->stats.collisions++; adapter->stats.tx_packets++; adapter->stats.tx_bytes += skb->len; - pr_debug("Transfer Collision\n"); + netdev_dbg(adapter->netdev, "Transfer Collision\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CMPLT) ) { adapter->stats.tx_packets++; adapter->stats.tx_bytes += skb->len; } if (buffer_info->mapped) { - pr_debug("unmap buffer_info->dma : %d\n", i); + netdev_dbg(adapter->netdev, + "unmap buffer_info->dma : %d\n", i); dma_unmap_single(&adapter->pdev->dev, buffer_info->dma, buffer_info->length, DMA_TO_DEVICE); buffer_info->mapped = false; } if (buffer_info->skb) { - pr_debug("trim buffer_info->skb : %d\n", i); + netdev_dbg(adapter->netdev, + "trim buffer_info->skb : %d\n", i); skb_trim(buffer_info->skb, 0); } tx_desc->gbec_status = DSC_INIT16; @@ -1613,8 +1643,9 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, break; } } - pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n", - cleaned_count); + netdev_dbg(adapter->netdev, + "called pch_gbe_unmap_and_free_tx_resource() %d count\n", + cleaned_count); if (cleaned_count > 0) { /*skip this if nothing cleaned*/ /* Recover from running out of Tx resources in xmit_frame */ spin_lock(&tx_ring->tx_lock); @@ -1622,12 +1653,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, { netif_wake_queue(adapter->netdev); adapter->stats.tx_restart_count++; - pr_debug("Tx wake queue\n"); + netdev_dbg(adapter->netdev, "Tx wake queue\n"); } tx_ring->next_to_clean = i; - pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); + netdev_dbg(adapter->netdev, "next_to_clean : %d\n", + tx_ring->next_to_clean); spin_unlock(&tx_ring->tx_lock); } return cleaned; @@ -1684,22 +1716,22 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, buffer_info->length, DMA_FROM_DEVICE); buffer_info->mapped = false; - pr_debug("RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x " - "TCP:0x%08x] BufInf = 0x%p\n", - i, dma_status, gbec_status, tcp_ip_status, - buffer_info); + netdev_dbg(netdev, + "RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x TCP:0x%08x] BufInf = 0x%p\n", + i, dma_status, gbec_status, tcp_ip_status, + buffer_info); /* Error check */ if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NOTOCTAL)) { adapter->stats.rx_frame_errors++; - pr_err("Receive Not Octal Error\n"); + netdev_err(netdev, "Receive Not Octal Error\n"); } else if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NBLERR)) { adapter->stats.rx_frame_errors++; - pr_err("Receive Nibble Error\n"); + netdev_err(netdev, "Receive Nibble Error\n"); } else if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_CRCERR)) { adapter->stats.rx_crc_errors++; - pr_err("Receive CRC Error\n"); + netdev_err(netdev, "Receive CRC Error\n"); } else { /* get receive length */ /* length convert[-3], length includes FCS length */ @@ -1730,8 +1762,9 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, napi_gro_receive(&adapter->napi, skb); (*work_done)++; - pr_debug("Receive skb->ip_summed: %d length: %d\n", - skb->ip_summed, length); + netdev_dbg(netdev, + "Receive skb->ip_summed: %d length: %d\n", + skb->ip_summed, length); } /* return some buffers to hardware, one at a time is too slow */ if (unlikely(cleaned_count >= PCH_GBE_RX_BUFFER_WRITE)) { @@ -1787,10 +1820,10 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter, tx_desc = PCH_GBE_TX_DESC(*tx_ring, desNo); tx_desc->gbec_status = DSC_INIT16; } - pr_debug("tx_ring->desc = 0x%p tx_ring->dma = 0x%08llx\n" - "next_to_clean = 0x%08x next_to_use = 0x%08x\n", - tx_ring->desc, (unsigned long long)tx_ring->dma, - tx_ring->next_to_clean, tx_ring->next_to_use); + netdev_dbg(adapter->netdev, + "tx_ring->desc = 0x%p tx_ring->dma = 0x%08llx next_to_clean = 0x%08x next_to_use = 0x%08x\n", + tx_ring->desc, (unsigned long long)tx_ring->dma, + tx_ring->next_to_clean, tx_ring->next_to_use); return 0; } @@ -1829,10 +1862,10 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter, rx_desc = PCH_GBE_RX_DESC(*rx_ring, desNo); rx_desc->gbec_status = DSC_INIT16; } - pr_debug("rx_ring->desc = 0x%p rx_ring->dma = 0x%08llx " - "next_to_clean = 0x%08x next_to_use = 0x%08x\n", - rx_ring->desc, (unsigned long long)rx_ring->dma, - rx_ring->next_to_clean, rx_ring->next_to_use); + netdev_dbg(adapter->netdev, + "rx_ring->desc = 0x%p rx_ring->dma = 0x%08llx next_to_clean = 0x%08x next_to_use = 0x%08x\n", + rx_ring->desc, (unsigned long long)rx_ring->dma, + rx_ring->next_to_clean, rx_ring->next_to_use); return 0; } @@ -1886,9 +1919,9 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter) flags = IRQF_SHARED; adapter->have_msi = false; err = pci_enable_msi(adapter->pdev); - pr_debug("call pci_enable_msi\n"); + netdev_dbg(netdev, "call pci_enable_msi\n"); if (err) { - pr_debug("call pci_enable_msi - Error: %d\n", err); + netdev_dbg(netdev, "call pci_enable_msi - Error: %d\n", err); } else { flags = 0; adapter->have_msi = true; @@ -1896,9 +1929,11 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter) err = request_irq(adapter->pdev->irq, &pch_gbe_intr, flags, netdev->name, netdev); if (err) - pr_err("Unable to allocate interrupt Error: %d\n", err); - pr_debug("adapter->have_msi : %d flags : 0x%04x return : 0x%04x\n", - adapter->have_msi, flags, err); + netdev_err(netdev, "Unable to allocate interrupt Error: %d\n", + err); + netdev_dbg(netdev, + "adapter->have_msi : %d flags : 0x%04x return : 0x%04x\n", + adapter->have_msi, flags, err); return err; } @@ -1919,7 +1954,7 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) /* Ensure we have a valid MAC */ if (!is_valid_ether_addr(adapter->hw.mac.addr)) { - pr_err("Error: Invalid MAC address\n"); + netdev_err(netdev, "Error: Invalid MAC address\n"); goto out; } @@ -1933,12 +1968,14 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) err = pch_gbe_request_irq(adapter); if (err) { - pr_err("Error: can't bring device up - irq request failed\n"); + netdev_err(netdev, + "Error: can't bring device up - irq request failed\n"); goto out; } err = pch_gbe_alloc_rx_buffers_pool(adapter, rx_ring, rx_ring->count); if (err) { - pr_err("Error: can't bring device up - alloc rx buffers pool failed\n"); + netdev_err(netdev, + "Error: can't bring device up - alloc rx buffers pool failed\n"); goto freeirq; } pch_gbe_alloc_tx_buffers(adapter, tx_ring); @@ -2015,11 +2052,11 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) /* Initialize the hardware-specific values */ if (pch_gbe_hal_setup_init_funcs(hw)) { - pr_err("Hardware Initialization Failure\n"); + netdev_err(netdev, "Hardware Initialization Failure\n"); return -EIO; } if (pch_gbe_alloc_queues(adapter)) { - pr_err("Unable to allocate memory for queues\n"); + netdev_err(netdev, "Unable to allocate memory for queues\n"); return -ENOMEM; } spin_lock_init(&adapter->hw.miim_lock); @@ -2030,9 +2067,10 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) pch_gbe_init_stats(adapter); - pr_debug("rx_buffer_len : %d mac.min_frame_size : %d mac.max_frame_size : %d\n", - (u32) adapter->rx_buffer_len, - hw->mac.min_frame_size, hw->mac.max_frame_size); + netdev_dbg(netdev, + "rx_buffer_len : %d mac.min_frame_size : %d mac.max_frame_size : %d\n", + (u32) adapter->rx_buffer_len, + hw->mac.min_frame_size, hw->mac.max_frame_size); return 0; } @@ -2061,7 +2099,7 @@ static int pch_gbe_open(struct net_device *netdev) err = pch_gbe_up(adapter); if (err) goto err_up; - pr_debug("Success End\n"); + netdev_dbg(netdev, "Success End\n"); return 0; err_up: @@ -2072,7 +2110,7 @@ err_setup_rx: pch_gbe_free_tx_resources(adapter, adapter->tx_ring); err_setup_tx: pch_gbe_reset(adapter); - pr_err("Error End\n"); + netdev_err(netdev, "Error End\n"); return err; } @@ -2116,8 +2154,9 @@ static int pch_gbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) { netif_stop_queue(netdev); spin_unlock_irqrestore(&tx_ring->tx_lock, flags); - pr_debug("Return : BUSY next_to use : 0x%08x next_to clean : 0x%08x\n", - tx_ring->next_to_use, tx_ring->next_to_clean); + netdev_dbg(netdev, + "Return : BUSY next_to use : 0x%08x next_to clean : 0x%08x\n", + tx_ring->next_to_use, tx_ring->next_to_clean); return NETDEV_TX_BUSY; } @@ -2152,7 +2191,7 @@ static void pch_gbe_set_multi(struct net_device *netdev) int i; int mc_count; - pr_debug("netdev->flags : 0x%08x\n", netdev->flags); + netdev_dbg(netdev, "netdev->flags : 0x%08x\n", netdev->flags); /* Check for Promiscuous and All Multicast modes */ rctl = ioread32(&hw->reg->RX_MODE); @@ -2192,7 +2231,8 @@ static void pch_gbe_set_multi(struct net_device *netdev) PCH_GBE_MAR_ENTRIES); kfree(mta_list); - pr_debug("RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x netdev->mc_count : 0x%08x\n", + netdev_dbg(netdev, + "RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x netdev->mc_count : 0x%08x\n", ioread32(&hw->reg->RX_MODE), mc_count); } @@ -2218,12 +2258,12 @@ static int pch_gbe_set_mac(struct net_device *netdev, void *addr) pch_gbe_mac_mar_set(&adapter->hw, adapter->hw.mac.addr, 0); ret_val = 0; } - pr_debug("ret_val : 0x%08x\n", ret_val); - pr_debug("dev_addr : %pM\n", netdev->dev_addr); - pr_debug("mac_addr : %pM\n", adapter->hw.mac.addr); - pr_debug("MAC_ADR1AB reg : 0x%08x 0x%08x\n", - ioread32(&adapter->hw.reg->mac_adr[0].high), - ioread32(&adapter->hw.reg->mac_adr[0].low)); + netdev_dbg(netdev, "ret_val : 0x%08x\n", ret_val); + netdev_dbg(netdev, "dev_addr : %pM\n", netdev->dev_addr); + netdev_dbg(netdev, "mac_addr : %pM\n", adapter->hw.mac.addr); + netdev_dbg(netdev, "MAC_ADR1AB reg : 0x%08x 0x%08x\n", + ioread32(&adapter->hw.reg->mac_adr[0].high), + ioread32(&adapter->hw.reg->mac_adr[0].low)); return ret_val; } @@ -2245,7 +2285,7 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || (max_frame > PCH_GBE_MAX_JUMBO_FRAME_SIZE)) { - pr_err("Invalid MTU setting\n"); + netdev_err(netdev, "Invalid MTU setting\n"); return -EINVAL; } if (max_frame <= PCH_GBE_FRAME_SIZE_2048) @@ -2274,9 +2314,10 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) adapter->hw.mac.max_frame_size = max_frame; } - pr_debug("max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n", - max_frame, (u32) adapter->rx_buffer_len, netdev->mtu, - adapter->hw.mac.max_frame_size); + netdev_dbg(netdev, + "max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n", + max_frame, (u32) adapter->rx_buffer_len, netdev->mtu, + adapter->hw.mac.max_frame_size); return 0; } @@ -2317,7 +2358,7 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct pch_gbe_adapter *adapter = netdev_priv(netdev); - pr_debug("cmd : 0x%04x\n", cmd); + netdev_dbg(netdev, "cmd : 0x%04x\n", cmd); if (cmd == SIOCSHWTSTAMP) return hwtstamp_ioctl(netdev, ifr, cmd); @@ -2354,7 +2395,7 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) bool poll_end_flag = false; bool cleaned = false; - pr_debug("budget : %d\n", budget); + netdev_dbg(adapter->netdev, "budget : %d\n", budget); pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget); cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring); @@ -2377,8 +2418,9 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) pch_gbe_enable_dma_rx(&adapter->hw); } - pr_debug("poll_end_flag : %d work_done : %d budget : %d\n", - poll_end_flag, work_done, budget); + netdev_dbg(adapter->netdev, + "poll_end_flag : %d work_done : %d budget : %d\n", + poll_end_flag, work_done, budget); return work_done; } @@ -2435,7 +2477,7 @@ static pci_ers_result_t pch_gbe_io_slot_reset(struct pci_dev *pdev) struct pch_gbe_hw *hw = &adapter->hw; if (pci_enable_device(pdev)) { - pr_err("Cannot re-enable PCI device after reset\n"); + netdev_err(netdev, "Cannot re-enable PCI device after reset\n"); return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); @@ -2455,7 +2497,8 @@ static void pch_gbe_io_resume(struct pci_dev *pdev) if (netif_running(netdev)) { if (pch_gbe_up(adapter)) { - pr_debug("can't bring device back up after reset\n"); + netdev_dbg(netdev, + "can't bring device back up after reset\n"); return; } } @@ -2509,7 +2552,7 @@ static int pch_gbe_resume(struct device *device) err = pci_enable_device(pdev); if (err) { - pr_err("Cannot enable PCI device from suspend\n"); + netdev_err(netdev, "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); @@ -2545,13 +2588,7 @@ static void pch_gbe_remove(struct pci_dev *pdev) pch_gbe_hal_phy_hw_reset(&adapter->hw); - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); - - iounmap(adapter->hw.reg); - pci_release_regions(pdev); free_netdev(netdev); - pci_disable_device(pdev); } static int pch_gbe_probe(struct pci_dev *pdev, @@ -2561,7 +2598,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, struct pch_gbe_adapter *adapter; int ret; - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret; @@ -2574,24 +2611,22 @@ static int pch_gbe_probe(struct pci_dev *pdev, if (ret) { dev_err(&pdev->dev, "ERR: No usable DMA " "configuration, aborting\n"); - goto err_disable_device; + return ret; } } } - ret = pci_request_regions(pdev, KBUILD_MODNAME); + ret = pcim_iomap_regions(pdev, 1 << PCH_GBE_PCI_BAR, pci_name(pdev)); if (ret) { dev_err(&pdev->dev, "ERR: Can't reserve PCI I/O and memory resources\n"); - goto err_disable_device; + return ret; } pci_set_master(pdev); netdev = alloc_etherdev((int)sizeof(struct pch_gbe_adapter)); - if (!netdev) { - ret = -ENOMEM; - goto err_release_pci; - } + if (!netdev) + return -ENOMEM; SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); @@ -2599,18 +2634,14 @@ static int pch_gbe_probe(struct pci_dev *pdev, adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; - adapter->hw.reg = pci_iomap(pdev, PCH_GBE_PCI_BAR, 0); - if (!adapter->hw.reg) { - ret = -EIO; - dev_err(&pdev->dev, "Can't ioremap\n"); - goto err_free_netdev; - } + adapter->hw.reg = pcim_iomap_table(pdev)[PCH_GBE_PCI_BAR]; adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number, PCI_DEVFN(12, 4)); if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) { - pr_err("Bad ptp filter\n"); - return -EINVAL; + dev_err(&pdev->dev, "Bad ptp filter\n"); + ret = -EINVAL; + goto err_free_netdev; } netdev->netdev_ops = &pch_gbe_netdev_ops; @@ -2628,7 +2659,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, /* setup the private structure */ ret = pch_gbe_sw_init(adapter); if (ret) - goto err_iounmap; + goto err_free_netdev; /* Initialize PHY */ ret = pch_gbe_init_phy(adapter); @@ -2684,16 +2715,8 @@ static int pch_gbe_probe(struct pci_dev *pdev, err_free_adapter: pch_gbe_hal_phy_hw_reset(&adapter->hw); - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); -err_iounmap: - iounmap(adapter->hw.reg); err_free_netdev: free_netdev(netdev); -err_release_pci: - pci_release_regions(pdev); -err_disable_device: - pci_disable_device(pdev); return ret; } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c index 8653c3b81f8..cf7c9b3a255 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c @@ -237,16 +237,17 @@ static int pch_gbe_validate_option(int *value, case enable_option: switch (*value) { case OPTION_ENABLED: - pr_debug("%s Enabled\n", opt->name); + netdev_dbg(adapter->netdev, "%s Enabled\n", opt->name); return 0; case OPTION_DISABLED: - pr_debug("%s Disabled\n", opt->name); + netdev_dbg(adapter->netdev, "%s Disabled\n", opt->name); return 0; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { - pr_debug("%s set to %i\n", opt->name, *value); + netdev_dbg(adapter->netdev, "%s set to %i\n", + opt->name, *value); return 0; } break; @@ -258,7 +259,8 @@ static int pch_gbe_validate_option(int *value, ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') - pr_debug("%s\n", ent->str); + netdev_dbg(adapter->netdev, "%s\n", + ent->str); return 0; } } @@ -268,8 +270,8 @@ static int pch_gbe_validate_option(int *value, BUG(); } - pr_debug("Invalid %s value specified (%i) %s\n", - opt->name, *value, opt->err); + netdev_dbg(adapter->netdev, "Invalid %s value specified (%i) %s\n", + opt->name, *value, opt->err); *value = opt->def; return -1; } @@ -318,7 +320,8 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) .p = an_list} } }; if (speed || dplx) { - pr_debug("AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n"); + netdev_dbg(adapter->netdev, + "AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n"); hw->phy.autoneg_advertised = opt.def; } else { int tmp = AutoNeg; @@ -332,13 +335,16 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) case 0: hw->mac.autoneg = hw->mac.fc_autoneg = 1; if ((speed || dplx)) - pr_debug("Speed and duplex autonegotiation enabled\n"); + netdev_dbg(adapter->netdev, + "Speed and duplex autonegotiation enabled\n"); hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_HALF; break; case HALF_DUPLEX: - pr_debug("Half Duplex specified without Speed\n"); - pr_debug("Using Autonegotiation at Half Duplex only\n"); + netdev_dbg(adapter->netdev, + "Half Duplex specified without Speed\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at Half Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF | PHY_ADVERTISE_100_HALF; @@ -346,8 +352,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case FULL_DUPLEX: - pr_debug("Full Duplex specified without Speed\n"); - pr_debug("Using Autonegotiation at Full Duplex only\n"); + netdev_dbg(adapter->netdev, + "Full Duplex specified without Speed\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at Full Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_FULL | PHY_ADVERTISE_100_FULL | @@ -356,8 +364,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_10: - pr_debug("10 Mbps Speed specified without Duplex\n"); - pr_debug("Using Autonegotiation at 10 Mbps only\n"); + netdev_dbg(adapter->netdev, + "10 Mbps Speed specified without Duplex\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at 10 Mbps only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF | PHY_ADVERTISE_10_FULL; @@ -365,22 +375,24 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_10 + HALF_DUPLEX: - pr_debug("Forcing to 10 Mbps Half Duplex\n"); + netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Half Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_10 + FULL_DUPLEX: - pr_debug("Forcing to 10 Mbps Full Duplex\n"); + netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Full Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_100: - pr_debug("100 Mbps Speed specified without Duplex\n"); - pr_debug("Using Autonegotiation at 100 Mbps only\n"); + netdev_dbg(adapter->netdev, + "100 Mbps Speed specified without Duplex\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at 100 Mbps only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_100_HALF | PHY_ADVERTISE_100_FULL; @@ -388,28 +400,33 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_100 + HALF_DUPLEX: - pr_debug("Forcing to 100 Mbps Half Duplex\n"); + netdev_dbg(adapter->netdev, + "Forcing to 100 Mbps Half Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_100; hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_100 + FULL_DUPLEX: - pr_debug("Forcing to 100 Mbps Full Duplex\n"); + netdev_dbg(adapter->netdev, + "Forcing to 100 Mbps Full Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_100; hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_1000: - pr_debug("1000 Mbps Speed specified without Duplex\n"); + netdev_dbg(adapter->netdev, + "1000 Mbps Speed specified without Duplex\n"); goto full_duplex_only; case SPEED_1000 + HALF_DUPLEX: - pr_debug("Half Duplex is not supported at 1000 Mbps\n"); + netdev_dbg(adapter->netdev, + "Half Duplex is not supported at 1000 Mbps\n"); /* fall through */ case SPEED_1000 + FULL_DUPLEX: full_duplex_only: - pr_debug("Using Autonegotiation at 1000 Mbps Full Duplex only\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_1000_FULL; hw->mac.link_speed = SPEED_1000; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c index 28bb9603d73..da079073a6c 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c @@ -97,6 +97,7 @@ */ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct pch_gbe_phy_info *phy = &hw->phy; s32 ret; u16 phy_id1; @@ -115,8 +116,9 @@ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw) phy->id = (u32)phy_id1; phy->id = ((phy->id << 6) | ((phy_id2 & 0xFC00) >> 10)); phy->revision = (u32) (phy_id2 & 0x000F); - pr_debug("phy->id : 0x%08x phy->revision : 0x%08x\n", - phy->id, phy->revision); + netdev_dbg(adapter->netdev, + "phy->id : 0x%08x phy->revision : 0x%08x\n", + phy->id, phy->revision); return 0; } @@ -134,7 +136,10 @@ s32 pch_gbe_phy_read_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 *data) struct pch_gbe_phy_info *phy = &hw->phy; if (offset > PHY_MAX_REG_ADDRESS) { - pr_err("PHY Address %d is out of range\n", offset); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "PHY Address %d is out of range\n", + offset); return -EINVAL; } *data = pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_READ, @@ -156,7 +161,10 @@ s32 pch_gbe_phy_write_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 data) struct pch_gbe_phy_info *phy = &hw->phy; if (offset > PHY_MAX_REG_ADDRESS) { - pr_err("PHY Address %d is out of range\n", offset); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "PHY Address %d is out of range\n", + offset); return -EINVAL; } pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_WRITE, @@ -235,7 +243,7 @@ void pch_gbe_phy_power_down(struct pch_gbe_hw *hw) * pch_gbe_phy_set_rgmii - RGMII interface setting * @hw: Pointer to the HW structure */ -inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) +void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) { pch_gbe_phy_sw_reset(hw); } @@ -246,15 +254,14 @@ inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) */ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter; + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; int ret; u16 mii_reg; - adapter = container_of(hw, struct pch_gbe_adapter, hw); ret = mii_ethtool_gset(&adapter->mii, &cmd); if (ret) - pr_err("Error: mii_ethtool_gset\n"); + netdev_err(adapter->netdev, "Error: mii_ethtool_gset\n"); ethtool_cmd_speed_set(&cmd, hw->mac.link_speed); cmd.duplex = hw->mac.link_duplex; @@ -263,12 +270,11 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) pch_gbe_phy_write_reg_miic(hw, MII_BMCR, BMCR_RESET); ret = mii_ethtool_sset(&adapter->mii, &cmd); if (ret) - pr_err("Error: mii_ethtool_sset\n"); + netdev_err(adapter->netdev, "Error: mii_ethtool_sset\n"); pch_gbe_phy_sw_reset(hw); pch_gbe_phy_read_reg_miic(hw, PHY_PHYSP_CONTROL, &mii_reg); mii_reg |= PHYSP_CTRL_ASSERT_CRS_TX; pch_gbe_phy_write_reg_miic(hw, PHY_PHYSP_CONTROL, mii_reg); - } diff --git a/drivers/net/ethernet/packetengines/Kconfig b/drivers/net/ethernet/packetengines/Kconfig index cbbeca3f8c5..8d5180043c7 100644 --- a/drivers/net/ethernet/packetengines/Kconfig +++ b/drivers/net/ethernet/packetengines/Kconfig @@ -21,7 +21,6 @@ if NET_PACKET_ENGINE config HAMACHI tristate "Packet Engines Hamachi GNIC-II support" depends on PCI - select NET_CORE select MII ---help--- If you have a Gigabit Ethernet card of this type, say Y and read diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index 322a36b7672..3fe09ab2d7c 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -53,8 +53,8 @@ #define _NETXEN_NIC_LINUX_MAJOR 4 #define _NETXEN_NIC_LINUX_MINOR 0 -#define _NETXEN_NIC_LINUX_SUBVERSION 80 -#define NETXEN_NIC_LINUX_VERSIONID "4.0.80" +#define _NETXEN_NIC_LINUX_SUBVERSION 81 +#define NETXEN_NIC_LINUX_VERSIONID "4.0.81" #define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c)) #define _major(v) (((v) >> 24) & 0xff) @@ -1855,7 +1855,7 @@ static const struct netxen_brdinfo netxen_boards[] = { #define NUM_SUPPORTED_BOARDS ARRAY_SIZE(netxen_boards) -static inline void get_brd_name_by_type(u32 type, char *name) +static inline int netxen_nic_get_brd_name_by_type(u32 type, char *name) { int i, found = 0; for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) { @@ -1864,10 +1864,14 @@ static inline void get_brd_name_by_type(u32 type, char *name) found = 1; break; } + } + if (!found) { + strcpy(name, "Unknown"); + return -EINVAL; } - if (!found) - name = "Unknown"; + + return 0; } static inline u32 netxen_tx_avail(struct nx_host_tx_ring *tx_ring) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h index 28e076960bc..32c790659f9 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h @@ -734,6 +734,9 @@ enum { #define NIC_CRB_BASE_2 (NETXEN_CAM_RAM(0x700)) #define NETXEN_NIC_REG(X) (NIC_CRB_BASE+(X)) #define NETXEN_NIC_REG_2(X) (NIC_CRB_BASE_2+(X)) +#define NETXEN_INTR_MODE_REG NETXEN_NIC_REG(0x44) +#define NETXEN_MSI_MODE 0x1 +#define NETXEN_INTX_MODE 0x2 #define NX_CDRP_CRB_OFFSET (NETXEN_NIC_REG(0x18)) #define NX_ARG1_CRB_OFFSET (NETXEN_NIC_REG(0x1c)) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index af951f343ff..c401b0b4353 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -592,48 +592,60 @@ static const struct net_device_ops netxen_netdev_ops = { #endif }; -static void -netxen_setup_intr(struct netxen_adapter *adapter) +static inline bool netxen_function_zero(struct pci_dev *pdev) { - struct netxen_legacy_intr_set *legacy_intrp; - struct pci_dev *pdev = adapter->pdev; - int err, num_msix; + return (PCI_FUNC(pdev->devfn) == 0) ? true : false; +} - if (adapter->rss_supported) { - num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ? - MSIX_ENTRIES_PER_ADAPTER : 2; - } else - num_msix = 1; +static inline void netxen_set_interrupt_mode(struct netxen_adapter *adapter, + u32 mode) +{ + NXWR32(adapter, NETXEN_INTR_MODE_REG, mode); +} - adapter->max_sds_rings = 1; +static inline u32 netxen_get_interrupt_mode(struct netxen_adapter *adapter) +{ + return NXRD32(adapter, NETXEN_INTR_MODE_REG); +} - adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED); +static void +netxen_initialize_interrupt_registers(struct netxen_adapter *adapter) +{ + struct netxen_legacy_intr_set *legacy_intrp; + u32 tgt_status_reg, int_state_reg; if (adapter->ahw.revision_id >= NX_P3_B0) legacy_intrp = &legacy_intr[adapter->ahw.pci_func]; else legacy_intrp = &legacy_intr[0]; + tgt_status_reg = legacy_intrp->tgt_status_reg; + int_state_reg = ISR_INT_STATE_REG; + adapter->int_vec_bit = legacy_intrp->int_vec_bit; - adapter->tgt_status_reg = netxen_get_ioaddr(adapter, - legacy_intrp->tgt_status_reg); + adapter->tgt_status_reg = netxen_get_ioaddr(adapter, tgt_status_reg); adapter->tgt_mask_reg = netxen_get_ioaddr(adapter, - legacy_intrp->tgt_mask_reg); + legacy_intrp->tgt_mask_reg); adapter->pci_int_reg = netxen_get_ioaddr(adapter, - legacy_intrp->pci_int_reg); + legacy_intrp->pci_int_reg); adapter->isr_int_vec = netxen_get_ioaddr(adapter, ISR_INT_VECTOR); if (adapter->ahw.revision_id >= NX_P3_B1) adapter->crb_int_state_reg = netxen_get_ioaddr(adapter, - ISR_INT_STATE_REG); + int_state_reg); else adapter->crb_int_state_reg = netxen_get_ioaddr(adapter, - CRB_INT_VECTOR); + CRB_INT_VECTOR); +} - netxen_set_msix_bit(pdev, 0); +static int netxen_setup_msi_interrupts(struct netxen_adapter *adapter, + int num_msix) +{ + struct pci_dev *pdev = adapter->pdev; + u32 value; + int err; if (adapter->msix_supported) { - netxen_init_msix_entries(adapter, num_msix); err = pci_enable_msix(pdev, adapter->msix_entries, num_msix); if (err == 0) { @@ -644,26 +656,59 @@ netxen_setup_intr(struct netxen_adapter *adapter) adapter->max_sds_rings = num_msix; dev_info(&pdev->dev, "using msi-x interrupts\n"); - return; + return 0; } - - if (err > 0) - pci_disable_msix(pdev); - /* fall through for msi */ } if (use_msi && !pci_enable_msi(pdev)) { + value = msi_tgt_status[adapter->ahw.pci_func]; adapter->flags |= NETXEN_NIC_MSI_ENABLED; - adapter->tgt_status_reg = netxen_get_ioaddr(adapter, - msi_tgt_status[adapter->ahw.pci_func]); - dev_info(&pdev->dev, "using msi interrupts\n"); + adapter->tgt_status_reg = netxen_get_ioaddr(adapter, value); adapter->msix_entries[0].vector = pdev->irq; - return; + dev_info(&pdev->dev, "using msi interrupts\n"); + return 0; } - dev_info(&pdev->dev, "using legacy interrupts\n"); - adapter->msix_entries[0].vector = pdev->irq; + dev_err(&pdev->dev, "Failed to acquire MSI-X/MSI interrupt vector\n"); + return -EIO; +} + +static int netxen_setup_intr(struct netxen_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int num_msix; + + if (adapter->rss_supported) + num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ? + MSIX_ENTRIES_PER_ADAPTER : 2; + else + num_msix = 1; + + adapter->max_sds_rings = 1; + adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED); + + netxen_initialize_interrupt_registers(adapter); + netxen_set_msix_bit(pdev, 0); + + if (netxen_function_zero(pdev)) { + if (!netxen_setup_msi_interrupts(adapter, num_msix)) + netxen_set_interrupt_mode(adapter, NETXEN_MSI_MODE); + else + netxen_set_interrupt_mode(adapter, NETXEN_INTX_MODE); + } else { + if (netxen_get_interrupt_mode(adapter) == NETXEN_MSI_MODE && + netxen_setup_msi_interrupts(adapter, num_msix)) { + dev_err(&pdev->dev, "Co-existence of MSI-X/MSI and INTx interrupts is not supported\n"); + return -EIO; + } + } + + if (!NETXEN_IS_MSI_FAMILY(adapter)) { + adapter->msix_entries[0].vector = pdev->irq; + dev_info(&pdev->dev, "using legacy interrupts\n"); + } + return 0; } static void @@ -841,7 +886,9 @@ netxen_check_options(struct netxen_adapter *adapter) } if (adapter->portnum == 0) { - get_brd_name_by_type(adapter->ahw.board_type, brd_name); + if (netxen_nic_get_brd_name_by_type(adapter->ahw.board_type, + brd_name)) + strcpy(serial_num, "Unknown"); pr_info("%s: %s Board S/N %s Chip rev 0x%x\n", module_name(THIS_MODULE), @@ -860,9 +907,9 @@ netxen_check_options(struct netxen_adapter *adapter) adapter->ahw.cut_through = (i & 0x8000) ? 1 : 0; } - dev_info(&pdev->dev, "firmware v%d.%d.%d [%s]\n", - fw_major, fw_minor, fw_build, - adapter->ahw.cut_through ? "cut-through" : "legacy"); + dev_info(&pdev->dev, "Driver v%s, firmware v%d.%d.%d [%s]\n", + NETXEN_NIC_LINUX_VERSIONID, fw_major, fw_minor, fw_build, + adapter->ahw.cut_through ? "cut-through" : "legacy"); if (adapter->fw_version >= NETXEN_VERSION_CODE(4, 0, 222)) adapter->capabilities = NXRD32(adapter, CRB_FW_CAPABILITIES_1); @@ -1508,7 +1555,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netxen_nic_clear_stats(adapter); - netxen_setup_intr(adapter); + err = netxen_setup_intr(adapter); + + if (err) { + dev_err(&adapter->pdev->dev, + "Failed to setup interrupts, error = %d\n", err); + goto err_out_disable_msi; + } err = netxen_setup_netdev(adapter, netdev); if (err) @@ -1596,7 +1649,7 @@ static void netxen_nic_remove(struct pci_dev *pdev) clear_bit(__NX_RESETTING, &adapter->state); netxen_teardown_intr(adapter); - + netxen_set_interrupt_mode(adapter, 0); netxen_remove_diag_entries(adapter); netxen_cleanup_pci_map(adapter); @@ -2721,7 +2774,7 @@ netxen_store_bridged_mode(struct device *dev, if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) goto err_out; - if (strict_strtoul(buf, 2, &new)) + if (kstrtoul(buf, 2, &new)) goto err_out; if (!netxen_config_bridged_mode(adapter, !!new)) @@ -2760,7 +2813,7 @@ netxen_store_diag_mode(struct device *dev, struct netxen_adapter *adapter = dev_get_drvdata(dev); unsigned long new; - if (strict_strtoul(buf, 2, &new)) + if (kstrtoul(buf, 2, &new)) return -EINVAL; if (!!new != !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)) @@ -3311,7 +3364,7 @@ static int netxen_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct netxen_adapter *adapter; - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *orig_dev = dev; struct net_device *slave; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index c1b693cb3df..b00cf5665ea 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -38,8 +38,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 2 -#define _QLCNIC_LINUX_SUBVERSION 42 -#define QLCNIC_LINUX_VERSIONID "5.2.42" +#define _QLCNIC_LINUX_SUBVERSION 44 +#define QLCNIC_LINUX_VERSIONID "5.2.44" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) @@ -303,7 +303,6 @@ extern int qlcnic_use_msi; extern int qlcnic_use_msi_x; extern int qlcnic_auto_fw_reset; extern int qlcnic_load_fw_file; -extern int qlcnic_config_npars; /* Number of status descriptors to handle per interrupt */ #define MAX_STATUS_HANDLE (64) @@ -394,6 +393,9 @@ struct qlcnic_fw_dump { u32 size; /* total size of the dump */ void *data; /* dump data area */ struct qlcnic_dump_template_hdr *tmpl_hdr; + dma_addr_t phys_addr; + void *dma_buffer; + bool use_pex_dma; }; /* @@ -427,6 +429,7 @@ struct qlcnic_hardware_context { u8 nic_mode; char diag_cnt; + u16 max_uc_count; u16 port_type; u16 board_type; u16 supported_type; @@ -443,9 +446,10 @@ struct qlcnic_hardware_context { u16 max_mtu; u32 msg_enable; u16 act_pci_func; + u16 max_pci_func; u32 capabilities; - u32 capabilities2; + u32 extra_capability[3]; u32 temp; u32 int_vec_bit; u32 fw_hal_version; @@ -815,7 +819,8 @@ struct qlcnic_mac_list_s { #define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2 #define QLCNIC_FW_CAP2_HW_LRO_IPV6 BIT_3 -#define QLCNIC_FW_CAPABILITY_2_OCBB BIT_5 +#define QLCNIC_FW_CAPABILITY_SET_DRV_VER BIT_5 +#define QLCNIC_FW_CAPABILITY_2_BEACON BIT_7 /* module types */ #define LINKEVENT_MODULE_NOT_PRESENT 1 @@ -913,6 +918,9 @@ struct qlcnic_ipaddr { #define QLCNIC_IS_TSO_CAPABLE(adapter) \ ((adapter)->ahw->capabilities & QLCNIC_FW_CAPABILITY_TSO) +#define QLCNIC_BEACON_EANBLE 0xC +#define QLCNIC_BEACON_DISABLE 0xD + #define QLCNIC_DEF_NUM_STS_DESC_RINGS 4 #define QLCNIC_MSIX_TBL_SPACE 8192 #define QLCNIC_PCI_REG_MSIX_TBL 0x44 @@ -932,6 +940,7 @@ struct qlcnic_ipaddr { #define __QLCNIC_SRIOV_ENABLE 10 #define __QLCNIC_SRIOV_CAPABLE 11 #define __QLCNIC_MBX_POLL_ENABLE 12 +#define __QLCNIC_DIAG_MODE 13 #define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_LOOPBACK_TEST 2 @@ -1467,7 +1476,7 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter); int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu); -int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *); +int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *, u32); int qlcnic_change_mtu(struct net_device *netdev, int new_mtu); netdev_features_t qlcnic_fix_features(struct net_device *netdev, netdev_features_t features); @@ -1489,7 +1498,9 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, size_t); int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32); void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); +void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *); int qlcnic_enable_msix(struct qlcnic_adapter *, u32); +void qlcnic_set_drv_version(struct qlcnic_adapter *); /* eSwitch management functions */ int qlcnic_config_switch_port(struct qlcnic_adapter *, @@ -1543,6 +1554,7 @@ int qlcnic_set_default_offload_settings(struct qlcnic_adapter *); int qlcnic_reset_npar_config(struct qlcnic_adapter *); int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *); void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, u16); +int qlcnic_get_beacon_state(struct qlcnic_adapter *, u8 *); int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); int qlcnic_read_mac_addr(struct qlcnic_adapter *); int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); @@ -1584,6 +1596,8 @@ struct qlcnic_nic_template { void (*napi_del)(struct qlcnic_adapter *); void (*config_ipaddr)(struct qlcnic_adapter *, __be32, int); irqreturn_t (*clear_legacy_intr)(struct qlcnic_adapter *); + int (*shutdown)(struct pci_dev *); + int (*resume)(struct qlcnic_adapter *); }; /* Adapter hardware abstraction */ @@ -1625,6 +1639,7 @@ struct qlcnic_hardware_ops { int (*config_promisc_mode) (struct qlcnic_adapter *, u32); void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16); int (*get_board_info) (struct qlcnic_adapter *); + void (*set_mac_filter_count) (struct qlcnic_adapter *); void (*free_mac_list) (struct qlcnic_adapter *); }; @@ -1787,6 +1802,18 @@ static inline void qlcnic_napi_enable(struct qlcnic_adapter *adapter) adapter->ahw->hw_ops->napi_enable(adapter); } +static inline int __qlcnic_shutdown(struct pci_dev *pdev) +{ + struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); + + return adapter->nic_ops->shutdown(pdev); +} + +static inline int __qlcnic_resume(struct qlcnic_adapter *adapter) +{ + return adapter->nic_ops->resume(adapter); +} + static inline void qlcnic_napi_disable(struct qlcnic_adapter *adapter) { adapter->ahw->hw_ops->napi_disable(adapter); @@ -1840,6 +1867,11 @@ static inline void qlcnic_free_mac_list(struct qlcnic_adapter *adapter) return adapter->ahw->hw_ops->free_mac_list(adapter); } +static inline void qlcnic_set_mac_filter_count(struct qlcnic_adapter *adapter) +{ + adapter->ahw->hw_ops->set_mac_filter_count(adapter); +} + static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter, u32 key) { @@ -1886,6 +1918,21 @@ static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring) writel(0xfbff, adapter->tgt_mask_reg); } +static inline int qlcnic_get_diag_lock(struct qlcnic_adapter *adapter) +{ + return test_and_set_bit(__QLCNIC_DIAG_MODE, &adapter->state); +} + +static inline void qlcnic_release_diag_lock(struct qlcnic_adapter *adapter) +{ + clear_bit(__QLCNIC_DIAG_MODE, &adapter->state); +} + +static inline int qlcnic_check_diag_status(struct qlcnic_adapter *adapter) +{ + return test_bit(__QLCNIC_DIAG_MODE, &adapter->state); +} + extern const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_failed_ops; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index b4ff1e35a11..0913c623a67 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -63,6 +63,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = { {QLCNIC_CMD_STOP_NIC_FUNC, 2, 1}, {QLCNIC_CMD_SET_LED_CONFIG, 5, 1}, {QLCNIC_CMD_GET_LED_CONFIG, 1, 5}, + {QLCNIC_CMD_83XX_SET_DRV_VER, 4, 1}, {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, {QLCNIC_CMD_CONFIG_VPORT, 4, 4}, {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1}, @@ -172,6 +173,7 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = { .config_promisc_mode = qlcnic_83xx_nic_set_promisc, .change_l2_filter = qlcnic_83xx_change_l2_filter, .get_board_info = qlcnic_83xx_get_port_info, + .set_mac_filter_count = qlcnic_83xx_set_mac_filter_count, .free_mac_list = qlcnic_82xx_free_mac_list, }; @@ -184,6 +186,8 @@ static struct qlcnic_nic_template qlcnic_83xx_ops = { .napi_del = qlcnic_83xx_napi_del, .config_ipaddr = qlcnic_83xx_config_ipaddr, .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr, + .shutdown = qlcnic_83xx_shutdown, + .resume = qlcnic_83xx_resume, }; void qlcnic_83xx_register_map(struct qlcnic_hardware_context *ahw) @@ -312,6 +316,11 @@ inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter) writel(0, adapter->tgt_mask_reg); } +inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter) +{ + writel(1, adapter->tgt_mask_reg); +} + /* Enable MSI-x and INT-x interrupts */ void qlcnic_83xx_enable_intr(struct qlcnic_adapter *adapter, struct qlcnic_host_sds_ring *sds_ring) @@ -458,6 +467,9 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter) { u32 num_msix; + if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) + qlcnic_83xx_set_legacy_intr_mask(adapter); + qlcnic_83xx_disable_mbx_intr(adapter); if (adapter->flags & QLCNIC_MSIX_ENABLED) @@ -474,7 +486,6 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter) { irq_handler_t handler; u32 val; - char name[32]; int err = 0; unsigned long flags = 0; @@ -485,9 +496,7 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter) if (adapter->flags & QLCNIC_MSIX_ENABLED) { handler = qlcnic_83xx_handle_aen; val = adapter->msix_entries[adapter->ahw->num_msix - 1].vector; - snprintf(name, (IFNAMSIZ + 4), - "%s[%s]", "qlcnic", "aen"); - err = request_irq(val, handler, flags, name, adapter); + err = request_irq(val, handler, flags, "qlcnic-MB", adapter); if (err) { dev_err(&adapter->pdev->dev, "failed to register MBX interrupt\n"); @@ -604,6 +613,22 @@ int qlcnic_83xx_get_port_info(struct qlcnic_adapter *adapter) return status; } +void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + u16 act_pci_fn = ahw->act_pci_func; + u16 count; + + ahw->max_mc_count = QLC_83XX_MAX_MC_COUNT; + if (act_pci_fn <= 2) + count = (QLC_83XX_MAX_UC_COUNT - QLC_83XX_MAX_MC_COUNT) / + act_pci_fn; + else + count = (QLC_83XX_LB_MAX_FILTERS - QLC_83XX_MAX_MC_COUNT) / + act_pci_fn; + ahw->max_uc_count = count; +} + void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *adapter) { u32 val; @@ -839,7 +864,9 @@ void qlcnic_83xx_idc_aen_work(struct work_struct *work) int i, err = 0; adapter = container_of(work, struct qlcnic_adapter, idc_aen_work.work); - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK); + if (err) + return; for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++) cmd.req.arg[i] = adapter->ahw->mbox_aen[i]; @@ -1080,8 +1107,10 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter) cap |= QLC_83XX_FW_CAP_LRO_MSS; /* set mailbox hdr and capabilities */ - qlcnic_alloc_mbx_args(&cmd, adapter, - QLCNIC_CMD_CREATE_RX_CTX); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_CREATE_RX_CTX); + if (err) + return err; if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter)) cmd.req.arg[0] |= (0x3 << 29); @@ -1239,7 +1268,9 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter, mbx.intr_id = 0xffff; mbx.src = 0; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX); + if (err) + return err; if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter)) cmd.req.arg[0] |= (0x3 << 29); @@ -1385,8 +1416,11 @@ int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state, if (state) { /* Get LED configuration */ - qlcnic_alloc_mbx_args(&cmd, adapter, - QLCNIC_CMD_GET_LED_CONFIG); + status = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_GET_LED_CONFIG); + if (status) + return status; + status = qlcnic_issue_cmd(adapter, &cmd); if (status) { dev_err(&adapter->pdev->dev, @@ -1400,8 +1434,11 @@ int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state, /* Set LED Configuration */ mbx_in = (LSW(QLC_83XX_LED_CONFIG) << 16) | LSW(QLC_83XX_LED_CONFIG); - qlcnic_alloc_mbx_args(&cmd, adapter, - QLCNIC_CMD_SET_LED_CONFIG); + status = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_SET_LED_CONFIG); + if (status) + return status; + cmd.req.arg[1] = mbx_in; cmd.req.arg[2] = mbx_in; cmd.req.arg[3] = mbx_in; @@ -1418,8 +1455,11 @@ mbx_err: } else { /* Restoring default LED configuration */ - qlcnic_alloc_mbx_args(&cmd, adapter, - QLCNIC_CMD_SET_LED_CONFIG); + status = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_SET_LED_CONFIG); + if (status) + return status; + cmd.req.arg[1] = adapter->ahw->mbox_reg[0]; cmd.req.arg[2] = adapter->ahw->mbox_reg[1]; cmd.req.arg[3] = adapter->ahw->mbox_reg[2]; @@ -1489,10 +1529,18 @@ void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *adapter, return; if (enable) { - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INIT_NIC_FUNC); + status = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_INIT_NIC_FUNC); + if (status) + return; + cmd.req.arg[1] = BIT_0 | BIT_31; } else { - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC); + status = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_STOP_NIC_FUNC); + if (status) + return; + cmd.req.arg[1] = BIT_0 | BIT_31; } status = qlcnic_issue_cmd(adapter, &cmd); @@ -1509,7 +1557,10 @@ int qlcnic_83xx_set_port_config(struct qlcnic_adapter *adapter) struct qlcnic_cmd_args cmd; int err; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG); + if (err) + return err; + cmd.req.arg[1] = adapter->ahw->port_config; err = qlcnic_issue_cmd(adapter, &cmd); if (err) @@ -1523,7 +1574,10 @@ int qlcnic_83xx_get_port_config(struct qlcnic_adapter *adapter) struct qlcnic_cmd_args cmd; int err; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG); + if (err) + return err; + err = qlcnic_issue_cmd(adapter, &cmd); if (err) dev_info(&adapter->pdev->dev, "Get Port config failed\n"); @@ -1539,7 +1593,10 @@ int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable) u32 temp; struct qlcnic_cmd_args cmd; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT); + if (err) + return err; + temp = adapter->recv_ctx->context_id << 16; cmd.req.arg[1] = (enable ? 1 : 0) | BIT_8 | temp; err = qlcnic_issue_cmd(adapter, &cmd); @@ -1570,7 +1627,11 @@ int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) return -EIO; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_MAC_RX_MODE); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_CONFIGURE_MAC_RX_MODE); + if (err) + return err; + qlcnic_83xx_set_interface_id_promisc(adapter, &temp); cmd.req.arg[1] = (mode ? 1 : 0) | temp; err = qlcnic_issue_cmd(adapter, &cmd); @@ -1588,16 +1649,24 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) struct qlcnic_hardware_context *ahw = adapter->ahw; int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings; - QLCDB(adapter, DRV, "%s loopback test in progress\n", - mode == QLCNIC_ILB_MODE ? "internal" : "external"); if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { - dev_warn(&adapter->pdev->dev, - "Loopback test not supported for non privilege function\n"); + netdev_warn(netdev, + "Loopback test not supported in non privileged mode\n"); return ret; } - if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + netdev_info(netdev, "Device is resetting\n"); return -EBUSY; + } + + if (qlcnic_get_diag_lock(adapter)) { + netdev_info(netdev, "Device is in diagnostics mode\n"); + return -EBUSY; + } + + netdev_info(netdev, "%s loopback test in progress\n", + mode == QLCNIC_ILB_MODE ? "internal" : "external"); ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST, max_sds_rings); @@ -1610,13 +1679,19 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) /* Poll for link up event before running traffic */ do { - msleep(500); + msleep(QLC_83XX_LB_MSLEEP_COUNT); if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) qlcnic_83xx_process_aen(adapter); - if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { - dev_info(&adapter->pdev->dev, - "Firmware didn't sent link up event to loopback request\n"); + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + netdev_info(netdev, + "Device is resetting, free LB test resources\n"); + ret = -EIO; + goto free_diag_res; + } + if (loop++ > QLC_83XX_LB_WAIT_COUNT) { + netdev_info(netdev, + "Firmware didn't sent link up event to loopback request\n"); ret = -QLCNIC_FW_NOT_RESPOND; qlcnic_83xx_clear_lb_mode(adapter, mode); goto free_diag_res; @@ -1638,13 +1713,14 @@ free_diag_res: fail_diag_alloc: adapter->max_sds_rings = max_sds_rings; - clear_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_release_diag_lock(adapter); return ret; } int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) { struct qlcnic_hardware_context *ahw = adapter->ahw; + struct net_device *netdev = adapter->netdev; int status = 0, loop = 0; u32 config; @@ -1662,9 +1738,9 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) status = qlcnic_83xx_set_port_config(adapter); if (status) { - dev_err(&adapter->pdev->dev, - "Failed to Set Loopback Mode = 0x%x.\n", - ahw->port_config); + netdev_err(netdev, + "Failed to Set Loopback Mode = 0x%x.\n", + ahw->port_config); ahw->port_config = config; clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status); return status; @@ -1672,13 +1748,19 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) /* Wait for Link and IDC Completion AEN */ do { - msleep(300); + msleep(QLC_83XX_LB_MSLEEP_COUNT); if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) qlcnic_83xx_process_aen(adapter); - if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { - dev_err(&adapter->pdev->dev, - "FW did not generate IDC completion AEN\n"); + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + netdev_info(netdev, + "Device is resetting, free LB test resources\n"); + clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status); + return -EIO; + } + if (loop++ > QLC_83XX_LB_WAIT_COUNT) { + netdev_err(netdev, + "Did not receive IDC completion AEN\n"); clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status); qlcnic_83xx_clear_lb_mode(adapter, mode); return -EIO; @@ -1693,6 +1775,7 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode) { struct qlcnic_hardware_context *ahw = adapter->ahw; + struct net_device *netdev = adapter->netdev; int status = 0, loop = 0; u32 config = ahw->port_config; @@ -1704,9 +1787,9 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode) status = qlcnic_83xx_set_port_config(adapter); if (status) { - dev_err(&adapter->pdev->dev, - "Failed to Clear Loopback Mode = 0x%x.\n", - ahw->port_config); + netdev_err(netdev, + "Failed to Clear Loopback Mode = 0x%x.\n", + ahw->port_config); ahw->port_config = config; clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status); return status; @@ -1714,13 +1797,20 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode) /* Wait for Link and IDC Completion AEN */ do { - msleep(300); + msleep(QLC_83XX_LB_MSLEEP_COUNT); if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) qlcnic_83xx_process_aen(adapter); - if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { - dev_err(&adapter->pdev->dev, - "Firmware didn't sent IDC completion AEN\n"); + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + netdev_info(netdev, + "Device is resetting, free LB test resources\n"); + clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status); + return -EIO; + } + + if (loop++ > QLC_83XX_LB_WAIT_COUNT) { + netdev_err(netdev, + "Did not receive IDC completion AEN\n"); clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status); return -EIO; } @@ -1749,7 +1839,11 @@ void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip, u32 temp = 0, temp_ip; struct qlcnic_cmd_args cmd; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_IP_ADDR); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_CONFIGURE_IP_ADDR); + if (err) + return; + qlcnic_83xx_set_interface_id_ipaddr(adapter, &temp); if (mode == QLCNIC_IP_UP) @@ -1788,7 +1882,10 @@ int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *adapter, int mode) if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) return 0; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO); + if (err) + return err; + temp = adapter->recv_ctx->context_id << 16; arg1 = lro_bit_mask | temp; cmd.req.arg[1] = arg1; @@ -1810,8 +1907,9 @@ int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable) 0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL, 0x255b0ec26d5a56daULL }; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS); - + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS); + if (err) + return err; /* * RSS request: * bits 3-0: Rsvd @@ -1917,7 +2015,10 @@ int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac) struct qlcnic_cmd_args cmd; u32 mac_low, mac_high; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS); + if (err) + return err; + qlcnic_83xx_configure_mac(adapter, mac, QLCNIC_GET_CURRENT_MAC, &cmd); err = qlcnic_issue_cmd(adapter, &cmd); @@ -1948,7 +2049,10 @@ void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *adapter) if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) return; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL); + if (err) + return; + if (coal->type == QLCNIC_INTR_COAL_TYPE_RX) { temp = adapter->recv_ctx->context_id; cmd.req.arg[1] = QLCNIC_INTR_COAL_TYPE_RX | temp << 16; @@ -2020,7 +2124,10 @@ int qlcnic_enable_eswitch(struct qlcnic_adapter *adapter, u8 port, u8 enable) return err; } - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH); + if (err) + return err; + cmd.req.arg[1] = (port & 0xf) | BIT_4; err = qlcnic_issue_cmd(adapter, &cmd); @@ -2048,7 +2155,10 @@ int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *adapter, return err; } - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); + if (err) + return err; + cmd.req.arg[1] = (nic->pci_func << 16); cmd.req.arg[2] = 0x1 << 16; cmd.req.arg[3] = nic->phys_port | (nic->switch_mode << 16); @@ -2079,13 +2189,17 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter, u32 temp; u8 op = 0; struct qlcnic_cmd_args cmd; + struct qlcnic_hardware_context *ahw = adapter->ahw; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); - if (func_id != adapter->ahw->pci_func) { + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); + if (err) + return err; + + if (func_id != ahw->pci_func) { temp = func_id << 16; cmd.req.arg[1] = op | BIT_31 | temp; } else { - cmd.req.arg[1] = adapter->ahw->pci_func << 16; + cmd.req.arg[1] = ahw->pci_func << 16; } err = qlcnic_issue_cmd(adapter, &cmd); if (err) { @@ -2112,6 +2226,9 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter, temp = (cmd.rsp.arg[8] & 0x7FFE0000) >> 17; npar_info->max_linkspeed_reg_offset = temp; } + if (npar_info->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) + memcpy(ahw->extra_capability, &cmd.rsp.arg[16], + sizeof(ahw->extra_capability)); out: qlcnic_free_mbx_args(&cmd); @@ -2121,26 +2238,28 @@ out: int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, struct qlcnic_pci_info *pci_info) { + struct qlcnic_hardware_context *ahw = adapter->ahw; + struct device *dev = &adapter->pdev->dev; + struct qlcnic_cmd_args cmd; int i, err = 0, j = 0; u32 temp; - struct qlcnic_cmd_args cmd; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); + if (err) + return err; + err = qlcnic_issue_cmd(adapter, &cmd); - adapter->ahw->act_pci_func = 0; + ahw->act_pci_func = 0; if (err == QLCNIC_RCODE_SUCCESS) { - pci_info->func_count = cmd.rsp.arg[1] & 0xFF; - dev_info(&adapter->pdev->dev, - "%s: total functions = %d\n", - __func__, pci_info->func_count); + ahw->max_pci_func = cmd.rsp.arg[1] & 0xFF; for (i = 2, j = 0; j < QLCNIC_MAX_PCI_FUNC; j++, pci_info++) { pci_info->id = cmd.rsp.arg[i] & 0xFFFF; pci_info->active = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; i++; pci_info->type = cmd.rsp.arg[i] & 0xFFFF; if (pci_info->type == QLCNIC_TYPE_NIC) - adapter->ahw->act_pci_func++; + ahw->act_pci_func++; temp = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; pci_info->default_port = temp; i++; @@ -2152,18 +2271,21 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, i++; memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2); i = i + 3; - - dev_info(&adapter->pdev->dev, "%s:\n" - "\tid = %d active = %d type = %d\n" - "\tport = %d min bw = %d max bw = %d\n" - "\tmac_addr = %pM\n", __func__, - pci_info->id, pci_info->active, pci_info->type, - pci_info->default_port, pci_info->tx_min_bw, - pci_info->tx_max_bw, pci_info->mac); + if (ahw->op_mode == QLCNIC_MGMT_FUNC) + dev_info(dev, "id = %d active = %d type = %d\n" + "\tport = %d min bw = %d max bw = %d\n" + "\tmac_addr = %pM\n", pci_info->id, + pci_info->active, pci_info->type, + pci_info->default_port, + pci_info->tx_min_bw, + pci_info->tx_max_bw, pci_info->mac); } + if (ahw->op_mode == QLCNIC_MGMT_FUNC) + dev_info(dev, "Max vNIC functions = %d, active vNIC functions = %d\n", + ahw->max_pci_func, ahw->act_pci_func); + } else { - dev_err(&adapter->pdev->dev, "Failed to get PCI Info%d\n", - err); + dev_err(dev, "Failed to get PCI Info, error = %d\n", err); err = -EIO; } @@ -2180,7 +2302,10 @@ int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type) struct qlcnic_cmd_args cmd; max_ints = adapter->ahw->num_msix - 1; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT); + if (err) + return err; + cmd.req.arg[1] = max_ints; if (qlcnic_sriov_vf_check(adapter)) @@ -2808,7 +2933,11 @@ int qlcnic_83xx_test_link(struct qlcnic_adapter *adapter) dev_info(&adapter->pdev->dev, "link state down\n"); return config; } - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_STATUS); + + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_STATUS); + if (err) + return err; + err = qlcnic_issue_cmd(adapter, &cmd); if (err) { dev_info(&adapter->pdev->dev, @@ -3034,7 +3163,9 @@ void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data) struct net_device *netdev = adapter->netdev; int ret = 0; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS); + ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS); + if (ret) + return; /* Get Tx stats */ cmd.req.arg[1] = BIT_1 | (adapter->tx_ring->ctx_id << 16); cmd.rsp.num = QLC_83XX_TX_STAT_REGS; @@ -3113,8 +3244,10 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) u8 val; int ret, max_sds_rings = adapter->max_sds_rings; - if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) - return -EIO; + if (qlcnic_get_diag_lock(adapter)) { + netdev_info(netdev, "Device in diagnostics mode\n"); + return -EBUSY; + } ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST, max_sds_rings); @@ -3122,7 +3255,9 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) goto fail_diag_irq; ahw->diag_cnt = 0; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST); + ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST); + if (ret) + goto fail_diag_irq; if (adapter->flags & QLCNIC_MSIX_ENABLED) intrpt_id = ahw->intr_tbl[0].id; @@ -3156,7 +3291,7 @@ done: fail_diag_irq: adapter->max_sds_rings = max_sds_rings; - clear_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_release_diag_lock(adapter); return ret; } @@ -3260,3 +3395,54 @@ int qlcnic_83xx_flash_test(struct qlcnic_adapter *adapter) } return 0; } + +int qlcnic_83xx_shutdown(struct pci_dev *pdev) +{ + struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev = adapter->netdev; + int retval; + + netif_device_detach(netdev); + qlcnic_cancel_idc_work(adapter); + + if (netif_running(netdev)) + qlcnic_down(adapter, netdev); + + qlcnic_83xx_disable_mbx_intr(adapter); + cancel_delayed_work_sync(&adapter->idc_aen_work); + + retval = pci_save_state(pdev); + if (retval) + return retval; + + return 0; +} + +int qlcnic_83xx_resume(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + struct qlc_83xx_idc *idc = &ahw->idc; + int err = 0; + + err = qlcnic_83xx_idc_init(adapter); + if (err) + return err; + + if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) { + if (ahw->op_mode == QLCNIC_MGMT_FUNC) { + qlcnic_83xx_set_vnic_opmode(adapter); + } else { + err = qlcnic_83xx_check_vnic_state(adapter); + if (err) + return err; + } + } + + err = qlcnic_83xx_idc_reattach_driver(adapter); + if (err) + return err; + + qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state, + idc->delay); + return err; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index f5db67fc9f5..2548d1403d7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h @@ -36,7 +36,8 @@ #define QLC_83XX_MAX_DRV_LOCK_RECOVERY_ATTEMPT 3 #define QLC_83XX_DRV_LOCK_RECOVERY_DELAY 200 #define QLC_83XX_DRV_LOCK_RECOVERY_STATUS_MASK 0x3 - +#define QLC_83XX_LB_WAIT_COUNT 250 +#define QLC_83XX_LB_MSLEEP_COUNT 20 #define QLC_83XX_NO_NIC_RESOURCE 0x5 #define QLC_83XX_MAC_PRESENT 0xC #define QLC_83XX_MAC_ABSENT 0xD @@ -314,6 +315,7 @@ struct qlc_83xx_idc { u8 vnic_state; u8 vnic_wait_limit; u8 quiesce_req; + u8 delay_reset; char **name; }; @@ -392,6 +394,8 @@ enum qlcnic_83xx_states { #define QLC_83XX_LB_MAX_FILTERS 2048 #define QLC_83XX_LB_BUCKET_SIZE 256 #define QLC_83XX_MINIMUM_VECTOR 3 +#define QLC_83XX_MAX_MC_COUNT 38 +#define QLC_83XX_MAX_UC_COUNT 4096 #define QLC_83XX_GET_FUNC_MODE_FROM_NPAR_INFO(val) (val & 0x80000000) #define QLC_83XX_GET_LRO_CAPABILITY(val) (val & 0x20) @@ -623,4 +627,11 @@ u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *); u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *, u32 *); void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *); void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *); +void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *); +int qlcnic_83xx_shutdown(struct pci_dev *); +int qlcnic_83xx_resume(struct qlcnic_adapter *); +int qlcnic_83xx_idc_init(struct qlcnic_adapter *); +int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *); +int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *); +int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *); #endif diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index 5e7fb1dfb97..f41dfab1e9a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -606,7 +606,7 @@ static int qlcnic_83xx_idc_check_fan_failure(struct qlcnic_adapter *adapter) return 0; } -static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) +int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) { int err; @@ -629,6 +629,7 @@ static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) return -EIO; } + qlcnic_set_drv_version(adapter); qlcnic_83xx_idc_attach_driver(adapter); return 0; @@ -649,6 +650,7 @@ static void qlcnic_83xx_idc_update_idc_params(struct qlcnic_adapter *adapter) ahw->idc.collect_dump = 0; ahw->reset_context = 0; adapter->tx_timeo_cnt = 0; + ahw->idc.delay_reset = 0; clear_bit(__QLCNIC_RESETTING, &adapter->state); } @@ -883,21 +885,41 @@ static int qlcnic_83xx_idc_need_reset_state(struct qlcnic_adapter *adapter) int ret = 0; if (adapter->ahw->idc.prev_state != QLC_83XX_IDC_DEV_NEED_RESET) { - qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); set_bit(__QLCNIC_RESETTING, &adapter->state); clear_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); if (adapter->ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) qlcnic_83xx_disable_vnic_mode(adapter, 1); - qlcnic_83xx_idc_detach_driver(adapter); + + if (qlcnic_check_diag_status(adapter)) { + dev_info(&adapter->pdev->dev, + "%s: Wait for diag completion\n", __func__); + adapter->ahw->idc.delay_reset = 1; + return 0; + } else { + qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); + qlcnic_83xx_idc_detach_driver(adapter); + } } - /* Check ACK from other functions */ - ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter); - if (ret) { + if (qlcnic_check_diag_status(adapter)) { dev_info(&adapter->pdev->dev, - "%s: Waiting for reset ACK\n", __func__); - return 0; + "%s: Wait for diag completion\n", __func__); + return -1; + } else { + if (adapter->ahw->idc.delay_reset) { + qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); + qlcnic_83xx_idc_detach_driver(adapter); + adapter->ahw->idc.delay_reset = 0; + } + + /* Check for ACK from other functions */ + ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter); + if (ret) { + dev_info(&adapter->pdev->dev, + "%s: Waiting for reset ACK\n", __func__); + return -1; + } } /* Transit to INIT state and restart the HW */ @@ -1113,7 +1135,7 @@ qlcnic_83xx_idc_first_to_load_function_handler(struct qlcnic_adapter *adapter) return 0; } -static int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter) +int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter) { int ret = -EIO; @@ -1532,9 +1554,18 @@ static int qlcnic_83xx_reset_template_checksum(struct qlcnic_adapter *p_dev) int qlcnic_83xx_get_reset_instruction_template(struct qlcnic_adapter *p_dev) { - u8 *p_buff; - u32 addr, count; struct qlcnic_hardware_context *ahw = p_dev->ahw; + u32 addr, count, prev_ver, curr_ver; + u8 *p_buff; + + if (ahw->reset.buff != NULL) { + prev_ver = p_dev->fw_version; + curr_ver = qlcnic_83xx_get_fw_version(p_dev); + if (curr_ver > prev_ver) + kfree(ahw->reset.buff); + else + return 0; + } ahw->reset.seq_error = 0; ahw->reset.buff = kzalloc(QLC_83XX_RESTART_TEMPLATE_SIZE, GFP_KERNEL); @@ -2062,7 +2093,11 @@ static void qlcnic_83xx_clear_function_resources(struct qlcnic_adapter *adapter) audit_mask = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT); if (IS_QLC_83XX_USED(adapter, presence_mask, audit_mask)) { - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC); + status = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_STOP_NIC_FUNC); + if (status) + return; + cmd.req.arg[1] = BIT_31; status = qlcnic_issue_cmd(adapter, &cmd); if (status) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c index b0c3de9ede0..599d1fda52f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c @@ -39,30 +39,21 @@ int qlcnic_83xx_disable_vnic_mode(struct qlcnic_adapter *adapter, int lock) return 0; } -static int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter) +int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter) { u8 id; - int i, ret = -EBUSY; + int ret = -EBUSY; u32 data = QLCNIC_MGMT_FUNC; struct qlcnic_hardware_context *ahw = adapter->ahw; if (qlcnic_83xx_lock_driver(adapter)) return ret; - if (qlcnic_config_npars) { - for (i = 0; i < ahw->act_pci_func; i++) { - id = adapter->npars[i].pci_func; - if (id == ahw->pci_func) - continue; - data |= qlcnic_config_npars & - QLC_83XX_SET_FUNC_OPMODE(0x3, id); - } - } else { - data = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE); - data = (data & ~QLC_83XX_SET_FUNC_OPMODE(0x3, ahw->pci_func)) | - QLC_83XX_SET_FUNC_OPMODE(QLCNIC_MGMT_FUNC, - ahw->pci_func); - } + id = ahw->pci_func; + data = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE); + data = (data & ~QLC_83XX_SET_FUNC_OPMODE(0x3, id)) | + QLC_83XX_SET_FUNC_OPMODE(QLCNIC_MGMT_FUNC, id); + QLCWRX(adapter->ahw, QLC_83XX_DRV_OP_MODE, data); qlcnic_83xx_unlock_driver(adapter); @@ -196,20 +187,24 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter) else priv_level = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode, ahw->pci_func); - - if (priv_level == QLCNIC_NON_PRIV_FUNC) { + switch (priv_level) { + case QLCNIC_NON_PRIV_FUNC: ahw->op_mode = QLCNIC_NON_PRIV_FUNC; ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; nic_ops->init_driver = qlcnic_83xx_init_non_privileged_vnic; - } else if (priv_level == QLCNIC_PRIV_FUNC) { + break; + case QLCNIC_PRIV_FUNC: ahw->op_mode = QLCNIC_PRIV_FUNC; ahw->idc.state_entry = qlcnic_83xx_idc_vnic_pf_entry; nic_ops->init_driver = qlcnic_83xx_init_privileged_vnic; - } else if (priv_level == QLCNIC_MGMT_FUNC) { + break; + case QLCNIC_MGMT_FUNC: ahw->op_mode = QLCNIC_MGMT_FUNC; ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; nic_ops->init_driver = qlcnic_83xx_init_mgmt_vnic; - } else { + break; + default: + dev_err(&adapter->pdev->dev, "Invalid Virtual NIC opmode\n"); return -EIO; } @@ -218,8 +213,29 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter) else adapter->flags &= ~QLCNIC_ESWITCH_ENABLED; - adapter->ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER; - adapter->ahw->idc.vnic_wait_limit = QLCNIC_DEV_NPAR_OPER_TIMEO; + ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER; + ahw->idc.vnic_wait_limit = QLCNIC_DEV_NPAR_OPER_TIMEO; + + return 0; +} + +int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + struct qlc_83xx_idc *idc = &ahw->idc; + u32 state; + + state = QLCRDX(ahw, QLC_83XX_VNIC_STATE); + while (state != QLCNIC_DEV_NPAR_OPER && idc->vnic_wait_limit--) { + msleep(1000); + state = QLCRDX(ahw, QLC_83XX_VNIC_STATE); + } + + if (!idc->vnic_wait_limit) { + dev_err(&adapter->pdev->dev, + "vNIC mode not operational, state check timed out.\n"); + return -EIO; + } return 0; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 6acf82b9f01..0581a484ceb 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -36,7 +36,8 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = { {QLCNIC_CMD_CONFIG_PORT, 4, 1}, {QLCNIC_CMD_TEMP_SIZE, 4, 4}, {QLCNIC_CMD_GET_TEMP_HDR, 4, 1}, - {QLCNIC_CMD_SET_DRV_VER, 4, 1}, + {QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1}, + {QLCNIC_CMD_GET_LED_STATUS, 4, 2}, }; static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw) @@ -181,7 +182,7 @@ int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter, return cmd->rsp.arg[0]; } -int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter) +int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter, u32 fw_cmd) { struct qlcnic_cmd_args cmd; u32 arg1, arg2, arg3; @@ -193,7 +194,10 @@ int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter) _QLCNIC_LINUX_MAJOR, _QLCNIC_LINUX_MINOR, _QLCNIC_LINUX_SUBVERSION); - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_DRV_VER); + err = qlcnic_alloc_mbx_args(&cmd, adapter, fw_cmd); + if (err) + return err; + memcpy(&arg1, drv_string, sizeof(u32)); memcpy(&arg2, drv_string + 4, sizeof(u32)); memcpy(&arg3, drv_string + 8, sizeof(u32)); @@ -221,7 +225,10 @@ qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu) if (recv_ctx->state != QLCNIC_HOST_CTX_STATE_ACTIVE) return err; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU); + if (err) + return err; + cmd.req.arg[1] = recv_ctx->context_id; cmd.req.arg[2] = mtu; @@ -335,7 +342,10 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) } phys_addr = hostrq_phys_addr; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX); + if (err) + goto out_free_rsp; + cmd.req.arg[1] = MSD(phys_addr); cmd.req.arg[2] = LSD(phys_addr); cmd.req.arg[3] = rq_size; @@ -373,10 +383,10 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) recv_ctx->context_id = le16_to_cpu(prsp->context_id); recv_ctx->virt_port = prsp->virt_port; + qlcnic_free_mbx_args(&cmd); out_free_rsp: dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp, - cardrsp_phys_addr); - qlcnic_free_mbx_args(&cmd); + cardrsp_phys_addr); out_free_rq: dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr); return err; @@ -388,7 +398,10 @@ void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter) struct qlcnic_cmd_args cmd; struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX); + if (err) + return; + cmd.req.arg[1] = recv_ctx->context_id; err = qlcnic_issue_cmd(adapter, &cmd); if (err) @@ -457,7 +470,10 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter, phys_addr = rq_phys_addr; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX); + if (err) + goto out_free_rsp; + cmd.req.arg[1] = MSD(phys_addr); cmd.req.arg[2] = LSD(phys_addr); cmd.req.arg[3] = rq_size; @@ -473,12 +489,13 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter, err = -EIO; } + qlcnic_free_mbx_args(&cmd); + +out_free_rsp: dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr, rsp_phys_addr); - out_free_rq: dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr); - qlcnic_free_mbx_args(&cmd); return err; } @@ -487,8 +504,11 @@ void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter, struct qlcnic_host_tx_ring *tx_ring) { struct qlcnic_cmd_args cmd; + int ret; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX); + ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX); + if (ret) + return; cmd.req.arg[1] = tx_ring->ctx_id; if (qlcnic_issue_cmd(adapter, &cmd)) @@ -503,7 +523,10 @@ qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config) int err; struct qlcnic_cmd_args cmd; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT); + if (err) + return err; + cmd.req.arg[1] = config; err = qlcnic_issue_cmd(adapter, &cmd); qlcnic_free_mbx_args(&cmd); @@ -707,7 +730,10 @@ int qlcnic_82xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac) struct qlcnic_cmd_args cmd; u32 mac_low, mac_high; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS); + if (err) + return err; + cmd.req.arg[1] = adapter->ahw->pci_func | BIT_8; err = qlcnic_issue_cmd(adapter, &cmd); @@ -746,7 +772,10 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter, nic_info = nic_info_addr; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); + if (err) + goto out_free_dma; + cmd.req.arg[1] = MSD(nic_dma_t); cmd.req.arg[2] = LSD(nic_dma_t); cmd.req.arg[3] = (func_id << 16 | nic_size); @@ -768,9 +797,10 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter, npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu); } + qlcnic_free_mbx_args(&cmd); +out_free_dma: dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr, nic_dma_t); - qlcnic_free_mbx_args(&cmd); return err; } @@ -807,7 +837,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter, nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw); nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw); - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); + if (err) + goto out_free_dma; + cmd.req.arg[1] = MSD(nic_dma_t); cmd.req.arg[2] = LSD(nic_dma_t); cmd.req.arg[3] = ((nic->pci_func << 16) | nic_size); @@ -819,9 +852,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter, err = -EIO; } - dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr, - nic_dma_t); qlcnic_free_mbx_args(&cmd); +out_free_dma: + dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr, + nic_dma_t); return err; } @@ -845,7 +879,10 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter, return -ENOMEM; npar = pci_info_addr; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); + if (err) + goto out_free_dma; + cmd.req.arg[1] = MSD(pci_info_dma_t); cmd.req.arg[2] = LSD(pci_info_dma_t); cmd.req.arg[3] = pci_size; @@ -873,20 +910,22 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter, err = -EIO; } + qlcnic_free_mbx_args(&cmd); +out_free_dma: dma_free_coherent(&adapter->pdev->dev, pci_size, pci_info_addr, pci_info_dma_t); - qlcnic_free_mbx_args(&cmd); return err; } /* Configure eSwitch for port mirroring */ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id, - u8 enable_mirroring, u8 pci_func) + u8 enable_mirroring, u8 pci_func) { + struct device *dev = &adapter->pdev->dev; + struct qlcnic_cmd_args cmd; int err = -EIO; u32 arg1; - struct qlcnic_cmd_args cmd; if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC || !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) @@ -895,18 +934,20 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id, arg1 = id | (enable_mirroring ? BIT_4 : 0); arg1 |= pci_func << 8; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORTMIRRORING); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_SET_PORTMIRRORING); + if (err) + return err; + cmd.req.arg[1] = arg1; err = qlcnic_issue_cmd(adapter, &cmd); if (err != QLCNIC_RCODE_SUCCESS) - dev_err(&adapter->pdev->dev, - "Failed to configure port mirroring%d on eswitch:%d\n", + dev_err(dev, "Failed to configure port mirroring for vNIC function %d on eSwitch %d\n", pci_func, id); else - dev_info(&adapter->pdev->dev, - "Configured eSwitch %d for port mirroring:%d\n", - id, pci_func); + dev_info(dev, "Configured port mirroring for vNIC function %d on eSwitch %d\n", + pci_func, id); qlcnic_free_mbx_args(&cmd); return err; @@ -941,7 +982,11 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func, arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12; arg1 |= rx_tx << 15 | stats_size << 16; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_STATS); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_GET_ESWITCH_STATS); + if (err) + goto out_free_dma; + cmd.req.arg[1] = arg1; cmd.req.arg[2] = MSD(stats_dma_t); cmd.req.arg[3] = LSD(stats_dma_t); @@ -963,9 +1008,10 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func, esw_stats->numbytes = le64_to_cpu(stats->numbytes); } - dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr, - stats_dma_t); qlcnic_free_mbx_args(&cmd); +out_free_dma: + dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr, + stats_dma_t); return err; } @@ -989,7 +1035,10 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter, if (!stats_addr) return -ENOMEM; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS); + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS); + if (err) + goto out_free_dma; + cmd.req.arg[1] = stats_size << 16; cmd.req.arg[2] = MSD(stats_dma_t); cmd.req.arg[3] = LSD(stats_dma_t); @@ -1020,11 +1069,12 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter, "%s: Get mac stats failed, err=%d.\n", __func__, err); } - dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr, - stats_dma_t); - qlcnic_free_mbx_args(&cmd); +out_free_dma: + dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr, + stats_dma_t); + return err; } @@ -1108,7 +1158,11 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw, arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12; arg1 |= BIT_14 | rx_tx << 15; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_STATS); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_GET_ESWITCH_STATS); + if (err) + return err; + cmd.req.arg[1] = arg1; err = qlcnic_issue_cmd(adapter, &cmd); qlcnic_free_mbx_args(&cmd); @@ -1121,17 +1175,19 @@ err_ret: return -EIO; } -static int -__qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, - u32 *arg1, u32 *arg2) +static int __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, + u32 *arg1, u32 *arg2) { - int err = -EIO; + struct device *dev = &adapter->pdev->dev; struct qlcnic_cmd_args cmd; - u8 pci_func; - pci_func = (*arg1 >> 8); + u8 pci_func = *arg1 >> 8; + int err; + + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG); + if (err) + return err; - qlcnic_alloc_mbx_args(&cmd, adapter, - QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG); cmd.req.arg[1] = *arg1; err = qlcnic_issue_cmd(adapter, &cmd); *arg1 = cmd.rsp.arg[1]; @@ -1139,12 +1195,11 @@ __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, qlcnic_free_mbx_args(&cmd); if (err == QLCNIC_RCODE_SUCCESS) - dev_info(&adapter->pdev->dev, - "eSwitch port config for pci func %d\n", pci_func); + dev_info(dev, "Get eSwitch port config for vNIC function %d\n", + pci_func); else - dev_err(&adapter->pdev->dev, - "Failed to get eswitch port config for pci func %d\n", - pci_func); + dev_err(dev, "Failed to get eswitch port config for vNIC function %d\n", + pci_func); return err; } /* Configure eSwitch port @@ -1157,9 +1212,10 @@ op_type = 1 for port vlan_id int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, struct qlcnic_esw_func_cfg *esw_cfg) { + struct device *dev = &adapter->pdev->dev; + struct qlcnic_cmd_args cmd; int err = -EIO, index; u32 arg1, arg2 = 0; - struct qlcnic_cmd_args cmd; u8 pci_func; if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) @@ -1209,18 +1265,22 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, return err; } - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_ESWITCH); + err = qlcnic_alloc_mbx_args(&cmd, adapter, + QLCNIC_CMD_CONFIGURE_ESWITCH); + if (err) + return err; + cmd.req.arg[1] = arg1; cmd.req.arg[2] = arg2; err = qlcnic_issue_cmd(adapter, &cmd); qlcnic_free_mbx_args(&cmd); if (err != QLCNIC_RCODE_SUCCESS) - dev_err(&adapter->pdev->dev, - "Failed to configure eswitch pci func %d\n", pci_func); + dev_err(dev, "Failed to configure eswitch for vNIC function %d\n", + pci_func); else - dev_info(&adapter->pdev->dev, - "Configured eSwitch for pci func %d\n", pci_func); + dev_info(dev, "Configured eSwitch for vNIC function %d\n", + pci_func); return err; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index f67652de5a6..700a46324d0 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -846,7 +846,9 @@ static int qlcnic_irq_test(struct net_device *netdev) goto clear_diag_irq; ahw->diag_cnt = 0; - qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST); + ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST); + if (ret) + goto free_diag_res; cmd.req.arg[1] = ahw->pci_func; ret = qlcnic_issue_cmd(adapter, &cmd); @@ -858,6 +860,8 @@ static int qlcnic_irq_test(struct net_device *netdev) done: qlcnic_free_mbx_args(&cmd); + +free_diag_res: qlcnic_diag_free_res(netdev, max_sds_rings); clear_diag_irq: diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h index c0f0c0d0a79..d262211b03b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h @@ -672,6 +672,7 @@ enum { #define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT 10 #define QLCNIC_MAX_MC_COUNT 38 +#define QLCNIC_MAX_UC_COUNT 512 #define QLCNIC_WATCHDOG_TIMEOUTVALUE 5 #define ISR_MSI_INT_TRIGGER(FUNC) (QLCNIC_PCIX_PS_REG(PCIX_MSI_F(FUNC))) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 106a12f2a02..5b5d2edf125 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -499,6 +499,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan) void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) { struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_hardware_context *ahw = adapter->ahw; struct netdev_hw_addr *ha; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff @@ -515,25 +516,30 @@ void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) if (netdev->flags & IFF_PROMISC) { if (!(adapter->flags & QLCNIC_PROMISC_DISABLED)) mode = VPORT_MISS_MODE_ACCEPT_ALL; - goto send_fw_cmd; - } - - if ((netdev->flags & IFF_ALLMULTI) || - (netdev_mc_count(netdev) > adapter->ahw->max_mc_count)) { - mode = VPORT_MISS_MODE_ACCEPT_MULTI; - goto send_fw_cmd; + } else if (netdev->flags & IFF_ALLMULTI) { + if (netdev_mc_count(netdev) > ahw->max_mc_count) { + mode = VPORT_MISS_MODE_ACCEPT_MULTI; + } else if (!netdev_mc_empty(netdev) && + !qlcnic_sriov_vf_check(adapter)) { + netdev_for_each_mc_addr(ha, netdev) + qlcnic_nic_add_mac(adapter, ha->addr, + vlan); + } + if (mode != VPORT_MISS_MODE_ACCEPT_MULTI && + qlcnic_sriov_vf_check(adapter)) + qlcnic_vf_add_mc_list(netdev, vlan); } - if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) { - netdev_for_each_mc_addr(ha, netdev) { + /* configure unicast MAC address, if there is not sufficient space + * to store all the unicast addresses then enable promiscuous mode + */ + if (netdev_uc_count(netdev) > ahw->max_uc_count) { + mode = VPORT_MISS_MODE_ACCEPT_ALL; + } else if (!netdev_uc_empty(netdev)) { + netdev_for_each_uc_addr(ha, netdev) qlcnic_nic_add_mac(adapter, ha->addr, vlan); - } } - if (qlcnic_sriov_vf_check(adapter)) - qlcnic_vf_add_mc_list(netdev, vlan); - -send_fw_cmd: if (!qlcnic_sriov_vf_check(adapter)) { if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) { @@ -780,7 +786,8 @@ int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable) word = 0; if (enable) { word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK; - if (adapter->ahw->capabilities2 & QLCNIC_FW_CAP2_HW_LRO_IPV6) + if (adapter->ahw->extra_capability[0] & + QLCNIC_FW_CAP2_HW_LRO_IPV6) word |= QLCNIC_ENABLE_IPV6_LRO | QLCNIC_NO_DEST_IPV6_CHECK; } @@ -1503,6 +1510,21 @@ int qlcnic_82xx_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) return rv; } +int qlcnic_get_beacon_state(struct qlcnic_adapter *adapter, u8 *h_state) +{ + struct qlcnic_cmd_args cmd; + int err; + + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LED_STATUS); + if (!err) { + err = qlcnic_issue_cmd(adapter, &cmd); + if (!err) + *h_state = cmd.rsp.arg[1]; + } + qlcnic_free_mbx_args(&cmd); + return err; +} + void qlcnic_82xx_get_func_no(struct qlcnic_adapter *adapter) { void __iomem *msix_base_addr; @@ -1555,3 +1577,54 @@ void qlcnic_82xx_api_unlock(struct qlcnic_adapter *adapter) { qlcnic_pcie_sem_unlock(adapter, 5); } + +int qlcnic_82xx_shutdown(struct pci_dev *pdev) +{ + struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev = adapter->netdev; + int retval; + + netif_device_detach(netdev); + + qlcnic_cancel_idc_work(adapter); + + if (netif_running(netdev)) + qlcnic_down(adapter, netdev); + + qlcnic_clr_all_drv_state(adapter, 0); + + clear_bit(__QLCNIC_RESETTING, &adapter->state); + + retval = pci_save_state(pdev); + if (retval) + return retval; + + if (qlcnic_wol_supported(adapter)) { + pci_enable_wake(pdev, PCI_D3cold, 1); + pci_enable_wake(pdev, PCI_D3hot, 1); + } + + return 0; +} + +int qlcnic_82xx_resume(struct qlcnic_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int err; + + err = qlcnic_start_firmware(adapter); + if (err) { + dev_err(&adapter->pdev->dev, "failed to start firmware\n"); + return err; + } + + if (netif_running(netdev)) { + err = qlcnic_up(adapter, netdev); + if (!err) + qlcnic_restore_indev_addr(netdev, NETDEV_UP); + } + + netif_device_attach(netdev); + qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY); + return err; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index b6818f4356b..2c22504f57a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h @@ -86,7 +86,8 @@ enum qlcnic_regs { #define QLCNIC_CMD_BC_EVENT_SETUP 0x31 #define QLCNIC_CMD_CONFIG_VPORT 0x32 #define QLCNIC_CMD_GET_MAC_STATS 0x37 -#define QLCNIC_CMD_SET_DRV_VER 0x38 +#define QLCNIC_CMD_82XX_SET_DRV_VER 0x38 +#define QLCNIC_CMD_GET_LED_STATUS 0x3C #define QLCNIC_CMD_CONFIGURE_RSS 0x41 #define QLCNIC_CMD_CONFIG_INTR_COAL 0x43 #define QLCNIC_CMD_CONFIGURE_LED 0x44 @@ -102,6 +103,7 @@ enum qlcnic_regs { #define QLCNIC_CMD_GET_LINK_STATUS 0x68 #define QLCNIC_CMD_SET_LED_CONFIG 0x69 #define QLCNIC_CMD_GET_LED_CONFIG 0x6A +#define QLCNIC_CMD_83XX_SET_DRV_VER 0x6F #define QLCNIC_CMD_ADD_RCV_RINGS 0x0B #define QLCNIC_INTRPT_INTX 1 @@ -197,4 +199,8 @@ void qlcnic_82xx_api_unlock(struct qlcnic_adapter *); void qlcnic_82xx_napi_enable(struct qlcnic_adapter *); void qlcnic_82xx_napi_disable(struct qlcnic_adapter *); void qlcnic_82xx_napi_del(struct qlcnic_adapter *); +int qlcnic_82xx_shutdown(struct pci_dev *); +int qlcnic_82xx_resume(struct qlcnic_adapter *); +void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed); +void qlcnic_fw_poll_work(struct work_struct *work); #endif /* __QLCNIC_HW_H_ */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index aeb26a85067..4528f8ec333 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -52,10 +52,6 @@ int qlcnic_load_fw_file; MODULE_PARM_DESC(load_fw_file, "Load firmware from (0=flash, 1=file)"); module_param_named(load_fw_file, qlcnic_load_fw_file, int, 0444); -int qlcnic_config_npars; -module_param(qlcnic_config_npars, int, 0444); -MODULE_PARM_DESC(qlcnic_config_npars, "Configure NPARs (0=disabled, 1=enabled)"); - static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void qlcnic_remove(struct pci_dev *pdev); static int qlcnic_open(struct net_device *netdev); @@ -63,13 +59,11 @@ static int qlcnic_close(struct net_device *netdev); static void qlcnic_tx_timeout(struct net_device *netdev); static void qlcnic_attach_work(struct work_struct *work); static void qlcnic_fwinit_work(struct work_struct *work); -static void qlcnic_fw_poll_work(struct work_struct *work); #ifdef CONFIG_NET_POLL_CONTROLLER static void qlcnic_poll_controller(struct net_device *netdev); #endif static void qlcnic_idc_debug_info(struct qlcnic_adapter *adapter, u8 encoding); -static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8); static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter); static irqreturn_t qlcnic_tmp_intr(int irq, void *data); @@ -364,12 +358,15 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], return ndo_dflt_fdb_del(ndm, tb, netdev, addr); if (adapter->flags & QLCNIC_ESWITCH_ENABLED) { - if (is_unicast_ether_addr(addr)) - err = qlcnic_nic_del_mac(adapter, addr); - else if (is_multicast_ether_addr(addr)) + if (is_unicast_ether_addr(addr)) { + err = dev_uc_del(netdev, addr); + if (!err) + err = qlcnic_nic_del_mac(adapter, addr); + } else if (is_multicast_ether_addr(addr)) { err = dev_mc_del(netdev, addr); - else + } else { err = -EINVAL; + } } return err; } @@ -392,12 +389,16 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], if (ether_addr_equal(addr, adapter->mac_addr)) return err; - if (is_unicast_ether_addr(addr)) - err = qlcnic_nic_add_mac(adapter, addr, 0); - else if (is_multicast_ether_addr(addr)) + if (is_unicast_ether_addr(addr)) { + if (netdev_uc_count(netdev) < adapter->ahw->max_uc_count) + err = dev_uc_add_excl(netdev, addr); + else + err = -ENOMEM; + } else if (is_multicast_ether_addr(addr)) { err = dev_mc_add_excl(netdev, addr); - else + } else { err = -EINVAL; + } return err; } @@ -449,6 +450,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { .ndo_set_vf_tx_rate = qlcnic_sriov_set_vf_tx_rate, .ndo_get_vf_config = qlcnic_sriov_get_vf_config, .ndo_set_vf_vlan = qlcnic_sriov_set_vf_vlan, + .ndo_set_vf_spoofchk = qlcnic_sriov_set_vf_spoofchk, #endif }; @@ -465,6 +467,8 @@ static struct qlcnic_nic_template qlcnic_ops = { .napi_add = qlcnic_82xx_napi_add, .napi_del = qlcnic_82xx_napi_del, .config_ipaddr = qlcnic_82xx_config_ipaddr, + .shutdown = qlcnic_82xx_shutdown, + .resume = qlcnic_82xx_resume, .clear_legacy_intr = qlcnic_82xx_clear_legacy_intr, }; @@ -508,6 +512,7 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = { .config_promisc_mode = qlcnic_82xx_nic_set_promisc, .change_l2_filter = qlcnic_82xx_change_filter, .get_board_info = qlcnic_82xx_get_board_info, + .set_mac_filter_count = qlcnic_82xx_set_mac_filter_count, .free_mac_list = qlcnic_82xx_free_mac_list, }; @@ -768,7 +773,7 @@ static int qlcnic_set_function_modes(struct qlcnic_adapter *adapter) { u8 id; - int i, ret = 1; + int ret; u32 data = QLCNIC_MGMT_FUNC; struct qlcnic_hardware_context *ahw = adapter->ahw; @@ -776,20 +781,10 @@ qlcnic_set_function_modes(struct qlcnic_adapter *adapter) if (ret) goto err_lock; - if (qlcnic_config_npars) { - for (i = 0; i < ahw->act_pci_func; i++) { - id = adapter->npars[i].pci_func; - if (id == ahw->pci_func) - continue; - data |= (qlcnic_config_npars & - QLC_DEV_SET_DRV(0xf, id)); - } - } else { - data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE); - data = (data & ~QLC_DEV_SET_DRV(0xf, ahw->pci_func)) | - (QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, - ahw->pci_func)); - } + id = ahw->pci_func; + data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE); + data = (data & ~QLC_DEV_SET_DRV(0xf, id)) | + QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, id); QLC_SHARED_REG_WR32(adapter, QLCNIC_DRV_OP_MODE, data); qlcnic_api_unlock(adapter); err_lock: @@ -875,6 +870,27 @@ static int qlcnic_setup_pci_map(struct pci_dev *pdev, return 0; } +static inline bool qlcnic_validate_subsystem_id(struct qlcnic_adapter *adapter, + int index) +{ + struct pci_dev *pdev = adapter->pdev; + unsigned short subsystem_vendor; + bool ret = true; + + subsystem_vendor = pdev->subsystem_vendor; + + if (pdev->device == PCI_DEVICE_ID_QLOGIC_QLE824X || + pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X) { + if (qlcnic_boards[index].sub_vendor == subsystem_vendor && + qlcnic_boards[index].sub_device == pdev->subsystem_device) + ret = true; + else + ret = false; + } + + return ret; +} + static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name) { struct pci_dev *pdev = adapter->pdev; @@ -882,20 +898,18 @@ static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name) for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) { if (qlcnic_boards[i].vendor == pdev->vendor && - qlcnic_boards[i].device == pdev->device && - qlcnic_boards[i].sub_vendor == pdev->subsystem_vendor && - qlcnic_boards[i].sub_device == pdev->subsystem_device) { - sprintf(name, "%pM: %s" , - adapter->mac_addr, - qlcnic_boards[i].short_name); - found = 1; - break; + qlcnic_boards[i].device == pdev->device && + qlcnic_validate_subsystem_id(adapter, i)) { + found = 1; + break; } - } if (!found) sprintf(name, "%pM Gigabit Ethernet", adapter->mac_addr); + else + sprintf(name, "%pM: %s" , adapter->mac_addr, + qlcnic_boards[i].short_name); } static void @@ -980,7 +994,7 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter) if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) { u32 temp; temp = QLCRD32(adapter, CRB_FW_CAPABILITIES_2); - adapter->ahw->capabilities2 = temp; + adapter->ahw->extra_capability[0] = temp; } adapter->ahw->max_mac_filters = nic_info.max_mac_filters; adapter->ahw->max_mtu = nic_info.max_mtu; @@ -1395,16 +1409,23 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) for (ring = 0; ring < num_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (qlcnic_82xx_check(adapter) && - (ring == (num_sds_rings - 1))) - snprintf(sds_ring->name, - sizeof(sds_ring->name), - "qlcnic-%s[Tx0+Rx%d]", - netdev->name, ring); - else + (ring == (num_sds_rings - 1))) { + if (!(adapter->flags & + QLCNIC_MSIX_ENABLED)) + snprintf(sds_ring->name, + sizeof(sds_ring->name), + "qlcnic"); + else + snprintf(sds_ring->name, + sizeof(sds_ring->name), + "%s-tx-0-rx-%d", + netdev->name, ring); + } else { snprintf(sds_ring->name, sizeof(sds_ring->name), - "qlcnic-%s[Rx%d]", + "%s-rx-%d", netdev->name, ring); + } err = request_irq(sds_ring->irq, handler, flags, sds_ring->name, sds_ring); if (err) @@ -1419,7 +1440,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) ring++) { tx_ring = &adapter->tx_ring[ring]; snprintf(tx_ring->name, sizeof(tx_ring->name), - "qlcnic-%s[Tx%d]", netdev->name, ring); + "%s-tx-%d", netdev->name, ring); err = request_irq(tx_ring->irq, handler, flags, tx_ring->name, tx_ring); if (err) @@ -1465,7 +1486,7 @@ static void qlcnic_get_lro_mss_capability(struct qlcnic_adapter *adapter) u32 capab = 0; if (qlcnic_82xx_check(adapter)) { - if (adapter->ahw->capabilities2 & + if (adapter->ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG) adapter->flags |= QLCNIC_FW_LRO_MSS_CAP; } else { @@ -1816,6 +1837,22 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter) return err; } +void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + u16 act_pci_fn = ahw->act_pci_func; + u16 count; + + ahw->max_mc_count = QLCNIC_MAX_MC_COUNT; + if (act_pci_fn <= 2) + count = (QLCNIC_MAX_UC_COUNT - QLCNIC_MAX_MC_COUNT) / + act_pci_fn; + else + count = (QLCNIC_LB_MAX_FILTERS - QLCNIC_MAX_MC_COUNT) / + act_pci_fn; + ahw->max_uc_count = count; +} + int qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, int pci_using_dac) @@ -1825,7 +1862,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, adapter->rx_csum = 1; adapter->ahw->mc_enabled = 0; - adapter->ahw->max_mc_count = QLCNIC_MAX_MC_COUNT; + qlcnic_set_mac_filter_count(adapter); netdev->netdev_ops = &qlcnic_netdev_ops; netdev->watchdog_timeo = QLCNIC_WATCHDOG_TIMEOUTVALUE * HZ; @@ -1863,6 +1900,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, netdev->features |= NETIF_F_LRO; netdev->hw_features = netdev->features; + netdev->priv_flags |= IFF_UNICAST_FLT; netdev->irq = adapter->msix_entries[0].vector; err = register_netdev(netdev); @@ -1947,6 +1985,21 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter, return 0; } +void qlcnic_set_drv_version(struct qlcnic_adapter *adapter) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + u32 fw_cmd = 0; + + if (qlcnic_82xx_check(adapter)) + fw_cmd = QLCNIC_CMD_82XX_SET_DRV_VER; + else if (qlcnic_83xx_check(adapter)) + fw_cmd = QLCNIC_CMD_83XX_SET_DRV_VER; + + if ((ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) && + (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER)) + qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd); +} + static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1954,7 +2007,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct qlcnic_adapter *adapter = NULL; struct qlcnic_hardware_context *ahw; int err, pci_using_dac = -1; - u32 capab2; char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */ if (pdev->is_virtfn) @@ -2109,13 +2161,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_out_disable_mbx_intr; - if (qlcnic_82xx_check(adapter)) { - if (ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) { - capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2); - if (capab2 & QLCNIC_FW_CAPABILITY_2_OCBB) - qlcnic_fw_cmd_set_drv_version(adapter); - } - } + qlcnic_set_drv_version(adapter); pci_set_drvdata(pdev, adapter); @@ -2231,37 +2277,6 @@ static void qlcnic_remove(struct pci_dev *pdev) kfree(ahw); free_netdev(netdev); } -static int __qlcnic_shutdown(struct pci_dev *pdev) -{ - struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; - int retval; - - netif_device_detach(netdev); - - qlcnic_cancel_idc_work(adapter); - - if (netif_running(netdev)) - qlcnic_down(adapter, netdev); - - qlcnic_sriov_cleanup(adapter); - if (qlcnic_82xx_check(adapter)) - qlcnic_clr_all_drv_state(adapter, 0); - - clear_bit(__QLCNIC_RESETTING, &adapter->state); - - retval = pci_save_state(pdev); - if (retval) - return retval; - if (qlcnic_82xx_check(adapter)) { - if (qlcnic_wol_supported(adapter)) { - pci_enable_wake(pdev, PCI_D3cold, 1); - pci_enable_wake(pdev, PCI_D3hot, 1); - } - } - - return 0; -} static void qlcnic_shutdown(struct pci_dev *pdev) { @@ -2272,8 +2287,7 @@ static void qlcnic_shutdown(struct pci_dev *pdev) } #ifdef CONFIG_PM -static int -qlcnic_suspend(struct pci_dev *pdev, pm_message_t state) +static int qlcnic_suspend(struct pci_dev *pdev, pm_message_t state) { int retval; @@ -2285,11 +2299,9 @@ qlcnic_suspend(struct pci_dev *pdev, pm_message_t state) return 0; } -static int -qlcnic_resume(struct pci_dev *pdev) +static int qlcnic_resume(struct pci_dev *pdev) { struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; int err; err = pci_enable_device(pdev); @@ -2300,23 +2312,7 @@ qlcnic_resume(struct pci_dev *pdev) pci_set_master(pdev); pci_restore_state(pdev); - err = qlcnic_start_firmware(adapter); - if (err) { - dev_err(&pdev->dev, "failed to start firmware\n"); - return err; - } - - if (netif_running(netdev)) { - err = qlcnic_up(adapter, netdev); - if (err) - goto done; - - qlcnic_restore_indev_addr(netdev, NETDEV_UP); - } -done: - netif_device_attach(netdev); - qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY); - return 0; + return __qlcnic_resume(adapter); } #endif @@ -2655,8 +2651,7 @@ qlcnic_clr_drv_state(struct qlcnic_adapter *adapter) return 0; } -static void -qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed) +void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed) { u32 val; @@ -3086,6 +3081,7 @@ done: adapter->fw_fail_cnt = 0; adapter->flags &= ~QLCNIC_FW_HANG; clear_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_set_drv_version(adapter); if (!qlcnic_clr_drv_state(adapter)) qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, @@ -3166,8 +3162,7 @@ detach: return 1; } -static void -qlcnic_fw_poll_work(struct work_struct *work) +void qlcnic_fw_poll_work(struct work_struct *work) { struct qlcnic_adapter *adapter = container_of(work, struct qlcnic_adapter, fw_work.work); @@ -3219,7 +3214,6 @@ static int qlcnic_attach_func(struct pci_dev *pdev) if (err) return err; - pci_set_power_state(pdev, PCI_D0); pci_set_master(pdev); pci_restore_state(pdev); @@ -3517,7 +3511,7 @@ static int qlcnic_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct qlcnic_adapter *adapter; - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); recheck: if (dev == NULL) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 4b9bab18ebd..ab8a6744d40 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -15,6 +15,7 @@ #define QLC_83XX_MINIDUMP_FLASH 0x520000 #define QLC_83XX_OCM_INDEX 3 #define QLC_83XX_PCI_INDEX 0 +#define QLC_83XX_DMA_ENGINE_INDEX 8 static const u32 qlcnic_ms_read_data[] = { 0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC @@ -32,6 +33,16 @@ static const u32 qlcnic_ms_read_data[] = { #define QLCNIC_DUMP_MASK_MAX 0xff +struct qlcnic_pex_dma_descriptor { + u32 read_data_size; + u32 dma_desc_cmd; + u32 src_addr_low; + u32 src_addr_high; + u32 dma_bus_addr_low; + u32 dma_bus_addr_high; + u32 rsvd[6]; +} __packed; + struct qlcnic_common_entry_hdr { u32 type; u32 offset; @@ -90,7 +101,10 @@ struct __ocm { } __packed; struct __mem { - u8 rsvd[24]; + u32 desc_card_addr; + u32 dma_desc_cmd; + u32 start_dma_cmd; + u32 rsvd[3]; u32 addr; u32 size; } __packed; @@ -466,12 +480,12 @@ skip_poll: return l2->no_ops * l2->read_addr_num * sizeof(u32); } -static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) +static u32 qlcnic_read_memory_test_agent(struct qlcnic_adapter *adapter, + struct __mem *mem, __le32 *buffer, + int *ret) { - u32 addr, data, test, ret = 0; + u32 addr, data, test; int i, reg_read; - struct __mem *mem = &entry->region.mem; reg_read = mem->size; addr = mem->addr; @@ -480,7 +494,8 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter, dev_info(&adapter->pdev->dev, "Unaligned memory addr:0x%x size:0x%x\n", addr, reg_read); - return -EINVAL; + *ret = -EINVAL; + return 0; } mutex_lock(&adapter->ahw->mem_lock); @@ -499,7 +514,7 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter, if (printk_ratelimit()) { dev_err(&adapter->pdev->dev, "failed to read through agent\n"); - ret = -EINVAL; + *ret = -EIO; goto out; } } @@ -516,6 +531,181 @@ out: return mem->size; } +/* DMA register base address */ +#define QLC_DMA_REG_BASE_ADDR(dma_no) (0x77320000 + (dma_no * 0x10000)) + +/* DMA register offsets w.r.t base address */ +#define QLC_DMA_CMD_BUFF_ADDR_LOW 0 +#define QLC_DMA_CMD_BUFF_ADDR_HI 4 +#define QLC_DMA_CMD_STATUS_CTRL 8 + +#define QLC_PEX_DMA_READ_SIZE (PAGE_SIZE * 16) + +static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter, + struct __mem *mem) +{ + struct qlcnic_dump_template_hdr *tmpl_hdr; + struct device *dev = &adapter->pdev->dev; + u32 dma_no, dma_base_addr, temp_addr; + int i, ret, dma_sts; + + tmpl_hdr = adapter->ahw->fw_dump.tmpl_hdr; + dma_no = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX]; + dma_base_addr = QLC_DMA_REG_BASE_ADDR(dma_no); + + temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_LOW; + ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, + mem->desc_card_addr); + if (ret) + return ret; + + temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_HI; + ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, 0); + if (ret) + return ret; + + temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL; + ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, + mem->start_dma_cmd); + if (ret) + return ret; + + /* Wait for DMA to complete */ + temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL; + for (i = 0; i < 400; i++) { + dma_sts = qlcnic_ind_rd(adapter, temp_addr); + + if (dma_sts & BIT_1) + usleep_range(250, 500); + else + break; + } + + if (i >= 400) { + dev_info(dev, "PEX DMA operation timed out"); + ret = -EIO; + } + + return ret; +} + +static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter, + struct __mem *mem, + __le32 *buffer, int *ret) +{ + struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + u32 temp, dma_base_addr, size = 0, read_size = 0; + struct qlcnic_pex_dma_descriptor *dma_descr; + struct qlcnic_dump_template_hdr *tmpl_hdr; + struct device *dev = &adapter->pdev->dev; + dma_addr_t dma_phys_addr; + void *dma_buffer; + + tmpl_hdr = fw_dump->tmpl_hdr; + + /* Check if DMA engine is available */ + temp = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX]; + dma_base_addr = QLC_DMA_REG_BASE_ADDR(temp); + temp = qlcnic_ind_rd(adapter, + dma_base_addr + QLC_DMA_CMD_STATUS_CTRL); + + if (!(temp & BIT_31)) { + dev_info(dev, "%s: DMA engine is not available\n", __func__); + *ret = -EIO; + return 0; + } + + /* Create DMA descriptor */ + dma_descr = kzalloc(sizeof(struct qlcnic_pex_dma_descriptor), + GFP_KERNEL); + if (!dma_descr) { + *ret = -ENOMEM; + return 0; + } + + /* dma_desc_cmd 0:15 = 0 + * dma_desc_cmd 16:19 = mem->dma_desc_cmd 0:3 + * dma_desc_cmd 20:23 = pci function number + * dma_desc_cmd 24:31 = mem->dma_desc_cmd 8:15 + */ + dma_phys_addr = fw_dump->phys_addr; + dma_buffer = fw_dump->dma_buffer; + temp = 0; + temp = mem->dma_desc_cmd & 0xff0f; + temp |= (adapter->ahw->pci_func & 0xf) << 4; + dma_descr->dma_desc_cmd = (temp << 16) & 0xffff0000; + dma_descr->dma_bus_addr_low = LSD(dma_phys_addr); + dma_descr->dma_bus_addr_high = MSD(dma_phys_addr); + dma_descr->src_addr_high = 0; + + /* Collect memory dump using multiple DMA operations if required */ + while (read_size < mem->size) { + if (mem->size - read_size >= QLC_PEX_DMA_READ_SIZE) + size = QLC_PEX_DMA_READ_SIZE; + else + size = mem->size - read_size; + + dma_descr->src_addr_low = mem->addr + read_size; + dma_descr->read_data_size = size; + + /* Write DMA descriptor to MS memory*/ + temp = sizeof(struct qlcnic_pex_dma_descriptor) / 16; + *ret = qlcnic_83xx_ms_mem_write128(adapter, mem->desc_card_addr, + (u32 *)dma_descr, temp); + if (*ret) { + dev_info(dev, "Failed to write DMA descriptor to MS memory at address 0x%x\n", + mem->desc_card_addr); + goto free_dma_descr; + } + + *ret = qlcnic_start_pex_dma(adapter, mem); + if (*ret) { + dev_info(dev, "Failed to start PEX DMA operation\n"); + goto free_dma_descr; + } + + memcpy(buffer, dma_buffer, size); + buffer += size / 4; + read_size += size; + } + +free_dma_descr: + kfree(dma_descr); + + return read_size; +} + +static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + struct device *dev = &adapter->pdev->dev; + struct __mem *mem = &entry->region.mem; + u32 data_size; + int ret = 0; + + if (fw_dump->use_pex_dma) { + data_size = qlcnic_read_memory_pexdma(adapter, mem, buffer, + &ret); + if (ret) + dev_info(dev, + "Failed to read memory dump using PEX DMA: mask[0x%x]\n", + entry->hdr.mask); + else + return data_size; + } + + data_size = qlcnic_read_memory_test_agent(adapter, mem, buffer, &ret); + if (ret) { + dev_info(dev, + "Failed to read memory dump using test agent method: mask[0x%x]\n", + entry->hdr.mask); + return 0; + } else { + return data_size; + } +} + static u32 qlcnic_dump_nop(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry, __le32 *buffer) { @@ -893,6 +1083,12 @@ flash_temp: tmpl_hdr = ahw->fw_dump.tmpl_hdr; tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF; + + if ((tmpl_hdr->version & 0xffffff) >= 0x20001) + ahw->fw_dump.use_pex_dma = true; + else + ahw->fw_dump.use_pex_dma = false; + ahw->fw_dump.enable = 1; return 0; @@ -910,7 +1106,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr; static const struct qlcnic_dump_operations *fw_dump_ops; + struct device *dev = &adapter->pdev->dev; struct qlcnic_hardware_context *ahw; + void *temp_buffer; ahw = adapter->ahw; @@ -944,6 +1142,16 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION; tmpl_hdr->sys_info[1] = adapter->fw_version; + if (fw_dump->use_pex_dma) { + temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE, + &fw_dump->phys_addr, + GFP_KERNEL); + if (!temp_buffer) + fw_dump->use_pex_dma = false; + else + fw_dump->dma_buffer = temp_buffer; + } + if (qlcnic_82xx_check(adapter)) { ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops); fw_dump_ops = qlcnic_fw_dump_ops; @@ -1002,6 +1210,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) return 0; } error: + if (fw_dump->use_pex_dma) + dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE, + fw_dump->dma_buffer, fw_dump->phys_addr); vfree(fw_dump->data); return -EINVAL; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index d85fbb57c25..0daf660e12a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h @@ -129,6 +129,7 @@ struct qlcnic_vport { u8 vlan_mode; u16 vlan; u8 qos; + bool spoofchk; u8 mac[6]; }; @@ -194,6 +195,8 @@ int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *, int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *, struct qlcnic_info *, u16); int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *, u16, u8); +int qlcnic_sriov_vf_shutdown(struct pci_dev *); +int qlcnic_sriov_vf_resume(struct qlcnic_adapter *); static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) { @@ -225,6 +228,7 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); int qlcnic_sriov_get_vf_config(struct net_device *, int , struct ifla_vf_info *); int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8); +int qlcnic_sriov_set_vf_spoofchk(struct net_device *, int, bool); #else static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 196b2d10040..62380ce8990 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -35,6 +35,7 @@ static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *); static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *); static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *, struct qlcnic_cmd_args *); +static void qlcnic_sriov_process_bc_cmd(struct work_struct *); static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { .read_crb = qlcnic_83xx_read_crb, @@ -75,6 +76,8 @@ static struct qlcnic_nic_template qlcnic_sriov_vf_ops = { .cancel_idc_work = qlcnic_sriov_vf_cancel_fw_work, .napi_add = qlcnic_83xx_napi_add, .napi_del = qlcnic_83xx_napi_del, + .shutdown = qlcnic_sriov_vf_shutdown, + .resume = qlcnic_sriov_vf_resume, .config_ipaddr = qlcnic_83xx_config_ipaddr, .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr, }; @@ -179,6 +182,8 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) spin_lock_init(&vf->rcv_pend.lock); init_completion(&vf->ch_free_cmpl); + INIT_WORK(&vf->trans_work, qlcnic_sriov_process_bc_cmd); + if (qlcnic_sriov_pf_check(adapter)) { vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL); if (!vp) { @@ -187,6 +192,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) } sriov->vf_info[i].vp = vp; vp->max_tx_bw = MAX_BW; + vp->spoofchk = true; random_ether_addr(vp->mac); dev_info(&adapter->pdev->dev, "MAC Address %pM is configured for VF %d\n", @@ -652,6 +658,8 @@ int qlcnic_sriov_vf_init(struct qlcnic_adapter *adapter, int pci_using_dac) if (qlcnic_read_mac_addr(adapter)) dev_warn(&adapter->pdev->dev, "failed to read mac addr\n"); + INIT_DELAYED_WORK(&adapter->idc_aen_work, qlcnic_83xx_idc_aen_work); + clear_bit(__QLCNIC_RESETTING, &adapter->state); return 0; } @@ -864,7 +872,6 @@ static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov, vf->adapter->need_fw_reset) return; - INIT_WORK(&vf->trans_work, func); queue_work(sriov->bc.bc_trans_wq, &vf->trans_work); } @@ -1675,7 +1682,7 @@ static int qlcnic_sriov_vf_handle_dev_ready(struct qlcnic_adapter *adapter) qlcnic_sriov_vf_attach(adapter); adapter->fw_fail_cnt = 0; dev_info(dev, - "%s: Reinitalization of VF 0x%x done after FW reset\n", + "%s: Reinitialization of VF 0x%x done after FW reset\n", __func__, func); } else { dev_err(dev, @@ -1949,3 +1956,54 @@ static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *adapter) kfree(cur); } } + +int qlcnic_sriov_vf_shutdown(struct pci_dev *pdev) +{ + struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev = adapter->netdev; + int retval; + + netif_device_detach(netdev); + qlcnic_cancel_idc_work(adapter); + + if (netif_running(netdev)) + qlcnic_down(adapter, netdev); + + qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM); + qlcnic_sriov_cfg_bc_intr(adapter, 0); + qlcnic_83xx_disable_mbx_intr(adapter); + cancel_delayed_work_sync(&adapter->idc_aen_work); + + retval = pci_save_state(pdev); + if (retval) + return retval; + + return 0; +} + +int qlcnic_sriov_vf_resume(struct qlcnic_adapter *adapter) +{ + struct qlc_83xx_idc *idc = &adapter->ahw->idc; + struct net_device *netdev = adapter->netdev; + int err; + + set_bit(QLC_83XX_MODULE_LOADED, &idc->status); + qlcnic_83xx_enable_mbx_intrpt(adapter); + err = qlcnic_sriov_cfg_bc_intr(adapter, 1); + if (err) + return err; + + err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT); + if (!err) { + if (netif_running(netdev)) { + err = qlcnic_up(adapter, netdev); + if (!err) + qlcnic_restore_indev_addr(netdev, NETDEV_UP); + } + } + + netif_device_attach(netdev); + qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state, + idc->delay); + return err; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 1a66ccded23..ee0c1d30796 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -580,6 +580,7 @@ static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func) struct qlcnic_cmd_args cmd; struct qlcnic_vport *vp; int err, id; + u8 *mac; id = qlcnic_sriov_func_to_index(adapter, func); if (id < 0) @@ -591,6 +592,14 @@ static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func) return err; cmd.req.arg[1] = 0x3 | func << 16; + if (vp->spoofchk == true) { + mac = vp->mac; + cmd.req.arg[2] |= BIT_1 | BIT_3 | BIT_8; + cmd.req.arg[4] = mac[5] | mac[4] << 8 | mac[3] << 16 | + mac[2] << 24; + cmd.req.arg[5] = mac[1] | mac[0] << 8; + } + if (vp->vlan_mode == QLC_PVID_MODE) { cmd.req.arg[2] |= BIT_6; cmd.req.arg[3] |= vp->vlan << 8; @@ -1767,6 +1776,7 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, memcpy(&ivi->mac, vp->mac, ETH_ALEN); ivi->vlan = vp->vlan; ivi->qos = vp->qos; + ivi->spoofchk = vp->spoofchk; if (vp->max_tx_bw == MAX_BW) ivi->tx_rate = 0; else @@ -1775,3 +1785,29 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, ivi->vf = vf; return 0; } + +int qlcnic_sriov_set_vf_spoofchk(struct net_device *netdev, int vf, bool chk) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_sriov *sriov = adapter->ahw->sriov; + struct qlcnic_vf_info *vf_info; + struct qlcnic_vport *vp; + + if (!qlcnic_sriov_pf_check(adapter)) + return -EOPNOTSUPP; + + if (vf >= sriov->num_vfs) + return -EINVAL; + + vf_info = &sriov->vf_info[vf]; + vp = vf_info->vp; + if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { + netdev_err(netdev, + "Spoof check change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n", + vf); + return -EOPNOTSUPP; + } + + vp->spoofchk = chk; + return 0; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index e7a2fe21b64..10ed82b3bac 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -47,7 +47,7 @@ static ssize_t qlcnic_store_bridged_mode(struct device *dev, if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) goto err_out; - if (strict_strtoul(buf, 2, &new)) + if (kstrtoul(buf, 2, &new)) goto err_out; if (!qlcnic_config_bridged_mode(adapter, !!new)) @@ -77,7 +77,7 @@ static ssize_t qlcnic_store_diag_mode(struct device *dev, struct qlcnic_adapter *adapter = dev_get_drvdata(dev); unsigned long new; - if (strict_strtoul(buf, 2, &new)) + if (kstrtoul(buf, 2, &new)) return -EINVAL; if (!!new != !!(adapter->flags & QLCNIC_DIAG_ENABLED)) @@ -114,57 +114,51 @@ static int qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, return 0; } -static ssize_t qlcnic_store_beacon(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static int qlcnic_83xx_store_beacon(struct qlcnic_adapter *adapter, + const char *buf, size_t len) { - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_hardware_context *ahw = adapter->ahw; - int err, max_sds_rings = adapter->max_sds_rings; - u16 beacon; - u8 b_state, b_rate; unsigned long h_beacon; + int err; - if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { - dev_warn(dev, - "LED test not supported in non privileged mode\n"); - return -EOPNOTSUPP; - } + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) + return -EIO; - if (qlcnic_83xx_check(adapter) && - !test_bit(__QLCNIC_RESETTING, &adapter->state)) { - if (kstrtoul(buf, 2, &h_beacon)) - return -EINVAL; + if (kstrtoul(buf, 2, &h_beacon)) + return -EINVAL; - if (ahw->beacon_state == h_beacon) - return len; + if (ahw->beacon_state == h_beacon) + return len; - rtnl_lock(); - if (!ahw->beacon_state) { - if (test_and_set_bit(__QLCNIC_LED_ENABLE, - &adapter->state)) { - rtnl_unlock(); - return -EBUSY; - } - } - if (h_beacon) { - err = qlcnic_83xx_config_led(adapter, 1, h_beacon); - if (err) - goto beacon_err; - } else { - err = qlcnic_83xx_config_led(adapter, 0, !h_beacon); - if (err) - goto beacon_err; + rtnl_lock(); + if (!ahw->beacon_state) { + if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { + rtnl_unlock(); + return -EBUSY; } - /* set the current beacon state */ + } + + if (h_beacon) + err = qlcnic_83xx_config_led(adapter, 1, h_beacon); + else + err = qlcnic_83xx_config_led(adapter, 0, !h_beacon); + if (!err) ahw->beacon_state = h_beacon; -beacon_err: - if (!ahw->beacon_state) - clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); - rtnl_unlock(); - return len; - } + if (!ahw->beacon_state) + clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); + + rtnl_unlock(); + return len; +} + +static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, + const char *buf, size_t len) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + int err, max_sds_rings = adapter->max_sds_rings; + u16 beacon; + u8 h_beacon_state, b_state, b_rate; if (len != sizeof(u16)) return QL_STATUS_INVALID_PARAM; @@ -174,16 +168,29 @@ beacon_err: if (err) return err; - if (adapter->ahw->beacon_state == b_state) + if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_BEACON) { + err = qlcnic_get_beacon_state(adapter, &h_beacon_state); + if (!err) { + dev_info(&adapter->pdev->dev, + "Failed to get current beacon state\n"); + } else { + if (h_beacon_state == QLCNIC_BEACON_DISABLE) + ahw->beacon_state = 0; + else if (h_beacon_state == QLCNIC_BEACON_EANBLE) + ahw->beacon_state = 2; + } + } + + if (ahw->beacon_state == b_state) return len; rtnl_lock(); - - if (!adapter->ahw->beacon_state) + if (!ahw->beacon_state) { if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { rtnl_unlock(); return -EBUSY; } + } if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { err = -EIO; @@ -206,14 +213,37 @@ beacon_err: if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) qlcnic_diag_free_res(adapter->netdev, max_sds_rings); - out: - if (!adapter->ahw->beacon_state) +out: + if (!ahw->beacon_state) clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); rtnl_unlock(); return err; } +static ssize_t qlcnic_store_beacon(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int err = 0; + + if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { + dev_warn(dev, + "LED test not supported in non privileged mode\n"); + return -EOPNOTSUPP; + } + + if (qlcnic_82xx_check(adapter)) + err = qlcnic_82xx_store_beacon(adapter, buf, len); + else if (qlcnic_83xx_check(adapter)) + err = qlcnic_83xx_store_beacon(adapter, buf, len); + else + return -EIO; + + return err; +} + static ssize_t qlcnic_show_beacon(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index f87cc216045..2553cf4503b 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4946,15 +4946,4 @@ static struct pci_driver qlge_driver = { .err_handler = &qlge_err_handler }; -static int __init qlge_init_module(void) -{ - return pci_register_driver(&qlge_driver); -} - -static void __exit qlge_exit(void) -{ - pci_unregister_driver(&qlge_driver); -} - -module_init(qlge_init_module); -module_exit(qlge_exit); +module_pci_driver(qlge_driver); diff --git a/drivers/net/ethernet/rdc/Kconfig b/drivers/net/ethernet/rdc/Kconfig index c8ba4b3494c..2055f7eb2ba 100644 --- a/drivers/net/ethernet/rdc/Kconfig +++ b/drivers/net/ethernet/rdc/Kconfig @@ -22,7 +22,6 @@ config R6040 tristate "RDC R6040 Fast Ethernet Adapter support" depends on PCI select CRC32 - select NET_CORE select MII select PHYLIB ---help--- diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 03523459c40..e6acb9fa576 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -1817,7 +1817,7 @@ static int cp_set_eeprom(struct net_device *dev, /* Put the board into D3cold state and wait for WakeUp signal */ static void cp_set_d3_state (struct cp_private *cp) { - pci_enable_wake (cp->pdev, 0, 1); /* Enable PME# generation */ + pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */ pci_set_power_state (cp->pdev, PCI_D3hot); } diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig index 783fa8b5cde..ae5d027096e 100644 --- a/drivers/net/ethernet/realtek/Kconfig +++ b/drivers/net/ethernet/realtek/Kconfig @@ -37,7 +37,6 @@ config 8139CP tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This is a driver for the Fast Ethernet PCI network cards based on @@ -52,7 +51,6 @@ config 8139TOO tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This is a driver for the Fast Ethernet PCI network cards based on @@ -107,7 +105,6 @@ config R8169 depends on PCI select FW_LOADER select CRC32 - select NET_CORE select MII ---help--- Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter. diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index bed9841d728..544514e6618 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -4,14 +4,7 @@ config SH_ETH tristate "Renesas SuperH Ethernet support" - depends on (SUPERH || ARCH_SHMOBILE) && \ - (CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \ - CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \ - CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7734 || \ - CPU_SUBTYPE_SH7757 || ARCH_R8A7740 || \ - ARCH_R8A7778 || ARCH_R8A7779) select CRC32 - select NET_CORE select MII select MDIO_BITBANG select PHYLIB diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index e29fe8dbd22..a753928bab9 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -313,9 +313,14 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = { [TSU_ADRL31] = 0x01fc, }; -#if defined(CONFIG_CPU_SUBTYPE_SH7734) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_ARCH_R8A7740) +static int sh_eth_is_gether(struct sh_eth_private *mdp) +{ + if (mdp->reg_offset == sh_eth_offset_gigabit) + return 1; + else + return 0; +} + static void sh_eth_select_mii(struct net_device *ndev) { u32 value = 0x0; @@ -339,11 +344,7 @@ static void sh_eth_select_mii(struct net_device *ndev) sh_eth_write(ndev, value, RMII_MII); } -#endif -/* There is CPU dependent code */ -#if defined(CONFIG_ARCH_R8A7778) || defined(CONFIG_ARCH_R8A7779) -#define SH_ETH_RESET_DEFAULT 1 static void sh_eth_set_duplex(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -354,7 +355,8 @@ static void sh_eth_set_duplex(struct net_device *ndev) sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); } -static void sh_eth_set_rate(struct net_device *ndev) +/* There is CPU dependent code */ +static void sh_eth_set_rate_r8a777x(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -371,9 +373,9 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* R8A7778/9 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +static struct sh_eth_cpu_data r8a777x_data = { .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_r8a777x, .ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD, .ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP, @@ -383,26 +385,14 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, - .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE, .apr = 1, .mpr = 1, .tpauser = 1, .hw_swap = 1, }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -#define SH_ETH_RESET_DEFAULT 1 -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_sh7724(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -419,19 +409,18 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* SH7724 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +static struct sh_eth_cpu_data sh7724_data = { .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_sh7724, .ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD, .ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP, - .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x01ff009f, + .eesipr_value = 0x01ff009f, .tx_check = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO, .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, - .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE, .apr = 1, .mpr = 1, @@ -440,22 +429,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .rpadir = 1, .rpadir_value = 0x00020000, /* NET_IP_ALIGN assumed to be 2 */ }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) -#define SH_ETH_HAS_BOTH_MODULES 1 -#define SH_ETH_HAS_TSU 1 -static int sh_eth_check_reset(struct net_device *ndev); - -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_sh7757(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -472,9 +447,9 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* SH7757 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { - .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, +static struct sh_eth_cpu_data sh7757_data = { + .set_duplex = sh_eth_set_duplex, + .set_rate = sh_eth_set_rate_sh7757, .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .rmcr_value = 0x00000001, @@ -483,8 +458,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, - .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE, + .irq_flags = IRQF_SHARED, .apr = 1, .mpr = 1, .tpauser = 1, @@ -494,7 +469,7 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .rpadir_value = 2 << 16, }; -#define SH_GIGA_ETH_BASE 0xfee00000 +#define SH_GIGA_ETH_BASE 0xfee00000UL #define GIGA_MALR(port) (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c8) #define GIGA_MAHR(port) (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c0) static void sh_eth_chip_reset_giga(struct net_device *ndev) @@ -519,52 +494,6 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev) } } -static int sh_eth_is_gether(struct sh_eth_private *mdp); -static int sh_eth_reset(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - int ret = 0; - - if (sh_eth_is_gether(mdp)) { - sh_eth_write(ndev, 0x03, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, - EDMR); - - ret = sh_eth_check_reset(ndev); - if (ret) - goto out; - - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - } else { - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, - EDMR); - mdelay(3); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, - EDMR); - } - -out: - return ret; -} - -static void sh_eth_set_duplex_giga(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - static void sh_eth_set_rate_giga(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -585,9 +514,9 @@ static void sh_eth_set_rate_giga(struct net_device *ndev) } /* SH7757(GETHERC) */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { +static struct sh_eth_cpu_data sh7757_data_giga = { .chip_reset = sh_eth_chip_reset_giga, - .set_duplex = sh_eth_set_duplex_giga, + .set_duplex = sh_eth_set_duplex, .set_rate = sh_eth_set_rate_giga, .ecsr_value = ECSR_ICD | ECSR_MPD, @@ -598,11 +527,10 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, - .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \ - EESR_TFE, .fdr_value = 0x0000072f, .rmcr_value = 0x00000001, + .irq_flags = IRQF_SHARED, .apr = 1, .mpr = 1, .tpauser = 1, @@ -615,19 +543,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { .tsu = 1, }; -static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) -{ - if (sh_eth_is_gether(mdp)) - return &sh_eth_my_cpu_data_giga; - else - return &sh_eth_my_cpu_data; -} - -#elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) -#define SH_ETH_HAS_TSU 1 -static int sh_eth_check_reset(struct net_device *ndev); -static void sh_eth_reset_hw_crc(struct net_device *ndev); - static void sh_eth_chip_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -637,17 +552,7 @@ static void sh_eth_chip_reset(struct net_device *ndev) mdelay(1); } -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_gether(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -666,11 +571,11 @@ static void sh_eth_set_rate(struct net_device *ndev) } } -/* sh7763 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +/* SH7734 */ +static struct sh_eth_cpu_data sh7734_data = { .chip_reset = sh_eth_chip_reset, .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_gether, .ecsr_value = ECSR_ICD | ECSR_MPD, .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, @@ -680,8 +585,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, - .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \ - EESR_TFE, .apr = 1, .mpr = 1, @@ -691,54 +594,37 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .no_trimd = 1, .no_ade = 1, .tsu = 1, -#if defined(CONFIG_CPU_SUBTYPE_SH7734) - .hw_crc = 1, - .select_mii = 1, -#endif + .hw_crc = 1, + .select_mii = 1, }; -static int sh_eth_reset(struct net_device *ndev) -{ - int ret = 0; - - sh_eth_write(ndev, EDSR_ENALL, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - - ret = sh_eth_check_reset(ndev); - if (ret) - goto out; +/* SH7763 */ +static struct sh_eth_cpu_data sh7763_data = { + .chip_reset = sh_eth_chip_reset, + .set_duplex = sh_eth_set_duplex, + .set_rate = sh_eth_set_rate_gether, - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - - /* Reset HW CRC register */ - sh_eth_reset_hw_crc(ndev); - - /* Select MII mode */ - if (sh_eth_my_cpu_data.select_mii) - sh_eth_select_mii(ndev); -out: - return ret; -} + .ecsr_value = ECSR_ICD | ECSR_MPD, + .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, + .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, -static void sh_eth_reset_hw_crc(struct net_device *ndev) -{ - if (sh_eth_my_cpu_data.hw_crc) - sh_eth_write(ndev, 0x0, CSMR); -} + .tx_check = EESR_TC1 | EESR_FTC, + .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | \ + EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | \ + EESR_ECI, -#elif defined(CONFIG_ARCH_R8A7740) -#define SH_ETH_HAS_TSU 1 -static int sh_eth_check_reset(struct net_device *ndev); + .apr = 1, + .mpr = 1, + .tpauser = 1, + .bculr = 1, + .hw_swap = 1, + .no_trimd = 1, + .no_ade = 1, + .tsu = 1, + .irq_flags = IRQF_SHARED, +}; -static void sh_eth_chip_reset(struct net_device *ndev) +static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -749,65 +635,11 @@ static void sh_eth_chip_reset(struct net_device *ndev) sh_eth_select_mii(ndev); } -static int sh_eth_reset(struct net_device *ndev) -{ - int ret = 0; - - sh_eth_write(ndev, EDSR_ENALL, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - - ret = sh_eth_check_reset(ndev); - if (ret) - goto out; - - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - -out: - return ret; -} - -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - -static void sh_eth_set_rate(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - switch (mdp->speed) { - case 10: /* 10BASE */ - sh_eth_write(ndev, GECMR_10, GECMR); - break; - case 100:/* 100BASE */ - sh_eth_write(ndev, GECMR_100, GECMR); - break; - case 1000: /* 1000BASE */ - sh_eth_write(ndev, GECMR_1000, GECMR); - break; - default: - break; - } -} - /* R8A7740 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { - .chip_reset = sh_eth_chip_reset, +static struct sh_eth_cpu_data r8a7740_data = { + .chip_reset = sh_eth_chip_reset_r8a7740, .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_gether, .ecsr_value = ECSR_ICD | ECSR_MPD, .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, @@ -817,8 +649,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, - .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \ - EESR_TFE, .apr = 1, .mpr = 1, @@ -829,11 +659,10 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .no_ade = 1, .tsu = 1, .select_mii = 1, + .shift_rd0 = 1, }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7619) -#define SH_ETH_RESET_DEFAULT 1 -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +static struct sh_eth_cpu_data sh7619_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .apr = 1, @@ -841,14 +670,11 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tpauser = 1, .hw_swap = 1, }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -#define SH_ETH_RESET_DEFAULT 1 -#define SH_ETH_HAS_TSU 1 -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { + +static struct sh_eth_cpu_data sh771x_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .tsu = 1, }; -#endif static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd) { @@ -873,22 +699,8 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd) if (!cd->eesr_err_check) cd->eesr_err_check = DEFAULT_EESR_ERR_CHECK; - - if (!cd->tx_error_check) - cd->tx_error_check = DEFAULT_TX_ERROR_CHECK; } -#if defined(SH_ETH_RESET_DEFAULT) -/* Chip Reset */ -static int sh_eth_reset(struct net_device *ndev) -{ - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, EDMR); - mdelay(3); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, EDMR); - - return 0; -} -#else static int sh_eth_check_reset(struct net_device *ndev) { int ret = 0; @@ -906,7 +718,49 @@ static int sh_eth_check_reset(struct net_device *ndev) } return ret; } -#endif + +static int sh_eth_reset(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + int ret = 0; + + if (sh_eth_is_gether(mdp)) { + sh_eth_write(ndev, EDSR_ENALL, EDSR); + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, + EDMR); + + ret = sh_eth_check_reset(ndev); + if (ret) + goto out; + + /* Table Init */ + sh_eth_write(ndev, 0x0, TDLAR); + sh_eth_write(ndev, 0x0, TDFAR); + sh_eth_write(ndev, 0x0, TDFXR); + sh_eth_write(ndev, 0x0, TDFFR); + sh_eth_write(ndev, 0x0, RDLAR); + sh_eth_write(ndev, 0x0, RDFAR); + sh_eth_write(ndev, 0x0, RDFXR); + sh_eth_write(ndev, 0x0, RDFFR); + + /* Reset HW CRC register */ + if (mdp->cd->hw_crc) + sh_eth_write(ndev, 0x0, CSMR); + + /* Select MII mode */ + if (mdp->cd->select_mii) + sh_eth_select_mii(ndev); + } else { + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, + EDMR); + mdelay(3); + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, + EDMR); + } + +out: + return ret; +} #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static void sh_eth_set_receive_align(struct sk_buff *skb) @@ -982,14 +836,6 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac) } } -static int sh_eth_is_gether(struct sh_eth_private *mdp) -{ - if (mdp->reg_offset == sh_eth_offset_gigabit) - return 1; - else - return 0; -} - static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp) { if (sh_eth_is_gether(mdp)) @@ -1388,7 +1234,7 @@ static int sh_eth_txfree(struct net_device *ndev) } /* Packet receive function */ -static int sh_eth_rx(struct net_device *ndev, u32 intr_status) +static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) { struct sh_eth_private *mdp = netdev_priv(ndev); struct sh_eth_rxdesc *rxdesc; @@ -1396,6 +1242,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) int entry = mdp->cur_rx % mdp->num_rx_ring; int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx; struct sk_buff *skb; + int exceeded = 0; u16 pkt_len = 0; u32 desc_status; @@ -1407,10 +1254,15 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) if (--boguscnt < 0) break; + if (*quota <= 0) { + exceeded = 1; + break; + } + (*quota)--; + if (!(desc_status & RDFEND)) ndev->stats.rx_length_errors++; -#if defined(CONFIG_ARCH_R8A7740) /* * In case of almost all GETHER/ETHERs, the Receive Frame State * (RFS) bits in the Receive Descriptor 0 are from bit 9 to @@ -1418,8 +1270,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) * bits are from bit 25 to bit 16. So, the driver needs right * shifting by 16. */ - desc_status >>= 16; -#endif + if (mdp->cd->shift_rd0) + desc_status >>= 16; if (desc_status & (RD_RFS1 | RD_RFS2 | RD_RFS3 | RD_RFS4 | RD_RFS5 | RD_RFS6 | RD_RFS10)) { @@ -1494,7 +1346,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) sh_eth_write(ndev, EDRRR_R, EDRRR); } - return 0; + return exceeded; } static void sh_eth_rcv_snd_disable(struct net_device *ndev) @@ -1636,7 +1488,7 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) struct sh_eth_private *mdp = netdev_priv(ndev); struct sh_eth_cpu_data *cd = mdp->cd; irqreturn_t ret = IRQ_NONE; - unsigned long intr_status; + unsigned long intr_status, intr_enable; spin_lock(&mdp->lock); @@ -1647,34 +1499,41 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) * and we need to fully handle it in sh_eth_error() in order to quench * it as it doesn't get cleared by just writing 1 to the ECI bit... */ - intr_status &= sh_eth_read(ndev, EESIPR) | DMAC_M_ECI; - /* Clear interrupt */ - if (intr_status & (EESR_FRC | EESR_RMAF | EESR_RRF | - EESR_RTLF | EESR_RTSF | EESR_PRE | EESR_CERF | - cd->tx_check | cd->eesr_err_check)) { - sh_eth_write(ndev, intr_status, EESR); + intr_enable = sh_eth_read(ndev, EESIPR); + intr_status &= intr_enable | DMAC_M_ECI; + if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check)) ret = IRQ_HANDLED; - } else + else goto other_irq; - if (intr_status & (EESR_FRC | /* Frame recv*/ - EESR_RMAF | /* Multi cast address recv*/ - EESR_RRF | /* Bit frame recv */ - EESR_RTLF | /* Long frame recv*/ - EESR_RTSF | /* short frame recv */ - EESR_PRE | /* PHY-LSI recv error */ - EESR_CERF)){ /* recv frame CRC error */ - sh_eth_rx(ndev, intr_status); + if (intr_status & EESR_RX_CHECK) { + if (napi_schedule_prep(&mdp->napi)) { + /* Mask Rx interrupts */ + sh_eth_write(ndev, intr_enable & ~EESR_RX_CHECK, + EESIPR); + __napi_schedule(&mdp->napi); + } else { + dev_warn(&ndev->dev, + "ignoring interrupt, status 0x%08lx, mask 0x%08lx.\n", + intr_status, intr_enable); + } } /* Tx Check */ if (intr_status & cd->tx_check) { + /* Clear Tx interrupts */ + sh_eth_write(ndev, intr_status & cd->tx_check, EESR); + sh_eth_txfree(ndev); netif_wake_queue(ndev); } - if (intr_status & cd->eesr_err_check) + if (intr_status & cd->eesr_err_check) { + /* Clear error interrupts */ + sh_eth_write(ndev, intr_status & cd->eesr_err_check, EESR); + sh_eth_error(ndev, intr_status); + } other_irq: spin_unlock(&mdp->lock); @@ -1682,6 +1541,33 @@ other_irq: return ret; } +static int sh_eth_poll(struct napi_struct *napi, int budget) +{ + struct sh_eth_private *mdp = container_of(napi, struct sh_eth_private, + napi); + struct net_device *ndev = napi->dev; + int quota = budget; + unsigned long intr_status; + + for (;;) { + intr_status = sh_eth_read(ndev, EESR); + if (!(intr_status & EESR_RX_CHECK)) + break; + /* Clear Rx interrupts */ + sh_eth_write(ndev, intr_status & EESR_RX_CHECK, EESR); + + if (sh_eth_rx(ndev, intr_status, "a)) + goto out; + } + + napi_complete(napi); + + /* Reenable Rx interrupts */ + sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); +out: + return budget - quota; +} + /* PHY state control function */ static void sh_eth_adjust_link(struct net_device *ndev) { @@ -1972,14 +1858,7 @@ static int sh_eth_open(struct net_device *ndev) pm_runtime_get_sync(&mdp->pdev->dev); ret = request_irq(ndev->irq, sh_eth_interrupt, -#if defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7764) || \ - defined(CONFIG_CPU_SUBTYPE_SH7757) - IRQF_SHARED, -#else - 0, -#endif - ndev->name, ndev); + mdp->cd->irq_flags, ndev->name, ndev); if (ret) { dev_err(&ndev->dev, "Can not assign IRQ number\n"); return ret; @@ -2000,6 +1879,8 @@ static int sh_eth_open(struct net_device *ndev) if (ret) goto out_free_irq; + napi_enable(&mdp->napi); + return ret; out_free_irq: @@ -2095,6 +1976,8 @@ static int sh_eth_close(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); + napi_disable(&mdp->napi); + netif_stop_queue(ndev); /* Disable interrupts by clearing the interrupt mask. */ @@ -2165,7 +2048,6 @@ static int sh_eth_do_ioctl(struct net_device *ndev, struct ifreq *rq, return phy_mii_ioctl(phydev, rq, cmd); } -#if defined(SH_ETH_HAS_TSU) /* For TSU_POSTn. Please refer to the manual about this (strange) bitfields */ static void *sh_eth_tsu_get_post_reg_offset(struct sh_eth_private *mdp, int entry) @@ -2508,7 +2390,6 @@ static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev, return 0; } -#endif /* SH_ETH_HAS_TSU */ /* SuperH's TSU register init function */ static void sh_eth_tsu_init(struct sh_eth_private *mdp) @@ -2652,11 +2533,21 @@ static const struct net_device_ops sh_eth_netdev_ops = { .ndo_stop = sh_eth_close, .ndo_start_xmit = sh_eth_start_xmit, .ndo_get_stats = sh_eth_get_stats, -#if defined(SH_ETH_HAS_TSU) + .ndo_tx_timeout = sh_eth_tx_timeout, + .ndo_do_ioctl = sh_eth_do_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static const struct net_device_ops sh_eth_netdev_ops_tsu = { + .ndo_open = sh_eth_open, + .ndo_stop = sh_eth_close, + .ndo_start_xmit = sh_eth_start_xmit, + .ndo_get_stats = sh_eth_get_stats, .ndo_set_rx_mode = sh_eth_set_multicast_list, .ndo_vlan_rx_add_vid = sh_eth_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = sh_eth_vlan_rx_kill_vid, -#endif .ndo_tx_timeout = sh_eth_tx_timeout, .ndo_do_ioctl = sh_eth_do_ioctl, .ndo_validate_addr = eth_validate_addr, @@ -2671,6 +2562,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) struct net_device *ndev = NULL; struct sh_eth_private *mdp = NULL; struct sh_eth_plat_data *pd = pdev->dev.platform_data; + const struct platform_device_id *id = platform_get_device_id(pdev); /* get base addr */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -2729,15 +2621,14 @@ static int sh_eth_drv_probe(struct platform_device *pdev) mdp->reg_offset = sh_eth_get_register_offset(pd->register_type); /* set cpu data */ -#if defined(SH_ETH_HAS_BOTH_MODULES) - mdp->cd = sh_eth_get_cpu_data(mdp); -#else - mdp->cd = &sh_eth_my_cpu_data; -#endif + mdp->cd = (struct sh_eth_cpu_data *)id->driver_data; sh_eth_set_default_cpu_data(mdp->cd); /* set function */ - ndev->netdev_ops = &sh_eth_netdev_ops; + if (mdp->cd->tsu) + ndev->netdev_ops = &sh_eth_netdev_ops_tsu; + else + ndev->netdev_ops = &sh_eth_netdev_ops; SET_ETHTOOL_OPS(ndev, &sh_eth_ethtool_ops); ndev->watchdog_timeo = TX_TIMEOUT; @@ -2776,10 +2667,12 @@ static int sh_eth_drv_probe(struct platform_device *pdev) } } + netif_napi_add(ndev, &mdp->napi, sh_eth_poll, 64); + /* network device register */ ret = register_netdev(ndev); if (ret) - goto out_release; + goto out_napi_del; /* mdio bus init */ ret = sh_mdio_init(ndev, pdev->id, pd); @@ -2797,6 +2690,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev) out_unregister: unregister_netdev(ndev); +out_napi_del: + netif_napi_del(&mdp->napi); + out_release: /* net_dev free */ if (ndev) @@ -2809,16 +2705,18 @@ out: static int sh_eth_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); + struct sh_eth_private *mdp = netdev_priv(ndev); sh_mdio_release(ndev); unregister_netdev(ndev); + netif_napi_del(&mdp->napi); pm_runtime_disable(&pdev->dev); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } +#ifdef CONFIG_PM static int sh_eth_runtime_nop(struct device *dev) { /* @@ -2832,17 +2730,36 @@ static int sh_eth_runtime_nop(struct device *dev) return 0; } -static struct dev_pm_ops sh_eth_dev_pm_ops = { +static const struct dev_pm_ops sh_eth_dev_pm_ops = { .runtime_suspend = sh_eth_runtime_nop, .runtime_resume = sh_eth_runtime_nop, }; +#define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops) +#else +#define SH_ETH_PM_OPS NULL +#endif + +static struct platform_device_id sh_eth_id_table[] = { + { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, + { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, + { "sh7724-ether", (kernel_ulong_t)&sh7724_data }, + { "sh7734-gether", (kernel_ulong_t)&sh7734_data }, + { "sh7757-ether", (kernel_ulong_t)&sh7757_data }, + { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, + { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, + { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, + { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data }, + { } +}; +MODULE_DEVICE_TABLE(platform, sh_eth_id_table); static struct platform_driver sh_eth_driver = { .probe = sh_eth_drv_probe, .remove = sh_eth_drv_remove, + .id_table = sh_eth_id_table, .driver = { .name = CARDNAME, - .pm = &sh_eth_dev_pm_ops, + .pm = SH_ETH_PM_OPS, }, }; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 62689a5823b..99995bf38c4 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -166,19 +166,16 @@ enum { /* * Register's bits */ -#if defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) ||\ - defined(CONFIG_ARCH_R8A7740) -/* EDSR */ +/* EDSR : sh7734, sh7757, sh7763, and r8a7740 only */ enum EDSR_BIT { EDSR_ENT = 0x01, EDSR_ENR = 0x02, }; #define EDSR_ENALL (EDSR_ENT|EDSR_ENR) -/* GECMR */ +/* GECMR : sh7734, sh7763 and r8a7740 only */ enum GECMR_BIT { GECMR_10 = 0x0, GECMR_100 = 0x04, GECMR_1000 = 0x01, }; -#endif /* EDMR */ enum DMAC_M_BIT { @@ -251,13 +248,19 @@ enum EESR_BIT { EESR_CERF = 0x00000001, }; +#define EESR_RX_CHECK (EESR_FRC | /* Frame recv */ \ + EESR_RMAF | /* Multicast address recv */ \ + EESR_RRF | /* Bit frame recv */ \ + EESR_RTLF | /* Long frame recv */ \ + EESR_RTSF | /* Short frame recv */ \ + EESR_PRE | /* PHY-LSI recv error */ \ + EESR_CERF) /* Recv frame CRC error */ + #define DEFAULT_TX_CHECK (EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | \ EESR_RTO) #define DEFAULT_EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | \ EESR_RDE | EESR_RFRMER | EESR_ADE | \ EESR_TFE | EESR_TDE | EESR_ECI) -#define DEFAULT_TX_ERROR_CHECK (EESR_TWB | EESR_TABT | EESR_ADE | EESR_TDE | \ - EESR_TFE) /* EESIPR */ enum DMAC_IM_BIT { @@ -299,11 +302,11 @@ enum FCFTR_BIT { #define DEFAULT_FIFO_F_D_RFF (FCFTR_RFF2 | FCFTR_RFF1 | FCFTR_RFF0) #define DEFAULT_FIFO_F_D_RFD (FCFTR_RFD2 | FCFTR_RFD1 | FCFTR_RFD0) -/* Transfer descriptor bit */ +/* Transmit descriptor bit */ enum TD_STS_BIT { - TD_TACT = 0x80000000, - TD_TDLE = 0x40000000, TD_TFP1 = 0x20000000, - TD_TFP0 = 0x10000000, + TD_TACT = 0x80000000, TD_TDLE = 0x40000000, + TD_TFP1 = 0x20000000, TD_TFP0 = 0x10000000, + TD_TFE = 0x08000000, TD_TWBI = 0x04000000, }; #define TDF1ST TD_TFP1 #define TDFEND TD_TFP0 @@ -463,9 +466,9 @@ struct sh_eth_cpu_data { /* interrupt checking mask */ unsigned long tx_check; unsigned long eesr_err_check; - unsigned long tx_error_check; /* hardware features */ + unsigned long irq_flags; /* IRQ configuration flags */ unsigned no_psr:1; /* EtherC DO NOT have PSR */ unsigned apr:1; /* EtherC have APR */ unsigned mpr:1; /* EtherC have MPR */ @@ -478,6 +481,7 @@ struct sh_eth_cpu_data { unsigned no_ade:1; /* E-DMAC DO NOT have ADE bit in EESR */ unsigned hw_crc:1; /* E-DMAC have CSMR */ unsigned select_mii:1; /* EtherC have RMII_MII (MII select register) */ + unsigned shift_rd0:1; /* shift Rx descriptor word 0 right by 16 */ }; struct sh_eth_private { @@ -499,6 +503,7 @@ struct sh_eth_private { u32 cur_tx, dirty_tx; u32 rx_buf_sz; /* Based on MTU+slack. */ int edmac_endian; + struct napi_struct napi; /* MII transceiver section. */ u32 phy_id; /* PHY ID */ struct mii_bus *mii_bus; /* MDIO bus control */ diff --git a/drivers/net/ethernet/s6gmac.c b/drivers/net/ethernet/s6gmac.c index b6739afeaca..a99739c5142 100644 --- a/drivers/net/ethernet/s6gmac.c +++ b/drivers/net/ethernet/s6gmac.c @@ -1040,7 +1040,6 @@ static int s6gmac_remove(struct platform_device *pdev) unregister_netdev(dev); free_irq(dev->irq, dev); free_netdev(dev); - platform_set_drvdata(pdev, NULL); } return 0; } diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c index 0ad5694b41f..856e523ac93 100644 --- a/drivers/net/ethernet/seeq/sgiseeq.c +++ b/drivers/net/ethernet/seeq/sgiseeq.c @@ -818,7 +818,6 @@ static int __exit sgiseeq_remove(struct platform_device *pdev) dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings, sp->srings_dma); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 4a14a940c65..c72968840f1 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -21,8 +21,8 @@ #include <linux/ethtool.h> #include <linux/topology.h> #include <linux/gfp.h> -#include <linux/cpu_rmap.h> #include <linux/aer.h> +#include <linux/interrupt.h> #include "net_driver.h" #include "efx.h" #include "nic.h" @@ -1283,29 +1283,6 @@ static unsigned int efx_wanted_parallelism(struct efx_nic *efx) return count; } -static int -efx_init_rx_cpu_rmap(struct efx_nic *efx, struct msix_entry *xentries) -{ -#ifdef CONFIG_RFS_ACCEL - unsigned int i; - int rc; - - efx->net_dev->rx_cpu_rmap = alloc_irq_cpu_rmap(efx->n_rx_channels); - if (!efx->net_dev->rx_cpu_rmap) - return -ENOMEM; - for (i = 0; i < efx->n_rx_channels; i++) { - rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap, - xentries[i].vector); - if (rc) { - free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); - efx->net_dev->rx_cpu_rmap = NULL; - return rc; - } - } -#endif - return 0; -} - /* Probe the number and type of interrupts we are able to obtain, and * the resulting numbers of channels and RX queues. */ @@ -1359,11 +1336,6 @@ static int efx_probe_interrupts(struct efx_nic *efx) efx->n_tx_channels = n_channels; efx->n_rx_channels = n_channels; } - rc = efx_init_rx_cpu_rmap(efx, xentries); - if (rc) { - pci_disable_msix(efx->pci_dev); - return rc; - } for (i = 0; i < efx->n_channels; i++) efx_get_channel(efx, i)->irq = xentries[i].vector; @@ -1427,6 +1399,10 @@ static void efx_start_interrupts(struct efx_nic *efx, bool may_keep_eventq) BUG_ON(efx->state == STATE_DISABLED); + if (efx->eeh_disabled_legacy_irq) { + enable_irq(efx->legacy_irq); + efx->eeh_disabled_legacy_irq = false; + } if (efx->legacy_irq) efx->legacy_irq_enabled = true; efx_nic_enable_interrupts(efx); @@ -2120,7 +2096,7 @@ static void efx_update_name(struct efx_nic *efx) static int efx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = ptr; + struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); if (net_dev->netdev_ops == &efx_netdev_ops && event == NETDEV_CHANGENAME) @@ -2365,7 +2341,7 @@ out: * Returns 0 if the recovery mechanisms are unsuccessful. * Returns a non-zero value otherwise. */ -static int efx_try_recovery(struct efx_nic *efx) +int efx_try_recovery(struct efx_nic *efx) { #ifdef CONFIG_EEH /* A PCI error can occur and not be seen by EEH because nothing @@ -2603,10 +2579,6 @@ static void efx_pci_remove_main(struct efx_nic *efx) BUG_ON(efx->state == STATE_READY); cancel_work_sync(&efx->reset_work); -#ifdef CONFIG_RFS_ACCEL - free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); - efx->net_dev->rx_cpu_rmap = NULL; -#endif efx_stop_interrupts(efx, false); efx_nic_fini_interrupt(efx); efx_fini_port(efx); diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index 8372da239b4..bdb30bbb0c9 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -124,6 +124,7 @@ extern const struct ethtool_ops efx_ethtool_ops; extern int efx_reset(struct efx_nic *efx, enum reset_type method); extern void efx_reset_down(struct efx_nic *efx, enum reset_type method); extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); +extern int efx_try_recovery(struct efx_nic *efx); /* Global */ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 6e768175e7e..1fc21458413 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -1114,6 +1114,20 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, return 0; } +int efx_ethtool_get_ts_info(struct net_device *net_dev, + struct ethtool_ts_info *ts_info) +{ + struct efx_nic *efx = netdev_priv(net_dev); + + /* Software capabilities */ + ts_info->so_timestamping = (SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE); + ts_info->phc_index = -1; + + efx_ptp_get_ts_info(efx, ts_info); + return 0; +} + static int efx_ethtool_get_module_eeprom(struct net_device *net_dev, struct ethtool_eeprom *ee, u8 *data) @@ -1176,7 +1190,7 @@ const struct ethtool_ops efx_ethtool_ops = { .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, .get_rxfh_indir = efx_ethtool_get_rxfh_indir, .set_rxfh_indir = efx_ethtool_set_rxfh_indir, - .get_ts_info = efx_ptp_get_ts_info, + .get_ts_info = efx_ethtool_get_ts_info, .get_module_info = efx_ethtool_get_module_info, .get_module_eeprom = efx_ethtool_get_module_eeprom, }; diff --git a/drivers/net/ethernet/sfc/filter.c b/drivers/net/ethernet/sfc/filter.c index 2397f0e8d3e..b74a60ab9ac 100644 --- a/drivers/net/ethernet/sfc/filter.c +++ b/drivers/net/ethernet/sfc/filter.c @@ -1185,8 +1185,21 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, nhoff = skb_network_offset(skb); - if (skb->protocol != htons(ETH_P_IP)) + if (skb->protocol == htons(ETH_P_8021Q)) { + EFX_BUG_ON_PARANOID(skb_headlen(skb) < + nhoff + sizeof(struct vlan_hdr)); + if (((const struct vlan_hdr *)skb->data + nhoff)-> + h_vlan_encapsulated_proto != htons(ETH_P_IP)) + return -EPROTONOSUPPORT; + + /* This is IP over 802.1q VLAN. We can't filter on the + * IP 5-tuple and the vlan together, so just strip the + * vlan header and filter on the IP part. + */ + nhoff += sizeof(struct vlan_hdr); + } else if (skb->protocol != htons(ETH_P_IP)) { return -EPROTONOSUPPORT; + } /* RFS must validate the IP header length before calling us */ EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip)); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 39d6bd77f01..f4c7e6b6774 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -243,6 +243,7 @@ struct efx_rx_buffer { #define EFX_RX_BUF_LAST_IN_PAGE 0x0001 #define EFX_RX_PKT_CSUMMED 0x0002 #define EFX_RX_PKT_DISCARD 0x0004 +#define EFX_RX_PKT_TCP 0x0040 /** * struct efx_rx_page_state - Page-based rx buffer state @@ -784,9 +785,11 @@ struct efx_nic { char name[IFNAMSIZ]; struct pci_dev *pci_dev; + unsigned int port_num; const struct efx_nic_type *type; int legacy_irq; bool legacy_irq_enabled; + bool eeh_disabled_legacy_irq; struct workqueue_struct *workqueue; char workqueue_name[16]; struct work_struct reset_work; @@ -916,7 +919,7 @@ static inline int efx_dev_registered(struct efx_nic *efx) static inline unsigned int efx_port_num(struct efx_nic *efx) { - return efx->net_dev->dev_id; + return efx->port_num; } /** diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index b0503cd8c2a..56ed3bc71e0 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -14,6 +14,7 @@ #include <linux/pci.h> #include <linux/module.h> #include <linux/seq_file.h> +#include <linux/cpu_rmap.h> #include "net_driver.h" #include "bitfield.h" #include "efx.h" @@ -1080,12 +1081,21 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event) rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); if (likely(rx_ev_pkt_ok)) { - /* If packet is marked as OK and packet type is TCP/IP or - * UDP/IP, then we can rely on the hardware checksum. + /* If packet is marked as OK then we can rely on the + * hardware checksum and classification. */ - flags = (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP || - rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP) ? - EFX_RX_PKT_CSUMMED : 0; + flags = 0; + switch (rx_ev_hdr_type) { + case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP: + flags |= EFX_RX_PKT_TCP; + /* fall through */ + case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP: + flags |= EFX_RX_PKT_CSUMMED; + /* fall through */ + case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER: + case FSE_AZ_RX_EV_HDR_TYPE_OTHER: + break; + } } else { flags = efx_handle_rx_not_ok(rx_queue, event); } @@ -1579,6 +1589,16 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id) efx_readd(efx, ®, FR_BZ_INT_ISR0); queues = EFX_EXTRACT_DWORD(reg, 0, 31); + /* Legacy interrupts are disabled too late by the EEH kernel + * code. Disable them earlier. + * If an EEH error occurred, the read will have returned all ones. + */ + if (EFX_DWORD_IS_ALL_ONES(reg) && efx_try_recovery(efx) && + !efx->eeh_disabled_legacy_irq) { + disable_irq_nosync(efx->legacy_irq); + efx->eeh_disabled_legacy_irq = true; + } + /* Handle non-event-queue sources */ if (queues & (1U << efx->irq_level)) { syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); @@ -1687,6 +1707,7 @@ void efx_nic_push_rx_indir_table(struct efx_nic *efx) int efx_nic_init_interrupt(struct efx_nic *efx) { struct efx_channel *channel; + unsigned int n_irqs; int rc; if (!EFX_INT_MODE_USE_MSI(efx)) { @@ -1707,7 +1728,19 @@ int efx_nic_init_interrupt(struct efx_nic *efx) return 0; } +#ifdef CONFIG_RFS_ACCEL + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { + efx->net_dev->rx_cpu_rmap = + alloc_irq_cpu_rmap(efx->n_rx_channels); + if (!efx->net_dev->rx_cpu_rmap) { + rc = -ENOMEM; + goto fail1; + } + } +#endif + /* Hook MSI or MSI-X interrupt */ + n_irqs = 0; efx_for_each_channel(channel, efx) { rc = request_irq(channel->irq, efx_msi_interrupt, IRQF_PROBE_SHARED, /* Not shared */ @@ -1718,13 +1751,31 @@ int efx_nic_init_interrupt(struct efx_nic *efx) "failed to hook IRQ %d\n", channel->irq); goto fail2; } + ++n_irqs; + +#ifdef CONFIG_RFS_ACCEL + if (efx->interrupt_mode == EFX_INT_MODE_MSIX && + channel->channel < efx->n_rx_channels) { + rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap, + channel->irq); + if (rc) + goto fail2; + } +#endif } return 0; fail2: - efx_for_each_channel(channel, efx) +#ifdef CONFIG_RFS_ACCEL + free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); + efx->net_dev->rx_cpu_rmap = NULL; +#endif + efx_for_each_channel(channel, efx) { + if (n_irqs-- == 0) + break; free_irq(channel->irq, &efx->channel[channel->channel]); + } fail1: return rc; } @@ -1734,11 +1785,14 @@ void efx_nic_fini_interrupt(struct efx_nic *efx) struct efx_channel *channel; efx_oword_t reg; +#ifdef CONFIG_RFS_ACCEL + free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); + efx->net_dev->rx_cpu_rmap = NULL; +#endif + /* Disable MSI/MSI-X interrupts */ - efx_for_each_channel(channel, efx) { - if (channel->irq) - free_irq(channel->irq, &efx->channel[channel->channel]); - } + efx_for_each_channel(channel, efx) + free_irq(channel->irq, &efx->channel[channel->channel]); /* ACK legacy interrupt */ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 1b000332349..d63c2991a75 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -254,8 +254,8 @@ extern int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf, struct ethtool_ts_info; extern void efx_ptp_probe(struct efx_nic *efx); extern int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd); -extern int efx_ptp_get_ts_info(struct net_device *net_dev, - struct ethtool_ts_info *ts_info); +extern void efx_ptp_get_ts_info(struct efx_nic *efx, + struct ethtool_ts_info *ts_info); extern bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); extern int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); extern void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev); diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index 9a95abf2ded..b495394a6df 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -1203,18 +1203,16 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init) return 0; } -int -efx_ptp_get_ts_info(struct net_device *net_dev, struct ethtool_ts_info *ts_info) +void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info) { - struct efx_nic *efx = netdev_priv(net_dev); struct efx_ptp_data *ptp = efx->ptp_data; if (!ptp) - return -EOPNOTSUPP; + return; - ts_info->so_timestamping = (SOF_TIMESTAMPING_TX_HARDWARE | - SOF_TIMESTAMPING_RX_HARDWARE | - SOF_TIMESTAMPING_RAW_HARDWARE); + ts_info->so_timestamping |= (SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE); ts_info->phc_index = ptp_clock_index(ptp->phc_clock); ts_info->tx_types = 1 << HWTSTAMP_TX_OFF | 1 << HWTSTAMP_TX_ON; ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE | @@ -1224,7 +1222,6 @@ efx_ptp_get_ts_info(struct net_device *net_dev, struct ethtool_ts_info *ts_info) 1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT | 1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC | 1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ); - return 0; } int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd) diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index a7dfe36cabf..6af9cfda50f 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -36,7 +36,7 @@ #define EFX_RECYCLE_RING_SIZE_NOIOMMU (2 * EFX_RX_PREFERRED_BATCH) /* Size of buffer allocated for skb header area. */ -#define EFX_SKB_HEADERS 64u +#define EFX_SKB_HEADERS 128u /* This is the percentage fill level below which new RX descriptors * will be added to the RX descriptor ring. @@ -282,9 +282,9 @@ static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, } /* Recycle the pages that are used by buffers that have just been received. */ -static void efx_recycle_rx_buffers(struct efx_channel *channel, - struct efx_rx_buffer *rx_buf, - unsigned int n_frags) +static void efx_recycle_rx_pages(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, + unsigned int n_frags) { struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); @@ -294,6 +294,20 @@ static void efx_recycle_rx_buffers(struct efx_channel *channel, } while (--n_frags); } +static void efx_discard_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, + unsigned int n_frags) +{ + struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); + + efx_recycle_rx_pages(channel, rx_buf, n_frags); + + do { + efx_free_rx_buffer(rx_buf); + rx_buf = efx_rx_buf_next(rx_queue, rx_buf); + } while (--n_frags); +} + /** * efx_fast_push_rx_descriptors - push new RX descriptors quickly * @rx_queue: RX descriptor queue @@ -533,8 +547,7 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, */ if (unlikely(rx_buf->flags & EFX_RX_PKT_DISCARD)) { efx_rx_flush_packet(channel); - put_page(rx_buf->page); - efx_recycle_rx_buffers(channel, rx_buf, n_frags); + efx_discard_rx_packet(channel, rx_buf, n_frags); return; } @@ -570,9 +583,9 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, efx_sync_rx_buffer(efx, rx_buf, rx_buf->len); } - /* All fragments have been DMA-synced, so recycle buffers and pages. */ + /* All fragments have been DMA-synced, so recycle pages. */ rx_buf = efx_rx_buffer(rx_queue, index); - efx_recycle_rx_buffers(channel, rx_buf, n_frags); + efx_recycle_rx_pages(channel, rx_buf, n_frags); /* Pipeline receives so that we give time for packet headers to be * prefetched into cache. @@ -598,6 +611,8 @@ static void efx_rx_deliver(struct efx_channel *channel, u8 *eh, /* Set the SKB flags */ skb_checksum_none_assert(skb); + if (likely(rx_buf->flags & EFX_RX_PKT_CSUMMED)) + skb->ip_summed = CHECKSUM_UNNECESSARY; if (channel->type->receive_skb) if (channel->type->receive_skb(channel, skb)) @@ -627,7 +642,7 @@ void __efx_rx_packet(struct efx_channel *channel) if (unlikely(!(efx->net_dev->features & NETIF_F_RXCSUM))) rx_buf->flags &= ~EFX_RX_PKT_CSUMMED; - if (!channel->type->receive_skb) + if ((rx_buf->flags & EFX_RX_PKT_TCP) && !channel->type->receive_skb) efx_rx_packet_gro(channel, rx_buf, channel->rx_pkt_n_frags, eh); else efx_rx_deliver(channel, eh, rx_buf, channel->rx_pkt_n_frags); @@ -675,7 +690,7 @@ static void efx_init_rx_recycle_ring(struct efx_nic *efx, #ifdef CONFIG_PPC64 bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU; #else - if (efx->pci_dev->dev.iommu_group) + if (iommu_present(&pci_bus_type)) bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU; else bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_NOIOMMU; diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 51669244d15..8c91775e3c5 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -304,7 +304,7 @@ static int siena_probe_nic(struct efx_nic *efx) } efx_reado(efx, ®, FR_AZ_CS_DEBUG); - efx->net_dev->dev_id = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1; + efx->port_num = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1; efx_mcdi_init(efx); diff --git a/drivers/net/ethernet/sgi/Kconfig b/drivers/net/ethernet/sgi/Kconfig index c1c4bb868a3..e832f46660c 100644 --- a/drivers/net/ethernet/sgi/Kconfig +++ b/drivers/net/ethernet/sgi/Kconfig @@ -22,7 +22,6 @@ config SGI_IOC3_ETH bool "SGI IOC3 Ethernet" depends on PCI && SGI_IP27 select CRC32 - select NET_CORE select MII ---help--- If you have a network (Ethernet) card of this type, say Y and read diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index 7ed08c32a9c..ffa78432164 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1398,16 +1398,6 @@ static struct pci_driver ioc3_driver = { .remove = ioc3_remove_one, }; -static int __init ioc3_init_module(void) -{ - return pci_register_driver(&ioc3_driver); -} - -static void __exit ioc3_cleanup_module(void) -{ - pci_unregister_driver(&ioc3_driver); -} - static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long data; @@ -1677,9 +1667,7 @@ static void ioc3_set_multicast_list(struct net_device *dev) netif_wake_queue(dev); /* Let us get going again. */ } +module_pci_driver(ioc3_driver); MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); MODULE_DESCRIPTION("SGI IOC3 Ethernet driver"); MODULE_LICENSE("GPL"); - -module_init(ioc3_init_module); -module_exit(ioc3_cleanup_module); diff --git a/drivers/net/ethernet/sgi/meth.c b/drivers/net/ethernet/sgi/meth.c index 4bdbaad9932..9f5f35e041a 100644 --- a/drivers/net/ethernet/sgi/meth.c +++ b/drivers/net/ethernet/sgi/meth.c @@ -863,7 +863,6 @@ static int __exit meth_remove(struct platform_device *pdev) unregister_netdev(dev); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/silan/sc92031.c b/drivers/net/ethernet/silan/sc92031.c index 28f7268f1b8..5eb933c97bb 100644 --- a/drivers/net/ethernet/silan/sc92031.c +++ b/drivers/net/ethernet/silan/sc92031.c @@ -1578,19 +1578,7 @@ static struct pci_driver sc92031_pci_driver = { .resume = sc92031_resume, }; -static int __init sc92031_init(void) -{ - return pci_register_driver(&sc92031_pci_driver); -} - -static void __exit sc92031_exit(void) -{ - pci_unregister_driver(&sc92031_pci_driver); -} - -module_init(sc92031_init); -module_exit(sc92031_exit); - +module_pci_driver(sc92031_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Cesar Eduardo Barros <cesarb@cesarb.net>"); MODULE_DESCRIPTION("Silan SC92031 PCI Fast Ethernet Adapter driver"); diff --git a/drivers/net/ethernet/sis/Kconfig b/drivers/net/ethernet/sis/Kconfig index f1135cc1bd4..68d052b09af 100644 --- a/drivers/net/ethernet/sis/Kconfig +++ b/drivers/net/ethernet/sis/Kconfig @@ -22,7 +22,6 @@ config SIS900 tristate "SiS 900/7016 PCI Fast Ethernet Adapter support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This is a driver for the Fast Ethernet PCI network cards based on @@ -39,7 +38,6 @@ config SIS190 tristate "SiS190/SiS191 gigabit ethernet support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- Say Y here if you have a SiS 190 PCI Fast Ethernet adapter or diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index 9a9c379420d..02df0894690 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1934,15 +1934,4 @@ static struct pci_driver sis190_pci_driver = { .remove = sis190_remove_one, }; -static int __init sis190_init_module(void) -{ - return pci_register_driver(&sis190_pci_driver); -} - -static void __exit sis190_cleanup_module(void) -{ - pci_unregister_driver(&sis190_pci_driver); -} - -module_init(sis190_init_module); -module_exit(sis190_cleanup_module); +module_pci_driver(sis190_pci_driver); diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index bb4c1674ff9..068fc44d37e 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -37,7 +37,6 @@ config SMC9194 config SMC91X tristate "SMC 91C9x/91C1xxx support" select CRC32 - select NET_CORE select MII depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \ MN10300 || COLDFIRE || ARM64) @@ -57,7 +56,6 @@ config PCMCIA_SMC91C92 tristate "SMC 91Cxx PCMCIA support" depends on PCMCIA select CRC32 - select NET_CORE select MII ---help--- Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA @@ -70,7 +68,6 @@ config EPIC100 tristate "SMC EtherPower II" depends on PCI select CRC32 - select NET_CORE select MII ---help--- This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC, @@ -81,7 +78,6 @@ config EPIC100 config SMC911X tristate "SMSC LAN911[5678] support" select CRC32 - select NET_CORE select MII depends on (ARM || SUPERH || MN10300) ---help--- @@ -97,9 +93,8 @@ config SMC911X config SMSC911X tristate "SMSC LAN911x/LAN921x families embedded ethernet support" - depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300) + depends on HAS_IOMEM select CRC32 - select NET_CORE select MII select PHYLIB ---help--- diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index 9dd842dbb85..345558fe736 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -2087,7 +2087,6 @@ static int smc911x_drv_probe(struct platform_device *pdev) ndev->base_addr = res->start; ret = smc911x_probe(ndev); if (ret != 0) { - platform_set_drvdata(pdev, NULL); iounmap(addr); release_both: free_netdev(ndev); @@ -2113,7 +2112,6 @@ static int smc911x_drv_remove(struct platform_device *pdev) struct resource *res; DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); - platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index dfbf978315d..cde13be7c7d 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2299,7 +2299,6 @@ static int smc_drv_probe(struct platform_device *pdev) return 0; out_iounmap: - platform_set_drvdata(pdev, NULL); iounmap(addr); out_release_attrib: smc_release_attrib(pdev, ndev); @@ -2319,8 +2318,6 @@ static int smc_drv_remove(struct platform_device *pdev) struct smc_local *lp = netdev_priv(ndev); struct resource *res; - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); free_irq(ndev->irq, ndev); diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 3663b9e04a3..a1419211585 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -2284,7 +2284,6 @@ static int smsc911x_drv_remove(struct platform_device *pdev) mdiobus_unregister(pdata->mii_bus); mdiobus_free(pdata->mii_bus); - platform_set_drvdata(pdev, NULL); unregister_netdev(dev); free_irq(dev->irq, dev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -2539,7 +2538,6 @@ out_disable_resources: out_enable_resources_fail: smsc911x_free_resources(pdev); out_request_resources_fail: - platform_set_drvdata(pdev, NULL); iounmap(pdata->ioaddr); free_netdev(dev); out_release_io_1: diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 43c1f322332..6e52c0f74cd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -1,7 +1,6 @@ config STMMAC_ETH tristate "STMicroelectronics 10/100/1000 Ethernet driver" depends on HAS_IOMEM && HAS_DMA - select NET_CORE select MII select PHYLIB select CRC32 diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 95176979b2d..7eb8babed2c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -38,16 +38,6 @@ #include "descs.h" #include "mmc.h" -#undef CHIP_DEBUG_PRINT -/* Turn-on extra printk debug for MAC core, dma and descriptors */ -/* #define CHIP_DEBUG_PRINT */ - -#ifdef CHIP_DEBUG_PRINT -#define CHIP_DBG(fmt, args...) printk(fmt, ## args) -#else -#define CHIP_DBG(fmt, args...) do { } while (0) -#endif - /* Synopsys Core versions */ #define DWMAC_CORE_3_40 0x34 #define DWMAC_CORE_3_50 0x35 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 7e05e8d0f1c..cdd926832e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -91,8 +91,8 @@ static void dwmac1000_set_filter(struct net_device *dev, int id) unsigned int value = 0; unsigned int perfect_addr_number; - CHIP_DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n", - __func__, netdev_mc_count(dev), netdev_uc_count(dev)); + pr_debug("%s: # mcasts %d, # unicast %d\n", __func__, + netdev_mc_count(dev), netdev_uc_count(dev)); if (dev->flags & IFF_PROMISC) value = GMAC_FRAME_FILTER_PR; @@ -152,7 +152,7 @@ static void dwmac1000_set_filter(struct net_device *dev, int id) #endif writel(value, ioaddr + GMAC_FRAME_FILTER); - CHIP_DBG(KERN_INFO "\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n", + pr_debug("\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GMAC_FRAME_FILTER), readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW)); } @@ -162,18 +162,18 @@ static void dwmac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, { unsigned int flow = 0; - CHIP_DBG(KERN_DEBUG "GMAC Flow-Control:\n"); + pr_debug("GMAC Flow-Control:\n"); if (fc & FLOW_RX) { - CHIP_DBG(KERN_DEBUG "\tReceive Flow-Control ON\n"); + pr_debug("\tReceive Flow-Control ON\n"); flow |= GMAC_FLOW_CTRL_RFE; } if (fc & FLOW_TX) { - CHIP_DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n"); + pr_debug("\tTransmit Flow-Control ON\n"); flow |= GMAC_FLOW_CTRL_TFE; } if (duplex) { - CHIP_DBG(KERN_DEBUG "\tduplex mode: PAUSE %d\n", pause_time); + pr_debug("\tduplex mode: PAUSE %d\n", pause_time); flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); } @@ -185,11 +185,11 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode) unsigned int pmt = 0; if (mode & WAKE_MAGIC) { - CHIP_DBG(KERN_DEBUG "GMAC: WOL Magic frame\n"); + pr_debug("GMAC: WOL Magic frame\n"); pmt |= power_down | magic_pkt_en; } if (mode & WAKE_UCAST) { - CHIP_DBG(KERN_DEBUG "GMAC: WOL on global unicast\n"); + pr_debug("GMAC: WOL on global unicast\n"); pmt |= global_unicast; } @@ -203,23 +203,13 @@ static int dwmac1000_irq_status(void __iomem *ioaddr, int ret = 0; /* Not used events (e.g. MMC interrupts) are not handled. */ - if ((intr_status & mmc_tx_irq)) { - CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n", - readl(ioaddr + GMAC_MMC_TX_INTR)); + if ((intr_status & mmc_tx_irq)) x->mmc_tx_irq_n++; - } - if (unlikely(intr_status & mmc_rx_irq)) { - CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n", - readl(ioaddr + GMAC_MMC_RX_INTR)); + if (unlikely(intr_status & mmc_rx_irq)) x->mmc_rx_irq_n++; - } - if (unlikely(intr_status & mmc_rx_csum_offload_irq)) { - CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n", - readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); + if (unlikely(intr_status & mmc_rx_csum_offload_irq)) x->mmc_rx_csum_offload_irq_n++; - } if (unlikely(intr_status & pmt_irq)) { - CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n"); /* clear the PMT bits 5 and 6 by reading the PMT status reg */ readl(ioaddr + GMAC_PMT); x->irq_receive_pmt_irq_n++; @@ -229,32 +219,22 @@ static int dwmac1000_irq_status(void __iomem *ioaddr, /* Clean LPI interrupt by reading the Reg 12 */ ret = readl(ioaddr + LPI_CTRL_STATUS); - if (ret & LPI_CTRL_STATUS_TLPIEN) { - CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n"); + if (ret & LPI_CTRL_STATUS_TLPIEN) x->irq_tx_path_in_lpi_mode_n++; - } - if (ret & LPI_CTRL_STATUS_TLPIEX) { - CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n"); + if (ret & LPI_CTRL_STATUS_TLPIEX) x->irq_tx_path_exit_lpi_mode_n++; - } - if (ret & LPI_CTRL_STATUS_RLPIEN) { - CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n"); + if (ret & LPI_CTRL_STATUS_RLPIEN) x->irq_rx_path_in_lpi_mode_n++; - } - if (ret & LPI_CTRL_STATUS_RLPIEX) { - CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n"); + if (ret & LPI_CTRL_STATUS_RLPIEX) x->irq_rx_path_exit_lpi_mode_n++; - } } if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) { - CHIP_DBG(KERN_INFO "GMAC PCS ANE IRQ\n"); readl(ioaddr + GMAC_AN_STATUS); x->irq_pcs_ane_n++; } if (intr_status & rgmii_irq) { u32 status = readl(ioaddr + GMAC_S_R_GMII); - CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n"); x->irq_rgmii_n++; /* Save and dump the link status. */ @@ -271,11 +251,12 @@ static int dwmac1000_irq_status(void __iomem *ioaddr, x->pcs_speed = SPEED_10; x->pcs_link = 1; - pr_debug("Link is Up - %d/%s\n", (int)x->pcs_speed, + pr_debug("%s: Link is Up - %d/%s\n", __func__, + (int)x->pcs_speed, x->pcs_duplex ? "Full" : "Half"); } else { x->pcs_link = 0; - pr_debug("Link is Down\n"); + pr_debug("%s: Link is Down\n", __func__); } } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 2c431b61605..0c2058a69fd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -116,7 +116,7 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, u32 csr6 = readl(ioaddr + DMA_CONTROL); if (txmode == SF_DMA_MODE) { - CHIP_DBG(KERN_DEBUG "GMAC: enable TX store and forward mode\n"); + pr_debug("GMAC: enable TX store and forward mode\n"); /* Transmit COE type 2 cannot be done in cut-through mode. */ csr6 |= DMA_CONTROL_TSF; /* Operating on second frame increase the performance @@ -124,8 +124,7 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, */ csr6 |= DMA_CONTROL_OSF; } else { - CHIP_DBG(KERN_DEBUG "GMAC: disabling TX SF (threshold %d)\n", - txmode); + pr_debug("GMAC: disabling TX SF (threshold %d)\n", txmode); csr6 &= ~DMA_CONTROL_TSF; csr6 &= DMA_CONTROL_TC_TX_MASK; /* Set the transmit threshold */ @@ -142,11 +141,10 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, } if (rxmode == SF_DMA_MODE) { - CHIP_DBG(KERN_DEBUG "GMAC: enable RX store and forward mode\n"); + pr_debug("GMAC: enable RX store and forward mode\n"); csr6 |= DMA_CONTROL_RSF; } else { - CHIP_DBG(KERN_DEBUG "GMAC: disable RX SF mode (threshold %d)\n", - rxmode); + pr_debug("GMAC: disable RX SF mode (threshold %d)\n", rxmode); csr6 &= ~DMA_CONTROL_RSF; csr6 &= DMA_CONTROL_TC_RX_MASK; if (rxmode <= 32) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 007bb2be3f1..5857d677dac 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -135,10 +135,6 @@ static void dwmac100_set_filter(struct net_device *dev, int id) } writel(value, ioaddr + MAC_CONTROL); - - CHIP_DBG(KERN_INFO "%s: Filter: 0x%08x Hash: HI 0x%08x, LO 0x%08x\n", - __func__, readl(ioaddr + MAC_CONTROL), - readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW)); } static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index 67551c15413..7d1dce9e7ff 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -90,14 +90,14 @@ static void dwmac100_dump_dma_regs(void __iomem *ioaddr) { int i; - CHIP_DBG(KERN_DEBUG "DWMAC 100 DMA CSR\n"); + pr_debug("DWMAC 100 DMA CSR\n"); for (i = 0; i < 9; i++) pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i, (DMA_BUS_MODE + i * 4), readl(ioaddr + DMA_BUS_MODE + i * 4)); - CHIP_DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n", - DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR)); - CHIP_DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n", + + pr_debug("\tCSR20 (0x%x): 0x%08x, CSR21 (0x%x): 0x%08x\n", + DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR), DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR)); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 491d7e93060..484e3cf9c41 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -24,13 +24,6 @@ #include "common.h" #include "dwmac_dma.h" -#undef DWMAC_DMA_DEBUG -#ifdef DWMAC_DMA_DEBUG -#define DWMAC_LIB_DBG(fmt, args...) printk(fmt, ## args) -#else -#define DWMAC_LIB_DBG(fmt, args...) do { } while (0) -#endif - #define GMAC_HI_REG_AE 0x80000000 /* CSR1 enables the transmit DMA to check for new descriptor */ @@ -85,24 +78,24 @@ static void show_tx_process_state(unsigned int status) switch (state) { case 0: - pr_info("- TX (Stopped): Reset or Stop command\n"); + pr_debug("- TX (Stopped): Reset or Stop command\n"); break; case 1: - pr_info("- TX (Running):Fetching the Tx desc\n"); + pr_debug("- TX (Running):Fetching the Tx desc\n"); break; case 2: - pr_info("- TX (Running): Waiting for end of tx\n"); + pr_debug("- TX (Running): Waiting for end of tx\n"); break; case 3: - pr_info("- TX (Running): Reading the data " + pr_debug("- TX (Running): Reading the data " "and queuing the data into the Tx buf\n"); break; case 6: - pr_info("- TX (Suspended): Tx Buff Underflow " + pr_debug("- TX (Suspended): Tx Buff Underflow " "or an unavailable Transmit descriptor\n"); break; case 7: - pr_info("- TX (Running): Closing Tx descriptor\n"); + pr_debug("- TX (Running): Closing Tx descriptor\n"); break; default: break; @@ -116,29 +109,29 @@ static void show_rx_process_state(unsigned int status) switch (state) { case 0: - pr_info("- RX (Stopped): Reset or Stop command\n"); + pr_debug("- RX (Stopped): Reset or Stop command\n"); break; case 1: - pr_info("- RX (Running): Fetching the Rx desc\n"); + pr_debug("- RX (Running): Fetching the Rx desc\n"); break; case 2: - pr_info("- RX (Running):Checking for end of pkt\n"); + pr_debug("- RX (Running):Checking for end of pkt\n"); break; case 3: - pr_info("- RX (Running): Waiting for Rx pkt\n"); + pr_debug("- RX (Running): Waiting for Rx pkt\n"); break; case 4: - pr_info("- RX (Suspended): Unavailable Rx buf\n"); + pr_debug("- RX (Suspended): Unavailable Rx buf\n"); break; case 5: - pr_info("- RX (Running): Closing Rx descriptor\n"); + pr_debug("- RX (Running): Closing Rx descriptor\n"); break; case 6: - pr_info("- RX(Running): Flushing the current frame" + pr_debug("- RX(Running): Flushing the current frame" " from the Rx buf\n"); break; case 7: - pr_info("- RX (Running): Queuing the Rx frame" + pr_debug("- RX (Running): Queuing the Rx frame" " from the Rx buf into memory\n"); break; default: @@ -154,51 +147,37 @@ int dwmac_dma_interrupt(void __iomem *ioaddr, /* read the status register (CSR5) */ u32 intr_status = readl(ioaddr + DMA_STATUS); - DWMAC_LIB_DBG(KERN_INFO "%s: [CSR5: 0x%08x]\n", __func__, intr_status); #ifdef DWMAC_DMA_DEBUG - /* It displays the DMA process states (CSR5 register) */ + /* Enable it to monitor DMA rx/tx status in case of critical problems */ + pr_debug("%s: [CSR5: 0x%08x]\n", __func__, intr_status); show_tx_process_state(intr_status); show_rx_process_state(intr_status); #endif /* ABNORMAL interrupts */ if (unlikely(intr_status & DMA_STATUS_AIS)) { - DWMAC_LIB_DBG(KERN_INFO "CSR5[15] DMA ABNORMAL IRQ: "); if (unlikely(intr_status & DMA_STATUS_UNF)) { - DWMAC_LIB_DBG(KERN_INFO "transmit underflow\n"); ret = tx_hard_error_bump_tc; x->tx_undeflow_irq++; } - if (unlikely(intr_status & DMA_STATUS_TJT)) { - DWMAC_LIB_DBG(KERN_INFO "transmit jabber\n"); + if (unlikely(intr_status & DMA_STATUS_TJT)) x->tx_jabber_irq++; - } - if (unlikely(intr_status & DMA_STATUS_OVF)) { - DWMAC_LIB_DBG(KERN_INFO "recv overflow\n"); + + if (unlikely(intr_status & DMA_STATUS_OVF)) x->rx_overflow_irq++; - } - if (unlikely(intr_status & DMA_STATUS_RU)) { - DWMAC_LIB_DBG(KERN_INFO "receive buffer unavailable\n"); + + if (unlikely(intr_status & DMA_STATUS_RU)) x->rx_buf_unav_irq++; - } - if (unlikely(intr_status & DMA_STATUS_RPS)) { - DWMAC_LIB_DBG(KERN_INFO "receive process stopped\n"); + if (unlikely(intr_status & DMA_STATUS_RPS)) x->rx_process_stopped_irq++; - } - if (unlikely(intr_status & DMA_STATUS_RWT)) { - DWMAC_LIB_DBG(KERN_INFO "receive watchdog\n"); + if (unlikely(intr_status & DMA_STATUS_RWT)) x->rx_watchdog_irq++; - } - if (unlikely(intr_status & DMA_STATUS_ETI)) { - DWMAC_LIB_DBG(KERN_INFO "transmit early interrupt\n"); + if (unlikely(intr_status & DMA_STATUS_ETI)) x->tx_early_irq++; - } if (unlikely(intr_status & DMA_STATUS_TPS)) { - DWMAC_LIB_DBG(KERN_INFO "transmit process stopped\n"); x->tx_process_stopped_irq++; ret = tx_hard_error; } if (unlikely(intr_status & DMA_STATUS_FBI)) { - DWMAC_LIB_DBG(KERN_INFO "fatal bus error\n"); x->fatal_bus_error_irq++; ret = tx_hard_error; } @@ -224,12 +203,11 @@ int dwmac_dma_interrupt(void __iomem *ioaddr, /* Optional hardware blocks, interrupts should be disabled */ if (unlikely(intr_status & (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI))) - pr_info("%s: unexpected status %08x\n", __func__, intr_status); + pr_warn("%s: unexpected status %08x\n", __func__, intr_status); /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */ writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS); - DWMAC_LIB_DBG(KERN_INFO "\n\n"); return ret; } diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 0fbc8fafa70..7e6628a9151 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -33,54 +33,40 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, struct net_device_stats *stats = (struct net_device_stats *)data; if (unlikely(p->des01.etx.error_summary)) { - CHIP_DBG(KERN_ERR "GMAC TX error... 0x%08x\n", p->des01.etx); - if (unlikely(p->des01.etx.jabber_timeout)) { - CHIP_DBG(KERN_ERR "\tjabber_timeout error\n"); + if (unlikely(p->des01.etx.jabber_timeout)) x->tx_jabber++; - } if (unlikely(p->des01.etx.frame_flushed)) { - CHIP_DBG(KERN_ERR "\tframe_flushed error\n"); x->tx_frame_flushed++; dwmac_dma_flush_tx_fifo(ioaddr); } if (unlikely(p->des01.etx.loss_carrier)) { - CHIP_DBG(KERN_ERR "\tloss_carrier error\n"); x->tx_losscarrier++; stats->tx_carrier_errors++; } if (unlikely(p->des01.etx.no_carrier)) { - CHIP_DBG(KERN_ERR "\tno_carrier error\n"); x->tx_carrier++; stats->tx_carrier_errors++; } - if (unlikely(p->des01.etx.late_collision)) { - CHIP_DBG(KERN_ERR "\tlate_collision error\n"); + if (unlikely(p->des01.etx.late_collision)) stats->collisions += p->des01.etx.collision_count; - } - if (unlikely(p->des01.etx.excessive_collisions)) { - CHIP_DBG(KERN_ERR "\texcessive_collisions\n"); + + if (unlikely(p->des01.etx.excessive_collisions)) stats->collisions += p->des01.etx.collision_count; - } - if (unlikely(p->des01.etx.excessive_deferral)) { - CHIP_DBG(KERN_INFO "\texcessive tx_deferral\n"); + + if (unlikely(p->des01.etx.excessive_deferral)) x->tx_deferred++; - } if (unlikely(p->des01.etx.underflow_error)) { - CHIP_DBG(KERN_ERR "\tunderflow error\n"); dwmac_dma_flush_tx_fifo(ioaddr); x->tx_underflow++; } - if (unlikely(p->des01.etx.ip_header_error)) { - CHIP_DBG(KERN_ERR "\tTX IP header csum error\n"); + if (unlikely(p->des01.etx.ip_header_error)) x->tx_ip_header_error++; - } if (unlikely(p->des01.etx.payload_error)) { - CHIP_DBG(KERN_ERR "\tAddr/Payload csum error\n"); x->tx_payload_error++; dwmac_dma_flush_tx_fifo(ioaddr); } @@ -88,15 +74,12 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, ret = -1; } - if (unlikely(p->des01.etx.deferred)) { - CHIP_DBG(KERN_INFO "GMAC TX status: tx deferred\n"); + if (unlikely(p->des01.etx.deferred)) x->tx_deferred++; - } + #ifdef STMMAC_VLAN_TAG_USED - if (p->des01.etx.vlan_frame) { - CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n"); + if (p->des01.etx.vlan_frame) x->tx_vlan++; - } #endif return ret; @@ -123,30 +106,20 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) * 0 1 1 | COE bypassed.. no IPv4/6 frame * 0 1 0 | Reserved. */ - if (status == 0x0) { - CHIP_DBG(KERN_INFO "RX Des0 status: IEEE 802.3 Type frame.\n"); + if (status == 0x0) ret = llc_snap; - } else if (status == 0x4) { - CHIP_DBG(KERN_INFO "RX Des0 status: IPv4/6 No CSUM errorS.\n"); + else if (status == 0x4) ret = good_frame; - } else if (status == 0x5) { - CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Payload Error.\n"); + else if (status == 0x5) ret = csum_none; - } else if (status == 0x6) { - CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Header Error.\n"); + else if (status == 0x6) ret = csum_none; - } else if (status == 0x7) { - CHIP_DBG(KERN_ERR - "RX Des0 status: IPv4/6 Header and Payload Error.\n"); + else if (status == 0x7) ret = csum_none; - } else if (status == 0x1) { - CHIP_DBG(KERN_ERR - "RX Des0 status: IPv4/6 unsupported IP PAYLOAD.\n"); + else if (status == 0x1) ret = discard_frame; - } else if (status == 0x3) { - CHIP_DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n"); + else if (status == 0x3) ret = discard_frame; - } return ret; } @@ -208,36 +181,26 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, struct net_device_stats *stats = (struct net_device_stats *)data; if (unlikely(p->des01.erx.error_summary)) { - CHIP_DBG(KERN_ERR "GMAC RX Error Summary 0x%08x\n", - p->des01.erx); if (unlikely(p->des01.erx.descriptor_error)) { - CHIP_DBG(KERN_ERR "\tdescriptor error\n"); x->rx_desc++; stats->rx_length_errors++; } - if (unlikely(p->des01.erx.overflow_error)) { - CHIP_DBG(KERN_ERR "\toverflow error\n"); + if (unlikely(p->des01.erx.overflow_error)) x->rx_gmac_overflow++; - } if (unlikely(p->des01.erx.ipc_csum_error)) - CHIP_DBG(KERN_ERR "\tIPC Csum Error/Giant frame\n"); + pr_err("\tIPC Csum Error/Giant frame\n"); if (unlikely(p->des01.erx.late_collision)) { - CHIP_DBG(KERN_ERR "\tlate_collision error\n"); - stats->collisions++; stats->collisions++; } - if (unlikely(p->des01.erx.receive_watchdog)) { - CHIP_DBG(KERN_ERR "\treceive_watchdog error\n"); + if (unlikely(p->des01.erx.receive_watchdog)) x->rx_watchdog++; - } - if (unlikely(p->des01.erx.error_gmii)) { - CHIP_DBG(KERN_ERR "\tReceive Error\n"); + + if (unlikely(p->des01.erx.error_gmii)) x->rx_mii++; - } + if (unlikely(p->des01.erx.crc_error)) { - CHIP_DBG(KERN_ERR "\tCRC error\n"); x->rx_crc++; stats->rx_crc_errors++; } @@ -251,30 +214,24 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error, p->des01.erx.frame_type, p->des01.erx.rx_mac_addr); - if (unlikely(p->des01.erx.dribbling)) { - CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n"); + if (unlikely(p->des01.erx.dribbling)) x->dribbling_bit++; - } + if (unlikely(p->des01.erx.sa_filter_fail)) { - CHIP_DBG(KERN_ERR "GMAC RX : Source Address filter fail\n"); x->sa_rx_filter_fail++; ret = discard_frame; } if (unlikely(p->des01.erx.da_filter_fail)) { - CHIP_DBG(KERN_ERR "GMAC RX : Dest Address filter fail\n"); x->da_rx_filter_fail++; ret = discard_frame; } if (unlikely(p->des01.erx.length_error)) { - CHIP_DBG(KERN_ERR "GMAC RX: length_error error\n"); x->rx_length++; ret = discard_frame; } #ifdef STMMAC_VLAN_TAG_USED - if (p->des01.erx.vlan_tag) { - CHIP_DBG(KERN_INFO "GMAC RX: VLAN frame tagged\n"); + if (p->des01.erx.vlan_tag) x->rx_vlan++; - } #endif return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 11775b99afc..35ad4f427ae 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -52,10 +52,8 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, ret = -1; } - if (p->des01.etx.vlan_frame) { - CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n"); + if (p->des01.etx.vlan_frame) x->tx_vlan++; - } if (unlikely(p->des01.tx.deferred)) x->tx_deferred++; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e9eab29db7b..f2ccb36e868 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -51,32 +51,6 @@ #include "stmmac_ptp.h" #include "stmmac.h" -#undef STMMAC_DEBUG -/*#define STMMAC_DEBUG*/ -#ifdef STMMAC_DEBUG -#define DBG(nlevel, klevel, fmt, args...) \ - ((void)(netif_msg_##nlevel(priv) && \ - printk(KERN_##klevel fmt, ## args))) -#else -#define DBG(nlevel, klevel, fmt, args...) do { } while (0) -#endif - -#undef STMMAC_RX_DEBUG -/*#define STMMAC_RX_DEBUG*/ -#ifdef STMMAC_RX_DEBUG -#define RX_DBG(fmt, args...) printk(fmt, ## args) -#else -#define RX_DBG(fmt, args...) do { } while (0) -#endif - -#undef STMMAC_XMIT_DEBUG -/*#define STMMAC_XMIT_DEBUG*/ -#ifdef STMMAC_XMIT_DEBUG -#define TX_DBG(fmt, args...) printk(fmt, ## args) -#else -#define TX_DBG(fmt, args...) do { } while (0) -#endif - #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) #define JUMBO_LEN 9000 @@ -214,19 +188,17 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv) } } -#if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG) static void print_pkt(unsigned char *buf, int len) { int j; - pr_info("len = %d byte, buf addr: 0x%p", len, buf); + pr_debug("len = %d byte, buf addr: 0x%p", len, buf); for (j = 0; j < len; j++) { if ((j % 16) == 0) - pr_info("\n %03x:", j); - pr_info(" %02x", buf[j]); + pr_debug("\n %03x:", j); + pr_debug(" %02x", buf[j]); } - pr_info("\n"); + pr_debug("\n"); } -#endif /* minimum number of free TX descriptors required to wake up TX process */ #define STMMAC_TX_THRESH(x) (x->dma_tx_size/4) @@ -696,9 +668,6 @@ static void stmmac_adjust_link(struct net_device *dev) if (phydev == NULL) return; - DBG(probe, DEBUG, "stmmac_adjust_link: called. address %d link %d\n", - phydev->addr, phydev->link); - spin_lock_irqsave(&priv->lock, flags); if (phydev->link) { @@ -773,8 +742,6 @@ static void stmmac_adjust_link(struct net_device *dev) priv->eee_enabled = stmmac_eee_init(priv); spin_unlock_irqrestore(&priv->lock, flags); - - DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n"); } /** @@ -789,13 +756,13 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) int interface = priv->plat->interface; if (priv->dma_cap.pcs) { - if ((interface & PHY_INTERFACE_MODE_RGMII) || - (interface & PHY_INTERFACE_MODE_RGMII_ID) || - (interface & PHY_INTERFACE_MODE_RGMII_RXID) || - (interface & PHY_INTERFACE_MODE_RGMII_TXID)) { + if ((interface == PHY_INTERFACE_MODE_RGMII) || + (interface == PHY_INTERFACE_MODE_RGMII_ID) || + (interface == PHY_INTERFACE_MODE_RGMII_RXID) || + (interface == PHY_INTERFACE_MODE_RGMII_TXID)) { pr_debug("STMMAC: PCS RGMII support enable\n"); priv->pcs = STMMAC_PCS_RGMII; - } else if (interface & PHY_INTERFACE_MODE_SGMII) { + } else if (interface == PHY_INTERFACE_MODE_SGMII) { pr_debug("STMMAC: PCS SGMII support enable\n"); priv->pcs = STMMAC_PCS_SGMII; } @@ -1015,8 +982,9 @@ static void init_dma_desc_rings(struct net_device *dev) if (bfsize < BUF_SIZE_16KiB) bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); - DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n", - txsize, rxsize, bfsize); + if (netif_msg_probe(priv)) + pr_debug("%s: txsize %d, rxsize %d, bfsize %d\n", __func__, + txsize, rxsize, bfsize); if (priv->extend_desc) { priv->dma_erx = dma_alloc_coherent(priv->device, rxsize * @@ -1052,12 +1020,13 @@ static void init_dma_desc_rings(struct net_device *dev) GFP_KERNEL); priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *), GFP_KERNEL); - if (netif_msg_drv(priv)) + if (netif_msg_probe(priv)) { pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__, (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy); - /* RX INITIALIZATION */ - DBG(probe, INFO, "stmmac: SKB addresses:\nskb\t\tskb data\tdma data\n"); + /* RX INITIALIZATION */ + pr_debug("\tSKB addresses:\nskb\t\tskb data\tdma data\n"); + } for (i = 0; i < rxsize; i++) { struct dma_desc *p; if (priv->extend_desc) @@ -1068,8 +1037,10 @@ static void init_dma_desc_rings(struct net_device *dev) if (stmmac_init_rx_buffers(priv, p, i)) break; - DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], - priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]); + if (netif_msg_probe(priv)) + pr_debug("[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], + priv->rx_skbuff[i]->data, + (unsigned int)priv->rx_skbuff_dma[i]); } priv->cur_rx = 0; priv->dirty_rx = (unsigned int)(i - rxsize); @@ -1244,8 +1215,9 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) stmmac_get_tx_hwtstamp(priv, entry, skb); } - TX_DBG("%s: curr %d, dirty %d\n", __func__, - priv->cur_tx, priv->dirty_tx); + if (netif_msg_tx_done(priv)) + pr_debug("%s: curr %d, dirty %d\n", __func__, + priv->cur_tx, priv->dirty_tx); if (likely(priv->tx_skbuff_dma[entry])) { dma_unmap_single(priv->device, @@ -1270,7 +1242,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) netif_tx_lock(priv->dev); if (netif_queue_stopped(priv->dev) && stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) { - TX_DBG("%s: restart transmit\n", __func__); + if (netif_msg_tx_done(priv)) + pr_debug("%s: restart transmit\n", __func__); netif_wake_queue(priv->dev); } netif_tx_unlock(priv->dev); @@ -1579,7 +1552,7 @@ static int stmmac_open(struct net_device *dev) if (ret) { pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret); - goto open_error; + goto phy_error; } } @@ -1593,7 +1566,7 @@ static int stmmac_open(struct net_device *dev) ret = stmmac_init_dma_engine(priv); if (ret < 0) { pr_err("%s: DMA initialization failed\n", __func__); - goto open_error; + goto init_error; } /* Copy the MAC addr into the HW */ @@ -1612,7 +1585,7 @@ static int stmmac_open(struct net_device *dev) if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n", __func__, dev->irq, ret); - goto open_error; + goto init_error; } /* Request the Wake IRQ in case of another line is used for WoL */ @@ -1622,7 +1595,7 @@ static int stmmac_open(struct net_device *dev) if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n", __func__, priv->wol_irq, ret); - goto open_error_wolirq; + goto wolirq_error; } } @@ -1633,7 +1606,7 @@ static int stmmac_open(struct net_device *dev) if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n", __func__, priv->lpi_irq, ret); - goto open_error_lpiirq; + goto lpiirq_error; } } @@ -1659,7 +1632,7 @@ static int stmmac_open(struct net_device *dev) pr_warn("%s: failed debugFS registration\n", __func__); #endif /* Start the ball rolling... */ - DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name); + pr_debug("%s: DMA RX/TX processes started...\n", dev->name); priv->hw->dma->start_tx(priv->ioaddr); priv->hw->dma->start_rx(priv->ioaddr); @@ -1691,17 +1664,17 @@ static int stmmac_open(struct net_device *dev) return 0; -open_error_lpiirq: +lpiirq_error: if (priv->wol_irq != dev->irq) free_irq(priv->wol_irq, dev); - -open_error_wolirq: +wolirq_error: free_irq(dev->irq, dev); -open_error: +init_error: + free_dma_desc_resources(priv); if (priv->phydev) phy_disconnect(priv->phydev); - +phy_error: clk_disable_unprepare(priv->stmmac_clk); return ret; @@ -1796,16 +1769,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = priv->cur_tx % txsize; -#ifdef STMMAC_XMIT_DEBUG - if ((skb->len > ETH_FRAME_LEN) || nfrags) - pr_debug("%s: [entry %d]: skb addr %p len: %d nopagedlen: %d\n" - "\tn_frags: %d - ip_summed: %d - %s gso\n" - "\ttx_count_frames %d\n", __func__, entry, - skb, skb->len, nopaged_len, nfrags, skb->ip_summed, - !skb_is_gso(skb) ? "isn't" : "is", - priv->tx_count_frames); -#endif - csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); if (priv->extend_desc) @@ -1815,12 +1778,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) first = desc; -#ifdef STMMAC_XMIT_DEBUG - if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN)) - pr_debug("\tskb len: %d, nopaged_len: %d,\n" - "\t\tn_frags: %d, ip_summed: %d\n", - skb->len, nopaged_len, nfrags, skb->ip_summed); -#endif priv->tx_skbuff[entry] = skb; /* To program the descriptors according to the size of the frame */ @@ -1856,7 +1813,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) else desc = priv->dma_tx + entry; - TX_DBG("\t[entry %d] segment len: %d\n", entry, len); desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE); priv->tx_skbuff_dma[entry] = desc->des2; @@ -1880,8 +1836,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->tx_coal_frames > priv->tx_count_frames) { priv->hw->desc->clear_tx_ic(desc); priv->xstats.tx_reset_ic_bit++; - TX_DBG("\t[entry %d]: tx_count_frames %d\n", entry, - priv->tx_count_frames); mod_timer(&priv->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer)); } else @@ -1893,22 +1847,22 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->cur_tx++; -#ifdef STMMAC_XMIT_DEBUG if (netif_msg_pktdata(priv)) { - pr_info("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d", + pr_debug("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d", __func__, (priv->cur_tx % txsize), (priv->dirty_tx % txsize), entry, first, nfrags); + if (priv->extend_desc) stmmac_display_ring((void *)priv->dma_etx, txsize, 1); else stmmac_display_ring((void *)priv->dma_tx, txsize, 0); - pr_info(">>> frame to be transmitted: "); + pr_debug(">>> frame to be transmitted: "); print_pkt(skb->data, skb->len); } -#endif if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { - TX_DBG("%s: stop transmitted packets\n", __func__); + if (netif_msg_hw(priv)) + pr_debug("%s: stop transmitted packets\n", __func__); netif_stop_queue(dev); } @@ -1968,7 +1922,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) priv->hw->ring->refill_desc3(priv, p); - RX_DBG(KERN_INFO "\trefill entry #%d\n", entry); + if (netif_msg_rx_status(priv)) + pr_debug("\trefill entry #%d\n", entry); } wmb(); priv->hw->desc->set_rx_owner(p); @@ -1991,15 +1946,13 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) unsigned int count = 0; int coe = priv->plat->rx_coe; -#ifdef STMMAC_RX_DEBUG - if (netif_msg_hw(priv)) { - pr_debug(">>> stmmac_rx: descriptor ring:\n"); + if (netif_msg_rx_status(priv)) { + pr_debug("%s: descriptor ring:\n", __func__); if (priv->extend_desc) stmmac_display_ring((void *)priv->dma_erx, rxsize, 1); else stmmac_display_ring((void *)priv->dma_rx, rxsize, 0); } -#endif while (count < limit) { int status; struct dma_desc *p; @@ -2053,15 +2006,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) */ if (unlikely(status != llc_snap)) frame_len -= ETH_FCS_LEN; -#ifdef STMMAC_RX_DEBUG - if (frame_len > ETH_FRAME_LEN) - pr_debug("\tRX frame size %d, COE status: %d\n", - frame_len, status); - if (netif_msg_hw(priv)) + if (netif_msg_rx_status(priv)) { pr_debug("\tdesc: %p [entry %d] buff=0x%x\n", p, entry, p->des2); -#endif + if (frame_len > ETH_FRAME_LEN) + pr_debug("\tframe size %d, COE: %d\n", + frame_len, status); + } skb = priv->rx_skbuff[entry]; if (unlikely(!skb)) { pr_err("%s: Inconsistent Rx descriptor chain\n", @@ -2078,12 +2030,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) dma_unmap_single(priv->device, priv->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); -#ifdef STMMAC_RX_DEBUG + if (netif_msg_pktdata(priv)) { - pr_info(" frame received (%dbytes)", frame_len); + pr_debug("frame received (%dbytes)", frame_len); print_pkt(skb->data, frame_len); } -#endif + skb->protocol = eth_type_trans(skb, priv->dev); if (unlikely(!coe)) @@ -2562,9 +2514,6 @@ static int stmmac_hw_init(struct stmmac_priv *priv) /* Get and dump the chip ID */ priv->synopsys_id = stmmac_get_synopsys_id(priv); - /* To use alternate (extended) or normal descriptor structures */ - stmmac_selec_desc_mode(priv); - /* To use the chained or ring mode */ if (chain_mode) { priv->hw->chain = &chain_mode_ops; @@ -2599,6 +2548,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) } else pr_info(" No HW DMA feature register supported"); + /* To use alternate (extended) or normal descriptor structures */ + stmmac_selec_desc_mode(priv); + ret = priv->hw->mac->rx_ipc(priv->ioaddr); if (!ret) { pr_warn(" RX IPC Checksum Offload not configured.\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index cc15039eaa4..fe7bc990386 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -27,6 +27,9 @@ #include <linux/mii.h> #include <linux/phy.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_gpio.h> + #include <asm/io.h> #include "stmmac.h" @@ -131,10 +134,46 @@ static int stmmac_mdio_reset(struct mii_bus *bus) struct net_device *ndev = bus->priv; struct stmmac_priv *priv = netdev_priv(ndev); unsigned int mii_address = priv->hw->mii.addr; + struct stmmac_mdio_bus_data *data = priv->plat->mdio_bus_data; + +#ifdef CONFIG_OF + if (priv->device->of_node) { + int reset_gpio, active_low; + + if (data->reset_gpio < 0) { + struct device_node *np = priv->device->of_node; + if (!np) + return 0; + + data->reset_gpio = of_get_named_gpio(np, + "snps,reset-gpio", 0); + if (data->reset_gpio < 0) + return 0; + + data->active_low = of_property_read_bool(np, + "snps,reset-active-low"); + of_property_read_u32_array(np, + "snps,reset-delays-us", data->delays, 3); + } + + reset_gpio = data->reset_gpio; + active_low = data->active_low; + + if (!gpio_request(reset_gpio, "mdio-reset")) { + gpio_direction_output(reset_gpio, active_low ? 1 : 0); + udelay(data->delays[0]); + gpio_set_value(reset_gpio, active_low ? 0 : 1); + udelay(data->delays[1]); + gpio_set_value(reset_gpio, active_low ? 1 : 0); + udelay(data->delays[2]); + gpio_free(reset_gpio); + } + } +#endif - if (priv->plat->mdio_bus_data->phy_reset) { + if (data->phy_reset) { pr_debug("stmmac_mdio_reset: calling phy_reset\n"); - priv->plat->mdio_bus_data->phy_reset(priv->plat->bsp_priv); + data->phy_reset(priv->plat->bsp_priv); } /* This is a workaround for problems with the STE101P PHY. @@ -172,6 +211,11 @@ int stmmac_mdio_register(struct net_device *ndev) else irqlist = priv->mii_irq; +#ifdef CONFIG_OF + if (priv->device->of_node) + mdio_bus_data->reset_gpio = -1; +#endif + new_bus->name = "stmmac"; new_bus->read = &stmmac_mdio_read; new_bus->write = &stmmac_mdio_write; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 1d3780f55ba..03de76c7a17 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -34,12 +34,20 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) { struct device_node *np = pdev->dev.of_node; + struct stmmac_dma_cfg *dma_cfg; if (!np) return -ENODEV; *mac = of_get_mac_address(np); plat->interface = of_get_phy_mode(np); + + plat->bus_id = of_alias_get_id(np, "ethernet"); + if (plat->bus_id < 0) + plat->bus_id = 0; + + of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr); + plat->mdio_bus_data = devm_kzalloc(&pdev->dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); @@ -56,6 +64,22 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, plat->pmt = 1; } + if (of_device_is_compatible(np, "snps,dwmac-3.610") || + of_device_is_compatible(np, "snps,dwmac-3.710")) { + plat->enh_desc = 1; + plat->bugged_jumbo = 1; + plat->force_sf_dma_mode = 1; + } + + dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL); + if (!dma_cfg) + return -ENOMEM; + + plat->dma_cfg = dma_cfg; + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); + dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); + dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); + return 0; } #else @@ -92,8 +116,10 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) if (IS_ERR(addr)) return PTR_ERR(addr); + plat_dat = pdev->dev.platform_data; if (pdev->dev.of_node) { - plat_dat = devm_kzalloc(&pdev->dev, + if (!plat_dat) + plat_dat = devm_kzalloc(&pdev->dev, sizeof(struct plat_stmmacenet_data), GFP_KERNEL); if (!plat_dat) { @@ -106,8 +132,6 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) pr_err("%s: main dt probe failed", __func__); return ret; } - } else { - plat_dat = pdev->dev.platform_data; } /* Custom initialisation (if needed)*/ @@ -171,8 +195,6 @@ static int stmmac_pltfr_remove(struct platform_device *pdev) if (priv->plat->exit) priv->plat->exit(pdev); - platform_set_drvdata(pdev, NULL); - return ret; } @@ -230,7 +252,9 @@ static const struct dev_pm_ops stmmac_pltfr_pm_ops; static const struct of_device_id stmmac_dt_ids[] = { { .compatible = "st,spear600-gmac"}, + { .compatible = "snps,dwmac-3.610"}, { .compatible = "snps,dwmac-3.70a"}, + { .compatible = "snps,dwmac-3.710"}, { .compatible = "snps,dwmac"}, { /* sentinel */ } }; diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 4c682a3d042..759441b29e5 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -808,44 +808,43 @@ static int cas_reset_mii_phy(struct cas *cp) return limit <= 0; } -static int cas_saturn_firmware_init(struct cas *cp) +static void cas_saturn_firmware_init(struct cas *cp) { const struct firmware *fw; const char fw_name[] = "sun/cassini.bin"; int err; if (PHY_NS_DP83065 != cp->phy_id) - return 0; + return; err = request_firmware(&fw, fw_name, &cp->pdev->dev); if (err) { pr_err("Failed to load firmware \"%s\"\n", fw_name); - return err; + return; } if (fw->size < 2) { pr_err("bogus length %zu in \"%s\"\n", fw->size, fw_name); - err = -EINVAL; goto out; } cp->fw_load_addr= fw->data[1] << 8 | fw->data[0]; cp->fw_size = fw->size - 2; cp->fw_data = vmalloc(cp->fw_size); - if (!cp->fw_data) { - err = -ENOMEM; + if (!cp->fw_data) goto out; - } memcpy(cp->fw_data, &fw->data[2], cp->fw_size); out: release_firmware(fw); - return err; } static void cas_saturn_firmware_load(struct cas *cp) { int i; + if (!cp->fw_data) + return; + cas_phy_powerdown(cp); /* expanded memory access mode */ @@ -5083,8 +5082,7 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (cas_check_invariants(cp)) goto err_out_iounmap; if (cp->cas_flags & CAS_FLAG_SATURN) - if (cas_saturn_firmware_init(cp)) - goto err_out_iounmap; + cas_saturn_firmware_init(cp); cp->init_block = (struct cas_init_block *) pci_alloc_consistent(pdev, sizeof(struct cas_init_block), diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 95cff98d8a3..fa322409bff 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -10108,7 +10108,7 @@ static int niu_of_probe(struct platform_device *op) goto err_out_iounmap; } - dev_set_drvdata(&op->dev, dev); + platform_set_drvdata(op, dev); niu_device_announce(np); @@ -10145,7 +10145,7 @@ err_out: static int niu_of_remove(struct platform_device *op) { - struct net_device *dev = dev_get_drvdata(&op->dev); + struct net_device *dev = platform_get_drvdata(op); if (dev) { struct niu *np = netdev_priv(dev); @@ -10175,7 +10175,6 @@ static int niu_of_remove(struct platform_device *op) niu_put_parent(np); free_netdev(dev); - dev_set_drvdata(&op->dev, NULL); } return 0; } diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c index 054975939a1..0d43fa9ff98 100644 --- a/drivers/net/ethernet/sun/sunbmac.c +++ b/drivers/net/ethernet/sun/sunbmac.c @@ -995,7 +995,6 @@ static void bigmac_set_multicast(struct net_device *dev) struct bigmac *bp = netdev_priv(dev); void __iomem *bregs = bp->bregs; struct netdev_hw_addr *ha; - int i; u32 tmp, crc; /* Disable the receiver. The bit self-clears when @@ -1017,10 +1016,7 @@ static void bigmac_set_multicast(struct net_device *dev) tmp |= BIGMAC_RXCFG_PMISC; sbus_writel(tmp, bregs + BMAC_RXCFG); } else { - u16 hash_table[4]; - - for (i = 0; i < 4; i++) - hash_table[i] = 0; + u16 hash_table[4] = { 0 }; netdev_for_each_mc_addr(ha, dev) { crc = ether_crc_le(6, ha->addr); diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 5f3f9d52757..e62df2b8130 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -3028,15 +3028,4 @@ static struct pci_driver gem_driver = { #endif /* CONFIG_PM */ }; -static int __init gem_init(void) -{ - return pci_register_driver(&gem_driver); -} - -static void __exit gem_cleanup(void) -{ - pci_unregister_driver(&gem_driver); -} - -module_init(gem_init); -module_exit(gem_cleanup); +module_pci_driver(gem_driver); diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index 436fa9d5a07..171f5b0809c 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -2506,7 +2506,7 @@ static struct quattro *quattro_sbus_find(struct platform_device *child) struct quattro *qp; op = to_platform_device(parent); - qp = dev_get_drvdata(&op->dev); + qp = platform_get_drvdata(op); if (qp) return qp; @@ -2521,7 +2521,7 @@ static struct quattro *quattro_sbus_find(struct platform_device *child) qp->next = qfe_sbus_list; qfe_sbus_list = qp; - dev_set_drvdata(&op->dev, qp); + platform_set_drvdata(op, qp); } return qp; } diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index 8182591bc18..b072f4dba03 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -767,7 +767,7 @@ static struct sunqec *get_qec(struct platform_device *child) struct platform_device *op = to_platform_device(child->dev.parent); struct sunqec *qecp; - qecp = dev_get_drvdata(&op->dev); + qecp = platform_get_drvdata(op); if (!qecp) { qecp = kzalloc(sizeof(struct sunqec), GFP_KERNEL); if (qecp) { @@ -801,7 +801,7 @@ static struct sunqec *get_qec(struct platform_device *child) goto fail; } - dev_set_drvdata(&op->dev, qecp); + platform_set_drvdata(op, qecp); qecp->next_module = root_qec_dev; root_qec_dev = qecp; @@ -902,7 +902,7 @@ static int qec_ether_init(struct platform_device *op) if (res) goto fail; - dev_set_drvdata(&op->dev, qe); + platform_set_drvdata(op, qe); printk(KERN_INFO "%s: qe channel[%d] %pM\n", dev->name, qe->channel, dev->dev_addr); @@ -934,7 +934,7 @@ static int qec_sbus_probe(struct platform_device *op) static int qec_sbus_remove(struct platform_device *op) { - struct sunqe *qp = dev_get_drvdata(&op->dev); + struct sunqe *qp = platform_get_drvdata(op); struct net_device *net_dev = qp->dev; unregister_netdev(net_dev); @@ -948,8 +948,6 @@ static int qec_sbus_remove(struct platform_device *op) free_netdev(net_dev); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 1df0ff3839e..3df56840a3b 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1239,6 +1239,8 @@ static int vnet_port_remove(struct vio_dev *vdev) dev_set_drvdata(&vdev->dev, NULL); kfree(port); + + unregister_netdev(vp->dev); } return 0; } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index d1a769f35f9..05a1674e204 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -35,6 +35,7 @@ #include <linux/if_vlan.h> #include <linux/platform_data/cpsw.h> +#include <linux/pinctrl/consumer.h> #include "cpsw_ale.h" #include "cpts.h" @@ -1554,6 +1555,8 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, if (mac_addr) memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); + slave_data->phy_if = of_get_phy_mode(slave_node); + if (data->dual_emac) { if (of_property_read_u32(slave_node, "dual_emac_res_vlan", &prop)) { @@ -1689,6 +1692,9 @@ static int cpsw_probe(struct platform_device *pdev) */ pm_runtime_enable(&pdev->dev); + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + if (cpsw_probe_dt(&priv->data, pdev)) { pr_err("cpsw: platform data missing\n"); ret = -ENODEV; @@ -1698,10 +1704,10 @@ static int cpsw_probe(struct platform_device *pdev) if (is_valid_ether_addr(data->slave_data[0].mac_addr)) { memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN); - pr_info("Detected MACID = %pM", priv->mac_addr); + pr_info("Detected MACID = %pM\n", priv->mac_addr); } else { eth_random_addr(priv->mac_addr); - pr_info("Random MACID = %pM", priv->mac_addr); + pr_info("Random MACID = %pM\n", priv->mac_addr); } memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); @@ -1940,7 +1946,6 @@ static int cpsw_remove(struct platform_device *pdev) struct cpsw_priv *priv = netdev_priv(ndev); int i; - platform_set_drvdata(pdev, NULL); if (priv->data.dual_emac) unregister_netdev(cpsw_get_slave_ndev(priv, 1)); unregister_netdev(ndev); @@ -1981,6 +1986,9 @@ static int cpsw_suspend(struct device *dev) soft_reset("sliver 1", &priv->slaves[1].sliver->soft_reset); pm_runtime_put_sync(&pdev->dev); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(&pdev->dev); + return 0; } @@ -1990,6 +1998,10 @@ static int cpsw_resume(struct device *dev) struct net_device *ndev = platform_get_drvdata(pdev); pm_runtime_get_sync(&pdev->dev); + + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + if (netif_running(ndev)) cpsw_ndo_open(ndev); return 0; diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 053c84fd085..031ebc81b50 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -64,6 +64,7 @@ #define CPDMA_DESC_TO_PORT_EN BIT(20) #define CPDMA_TO_PORT_SHIFT 16 #define CPDMA_DESC_PORT_MASK (BIT(18) | BIT(17) | BIT(16)) +#define CPDMA_DESC_CRC_LEN 4 #define CPDMA_TEARDOWN_VALUE 0xfffffffc @@ -805,6 +806,10 @@ static int __cpdma_chan_process(struct cpdma_chan *chan) status = -EBUSY; goto unlock_ret; } + + if (status & CPDMA_DESC_PASS_CRC) + outlen -= CPDMA_DESC_CRC_LEN; + status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE | CPDMA_DESC_PORT_MASK); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 860e15ddfbc..07b176bcf92 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1532,7 +1532,7 @@ static int emac_dev_open(struct net_device *ndev) struct device *emac_dev = &ndev->dev; u32 cnt; struct resource *res; - int q, m, ret; + int ret; int i = 0; int k = 0; struct emac_priv *priv = netdev_priv(ndev); @@ -1567,8 +1567,9 @@ static int emac_dev_open(struct net_device *ndev) while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) { for (i = res->start; i <= res->end; i++) { - if (request_irq(i, emac_irq, IRQF_DISABLED, - ndev->name, ndev)) + if (devm_request_irq(&priv->pdev->dev, i, emac_irq, + IRQF_DISABLED, + ndev->name, ndev)) goto rollback; } k++; @@ -1641,15 +1642,7 @@ static int emac_dev_open(struct net_device *ndev) rollback: - dev_err(emac_dev, "DaVinci EMAC: request_irq() failed"); - - for (q = k; k >= 0; k--) { - for (m = i; m >= res->start; m--) - free_irq(m, ndev); - res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k-1); - m = res->end; - } - + dev_err(emac_dev, "DaVinci EMAC: devm_request_irq() failed"); ret = -EBUSY; err: pm_runtime_put(&priv->pdev->dev); @@ -1667,9 +1660,6 @@ err: */ static int emac_dev_stop(struct net_device *ndev) { - struct resource *res; - int i = 0; - int irq_num; struct emac_priv *priv = netdev_priv(ndev); struct device *emac_dev = &ndev->dev; @@ -1685,13 +1675,6 @@ static int emac_dev_stop(struct net_device *ndev) if (priv->phydev) phy_disconnect(priv->phydev); - /* Free IRQ */ - while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) { - for (irq_num = res->start; irq_num <= res->end; irq_num++) - free_irq(irq_num, priv->ndev); - i++; - } - if (netif_msg_drv(priv)) dev_notice(emac_dev, "DaVinci EMAC: %s stopped\n", ndev->name); @@ -1771,29 +1754,22 @@ static const struct net_device_ops emac_netdev_ops = { #endif }; -#ifdef CONFIG_OF -static struct emac_platform_data - *davinci_emac_of_get_pdata(struct platform_device *pdev, - struct emac_priv *priv) +static struct emac_platform_data * +davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) { struct device_node *np; struct emac_platform_data *pdata = NULL; const u8 *mac_addr; - u32 data; - int ret; - pdata = pdev->dev.platform_data; - if (!pdata) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - goto nodata; - } + if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) + return pdev->dev.platform_data; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; np = pdev->dev.of_node; - if (!np) - goto nodata; - else - pdata->version = EMAC_VERSION_2; + pdata->version = EMAC_VERSION_2; if (!is_valid_ether_addr(pdata->mac_addr)) { mac_addr = of_get_mac_address(np); @@ -1801,47 +1777,31 @@ static struct emac_platform_data memcpy(pdata->mac_addr, mac_addr, ETH_ALEN); } - ret = of_property_read_u32(np, "ti,davinci-ctrl-reg-offset", &data); - if (!ret) - pdata->ctrl_reg_offset = data; + of_property_read_u32(np, "ti,davinci-ctrl-reg-offset", + &pdata->ctrl_reg_offset); - ret = of_property_read_u32(np, "ti,davinci-ctrl-mod-reg-offset", - &data); - if (!ret) - pdata->ctrl_mod_reg_offset = data; + of_property_read_u32(np, "ti,davinci-ctrl-mod-reg-offset", + &pdata->ctrl_mod_reg_offset); - ret = of_property_read_u32(np, "ti,davinci-ctrl-ram-offset", &data); - if (!ret) - pdata->ctrl_ram_offset = data; + of_property_read_u32(np, "ti,davinci-ctrl-ram-offset", + &pdata->ctrl_ram_offset); - ret = of_property_read_u32(np, "ti,davinci-ctrl-ram-size", &data); - if (!ret) - pdata->ctrl_ram_size = data; + of_property_read_u32(np, "ti,davinci-ctrl-ram-size", + &pdata->ctrl_ram_size); - ret = of_property_read_u32(np, "ti,davinci-rmii-en", &data); - if (!ret) - pdata->rmii_en = data; + of_property_read_u8(np, "ti,davinci-rmii-en", &pdata->rmii_en); - ret = of_property_read_u32(np, "ti,davinci-no-bd-ram", &data); - if (!ret) - pdata->no_bd_ram = data; + pdata->no_bd_ram = of_property_read_bool(np, "ti,davinci-no-bd-ram"); priv->phy_node = of_parse_phandle(np, "phy-handle", 0); if (!priv->phy_node) pdata->phy_id = ""; pdev->dev.platform_data = pdata; -nodata: + return pdata; } -#else -static struct emac_platform_data - *davinci_emac_of_get_pdata(struct platform_device *pdev, - struct emac_priv *priv) -{ - return pdev->dev.platform_data; -} -#endif + /** * davinci_emac_probe - EMAC device probe * @pdev: The DaVinci EMAC device that we are removing @@ -1856,7 +1816,7 @@ static int davinci_emac_probe(struct platform_device *pdev) struct resource *res; struct net_device *ndev; struct emac_priv *priv; - unsigned long size, hw_ram_addr; + unsigned long hw_ram_addr; struct emac_platform_data *pdata; struct device *emac_dev; struct cpdma_params dma_params; @@ -1907,25 +1867,10 @@ static int davinci_emac_probe(struct platform_device *pdev) emac_dev = &ndev->dev; /* Get EMAC platform data */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev,"error getting res\n"); - rc = -ENOENT; - goto no_pdata; - } - priv->emac_base_phys = res->start + pdata->ctrl_reg_offset; - size = resource_size(res); - if (!devm_request_mem_region(&pdev->dev, res->start, - size, ndev->name)) { - dev_err(&pdev->dev, "failed request_mem_region() for regs\n"); - rc = -ENXIO; - goto no_pdata; - } - - priv->remap_addr = devm_ioremap(&pdev->dev, res->start, size); - if (!priv->remap_addr) { - dev_err(&pdev->dev, "unable to map IO\n"); - rc = -ENOMEM; + priv->remap_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->remap_addr)) { + rc = PTR_ERR(priv->remap_addr); goto no_pdata; } priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset; @@ -2037,8 +1982,6 @@ static int davinci_emac_remove(struct platform_device *pdev) dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n"); - platform_set_drvdata(pdev, NULL); - if (priv->txchan) cpdma_chan_destroy(priv->txchan); if (priv->rxchan) @@ -2078,11 +2021,13 @@ static const struct dev_pm_ops davinci_emac_pm_ops = { .resume = davinci_emac_resume, }; +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id davinci_emac_of_match[] = { {.compatible = "ti,davinci-dm6467-emac", }, {}, }; MODULE_DEVICE_TABLE(of, davinci_emac_of_match); +#endif /* davinci_emac_driver: EMAC platform driver structure */ static struct platform_driver davinci_emac_driver = { diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index c47f0dbcebb..16ddfc34806 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -38,6 +38,7 @@ #include <linux/davinci_emac.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> /* * This timeout definition is a worst-case ultra defensive measure against @@ -291,6 +292,7 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id, return 0; } +#if IS_ENABLED(CONFIG_OF) static int davinci_mdio_probe_dt(struct mdio_platform_data *data, struct platform_device *pdev) { @@ -308,7 +310,7 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data, return 0; } - +#endif static int davinci_mdio_probe(struct platform_device *pdev) { @@ -347,6 +349,9 @@ static int davinci_mdio_probe(struct platform_device *pdev) data->bus->parent = dev; data->bus->priv = data; + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); data->clk = clk_get(&pdev->dev, "fck"); @@ -453,6 +458,9 @@ static int davinci_mdio_suspend(struct device *dev) spin_unlock(&data->lock); pm_runtime_put_sync(data->dev); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -460,6 +468,9 @@ static int davinci_mdio_resume(struct device *dev) { struct davinci_mdio_data *data = dev_get_drvdata(dev); + /* Select default pin state */ + pinctrl_pm_select_default_state(dev); + pm_runtime_get_sync(data->dev); spin_lock(&data->lock); @@ -477,11 +488,13 @@ static const struct dev_pm_ops davinci_mdio_pm_ops = { .resume_early = davinci_mdio_resume, }; +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id davinci_mdio_of_mtable[] = { { .compatible = "ti,davinci_mdio", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, davinci_mdio_of_mtable); +#endif static struct platform_driver davinci_mdio_driver = { .driver = { diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 60c400f6d01..591437e59b9 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -372,7 +372,7 @@ static int tlan_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_wake(pdev, 0, 0); + pci_enable_wake(pdev, PCI_D0, 0); netif_device_attach(dev); if (netif_running(dev)) @@ -533,7 +533,6 @@ static int tlan_probe1(struct pci_dev *pdev, long ioaddr, int irq, int rev, /* This is a hack. We need to know which board structure * is suited for this adapter */ device_id = inw(ioaddr + EISA_ID2); - priv->is_eisa = 1; if (device_id == 0x20F1) { priv->adapter = &board_info[13]; /* NetFlex-3/E */ priv->adapter_rev = 23; /* TLAN 2.3 */ diff --git a/drivers/net/ethernet/ti/tlan.h b/drivers/net/ethernet/ti/tlan.h index 5fc98a8e488..2eb33a25078 100644 --- a/drivers/net/ethernet/ti/tlan.h +++ b/drivers/net/ethernet/ti/tlan.h @@ -207,7 +207,6 @@ struct tlan_priv { u8 tlan_full_duplex; spinlock_t lock; u8 link; - u8 is_eisa; struct work_struct tlan_tqueue; u8 neg_be_verbose; }; diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index fe256094db3..a971b9cca56 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -2209,18 +2209,6 @@ MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps"); module_param_named(duplex, options.duplex, int, 0); MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full"); -static int __init tc35815_init_module(void) -{ - return pci_register_driver(&tc35815_pci_driver); -} - -static void __exit tc35815_cleanup_module(void) -{ - pci_unregister_driver(&tc35815_pci_driver); -} - -module_init(tc35815_init_module); -module_exit(tc35815_cleanup_module); - +module_pci_driver(tc35815_pci_driver); MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index 3c69a046083..01bdc6ca075 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -1682,7 +1682,6 @@ static int tsi108_ether_remove(struct platform_device *pdev) unregister_netdev(dev); tsi108_stop_ethernet(dev); - platform_set_drvdata(pdev, NULL); iounmap(priv->regs); iounmap(priv->phyregs); free_netdev(dev); diff --git a/drivers/net/ethernet/via/Kconfig b/drivers/net/ethernet/via/Kconfig index 68a9ba66feb..8a049a2b447 100644 --- a/drivers/net/ethernet/via/Kconfig +++ b/drivers/net/ethernet/via/Kconfig @@ -5,7 +5,6 @@ config NET_VENDOR_VIA bool "VIA devices" default y - depends on PCI ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -22,7 +21,6 @@ config VIA_RHINE tristate "VIA Rhine support" depends on PCI select CRC32 - select NET_CORE select MII ---help--- If you have a VIA "Rhine" based network card (Rhine-I (VT86C100A), @@ -45,10 +43,9 @@ config VIA_RHINE_MMIO config VIA_VELOCITY tristate "VIA Velocity support" - depends on PCI + depends on (PCI || USE_OF) select CRC32 select CRC_CCITT - select NET_CORE select MII ---help--- If you have a VIA "Velocity" based network card say Y here. diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index fb6248956ee..1d6dc41f755 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -46,6 +46,7 @@ #include <linux/types.h> #include <linux/bitops.h> #include <linux/init.h> +#include <linux/dma-mapping.h> #include <linux/mm.h> #include <linux/errno.h> #include <linux/ioport.h> @@ -64,7 +65,11 @@ #include <linux/if.h> #include <linux/uaccess.h> #include <linux/proc_fs.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/inetdevice.h> +#include <linux/platform_device.h> #include <linux/reboot.h> #include <linux/ethtool.h> #include <linux/mii.h> @@ -79,10 +84,24 @@ #include "via-velocity.h" +enum velocity_bus_type { + BUS_PCI, + BUS_PLATFORM, +}; static int velocity_nics; static int msglevel = MSG_LEVEL_INFO; +static void velocity_set_power_state(struct velocity_info *vptr, char state) +{ + void *addr = vptr->mac_regs; + + if (vptr->pdev) + pci_set_power_state(vptr->pdev, state); + else + writeb(state, addr + 0x154); +} + /** * mac_get_cam_mask - Read a CAM mask * @regs: register block for this velocity @@ -361,12 +380,23 @@ static struct velocity_info_tbl chip_info_table[] = { * Describe the PCI device identifiers that we support in this * device driver. Used for hotplug autoloading. */ -static DEFINE_PCI_DEVICE_TABLE(velocity_id_table) = { + +static DEFINE_PCI_DEVICE_TABLE(velocity_pci_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, { } }; -MODULE_DEVICE_TABLE(pci, velocity_id_table); +MODULE_DEVICE_TABLE(pci, velocity_pci_id_table); + +/** + * Describe the OF device identifiers that we support in this + * device driver. Used for devicetree nodes. + */ +static struct of_device_id velocity_of_ids[] = { + { .compatible = "via,velocity-vt6110", .data = &chip_info_table[0] }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, velocity_of_ids); /** * get_chip_name - identifier to name @@ -385,29 +415,6 @@ static const char *get_chip_name(enum chip_type chip_id) } /** - * velocity_remove1 - device unplug - * @pdev: PCI device being removed - * - * Device unload callback. Called on an unplug or on module - * unload for each active device that is present. Disconnects - * the device from the network layer and frees all the resources - */ -static void velocity_remove1(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = netdev_priv(dev); - - unregister_netdev(dev); - iounmap(vptr->mac_regs); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - free_netdev(dev); - - velocity_nics--; -} - -/** * velocity_set_int_opt - parser for integer options * @opt: pointer to option value * @val: value the user requested (or -1 for default) @@ -998,9 +1005,9 @@ static void velocity_print_link_status(struct velocity_info *vptr) { if (vptr->mii_status & VELOCITY_LINK_FAIL) { - VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->netdev->name); } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) { - VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->netdev->name); if (vptr->mii_status & VELOCITY_SPEED_1000) VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps"); @@ -1014,7 +1021,7 @@ static void velocity_print_link_status(struct velocity_info *vptr) else VELOCITY_PRT(MSG_LEVEL_INFO, " half duplex\n"); } else { - VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->netdev->name); switch (vptr->options.spd_dpx) { case SPD_DPX_1000_FULL: VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps full duplex\n"); @@ -1180,6 +1187,17 @@ static void mii_init(struct velocity_info *vptr, u32 mii_status) u16 BMCR; switch (PHYID_GET_PHY_ID(vptr->phy_id)) { + case PHYID_ICPLUS_IP101A: + MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), + MII_ADVERTISE, vptr->mac_regs); + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, + vptr->mac_regs); + else + MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, + vptr->mac_regs); + MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs); + break; case PHYID_CICADA_CS8201: /* * Reset to hardware default @@ -1311,6 +1329,7 @@ static void velocity_init_registers(struct velocity_info *vptr, enum velocity_init_type type) { struct mac_regs __iomem *regs = vptr->mac_regs; + struct net_device *netdev = vptr->netdev; int i, mii_status; mac_wol_reset(regs); @@ -1319,7 +1338,7 @@ static void velocity_init_registers(struct velocity_info *vptr, case VELOCITY_INIT_RESET: case VELOCITY_INIT_WOL: - netif_stop_queue(vptr->dev); + netif_stop_queue(netdev); /* * Reset RX to prevent RX pointer not on the 4X location @@ -1332,7 +1351,7 @@ static void velocity_init_registers(struct velocity_info *vptr, if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { velocity_print_link_status(vptr); if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->dev); + netif_wake_queue(netdev); } enable_flow_control_ability(vptr); @@ -1352,9 +1371,11 @@ static void velocity_init_registers(struct velocity_info *vptr, velocity_soft_reset(vptr); mdelay(5); - mac_eeprom_reload(regs); - for (i = 0; i < 6; i++) - writeb(vptr->dev->dev_addr[i], &(regs->PAR[i])); + if (!vptr->no_eeprom) { + mac_eeprom_reload(regs); + for (i = 0; i < 6; i++) + writeb(netdev->dev_addr[i], regs->PAR + i); + } /* * clear Pre_ACPI bit. @@ -1377,7 +1398,7 @@ static void velocity_init_registers(struct velocity_info *vptr, /* * Set packet filter: Receive directed and broadcast address */ - velocity_set_multi(vptr->dev); + velocity_set_multi(netdev); /* * Enable MII auto-polling @@ -1404,14 +1425,14 @@ static void velocity_init_registers(struct velocity_info *vptr, writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); mii_status = velocity_get_opt_media_mode(vptr); - netif_stop_queue(vptr->dev); + netif_stop_queue(netdev); mii_init(vptr, mii_status); if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { velocity_print_link_status(vptr); if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->dev); + netif_wake_queue(netdev); } enable_flow_control_ability(vptr); @@ -1459,7 +1480,6 @@ static int velocity_init_dma_rings(struct velocity_info *vptr) struct velocity_opt *opt = &vptr->options; const unsigned int rx_ring_size = opt->numrx * sizeof(struct rx_desc); const unsigned int tx_ring_size = opt->numtx * sizeof(struct tx_desc); - struct pci_dev *pdev = vptr->pdev; dma_addr_t pool_dma; void *pool; unsigned int i; @@ -1467,14 +1487,14 @@ static int velocity_init_dma_rings(struct velocity_info *vptr) /* * Allocate all RD/TD rings a single pool. * - * pci_alloc_consistent() fulfills the requirement for 64 bytes + * dma_alloc_coherent() fulfills the requirement for 64 bytes * alignment */ - pool = pci_alloc_consistent(pdev, tx_ring_size * vptr->tx.numq + - rx_ring_size, &pool_dma); + pool = dma_alloc_coherent(vptr->dev, tx_ring_size * vptr->tx.numq + + rx_ring_size, &pool_dma, GFP_ATOMIC); if (!pool) { - dev_err(&pdev->dev, "%s : DMA memory allocation failed.\n", - vptr->dev->name); + dev_err(vptr->dev, "%s : DMA memory allocation failed.\n", + vptr->netdev->name); return -ENOMEM; } @@ -1514,7 +1534,7 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) struct rx_desc *rd = &(vptr->rx.ring[idx]); struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); - rd_info->skb = netdev_alloc_skb(vptr->dev, vptr->rx.buf_sz + 64); + rd_info->skb = netdev_alloc_skb(vptr->netdev, vptr->rx.buf_sz + 64); if (rd_info->skb == NULL) return -ENOMEM; @@ -1524,8 +1544,8 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) */ skb_reserve(rd_info->skb, 64 - ((unsigned long) rd_info->skb->data & 63)); - rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data, - vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); + rd_info->skb_dma = dma_map_single(vptr->dev, rd_info->skb->data, + vptr->rx.buf_sz, DMA_FROM_DEVICE); /* * Fill in the descriptor to match @@ -1588,8 +1608,8 @@ static void velocity_free_rd_ring(struct velocity_info *vptr) if (!rd_info->skb) continue; - pci_unmap_single(vptr->pdev, rd_info->skb_dma, vptr->rx.buf_sz, - PCI_DMA_FROMDEVICE); + dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, + DMA_FROM_DEVICE); rd_info->skb_dma = 0; dev_kfree_skb(rd_info->skb); @@ -1620,7 +1640,7 @@ static int velocity_init_rd_ring(struct velocity_info *vptr) if (velocity_rx_refill(vptr) != vptr->options.numrx) { VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR - "%s: failed to allocate RX buffer.\n", vptr->dev->name); + "%s: failed to allocate RX buffer.\n", vptr->netdev->name); velocity_free_rd_ring(vptr); goto out; } @@ -1670,7 +1690,7 @@ static void velocity_free_dma_rings(struct velocity_info *vptr) const int size = vptr->options.numrx * sizeof(struct rx_desc) + vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq; - pci_free_consistent(vptr->pdev, size, vptr->rx.ring, vptr->rx.pool_dma); + dma_free_coherent(vptr->dev, size, vptr->rx.ring, vptr->rx.pool_dma); } static int velocity_init_rings(struct velocity_info *vptr, int mtu) @@ -1727,8 +1747,8 @@ static void velocity_free_tx_buf(struct velocity_info *vptr, pktlen = max_t(size_t, pktlen, td->td_buf[i].size & ~TD_QUEUE); - pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], - le16_to_cpu(pktlen), PCI_DMA_TODEVICE); + dma_unmap_single(vptr->dev, tdinfo->skb_dma[i], + le16_to_cpu(pktlen), DMA_TO_DEVICE); } } dev_kfree_skb_irq(skb); @@ -1750,8 +1770,8 @@ static void velocity_free_td_ring_entry(struct velocity_info *vptr, if (td_info->skb) { for (i = 0; i < td_info->nskb_dma; i++) { if (td_info->skb_dma[i]) { - pci_unmap_single(vptr->pdev, td_info->skb_dma[i], - td_info->skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(vptr->dev, td_info->skb_dma[i], + td_info->skb->len, DMA_TO_DEVICE); td_info->skb_dma[i] = 0; } } @@ -1809,7 +1829,7 @@ static void velocity_error(struct velocity_info *vptr, int status) printk(KERN_ERR "TD structure error TDindex=%hx\n", readw(®s->TDIdx[0])); BYTE_REG_BITS_ON(TXESR_TDSTR, ®s->TXESR); writew(TRDCSR_RUN, ®s->TDCSRClr); - netif_stop_queue(vptr->dev); + netif_stop_queue(vptr->netdev); /* FIXME: port over the pci_device_failed code and use it here */ @@ -1850,10 +1870,10 @@ static void velocity_error(struct velocity_info *vptr, int status) if (linked) { vptr->mii_status &= ~VELOCITY_LINK_FAIL; - netif_carrier_on(vptr->dev); + netif_carrier_on(vptr->netdev); } else { vptr->mii_status |= VELOCITY_LINK_FAIL; - netif_carrier_off(vptr->dev); + netif_carrier_off(vptr->netdev); } velocity_print_link_status(vptr); @@ -1867,9 +1887,9 @@ static void velocity_error(struct velocity_info *vptr, int status) enable_mii_autopoll(regs); if (vptr->mii_status & VELOCITY_LINK_FAIL) - netif_stop_queue(vptr->dev); + netif_stop_queue(vptr->netdev); else - netif_wake_queue(vptr->dev); + netif_wake_queue(vptr->netdev); } if (status & ISR_MIBFI) @@ -1894,7 +1914,7 @@ static int velocity_tx_srv(struct velocity_info *vptr) int idx; int works = 0; struct velocity_td_info *tdinfo; - struct net_device_stats *stats = &vptr->dev->stats; + struct net_device_stats *stats = &vptr->netdev->stats; for (qnum = 0; qnum < vptr->tx.numq; qnum++) { for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0; @@ -1939,9 +1959,9 @@ static int velocity_tx_srv(struct velocity_info *vptr) * Look to see if we should kick the transmit network * layer for more work. */ - if (netif_queue_stopped(vptr->dev) && (full == 0) && + if (netif_queue_stopped(vptr->netdev) && (full == 0) && (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { - netif_wake_queue(vptr->dev); + netif_wake_queue(vptr->netdev); } return works; } @@ -1989,7 +2009,7 @@ static int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, if (pkt_size < rx_copybreak) { struct sk_buff *new_skb; - new_skb = netdev_alloc_skb_ip_align(vptr->dev, pkt_size); + new_skb = netdev_alloc_skb_ip_align(vptr->netdev, pkt_size); if (new_skb) { new_skb->ip_summed = rx_skb[0]->ip_summed; skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size); @@ -2029,15 +2049,14 @@ static inline void velocity_iph_realign(struct velocity_info *vptr, */ static int velocity_receive_frame(struct velocity_info *vptr, int idx) { - void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int); - struct net_device_stats *stats = &vptr->dev->stats; + struct net_device_stats *stats = &vptr->netdev->stats; struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); struct rx_desc *rd = &(vptr->rx.ring[idx]); int pkt_len = le16_to_cpu(rd->rdesc0.len) & 0x3fff; struct sk_buff *skb; if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) { - VELOCITY_PRT(MSG_LEVEL_VERBOSE, KERN_ERR " %s : the received frame span multple RDs.\n", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_VERBOSE, KERN_ERR " %s : the received frame span multple RDs.\n", vptr->netdev->name); stats->rx_length_errors++; return -EINVAL; } @@ -2047,8 +2066,8 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) skb = rd_info->skb; - pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma, - vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(vptr->dev, rd_info->skb_dma, + vptr->rx.buf_sz, DMA_FROM_DEVICE); /* * Drop frame not meeting IEEE 802.3 @@ -2061,21 +2080,20 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) } } - pci_action = pci_dma_sync_single_for_device; - velocity_rx_csum(rd, skb); if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) { velocity_iph_realign(vptr, skb, pkt_len); - pci_action = pci_unmap_single; rd_info->skb = NULL; + dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, + DMA_FROM_DEVICE); + } else { + dma_sync_single_for_device(vptr->dev, rd_info->skb_dma, + vptr->rx.buf_sz, DMA_FROM_DEVICE); } - pci_action(vptr->pdev, rd_info->skb_dma, vptr->rx.buf_sz, - PCI_DMA_FROMDEVICE); - skb_put(skb, pkt_len - 4); - skb->protocol = eth_type_trans(skb, vptr->dev); + skb->protocol = eth_type_trans(skb, vptr->netdev); if (rd->rdesc0.RSR & RSR_DETAG) { u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG)); @@ -2100,7 +2118,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) */ static int velocity_rx_srv(struct velocity_info *vptr, int budget_left) { - struct net_device_stats *stats = &vptr->dev->stats; + struct net_device_stats *stats = &vptr->netdev->stats; int rd_curr = vptr->rx.curr; int works = 0; @@ -2235,15 +2253,15 @@ static int velocity_open(struct net_device *dev) goto out; /* Ensure chip is running */ - pci_set_power_state(vptr->pdev, PCI_D0); + velocity_set_power_state(vptr, PCI_D0); velocity_init_registers(vptr, VELOCITY_INIT_COLD); - ret = request_irq(vptr->pdev->irq, velocity_intr, IRQF_SHARED, + ret = request_irq(dev->irq, velocity_intr, IRQF_SHARED, dev->name, dev); if (ret < 0) { /* Power down the chip */ - pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); velocity_free_rings(vptr); goto out; } @@ -2292,7 +2310,7 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) if ((new_mtu < VELOCITY_MIN_MTU) || new_mtu > (VELOCITY_MAX_MTU)) { VELOCITY_PRT(MSG_LEVEL_ERR, KERN_NOTICE "%s: Invalid MTU.\n", - vptr->dev->name); + vptr->netdev->name); ret = -EINVAL; goto out_0; } @@ -2314,8 +2332,9 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) goto out_0; } - tmp_vptr->dev = dev; + tmp_vptr->netdev = dev; tmp_vptr->pdev = vptr->pdev; + tmp_vptr->dev = vptr->dev; tmp_vptr->options = vptr->options; tmp_vptr->tx.numq = vptr->tx.numq; @@ -2415,7 +2434,7 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) saving then we need to bring the device back up to talk to it */ if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D0); + velocity_set_power_state(vptr, PCI_D0); switch (cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */ @@ -2428,7 +2447,7 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ret = -EOPNOTSUPP; } if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); return ret; @@ -2494,7 +2513,7 @@ static int velocity_close(struct net_device *dev) if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) velocity_get_ip(vptr); - free_irq(vptr->pdev->irq, dev); + free_irq(dev->irq, dev); velocity_free_rings(vptr); @@ -2550,7 +2569,8 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, * add it to the transmit ring. */ tdinfo->skb = skb; - tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE); + tdinfo->skb_dma[0] = dma_map_single(vptr->dev, skb->data, pktlen, + DMA_TO_DEVICE); td_ptr->tdesc0.len = cpu_to_le16(pktlen); td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); td_ptr->td_buf[0].pa_high = 0; @@ -2560,7 +2580,7 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - tdinfo->skb_dma[i + 1] = skb_frag_dma_map(&vptr->pdev->dev, + tdinfo->skb_dma[i + 1] = skb_frag_dma_map(vptr->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); @@ -2632,12 +2652,9 @@ static const struct net_device_ops velocity_netdev_ops = { * Set up the initial velocity_info struct for the device that has been * discovered. */ -static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, - const struct velocity_info_tbl *info) +static void velocity_init_info(struct velocity_info *vptr, + const struct velocity_info_tbl *info) { - memset(vptr, 0, sizeof(struct velocity_info)); - - vptr->pdev = pdev; vptr->chip_id = info->chip_id; vptr->tx.numq = info->txqueue; vptr->multicast_limit = MCAM_SIZE; @@ -2652,10 +2669,9 @@ static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, * Retrieve the PCI configuration space data that interests us from * the kernel PCI layer */ -static int velocity_get_pci_info(struct velocity_info *vptr, - struct pci_dev *pdev) +static int velocity_get_pci_info(struct velocity_info *vptr) { - vptr->rev_id = pdev->revision; + struct pci_dev *pdev = vptr->pdev; pci_set_master(pdev); @@ -2678,7 +2694,37 @@ static int velocity_get_pci_info(struct velocity_info *vptr, dev_err(&pdev->dev, "region #1 is too small.\n"); return -EINVAL; } - vptr->pdev = pdev; + + return 0; +} + +/** + * velocity_get_platform_info - retrieve platform info for device + * @vptr: velocity device + * @pdev: platform device it matches + * + * Retrieve the Platform configuration data that interests us + */ +static int velocity_get_platform_info(struct velocity_info *vptr) +{ + struct resource res; + int ret; + + if (of_get_property(vptr->dev->of_node, "no-eeprom", NULL)) + vptr->no_eeprom = 1; + + ret = of_address_to_resource(vptr->dev->of_node, 0, &res); + if (ret) { + dev_err(vptr->dev, "unable to find memory address\n"); + return ret; + } + + vptr->memaddr = res.start; + + if (resource_size(&res) < VELOCITY_IO_SIZE) { + dev_err(vptr->dev, "memory region is too small.\n"); + return -EINVAL; + } return 0; } @@ -2692,7 +2738,7 @@ static int velocity_get_pci_info(struct velocity_info *vptr, */ static void velocity_print_info(struct velocity_info *vptr) { - struct net_device *dev = vptr->dev; + struct net_device *dev = vptr->netdev; printk(KERN_INFO "%s: %s\n", dev->name, get_chip_name(vptr->chip_id)); printk(KERN_INFO "%s: Ethernet Address: %pM\n", @@ -2707,21 +2753,22 @@ static u32 velocity_get_link(struct net_device *dev) } /** - * velocity_found1 - set up discovered velocity card + * velocity_probe - set up discovered velocity device * @pdev: PCI device * @ent: PCI device table entry that matched + * @bustype: bus that device is connected to * * Configure a discovered adapter from scratch. Return a negative * errno error code on failure paths. */ -static int velocity_found1(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int velocity_probe(struct device *dev, int irq, + const struct velocity_info_tbl *info, + enum velocity_bus_type bustype) { static int first = 1; - struct net_device *dev; + struct net_device *netdev; int i; const char *drv_string; - const struct velocity_info_tbl *info = &chip_info_table[ent->driver_data]; struct velocity_info *vptr; struct mac_regs __iomem *regs; int ret = -ENOMEM; @@ -2730,20 +2777,18 @@ static int velocity_found1(struct pci_dev *pdev, * can support more than MAX_UNITS. */ if (velocity_nics >= MAX_UNITS) { - dev_notice(&pdev->dev, "already found %d NICs.\n", - velocity_nics); + dev_notice(dev, "already found %d NICs.\n", velocity_nics); return -ENODEV; } - dev = alloc_etherdev(sizeof(struct velocity_info)); - if (!dev) + netdev = alloc_etherdev(sizeof(struct velocity_info)); + if (!netdev) goto out; /* Chain it all together */ - SET_NETDEV_DEV(dev, &pdev->dev); - vptr = netdev_priv(dev); - + SET_NETDEV_DEV(netdev, dev); + vptr = netdev_priv(netdev); if (first) { printk(KERN_INFO "%s Ver. %s\n", @@ -2753,41 +2798,41 @@ static int velocity_found1(struct pci_dev *pdev, first = 0; } - velocity_init_info(pdev, vptr, info); - + netdev->irq = irq; + vptr->netdev = netdev; vptr->dev = dev; - ret = pci_enable_device(pdev); - if (ret < 0) - goto err_free_dev; + velocity_init_info(vptr, info); - ret = velocity_get_pci_info(vptr, pdev); - if (ret < 0) { - /* error message already printed */ - goto err_disable; - } + if (bustype == BUS_PCI) { + vptr->pdev = to_pci_dev(dev); - ret = pci_request_regions(pdev, VELOCITY_NAME); - if (ret < 0) { - dev_err(&pdev->dev, "No PCI resources.\n"); - goto err_disable; + ret = velocity_get_pci_info(vptr); + if (ret < 0) + goto err_free_dev; + } else { + vptr->pdev = NULL; + ret = velocity_get_platform_info(vptr); + if (ret < 0) + goto err_free_dev; } regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); if (regs == NULL) { ret = -EIO; - goto err_release_res; + goto err_free_dev; } vptr->mac_regs = regs; + vptr->rev_id = readb(®s->rev_id); mac_wol_reset(regs); for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(®s->PAR[i]); + netdev->dev_addr[i] = readb(®s->PAR[i]); - drv_string = dev_driver_string(&pdev->dev); + drv_string = dev_driver_string(dev); velocity_get_options(&vptr->options, velocity_nics, drv_string); @@ -2808,46 +2853,125 @@ static int velocity_found1(struct pci_dev *pdev, vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); - dev->netdev_ops = &velocity_netdev_ops; - dev->ethtool_ops = &velocity_ethtool_ops; - netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT); + netdev->netdev_ops = &velocity_netdev_ops; + netdev->ethtool_ops = &velocity_ethtool_ops; + netif_napi_add(netdev, &vptr->napi, velocity_poll, + VELOCITY_NAPI_WEIGHT); - dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | + netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX; - dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER | - NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_IP_CSUM; + netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_IP_CSUM; - ret = register_netdev(dev); + ret = register_netdev(netdev); if (ret < 0) goto err_iounmap; - if (!velocity_get_link(dev)) { - netif_carrier_off(dev); + if (!velocity_get_link(netdev)) { + netif_carrier_off(netdev); vptr->mii_status |= VELOCITY_LINK_FAIL; } velocity_print_info(vptr); - pci_set_drvdata(pdev, dev); + dev_set_drvdata(vptr->dev, netdev); /* and leave the chip powered down */ - pci_set_power_state(pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); velocity_nics++; out: return ret; err_iounmap: iounmap(regs); -err_release_res: - pci_release_regions(pdev); -err_disable: - pci_disable_device(pdev); err_free_dev: - free_netdev(dev); + free_netdev(netdev); goto out; } -#ifdef CONFIG_PM +/** + * velocity_remove - device unplug + * @dev: device being removed + * + * Device unload callback. Called on an unplug or on module + * unload for each active device that is present. Disconnects + * the device from the network layer and frees all the resources + */ +static int velocity_remove(struct device *dev) +{ + struct net_device *netdev = dev_get_drvdata(dev); + struct velocity_info *vptr = netdev_priv(netdev); + + unregister_netdev(netdev); + iounmap(vptr->mac_regs); + free_netdev(netdev); + velocity_nics--; + + return 0; +} + +static int velocity_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + const struct velocity_info_tbl *info = + &chip_info_table[ent->driver_data]; + int ret; + + ret = pci_enable_device(pdev); + if (ret < 0) + return ret; + + ret = pci_request_regions(pdev, VELOCITY_NAME); + if (ret < 0) { + dev_err(&pdev->dev, "No PCI resources.\n"); + goto fail1; + } + + ret = velocity_probe(&pdev->dev, pdev->irq, info, BUS_PCI); + if (ret == 0) + return 0; + + pci_release_regions(pdev); +fail1: + pci_disable_device(pdev); + return ret; +} + +static void velocity_pci_remove(struct pci_dev *pdev) +{ + velocity_remove(&pdev->dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int velocity_platform_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + const struct velocity_info_tbl *info; + int irq; + + of_id = of_match_device(velocity_of_ids, &pdev->dev); + if (!of_id) + return -EINVAL; + info = of_id->data; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + return -EINVAL; + + return velocity_probe(&pdev->dev, irq, info, BUS_PLATFORM); +} + +static int velocity_platform_remove(struct platform_device *pdev) +{ + velocity_remove(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP /** * wol_calc_crc - WOL CRC * @pattern: data pattern @@ -3004,32 +3128,35 @@ static void velocity_save_context(struct velocity_info *vptr, struct velocity_co } -static int velocity_suspend(struct pci_dev *pdev, pm_message_t state) +static int velocity_suspend(struct device *dev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = netdev_priv(dev); + struct net_device *netdev = dev_get_drvdata(dev); + struct velocity_info *vptr = netdev_priv(netdev); unsigned long flags; - if (!netif_running(vptr->dev)) + if (!netif_running(vptr->netdev)) return 0; - netif_device_detach(vptr->dev); + netif_device_detach(vptr->netdev); spin_lock_irqsave(&vptr->lock, flags); - pci_save_state(pdev); + if (vptr->pdev) + pci_save_state(vptr->pdev); if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { velocity_get_ip(vptr); velocity_save_context(vptr, &vptr->context); velocity_shutdown(vptr); velocity_set_wol(vptr); - pci_enable_wake(pdev, PCI_D3hot, 1); - pci_set_power_state(pdev, PCI_D3hot); + if (vptr->pdev) + pci_enable_wake(vptr->pdev, PCI_D3hot, 1); + velocity_set_power_state(vptr, PCI_D3hot); } else { velocity_save_context(vptr, &vptr->context); velocity_shutdown(vptr); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); + if (vptr->pdev) + pci_disable_device(vptr->pdev); + velocity_set_power_state(vptr, PCI_D3hot); } spin_unlock_irqrestore(&vptr->lock, flags); @@ -3071,19 +3198,22 @@ static void velocity_restore_context(struct velocity_info *vptr, struct velocity writeb(*((u8 *) (context->mac_reg + i)), ptr + i); } -static int velocity_resume(struct pci_dev *pdev) +static int velocity_resume(struct device *dev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = netdev_priv(dev); + struct net_device *netdev = dev_get_drvdata(dev); + struct velocity_info *vptr = netdev_priv(netdev); unsigned long flags; int i; - if (!netif_running(vptr->dev)) + if (!netif_running(vptr->netdev)) return 0; - pci_set_power_state(pdev, PCI_D0); - pci_enable_wake(pdev, 0, 0); - pci_restore_state(pdev); + velocity_set_power_state(vptr, PCI_D0); + + if (vptr->pdev) { + pci_enable_wake(vptr->pdev, PCI_D0, 0); + pci_restore_state(vptr->pdev); + } mac_wol_reset(vptr->mac_regs); @@ -3101,27 +3231,38 @@ static int velocity_resume(struct pci_dev *pdev) mac_enable_int(vptr->mac_regs); spin_unlock_irqrestore(&vptr->lock, flags); - netif_device_attach(vptr->dev); + netif_device_attach(vptr->netdev); return 0; } -#endif +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(velocity_pm_ops, velocity_suspend, velocity_resume); /* * Definition for our device driver. The PCI layer interface * uses this to handle all our card discover and plugging */ -static struct pci_driver velocity_driver = { +static struct pci_driver velocity_pci_driver = { .name = VELOCITY_NAME, - .id_table = velocity_id_table, - .probe = velocity_found1, - .remove = velocity_remove1, -#ifdef CONFIG_PM - .suspend = velocity_suspend, - .resume = velocity_resume, -#endif + .id_table = velocity_pci_id_table, + .probe = velocity_pci_probe, + .remove = velocity_pci_remove, + .driver = { + .pm = &velocity_pm_ops, + }, }; +static struct platform_driver velocity_platform_driver = { + .probe = velocity_platform_probe, + .remove = velocity_platform_remove, + .driver = { + .name = "via-velocity", + .owner = THIS_MODULE, + .of_match_table = velocity_of_ids, + .pm = &velocity_pm_ops, + }, +}; /** * velocity_ethtool_up - pre hook for ethtool @@ -3134,7 +3275,7 @@ static int velocity_ethtool_up(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D0); + velocity_set_power_state(vptr, PCI_D0); return 0; } @@ -3149,7 +3290,7 @@ static void velocity_ethtool_down(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); } static int velocity_get_settings(struct net_device *dev, @@ -3269,9 +3410,14 @@ static int velocity_set_settings(struct net_device *dev, static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct velocity_info *vptr = netdev_priv(dev); + strlcpy(info->driver, VELOCITY_NAME, sizeof(info->driver)); strlcpy(info->version, VELOCITY_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(vptr->pdev), sizeof(info->bus_info)); + if (vptr->pdev) + strlcpy(info->bus_info, pci_name(vptr->pdev), + sizeof(info->bus_info)); + else + strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); } static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) @@ -3561,13 +3707,20 @@ static void velocity_unregister_notifier(void) */ static int __init velocity_init_module(void) { - int ret; + int ret_pci, ret_platform; velocity_register_notifier(); - ret = pci_register_driver(&velocity_driver); - if (ret < 0) + + ret_pci = pci_register_driver(&velocity_pci_driver); + ret_platform = platform_driver_register(&velocity_platform_driver); + + /* if both_registers failed, remove the notifier */ + if ((ret_pci < 0) && (ret_platform < 0)) { velocity_unregister_notifier(); - return ret; + return ret_pci; + } + + return 0; } /** @@ -3581,7 +3734,9 @@ static int __init velocity_init_module(void) static void __exit velocity_cleanup_module(void) { velocity_unregister_notifier(); - pci_unregister_driver(&velocity_driver); + + pci_unregister_driver(&velocity_pci_driver); + platform_driver_unregister(&velocity_platform_driver); } module_init(velocity_init_module); diff --git a/drivers/net/ethernet/via/via-velocity.h b/drivers/net/ethernet/via/via-velocity.h index 4cb9f13485e..9453bfa9324 100644 --- a/drivers/net/ethernet/via/via-velocity.h +++ b/drivers/net/ethernet/via/via-velocity.h @@ -1265,7 +1265,7 @@ struct velocity_context { #define PHYID_VT3216_64BIT 0x000FC600UL #define PHYID_MARVELL_1000 0x01410C50UL #define PHYID_MARVELL_1000S 0x01410C40UL - +#define PHYID_ICPLUS_IP101A 0x02430C54UL #define PHYID_REV_ID_MASK 0x0000000FUL #define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK) @@ -1434,8 +1434,10 @@ struct velocity_opt { #define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) struct velocity_info { + struct device *dev; struct pci_dev *pdev; - struct net_device *dev; + struct net_device *netdev; + int no_eeprom; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; u8 ip_addr[4]; @@ -1514,7 +1516,7 @@ static inline int velocity_get_ip(struct velocity_info *vptr) int res = -ENOENT; rcu_read_lock(); - in_dev = __in_dev_get_rcu(vptr->dev); + in_dev = __in_dev_get_rcu(vptr->netdev); if (in_dev != NULL) { ifa = (struct in_ifaddr *) in_dev->ifa_list; if (ifa != NULL) { diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index a518dcab396..30fed08d167 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -734,7 +734,6 @@ err_hw_probe: unregister_netdev(ndev); err_register: free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return err; } @@ -750,7 +749,6 @@ static int w5100_remove(struct platform_device *pdev) unregister_netdev(ndev); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 6e00e3f94ce..e92884564e1 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -646,7 +646,6 @@ err_hw_probe: unregister_netdev(ndev); err_register: free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return err; } @@ -662,7 +661,6 @@ static int w5300_remove(struct platform_device *pdev) unregister_netdev(ndev); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 122d60c0481..7b90a5eba09 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_XILINX bool "Xilinx devices" default y - depends on PPC || PPC32 || MICROBLAZE + depends on PPC || PPC32 || MICROBLAZE || ARCH_ZYNQ ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -20,7 +20,7 @@ if NET_VENDOR_XILINX config XILINX_EMACLITE tristate "Xilinx 10/100 Ethernet Lite support" - depends on (PPC32 || MICROBLAZE) + depends on (PPC32 || MICROBLAZE || ARCH_ZYNQ) select PHYLIB ---help--- This driver supports the 10/100 Ethernet Lite from Xilinx. diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 57c2e5ef280..58eb4488bef 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1007,7 +1007,7 @@ static int temac_of_probe(struct platform_device *op) return -ENOMEM; ether_setup(ndev); - dev_set_drvdata(&op->dev, ndev); + platform_set_drvdata(op, ndev); SET_NETDEV_DEV(ndev, &op->dev); ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; @@ -1136,7 +1136,7 @@ static int temac_of_probe(struct platform_device *op) static int temac_of_remove(struct platform_device *op) { - struct net_device *ndev = dev_get_drvdata(&op->dev); + struct net_device *ndev = platform_get_drvdata(op); struct temac_local *lp = netdev_priv(ndev); temac_mdio_teardown(lp); @@ -1145,7 +1145,6 @@ static int temac_of_remove(struct platform_device *op) if (lp->phy_node) of_node_put(lp->phy_node); lp->phy_node = NULL; - dev_set_drvdata(&op->dev, NULL); iounmap(lp->regs); if (lp->sdma_regs) iounmap(lp->sdma_regs); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 24748e8367a..fb7d1c28a2e 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1484,7 +1484,7 @@ static int axienet_of_probe(struct platform_device *op) return -ENOMEM; ether_setup(ndev); - dev_set_drvdata(&op->dev, ndev); + platform_set_drvdata(op, ndev); SET_NETDEV_DEV(ndev, &op->dev); ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ @@ -1622,7 +1622,7 @@ nodev: static int axienet_of_remove(struct platform_device *op) { - struct net_device *ndev = dev_get_drvdata(&op->dev); + struct net_device *ndev = platform_get_drvdata(op); struct axienet_local *lp = netdev_priv(ndev); axienet_mdio_teardown(lp); @@ -1632,8 +1632,6 @@ static int axienet_of_remove(struct platform_device *op) of_node_put(lp->phy_node); lp->phy_node = NULL; - dev_set_drvdata(&op->dev, NULL); - iounmap(lp->regs); if (lp->dma_regs) iounmap(lp->dma_regs); diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index b7268b3dae7..fd4dbdae533 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -2,9 +2,9 @@ * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. * * This is a new flat driver which is based on the original emac_lite - * driver from John Williams <john.williams@petalogix.com>. + * driver from John Williams <john.williams@xilinx.com>. * - * 2007-2009 (c) Xilinx, Inc. + * 2007 - 2013 (c) Xilinx, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -159,34 +159,32 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata) u32 reg_data; /* Enable the Tx interrupts for the first Buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_TSR_OFFSET, - reg_data | XEL_TSR_XMIT_IE_MASK); + reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET); + __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK, + drvdata->base_addr + XEL_TSR_OFFSET); /* Enable the Tx interrupts for the second Buffer if * configured in HW */ if (drvdata->tx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + + reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET, - reg_data | XEL_TSR_XMIT_IE_MASK); + __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK, + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); } /* Enable the Rx interrupts for the first buffer */ - out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - XEL_RSR_RECV_IE_MASK); + __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); /* Enable the Rx interrupts for the second Buffer if * configured in HW */ if (drvdata->rx_ping_pong != 0) { - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET, - XEL_RSR_RECV_IE_MASK); + __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET); } /* Enable the Global Interrupt Enable */ - out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); + __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); } /** @@ -201,37 +199,37 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata) u32 reg_data; /* Disable the Global Interrupt Enable */ - out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); + __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); /* Disable the Tx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_TSR_OFFSET, - reg_data & (~XEL_TSR_XMIT_IE_MASK)); + reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET); + __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), + drvdata->base_addr + XEL_TSR_OFFSET); /* Disable the Tx interrupts for the second Buffer * if configured in HW */ if (drvdata->tx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET, - reg_data & (~XEL_TSR_XMIT_IE_MASK)); + __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); } /* Disable the Rx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET); - out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - reg_data & (~XEL_RSR_RECV_IE_MASK)); + reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET); + __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), + drvdata->base_addr + XEL_RSR_OFFSET); /* Disable the Rx interrupts for the second buffer * if configured in HW */ if (drvdata->rx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET, - reg_data & (~XEL_RSR_RECV_IE_MASK)); + __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_RSR_OFFSET); } } @@ -351,7 +349,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, byte_count = ETH_FRAME_LEN; /* Check if the expected buffer is available */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { @@ -364,7 +362,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, addr = (void __iomem __force *)((u32 __force)addr ^ XEL_BUFFER_OFFSET); - reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK)) != 0) @@ -375,15 +373,16 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, /* Write the frame to the buffer */ xemaclite_aligned_write(data, (u32 __force *) addr, byte_count); - out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK)); + __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK), + addr + XEL_TPLR_OFFSET); /* Update the Tx Status Register to indicate that there is a * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which * is used by the interrupt handler to check whether a frame * has been transmitted */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); - out_be32(addr + XEL_TSR_OFFSET, reg_data); + __raw_writel(reg_data, addr + XEL_TSR_OFFSET); return 0; } @@ -408,7 +407,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); /* Verify which buffer has valid data */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data = __raw_readl(addr + XEL_RSR_OFFSET); if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { if (drvdata->rx_ping_pong != 0) @@ -425,14 +424,14 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) return 0; /* No data was available */ /* Verify that buffer has valid data */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data = __raw_readl(addr + XEL_RSR_OFFSET); if ((reg_data & XEL_RSR_RECV_DONE_MASK) != XEL_RSR_RECV_DONE_MASK) return 0; /* No data was available */ } /* Get the protocol type of the ethernet frame that arrived */ - proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET + + proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); @@ -441,7 +440,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) { if (proto_type == ETH_P_IP) { - length = ((ntohl(in_be32(addr + + length = ((ntohl(__raw_readl(addr + XEL_HEADER_IP_LENGTH_OFFSET + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & @@ -463,9 +462,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) data, length); /* Acknowledge the frame */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data = __raw_readl(addr + XEL_RSR_OFFSET); reg_data &= ~XEL_RSR_RECV_DONE_MASK; - out_be32(addr + XEL_RSR_OFFSET, reg_data); + __raw_writel(reg_data, addr + XEL_RSR_OFFSET); return length; } @@ -492,14 +491,14 @@ static void xemaclite_update_address(struct net_local *drvdata, xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN); - out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN); + __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET); /* Update the MAC address in the EmacLite */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); - out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); + __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET); /* Wait for EmacLite to finish with the MAC address update */ - while ((in_be32(addr + XEL_TSR_OFFSET) & + while ((__raw_readl(addr + XEL_TSR_OFFSET) & XEL_TSR_PROG_MAC_ADDR) != 0) ; } @@ -669,31 +668,32 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) u32 tx_status; /* Check if there is Rx Data available */ - if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) || - (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) + if ((__raw_readl(base_addr + XEL_RSR_OFFSET) & + XEL_RSR_RECV_DONE_MASK) || + (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK)) xemaclite_rx_handler(dev); /* Check if the Transmission for the first buffer is completed */ - tx_status = in_be32(base_addr + XEL_TSR_OFFSET); + tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET); if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - out_be32(base_addr + XEL_TSR_OFFSET, tx_status); + __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET); tx_complete = true; } /* Check if the Transmission for the second buffer is completed */ - tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, - tx_status); + __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); tx_complete = true; } @@ -726,7 +726,7 @@ static int xemaclite_mdio_wait(struct net_local *lp) /* wait for the MDIO interface to not be busy or timeout after some time. */ - while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) & + while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) & XEL_MDIOCTRL_MDIOSTS_MASK) { if (end - jiffies <= 0) { WARN_ON(1); @@ -762,17 +762,17 @@ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) * MDIO Address register. Set the Status bit in the MDIO Control * register to start a MDIO read transaction. */ - ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); - out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, - XEL_MDIOADDR_OP_MASK | - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); + __raw_writel(XEL_MDIOADDR_OP_MASK | + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), + lp->base_addr + XEL_MDIOADDR_OFFSET); + __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); if (xemaclite_mdio_wait(lp)) return -ETIMEDOUT; - rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET); + rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET); dev_dbg(&lp->ndev->dev, "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n", @@ -809,13 +809,13 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, * Data register. Finally, set the Status bit in the MDIO Control * register to start a MDIO write transaction. */ - ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); - out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, - ~XEL_MDIOADDR_OP_MASK & - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); - out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val); - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); + __raw_writel(~XEL_MDIOADDR_OP_MASK & + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), + lp->base_addr + XEL_MDIOADDR_OFFSET); + __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET); + __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); return 0; } @@ -848,24 +848,39 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) int rc; struct resource res; struct device_node *np = of_get_parent(lp->phy_node); + struct device_node *npp; /* Don't register the MDIO bus if the phy_node or its parent node * can't be found. */ - if (!np) + if (!np) { + dev_err(dev, "Failed to register mdio bus.\n"); return -ENODEV; + } + npp = of_get_parent(np); + + of_address_to_resource(npp, 0, &res); + if (lp->ndev->mem_start != res.start) { + struct phy_device *phydev; + phydev = of_phy_find_device(lp->phy_node); + if (!phydev) + dev_info(dev, + "MDIO of the phy is not registered yet\n"); + return 0; + } /* Enable the MDIO bus by asserting the enable bit in MDIO Control * register. */ - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - XEL_MDIOCTRL_MDIOEN_MASK); + __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); bus = mdiobus_alloc(); - if (!bus) + if (!bus) { + dev_err(dev, "Failed to allocate mdiobus\n"); return -ENOMEM; + } - of_address_to_resource(np, 0, &res); snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", (unsigned long long)res.start); bus->priv = lp; @@ -879,8 +894,10 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) lp->mii_bus = bus; rc = of_mdiobus_register(bus, np); - if (rc) + if (rc) { + dev_err(dev, "Failed to register mdio bus.\n"); goto err_register; + } return 0; @@ -896,7 +913,7 @@ err_register: * There's nothing in the Emaclite device to be configured when the link * state changes. We just print the status. */ -void xemaclite_adjust_link(struct net_device *ndev) +static void xemaclite_adjust_link(struct net_device *ndev) { struct net_local *lp = netdev_priv(ndev); struct phy_device *phy = lp->phy_dev; @@ -1058,13 +1075,14 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) * This function un maps the IO region of the Emaclite device and frees the net * device. */ -static void xemaclite_remove_ndev(struct net_device *ndev) +static void xemaclite_remove_ndev(struct net_device *ndev, + struct platform_device *pdev) { if (ndev) { struct net_local *lp = netdev_priv(ndev); if (lp->base_addr) - iounmap((void __iomem __force *) (lp->base_addr)); + devm_iounmap(&pdev->dev, lp->base_addr); free_netdev(ndev); } } @@ -1110,8 +1128,7 @@ static struct net_device_ops xemaclite_netdev_ops; */ static int xemaclite_of_probe(struct platform_device *ofdev) { - struct resource r_irq; /* Interrupt resources */ - struct resource r_mem; /* IO mem resources */ + struct resource *res; struct net_device *ndev = NULL; struct net_local *lp = NULL; struct device *dev = &ofdev->dev; @@ -1121,20 +1138,6 @@ static int xemaclite_of_probe(struct platform_device *ofdev) dev_info(dev, "Device Tree Probing\n"); - /* Get iospace for the device */ - rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); - if (rc) { - dev_err(dev, "invalid address\n"); - return rc; - } - - /* Get IRQ for the device */ - rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq); - if (!rc) { - dev_err(dev, "no IRQ found\n"); - return rc; - } - /* Create an ethernet device instance */ ndev = alloc_etherdev(sizeof(struct net_local)); if (!ndev) @@ -1143,30 +1146,28 @@ static int xemaclite_of_probe(struct platform_device *ofdev) dev_set_drvdata(dev, ndev); SET_NETDEV_DEV(ndev, &ofdev->dev); - ndev->irq = r_irq.start; - ndev->mem_start = r_mem.start; - ndev->mem_end = r_mem.end; - lp = netdev_priv(ndev); lp->ndev = ndev; - if (!request_mem_region(ndev->mem_start, - ndev->mem_end - ndev->mem_start + 1, - DRIVER_NAME)) { - dev_err(dev, "Couldn't lock memory region at %p\n", - (void *)ndev->mem_start); - rc = -EBUSY; - goto error2; + /* Get IRQ for the device */ + res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "no IRQ found\n"); + goto error; } - /* Get the virtual base address for the device */ - lp->base_addr = ioremap(r_mem.start, resource_size(&r_mem)); - if (NULL == lp->base_addr) { - dev_err(dev, "EmacLite: Could not allocate iomem\n"); - rc = -EIO; - goto error1; + ndev->irq = res->start; + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + lp->base_addr = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(lp->base_addr)) { + rc = PTR_ERR(lp->base_addr); + goto error; } + ndev->mem_start = res->start; + ndev->mem_end = res->end; + spin_lock_init(&lp->reset_lock); lp->next_tx_buf_to_use = 0x0; lp->next_rx_buf_to_use = 0x0; @@ -1181,8 +1182,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev) dev_warn(dev, "No MAC address found\n"); /* Clear the Tx CSR's in case this is a restart */ - out_be32(lp->base_addr + XEL_TSR_OFFSET, 0); - out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0); + __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET); + __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); /* Set the MAC address in the EmacLite device */ xemaclite_update_address(lp, ndev->dev_addr); @@ -1203,7 +1204,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev) if (rc) { dev_err(dev, "Cannot register network device, aborting\n"); - goto error1; + goto error; } dev_info(dev, @@ -1212,11 +1213,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev) (unsigned int __force)lp->base_addr, ndev->irq); return 0; -error1: - release_mem_region(ndev->mem_start, resource_size(&r_mem)); - -error2: - xemaclite_remove_ndev(ndev); +error: + xemaclite_remove_ndev(ndev, ofdev); return rc; } @@ -1251,9 +1249,7 @@ static int xemaclite_of_remove(struct platform_device *of_dev) of_node_put(lp->phy_node); lp->phy_node = NULL; - release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1); - - xemaclite_remove_ndev(ndev); + xemaclite_remove_ndev(ndev, of_dev); dev_set_drvdata(dev, NULL); return 0; diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 6958a5e8770..3d689fcb791 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1472,7 +1472,6 @@ err_phy_dis: phy_disconnect(port->phydev); err_free_mem: npe_port_tab[NPE_ID(port->id)] = NULL; - platform_set_drvdata(pdev, NULL); release_resource(port->mem_res); err_npe_rel: npe_release(port->npe); @@ -1489,7 +1488,6 @@ static int eth_remove_one(struct platform_device *pdev) unregister_netdev(dev); phy_disconnect(port->phydev); npe_port_tab[NPE_ID(port->id)] = NULL; - platform_set_drvdata(pdev, NULL); npe_release(port->npe); release_resource(port->mem_res); free_netdev(dev); diff --git a/drivers/net/fddi/skfp/skfddi.c b/drivers/net/fddi/skfp/skfddi.c index d5bd563ac13..f5d7305a578 100644 --- a/drivers/net/fddi/skfp/skfddi.c +++ b/drivers/net/fddi/skfp/skfddi.c @@ -2246,15 +2246,4 @@ static struct pci_driver skfddi_pci_driver = { .remove = skfp_remove_one, }; -static int __init skfd_init(void) -{ - return pci_register_driver(&skfddi_pci_driver); -} - -static void __exit skfd_exit(void) -{ - pci_unregister_driver(&skfddi_pci_driver); -} - -module_init(skfd_init); -module_exit(skfd_exit); +module_pci_driver(skfddi_pci_driver); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 02de6c89167..f91bf0ddf03 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -103,7 +103,7 @@ static struct packet_type bpq_packet_type __read_mostly = { }; static struct notifier_block bpq_dev_notifier = { - .notifier_call =bpq_device_event, + .notifier_call = bpq_device_event, }; @@ -544,9 +544,10 @@ static void bpq_free_device(struct net_device *ndev) /* * Handle device status changes. */ -static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr) +static int bpq_device_event(struct notifier_block *this, + unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/drivers/net/hippi/rrunner.c b/drivers/net/hippi/rrunner.c index 3c4d6274bb9..00ed75155ce 100644 --- a/drivers/net/hippi/rrunner.c +++ b/drivers/net/hippi/rrunner.c @@ -1686,15 +1686,4 @@ static struct pci_driver rr_driver = { .remove = rr_remove_one, }; -static int __init rr_init_module(void) -{ - return pci_register_driver(&rr_driver); -} - -static void __exit rr_cleanup_module(void) -{ - pci_unregister_driver(&rr_driver); -} - -module_init(rr_init_module); -module_exit(rr_cleanup_module); +module_pci_driver(rr_driver); diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c index 22b4527321b..c74f384c87d 100644 --- a/drivers/net/irda/bfin_sir.c +++ b/drivers/net/irda/bfin_sir.c @@ -794,7 +794,6 @@ static int bfin_sir_remove(struct platform_device *pdev) kfree(self->rx_buff.head); free_netdev(dev); kfree(sir_port); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c index 9448587de45..4455425f1c7 100644 --- a/drivers/net/irda/sh_irda.c +++ b/drivers/net/irda/sh_irda.c @@ -838,7 +838,6 @@ static int sh_irda_remove(struct platform_device *pdev) sh_irda_remove_iobuf(self); iounmap(self->membase); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c index 24aefcd8406..89682b49900 100644 --- a/drivers/net/irda/sh_sir.c +++ b/drivers/net/irda/sh_sir.c @@ -796,7 +796,6 @@ static int sh_sir_remove(struct platform_device *pdev) sh_sir_remove_iobuf(self); iounmap(self->membase); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 6e91931a1c2..18373b6ae37 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -638,6 +638,14 @@ static int macvlan_ethtool_get_settings(struct net_device *dev, return __ethtool_get_settings(vlan->lowerdev, cmd); } +static netdev_features_t macvlan_fix_features(struct net_device *dev, + netdev_features_t features) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + + return features & (vlan->set_features | ~MACVLAN_FEATURES); +} + static const struct ethtool_ops macvlan_ethtool_ops = { .get_link = ethtool_op_get_link, .get_settings = macvlan_ethtool_get_settings, @@ -651,6 +659,7 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_stop = macvlan_stop, .ndo_start_xmit = macvlan_start_xmit, .ndo_change_mtu = macvlan_change_mtu, + .ndo_fix_features = macvlan_fix_features, .ndo_change_rx_flags = macvlan_change_rx_flags, .ndo_set_mac_address = macvlan_set_mac_address, .ndo_set_rx_mode = macvlan_set_mac_lists, @@ -791,6 +800,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, vlan->port = port; vlan->receive = receive; vlan->forward = forward; + vlan->set_features = MACVLAN_FEATURES; vlan->mode = MACVLAN_MODE_VEPA; if (data && data[IFLA_MACVLAN_MODE]) @@ -927,7 +937,7 @@ static struct rtnl_link_ops macvlan_link_ops = { static int macvlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct macvlan_dev *vlan, *next; struct macvlan_port *port; LIST_HEAD(list_kill); diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index b6dd6a75919..f2c4a3b218f 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -31,10 +31,6 @@ * macvtap_proto is used to allocate queues through the sock allocation * mechanism. * - * TODO: multiqueue support is currently not implemented, even though - * macvtap is basically prepared for that. We will need to add this - * here as well as in virtio-net and qemu to get line rate on 10gbit - * adapters from a guest. */ struct macvtap_queue { struct sock sk; @@ -44,6 +40,9 @@ struct macvtap_queue { struct macvlan_dev __rcu *vlan; struct file *file; unsigned int flags; + u16 queue_index; + bool enabled; + struct list_head next; }; static struct proto macvtap_proto = { @@ -66,11 +65,14 @@ static struct cdev macvtap_cdev; static const struct proto_ops macvtap_socket_ops; +#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ + NETIF_F_TSO6 | NETIF_F_UFO) +#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO) /* * RCU usage: * The macvtap_queue and the macvlan_dev are loosely coupled, the * pointers from one to the other can only be read while rcu_read_lock - * or macvtap_lock is held. + * or rtnl is held. * * Both the file and the macvlan_dev hold a reference on the macvtap_queue * through sock_hold(&q->sk). When the macvlan_dev goes away first, @@ -82,54 +84,84 @@ static const struct proto_ops macvtap_socket_ops; * file or the dev. The data structure is freed through __sk_free * when both our references and any pending SKBs are gone. */ -static DEFINE_SPINLOCK(macvtap_lock); -/* - * get_slot: return a [unused/occupied] slot in vlan->taps[]: - * - if 'q' is NULL, return the first empty slot; - * - otherwise, return the slot this pointer occupies. - */ -static int get_slot(struct macvlan_dev *vlan, struct macvtap_queue *q) +static int macvtap_enable_queue(struct net_device *dev, struct file *file, + struct macvtap_queue *q) { - int i; + struct macvlan_dev *vlan = netdev_priv(dev); + int err = -EINVAL; - for (i = 0; i < MAX_MACVTAP_QUEUES; i++) { - if (rcu_dereference_protected(vlan->taps[i], - lockdep_is_held(&macvtap_lock)) == q) - return i; - } + ASSERT_RTNL(); + + if (q->enabled) + goto out; - /* Should never happen */ - BUG_ON(1); + err = 0; + rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); + q->queue_index = vlan->numvtaps; + q->enabled = true; + + vlan->numvtaps++; +out: + return err; } static int macvtap_set_queue(struct net_device *dev, struct file *file, - struct macvtap_queue *q) + struct macvtap_queue *q) { struct macvlan_dev *vlan = netdev_priv(dev); - int index; int err = -EBUSY; - spin_lock(&macvtap_lock); - if (vlan->numvtaps == MAX_MACVTAP_QUEUES) + rtnl_lock(); + if (vlan->numqueues == MAX_MACVTAP_QUEUES) goto out; err = 0; - index = get_slot(vlan, NULL); rcu_assign_pointer(q->vlan, vlan); - rcu_assign_pointer(vlan->taps[index], q); + rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); sock_hold(&q->sk); q->file = file; + q->queue_index = vlan->numvtaps; + q->enabled = true; file->private_data = q; + list_add_tail(&q->next, &vlan->queue_list); vlan->numvtaps++; + vlan->numqueues++; out: - spin_unlock(&macvtap_lock); + rtnl_unlock(); return err; } +static int macvtap_disable_queue(struct macvtap_queue *q) +{ + struct macvlan_dev *vlan; + struct macvtap_queue *nq; + + ASSERT_RTNL(); + if (!q->enabled) + return -EINVAL; + + vlan = rtnl_dereference(q->vlan); + + if (vlan) { + int index = q->queue_index; + BUG_ON(index >= vlan->numvtaps); + nq = rtnl_dereference(vlan->taps[vlan->numvtaps - 1]); + nq->queue_index = index; + + rcu_assign_pointer(vlan->taps[index], nq); + RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); + q->enabled = false; + + vlan->numvtaps--; + } + + return 0; +} + /* * The file owning the queue got closed, give up both * the reference that the files holds as well as the @@ -142,19 +174,20 @@ static void macvtap_put_queue(struct macvtap_queue *q) { struct macvlan_dev *vlan; - spin_lock(&macvtap_lock); - vlan = rcu_dereference_protected(q->vlan, - lockdep_is_held(&macvtap_lock)); + rtnl_lock(); + vlan = rtnl_dereference(q->vlan); + if (vlan) { - int index = get_slot(vlan, q); + if (q->enabled) + BUG_ON(macvtap_disable_queue(q)); - RCU_INIT_POINTER(vlan->taps[index], NULL); + vlan->numqueues--; RCU_INIT_POINTER(q->vlan, NULL); sock_put(&q->sk); - --vlan->numvtaps; + list_del_init(&q->next); } - spin_unlock(&macvtap_lock); + rtnl_unlock(); synchronize_rcu(); sock_put(&q->sk); @@ -172,7 +205,12 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, { struct macvlan_dev *vlan = netdev_priv(dev); struct macvtap_queue *tap = NULL; - int numvtaps = vlan->numvtaps; + /* Access to taps array is protected by rcu, but access to numvtaps + * isn't. Below we use it to lookup a queue, but treat it as a hint + * and validate that the result isn't NULL - in case we are + * racing against queue removal. + */ + int numvtaps = ACCESS_ONCE(vlan->numvtaps); __u32 rxq; if (!numvtaps) @@ -182,8 +220,7 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, rxq = skb_get_rxhash(skb); if (rxq) { tap = rcu_dereference(vlan->taps[rxq % numvtaps]); - if (tap) - goto out; + goto out; } if (likely(skb_rx_queue_recorded(skb))) { @@ -193,17 +230,10 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, rxq -= numvtaps; tap = rcu_dereference(vlan->taps[rxq]); - if (tap) - goto out; - } - - /* Everything failed - find first available queue */ - for (rxq = 0; rxq < MAX_MACVTAP_QUEUES; rxq++) { - tap = rcu_dereference(vlan->taps[rxq]); - if (tap) - break; + goto out; } + tap = rcu_dereference(vlan->taps[0]); out: return tap; } @@ -216,27 +246,24 @@ out: static void macvtap_del_queues(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); - struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES]; + struct macvtap_queue *q, *tmp, *qlist[MAX_MACVTAP_QUEUES]; int i, j = 0; - /* macvtap_put_queue can free some slots, so go through all slots */ - spin_lock(&macvtap_lock); - for (i = 0; i < MAX_MACVTAP_QUEUES && vlan->numvtaps; i++) { - q = rcu_dereference_protected(vlan->taps[i], - lockdep_is_held(&macvtap_lock)); - if (q) { - qlist[j++] = q; - RCU_INIT_POINTER(vlan->taps[i], NULL); - RCU_INIT_POINTER(q->vlan, NULL); + ASSERT_RTNL(); + list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) { + list_del_init(&q->next); + qlist[j++] = q; + RCU_INIT_POINTER(q->vlan, NULL); + if (q->enabled) vlan->numvtaps--; - } + vlan->numqueues--; } - BUG_ON(vlan->numvtaps != 0); + for (i = 0; i < vlan->numvtaps; i++) + RCU_INIT_POINTER(vlan->taps[i], NULL); + BUG_ON(vlan->numvtaps); + BUG_ON(vlan->numqueues); /* guarantee that any future macvtap_set_queue will fail */ vlan->numvtaps = MAX_MACVTAP_QUEUES; - spin_unlock(&macvtap_lock); - - synchronize_rcu(); for (--j; j >= 0; j--) sock_put(&qlist[j]->sk); @@ -249,14 +276,44 @@ static void macvtap_del_queues(struct net_device *dev) */ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) { + struct macvlan_dev *vlan = netdev_priv(dev); struct macvtap_queue *q = macvtap_get_queue(dev, skb); + netdev_features_t features; if (!q) goto drop; if (skb_queue_len(&q->sk.sk_receive_queue) >= dev->tx_queue_len) goto drop; - skb_queue_tail(&q->sk.sk_receive_queue, skb); + skb->dev = dev; + /* Apply the forward feature mask so that we perform segmentation + * according to users wishes. + */ + features = netif_skb_features(skb) & vlan->tap_features; + if (netif_needs_gso(skb, features)) { + struct sk_buff *segs = __skb_gso_segment(skb, features, false); + + if (IS_ERR(segs)) + goto drop; + + if (!segs) { + skb_queue_tail(&q->sk.sk_receive_queue, skb); + goto wake_up; + } + + kfree_skb(skb); + while (segs) { + struct sk_buff *nskb = segs->next; + + segs->next = NULL; + skb_queue_tail(&q->sk.sk_receive_queue, segs); + segs = nskb; + } + } else { + skb_queue_tail(&q->sk.sk_receive_queue, skb); + } + +wake_up: wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND); return NET_RX_SUCCESS; @@ -322,6 +379,14 @@ static int macvtap_newlink(struct net *src_net, struct nlattr *tb[], struct nlattr *data[]) { + struct macvlan_dev *vlan = netdev_priv(dev); + INIT_LIST_HEAD(&vlan->queue_list); + + /* Since macvlan supports all offloads by default, make + * tap support all offloads also. + */ + vlan->tap_features = TUN_OFFLOADS; + /* Don't put anything that may fail after macvlan_common_newlink * because we can't undo what it does. */ @@ -385,7 +450,7 @@ static int macvtap_open(struct inode *inode, struct file *file) if (!q) goto out; - q->sock.wq = &q->wq; + RCU_INIT_POINTER(q->sock.wq, &q->wq); init_waitqueue_head(&q->wq.wait); q->sock.type = SOCK_RAW; q->sock.state = SS_CONNECTED; @@ -729,8 +794,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, skb_probe_transport_header(skb, ETH_HLEN); - rcu_read_lock_bh(); - vlan = rcu_dereference_bh(q->vlan); + rcu_read_lock(); + vlan = rcu_dereference(q->vlan); /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { skb_shinfo(skb)->destructor_arg = m->msg_control; @@ -741,7 +806,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, macvlan_start_xmit(skb, vlan->dev); else kfree_skb(skb); - rcu_read_unlock_bh(); + rcu_read_unlock(); return total_len; @@ -749,11 +814,11 @@ err_kfree: kfree_skb(skb); err: - rcu_read_lock_bh(); - vlan = rcu_dereference_bh(q->vlan); + rcu_read_lock(); + vlan = rcu_dereference(q->vlan); if (vlan) vlan->dev->stats.tx_dropped++; - rcu_read_unlock_bh(); + rcu_read_unlock(); return err; } @@ -829,11 +894,11 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, copied += len; done: - rcu_read_lock_bh(); - vlan = rcu_dereference_bh(q->vlan); + rcu_read_lock(); + vlan = rcu_dereference(q->vlan); if (vlan) macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0); - rcu_read_unlock_bh(); + rcu_read_unlock(); return ret ? ret : copied; } @@ -847,7 +912,9 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, ssize_t ret = 0; while (len) { - prepare_to_wait(sk_sleep(&q->sk), &wait, TASK_INTERRUPTIBLE); + if (!noblock) + prepare_to_wait(sk_sleep(&q->sk), &wait, + TASK_INTERRUPTIBLE); /* Read frames from the queue */ skb = skb_dequeue(&q->sk.sk_receive_queue); @@ -869,7 +936,8 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, break; } - finish_wait(sk_sleep(&q->sk), &wait); + if (!noblock) + finish_wait(sk_sleep(&q->sk), &wait); return ret; } @@ -892,6 +960,96 @@ out: return ret; } +static struct macvlan_dev *macvtap_get_vlan(struct macvtap_queue *q) +{ + struct macvlan_dev *vlan; + + ASSERT_RTNL(); + vlan = rtnl_dereference(q->vlan); + if (vlan) + dev_hold(vlan->dev); + + return vlan; +} + +static void macvtap_put_vlan(struct macvlan_dev *vlan) +{ + dev_put(vlan->dev); +} + +static int macvtap_ioctl_set_queue(struct file *file, unsigned int flags) +{ + struct macvtap_queue *q = file->private_data; + struct macvlan_dev *vlan; + int ret; + + vlan = macvtap_get_vlan(q); + if (!vlan) + return -EINVAL; + + if (flags & IFF_ATTACH_QUEUE) + ret = macvtap_enable_queue(vlan->dev, file, q); + else if (flags & IFF_DETACH_QUEUE) + ret = macvtap_disable_queue(q); + else + ret = -EINVAL; + + macvtap_put_vlan(vlan); + return ret; +} + +static int set_offload(struct macvtap_queue *q, unsigned long arg) +{ + struct macvlan_dev *vlan; + netdev_features_t features; + netdev_features_t feature_mask = 0; + + vlan = rtnl_dereference(q->vlan); + if (!vlan) + return -ENOLINK; + + features = vlan->dev->features; + + if (arg & TUN_F_CSUM) { + feature_mask = NETIF_F_HW_CSUM; + + if (arg & (TUN_F_TSO4 | TUN_F_TSO6)) { + if (arg & TUN_F_TSO_ECN) + feature_mask |= NETIF_F_TSO_ECN; + if (arg & TUN_F_TSO4) + feature_mask |= NETIF_F_TSO; + if (arg & TUN_F_TSO6) + feature_mask |= NETIF_F_TSO6; + } + + if (arg & TUN_F_UFO) + feature_mask |= NETIF_F_UFO; + } + + /* tun/tap driver inverts the usage for TSO offloads, where + * setting the TSO bit means that the userspace wants to + * accept TSO frames and turning it off means that user space + * does not support TSO. + * For macvtap, we have to invert it to mean the same thing. + * When user space turns off TSO, we turn off GSO/LRO so that + * user-space will not receive TSO frames. + */ + if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO)) + features |= RX_OFFLOADS; + else + features &= ~RX_OFFLOADS; + + /* tap_features are the same as features on tun/tap and + * reflect user expectations. + */ + vlan->tap_features = vlan->dev->features & + (feature_mask | ~TUN_OFFLOADS); + vlan->set_features = features; + netdev_update_features(vlan->dev); + + return 0; +} + /* * provide compatibility with generic tun/tap interface */ @@ -915,7 +1073,8 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, return -EFAULT; ret = 0; - if ((u & ~IFF_VNET_HDR) != (IFF_NO_PI | IFF_TAP)) + if ((u & ~(IFF_VNET_HDR | IFF_MULTI_QUEUE)) != + (IFF_NO_PI | IFF_TAP)) ret = -EINVAL; else q->flags = u; @@ -923,24 +1082,31 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, return ret; case TUNGETIFF: - rcu_read_lock_bh(); - vlan = rcu_dereference_bh(q->vlan); - if (vlan) - dev_hold(vlan->dev); - rcu_read_unlock_bh(); - - if (!vlan) + rtnl_lock(); + vlan = macvtap_get_vlan(q); + if (!vlan) { + rtnl_unlock(); return -ENOLINK; + } ret = 0; if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) || put_user(q->flags, &ifr->ifr_flags)) ret = -EFAULT; - dev_put(vlan->dev); + macvtap_put_vlan(vlan); + rtnl_unlock(); return ret; + case TUNSETQUEUE: + if (get_user(u, &ifr->ifr_flags)) + return -EFAULT; + rtnl_lock(); + ret = macvtap_ioctl_set_queue(file, u); + rtnl_unlock(); + case TUNGETFEATURES: - if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up)) + if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR | + IFF_MULTI_QUEUE, up)) return -EFAULT; return 0; @@ -976,7 +1142,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, got enabled for forwarded frames */ if (!(q->flags & IFF_VNET_HDR)) return -EINVAL; - return 0; + rtnl_lock(); + ret = set_offload(q, arg); + rtnl_unlock(); + return ret; default: return -EINVAL; @@ -1055,7 +1224,7 @@ EXPORT_SYMBOL_GPL(macvtap_get_socket); static int macvtap_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct macvlan_dev *vlan; struct device *classdev; dev_t devt; diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 4f777ed9b08..4822aafe638 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -654,12 +654,11 @@ static struct configfs_subsystem netconsole_subsys = { /* Handle network interface device notifications */ static int netconsole_netdev_event(struct notifier_block *this, - unsigned long event, - void *ptr) + unsigned long event, void *ptr) { unsigned long flags; struct netconsole_target *nt; - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); bool stopped = false; if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER || diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c new file mode 100644 index 00000000000..b57ce5f4896 --- /dev/null +++ b/drivers/net/nlmon.c @@ -0,0 +1,181 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/netlink.h> +#include <net/net_namespace.h> +#include <linux/if_arp.h> +#include <net/rtnetlink.h> + +struct pcpu_lstats { + u64 packets; + u64 bytes; + struct u64_stats_sync syncp; +}; + +static netdev_tx_t nlmon_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int len = skb->len; + struct pcpu_lstats *stats = this_cpu_ptr(dev->lstats); + + u64_stats_update_begin(&stats->syncp); + stats->bytes += len; + stats->packets++; + u64_stats_update_end(&stats->syncp); + + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static int nlmon_is_valid_mtu(int new_mtu) +{ + /* Note that in netlink we do not really have an upper limit. On + * default, we use NLMSG_GOODSIZE. Here at least we should make + * sure that it's at least the header size. + */ + return new_mtu >= (int) sizeof(struct nlmsghdr); +} + +static int nlmon_change_mtu(struct net_device *dev, int new_mtu) +{ + if (!nlmon_is_valid_mtu(new_mtu)) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static int nlmon_dev_init(struct net_device *dev) +{ + dev->lstats = alloc_percpu(struct pcpu_lstats); + + return dev->lstats == NULL ? -ENOMEM : 0; +} + +static void nlmon_dev_uninit(struct net_device *dev) +{ + free_percpu(dev->lstats); +} + +struct nlmon { + struct netlink_tap nt; +}; + +static int nlmon_open(struct net_device *dev) +{ + struct nlmon *nlmon = netdev_priv(dev); + + nlmon->nt.dev = dev; + nlmon->nt.module = THIS_MODULE; + return netlink_add_tap(&nlmon->nt); +} + +static int nlmon_close(struct net_device *dev) +{ + struct nlmon *nlmon = netdev_priv(dev); + + return netlink_remove_tap(&nlmon->nt); +} + +static struct rtnl_link_stats64 * +nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + int i; + u64 bytes = 0, packets = 0; + + for_each_possible_cpu(i) { + const struct pcpu_lstats *nl_stats; + u64 tbytes, tpackets; + unsigned int start; + + nl_stats = per_cpu_ptr(dev->lstats, i); + + do { + start = u64_stats_fetch_begin_bh(&nl_stats->syncp); + tbytes = nl_stats->bytes; + tpackets = nl_stats->packets; + } while (u64_stats_fetch_retry_bh(&nl_stats->syncp, start)); + + packets += tpackets; + bytes += tbytes; + } + + stats->rx_packets = packets; + stats->tx_packets = 0; + + stats->rx_bytes = bytes; + stats->tx_bytes = 0; + + return stats; +} + +static u32 always_on(struct net_device *dev) +{ + return 1; +} + +static const struct ethtool_ops nlmon_ethtool_ops = { + .get_link = always_on, +}; + +static const struct net_device_ops nlmon_ops = { + .ndo_init = nlmon_dev_init, + .ndo_uninit = nlmon_dev_uninit, + .ndo_open = nlmon_open, + .ndo_stop = nlmon_close, + .ndo_start_xmit = nlmon_xmit, + .ndo_get_stats64 = nlmon_get_stats64, + .ndo_change_mtu = nlmon_change_mtu, +}; + +static void nlmon_setup(struct net_device *dev) +{ + dev->type = ARPHRD_NETLINK; + dev->tx_queue_len = 0; + + dev->netdev_ops = &nlmon_ops; + dev->ethtool_ops = &nlmon_ethtool_ops; + dev->destructor = free_netdev; + + dev->features = NETIF_F_FRAGLIST | NETIF_F_HIGHDMA; + dev->flags = IFF_NOARP; + + /* That's rather a softlimit here, which, of course, + * can be altered. Not a real MTU, but what is to be + * expected in most cases. + */ + dev->mtu = NLMSG_GOODSIZE; +} + +static int nlmon_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) + return -EINVAL; + return 0; +} + +static struct rtnl_link_ops nlmon_link_ops __read_mostly = { + .kind = "nlmon", + .priv_size = sizeof(struct nlmon), + .setup = nlmon_setup, + .validate = nlmon_validate, +}; + +static __init int nlmon_register(void) +{ + return rtnl_link_register(&nlmon_link_ops); +} + +static __exit void nlmon_unregister(void) +{ + rtnl_link_unregister(&nlmon_link_ops); +} + +module_init(nlmon_register); +module_exit(nlmon_unregister); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>"); +MODULE_AUTHOR("Mathieu Geli <geli@enseirb.fr>"); +MODULE_DESCRIPTION("Netlink monitoring device"); +MODULE_ALIAS_RTNL_LINK("nlmon"); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 1e11f2bfd9c..3a316b30089 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -144,6 +144,16 @@ config MDIO_OCTEON If in doubt, say Y. +config MDIO_SUN4I + tristate "Allwinner sun4i MDIO interface support" + depends on ARCH_SUNXI + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + help + This driver supports the MDIO interface found in the network + interface units of the Allwinner SoC that have an EMAC (A10, + A12, A10s, etc.) + config MDIO_BUS_MUX tristate depends on OF_MDIO diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 9645e389a58..23a2ab2e847 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o +obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 45cbc10de01..1f7091b3c27 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -27,15 +27,22 @@ #define AT803X_MMD_ACCESS_CONTROL 0x0D #define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E #define AT803X_FUNC_DATA 0x4003 +#define AT803X_DEBUG_ADDR 0x1D +#define AT803X_DEBUG_DATA 0x1E +#define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 +#define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) MODULE_DESCRIPTION("Atheros 803x PHY driver"); MODULE_AUTHOR("Matus Ujhelyi"); MODULE_LICENSE("GPL"); -static void at803x_set_wol_mac_addr(struct phy_device *phydev) +static int at803x_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; + int ret; + u32 value; unsigned int i, offsets[] = { AT803X_LOC_MAC_ADDR_32_47_OFFSET, AT803X_LOC_MAC_ADDR_16_31_OFFSET, @@ -43,30 +50,61 @@ static void at803x_set_wol_mac_addr(struct phy_device *phydev) }; if (!ndev) - return; + return -ENODEV; - mac = (const u8 *) ndev->dev_addr; + if (wol->wolopts & WAKE_MAGIC) { + mac = (const u8 *) ndev->dev_addr; - if (!is_valid_ether_addr(mac)) - return; + if (!is_valid_ether_addr(mac)) + return -EFAULT; - for (i = 0; i < 3; i++) { - phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, + for (i = 0; i < 3; i++) { + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, AT803X_DEVICE_ADDR); - phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, offsets[i]); - phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, AT803X_FUNC_DATA); - phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, + phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); + } + + value = phy_read(phydev, AT803X_INTR_ENABLE); + value |= AT803X_WOL_ENABLE; + ret = phy_write(phydev, AT803X_INTR_ENABLE, value); + if (ret) + return ret; + value = phy_read(phydev, AT803X_INTR_STATUS); + } else { + value = phy_read(phydev, AT803X_INTR_ENABLE); + value &= (~AT803X_WOL_ENABLE); + ret = phy_write(phydev, AT803X_INTR_ENABLE, value); + if (ret) + return ret; + value = phy_read(phydev, AT803X_INTR_STATUS); } + + return ret; +} + +static void at803x_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + u32 value; + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + value = phy_read(phydev, AT803X_INTR_ENABLE); + if (value & AT803X_WOL_ENABLE) + wol->wolopts |= WAKE_MAGIC; } static int at803x_config_init(struct phy_device *phydev) { int val; + int ret; u32 features; - int status; features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | SUPPORTED_BNC; @@ -100,20 +138,29 @@ static int at803x_config_init(struct phy_device *phydev) phydev->supported = features; phydev->advertising = features; - /* enable WOL */ - at803x_set_wol_mac_addr(phydev); - status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE); - status = phy_read(phydev, AT803X_INTR_STATUS); + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + ret = phy_write(phydev, AT803X_DEBUG_ADDR, + AT803X_DEBUG_SYSTEM_MODE_CTRL); + if (ret) + return ret; + ret = phy_write(phydev, AT803X_DEBUG_DATA, + AT803X_DEBUG_RGMII_TX_CLK_DLY); + if (ret) + return ret; + } return 0; } -/* ATHEROS 8035 */ -static struct phy_driver at8035_driver = { +static struct phy_driver at803x_driver[] = { +{ + /* ATHEROS 8035 */ .phy_id = 0x004dd072, .name = "Atheros 8035 ethernet", .phy_id_mask = 0xffffffef, .config_init = at803x_config_init, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_aneg = &genphy_config_aneg, @@ -121,14 +168,14 @@ static struct phy_driver at8035_driver = { .driver = { .owner = THIS_MODULE, }, -}; - -/* ATHEROS 8030 */ -static struct phy_driver at8030_driver = { +}, { + /* ATHEROS 8030 */ .phy_id = 0x004dd076, .name = "Atheros 8030 ethernet", .phy_id_mask = 0xffffffef, .config_init = at803x_config_init, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_aneg = &genphy_config_aneg, @@ -136,32 +183,33 @@ static struct phy_driver at8030_driver = { .driver = { .owner = THIS_MODULE, }, -}; +}, { + /* ATHEROS 8031 */ + .phy_id = 0x004dd074, + .name = "Atheros 8031 ethernet", + .phy_id_mask = 0xffffffef, + .config_init = at803x_config_init, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .driver = { + .owner = THIS_MODULE, + }, +} }; static int __init atheros_init(void) { - int ret; - - ret = phy_driver_register(&at8035_driver); - if (ret) - goto err1; - - ret = phy_driver_register(&at8030_driver); - if (ret) - goto err2; - - return 0; - -err2: - phy_driver_unregister(&at8035_driver); -err1: - return ret; + return phy_drivers_register(at803x_driver, + ARRAY_SIZE(at803x_driver)); } static void __exit atheros_exit(void) { - phy_driver_unregister(&at8035_driver); - phy_driver_unregister(&at8030_driver); + return phy_drivers_unregister(at803x_driver, + ARRAY_SIZE(at803x_driver)); } module_init(atheros_init); diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 84c7a39b1c6..ac55b080785 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -78,7 +78,7 @@ static struct phy_driver bcm63xx_driver[] = { .name = "Broadcom BCM63XX (1)", /* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .flags = PHY_HAS_INTERRUPT, + .flags = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL, .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -91,7 +91,7 @@ static struct phy_driver bcm63xx_driver[] = { .phy_id_mask = 0xfffffc00, .name = "Broadcom BCM63XX (2)", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .flags = PHY_HAS_INTERRUPT, + .flags = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL, .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 202fe1ff198..2e91477362d 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -116,6 +116,8 @@ #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 #define MII_M1011_PHY_STATUS_LINK 0x0400 +#define MII_M1116R_CONTROL_REG_MAC 21 + MODULE_DESCRIPTION("Marvell PHY driver"); MODULE_AUTHOR("Andy Fleming"); @@ -372,6 +374,66 @@ static int m88e1318_config_aneg(struct phy_device *phydev) return m88e1121_config_aneg(phydev); } +static int m88e1510_config_aneg(struct phy_device *phydev) +{ + int err; + + err = m88e1318_config_aneg(phydev); + if (err < 0) + return err; + + return marvell_of_reg_init(phydev); +} + +static int m88e1116r_config_init(struct phy_device *phydev) +{ + int temp; + int err; + + temp = phy_read(phydev, MII_BMCR); + temp |= BMCR_RESET; + err = phy_write(phydev, MII_BMCR, temp); + if (err < 0) + return err; + + mdelay(500); + + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0); + if (err < 0) + return err; + + temp = phy_read(phydev, MII_M1011_PHY_SCR); + temp |= (7 << 12); /* max number of gigabit attempts */ + temp |= (1 << 11); /* enable downshift */ + temp |= MII_M1011_PHY_SCR_AUTO_CROSS; + err = phy_write(phydev, MII_M1011_PHY_SCR, temp); + if (err < 0) + return err; + + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 2); + if (err < 0) + return err; + temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC); + temp |= (1 << 5); + temp |= (1 << 4); + err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp); + if (err < 0) + return err; + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0); + if (err < 0) + return err; + + temp = phy_read(phydev, MII_BMCR); + temp |= BMCR_RESET; + err = phy_write(phydev, MII_BMCR, temp); + if (err < 0) + return err; + + mdelay(500); + + return 0; +} + static int m88e1111_config_init(struct phy_device *phydev) { int err; @@ -940,6 +1002,32 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .driver = { .owner = THIS_MODULE }, }, + { + .phy_id = MARVELL_PHY_ID_88E1116R, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1116R", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &m88e1116r_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, + .driver = { .owner = THIS_MODULE }, + }, + { + .phy_id = MARVELL_PHY_ID_88E1510, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1510", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &m88e1510_config_aneg, + .read_status = &marvell_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, + .did_interrupt = &m88e1121_did_interrupt, + .driver = { .owner = THIS_MODULE }, + }, }; static int __init marvell_init(void) @@ -958,15 +1046,17 @@ module_init(marvell_init); module_exit(marvell_exit); static struct mdio_device_id __maybe_unused marvell_tbl[] = { - { 0x01410c60, 0xfffffff0 }, - { 0x01410c90, 0xfffffff0 }, - { 0x01410cc0, 0xfffffff0 }, - { 0x01410e10, 0xfffffff0 }, - { 0x01410cb0, 0xfffffff0 }, - { 0x01410cd0, 0xfffffff0 }, - { 0x01410e50, 0xfffffff0 }, - { 0x01410e30, 0xfffffff0 }, - { 0x01410e90, 0xfffffff0 }, + { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK }, { } }; diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c new file mode 100644 index 00000000000..61d3f4ebf52 --- /dev/null +++ b/drivers/net/phy/mdio-sun4i.c @@ -0,0 +1,194 @@ +/* + * Allwinner EMAC MDIO interface driver + * + * Copyright 2012-2013 Stefan Roese <sr@denx.de> + * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * Based on the Linux driver provided by Allwinner: + * Copyright (C) 1997 Sten Wang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_address.h> +#include <linux/of_mdio.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#define EMAC_MAC_MCMD_REG (0x00) +#define EMAC_MAC_MADR_REG (0x04) +#define EMAC_MAC_MWTD_REG (0x08) +#define EMAC_MAC_MRDD_REG (0x0c) +#define EMAC_MAC_MIND_REG (0x10) +#define EMAC_MAC_SSRR_REG (0x14) + +#define MDIO_TIMEOUT (msecs_to_jiffies(100)) + +struct sun4i_mdio_data { + void __iomem *membase; + struct regulator *regulator; +}; + +static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct sun4i_mdio_data *data = bus->priv; + unsigned long start_jiffies; + int value; + + /* issue the phy address and reg */ + writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG); + /* pull up the phy io line */ + writel(0x1, data->membase + EMAC_MAC_MCMD_REG); + + /* Wait read complete */ + start_jiffies = jiffies; + while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) { + if (time_after(start_jiffies, + start_jiffies + MDIO_TIMEOUT)) + return -ETIMEDOUT; + msleep(1); + } + + /* push down the phy io line */ + writel(0x0, data->membase + EMAC_MAC_MCMD_REG); + /* and read data */ + value = readl(data->membase + EMAC_MAC_MRDD_REG); + + return value; +} + +static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum, + u16 value) +{ + struct sun4i_mdio_data *data = bus->priv; + unsigned long start_jiffies; + + /* issue the phy address and reg */ + writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG); + /* pull up the phy io line */ + writel(0x1, data->membase + EMAC_MAC_MCMD_REG); + + /* Wait read complete */ + start_jiffies = jiffies; + while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) { + if (time_after(start_jiffies, + start_jiffies + MDIO_TIMEOUT)) + return -ETIMEDOUT; + msleep(1); + } + + /* push down the phy io line */ + writel(0x0, data->membase + EMAC_MAC_MCMD_REG); + /* and write data */ + writel(value, data->membase + EMAC_MAC_MWTD_REG); + + return 0; +} + +static int sun4i_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +static int sun4i_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mii_bus *bus; + struct sun4i_mdio_data *data; + int ret, i; + + bus = mdiobus_alloc_size(sizeof(*data)); + if (!bus) + return -ENOMEM; + + bus->name = "sun4i_mii_bus"; + bus->read = &sun4i_mdio_read; + bus->write = &sun4i_mdio_write; + bus->reset = &sun4i_mdio_reset; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); + bus->parent = &pdev->dev; + + bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!bus->irq) { + ret = -ENOMEM; + goto err_out_free_mdiobus; + } + + for (i = 0; i < PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_POLL; + + data = bus->priv; + data->membase = of_iomap(np, 0); + if (!data->membase) { + ret = -ENOMEM; + goto err_out_free_mdio_irq; + } + + data->regulator = devm_regulator_get(&pdev->dev, "phy"); + if (IS_ERR(data->regulator)) { + if (PTR_ERR(data->regulator) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_info(&pdev->dev, "no regulator found\n"); + } else { + ret = regulator_enable(data->regulator); + if (ret) + goto err_out_free_mdio_irq; + } + + ret = of_mdiobus_register(bus, np); + if (ret < 0) + goto err_out_disable_regulator; + + platform_set_drvdata(pdev, bus); + + return 0; + +err_out_disable_regulator: + regulator_disable(data->regulator); +err_out_free_mdio_irq: + kfree(bus->irq); +err_out_free_mdiobus: + mdiobus_free(bus); + return ret; +} + +static int sun4i_mdio_remove(struct platform_device *pdev) +{ + struct mii_bus *bus = platform_get_drvdata(pdev); + + mdiobus_unregister(bus); + kfree(bus->irq); + mdiobus_free(bus); + + return 0; +} + +static const struct of_device_id sun4i_mdio_dt_ids[] = { + { .compatible = "allwinner,sun4i-mdio" }, + { } +}; +MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids); + +static struct platform_driver sun4i_mdio_driver = { + .probe = sun4i_mdio_probe, + .remove = sun4i_mdio_remove, + .driver = { + .name = "sun4i-mdio", + .of_match_table = sun4i_mdio_dt_ids, + }, +}; + +module_platform_driver(sun4i_mdio_driver); + +MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 38f0b312ff8..36c6994436b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -294,7 +294,8 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) cmd->duplex = phydev->duplex; cmd->port = PORT_MII; cmd->phy_address = phydev->addr; - cmd->transceiver = XCVR_EXTERNAL; + cmd->transceiver = phy_is_internal(phydev) ? + XCVR_INTERNAL : XCVR_EXTERNAL; cmd->autoneg = phydev->autoneg; return 0; @@ -419,8 +420,6 @@ out_unlock: EXPORT_SYMBOL(phy_start_aneg); -static void phy_change(struct work_struct *work); - /** * phy_start_machine - start PHY state machine tracking * @phydev: the phy_device struct @@ -439,7 +438,7 @@ void phy_start_machine(struct phy_device *phydev, { phydev->adjust_state = handler; - schedule_delayed_work(&phydev->state_queue, HZ); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ); } /** @@ -500,7 +499,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) disable_irq_nosync(irq); atomic_inc(&phydev->irq_disable); - schedule_work(&phydev->phy_queue); + queue_work(system_power_efficient_wq, &phydev->phy_queue); return IRQ_HANDLED; } @@ -565,8 +564,6 @@ int phy_start_interrupts(struct phy_device *phydev) { int err = 0; - INIT_WORK(&phydev->phy_queue, phy_change); - atomic_set(&phydev->irq_disable, 0); if (request_irq(phydev->irq, phy_interrupt, IRQF_SHARED, @@ -623,7 +620,7 @@ EXPORT_SYMBOL(phy_stop_interrupts); * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes * @work: work_struct that describes the work to be done */ -static void phy_change(struct work_struct *work) +void phy_change(struct work_struct *work) { int err; struct phy_device *phydev = @@ -655,7 +652,7 @@ static void phy_change(struct work_struct *work) /* reschedule state queue work to run as soon as possible */ cancel_delayed_work_sync(&phydev->state_queue); - schedule_delayed_work(&phydev->state_queue, 0); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0); return; @@ -682,7 +679,7 @@ void phy_stop(struct phy_device *phydev) if (PHY_HALTED == phydev->state) goto out_unlock; - if (phydev->irq != PHY_POLL) { + if (phy_interrupt_is_valid(phydev)) { /* Disable PHY Interrupts */ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); @@ -828,8 +825,9 @@ void phy_state_machine(struct work_struct *work) break; case PHY_RUNNING: /* Only register a CHANGE if we are - * polling */ - if (PHY_POLL == phydev->irq) + * polling or ignoring interrupts + */ + if (!phy_interrupt_is_valid(phydev)) phydev->state = PHY_CHANGELINK; break; case PHY_CHANGELINK: @@ -848,7 +846,7 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); - if (PHY_POLL != phydev->irq) + if (phy_interrupt_is_valid(phydev)) err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); break; @@ -918,8 +916,17 @@ void phy_state_machine(struct work_struct *work) if (err < 0) phy_error(phydev); - schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, + PHY_STATE_TIME * HZ); +} + +void phy_mac_interrupt(struct phy_device *phydev, int new_link) +{ + cancel_work_sync(&phydev->phy_queue); + phydev->link = new_link; + schedule_work(&phydev->phy_queue); } +EXPORT_SYMBOL(phy_mac_interrupt); static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, int addr) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3657b4a2912..74630e94fa3 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -189,6 +189,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); + INIT_WORK(&dev->phy_queue, phy_change); /* Request the appropriate module unconditionally; don't bother trying to do so only if it isn't already loaded, @@ -1009,10 +1010,16 @@ static int phy_probe(struct device *dev) phydrv = to_phy_driver(drv); phydev->drv = phydrv; - /* Disable the interrupt if the PHY doesn't support it */ - if (!(phydrv->flags & PHY_HAS_INTERRUPT)) + /* Disable the interrupt if the PHY doesn't support it + * but the interrupt is still a valid one + */ + if (!(phydrv->flags & PHY_HAS_INTERRUPT) && + phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL; + if (phydrv->flags & PHY_IS_INTERNAL) + phydev->is_internal = true; + mutex_lock(&phydev->lock); /* Start out supporting everything. Eventually, diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index d11c93e69e0..f3bea134602 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -354,19 +354,7 @@ static struct spi_driver ks8995_driver = { .remove = ks8995_remove, }; -static int __init ks8995_init(void) -{ - pr_info(DRV_DESC " version " DRV_VERSION "\n"); - - return spi_register_driver(&ks8995_driver); -} -module_init(ks8995_init); - -static void __exit ks8995_exit(void) -{ - spi_unregister_driver(&ks8995_driver); -} -module_exit(ks8995_exit); +module_spi_driver(ks8995_driver); MODULE_DESCRIPTION(DRV_DESC); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 3492b539127..69b482bce7d 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -44,18 +44,19 @@ #define MII_VSC8244_ISTAT_DUPLEX 0x1000 /* Vitesse Auxiliary Control/Status Register */ -#define MII_VSC8244_AUX_CONSTAT 0x1c -#define MII_VSC8244_AUXCONSTAT_INIT 0x0000 -#define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 -#define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 -#define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 -#define MII_VSC8244_AUXCONSTAT_100 0x0008 +#define MII_VSC8244_AUX_CONSTAT 0x1c +#define MII_VSC8244_AUXCONSTAT_INIT 0x0000 +#define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 +#define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 +#define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 +#define MII_VSC8244_AUXCONSTAT_100 0x0008 #define MII_VSC8221_AUXCONSTAT_INIT 0x0004 /* need to set this bit? */ #define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004 #define PHY_ID_VSC8244 0x000fc6c0 #define PHY_ID_VSC8221 0x000fc550 +#define PHY_ID_VSC8211 0x000fc4b0 MODULE_DESCRIPTION("Vitesse PHY driver"); MODULE_AUTHOR("Kriston Carson"); @@ -100,9 +101,8 @@ static int vsc824x_config_init(struct phy_device *phydev) static int vsc824x_ack_interrupt(struct phy_device *phydev) { int err = 0; - - /* - * Don't bother to ACK the interrupts if interrupts + + /* Don't bother to ACK the interrupts if interrupts * are disabled. The 824x cannot clear the interrupts * if they are disabled. */ @@ -122,8 +122,7 @@ static int vsc82xx_config_intr(struct phy_device *phydev) MII_VSC8244_IMASK_MASK : MII_VSC8221_IMASK_MASK); else { - /* - * The Vitesse PHY cannot clear the interrupt + /* The Vitesse PHY cannot clear the interrupt * once it has disabled them, so we clear them first */ err = phy_read(phydev, MII_VSC8244_ISTAT); @@ -146,7 +145,8 @@ static int vsc8221_config_init(struct phy_device *phydev) return err; /* Perhaps we should set EXT_CON1 based on the interface? - Options are 802.3Z SerDes or SGMII */ + * Options are 802.3Z SerDes or SGMII + */ } /* Vitesse 824x */ @@ -176,6 +176,19 @@ static struct phy_driver vsc82xx_driver[] = { .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, .driver = { .owner = THIS_MODULE,}, +}, { + /* Vitesse 8211 */ + .phy_id = PHY_ID_VSC8211, + .phy_id_mask = 0x000ffff0, + .name = "Vitesse VSC8211", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &vsc8221_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc824x_ack_interrupt, + .config_intr = &vsc82xx_config_intr, + .driver = { .owner = THIS_MODULE,}, } }; static int __init vsc82xx_init(void) @@ -196,6 +209,7 @@ module_exit(vsc82xx_exit); static struct mdio_device_id __maybe_unused vitesse_tbl[] = { { PHY_ID_VSC8244, 0x000fffc0 }, { PHY_ID_VSC8221, 0x000ffff0 }, + { PHY_ID_VSC8211, 0x000ffff0 }, { } }; diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index bb07ba94c3a..5f66e30d982 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -338,7 +338,7 @@ static void pppoe_flush_dev(struct net_device *dev) static int pppoe_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); /* Only look at sockets that are using this specific device. */ switch (event) { diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index f433b594388..6d1f6ed3113 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -208,6 +208,17 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (nets[rnet->mport->id].active[destid]) rionet_queue_tx_msg(skb, ndev, nets[rnet->mport->id].active[destid]); + else { + /* + * If the target device was removed from the list of + * active peers but we still have TX packets targeting + * it just report sending a packet to the target + * (without actual packet transfer). + */ + dev_kfree_skb_any(skb); + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + } } spin_unlock_irqrestore(&rnet->tx_lock, flags); @@ -385,24 +396,28 @@ static int rionet_close(struct net_device *ndev) return 0; } -static void rionet_remove(struct rio_dev *rdev) +static int rionet_remove_dev(struct device *dev, struct subsys_interface *sif) { - struct net_device *ndev = rio_get_drvdata(rdev); + struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; struct rionet_peer *peer, *tmp; - unregister_netdev(ndev); - - free_pages((unsigned long)nets[netid].active, get_order(sizeof(void *) * - RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size))); - nets[netid].active = NULL; + if (dev_rionet_capable(rdev)) { + list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) { + if (peer->rdev == rdev) { + if (nets[netid].active[rdev->destid]) { + nets[netid].active[rdev->destid] = NULL; + nets[netid].nact--; + } - list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) { - list_del(&peer->node); - kfree(peer); + list_del(&peer->node); + kfree(peer); + break; + } + } } - free_netdev(ndev); + return 0; } static void rionet_get_drvinfo(struct net_device *ndev, @@ -503,12 +518,13 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) static unsigned long net_table[RIONET_MAX_NETS/sizeof(unsigned long) + 1]; -static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) +static int rionet_add_dev(struct device *dev, struct subsys_interface *sif) { int rc = -ENODEV; u32 lsrc_ops, ldst_ops; struct rionet_peer *peer; struct net_device *ndev = NULL; + struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; int oldnet; @@ -518,8 +534,9 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) oldnet = test_and_set_bit(netid, net_table); /* - * First time through, make sure local device is rionet - * capable, setup netdev (will be skipped on later probes) + * If first time through this net, make sure local device is rionet + * capable and setup netdev (this step will be skipped in later probes + * on the same net). */ if (!oldnet) { rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR, @@ -541,6 +558,12 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) } nets[netid].ndev = ndev; rc = rionet_setup_netdev(rdev->net->hport, ndev); + if (rc) { + printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n", + DRV_NAME, rc); + goto out; + } + INIT_LIST_HEAD(&nets[netid].peers); nets[netid].nact = 0; } else if (nets[netid].ndev == NULL) @@ -559,31 +582,61 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) list_add_tail(&peer->node, &nets[netid].peers); } - rio_set_drvdata(rdev, nets[netid].ndev); - - out: + return 0; +out: return rc; } +#ifdef MODULE static struct rio_device_id rionet_id_table[] = { - {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)} + {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)}, + { 0, } /* terminate list */ }; -static struct rio_driver rionet_driver = { - .name = "rionet", - .id_table = rionet_id_table, - .probe = rionet_probe, - .remove = rionet_remove, +MODULE_DEVICE_TABLE(rapidio, rionet_id_table); +#endif + +static struct subsys_interface rionet_interface = { + .name = "rionet", + .subsys = &rio_bus_type, + .add_dev = rionet_add_dev, + .remove_dev = rionet_remove_dev, }; static int __init rionet_init(void) { - return rio_register_driver(&rionet_driver); + return subsys_interface_register(&rionet_interface); } static void __exit rionet_exit(void) { - rio_unregister_driver(&rionet_driver); + struct rionet_private *rnet; + struct net_device *ndev; + struct rionet_peer *peer, *tmp; + int i; + + for (i = 0; i < RIONET_MAX_NETS; i++) { + if (nets[i].ndev != NULL) { + ndev = nets[i].ndev; + rnet = netdev_priv(ndev); + unregister_netdev(ndev); + + list_for_each_entry_safe(peer, + tmp, &nets[i].peers, node) { + list_del(&peer->node); + kfree(peer); + } + + free_pages((unsigned long)nets[i].active, + get_order(sizeof(void *) * + RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size))); + nets[i].active = NULL; + + free_netdev(ndev); + } + } + + subsys_interface_unregister(&rionet_interface); } late_initcall(rionet_init); diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index b3051052f3a..bff7e0b0b4e 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -525,31 +525,26 @@ static void team_set_no_mode(struct team *team) team->mode = &__team_no_mode; } -static void __team_adjust_ops(struct team *team, int en_port_count) +static void team_adjust_ops(struct team *team) { /* * To avoid checks in rx/tx skb paths, ensure here that non-null and * correct ops are always set. */ - if (!en_port_count || !team_is_mode_set(team) || + if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->transmit) team->ops.transmit = team_dummy_transmit; else team->ops.transmit = team->mode->ops->transmit; - if (!en_port_count || !team_is_mode_set(team) || + if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->receive) team->ops.receive = team_dummy_receive; else team->ops.receive = team->mode->ops->receive; } -static void team_adjust_ops(struct team *team) -{ - __team_adjust_ops(team, team->en_port_count); -} - /* * We can benefit from the fact that it's ensured no port is present * at the time of mode change. Therefore no packets are in fly so there's no @@ -725,9 +720,9 @@ static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb) static void __team_queue_override_port_del(struct team *team, struct team_port *port) { + if (!port->queue_id) + return; list_del_rcu(&port->qom_list); - synchronize_rcu(); - INIT_LIST_HEAD(&port->qom_list); } static bool team_queue_override_port_has_gt_prio_than(struct team_port *port, @@ -749,9 +744,8 @@ static void __team_queue_override_port_add(struct team *team, struct list_head *qom_list; struct list_head *node; - if (!port->queue_id || !team_port_enabled(port)) + if (!port->queue_id) return; - qom_list = __team_get_qom_list(team, port->queue_id); node = qom_list; list_for_each_entry(cur, qom_list, qom_list) { @@ -768,7 +762,7 @@ static void __team_queue_override_enabled_check(struct team *team) bool enabled = false; list_for_each_entry(port, &team->port_list, list) { - if (!list_empty(&port->qom_list)) { + if (port->queue_id) { enabled = true; break; } @@ -780,14 +774,44 @@ static void __team_queue_override_enabled_check(struct team *team) team->queue_override_enabled = enabled; } -static void team_queue_override_port_refresh(struct team *team, - struct team_port *port) +static void team_queue_override_port_prio_changed(struct team *team, + struct team_port *port) { + if (!port->queue_id || team_port_enabled(port)) + return; __team_queue_override_port_del(team, port); __team_queue_override_port_add(team, port); __team_queue_override_enabled_check(team); } +static void team_queue_override_port_change_queue_id(struct team *team, + struct team_port *port, + u16 new_queue_id) +{ + if (team_port_enabled(port)) { + __team_queue_override_port_del(team, port); + port->queue_id = new_queue_id; + __team_queue_override_port_add(team, port); + __team_queue_override_enabled_check(team); + } else { + port->queue_id = new_queue_id; + } +} + +static void team_queue_override_port_add(struct team *team, + struct team_port *port) +{ + __team_queue_override_port_add(team, port); + __team_queue_override_enabled_check(team); +} + +static void team_queue_override_port_del(struct team *team, + struct team_port *port) +{ + __team_queue_override_port_del(team, port); + __team_queue_override_enabled_check(team); +} + /**************** * Port handling @@ -819,7 +843,7 @@ static void team_port_enable(struct team *team, hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); team_adjust_ops(team); - team_queue_override_port_refresh(team, port); + team_queue_override_port_add(team, port); if (team->ops.port_enabled) team->ops.port_enabled(team, port); } @@ -848,14 +872,9 @@ static void team_port_disable(struct team *team, hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); port->index = -1; - team_queue_override_port_refresh(team, port); - __team_adjust_ops(team, team->en_port_count - 1); - /* - * Wait until readers see adjusted ops. This ensures that - * readers never see team->en_port_count == 0 - */ - synchronize_rcu(); team->en_port_count--; + team_queue_override_port_del(team, port); + team_adjust_ops(team); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -1163,8 +1182,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) team_port_set_orig_dev_addr(port); dev_set_mtu(port_dev, port->orig.mtu); - synchronize_rcu(); - kfree(port); + kfree_rcu(port, rcu); netdev_info(dev, "Port device %s removed\n", portname); __team_compute_features(team); @@ -1259,9 +1277,12 @@ static int team_priority_option_set(struct team *team, struct team_gsetter_ctx *ctx) { struct team_port *port = ctx->info->port; + s32 priority = ctx->data.s32_val; - port->priority = ctx->data.s32_val; - team_queue_override_port_refresh(team, port); + if (port->priority == priority) + return 0; + port->priority = priority; + team_queue_override_port_prio_changed(team, port); return 0; } @@ -1278,17 +1299,16 @@ static int team_queue_id_option_set(struct team *team, struct team_gsetter_ctx *ctx) { struct team_port *port = ctx->info->port; + u16 new_queue_id = ctx->data.u32_val; - if (port->queue_id == ctx->data.u32_val) + if (port->queue_id == new_queue_id) return 0; - if (ctx->data.u32_val >= team->dev->real_num_tx_queues) + if (new_queue_id >= team->dev->real_num_tx_queues) return -EINVAL; - port->queue_id = ctx->data.u32_val; - team_queue_override_port_refresh(team, port); + team_queue_override_port_change_queue_id(team, port, new_queue_id); return 0; } - static const struct team_option team_options[] = { { .name = "mode", @@ -2648,7 +2668,7 @@ static void team_port_change_check(struct team_port *port, bool linkup) static int team_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *) ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct team_port *port; port = team_port_get_rtnl(dev); diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index cdc31b5ea15..829a9cd2b4d 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -112,9 +112,8 @@ static struct team_port *lb_hash_select_tx_port(struct team *team, struct sk_buff *skb, unsigned char hash) { - int port_index; + int port_index = team_num_to_port_index(team, hash); - port_index = hash % team->en_port_count; return team_get_port_by_index_rcu(team, port_index); } diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 472623f8ce3..53665850b59 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -30,7 +30,8 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb) struct team_port *port; int port_index; - port_index = rr_priv(team)->sent_packets++ % team->en_port_count; + port_index = team_num_to_port_index(team, + rr_priv(team)->sent_packets++); port = team_get_port_by_index_rcu(team, port_index); if (unlikely(!port)) goto drop; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9c61f8734a4..7eab5fcd064 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -841,7 +841,7 @@ static const struct net_device_ops tap_netdev_ops = { #endif }; -static int tun_flow_init(struct tun_struct *tun) +static void tun_flow_init(struct tun_struct *tun) { int i; @@ -852,8 +852,6 @@ static int tun_flow_init(struct tun_struct *tun) setup_timer(&tun->flow_gc_timer, tun_flow_cleanup, (unsigned long)tun); mod_timer(&tun->flow_gc_timer, round_jiffies_up(jiffies + tun->ageing_time)); - - return 0; } static void tun_flow_uninit(struct tun_struct *tun) @@ -1532,6 +1530,9 @@ static int tun_flags(struct tun_struct *tun) if (tun->flags & TUN_TAP_MQ) flags |= IFF_MULTI_QUEUE; + if (tun->flags & TUN_PERSIST) + flags |= IFF_PERSIST; + return flags; } @@ -1661,10 +1662,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) goto err_free_dev; tun_net_init(dev); - - err = tun_flow_init(tun); - if (err < 0) - goto err_free_dev; + tun_flow_init(tun); dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | TUN_USER_FEATURES; diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 287cc624b90..d84bfd4109a 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -67,7 +67,6 @@ config USB_KAWETH config USB_PEGASUS tristate "USB Pegasus/Pegasus-II based ethernet device support" - select NET_CORE select MII ---help--- Say Y here if you know you have Pegasus or Pegasus-II based adapter. @@ -83,7 +82,6 @@ config USB_PEGASUS config USB_RTL8150 tristate "USB RTL8150 based ethernet device support" - select NET_CORE select MII help Say Y here if you have RTL8150 based usb-ethernet adapter. @@ -95,7 +93,6 @@ config USB_RTL8150 config USB_RTL8152 tristate "Realtek RTL8152 Based USB 2.0 Ethernet Adapters" - select NET_CORE select MII help This option adds support for Realtek RTL8152 based USB 2.0 @@ -106,7 +103,6 @@ config USB_RTL8152 config USB_USBNET tristate "Multi-purpose USB Networking Framework" - select NET_CORE select MII ---help--- This driver supports several kinds of network links over USB, diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index bd8758fa38c..1e3c302d94f 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1371,7 +1371,7 @@ static int ax88179_stop(struct usbnet *dev) } static const struct driver_info ax88179_info = { - .description = "ASIX AX88179 USB 3.0 Gigibit Ethernet", + .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet", .bind = ax88179_bind, .unbind = ax88179_unbind, .status = ax88179_status, @@ -1384,7 +1384,7 @@ static const struct driver_info ax88179_info = { }; static const struct driver_info ax88178a_info = { - .description = "ASIX AX88178A USB 2.0 Gigibit Ethernet", + .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet", .bind = ax88179_bind, .unbind = ax88179_unbind, .status = ax88179_status, @@ -1433,6 +1433,7 @@ static struct usb_driver ax88179_178a_driver = { .probe = usbnet_probe, .suspend = ax88179_suspend, .resume = ax88179_resume, + .reset_resume = ax88179_resume, .disconnect = usbnet_disconnect, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 04ee044dde5..4393f148312 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -215,6 +215,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) goto bad_desc; } + /* some devices merge these - skip class check */ + if (info->control == info->data) + goto next_desc; + /* a data interface altsetting does the real i/o */ d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { @@ -304,19 +308,23 @@ next_desc: /* claim data interface and set it up ... with side effects. * network traffic can't flow until an altsetting is enabled. */ - status = usb_driver_claim_interface(driver, info->data, dev); - if (status < 0) - return status; + if (info->data != info->control) { + status = usb_driver_claim_interface(driver, info->data, dev); + if (status < 0) + return status; + } status = usbnet_get_endpoints(dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); - usb_driver_release_interface(driver, info->data); + if (info->data != info->control) + usb_driver_release_interface(driver, info->data); return status; } /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ - dev->status = NULL; + if (info->data != info->control) + dev->status = NULL; if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { struct usb_endpoint_descriptor *desc; @@ -349,6 +357,10 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf) struct cdc_state *info = (void *) &dev->data; struct usb_driver *driver = driver_of(intf); + /* combined interface - nothing to do */ + if (info->data == info->control) + return; + /* disconnect master --> disconnect slave */ if (intf == info->control && info->data) { /* ensure immediate exit from usbnet_disconnect */ diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 534d8becbbd..ff8594d8dd2 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -60,6 +60,7 @@ #define USB_PRODUCT_IPHONE_3GS 0x1294 #define USB_PRODUCT_IPHONE_4 0x1297 #define USB_PRODUCT_IPAD 0x129a +#define USB_PRODUCT_IPAD_MINI 0x12ab #define USB_PRODUCT_IPHONE_4_VZW 0x129c #define USB_PRODUCT_IPHONE_4S 0x12a0 #define USB_PRODUCT_IPHONE_5 0x12a8 @@ -107,6 +108,10 @@ static struct usb_device_id ipheth_table[] = { IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, IPHETH_USBINTF_PROTO) }, { USB_DEVICE_AND_INTERFACE_INFO( + USB_VENDOR_APPLE, USB_PRODUCT_IPAD_MINI, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO) }, + { USB_DEVICE_AND_INTERFACE_INFO( USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW, IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, IPHETH_USBINTF_PROTO) }, diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index 0192073e53a..6866eae3e38 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -221,12 +221,9 @@ done: memset(skb_put(skb, padlen), 0, padlen); } - netdev_dbg( - dev->net, - "Sending package with length %i and padding %i. Header: %02x:%02x:%02x:%02x:%02x:%02x.", - content_len, padlen, header_start[0], header_start[1], - header_start[2], header_start[3], header_start[4], - header_start[5]); + netdev_dbg(dev->net, + "Sending package with length %i and padding %i. Header: %6phC.", + content_len, padlen, header_start); return skb; } @@ -263,32 +260,23 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp( header_start, EXPECTED_UNKNOWN_HEADER_2, sizeof(EXPECTED_UNKNOWN_HEADER_2))) { - netdev_dbg( - dev->net, - "Received expected unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], - header_start[2], header_start[3], - header_start[4], header_start[5], + netdev_dbg(dev->net, + "Received expected unknown frame header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); } else { - netdev_err( - dev->net, - "Received unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], - header_start[2], header_start[3], - header_start[4], header_start[5], + netdev_err(dev->net, + "Received unknown frame header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); return 0; } } else - netdev_dbg( - dev->net, - "Received header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], header_start[2], - header_start[3], header_start[4], header_start[5], - skb->len - KALMIA_HEADER_LENGTH); + netdev_dbg(dev->net, + "Received header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); /* subtract start header and end header */ usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); @@ -310,12 +298,9 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) sizeof(HEADER_END_OF_USB_PACKET)) == 0); if (!is_last) { header_start = skb->data + ether_packet_length; - netdev_dbg( - dev->net, - "End header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], - header_start[2], header_start[3], - header_start[4], header_start[5], + netdev_dbg(dev->net, + "End header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); } } diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 56459215a22..606eba2872b 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -523,6 +523,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x0002, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0012, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0017, 3)}, + {QMI_FIXED_INTF(0x19d2, 0x0019, 3)}, /* ONDA MT689DC */ {QMI_FIXED_INTF(0x19d2, 0x0021, 4)}, {QMI_FIXED_INTF(0x19d2, 0x0025, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0031, 4)}, @@ -582,6 +583,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ + {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ {QMI_FIXED_INTF(0x1e2d, 0x12d1, 4)}, /* Cinterion PLxx */ @@ -618,6 +620,7 @@ static const struct usb_device_id products[] = { {QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */ {QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */ {QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */ + {QMI_GOBI_DEVICE(0x0af0, 0x8120)}, /* Option GTM681W */ {QMI_GOBI_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ {QMI_GOBI_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */ {QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ @@ -631,7 +634,6 @@ static const struct usb_device_id products[] = { {QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ {QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ {QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */ - {QMI_FIXED_INTF(0x1199, 0x9011, 5)}, /* alternate interface number!? */ {QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ {QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 14e51988863..d02bac82fc5 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -11,7 +11,6 @@ #include <linux/signal.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/version.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/mii.h> @@ -1749,18 +1748,7 @@ static struct usb_driver rtl8152_driver = { .resume = rtl8152_resume }; -static int __init usb_rtl8152_init(void) -{ - return usb_register(&rtl8152_driver); -} - -static void __exit usb_rtl8152_exit(void) -{ - usb_deregister(&rtl8152_driver); -} - -module_init(usb_rtl8152_init); -module_exit(usb_rtl8152_exit); +module_usb_driver(rtl8152_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 177f911f594..da866523cf2 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -379,12 +379,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, else snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); - if (strchr(dev->name, '%')) { - err = dev_alloc_name(dev, dev->name); - if (err < 0) - goto err_alloc_name; - } - err = register_netdevice(dev); if (err < 0) goto err_register_dev; @@ -404,7 +398,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, err_register_dev: /* nothing to do */ -err_alloc_name: err_configure_peer: unregister_netdevice(peer); return err; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index c9e00387d99..3d2a90a6264 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -602,7 +602,7 @@ static int virtnet_poll(struct napi_struct *napi, int budget) container_of(napi, struct receive_queue, napi); struct virtnet_info *vi = rq->vq->vdev->priv; void *buf; - unsigned int len, received = 0; + unsigned int r, len, received = 0; again: while (received < budget && @@ -619,8 +619,9 @@ again: /* Out of packets? */ if (received < budget) { + r = virtqueue_enable_cb_prepare(rq->vq); napi_complete(napi); - if (unlikely(!virtqueue_enable_cb(rq->vq)) && + if (unlikely(virtqueue_poll(rq->vq, r)) && napi_schedule_prep(napi)) { virtqueue_disable_cb(rq->vq); __napi_schedule(napi); @@ -901,7 +902,6 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) struct scatterlist sg; struct virtio_net_ctrl_mq s; struct net_device *dev = vi->dev; - int i; if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ)) return 0; @@ -915,10 +915,8 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) queue_pairs); return -EINVAL; } else { - for (i = vi->curr_queue_pairs; i < queue_pairs; i++) - if (!try_fill_recv(&vi->rq[i], GFP_KERNEL)) - schedule_delayed_work(&vi->refill, 0); vi->curr_queue_pairs = queue_pairs; + schedule_delayed_work(&vi->refill, 0); } return 0; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 57325f356d4..227b54a1f88 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -44,6 +44,8 @@ #define VXLAN_VERSION "0.1" +#define PORT_HASH_BITS 8 +#define PORT_HASH_SIZE (1<<PORT_HASH_BITS) #define VNI_HASH_BITS 10 #define VNI_HASH_SIZE (1<<VNI_HASH_BITS) #define FDB_HASH_BITS 8 @@ -66,30 +68,44 @@ struct vxlanhdr { /* UDP port for VXLAN traffic. * The IANA assigned port is 4789, but the Linux default is 8472 - * for compatability with early adopters. + * for compatibility with early adopters. */ -static unsigned int vxlan_port __read_mostly = 8472; -module_param_named(udp_port, vxlan_port, uint, 0444); +static unsigned short vxlan_port __read_mostly = 8472; +module_param_named(udp_port, vxlan_port, ushort, 0444); MODULE_PARM_DESC(udp_port, "Destination UDP port"); static bool log_ecn_error = true; module_param(log_ecn_error, bool, 0644); MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); -/* per-net private data for this module */ -static unsigned int vxlan_net_id; -struct vxlan_net { - struct socket *sock; /* UDP encap socket */ +static int vxlan_net_id; + +static const u8 all_zeros_mac[ETH_ALEN]; + +/* per UDP socket information */ +struct vxlan_sock { + struct hlist_node hlist; + struct rcu_head rcu; + struct work_struct del_work; + atomic_t refcnt; + struct socket *sock; struct hlist_head vni_list[VNI_HASH_SIZE]; }; +/* per-network namespace private data for this module */ +struct vxlan_net { + struct list_head vxlan_list; + struct hlist_head sock_list[PORT_HASH_SIZE]; + spinlock_t sock_lock; +}; + struct vxlan_rdst { - struct rcu_head rcu; __be32 remote_ip; __be16 remote_port; u32 remote_vni; u32 remote_ifindex; - struct vxlan_rdst *remote_next; + struct list_head list; + struct rcu_head rcu; }; /* Forwarding table entry */ @@ -98,7 +114,7 @@ struct vxlan_fdb { struct rcu_head rcu; unsigned long updated; /* jiffies */ unsigned long used; - struct vxlan_rdst remote; + struct list_head remotes; u16 state; /* see ndm_state */ u8 flags; /* see ndm_flags */ u8 eth_addr[ETH_ALEN]; @@ -106,7 +122,9 @@ struct vxlan_fdb { /* Pseudo network device */ struct vxlan_dev { - struct hlist_node hlist; + struct hlist_node hlist; /* vni hash table */ + struct list_head next; /* vxlan's per namespace list */ + struct vxlan_sock *vn_sock; /* listening socket */ struct net_device *dev; struct vxlan_rdst default_dst; /* default destination */ __be32 saddr; /* source address */ @@ -117,6 +135,9 @@ struct vxlan_dev { __u8 ttl; u32 flags; /* VXLAN_F_* below */ + struct work_struct sock_work; + struct work_struct igmp_work; + unsigned long age_interval; struct timer_list age_timer; spinlock_t hash_lock; @@ -134,20 +155,55 @@ struct vxlan_dev { /* salt for hash table */ static u32 vxlan_salt __read_mostly; +static struct workqueue_struct *vxlan_wq; + +static void vxlan_sock_work(struct work_struct *work); + +/* Virtual Network hash table head */ +static inline struct hlist_head *vni_head(struct vxlan_sock *vs, u32 id) +{ + return &vs->vni_list[hash_32(id, VNI_HASH_BITS)]; +} -static inline struct hlist_head *vni_head(struct net *net, u32 id) +/* Socket hash table head */ +static inline struct hlist_head *vs_head(struct net *net, __be16 port) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); - return &vn->vni_list[hash_32(id, VNI_HASH_BITS)]; + return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; +} + +/* First remote destination for a forwarding entry. + * Guaranteed to be non-NULL because remotes are never deleted. + */ +static inline struct vxlan_rdst *first_remote(struct vxlan_fdb *fdb) +{ + return list_first_or_null_rcu(&fdb->remotes, struct vxlan_rdst, list); +} + +/* Find VXLAN socket based on network namespace and UDP port */ +static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port) +{ + struct vxlan_sock *vs; + + hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) { + if (inet_sk(vs->sock->sk)->inet_sport == port) + return vs; + } + return NULL; } /* Look up VNI in a per net namespace table */ -static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id) +static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port) { + struct vxlan_sock *vs; struct vxlan_dev *vxlan; - hlist_for_each_entry_rcu(vxlan, vni_head(net, id), hlist) { + vs = vxlan_find_port(net, port); + if (!vs) + return NULL; + + hlist_for_each_entry_rcu(vxlan, vni_head(vs, id), hlist) { if (vxlan->default_dst.remote_vni == id) return vxlan; } @@ -157,9 +213,9 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id) /* Fill in neighbour message in skbuff. */ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, - const struct vxlan_fdb *fdb, - u32 portid, u32 seq, int type, unsigned int flags, - const struct vxlan_rdst *rdst) + const struct vxlan_fdb *fdb, + u32 portid, u32 seq, int type, unsigned int flags, + const struct vxlan_rdst *rdst) { unsigned long now = jiffies; struct nda_cacheinfo ci; @@ -197,7 +253,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, nla_put_be16(skb, NDA_PORT, rdst->remote_port)) goto nla_put_failure; if (rdst->remote_vni != vxlan->default_dst.remote_vni && - nla_put_be32(skb, NDA_VNI, rdst->remote_vni)) + nla_put_u32(skb, NDA_VNI, rdst->remote_vni)) goto nla_put_failure; if (rdst->remote_ifindex && nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex)) @@ -230,7 +286,7 @@ static inline size_t vxlan_nlmsg_size(void) } static void vxlan_fdb_notify(struct vxlan_dev *vxlan, - const struct vxlan_fdb *fdb, int type) + struct vxlan_fdb *fdb, int type) { struct net *net = dev_net(vxlan->dev); struct sk_buff *skb; @@ -240,7 +296,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan, if (skb == NULL) goto errout; - err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote); + err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, first_remote(fdb)); if (err < 0) { /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -258,22 +314,27 @@ errout: static void vxlan_ip_miss(struct net_device *dev, __be32 ipa) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_fdb f; + struct vxlan_fdb f = { + .state = NUD_STALE, + }; + struct vxlan_rdst remote = { + .remote_ip = ipa, /* goes to NDA_DST */ + .remote_vni = VXLAN_N_VID, + }; - memset(&f, 0, sizeof f); - f.state = NUD_STALE; - f.remote.remote_ip = ipa; /* goes to NDA_DST */ - f.remote.remote_vni = VXLAN_N_VID; + INIT_LIST_HEAD(&f.remotes); + list_add_rcu(&remote.list, &f.remotes); vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH); } static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN]) { - struct vxlan_fdb f; + struct vxlan_fdb f = { + .state = NUD_STALE, + }; - memset(&f, 0, sizeof f); - f.state = NUD_STALE; + INIT_LIST_HEAD(&f.remotes); memcpy(f.eth_addr, eth_addr, ETH_ALEN); vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH); @@ -328,21 +389,34 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, return f; } -/* Add/update destinations for multicast */ -static int vxlan_fdb_append(struct vxlan_fdb *f, - __be32 ip, __be16 port, __u32 vni, __u32 ifindex) +/* caller should hold vxlan->hash_lock */ +static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f, + __be32 ip, __be16 port, + __u32 vni, __u32 ifindex) { - struct vxlan_rdst *rd_prev, *rd; + struct vxlan_rdst *rd; - rd_prev = NULL; - for (rd = &f->remote; rd; rd = rd->remote_next) { + list_for_each_entry(rd, &f->remotes, list) { if (rd->remote_ip == ip && rd->remote_port == port && rd->remote_vni == vni && rd->remote_ifindex == ifindex) - return 0; - rd_prev = rd; + return rd; } + + return NULL; +} + +/* Add/update destinations for multicast */ +static int vxlan_fdb_append(struct vxlan_fdb *f, + __be32 ip, __be16 port, __u32 vni, __u32 ifindex) +{ + struct vxlan_rdst *rd; + + rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex); + if (rd) + return 0; + rd = kmalloc(sizeof(*rd), GFP_ATOMIC); if (rd == NULL) return -ENOBUFS; @@ -350,8 +424,9 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, rd->remote_port = port; rd->remote_vni = vni; rd->remote_ifindex = ifindex; - rd->remote_next = NULL; - rd_prev->remote_next = rd; + + list_add_tail_rcu(&rd->list, &f->remotes); + return 1; } @@ -383,7 +458,8 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan, notify = 1; } if ((flags & NLM_F_APPEND) && - is_multicast_ether_addr(f->eth_addr)) { + (is_multicast_ether_addr(f->eth_addr) || + is_zero_ether_addr(f->eth_addr))) { int rc = vxlan_fdb_append(f, ip, port, vni, ifindex); if (rc < 0) @@ -403,16 +479,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan, return -ENOMEM; notify = 1; - f->remote.remote_ip = ip; - f->remote.remote_port = port; - f->remote.remote_vni = vni; - f->remote.remote_ifindex = ifindex; - f->remote.remote_next = NULL; f->state = state; f->flags = ndm_flags; f->updated = f->used = jiffies; + INIT_LIST_HEAD(&f->remotes); memcpy(f->eth_addr, mac, ETH_ALEN); + vxlan_fdb_append(f, ip, port, vni, ifindex); + ++vxlan->addrcnt; hlist_add_head_rcu(&f->hlist, vxlan_fdb_head(vxlan, mac)); @@ -424,16 +498,19 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan, return 0; } +static void vxlan_fdb_free_rdst(struct rcu_head *head) +{ + struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu); + kfree(rd); +} + static void vxlan_fdb_free(struct rcu_head *head) { struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu); + struct vxlan_rdst *rd, *nd; - while (f->remote.remote_next) { - struct vxlan_rdst *rd = f->remote.remote_next; - - f->remote.remote_next = rd->remote_next; + list_for_each_entry_safe(rd, nd, &f->remotes, list) kfree(rd); - } kfree(f); } @@ -449,58 +526,77 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f) call_rcu(&f->rcu, vxlan_fdb_free); } -/* Add static entry (via netlink) */ -static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, u16 flags) +static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, + __be32 *ip, __be16 *port, u32 *vni, u32 *ifindex) { - struct vxlan_dev *vxlan = netdev_priv(dev); struct net *net = dev_net(vxlan->dev); - __be32 ip; - __be16 port; - u32 vni, ifindex; - int err; - if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) { - pr_info("RTM_NEWNEIGH with invalid state %#x\n", - ndm->ndm_state); - return -EINVAL; - } - - if (tb[NDA_DST] == NULL) - return -EINVAL; - - if (nla_len(tb[NDA_DST]) != sizeof(__be32)) - return -EAFNOSUPPORT; + if (tb[NDA_DST]) { + if (nla_len(tb[NDA_DST]) != sizeof(__be32)) + return -EAFNOSUPPORT; - ip = nla_get_be32(tb[NDA_DST]); + *ip = nla_get_be32(tb[NDA_DST]); + } else { + *ip = htonl(INADDR_ANY); + } if (tb[NDA_PORT]) { if (nla_len(tb[NDA_PORT]) != sizeof(__be16)) return -EINVAL; - port = nla_get_be16(tb[NDA_PORT]); - } else - port = vxlan->dst_port; + *port = nla_get_be16(tb[NDA_PORT]); + } else { + *port = vxlan->dst_port; + } if (tb[NDA_VNI]) { if (nla_len(tb[NDA_VNI]) != sizeof(u32)) return -EINVAL; - vni = nla_get_u32(tb[NDA_VNI]); - } else - vni = vxlan->default_dst.remote_vni; + *vni = nla_get_u32(tb[NDA_VNI]); + } else { + *vni = vxlan->default_dst.remote_vni; + } if (tb[NDA_IFINDEX]) { struct net_device *tdev; if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32)) return -EINVAL; - ifindex = nla_get_u32(tb[NDA_IFINDEX]); - tdev = dev_get_by_index(net, ifindex); + *ifindex = nla_get_u32(tb[NDA_IFINDEX]); + tdev = dev_get_by_index(net, *ifindex); if (!tdev) return -EADDRNOTAVAIL; dev_put(tdev); - } else - ifindex = 0; + } else { + *ifindex = 0; + } + + return 0; +} + +/* Add static entry (via netlink) */ +static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 flags) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + /* struct net *net = dev_net(vxlan->dev); */ + __be32 ip; + __be16 port; + u32 vni, ifindex; + int err; + + if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) { + pr_info("RTM_NEWNEIGH with invalid state %#x\n", + ndm->ndm_state); + return -EINVAL; + } + + if (tb[NDA_DST] == NULL) + return -EINVAL; + + err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex); + if (err) + return err; spin_lock_bh(&vxlan->hash_lock); err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags, @@ -517,14 +613,43 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_fdb *f; - int err = -ENOENT; + struct vxlan_rdst *rd = NULL; + __be32 ip; + __be16 port; + u32 vni, ifindex; + int err; + + err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex); + if (err) + return err; + + err = -ENOENT; spin_lock_bh(&vxlan->hash_lock); f = vxlan_find_mac(vxlan, addr); - if (f) { - vxlan_fdb_destroy(vxlan, f); - err = 0; + if (!f) + goto out; + + if (ip != htonl(INADDR_ANY)) { + rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex); + if (!rd) + goto out; + } + + err = 0; + + /* remove a destination if it's not the only one on the list, + * otherwise destroy the fdb entry + */ + if (rd && !list_is_singular(&f->remotes)) { + list_del_rcu(&rd->list); + call_rcu(&rd->rcu, vxlan_fdb_free_rdst); + goto out; } + + vxlan_fdb_destroy(vxlan, f); + +out: spin_unlock_bh(&vxlan->hash_lock); return err; @@ -543,23 +668,24 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) { struct vxlan_rdst *rd; - for (rd = &f->remote; rd; rd = rd->remote_next) { - if (idx < cb->args[0]) - goto skip; + if (idx < cb->args[0]) + goto skip; + + list_for_each_entry_rcu(rd, &f->remotes, list) { err = vxlan_fdb_info(skb, vxlan, f, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH, NLM_F_MULTI, rd); if (err < 0) - break; -skip: - ++idx; + goto out; } +skip: + ++idx; } } - +out: return idx; } @@ -575,7 +701,9 @@ static bool vxlan_snoop(struct net_device *dev, f = vxlan_find_mac(vxlan, src_mac); if (likely(f)) { - if (likely(f->remote.remote_ip == src_ip)) + struct vxlan_rdst *rdst = first_remote(f); + + if (likely(rdst->remote_ip == src_ip)) return false; /* Don't migrate static entries, drop packets */ @@ -585,10 +713,11 @@ static bool vxlan_snoop(struct net_device *dev, if (net_ratelimit()) netdev_info(dev, "%pM migrated from %pI4 to %pI4\n", - src_mac, &f->remote.remote_ip, &src_ip); + src_mac, &rdst->remote_ip, &src_ip); - f->remote.remote_ip = src_ip; + rdst->remote_ip = src_ip; f->updated = jiffies; + vxlan_fdb_notify(vxlan, f, RTM_NEWNEIGH); } else { /* learned new entry */ spin_lock(&vxlan->hash_lock); @@ -609,78 +738,61 @@ static bool vxlan_snoop(struct net_device *dev, /* See if multicast group is already in use by other ID */ -static bool vxlan_group_used(struct vxlan_net *vn, - const struct vxlan_dev *this) +static bool vxlan_group_used(struct vxlan_net *vn, __be32 remote_ip) { - const struct vxlan_dev *vxlan; - unsigned h; - - for (h = 0; h < VNI_HASH_SIZE; ++h) - hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) { - if (vxlan == this) - continue; + struct vxlan_dev *vxlan; - if (!netif_running(vxlan->dev)) - continue; + list_for_each_entry(vxlan, &vn->vxlan_list, next) { + if (!netif_running(vxlan->dev)) + continue; - if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip) - return true; - } + if (vxlan->default_dst.remote_ip == remote_ip) + return true; + } return false; } -/* kernel equivalent to IP_ADD_MEMBERSHIP */ -static int vxlan_join_group(struct net_device *dev) +static void vxlan_sock_hold(struct vxlan_sock *vs) { - struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - struct sock *sk = vn->sock->sk; - struct ip_mreqn mreq = { - .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip, - .imr_ifindex = vxlan->default_dst.remote_ifindex, - }; - int err; + atomic_inc(&vs->refcnt); +} - /* Already a member of group */ - if (vxlan_group_used(vn, vxlan)) - return 0; +static void vxlan_sock_release(struct vxlan_net *vn, struct vxlan_sock *vs) +{ + if (!atomic_dec_and_test(&vs->refcnt)) + return; - /* Need to drop RTNL to call multicast join */ - rtnl_unlock(); - lock_sock(sk); - err = ip_mc_join_group(sk, &mreq); - release_sock(sk); - rtnl_lock(); + spin_lock(&vn->sock_lock); + hlist_del_rcu(&vs->hlist); + spin_unlock(&vn->sock_lock); - return err; + queue_work(vxlan_wq, &vs->del_work); } - -/* kernel equivalent to IP_DROP_MEMBERSHIP */ -static int vxlan_leave_group(struct net_device *dev) +/* Callback to update multicast group membership. + * Scheduled when vxlan goes up/down. + */ +static void vxlan_igmp_work(struct work_struct *work) { - struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - int err = 0; - struct sock *sk = vn->sock->sk; + struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_work); + struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id); + struct vxlan_sock *vs = vxlan->vn_sock; + struct sock *sk = vs->sock->sk; struct ip_mreqn mreq = { .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip, .imr_ifindex = vxlan->default_dst.remote_ifindex, }; - /* Only leave group when last vxlan is done. */ - if (vxlan_group_used(vn, vxlan)) - return 0; - - /* Need to drop RTNL to call multicast leave */ - rtnl_unlock(); lock_sock(sk); - err = ip_mc_leave_group(sk, &mreq); + if (vxlan_group_used(vn, vxlan->default_dst.remote_ip)) + ip_mc_join_group(sk, &mreq); + else + ip_mc_leave_group(sk, &mreq); release_sock(sk); - rtnl_lock(); - return err; + vxlan_sock_release(vn, vs); + dev_put(vxlan->dev); } /* Callback from net/ipv4/udp.c to receive packets */ @@ -690,6 +802,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) struct vxlanhdr *vxh; struct vxlan_dev *vxlan; struct pcpu_tstats *stats; + __be16 port; __u32 vni; int err; @@ -713,9 +826,11 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) /* Is this VNI defined? */ vni = ntohl(vxh->vx_vni) >> 8; - vxlan = vxlan_find_vni(sock_net(sk), vni); + port = inet_sk(sk)->inet_sport; + vxlan = vxlan_find_vni(sock_net(sk), vni, port); if (!vxlan) { - netdev_dbg(skb->dev, "unknown vni %d\n", vni); + netdev_dbg(skb->dev, "unknown vni %d port %u\n", + vni, ntohs(port)); goto drop; } @@ -834,7 +949,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb) } f = vxlan_find_mac(vxlan, n->ha); - if (f && f->remote.remote_ip == htonl(INADDR_ANY)) { + if (f && first_remote(f)->remote_ip == htonl(INADDR_ANY)) { /* bridge-local neighbor */ neigh_release(n); goto out; @@ -896,7 +1011,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } -static void vxlan_sock_free(struct sk_buff *skb) +static void vxlan_sock_put(struct sk_buff *skb) { sock_put(skb->sk); } @@ -904,13 +1019,13 @@ static void vxlan_sock_free(struct sk_buff *skb) /* On transmit, associate with the tunnel socket */ static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb) { - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - struct sock *sk = vn->sock->sk; + struct vxlan_dev *vxlan = netdev_priv(dev); + struct sock *sk = vxlan->vn_sock->sock->sk; skb_orphan(skb); sock_hold(sk); skb->sk = sk; - skb->destructor = vxlan_sock_free; + skb->destructor = vxlan_sock_put; } /* Compute source port for outgoing packet @@ -976,21 +1091,21 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, } } -static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - struct vxlan_rdst *rdst, bool did_rsc) +static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + struct vxlan_rdst *rdst, bool did_rsc) { struct vxlan_dev *vxlan = netdev_priv(dev); struct rtable *rt; const struct iphdr *old_iph; - struct iphdr *iph; struct vxlanhdr *vxh; struct udphdr *uh; struct flowi4 fl4; __be32 dst; __be16 src_port, dst_port; - u32 vni; + u32 vni; __be16 df = 0; __u8 tos, ttl; + int err; dst_port = rdst->remote_port ? rdst->remote_port : vxlan->dst_port; vni = rdst->remote_vni; @@ -1000,7 +1115,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (did_rsc) { /* short-circuited back to local bridge */ vxlan_encap_bypass(skb, vxlan, vxlan); - return NETDEV_TX_OK; + return; } goto drop; } @@ -1052,19 +1167,12 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_dev *dst_vxlan; ip_rt_put(rt); - dst_vxlan = vxlan_find_vni(dev_net(dev), vni); + dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port); if (!dst_vxlan) goto tx_error; vxlan_encap_bypass(skb, vxlan, dst_vxlan); - return NETDEV_TX_OK; + return; } - - memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); - IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | - IPSKB_REROUTED); - skb_dst_drop(skb); - skb_dst_set(skb, &rt->dst); - vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = htonl(VXLAN_FLAGS); vxh->vx_vni = htonl(vni << 8); @@ -1079,28 +1187,19 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, uh->len = htons(skb->len); uh->check = 0; - __skb_push(skb, sizeof(*iph)); - skb_reset_network_header(skb); - iph = ip_hdr(skb); - iph->version = 4; - iph->ihl = sizeof(struct iphdr) >> 2; - iph->frag_off = df; - iph->protocol = IPPROTO_UDP; - iph->tos = ip_tunnel_ecn_encap(tos, old_iph, skb); - iph->daddr = dst; - iph->saddr = fl4.saddr; - iph->ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); - tunnel_ip_select_ident(skb, old_iph, &rt->dst); - - nf_reset(skb); - vxlan_set_owner(dev, skb); if (handle_offloads(skb)) goto drop; - iptunnel_xmit(skb, dev); - return NETDEV_TX_OK; + tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); + + err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, dst, + IPPROTO_UDP, tos, ttl, df); + iptunnel_xmit_stats(err, &dev->stats, dev->tstats); + + return; drop: dev->stats.tx_dropped++; @@ -1110,7 +1209,6 @@ tx_error: dev->stats.tx_errors++; tx_free: dev_kfree_skb(skb); - return NETDEV_TX_OK; } /* Transmit local packets over Vxlan @@ -1124,9 +1222,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) struct vxlan_dev *vxlan = netdev_priv(dev); struct ethhdr *eth; bool did_rsc = false; - struct vxlan_rdst *rdst0, *rdst; + struct vxlan_rdst *rdst; struct vxlan_fdb *f; - int rc1, rc; skb_reset_mac_header(skb); eth = eth_hdr(skb); @@ -1145,33 +1242,28 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) } if (f == NULL) { - rdst0 = &vxlan->default_dst; - - if (rdst0->remote_ip == htonl(INADDR_ANY) && - (vxlan->flags & VXLAN_F_L2MISS) && - !is_multicast_ether_addr(eth->h_dest)) - vxlan_fdb_miss(vxlan, eth->h_dest); - } else - rdst0 = &f->remote; - - rc = NETDEV_TX_OK; + f = vxlan_find_mac(vxlan, all_zeros_mac); + if (f == NULL) { + if ((vxlan->flags & VXLAN_F_L2MISS) && + !is_multicast_ether_addr(eth->h_dest)) + vxlan_fdb_miss(vxlan, eth->h_dest); + + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + } - /* if there are multiple destinations, send copies */ - for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) { + list_for_each_entry_rcu(rdst, &f->remotes, list) { struct sk_buff *skb1; skb1 = skb_clone(skb, GFP_ATOMIC); - if (skb1) { - rc1 = vxlan_xmit_one(skb1, dev, rdst, did_rsc); - if (rc == NETDEV_TX_OK) - rc = rc1; - } + if (skb1) + vxlan_xmit_one(skb1, dev, rdst, did_rsc); } - rc1 = vxlan_xmit_one(skb, dev, rdst0, did_rsc); - if (rc == NETDEV_TX_OK) - rc = rc1; - return rc; + dev_kfree_skb(skb); + return NETDEV_TX_OK; } /* Walk the forwarding table and purge stale entries */ @@ -1214,23 +1306,70 @@ static void vxlan_cleanup(unsigned long arg) /* Setup stats when device is created */ static int vxlan_init(struct net_device *dev) { + struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); + struct vxlan_sock *vs; + __u32 vni = vxlan->default_dst.remote_vni; + dev->tstats = alloc_percpu(struct pcpu_tstats); if (!dev->tstats) return -ENOMEM; + spin_lock(&vn->sock_lock); + vs = vxlan_find_port(dev_net(dev), vxlan->dst_port); + if (vs) { + /* If we have a socket with same port already, reuse it */ + atomic_inc(&vs->refcnt); + vxlan->vn_sock = vs; + hlist_add_head_rcu(&vxlan->hlist, vni_head(vs, vni)); + } else { + /* otherwise make new socket outside of RTNL */ + dev_hold(dev); + queue_work(vxlan_wq, &vxlan->sock_work); + } + spin_unlock(&vn->sock_lock); + return 0; } +static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan) +{ + struct vxlan_fdb *f; + + spin_lock_bh(&vxlan->hash_lock); + f = __vxlan_find_mac(vxlan, all_zeros_mac); + if (f) + vxlan_fdb_destroy(vxlan, f); + spin_unlock_bh(&vxlan->hash_lock); +} + +static void vxlan_uninit(struct net_device *dev) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); + struct vxlan_sock *vs = vxlan->vn_sock; + + vxlan_fdb_delete_default(vxlan); + + if (vs) + vxlan_sock_release(vn, vs); + free_percpu(dev->tstats); +} + /* Start ageing timer and join group when device is brought up */ static int vxlan_open(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - int err; + struct vxlan_sock *vs = vxlan->vn_sock; + + /* socket hasn't been created */ + if (!vs) + return -ENOTCONN; if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) { - err = vxlan_join_group(dev); - if (err) - return err; + vxlan_sock_hold(vs); + dev_hold(dev); + queue_work(vxlan_wq, &vxlan->igmp_work); } if (vxlan->age_interval) @@ -1242,7 +1381,7 @@ static int vxlan_open(struct net_device *dev) /* Purge the forwarding table */ static void vxlan_flush(struct vxlan_dev *vxlan) { - unsigned h; + unsigned int h; spin_lock_bh(&vxlan->hash_lock); for (h = 0; h < FDB_HASH_SIZE; ++h) { @@ -1250,7 +1389,9 @@ static void vxlan_flush(struct vxlan_dev *vxlan) hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) { struct vxlan_fdb *f = container_of(p, struct vxlan_fdb, hlist); - vxlan_fdb_destroy(vxlan, f); + /* the all_zeros_mac entry is deleted at vxlan_uninit */ + if (!is_zero_ether_addr(f->eth_addr)) + vxlan_fdb_destroy(vxlan, f); } } spin_unlock_bh(&vxlan->hash_lock); @@ -1260,9 +1401,13 @@ static void vxlan_flush(struct vxlan_dev *vxlan) static int vxlan_stop(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_sock *vs = vxlan->vn_sock; - if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) - vxlan_leave_group(dev); + if (vs && IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) { + vxlan_sock_hold(vs); + dev_hold(dev); + queue_work(vxlan_wq, &vxlan->igmp_work); + } del_timer_sync(&vxlan->age_timer); @@ -1278,6 +1423,7 @@ static void vxlan_set_multicast_list(struct net_device *dev) static const struct net_device_ops vxlan_netdev_ops = { .ndo_init = vxlan_init, + .ndo_uninit = vxlan_uninit, .ndo_open = vxlan_open, .ndo_stop = vxlan_stop, .ndo_start_xmit = vxlan_xmit, @@ -1296,17 +1442,11 @@ static struct device_type vxlan_type = { .name = "vxlan", }; -static void vxlan_free(struct net_device *dev) -{ - free_percpu(dev->tstats); - free_netdev(dev); -} - /* Initialize the device structure. */ static void vxlan_setup(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - unsigned h; + unsigned int h; int low, high; eth_hw_addr_random(dev); @@ -1314,7 +1454,7 @@ static void vxlan_setup(struct net_device *dev) dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM; dev->netdev_ops = &vxlan_netdev_ops; - dev->destructor = vxlan_free; + dev->destructor = free_netdev; SET_NETDEV_DEVTYPE(dev, &vxlan_type); dev->tx_queue_len = 0; @@ -1329,7 +1469,10 @@ static void vxlan_setup(struct net_device *dev) dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + INIT_LIST_HEAD(&vxlan->next); spin_lock_init(&vxlan->hash_lock); + INIT_WORK(&vxlan->igmp_work, vxlan_igmp_work); + INIT_WORK(&vxlan->sock_work, vxlan_sock_work); init_timer_deferrable(&vxlan->age_timer); vxlan->age_timer.function = vxlan_cleanup; @@ -1413,9 +1556,113 @@ static const struct ethtool_ops vxlan_ethtool_ops = { .get_link = ethtool_op_get_link, }; +static void vxlan_del_work(struct work_struct *work) +{ + struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work); + + sk_release_kernel(vs->sock->sk); + kfree_rcu(vs, rcu); +} + +static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port) +{ + struct vxlan_sock *vs; + struct sock *sk; + struct sockaddr_in vxlan_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = port, + }; + int rc; + unsigned int h; + + vs = kmalloc(sizeof(*vs), GFP_KERNEL); + if (!vs) + return ERR_PTR(-ENOMEM); + + for (h = 0; h < VNI_HASH_SIZE; ++h) + INIT_HLIST_HEAD(&vs->vni_list[h]); + + INIT_WORK(&vs->del_work, vxlan_del_work); + + /* Create UDP socket for encapsulation receive. */ + rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vs->sock); + if (rc < 0) { + pr_debug("UDP socket create failed\n"); + kfree(vs); + return ERR_PTR(rc); + } + + /* Put in proper namespace */ + sk = vs->sock->sk; + sk_change_net(sk, net); + + rc = kernel_bind(vs->sock, (struct sockaddr *) &vxlan_addr, + sizeof(vxlan_addr)); + if (rc < 0) { + pr_debug("bind for UDP socket %pI4:%u (%d)\n", + &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); + sk_release_kernel(sk); + kfree(vs); + return ERR_PTR(rc); + } + + /* Disable multicast loopback */ + inet_sk(sk)->mc_loop = 0; + + /* Mark socket as an encapsulation socket. */ + udp_sk(sk)->encap_type = 1; + udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv; + udp_encap_enable(); + atomic_set(&vs->refcnt, 1); + + return vs; +} + +/* Scheduled at device creation to bind to a socket */ +static void vxlan_sock_work(struct work_struct *work) +{ + struct vxlan_dev *vxlan + = container_of(work, struct vxlan_dev, sock_work); + struct net_device *dev = vxlan->dev; + struct net *net = dev_net(dev); + __u32 vni = vxlan->default_dst.remote_vni; + __be16 port = vxlan->dst_port; + struct vxlan_net *vn = net_generic(net, vxlan_net_id); + struct vxlan_sock *nvs, *ovs; + + nvs = vxlan_socket_create(net, port); + if (IS_ERR(nvs)) { + netdev_err(vxlan->dev, "Can not create UDP socket, %ld\n", + PTR_ERR(nvs)); + goto out; + } + + spin_lock(&vn->sock_lock); + /* Look again to see if can reuse socket */ + ovs = vxlan_find_port(net, port); + if (ovs) { + atomic_inc(&ovs->refcnt); + vxlan->vn_sock = ovs; + hlist_add_head_rcu(&vxlan->hlist, vni_head(ovs, vni)); + spin_unlock(&vn->sock_lock); + + sk_release_kernel(nvs->sock->sk); + kfree(nvs); + } else { + vxlan->vn_sock = nvs; + hlist_add_head_rcu(&nvs->hlist, vs_head(net, port)); + hlist_add_head_rcu(&vxlan->hlist, vni_head(nvs, vni)); + spin_unlock(&vn->sock_lock); + } +out: + dev_put(dev); +} + static int vxlan_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { + struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; __u32 vni; @@ -1425,10 +1672,6 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, return -EINVAL; vni = nla_get_u32(data[IFLA_VXLAN_ID]); - if (vxlan_find_vni(net, vni)) { - pr_info("duplicate VNI %u\n", vni); - return -EEXIST; - } dst->remote_vni = vni; if (data[IFLA_VXLAN_GROUP]) @@ -1494,13 +1737,32 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (data[IFLA_VXLAN_PORT]) vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]); + if (vxlan_find_vni(net, vni, vxlan->dst_port)) { + pr_info("duplicate VNI %u\n", vni); + return -EEXIST; + } + SET_ETHTOOL_OPS(dev, &vxlan_ethtool_ops); + /* create an fdb entry for default destination */ + err = vxlan_fdb_create(vxlan, all_zeros_mac, + vxlan->default_dst.remote_ip, + NUD_REACHABLE|NUD_PERMANENT, + NLM_F_EXCL|NLM_F_CREATE, + vxlan->dst_port, vxlan->default_dst.remote_vni, + vxlan->default_dst.remote_ifindex, NTF_SELF); + if (err) + return err; + err = register_netdevice(dev); - if (!err) - hlist_add_head_rcu(&vxlan->hlist, vni_head(net, dst->remote_vni)); + if (err) { + vxlan_fdb_delete_default(vxlan); + return err; + } - return err; + list_add(&vxlan->next, &vn->vxlan_list); + + return 0; } static void vxlan_dellink(struct net_device *dev, struct list_head *head) @@ -1508,7 +1770,7 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head) struct vxlan_dev *vxlan = netdev_priv(dev); hlist_del_rcu(&vxlan->hlist); - + list_del(&vxlan->next); unregister_netdevice_queue(dev, head); } @@ -1595,46 +1857,13 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = { static __net_init int vxlan_init_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); - struct sock *sk; - struct sockaddr_in vxlan_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(INADDR_ANY), - }; - int rc; - unsigned h; - - /* Create UDP socket for encapsulation receive. */ - rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vn->sock); - if (rc < 0) { - pr_debug("UDP socket create failed\n"); - return rc; - } - /* Put in proper namespace */ - sk = vn->sock->sk; - sk_change_net(sk, net); - - vxlan_addr.sin_port = htons(vxlan_port); - - rc = kernel_bind(vn->sock, (struct sockaddr *) &vxlan_addr, - sizeof(vxlan_addr)); - if (rc < 0) { - pr_debug("bind for UDP socket %pI4:%u (%d)\n", - &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); - sk_release_kernel(sk); - vn->sock = NULL; - return rc; - } - - /* Disable multicast loopback */ - inet_sk(sk)->mc_loop = 0; + unsigned int h; - /* Mark socket as an encapsulation socket. */ - udp_sk(sk)->encap_type = 1; - udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv; - udp_encap_enable(); + INIT_LIST_HEAD(&vn->vxlan_list); + spin_lock_init(&vn->sock_lock); - for (h = 0; h < VNI_HASH_SIZE; ++h) - INIT_HLIST_HEAD(&vn->vni_list[h]); + for (h = 0; h < PORT_HASH_SIZE; ++h) + INIT_HLIST_HEAD(&vn->sock_list[h]); return 0; } @@ -1643,18 +1872,11 @@ static __net_exit void vxlan_exit_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan; - unsigned h; rtnl_lock(); - for (h = 0; h < VNI_HASH_SIZE; ++h) - hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) - dev_close(vxlan->dev); + list_for_each_entry(vxlan, &vn->vxlan_list, next) + dev_close(vxlan->dev); rtnl_unlock(); - - if (vn->sock) { - sk_release_kernel(vn->sock->sk); - vn->sock = NULL; - } } static struct pernet_operations vxlan_net_ops = { @@ -1668,6 +1890,10 @@ static int __init vxlan_init_module(void) { int rc; + vxlan_wq = alloc_workqueue("vxlan", 0, 0); + if (!vxlan_wq) + return -ENOMEM; + get_random_bytes(&vxlan_salt, sizeof(vxlan_salt)); rc = register_pernet_device(&vxlan_net_ops); @@ -1683,14 +1909,16 @@ static int __init vxlan_init_module(void) out2: unregister_pernet_device(&vxlan_net_ops); out1: + destroy_workqueue(vxlan_wq); return rc; } -module_init(vxlan_init_module); +late_initcall(vxlan_init_module); static void __exit vxlan_cleanup_module(void) { - rtnl_link_unregister(&vxlan_link_ops); unregister_pernet_device(&vxlan_net_ops); + rtnl_link_unregister(&vxlan_link_ops); + destroy_workqueue(vxlan_wq); rcu_barrier(); } module_exit(vxlan_cleanup_module); diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c index 6a8a382c5f4..0d1c7592efa 100644 --- a/drivers/net/wan/dlci.c +++ b/drivers/net/wan/dlci.c @@ -493,7 +493,7 @@ static void dlci_setup(struct net_device *dev) static int dlci_dev_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *) ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (dev_net(dev) != &init_net) return NOTIFY_DONE; diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index a0a932c63d0..9c33ca918e1 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -99,7 +99,7 @@ static inline void hdlc_proto_stop(struct net_device *dev) static int hdlc_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); hdlc_device *hdlc; unsigned long flags; int on; diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index fc9d11d74d6..e7bbdb7af53 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -1384,7 +1384,6 @@ static int hss_remove_one(struct platform_device *pdev) unregister_hdlc_device(port->netdev); free_netdev(port->netdev); npe_release(port->npe); - platform_set_drvdata(pdev, NULL); kfree(port); return 0; } diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index a73b49eb87e..a33a46fa88d 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -370,7 +370,7 @@ static int lapbeth_device_event(struct notifier_block *this, unsigned long event, void *ptr) { struct lapbethdev *lapbeth; - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (dev_net(dev) != &init_net) return NOTIFY_DONE; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f8f0156dff4..200020eb300 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig" source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/mwifiex/Kconfig" +source "drivers/net/wireless/cw1200/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 67156efe14c..0fab227025b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/ obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/ + +obj-$(CONFIG_CW1200) += cw1200/ diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 6125adb520a..d0adbaf8618 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1893,7 +1893,8 @@ static int airo_open(struct net_device *dev) { if (ai->wifidev != dev) { clear_bit(JOB_DIE, &ai->jobs); - ai->airo_thread_task = kthread_run(airo_thread, dev, dev->name); + ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", + dev->name); if (IS_ERR(ai->airo_thread_task)) return (int)PTR_ERR(ai->airo_thread_task); diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 2c02b4e8409..1abf1d42117 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -31,5 +31,6 @@ source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" +source "drivers/net/wireless/ath/ath10k/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 97b964ded2b..fb05cfd1936 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ +obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 4521342c62c..daeafeff186 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -239,13 +239,12 @@ enum ATH_DEBUG { ATH_DBG_CONFIG = 0x00000200, ATH_DBG_FATAL = 0x00000400, ATH_DBG_PS = 0x00000800, - ATH_DBG_HWTIMER = 0x00001000, - ATH_DBG_BTCOEX = 0x00002000, - ATH_DBG_WMI = 0x00004000, - ATH_DBG_BSTUCK = 0x00008000, - ATH_DBG_MCI = 0x00010000, - ATH_DBG_DFS = 0x00020000, - ATH_DBG_WOW = 0x00040000, + ATH_DBG_BTCOEX = 0x00001000, + ATH_DBG_WMI = 0x00002000, + ATH_DBG_BSTUCK = 0x00004000, + ATH_DBG_MCI = 0x00008000, + ATH_DBG_DFS = 0x00010000, + ATH_DBG_WOW = 0x00020000, ATH_DBG_ANY = 0xffffffff }; diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig new file mode 100644 index 00000000000..cde58fe9625 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -0,0 +1,39 @@ +config ATH10K + tristate "Atheros 802.11ac wireless cards support" + depends on MAC80211 + select ATH_COMMON + ---help--- + This module adds support for wireless adapters based on + Atheros IEEE 802.11ac family of chipsets. + + If you choose to build a module, it'll be called ath10k. + +config ATH10K_PCI + tristate "Atheros ath10k PCI support" + depends on ATH10K && PCI + ---help--- + This module adds support for PCIE bus + +config ATH10K_DEBUG + bool "Atheros ath10k debugging" + depends on ATH10K + ---help--- + Enables debug support + + If unsure, say Y to make it easier to debug problems. + +config ATH10K_DEBUGFS + bool "Atheros ath10k debugfs support" + depends on ATH10K + ---help--- + Enabled debugfs support + + If unsure, say Y to make it easier to debug problems. + +config ATH10K_TRACING + bool "Atheros ath10k tracing support" + depends on ATH10K + depends on EVENT_TRACING + ---help--- + Select this to ath10k use tracing infrastructure. + diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile new file mode 100644 index 00000000000..a4179f49ee1 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -0,0 +1,20 @@ +obj-$(CONFIG_ATH10K) += ath10k_core.o +ath10k_core-y += mac.o \ + debug.o \ + core.o \ + htc.o \ + htt.o \ + htt_rx.o \ + htt_tx.o \ + txrx.o \ + wmi.o \ + bmi.o + +ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o + +obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o +ath10k_pci-y += pci.o \ + ce.o + +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c new file mode 100644 index 00000000000..1a2ef51b69d --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bmi.h" +#include "hif.h" +#include "debug.h" +#include "htc.h" + +int ath10k_bmi_done(struct ath10k *ar) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); + int ret; + + if (ar->bmi.done_sent) { + ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__); + return 0; + } + + ar->bmi.done_sent = true; + cmd.id = __cpu_to_le32(BMI_DONE); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn("unable to write to the device: %d\n", ret); + return ret; + } + + ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n"); + return 0; +} + +int ath10k_bmi_get_target_info(struct ath10k *ar, + struct bmi_target_info *target_info) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info); + u32 resplen = sizeof(resp.get_target_info); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("BMI Get Target Info Command disallowed\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); + if (ret) { + ath10k_warn("unable to get target info from device\n"); + return ret; + } + + if (resplen < sizeof(resp.get_target_info)) { + ath10k_warn("invalid get_target_info response length (%d)\n", + resplen); + return -EIO; + } + + target_info->version = __le32_to_cpu(resp.get_target_info.version); + target_info->type = __le32_to_cpu(resp.get_target_info.type); + return 0; +} + +int ath10k_bmi_read_memory(struct ath10k *ar, + u32 address, void *buffer, u32 length) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem); + u32 rxlen; + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "%s: (device: 0x%p, address: 0x%x, length: %d)\n", + __func__, ar, address, length); + + while (length) { + rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE); + + cmd.id = __cpu_to_le32(BMI_READ_MEMORY); + cmd.read_mem.addr = __cpu_to_le32(address); + cmd.read_mem.len = __cpu_to_le32(rxlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, + &resp, &rxlen); + if (ret) { + ath10k_warn("unable to read from the device\n"); + return ret; + } + + memcpy(buffer, resp.read_mem.payload, rxlen); + address += rxlen; + buffer += rxlen; + length -= rxlen; + } + + return 0; +} + +int ath10k_bmi_write_memory(struct ath10k *ar, + u32 address, const void *buffer, u32 length) +{ + struct bmi_cmd cmd; + u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem); + u32 txlen; + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "%s: (device: 0x%p, address: 0x%x, length: %d)\n", + __func__, ar, address, length); + + while (length) { + txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); + + /* copy before roundup to avoid reading beyond buffer*/ + memcpy(cmd.write_mem.payload, buffer, txlen); + txlen = roundup(txlen, 4); + + cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY); + cmd.write_mem.addr = __cpu_to_le32(address); + cmd.write_mem.len = __cpu_to_le32(txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn("unable to write to the device\n"); + return ret; + } + + /* fixup roundup() so `length` zeroes out for last chunk */ + txlen = min(txlen, length); + + address += txlen; + buffer += txlen; + length -= txlen; + } + + return 0; +} + +int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute); + u32 resplen = sizeof(resp.execute); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "%s: (device: 0x%p, address: 0x%x, param: %d)\n", + __func__, ar, address, *param); + + cmd.id = __cpu_to_le32(BMI_EXECUTE); + cmd.execute.addr = __cpu_to_le32(address); + cmd.execute.param = __cpu_to_le32(*param); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); + if (ret) { + ath10k_warn("unable to read from the device\n"); + return ret; + } + + if (resplen < sizeof(resp.execute)) { + ath10k_warn("invalid execute response length (%d)\n", + resplen); + return ret; + } + + *param = __le32_to_cpu(resp.execute.result); + return 0; +} + +int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) +{ + struct bmi_cmd cmd; + u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data); + u32 txlen; + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + while (length) { + txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); + + WARN_ON_ONCE(txlen & 3); + + cmd.id = __cpu_to_le32(BMI_LZ_DATA); + cmd.lz_data.len = __cpu_to_le32(txlen); + memcpy(cmd.lz_data.payload, buffer, txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn("unable to write to the device\n"); + return ret; + } + + buffer += txlen; + length -= txlen; + } + + return 0; +} + +int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START); + cmd.lz_start.addr = __cpu_to_le32(address); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn("unable to Start LZ Stream to the device\n"); + return ret; + } + + return 0; +} + +int ath10k_bmi_fast_download(struct ath10k *ar, + u32 address, const void *buffer, u32 length) +{ + u8 trailer[4] = {}; + u32 head_len = rounddown(length, 4); + u32 trailer_len = length - head_len; + int ret; + + ret = ath10k_bmi_lz_stream_start(ar, address); + if (ret) + return ret; + + /* copy the last word into a zero padded buffer */ + if (trailer_len > 0) + memcpy(trailer, buffer + head_len, trailer_len); + + ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ret) + return ret; + + if (trailer_len > 0) + ret = ath10k_bmi_lz_data(ar, trailer, 4); + + if (ret != 0) + return ret; + + /* + * Close compressed stream and open a new (fake) one. + * This serves mainly to flush Target caches. + */ + ret = ath10k_bmi_lz_stream_start(ar, 0x00); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h new file mode 100644 index 00000000000..32c56aa33a5 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BMI_H_ +#define _BMI_H_ + +#include "core.h" + +/* + * Bootloader Messaging Interface (BMI) + * + * BMI is a very simple messaging interface used during initialization + * to read memory, write memory, execute code, and to define an + * application entry PC. + * + * It is used to download an application to QCA988x, to provide + * patches to code that is already resident on QCA988x, and generally + * to examine and modify state. The Host has an opportunity to use + * BMI only once during bootup. Once the Host issues a BMI_DONE + * command, this opportunity ends. + * + * The Host writes BMI requests to mailbox0, and reads BMI responses + * from mailbox0. BMI requests all begin with a command + * (see below for specific commands), and are followed by + * command-specific data. + * + * Flow control: + * The Host can only issue a command once the Target gives it a + * "BMI Command Credit", using AR8K Counter #4. As soon as the + * Target has completed a command, it issues another BMI Command + * Credit (so the Host can issue the next command). + * + * BMI handles all required Target-side cache flushing. + */ + +/* Maximum data size used for BMI transfers */ +#define BMI_MAX_DATA_SIZE 256 + +/* len = cmd + addr + length */ +#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \ + sizeof(u32) + \ + sizeof(u32) + \ + sizeof(u32)) + +/* BMI Commands */ + +enum bmi_cmd_id { + BMI_NO_COMMAND = 0, + BMI_DONE = 1, + BMI_READ_MEMORY = 2, + BMI_WRITE_MEMORY = 3, + BMI_EXECUTE = 4, + BMI_SET_APP_START = 5, + BMI_READ_SOC_REGISTER = 6, + BMI_READ_SOC_WORD = 6, + BMI_WRITE_SOC_REGISTER = 7, + BMI_WRITE_SOC_WORD = 7, + BMI_GET_TARGET_ID = 8, + BMI_GET_TARGET_INFO = 8, + BMI_ROMPATCH_INSTALL = 9, + BMI_ROMPATCH_UNINSTALL = 10, + BMI_ROMPATCH_ACTIVATE = 11, + BMI_ROMPATCH_DEACTIVATE = 12, + BMI_LZ_STREAM_START = 13, /* should be followed by LZ_DATA */ + BMI_LZ_DATA = 14, + BMI_NVRAM_PROCESS = 15, +}; + +#define BMI_NVRAM_SEG_NAME_SZ 16 + +struct bmi_cmd { + __le32 id; /* enum bmi_cmd_id */ + union { + struct { + } done; + struct { + __le32 addr; + __le32 len; + } read_mem; + struct { + __le32 addr; + __le32 len; + u8 payload[0]; + } write_mem; + struct { + __le32 addr; + __le32 param; + } execute; + struct { + __le32 addr; + } set_app_start; + struct { + __le32 addr; + } read_soc_reg; + struct { + __le32 addr; + __le32 value; + } write_soc_reg; + struct { + } get_target_info; + struct { + __le32 rom_addr; + __le32 ram_addr; /* or value */ + __le32 size; + __le32 activate; /* 0=install, but dont activate */ + } rompatch_install; + struct { + __le32 patch_id; + } rompatch_uninstall; + struct { + __le32 count; + __le32 patch_ids[0]; /* length of @count */ + } rompatch_activate; + struct { + __le32 count; + __le32 patch_ids[0]; /* length of @count */ + } rompatch_deactivate; + struct { + __le32 addr; + } lz_start; + struct { + __le32 len; /* max BMI_MAX_DATA_SIZE */ + u8 payload[0]; /* length of @len */ + } lz_data; + struct { + u8 name[BMI_NVRAM_SEG_NAME_SZ]; + } nvram_process; + u8 payload[BMI_MAX_CMDBUF_SIZE]; + }; +} __packed; + +union bmi_resp { + struct { + u8 payload[0]; + } read_mem; + struct { + __le32 result; + } execute; + struct { + __le32 value; + } read_soc_reg; + struct { + __le32 len; + __le32 version; + __le32 type; + } get_target_info; + struct { + __le32 patch_id; + } rompatch_install; + struct { + __le32 patch_id; + } rompatch_uninstall; + struct { + /* 0 = nothing executed + * otherwise = NVRAM segment return value */ + __le32 result; + } nvram_process; + u8 payload[BMI_MAX_CMDBUF_SIZE]; +} __packed; + +struct bmi_target_info { + u32 version; + u32 type; +}; + + +/* in msec */ +#define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ) + +#define BMI_CE_NUM_TO_TARG 0 +#define BMI_CE_NUM_TO_HOST 1 + +int ath10k_bmi_done(struct ath10k *ar); +int ath10k_bmi_get_target_info(struct ath10k *ar, + struct bmi_target_info *target_info); +int ath10k_bmi_read_memory(struct ath10k *ar, u32 address, + void *buffer, u32 length); +int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, + const void *buffer, u32 length); + +#define ath10k_bmi_read32(ar, item, val) \ + ({ \ + int ret; \ + u32 addr; \ + __le32 tmp; \ + \ + addr = host_interest_item_address(HI_ITEM(item)); \ + ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \ + *val = __le32_to_cpu(tmp); \ + ret; \ + }) + +#define ath10k_bmi_write32(ar, item, val) \ + ({ \ + int ret; \ + u32 address; \ + __le32 v = __cpu_to_le32(val); \ + \ + address = host_interest_item_address(HI_ITEM(item)); \ + ret = ath10k_bmi_write_memory(ar, address, \ + (u8 *)&v, sizeof(v)); \ + ret; \ + }) + +int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param); +int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); +int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); +int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, + const void *buffer, u32 length); +#endif /* _BMI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c new file mode 100644 index 00000000000..61a8ac70d3c --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hif.h" +#include "pci.h" +#include "ce.h" +#include "debug.h" + +/* + * Support for Copy Engine hardware, which is mainly used for + * communication between Host and Target over a PCIe interconnect. + */ + +/* + * A single CopyEngine (CE) comprises two "rings": + * a source ring + * a destination ring + * + * Each ring consists of a number of descriptors which specify + * an address, length, and meta-data. + * + * Typically, one side of the PCIe interconnect (Host or Target) + * controls one ring and the other side controls the other ring. + * The source side chooses when to initiate a transfer and it + * chooses what to send (buffer address, length). The destination + * side keeps a supply of "anonymous receive buffers" available and + * it handles incoming data as it arrives (when the destination + * recieves an interrupt). + * + * The sender may send a simple buffer (address/length) or it may + * send a small list of buffers. When a small list is sent, hardware + * "gathers" these and they end up in a single destination buffer + * with a single interrupt. + * + * There are several "contexts" managed by this layer -- more, it + * may seem -- than should be needed. These are provided mainly for + * maximum flexibility and especially to facilitate a simpler HIF + * implementation. There are per-CopyEngine recv, send, and watermark + * contexts. These are supplied by the caller when a recv, send, + * or watermark handler is established and they are echoed back to + * the caller when the respective callbacks are invoked. There is + * also a per-transfer context supplied by the caller when a buffer + * (or sendlist) is sent and when a buffer is enqueued for recv. + * These per-transfer contexts are echoed back to the caller when + * the buffer is sent/received. + */ + +static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n); +} + +static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS); +} + +static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *indicator_addr; + + if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); + return; + } + + /* workaround for QCA988x_1.0 HW CE */ + indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS; + + if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) { + iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr); + } else { + unsigned long irq_flags; + local_irq_save(irq_flags); + iowrite32(1, indicator_addr); + + /* + * PCIE write waits for ACK in IPQ8K, there is no + * need to read back value. + */ + (void)ioread32(indicator_addr); + (void)ioread32(indicator_addr); /* conservative */ + + ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); + + iowrite32(0, indicator_addr); + local_irq_restore(irq_flags); + } +} + +static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS); +} + +static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS); +} + +static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int addr) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr); +} + +static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n); +} + +static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 ctrl1_addr = ath10k_pci_read32((ar), + (ce_ctrl_addr) + CE_CTRL1_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, + (ctrl1_addr & ~CE_CTRL1_DMAX_LENGTH_MASK) | + CE_CTRL1_DMAX_LENGTH_SET(n)); +} + +static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, + (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) | + CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n)); +} + +static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, + (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) | + CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n)); +} + +static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS); +} + +static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar, + u32 ce_ctrl_addr, + u32 addr) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr); +} + +static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n); +} + +static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS, + (addr & ~SRC_WATERMARK_HIGH_MASK) | + SRC_WATERMARK_HIGH_SET(n)); +} + +static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS, + (addr & ~SRC_WATERMARK_LOW_MASK) | + SRC_WATERMARK_LOW_SET(n)); +} + +static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS, + (addr & ~DST_WATERMARK_HIGH_MASK) | + DST_WATERMARK_HIGH_SET(n)); +} + +static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS, + (addr & ~DST_WATERMARK_LOW_MASK) | + DST_WATERMARK_LOW_SET(n)); +} + +static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 host_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + HOST_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, + host_ie_addr | HOST_IE_COPY_COMPLETE_MASK); +} + +static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 host_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + HOST_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, + host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK); +} + +static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 host_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + HOST_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, + host_ie_addr & ~CE_WATERMARK_MASK); +} + +static inline void ath10k_ce_error_intr_enable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 misc_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + MISC_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS, + misc_ie_addr | CE_ERROR_MASK); +} + +static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int mask) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask); +} + + +/* + * Guts of ath10k_ce_send, used by both ath10k_ce_send and + * ath10k_ce_sendlist_send. + * The caller takes responsibility for any needed locking. + */ +static int ath10k_ce_send_nolock(struct ce_state *ce_state, + void *per_transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct ath10k *ar = ce_state->ar; + struct ce_ring_state *src_ring = ce_state->src_ring; + struct ce_desc *desc, *sdesc; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int sw_index = src_ring->sw_index; + unsigned int write_index = src_ring->write_index; + u32 ctrl_addr = ce_state->ctrl_addr; + u32 desc_flags = 0; + int ret = 0; + + if (nbytes > ce_state->src_sz_max) + ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n", + __func__, nbytes, ce_state->src_sz_max); + + ath10k_pci_wake(ar); + + if (unlikely(CE_RING_DELTA(nentries_mask, + write_index, sw_index - 1) <= 0)) { + ret = -EIO; + goto exit; + } + + desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space, + write_index); + sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index); + + desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA); + + if (flags & CE_SEND_FLAG_GATHER) + desc_flags |= CE_DESC_FLAGS_GATHER; + if (flags & CE_SEND_FLAG_BYTE_SWAP) + desc_flags |= CE_DESC_FLAGS_BYTE_SWAP; + + sdesc->addr = __cpu_to_le32(buffer); + sdesc->nbytes = __cpu_to_le16(nbytes); + sdesc->flags = __cpu_to_le16(desc_flags); + + *desc = *sdesc; + + src_ring->per_transfer_context[write_index] = per_transfer_context; + + /* Update Source Ring Write Index */ + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + + /* WORKAROUND */ + if (!(flags & CE_SEND_FLAG_GATHER)) + ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index); + + src_ring->write_index = write_index; +exit: + ath10k_pci_sleep(ar); + return ret; +} + +int ath10k_ce_send(struct ce_state *ce_state, + void *per_transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, + buffer, nbytes, transfer_id, flags); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer, + unsigned int nbytes, u32 flags) +{ + unsigned int num_items = sendlist->num_items; + struct ce_sendlist_item *item; + + item = &sendlist->item[num_items]; + item->data = buffer; + item->u.nbytes = nbytes; + item->flags = flags; + sendlist->num_items++; +} + +int ath10k_ce_sendlist_send(struct ce_state *ce_state, + void *per_transfer_context, + struct ce_sendlist *sendlist, + unsigned int transfer_id) +{ + struct ce_ring_state *src_ring = ce_state->src_ring; + struct ce_sendlist_item *item; + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int num_items = sendlist->num_items; + unsigned int sw_index; + unsigned int write_index; + int i, delta, ret = -ENOMEM; + + spin_lock_bh(&ar_pci->ce_lock); + + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + + delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); + + if (delta >= num_items) { + /* + * Handle all but the last item uniformly. + */ + for (i = 0; i < num_items - 1; i++) { + item = &sendlist->item[i]; + ret = ath10k_ce_send_nolock(ce_state, + CE_SENDLIST_ITEM_CTXT, + (u32) item->data, + item->u.nbytes, transfer_id, + item->flags | + CE_SEND_FLAG_GATHER); + if (ret) + ath10k_warn("CE send failed for item: %d\n", i); + } + /* + * Provide valid context pointer for final item. + */ + item = &sendlist->item[i]; + ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, + (u32) item->data, item->u.nbytes, + transfer_id, item->flags); + if (ret) + ath10k_warn("CE send failed for last item: %d\n", i); + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, + void *per_recv_context, + u32 buffer) +{ + struct ce_ring_state *dest_ring = ce_state->dest_ring; + u32 ctrl_addr = ce_state->ctrl_addr; + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int write_index; + unsigned int sw_index; + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + write_index = dest_ring->write_index; + sw_index = dest_ring->sw_index; + + ath10k_pci_wake(ar); + + if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) { + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index); + + /* Update destination descriptor */ + desc->addr = __cpu_to_le32(buffer); + desc->nbytes = 0; + + dest_ring->per_transfer_context[write_index] = + per_recv_context; + + /* Update Destination Ring Write Index */ + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index); + dest_ring->write_index = write_index; + ret = 0; + } else { + ret = -EIO; + } + ath10k_pci_sleep(ar); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +/* + * Guts of ath10k_ce_completed_recv_next. + * The caller takes responsibility for any necessary locking. + */ +static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp) +{ + struct ce_ring_state *dest_ring = ce_state->dest_ring; + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int sw_index = dest_ring->sw_index; + + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index); + struct ce_desc sdesc; + u16 nbytes; + + /* Copy in one go for performance reasons */ + sdesc = *desc; + + nbytes = __le16_to_cpu(sdesc.nbytes); + if (nbytes == 0) { + /* + * This closes a relatively unusual race where the Host + * sees the updated DRRI before the update to the + * corresponding descriptor has completed. We treat this + * as a descriptor that is not yet done. + */ + return -EIO; + } + + desc->nbytes = 0; + + /* Return data from completed destination descriptor */ + *bufferp = __le32_to_cpu(sdesc.addr); + *nbytesp = nbytes; + *transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA); + + if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP) + *flagsp = CE_RECV_FLAG_SWAPPED; + else + *flagsp = 0; + + if (per_transfer_contextp) + *per_transfer_contextp = + dest_ring->per_transfer_context[sw_index]; + + /* sanity */ + dest_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + dest_ring->sw_index = sw_index; + + return 0; +} + +int ath10k_ce_completed_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + ret = ath10k_ce_completed_recv_next_nolock(ce_state, + per_transfer_contextp, + bufferp, nbytesp, + transfer_idp, flagsp); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp) +{ + struct ce_ring_state *dest_ring; + unsigned int nentries_mask; + unsigned int sw_index; + unsigned int write_index; + int ret; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + + dest_ring = ce_state->dest_ring; + + if (!dest_ring) + return -EIO; + + ar = ce_state->ar; + ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + + nentries_mask = dest_ring->nentries_mask; + sw_index = dest_ring->sw_index; + write_index = dest_ring->write_index; + if (write_index != sw_index) { + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index); + + /* Return data from completed destination descriptor */ + *bufferp = __le32_to_cpu(desc->addr); + + if (per_transfer_contextp) + *per_transfer_contextp = + dest_ring->per_transfer_context[sw_index]; + + /* sanity */ + dest_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + dest_ring->sw_index = sw_index; + ret = 0; + } else { + ret = -EIO; + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +/* + * Guts of ath10k_ce_completed_send_next. + * The caller takes responsibility for any necessary locking. + */ +static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp) +{ + struct ce_ring_state *src_ring = ce_state->src_ring; + u32 ctrl_addr = ce_state->ctrl_addr; + struct ath10k *ar = ce_state->ar; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int sw_index = src_ring->sw_index; + unsigned int read_index; + int ret = -EIO; + + if (src_ring->hw_index == sw_index) { + /* + * The SW completion index has caught up with the cached + * version of the HW completion index. + * Update the cached HW completion index to see whether + * the SW has really caught up to the HW, or if the cached + * value of the HW index has become stale. + */ + ath10k_pci_wake(ar); + src_ring->hw_index = + ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + ath10k_pci_sleep(ar); + } + read_index = src_ring->hw_index; + + if ((read_index != sw_index) && (read_index != 0xffffffff)) { + struct ce_desc *sbase = src_ring->shadow_base; + struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); + + /* Return data from completed source descriptor */ + *bufferp = __le32_to_cpu(sdesc->addr); + *nbytesp = __le16_to_cpu(sdesc->nbytes); + *transfer_idp = MS(__le16_to_cpu(sdesc->flags), + CE_DESC_FLAGS_META_DATA); + + if (per_transfer_contextp) + *per_transfer_contextp = + src_ring->per_transfer_context[sw_index]; + + /* sanity */ + src_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + src_ring->sw_index = sw_index; + ret = 0; + } + + return ret; +} + +/* NB: Modeled after ath10k_ce_completed_send_next */ +int ath10k_ce_cancel_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp) +{ + struct ce_ring_state *src_ring; + unsigned int nentries_mask; + unsigned int sw_index; + unsigned int write_index; + int ret; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + + src_ring = ce_state->src_ring; + + if (!src_ring) + return -EIO; + + ar = ce_state->ar; + ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + + nentries_mask = src_ring->nentries_mask; + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + + if (write_index != sw_index) { + struct ce_desc *base = src_ring->base_addr_owner_space; + struct ce_desc *desc = CE_SRC_RING_TO_DESC(base, sw_index); + + /* Return data from completed source descriptor */ + *bufferp = __le32_to_cpu(desc->addr); + *nbytesp = __le16_to_cpu(desc->nbytes); + *transfer_idp = MS(__le16_to_cpu(desc->flags), + CE_DESC_FLAGS_META_DATA); + + if (per_transfer_contextp) + *per_transfer_contextp = + src_ring->per_transfer_context[sw_index]; + + /* sanity */ + src_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + src_ring->sw_index = sw_index; + ret = 0; + } else { + ret = -EIO; + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +int ath10k_ce_completed_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + ret = ath10k_ce_completed_send_next_nolock(ce_state, + per_transfer_contextp, + bufferp, nbytesp, + transfer_idp); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +/* + * Guts of interrupt handler for per-engine interrupts on a particular CE. + * + * Invokes registered callbacks for recv_complete, + * send_complete, and watermarks. + */ +void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + u32 ctrl_addr = ce_state->ctrl_addr; + void *transfer_context; + u32 buf; + unsigned int nbytes; + unsigned int id; + unsigned int flags; + + ath10k_pci_wake(ar); + spin_lock_bh(&ar_pci->ce_lock); + + /* Clear the copy-complete interrupts that will be handled here. */ + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, + HOST_IS_COPY_COMPLETE_MASK); + + if (ce_state->recv_cb) { + /* + * Pop completed recv buffers and call the registered + * recv callback for each + */ + while (ath10k_ce_completed_recv_next_nolock(ce_state, + &transfer_context, + &buf, &nbytes, + &id, &flags) == 0) { + spin_unlock_bh(&ar_pci->ce_lock); + ce_state->recv_cb(ce_state, transfer_context, buf, + nbytes, id, flags); + spin_lock_bh(&ar_pci->ce_lock); + } + } + + if (ce_state->send_cb) { + /* + * Pop completed send buffers and call the registered + * send callback for each + */ + while (ath10k_ce_completed_send_next_nolock(ce_state, + &transfer_context, + &buf, + &nbytes, + &id) == 0) { + spin_unlock_bh(&ar_pci->ce_lock); + ce_state->send_cb(ce_state, transfer_context, + buf, nbytes, id); + spin_lock_bh(&ar_pci->ce_lock); + } + } + + /* + * Misc CE interrupts are not being handled, but still need + * to be cleared. + */ + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK); + + spin_unlock_bh(&ar_pci->ce_lock); + ath10k_pci_sleep(ar); +} + +/* + * Handler for per-engine interrupts on ALL active CEs. + * This is used in cases where the system is sharing a + * single interrput for all CEs + */ + +void ath10k_ce_per_engine_service_any(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id; + u32 intr_summary; + + ath10k_pci_wake(ar); + intr_summary = CE_INTERRUPT_SUMMARY(ar); + + for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) { + if (intr_summary & (1 << ce_id)) + intr_summary &= ~(1 << ce_id); + else + /* no intr pending on this CE */ + continue; + + ath10k_ce_per_engine_service(ar, ce_id); + } + + ath10k_pci_sleep(ar); +} + +/* + * Adjust interrupts for the copy complete handler. + * If it's needed for either send or recv, then unmask + * this interrupt; otherwise, mask it. + * + * Called with ce_lock held. + */ +static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state, + int disable_copy_compl_intr) +{ + u32 ctrl_addr = ce_state->ctrl_addr; + struct ath10k *ar = ce_state->ar; + + ath10k_pci_wake(ar); + + if ((!disable_copy_compl_intr) && + (ce_state->send_cb || ce_state->recv_cb)) + ath10k_ce_copy_complete_inter_enable(ar, ctrl_addr); + else + ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); + + ath10k_ce_watermark_intr_disable(ar, ctrl_addr); + + ath10k_pci_sleep(ar); +} + +void ath10k_ce_disable_interrupts(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id; + + ath10k_pci_wake(ar); + for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) { + struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + u32 ctrl_addr = ce_state->ctrl_addr; + + ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); + } + ath10k_pci_sleep(ar); +} + +void ath10k_ce_send_cb_register(struct ce_state *ce_state, + void (*send_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id), + int disable_interrupts) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + ce_state->send_cb = send_cb; + ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts); + spin_unlock_bh(&ar_pci->ce_lock); +} + +void ath10k_ce_recv_cb_register(struct ce_state *ce_state, + void (*recv_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags)) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + ce_state->recv_cb = recv_cb; + ath10k_ce_per_engine_handler_adjust(ce_state, 0); + spin_unlock_bh(&ar_pci->ce_lock); +} + +static int ath10k_ce_init_src_ring(struct ath10k *ar, + unsigned int ce_id, + struct ce_state *ce_state, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_ring_state *src_ring; + unsigned int nentries = attr->src_nentries; + unsigned int ce_nbytes; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + dma_addr_t base_addr; + char *ptr; + + nentries = roundup_pow_of_two(nentries); + + if (ce_state->src_ring) { + WARN_ON(ce_state->src_ring->nentries != nentries); + return 0; + } + + ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); + ptr = kzalloc(ce_nbytes, GFP_KERNEL); + if (ptr == NULL) + return -ENOMEM; + + ce_state->src_ring = (struct ce_ring_state *)ptr; + src_ring = ce_state->src_ring; + + ptr += sizeof(struct ce_ring_state); + src_ring->nentries = nentries; + src_ring->nentries_mask = nentries - 1; + + ath10k_pci_wake(ar); + src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + src_ring->hw_index = src_ring->sw_index; + + src_ring->write_index = + ath10k_ce_src_ring_write_index_get(ar, ctrl_addr); + ath10k_pci_sleep(ar); + + src_ring->per_transfer_context = (void **)ptr; + + /* + * Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + src_ring->base_addr_owner_space_unaligned = + pci_alloc_consistent(ar_pci->pdev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + &base_addr); + src_ring->base_addr_ce_space_unaligned = base_addr; + + src_ring->base_addr_owner_space = PTR_ALIGN( + src_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + src_ring->base_addr_ce_space = ALIGN( + src_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + /* + * Also allocate a shadow src ring in regular + * mem to use for faster access. + */ + src_ring->shadow_base_unaligned = + kmalloc((nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), GFP_KERNEL); + + src_ring->shadow_base = PTR_ALIGN( + src_ring->shadow_base_unaligned, + CE_DESC_RING_ALIGN); + + ath10k_pci_wake(ar); + ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, + src_ring->base_addr_ce_space); + ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries); + ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max); + ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); + ath10k_pci_sleep(ar); + + return 0; +} + +static int ath10k_ce_init_dest_ring(struct ath10k *ar, + unsigned int ce_id, + struct ce_state *ce_state, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_ring_state *dest_ring; + unsigned int nentries = attr->dest_nentries; + unsigned int ce_nbytes; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + dma_addr_t base_addr; + char *ptr; + + nentries = roundup_pow_of_two(nentries); + + if (ce_state->dest_ring) { + WARN_ON(ce_state->dest_ring->nentries != nentries); + return 0; + } + + ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); + ptr = kzalloc(ce_nbytes, GFP_KERNEL); + if (ptr == NULL) + return -ENOMEM; + + ce_state->dest_ring = (struct ce_ring_state *)ptr; + dest_ring = ce_state->dest_ring; + + ptr += sizeof(struct ce_ring_state); + dest_ring->nentries = nentries; + dest_ring->nentries_mask = nentries - 1; + + ath10k_pci_wake(ar); + dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr); + dest_ring->write_index = + ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr); + ath10k_pci_sleep(ar); + + dest_ring->per_transfer_context = (void **)ptr; + + /* + * Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + dest_ring->base_addr_owner_space_unaligned = + pci_alloc_consistent(ar_pci->pdev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + &base_addr); + dest_ring->base_addr_ce_space_unaligned = base_addr; + + /* + * Correctly initialize memory to 0 to prevent garbage + * data crashing system when download firmware + */ + memset(dest_ring->base_addr_owner_space_unaligned, 0, + nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN); + + dest_ring->base_addr_owner_space = PTR_ALIGN( + dest_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + dest_ring->base_addr_ce_space = ALIGN( + dest_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + ath10k_pci_wake(ar); + ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, + dest_ring->base_addr_ce_space); + ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries); + ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); + ath10k_pci_sleep(ar); + + return 0; +} + +static struct ce_state *ath10k_ce_init_state(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_state = NULL; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + + spin_lock_bh(&ar_pci->ce_lock); + + if (!ar_pci->ce_id_to_state[ce_id]) { + ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC); + if (ce_state == NULL) { + spin_unlock_bh(&ar_pci->ce_lock); + return NULL; + } + + ar_pci->ce_id_to_state[ce_id] = ce_state; + ce_state->ar = ar; + ce_state->id = ce_id; + ce_state->ctrl_addr = ctrl_addr; + ce_state->state = CE_RUNNING; + /* Save attribute flags */ + ce_state->attr_flags = attr->flags; + ce_state->src_sz_max = attr->src_sz_max; + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ce_state; +} + +/* + * Initialize a Copy Engine based on caller-supplied attributes. + * This may be called once to initialize both source and destination + * rings or it may be called twice for separate source and destination + * initialization. It may be that only one side or the other is + * initialized by software/firmware. + */ +struct ce_state *ath10k_ce_init(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr) +{ + struct ce_state *ce_state; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + + ce_state = ath10k_ce_init_state(ar, ce_id, attr); + if (!ce_state) { + ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id); + return NULL; + } + + if (attr->src_nentries) { + if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) { + ath10k_err("Failed to initialize CE src ring for ID: %d\n", + ce_id); + ath10k_ce_deinit(ce_state); + return NULL; + } + } + + if (attr->dest_nentries) { + if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) { + ath10k_err("Failed to initialize CE dest ring for ID: %d\n", + ce_id); + ath10k_ce_deinit(ce_state); + return NULL; + } + } + + /* Enable CE error interrupts */ + ath10k_pci_wake(ar); + ath10k_ce_error_intr_enable(ar, ctrl_addr); + ath10k_pci_sleep(ar); + + return ce_state; +} + +void ath10k_ce_deinit(struct ce_state *ce_state) +{ + unsigned int ce_id = ce_state->id; + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ce_state->state = CE_UNUSED; + ar_pci->ce_id_to_state[ce_id] = NULL; + + if (ce_state->src_ring) { + kfree(ce_state->src_ring->shadow_base_unaligned); + pci_free_consistent(ar_pci->pdev, + (ce_state->src_ring->nentries * + sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + ce_state->src_ring->base_addr_owner_space, + ce_state->src_ring->base_addr_ce_space); + kfree(ce_state->src_ring); + } + + if (ce_state->dest_ring) { + pci_free_consistent(ar_pci->pdev, + (ce_state->dest_ring->nentries * + sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + ce_state->dest_ring->base_addr_owner_space, + ce_state->dest_ring->base_addr_ce_space); + kfree(ce_state->dest_ring); + } + kfree(ce_state); +} diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h new file mode 100644 index 00000000000..c17f07c026f --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CE_H_ +#define _CE_H_ + +#include "hif.h" + + +/* Maximum number of Copy Engine's supported */ +#define CE_COUNT_MAX 8 +#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048 + +/* Descriptor rings must be aligned to this boundary */ +#define CE_DESC_RING_ALIGN 8 +#define CE_SENDLIST_ITEMS_MAX 12 +#define CE_SEND_FLAG_GATHER 0x00010000 + +/* + * Copy Engine support: low-level Target-side Copy Engine API. + * This is a hardware access layer used by code that understands + * how to use copy engines. + */ + +struct ce_state; + + +/* Copy Engine operational state */ +enum ce_op_state { + CE_UNUSED, + CE_PAUSED, + CE_RUNNING, +}; + +#define CE_DESC_FLAGS_GATHER (1 << 0) +#define CE_DESC_FLAGS_BYTE_SWAP (1 << 1) +#define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC +#define CE_DESC_FLAGS_META_DATA_LSB 3 + +struct ce_desc { + __le32 addr; + __le16 nbytes; + __le16 flags; /* %CE_DESC_FLAGS_ */ +}; + +/* Copy Engine Ring internal state */ +struct ce_ring_state { + /* Number of entries in this ring; must be power of 2 */ + unsigned int nentries; + unsigned int nentries_mask; + + /* + * For dest ring, this is the next index to be processed + * by software after it was/is received into. + * + * For src ring, this is the last descriptor that was sent + * and completion processed by software. + * + * Regardless of src or dest ring, this is an invariant + * (modulo ring size): + * write index >= read index >= sw_index + */ + unsigned int sw_index; + /* cached copy */ + unsigned int write_index; + /* + * For src ring, this is the next index not yet processed by HW. + * This is a cached copy of the real HW index (read index), used + * for avoiding reading the HW index register more often than + * necessary. + * This extends the invariant: + * write index >= read index >= hw_index >= sw_index + * + * For dest ring, this is currently unused. + */ + /* cached copy */ + unsigned int hw_index; + + /* Start of DMA-coherent area reserved for descriptors */ + /* Host address space */ + void *base_addr_owner_space_unaligned; + /* CE address space */ + u32 base_addr_ce_space_unaligned; + + /* + * Actual start of descriptors. + * Aligned to descriptor-size boundary. + * Points into reserved DMA-coherent area, above. + */ + /* Host address space */ + void *base_addr_owner_space; + + /* CE address space */ + u32 base_addr_ce_space; + /* + * Start of shadow copy of descriptors, within regular memory. + * Aligned to descriptor-size boundary. + */ + void *shadow_base_unaligned; + struct ce_desc *shadow_base; + + void **per_transfer_context; +}; + +/* Copy Engine internal state */ +struct ce_state { + struct ath10k *ar; + unsigned int id; + + unsigned int attr_flags; + + u32 ctrl_addr; + enum ce_op_state state; + + void (*send_cb) (struct ce_state *ce_state, + void *per_transfer_send_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id); + void (*recv_cb) (struct ce_state *ce_state, + void *per_transfer_recv_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags); + + unsigned int src_sz_max; + struct ce_ring_state *src_ring; + struct ce_ring_state *dest_ring; +}; + +struct ce_sendlist_item { + /* e.g. buffer or desc list */ + dma_addr_t data; + union { + /* simple buffer */ + unsigned int nbytes; + /* Rx descriptor list */ + unsigned int ndesc; + } u; + /* externally-specified flags; OR-ed with internal flags */ + u32 flags; +}; + +struct ce_sendlist { + unsigned int num_items; + struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX]; +}; + +/* Copy Engine settable attributes */ +struct ce_attr; + +/*==================Send====================*/ + +/* ath10k_ce_send flags */ +#define CE_SEND_FLAG_BYTE_SWAP 1 + +/* + * Queue a source buffer to be sent to an anonymous destination buffer. + * ce - which copy engine to use + * buffer - address of buffer + * nbytes - number of bytes to send + * transfer_id - arbitrary ID; reflected to destination + * flags - CE_SEND_FLAG_* values + * Returns 0 on success; otherwise an error status. + * + * Note: If no flags are specified, use CE's default data swap mode. + * + * Implementation note: pushes 1 buffer to Source ring + */ +int ath10k_ce_send(struct ce_state *ce_state, + void *per_transfer_send_context, + u32 buffer, + unsigned int nbytes, + /* 14 bits */ + unsigned int transfer_id, + unsigned int flags); + +void ath10k_ce_send_cb_register(struct ce_state *ce_state, + void (*send_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id), + int disable_interrupts); + +/* Append a simple buffer (address/length) to a sendlist. */ +void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, + u32 buffer, + unsigned int nbytes, + /* OR-ed with internal flags */ + u32 flags); + +/* + * Queue a "sendlist" of buffers to be sent using gather to a single + * anonymous destination buffer + * ce - which copy engine to use + * sendlist - list of simple buffers to send using gather + * transfer_id - arbitrary ID; reflected to destination + * Returns 0 on success; otherwise an error status. + * + * Implemenation note: Pushes multiple buffers with Gather to Source ring. + */ +int ath10k_ce_sendlist_send(struct ce_state *ce_state, + void *per_transfer_send_context, + struct ce_sendlist *sendlist, + /* 14 bits */ + unsigned int transfer_id); + +/*==================Recv=======================*/ + +/* + * Make a buffer available to receive. The buffer must be at least of a + * minimal size appropriate for this copy engine (src_sz_max attribute). + * ce - which copy engine to use + * per_transfer_recv_context - context passed back to caller's recv_cb + * buffer - address of buffer in CE space + * Returns 0 on success; otherwise an error status. + * + * Implemenation note: Pushes a buffer to Dest ring. + */ +int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, + void *per_transfer_recv_context, + u32 buffer); + +void ath10k_ce_recv_cb_register(struct ce_state *ce_state, + void (*recv_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags)); + +/* recv flags */ +/* Data is byte-swapped */ +#define CE_RECV_FLAG_SWAPPED 1 + +/* + * Supply data for the next completed unprocessed receive descriptor. + * Pops buffer from Dest ring. + */ +int ath10k_ce_completed_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp); +/* + * Supply data for the next completed unprocessed send descriptor. + * Pops 1 completed send buffer from Source ring. + */ +int ath10k_ce_completed_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp); + +/*==================CE Engine Initialization=======================*/ + +/* Initialize an instance of a CE */ +struct ce_state *ath10k_ce_init(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr); + +/*==================CE Engine Shutdown=======================*/ +/* + * Support clean shutdown by allowing the caller to revoke + * receive buffers. Target DMA must be stopped before using + * this API. + */ +int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp); + +/* + * Support clean shutdown by allowing the caller to cancel + * pending sends. Target DMA must be stopped before using + * this API. + */ +int ath10k_ce_cancel_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp); + +void ath10k_ce_deinit(struct ce_state *ce_state); + +/*==================CE Interrupt Handlers====================*/ +void ath10k_ce_per_engine_service_any(struct ath10k *ar); +void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id); +void ath10k_ce_disable_interrupts(struct ath10k *ar); + +/* ce_attr.flags values */ +/* Use NonSnooping PCIe accesses? */ +#define CE_ATTR_NO_SNOOP 1 + +/* Byte swap data words */ +#define CE_ATTR_BYTE_SWAP_DATA 2 + +/* Swizzle descriptors? */ +#define CE_ATTR_SWIZZLE_DESCRIPTORS 4 + +/* no interrupt on copy completion */ +#define CE_ATTR_DIS_INTR 8 + +/* Attributes of an instance of a Copy Engine */ +struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; + + /* currently not in use */ + unsigned int priority; + + /* #entries in source ring - Must be a power of 2 */ + unsigned int src_nentries; + + /* + * Max source send size for this CE. + * This is also the minimum size of a destination buffer. + */ + unsigned int src_sz_max; + + /* #entries in destination ring - Must be a power of 2 */ + unsigned int dest_nentries; + + /* Future use */ + void *reserved; +}; + +/* + * When using sendlist_send to transfer multiple buffer fragments, the + * transfer context of each fragment, except last one, will be filled + * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for + * each fragment done with send and the transfer context would be + * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the + * status of a send completion. + */ +#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef) + +#define SR_BA_ADDRESS 0x0000 +#define SR_SIZE_ADDRESS 0x0004 +#define DR_BA_ADDRESS 0x0008 +#define DR_SIZE_ADDRESS 0x000c +#define CE_CMD_ADDRESS 0x0018 + +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 17 +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 17 +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00020000 +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \ + (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \ + CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) + +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16 +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16 +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00010000 +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \ + (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \ + CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \ + (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \ + CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) + +#define CE_CTRL1_DMAX_LENGTH_MSB 15 +#define CE_CTRL1_DMAX_LENGTH_LSB 0 +#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000ffff +#define CE_CTRL1_DMAX_LENGTH_GET(x) \ + (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB) +#define CE_CTRL1_DMAX_LENGTH_SET(x) \ + (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK) + +#define CE_CTRL1_ADDRESS 0x0010 +#define CE_CTRL1_HW_MASK 0x0007ffff +#define CE_CTRL1_SW_MASK 0x0007ffff +#define CE_CTRL1_HW_WRITE_MASK 0x00000000 +#define CE_CTRL1_SW_WRITE_MASK 0x0007ffff +#define CE_CTRL1_RSTMASK 0xffffffff +#define CE_CTRL1_RESET 0x00000080 + +#define CE_CMD_HALT_STATUS_MSB 3 +#define CE_CMD_HALT_STATUS_LSB 3 +#define CE_CMD_HALT_STATUS_MASK 0x00000008 +#define CE_CMD_HALT_STATUS_GET(x) \ + (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB) +#define CE_CMD_HALT_STATUS_SET(x) \ + (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK) +#define CE_CMD_HALT_STATUS_RESET 0 +#define CE_CMD_HALT_MSB 0 +#define CE_CMD_HALT_MASK 0x00000001 + +#define HOST_IE_COPY_COMPLETE_MSB 0 +#define HOST_IE_COPY_COMPLETE_LSB 0 +#define HOST_IE_COPY_COMPLETE_MASK 0x00000001 +#define HOST_IE_COPY_COMPLETE_GET(x) \ + (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB) +#define HOST_IE_COPY_COMPLETE_SET(x) \ + (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK) +#define HOST_IE_COPY_COMPLETE_RESET 0 +#define HOST_IE_ADDRESS 0x002c + +#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010 +#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008 +#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004 +#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002 +#define HOST_IS_COPY_COMPLETE_MASK 0x00000001 +#define HOST_IS_ADDRESS 0x0030 + +#define MISC_IE_ADDRESS 0x0034 + +#define MISC_IS_AXI_ERR_MASK 0x00000400 + +#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200 +#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100 +#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080 +#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040 +#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020 + +#define MISC_IS_ADDRESS 0x0038 + +#define SR_WR_INDEX_ADDRESS 0x003c + +#define DST_WR_INDEX_ADDRESS 0x0040 + +#define CURRENT_SRRI_ADDRESS 0x0044 + +#define CURRENT_DRRI_ADDRESS 0x0048 + +#define SRC_WATERMARK_LOW_MSB 31 +#define SRC_WATERMARK_LOW_LSB 16 +#define SRC_WATERMARK_LOW_MASK 0xffff0000 +#define SRC_WATERMARK_LOW_GET(x) \ + (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB) +#define SRC_WATERMARK_LOW_SET(x) \ + (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK) +#define SRC_WATERMARK_LOW_RESET 0 +#define SRC_WATERMARK_HIGH_MSB 15 +#define SRC_WATERMARK_HIGH_LSB 0 +#define SRC_WATERMARK_HIGH_MASK 0x0000ffff +#define SRC_WATERMARK_HIGH_GET(x) \ + (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB) +#define SRC_WATERMARK_HIGH_SET(x) \ + (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK) +#define SRC_WATERMARK_HIGH_RESET 0 +#define SRC_WATERMARK_ADDRESS 0x004c + +#define DST_WATERMARK_LOW_LSB 16 +#define DST_WATERMARK_LOW_MASK 0xffff0000 +#define DST_WATERMARK_LOW_SET(x) \ + (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK) +#define DST_WATERMARK_LOW_RESET 0 +#define DST_WATERMARK_HIGH_MSB 15 +#define DST_WATERMARK_HIGH_LSB 0 +#define DST_WATERMARK_HIGH_MASK 0x0000ffff +#define DST_WATERMARK_HIGH_GET(x) \ + (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB) +#define DST_WATERMARK_HIGH_SET(x) \ + (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK) +#define DST_WATERMARK_HIGH_RESET 0 +#define DST_WATERMARK_ADDRESS 0x0050 + + +static inline u32 ath10k_ce_base_address(unsigned int ce_id) +{ + return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id; +} + +#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK | \ + HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \ + HOST_IS_DST_RING_LOW_WATERMARK_MASK | \ + HOST_IS_DST_RING_HIGH_WATERMARK_MASK) + +#define CE_ERROR_MASK (MISC_IS_AXI_ERR_MASK | \ + MISC_IS_DST_ADDR_ERR_MASK | \ + MISC_IS_SRC_LEN_ERR_MASK | \ + MISC_IS_DST_MAX_LEN_VIO_MASK | \ + MISC_IS_DST_RING_OVERFLOW_MASK | \ + MISC_IS_SRC_RING_OVERFLOW_MASK) + +#define CE_SRC_RING_TO_DESC(baddr, idx) \ + (&(((struct ce_desc *)baddr)[idx])) + +#define CE_DEST_RING_TO_DESC(baddr, idx) \ + (&(((struct ce_desc *)baddr)[idx])) + +/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */ +#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \ + (((int)(toidx)-(int)(fromidx)) & (nentries_mask)) + +#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) + +#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB 8 +#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK 0x0000ff00 +#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \ + (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \ + CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB) +#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000 + +#define CE_INTERRUPT_SUMMARY(ar) \ + CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \ + ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \ + CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)) + +#endif /* _CE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c new file mode 100644 index 00000000000..2b3426b1ff3 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/firmware.h> + +#include "core.h" +#include "mac.h" +#include "htc.h" +#include "hif.h" +#include "wmi.h" +#include "bmi.h" +#include "debug.h" +#include "htt.h" + +unsigned int ath10k_debug_mask; +static bool uart_print; +static unsigned int ath10k_p2p; +module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); +module_param(uart_print, bool, 0644); +module_param_named(p2p, ath10k_p2p, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); +MODULE_PARM_DESC(uart_print, "Uart target debugging"); +MODULE_PARM_DESC(p2p, "Enable ath10k P2P support"); + +static const struct ath10k_hw_params ath10k_hw_params_list[] = { + { + .id = QCA988X_HW_1_0_VERSION, + .name = "qca988x hw1.0", + .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR, + .fw = { + .dir = QCA988X_HW_1_0_FW_DIR, + .fw = QCA988X_HW_1_0_FW_FILE, + .otp = QCA988X_HW_1_0_OTP_FILE, + .board = QCA988X_HW_1_0_BOARD_DATA_FILE, + }, + }, + { + .id = QCA988X_HW_2_0_VERSION, + .name = "qca988x hw2.0", + .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, + .fw = { + .dir = QCA988X_HW_2_0_FW_DIR, + .fw = QCA988X_HW_2_0_FW_FILE, + .otp = QCA988X_HW_2_0_OTP_FILE, + .board = QCA988X_HW_2_0_BOARD_DATA_FILE, + }, + }, +}; + +static void ath10k_send_suspend_complete(struct ath10k *ar) +{ + ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__); + + ar->is_target_paused = true; + wake_up(&ar->event_queue); +} + +static int ath10k_check_fw_version(struct ath10k *ar) +{ + char version[32]; + + if (ar->fw_version_major >= SUPPORTED_FW_MAJOR && + ar->fw_version_minor >= SUPPORTED_FW_MINOR && + ar->fw_version_release >= SUPPORTED_FW_RELEASE && + ar->fw_version_build >= SUPPORTED_FW_BUILD) + return 0; + + snprintf(version, sizeof(version), "%u.%u.%u.%u", + SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR, + SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD); + + ath10k_warn("WARNING: Firmware version %s is not officially supported.\n", + ar->hw->wiphy->fw_version); + ath10k_warn("Please upgrade to version %s (or newer)\n", version); + + return 0; +} + +static int ath10k_init_connect_htc(struct ath10k *ar) +{ + int status; + + status = ath10k_wmi_connect_htc_service(ar); + if (status) + goto conn_fail; + + /* Start HTC */ + status = ath10k_htc_start(ar->htc); + if (status) + goto conn_fail; + + /* Wait for WMI event to be ready */ + status = ath10k_wmi_wait_for_service_ready(ar); + if (status <= 0) { + ath10k_warn("wmi service ready event not received"); + status = -ETIMEDOUT; + goto timeout; + } + + ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n"); + return 0; + +timeout: + ath10k_htc_stop(ar->htc); +conn_fail: + return status; +} + +static int ath10k_init_configure_target(struct ath10k *ar) +{ + u32 param_host; + int ret; + + /* tell target which HTC version it is used*/ + ret = ath10k_bmi_write32(ar, hi_app_host_interest, + HTC_PROTOCOL_VERSION); + if (ret) { + ath10k_err("settings HTC version failed\n"); + return ret; + } + + /* set the firmware mode to STA/IBSS/AP */ + ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m_host); + if (ret) { + ath10k_err("setting firmware mode (1/2) failed\n"); + return ret; + } + + /* TODO following parameters need to be re-visited. */ + /* num_device */ + param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT); + /* Firmware mode */ + /* FIXME: Why FW_MODE_AP ??.*/ + param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT); + /* mac_addr_method */ + param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); + /* firmware_bridge */ + param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); + /* fwsubmode */ + param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT); + + ret = ath10k_bmi_write32(ar, hi_option_flag, param_host); + if (ret) { + ath10k_err("setting firmware mode (2/2) failed\n"); + return ret; + } + + /* We do all byte-swapping on the host */ + ret = ath10k_bmi_write32(ar, hi_be, 0); + if (ret) { + ath10k_err("setting host CPU BE mode failed\n"); + return ret; + } + + /* FW descriptor/Data swap flags */ + ret = ath10k_bmi_write32(ar, hi_fw_swap, 0); + + if (ret) { + ath10k_err("setting FW data/desc swap flags failed\n"); + return ret; + } + + return 0; +} + +static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, + const char *dir, + const char *file) +{ + char filename[100]; + const struct firmware *fw; + int ret; + + if (file == NULL) + return ERR_PTR(-ENOENT); + + if (dir == NULL) + dir = "."; + + snprintf(filename, sizeof(filename), "%s/%s", dir, file); + ret = request_firmware(&fw, filename, ar->dev); + if (ret) + return ERR_PTR(ret); + + return fw; +} + +static int ath10k_push_board_ext_data(struct ath10k *ar, + const struct firmware *fw) +{ + u32 board_data_size = QCA988X_BOARD_DATA_SZ; + u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; + u32 board_ext_data_addr; + int ret; + + ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr); + if (ret) { + ath10k_err("could not read board ext data addr (%d)\n", ret); + return ret; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "ath10k: Board extended Data download addr: 0x%x\n", + board_ext_data_addr); + + if (board_ext_data_addr == 0) + return 0; + + if (fw->size != (board_data_size + board_ext_data_size)) { + ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n", + fw->size, board_data_size, board_ext_data_size); + return -EINVAL; + } + + ret = ath10k_bmi_write_memory(ar, board_ext_data_addr, + fw->data + board_data_size, + board_ext_data_size); + if (ret) { + ath10k_err("could not write board ext data (%d)\n", ret); + return ret; + } + + ret = ath10k_bmi_write32(ar, hi_board_ext_data_config, + (board_ext_data_size << 16) | 1); + if (ret) { + ath10k_err("could not write board ext data bit (%d)\n", ret); + return ret; + } + + return 0; +} + +static int ath10k_download_board_data(struct ath10k *ar) +{ + u32 board_data_size = QCA988X_BOARD_DATA_SZ; + u32 address; + const struct firmware *fw; + int ret; + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(fw)) { + ath10k_err("could not fetch board data fw file (%ld)\n", + PTR_ERR(fw)); + return PTR_ERR(fw); + } + + ret = ath10k_push_board_ext_data(ar, fw); + if (ret) { + ath10k_err("could not push board ext data (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_read32(ar, hi_board_data, &address); + if (ret) { + ath10k_err("could not read board data addr (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_write_memory(ar, address, fw->data, + min_t(u32, board_data_size, fw->size)); + if (ret) { + ath10k_err("could not write board data (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1); + if (ret) { + ath10k_err("could not write board data bit (%d)\n", ret); + goto exit; + } + +exit: + release_firmware(fw); + return ret; +} + +static int ath10k_download_and_run_otp(struct ath10k *ar) +{ + const struct firmware *fw; + u32 address; + u32 exec_param; + int ret; + + /* OTP is optional */ + + if (ar->hw_params.fw.otp == NULL) { + ath10k_info("otp file not defined\n"); + return 0; + } + + address = ar->hw_params.patch_load_addr; + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.otp); + if (IS_ERR(fw)) { + ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw)); + return 0; + } + + ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + if (ret) { + ath10k_err("could not write otp (%d)\n", ret); + goto exit; + } + + exec_param = 0; + ret = ath10k_bmi_execute(ar, address, &exec_param); + if (ret) { + ath10k_err("could not execute otp (%d)\n", ret); + goto exit; + } + +exit: + release_firmware(fw); + return ret; +} + +static int ath10k_download_fw(struct ath10k *ar) +{ + const struct firmware *fw; + u32 address; + int ret; + + if (ar->hw_params.fw.fw == NULL) + return -EINVAL; + + address = ar->hw_params.patch_load_addr; + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.fw); + if (IS_ERR(fw)) { + ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw)); + return PTR_ERR(fw); + } + + ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + if (ret) { + ath10k_err("could not write fw (%d)\n", ret); + goto exit; + } + +exit: + release_firmware(fw); + return ret; +} + +static int ath10k_init_download_firmware(struct ath10k *ar) +{ + int ret; + + ret = ath10k_download_board_data(ar); + if (ret) + return ret; + + ret = ath10k_download_and_run_otp(ar); + if (ret) + return ret; + + ret = ath10k_download_fw(ar); + if (ret) + return ret; + + return ret; +} + +static int ath10k_init_uart(struct ath10k *ar) +{ + int ret; + + /* + * Explicitly setting UART prints to zero as target turns it on + * based on scratch registers. + */ + ret = ath10k_bmi_write32(ar, hi_serial_enable, 0); + if (ret) { + ath10k_warn("could not disable UART prints (%d)\n", ret); + return ret; + } + + if (!uart_print) { + ath10k_info("UART prints disabled\n"); + return 0; + } + + ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7); + if (ret) { + ath10k_warn("could not enable UART prints (%d)\n", ret); + return ret; + } + + ret = ath10k_bmi_write32(ar, hi_serial_enable, 1); + if (ret) { + ath10k_warn("could not enable UART prints (%d)\n", ret); + return ret; + } + + ath10k_info("UART prints enabled\n"); + return 0; +} + +static int ath10k_init_hw_params(struct ath10k *ar) +{ + const struct ath10k_hw_params *uninitialized_var(hw_params); + int i; + + for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) { + hw_params = &ath10k_hw_params_list[i]; + + if (hw_params->id == ar->target_version) + break; + } + + if (i == ARRAY_SIZE(ath10k_hw_params_list)) { + ath10k_err("Unsupported hardware version: 0x%x\n", + ar->target_version); + return -EINVAL; + } + + ar->hw_params = *hw_params; + + ath10k_info("Hardware name %s version 0x%x\n", + ar->hw_params.name, ar->target_version); + + return 0; +} + +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + enum ath10k_bus bus, + const struct ath10k_hif_ops *hif_ops) +{ + struct ath10k *ar; + + ar = ath10k_mac_create(); + if (!ar) + return NULL; + + ar->ath_common.priv = ar; + ar->ath_common.hw = ar->hw; + + ar->p2p = !!ath10k_p2p; + ar->dev = dev; + + ar->hif.priv = hif_priv; + ar->hif.ops = hif_ops; + ar->hif.bus = bus; + + ar->free_vdev_map = 0xFF; /* 8 vdevs */ + + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + init_completion(&ar->scan.on_channel); + + init_completion(&ar->install_key_done); + init_completion(&ar->vdev_setup_done); + + setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + + ar->workqueue = create_singlethread_workqueue("ath10k_wq"); + if (!ar->workqueue) + goto err_wq; + + mutex_init(&ar->conf_mutex); + spin_lock_init(&ar->data_lock); + + INIT_LIST_HEAD(&ar->peers); + init_waitqueue_head(&ar->peer_mapping_wq); + + init_completion(&ar->offchan_tx_completed); + INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); + skb_queue_head_init(&ar->offchan_tx_queue); + + init_waitqueue_head(&ar->event_queue); + + return ar; + +err_wq: + ath10k_mac_destroy(ar); + return NULL; +} +EXPORT_SYMBOL(ath10k_core_create); + +void ath10k_core_destroy(struct ath10k *ar) +{ + flush_workqueue(ar->workqueue); + destroy_workqueue(ar->workqueue); + + ath10k_mac_destroy(ar); +} +EXPORT_SYMBOL(ath10k_core_destroy); + + +int ath10k_core_register(struct ath10k *ar) +{ + struct ath10k_htc_ops htc_ops; + struct bmi_target_info target_info; + int status; + + memset(&target_info, 0, sizeof(target_info)); + status = ath10k_bmi_get_target_info(ar, &target_info); + if (status) + goto err; + + ar->target_version = target_info.version; + ar->hw->wiphy->hw_version = target_info.version; + + status = ath10k_init_hw_params(ar); + if (status) + goto err; + + if (ath10k_init_configure_target(ar)) { + status = -EINVAL; + goto err; + } + + status = ath10k_init_download_firmware(ar); + if (status) + goto err; + + status = ath10k_init_uart(ar); + if (status) + goto err; + + htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; + + ar->htc = ath10k_htc_create(ar, &htc_ops); + if (IS_ERR(ar->htc)) { + status = PTR_ERR(ar->htc); + ath10k_err("could not create HTC (%d)\n", status); + goto err; + } + + status = ath10k_bmi_done(ar); + if (status) + goto err_htc_destroy; + + status = ath10k_wmi_attach(ar); + if (status) { + ath10k_err("WMI attach failed: %d\n", status); + goto err_htc_destroy; + } + + status = ath10k_htc_wait_target(ar->htc); + if (status) + goto err_wmi_detach; + + ar->htt = ath10k_htt_attach(ar); + if (!ar->htt) { + status = -ENOMEM; + goto err_wmi_detach; + } + + status = ath10k_init_connect_htc(ar); + if (status) + goto err_htt_detach; + + ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version); + + status = ath10k_check_fw_version(ar); + if (status) + goto err_disconnect_htc; + + status = ath10k_wmi_cmd_init(ar); + if (status) { + ath10k_err("could not send WMI init command (%d)\n", status); + goto err_disconnect_htc; + } + + status = ath10k_wmi_wait_for_unified_ready(ar); + if (status <= 0) { + ath10k_err("wmi unified ready event not received\n"); + status = -ETIMEDOUT; + goto err_disconnect_htc; + } + + status = ath10k_htt_attach_target(ar->htt); + if (status) + goto err_disconnect_htc; + + status = ath10k_mac_register(ar); + if (status) + goto err_disconnect_htc; + + status = ath10k_debug_create(ar); + if (status) { + ath10k_err("unable to initialize debugfs\n"); + goto err_unregister_mac; + } + + return 0; + +err_unregister_mac: + ath10k_mac_unregister(ar); +err_disconnect_htc: + ath10k_htc_stop(ar->htc); +err_htt_detach: + ath10k_htt_detach(ar->htt); +err_wmi_detach: + ath10k_wmi_detach(ar); +err_htc_destroy: + ath10k_htc_destroy(ar->htc); +err: + return status; +} +EXPORT_SYMBOL(ath10k_core_register); + +void ath10k_core_unregister(struct ath10k *ar) +{ + /* We must unregister from mac80211 before we stop HTC and HIF. + * Otherwise we will fail to submit commands to FW and mac80211 will be + * unhappy about callback failures. */ + ath10k_mac_unregister(ar); + ath10k_htc_stop(ar->htc); + ath10k_htt_detach(ar->htt); + ath10k_wmi_detach(ar); + ath10k_htc_destroy(ar->htc); +} +EXPORT_SYMBOL(ath10k_core_unregister); + +int ath10k_core_target_suspend(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); + + ret = ath10k_wmi_pdev_suspend_target(ar); + if (ret) + ath10k_warn("could not suspend target (%d)\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath10k_core_target_suspend); + +int ath10k_core_target_resume(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); + + ret = ath10k_wmi_pdev_resume_target(ar); + if (ret) + ath10k_warn("could not resume target (%d)\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath10k_core_target_resume); + +MODULE_AUTHOR("Qualcomm Atheros"); +MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h new file mode 100644 index 00000000000..539336d1be4 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CORE_H_ +#define _CORE_H_ + +#include <linux/completion.h> +#include <linux/if_ether.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include "htc.h" +#include "hw.h" +#include "targaddrs.h" +#include "wmi.h" +#include "../ath.h" +#include "../regd.h" + +#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) +#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) +#define WO(_f) ((_f##_OFFSET) >> 2) + +#define ATH10K_SCAN_ID 0 +#define WMI_READY_TIMEOUT (5 * HZ) +#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ) + +/* Antenna noise floor */ +#define ATH10K_DEFAULT_NOISE_FLOOR -95 + +struct ath10k; + +enum ath10k_bus { + ATH10K_BUS_PCI, +}; + +struct ath10k_skb_cb { + dma_addr_t paddr; + bool is_mapped; + bool is_aborted; + + struct { + u8 vdev_id; + u16 msdu_id; + u8 tid; + bool is_offchan; + bool is_conf; + bool discard; + bool no_ack; + u8 refcount; + struct sk_buff *txfrag; + struct sk_buff *msdu; + } __packed htt; + + /* 4 bytes left on 64bit arch */ +} __packed; + +static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) > + IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb) +{ + if (ATH10K_SKB_CB(skb)->is_mapped) + return -EINVAL; + + ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr))) + return -EIO; + + ATH10K_SKB_CB(skb)->is_mapped = true; + return 0; +} + +static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb) +{ + if (!ATH10K_SKB_CB(skb)->is_mapped) + return -EINVAL; + + dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len, + DMA_TO_DEVICE); + ATH10K_SKB_CB(skb)->is_mapped = false; + return 0; +} + +static inline u32 host_interest_item_address(u32 item_offset) +{ + return QCA988X_HOST_INTEREST_ADDRESS + item_offset; +} + +struct ath10k_bmi { + bool done_sent; +}; + +struct ath10k_wmi { + enum ath10k_htc_ep_id eid; + struct completion service_ready; + struct completion unified_ready; + atomic_t pending_tx_count; + wait_queue_head_t wq; + + struct sk_buff_head wmi_event_list; + struct work_struct wmi_event_work; +}; + +struct ath10k_peer_stat { + u8 peer_macaddr[ETH_ALEN]; + u32 peer_rssi; + u32 peer_tx_rate; +}; + +struct ath10k_target_stats { + /* PDEV stats */ + s32 ch_noise_floor; + u32 tx_frame_count; + u32 rx_frame_count; + u32 rx_clear_count; + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + + /* PDEV TX stats */ + s32 comp_queued; + s32 comp_delivered; + s32 msdu_enqued; + s32 mpdu_enqued; + s32 wmm_drop; + s32 local_enqued; + s32 local_freed; + s32 hw_queued; + s32 hw_reaped; + s32 underrun; + s32 tx_abort; + s32 mpdus_requed; + u32 tx_ko; + u32 data_rc; + u32 self_triggers; + u32 sw_retry_failure; + u32 illgl_rate_phy_err; + u32 pdev_cont_xretry; + u32 pdev_tx_timeout; + u32 pdev_resets; + u32 phy_underrun; + u32 txop_ovf; + + /* PDEV RX stats */ + s32 mid_ppdu_route_change; + s32 status_rcvd; + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + s32 htt_msdus; + s32 htt_mpdus; + s32 loc_msdus; + s32 loc_mpdus; + s32 oversize_amsdu; + s32 phy_errs; + s32 phy_err_drop; + s32 mpdu_errs; + + /* VDEV STATS */ + + /* PEER STATS */ + u8 peers; + struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS]; + + /* TODO: Beacon filter stats */ + +}; + +#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */ + +struct ath10k_peer { + struct list_head list; + int vdev_id; + u8 addr[ETH_ALEN]; + DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS); + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; +}; + +#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) + +struct ath10k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + u32 beacon_interval; + u32 dtim_period; + + struct ath10k *ar; + struct ieee80211_vif *vif; + + struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; + u8 def_wep_key_index; + + u16 tx_seq_no; + + union { + struct { + u8 bssid[ETH_ALEN]; + u32 uapsd; + } sta; + struct { + /* 127 stations; wmi limit */ + u8 tim_bitmap[16]; + u8 tim_len; + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool hidden_ssid; + /* P2P_IE with NoA attribute for P2P_GO case */ + u32 noa_len; + u8 *noa_data; + } ap; + struct { + u8 bssid[ETH_ALEN]; + } ibss; + } u; +}; + +struct ath10k_vif_iter { + u32 vdev_id; + struct ath10k_vif *arvif; +}; + +struct ath10k_debug { + struct dentry *debugfs_phy; + + struct ath10k_target_stats target_stats; + u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + + struct completion event_stats_compl; +}; + +struct ath10k { + struct ath_common ath_common; + struct ieee80211_hw *hw; + struct device *dev; + u8 mac_addr[ETH_ALEN]; + + u32 target_version; + u8 fw_version_major; + u32 fw_version_minor; + u16 fw_version_release; + u16 fw_version_build; + u32 phy_capability; + u32 hw_min_tx_power; + u32 hw_max_tx_power; + u32 ht_cap_info; + u32 vht_cap_info; + + struct targetdef *targetdef; + struct hostdef *hostdef; + + bool p2p; + + struct { + void *priv; + enum ath10k_bus bus; + const struct ath10k_hif_ops *ops; + } hif; + + struct ath10k_wmi wmi; + + wait_queue_head_t event_queue; + bool is_target_paused; + + struct ath10k_bmi bmi; + + struct ath10k_htc *htc; + struct ath10k_htt *htt; + + struct ath10k_hw_params { + u32 id; + const char *name; + u32 patch_load_addr; + + struct ath10k_hw_params_fw { + const char *dir; + const char *fw; + const char *otp; + const char *board; + } fw; + } hw_params; + + struct { + struct completion started; + struct completion completed; + struct completion on_channel; + struct timer_list timeout; + bool is_roc; + bool in_progress; + bool aborting; + int vdev_id; + int roc_freq; + } scan; + + struct { + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + } mac; + + /* should never be NULL; needed for regular htt rx */ + struct ieee80211_channel *rx_channel; + + /* valid during scan; needed for mgmt rx during scan */ + struct ieee80211_channel *scan_channel; + + int free_vdev_map; + int monitor_vdev_id; + bool monitor_enabled; + bool monitor_present; + unsigned int filter_flags; + + struct wmi_pdev_set_wmm_params_arg wmm_params; + struct completion install_key_done; + + struct completion vdev_setup_done; + + struct workqueue_struct *workqueue; + + /* prevents concurrent FW reconfiguration */ + struct mutex conf_mutex; + + /* protects shared structure data */ + spinlock_t data_lock; + + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + + struct work_struct offchan_tx_work; + struct sk_buff_head offchan_tx_queue; + struct completion offchan_tx_completed; + struct sk_buff *offchan_tx_skb; + +#ifdef CONFIG_ATH10K_DEBUGFS + struct ath10k_debug debug; +#endif +}; + +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + enum ath10k_bus bus, + const struct ath10k_hif_ops *hif_ops); +void ath10k_core_destroy(struct ath10k *ar); + +int ath10k_core_register(struct ath10k *ar); +void ath10k_core_unregister(struct ath10k *ar); + +int ath10k_core_target_suspend(struct ath10k *ar); +int ath10k_core_target_resume(struct ath10k *ar); + +#endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c new file mode 100644 index 00000000000..499034b873d --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/debugfs.h> + +#include "core.h" +#include "debug.h" + +static int ath10k_printk(const char *level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int rtn; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + rtn = printk("%sath10k: %pV", level, &vaf); + + va_end(args); + + return rtn; +} + +int ath10k_info(const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = ath10k_printk(KERN_INFO, "%pV", &vaf); + trace_ath10k_log_info(&vaf); + va_end(args); + + return ret; +} +EXPORT_SYMBOL(ath10k_info); + +int ath10k_err(const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = ath10k_printk(KERN_ERR, "%pV", &vaf); + trace_ath10k_log_err(&vaf); + va_end(args); + + return ret; +} +EXPORT_SYMBOL(ath10k_err); + +int ath10k_warn(const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret = 0; + + va_start(args, fmt); + vaf.va = &args; + + if (net_ratelimit()) + ret = ath10k_printk(KERN_WARNING, "%pV", &vaf); + + trace_ath10k_log_warn(&vaf); + + va_end(args); + + return ret; +} +EXPORT_SYMBOL(ath10k_warn); + +#ifdef CONFIG_ATH10K_DEBUGFS + +void ath10k_debug_read_service_map(struct ath10k *ar, + void *service_map, + size_t map_size) +{ + memcpy(ar->debug.wmi_service_bitmap, service_map, map_size); +} + +static ssize_t ath10k_read_wmi_services(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char *buf; + unsigned int len = 0, buf_len = 1500; + const char *status; + ssize_t ret_cnt; + int i; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + if (len > buf_len) + len = buf_len; + + for (i = 0; i < WMI_SERVICE_LAST; i++) { + if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i)) + status = "enabled"; + else + status = "disabled"; + + len += scnprintf(buf + len, buf_len - len, + "0x%02x - %20s - %s\n", + i, wmi_service_name(i), status); + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + mutex_unlock(&ar->conf_mutex); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_wmi_services = { + .read = ath10k_read_wmi_services, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath10k_debug_read_target_stats(struct ath10k *ar, + struct wmi_stats_event *ev) +{ + u8 *tmp = ev->data; + struct ath10k_target_stats *stats; + int num_pdev_stats, num_vdev_stats, num_peer_stats; + struct wmi_pdev_stats *ps; + int i; + + mutex_lock(&ar->conf_mutex); + + stats = &ar->debug.target_stats; + + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */ + num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */ + num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */ + + if (num_pdev_stats) { + ps = (struct wmi_pdev_stats *)tmp; + + stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf); + stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count); + stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count); + stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count); + stats->cycle_count = __le32_to_cpu(ps->cycle_count); + stats->phy_err_count = __le32_to_cpu(ps->phy_err_count); + stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr); + + stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued); + stats->comp_delivered = + __le32_to_cpu(ps->wal.tx.comp_delivered); + stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued); + stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued); + stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop); + stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued); + stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed); + stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued); + stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped); + stats->underrun = __le32_to_cpu(ps->wal.tx.underrun); + stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort); + stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed); + stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko); + stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc); + stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers); + stats->sw_retry_failure = + __le32_to_cpu(ps->wal.tx.sw_retry_failure); + stats->illgl_rate_phy_err = + __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err); + stats->pdev_cont_xretry = + __le32_to_cpu(ps->wal.tx.pdev_cont_xretry); + stats->pdev_tx_timeout = + __le32_to_cpu(ps->wal.tx.pdev_tx_timeout); + stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets); + stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun); + stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf); + + stats->mid_ppdu_route_change = + __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change); + stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd); + stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags); + stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags); + stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags); + stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags); + stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus); + stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus); + stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus); + stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus); + stats->oversize_amsdu = + __le32_to_cpu(ps->wal.rx.oversize_amsdu); + stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs); + stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop); + stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs); + + tmp += sizeof(struct wmi_pdev_stats); + } + + /* 0 or max vdevs */ + /* Currently firmware does not support VDEV stats */ + if (num_vdev_stats) { + struct wmi_vdev_stats *vdev_stats; + + for (i = 0; i < num_vdev_stats; i++) { + vdev_stats = (struct wmi_vdev_stats *)tmp; + tmp += sizeof(struct wmi_vdev_stats); + } + } + + if (num_peer_stats) { + struct wmi_peer_stats *peer_stats; + struct ath10k_peer_stat *s; + + stats->peers = num_peer_stats; + + for (i = 0; i < num_peer_stats; i++) { + peer_stats = (struct wmi_peer_stats *)tmp; + s = &stats->peer_stat[i]; + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr, + s->peer_macaddr); + s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi); + s->peer_tx_rate = + __le32_to_cpu(peer_stats->peer_tx_rate); + + tmp += sizeof(struct wmi_peer_stats); + } + } + + mutex_unlock(&ar->conf_mutex); + complete(&ar->debug.event_stats_compl); +} + +static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + struct ath10k_target_stats *fw_stats; + char *buf; + unsigned int len = 0, buf_len = 2500; + ssize_t ret_cnt; + long left; + int i; + int ret; + + fw_stats = &ar->debug.target_stats; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT); + if (ret) { + ath10k_warn("could not request stats (%d)\n", ret); + kfree(buf); + return -EIO; + } + + left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ); + + if (left <= 0) { + kfree(buf); + return -ETIMEDOUT; + } + + mutex_lock(&ar->conf_mutex); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", fw_stats->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", fw_stats->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", fw_stats->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", fw_stats->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", fw_stats->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", fw_stats->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", fw_stats->phy_err_count); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", fw_stats->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", fw_stats->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", fw_stats->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", fw_stats->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", fw_stats->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", fw_stats->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", fw_stats->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", fw_stats->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", fw_stats->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", fw_stats->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", fw_stats->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requed", fw_stats->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Excessive retries", fw_stats->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW rate", fw_stats->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Sched self tiggers", fw_stats->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Dropped due to SW retries", + fw_stats->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Illegal rate phy errors", + fw_stats->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Pdev continous xretry", fw_stats->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "TX timeout", fw_stats->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PDEV resets", fw_stats->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY underrun", fw_stats->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU is more than txop limit", fw_stats->txop_ovf); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + fw_stats->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", fw_stats->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", fw_stats->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", fw_stats->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", fw_stats->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", fw_stats->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", fw_stats->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", fw_stats->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", fw_stats->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", fw_stats->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", fw_stats->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", fw_stats->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", fw_stats->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PEER stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + for (i = 0; i < fw_stats->peers; i++) { + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "Peer MAC address", + fw_stats->peer_stat[i].peer_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer RSSI", fw_stats->peer_stat[i].peer_rssi); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer TX rate", + fw_stats->peer_stat[i].peer_tx_rate); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + mutex_unlock(&ar->conf_mutex); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_fw_stats = { + .read = ath10k_read_fw_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath10k_debug_create(struct ath10k *ar) +{ + ar->debug.debugfs_phy = debugfs_create_dir("ath10k", + ar->hw->wiphy->debugfsdir); + + if (!ar->debug.debugfs_phy) + return -ENOMEM; + + init_completion(&ar->debug.event_stats_compl); + + debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, + &fops_fw_stats); + + debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, + &fops_wmi_services); + + return 0; +} +#endif /* CONFIG_ATH10K_DEBUGFS */ + +#ifdef CONFIG_ATH10K_DEBUG +void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (ath10k_debug_mask & mask) + ath10k_printk(KERN_DEBUG, "%pV", &vaf); + + trace_ath10k_log_dbg(mask, &vaf); + + va_end(args); +} +EXPORT_SYMBOL(ath10k_dbg); + +void ath10k_dbg_dump(enum ath10k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + if (ath10k_debug_mask & mask) { + if (msg) + ath10k_dbg(mask, "%s\n", msg); + + print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); + } + + /* tracing code doesn't like null strings :/ */ + trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "", + buf, len); +} +EXPORT_SYMBOL(ath10k_dbg_dump); + +#endif /* CONFIG_ATH10K_DEBUG */ diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h new file mode 100644 index 00000000000..168140c5402 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include <linux/types.h> +#include "trace.h" + +enum ath10k_debug_mask { + ATH10K_DBG_PCI = 0x00000001, + ATH10K_DBG_WMI = 0x00000002, + ATH10K_DBG_HTC = 0x00000004, + ATH10K_DBG_HTT = 0x00000008, + ATH10K_DBG_MAC = 0x00000010, + ATH10K_DBG_CORE = 0x00000020, + ATH10K_DBG_PCI_DUMP = 0x00000040, + ATH10K_DBG_HTT_DUMP = 0x00000080, + ATH10K_DBG_MGMT = 0x00000100, + ATH10K_DBG_DATA = 0x00000200, + ATH10K_DBG_ANY = 0xffffffff, +}; + +extern unsigned int ath10k_debug_mask; + +extern __printf(1, 2) int ath10k_info(const char *fmt, ...); +extern __printf(1, 2) int ath10k_err(const char *fmt, ...); +extern __printf(1, 2) int ath10k_warn(const char *fmt, ...); + +#ifdef CONFIG_ATH10K_DEBUGFS +int ath10k_debug_create(struct ath10k *ar); +void ath10k_debug_read_service_map(struct ath10k *ar, + void *service_map, + size_t map_size); +void ath10k_debug_read_target_stats(struct ath10k *ar, + struct wmi_stats_event *ev); + +#else +static inline int ath10k_debug_create(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_debug_read_service_map(struct ath10k *ar, + void *service_map, + size_t map_size) +{ +} + +static inline void ath10k_debug_read_target_stats(struct ath10k *ar, + struct wmi_stats_event *ev) +{ +} +#endif /* CONFIG_ATH10K_DEBUGFS */ + +#ifdef CONFIG_ATH10K_DEBUG +extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, + const char *fmt, ...); +void ath10k_dbg_dump(enum ath10k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len); +#else /* CONFIG_ATH10K_DEBUG */ + +static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask, + const char *fmt, ...) +{ + return 0; +} + +static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ +} +#endif /* CONFIG_ATH10K_DEBUG */ +#endif /* _DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h new file mode 100644 index 00000000000..73a24d44d1b --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HIF_H_ +#define _HIF_H_ + +#include <linux/kernel.h> +#include "core.h" + +struct ath10k_hif_cb { + int (*tx_completion)(struct ath10k *ar, + struct sk_buff *wbuf, + unsigned transfer_id); + int (*rx_completion)(struct ath10k *ar, + struct sk_buff *wbuf, + u8 pipe_id); +}; + +struct ath10k_hif_ops { + /* Send the head of a buffer to HIF for transmission to the target. */ + int (*send_head)(struct ath10k *ar, u8 pipe_id, + unsigned int transfer_id, + unsigned int nbytes, + struct sk_buff *buf); + + /* + * API to handle HIF-specific BMI message exchanges, this API is + * synchronous and only allowed to be called from a context that + * can block (sleep) + */ + int (*exchange_bmi_msg)(struct ath10k *ar, + void *request, u32 request_len, + void *response, u32 *response_len); + + int (*start)(struct ath10k *ar); + + void (*stop)(struct ath10k *ar); + + int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe, + int *ul_is_polled, int *dl_is_polled); + + void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe); + + /* + * Check if prior sends have completed. + * + * Check whether the pipe in question has any completed + * sends that have not yet been processed. + * This function is only relevant for HIF pipes that are configured + * to be polled rather than interrupt-driven. + */ + void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); + + void (*init)(struct ath10k *ar, + struct ath10k_hif_cb *callbacks); + + u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); +}; + + +static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id, + unsigned int transfer_id, + unsigned int nbytes, + struct sk_buff *buf) +{ + return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf); +} + +static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar, + void *request, u32 request_len, + void *response, u32 *response_len) +{ + return ar->hif.ops->exchange_bmi_msg(ar, request, request_len, + response, response_len); +} + +static inline int ath10k_hif_start(struct ath10k *ar) +{ + return ar->hif.ops->start(ar); +} + +static inline void ath10k_hif_stop(struct ath10k *ar) +{ + return ar->hif.ops->stop(ar); +} + +static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar, + u16 service_id, + u8 *ul_pipe, u8 *dl_pipe, + int *ul_is_polled, + int *dl_is_polled) +{ + return ar->hif.ops->map_service_to_pipe(ar, service_id, + ul_pipe, dl_pipe, + ul_is_polled, dl_is_polled); +} + +static inline void ath10k_hif_get_default_pipe(struct ath10k *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe); +} + +static inline void ath10k_hif_send_complete_check(struct ath10k *ar, + u8 pipe_id, int force) +{ + ar->hif.ops->send_complete_check(ar, pipe_id, force); +} + +static inline void ath10k_hif_init(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) +{ + ar->hif.ops->init(ar, callbacks); +} + +static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, + u8 pipe_id) +{ + return ar->hif.ops->get_free_queue_number(ar, pipe_id); +} + +#endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c new file mode 100644 index 00000000000..74363c94939 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -0,0 +1,1000 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "hif.h" +#include "debug.h" + +/********/ +/* Send */ +/********/ + +static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep, + int force) +{ + /* + * Check whether HIF has any prior sends that have finished, + * have not had the post-processing done. + */ + ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force); +} + +static void ath10k_htc_control_tx_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + kfree_skb(skb); +} + +static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *skb_cb; + + skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE); + if (!skb) { + ath10k_warn("Unable to allocate ctrl skb\n"); + return NULL; + } + + skb_reserve(skb, 20); /* FIXME: why 20 bytes? */ + WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb"); + + skb_cb = ATH10K_SKB_CB(skb); + memset(skb_cb, 0, sizeof(*skb_cb)); + + ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb); + return skb; +} + +static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc, + struct sk_buff *skb) +{ + ath10k_skb_unmap(htc->ar->dev, skb); + skb_pull(skb, sizeof(struct ath10k_htc_hdr)); +} + +static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, + ep->eid, skb); + + ath10k_htc_restore_tx_skb(ep->htc, skb); + + if (!ep->ep_ops.ep_tx_complete) { + ath10k_warn("no tx handler for eid %d\n", ep->eid); + dev_kfree_skb_any(skb); + return; + } + + ep->ep_ops.ep_tx_complete(ep->htc->ar, skb); +} + +/* assumes tx_lock is held */ +static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep) +{ + if (!ep->tx_credit_flow_enabled) + return false; + if (ep->tx_credits >= ep->tx_credits_per_max_message) + return false; + + ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n", + ep->eid); + return true; +} + +static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, + struct sk_buff *skb) +{ + struct ath10k_htc_hdr *hdr; + + hdr = (struct ath10k_htc_hdr *)skb->data; + memset(hdr, 0, sizeof(*hdr)); + + hdr->eid = ep->eid; + hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr)); + + spin_lock_bh(&ep->htc->tx_lock); + hdr->seq_no = ep->seq_no++; + + if (ath10k_htc_ep_need_credit_update(ep)) + hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE; + + spin_unlock_bh(&ep->htc->tx_lock); +} + +static int ath10k_htc_issue_skb(struct ath10k_htc *htc, + struct ath10k_htc_ep *ep, + struct sk_buff *skb, + u8 credits) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + int ret; + + ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, + ep->eid, skb); + + ath10k_htc_prepare_tx_skb(ep, skb); + + ret = ath10k_skb_map(htc->ar->dev, skb); + if (ret) + goto err; + + ret = ath10k_hif_send_head(htc->ar, + ep->ul_pipe_id, + ep->eid, + skb->len, + skb); + if (unlikely(ret)) + goto err; + + return 0; +err: + ath10k_warn("HTC issue failed: %d\n", ret); + + spin_lock_bh(&htc->tx_lock); + ep->tx_credits += credits; + spin_unlock_bh(&htc->tx_lock); + + /* this is the simplest way to handle out-of-resources for non-credit + * based endpoints. credit based endpoints can still get -ENOSR, but + * this is highly unlikely as credit reservation should prevent that */ + if (ret == -ENOSR) { + spin_lock_bh(&htc->tx_lock); + __skb_queue_head(&ep->tx_queue, skb); + spin_unlock_bh(&htc->tx_lock); + + return ret; + } + + skb_cb->is_aborted = true; + ath10k_htc_notify_tx_completion(ep, skb); + + return ret; +} + +static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc, + struct ath10k_htc_ep *ep, + u8 *credits) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *skb_cb; + int credits_required; + int remainder; + unsigned int transfer_len; + + lockdep_assert_held(&htc->tx_lock); + + skb = __skb_dequeue(&ep->tx_queue); + if (!skb) + return NULL; + + skb_cb = ATH10K_SKB_CB(skb); + transfer_len = skb->len; + + if (likely(transfer_len <= htc->target_credit_size)) { + credits_required = 1; + } else { + /* figure out how many credits this message requires */ + credits_required = transfer_len / htc->target_credit_size; + remainder = transfer_len % htc->target_credit_size; + + if (remainder) + credits_required++; + } + + ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n", + credits_required, ep->tx_credits); + + if (ep->tx_credits < credits_required) { + __skb_queue_head(&ep->tx_queue, skb); + return NULL; + } + + ep->tx_credits -= credits_required; + *credits = credits_required; + return skb; +} + +static void ath10k_htc_send_work(struct work_struct *work) +{ + struct ath10k_htc_ep *ep = container_of(work, + struct ath10k_htc_ep, send_work); + struct ath10k_htc *htc = ep->htc; + struct sk_buff *skb; + u8 credits = 0; + int ret; + + while (true) { + if (ep->ul_is_polled) + ath10k_htc_send_complete_check(ep, 0); + + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credit_flow_enabled) + skb = ath10k_htc_get_skb_credit_based(htc, ep, + &credits); + else + skb = __skb_dequeue(&ep->tx_queue); + spin_unlock_bh(&htc->tx_lock); + + if (!skb) + break; + + ret = ath10k_htc_issue_skb(htc, ep, skb, credits); + if (ret == -ENOSR) + break; + } +} + +int ath10k_htc_send(struct ath10k_htc *htc, + enum ath10k_htc_ep_id eid, + struct sk_buff *skb) +{ + struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + + if (eid >= ATH10K_HTC_EP_COUNT) { + ath10k_warn("Invalid endpoint id: %d\n", eid); + return -ENOENT; + } + + skb_push(skb, sizeof(struct ath10k_htc_hdr)); + + spin_lock_bh(&htc->tx_lock); + __skb_queue_tail(&ep->tx_queue, skb); + spin_unlock_bh(&htc->tx_lock); + + queue_work(htc->ar->workqueue, &ep->send_work); + return 0; +} + +static int ath10k_htc_tx_completion_handler(struct ath10k *ar, + struct sk_buff *skb, + unsigned int eid) +{ + struct ath10k_htc *htc = ar->htc; + struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + bool stopping; + + ath10k_htc_notify_tx_completion(ep, skb); + /* the skb now belongs to the completion handler */ + + spin_lock_bh(&htc->tx_lock); + stopping = htc->stopping; + spin_unlock_bh(&htc->tx_lock); + + if (!ep->tx_credit_flow_enabled && !stopping) + /* + * note: when using TX credit flow, the re-checking of + * queues happens when credits flow back from the target. + * in the non-TX credit case, we recheck after the packet + * completes + */ + queue_work(ar->workqueue, &ep->send_work); + + return 0; +} + +/* flush endpoint TX queue */ +static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc, + struct ath10k_htc_ep *ep) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *skb_cb; + + spin_lock_bh(&htc->tx_lock); + for (;;) { + skb = __skb_dequeue(&ep->tx_queue); + if (!skb) + break; + + skb_cb = ATH10K_SKB_CB(skb); + skb_cb->is_aborted = true; + ath10k_htc_notify_tx_completion(ep, skb); + } + spin_unlock_bh(&htc->tx_lock); + + cancel_work_sync(&ep->send_work); +} + +/***********/ +/* Receive */ +/***********/ + +static void +ath10k_htc_process_credit_report(struct ath10k_htc *htc, + const struct ath10k_htc_credit_report *report, + int len, + enum ath10k_htc_ep_id eid) +{ + struct ath10k_htc_ep *ep; + int i, n_reports; + + if (len % sizeof(*report)) + ath10k_warn("Uneven credit report len %d", len); + + n_reports = len / sizeof(*report); + + spin_lock_bh(&htc->tx_lock); + for (i = 0; i < n_reports; i++, report++) { + if (report->eid >= ATH10K_HTC_EP_COUNT) + break; + + ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n", + report->eid, report->credits); + + ep = &htc->endpoint[report->eid]; + ep->tx_credits += report->credits; + + if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue)) + queue_work(htc->ar->workqueue, &ep->send_work); + } + spin_unlock_bh(&htc->tx_lock); +} + +static int ath10k_htc_process_trailer(struct ath10k_htc *htc, + u8 *buffer, + int length, + enum ath10k_htc_ep_id src_eid) +{ + int status = 0; + struct ath10k_htc_record *record; + u8 *orig_buffer; + int orig_length; + size_t len; + + orig_buffer = buffer; + orig_length = length; + + while (length > 0) { + record = (struct ath10k_htc_record *)buffer; + + if (length < sizeof(record->hdr)) { + status = -EINVAL; + break; + } + + if (record->hdr.len > length) { + /* no room left in buffer for record */ + ath10k_warn("Invalid record length: %d\n", + record->hdr.len); + status = -EINVAL; + break; + } + + switch (record->hdr.id) { + case ATH10K_HTC_RECORD_CREDITS: + len = sizeof(struct ath10k_htc_credit_report); + if (record->hdr.len < len) { + ath10k_warn("Credit report too long\n"); + status = -EINVAL; + break; + } + ath10k_htc_process_credit_report(htc, + record->credit_report, + record->hdr.len, + src_eid); + break; + default: + ath10k_warn("Unhandled record: id:%d length:%d\n", + record->hdr.id, record->hdr.len); + break; + } + + if (status) + break; + + /* multiple records may be present in a trailer */ + buffer += sizeof(record->hdr) + record->hdr.len; + length -= sizeof(record->hdr) + record->hdr.len; + } + + if (status) + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "", + orig_buffer, orig_length); + + return status; +} + +static int ath10k_htc_rx_completion_handler(struct ath10k *ar, + struct sk_buff *skb, + u8 pipe_id) +{ + int status = 0; + struct ath10k_htc *htc = ar->htc; + struct ath10k_htc_hdr *hdr; + struct ath10k_htc_ep *ep; + u16 payload_len; + u32 trailer_len = 0; + size_t min_len; + u8 eid; + bool trailer_present; + + hdr = (struct ath10k_htc_hdr *)skb->data; + skb_pull(skb, sizeof(*hdr)); + + eid = hdr->eid; + + if (eid >= ATH10K_HTC_EP_COUNT) { + ath10k_warn("HTC Rx: invalid eid %d\n", eid); + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "", + hdr, sizeof(*hdr)); + status = -EINVAL; + goto out; + } + + ep = &htc->endpoint[eid]; + + /* + * If this endpoint that received a message from the target has + * a to-target HIF pipe whose send completions are polled rather + * than interrupt-driven, this is a good point to ask HIF to check + * whether it has any completed sends to handle. + */ + if (ep->ul_is_polled) + ath10k_htc_send_complete_check(ep, 1); + + payload_len = __le16_to_cpu(hdr->len); + + if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) { + ath10k_warn("HTC rx frame too long, len: %zu\n", + payload_len + sizeof(*hdr)); + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "", + hdr, sizeof(*hdr)); + status = -EINVAL; + goto out; + } + + if (skb->len < payload_len) { + ath10k_dbg(ATH10K_DBG_HTC, + "HTC Rx: insufficient length, got %d, expected %d\n", + skb->len, payload_len); + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", + "", hdr, sizeof(*hdr)); + status = -EINVAL; + goto out; + } + + /* get flags to check for trailer */ + trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT; + if (trailer_present) { + u8 *trailer; + + trailer_len = hdr->trailer_len; + min_len = sizeof(struct ath10k_ath10k_htc_record_hdr); + + if ((trailer_len < min_len) || + (trailer_len > payload_len)) { + ath10k_warn("Invalid trailer length: %d\n", + trailer_len); + status = -EPROTO; + goto out; + } + + trailer = (u8 *)hdr; + trailer += sizeof(*hdr); + trailer += payload_len; + trailer -= trailer_len; + status = ath10k_htc_process_trailer(htc, trailer, + trailer_len, hdr->eid); + if (status) + goto out; + + skb_trim(skb, skb->len - trailer_len); + } + + if (((int)payload_len - (int)trailer_len) <= 0) + /* zero length packet with trailer data, just drop these */ + goto out; + + if (eid == ATH10K_HTC_EP_0) { + struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data; + + switch (__le16_to_cpu(msg->hdr.message_id)) { + default: + /* handle HTC control message */ + if (completion_done(&htc->ctl_resp)) { + /* + * this is a fatal error, target should not be + * sending unsolicited messages on the ep 0 + */ + ath10k_warn("HTC rx ctrl still processing\n"); + status = -EINVAL; + complete(&htc->ctl_resp); + goto out; + } + + htc->control_resp_len = + min_t(int, skb->len, + ATH10K_HTC_MAX_CTRL_MSG_LEN); + + memcpy(htc->control_resp_buffer, skb->data, + htc->control_resp_len); + + complete(&htc->ctl_resp); + break; + case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE: + htc->htc_ops.target_send_suspend_complete(ar); + } + goto out; + } + + ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n", + eid, skb); + ep->ep_ops.ep_rx_complete(ar, skb); + + /* skb is now owned by the rx completion handler */ + skb = NULL; +out: + kfree_skb(skb); + + return status; +} + +static void ath10k_htc_control_rx_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + /* This is unexpected. FW is not supposed to send regular rx on this + * endpoint. */ + ath10k_warn("unexpected htc rx\n"); + kfree_skb(skb); +} + +/***************/ +/* Init/Deinit */ +/***************/ + +static const char *htc_service_name(enum ath10k_htc_svc_id id) +{ + switch (id) { + case ATH10K_HTC_SVC_ID_RESERVED: + return "Reserved"; + case ATH10K_HTC_SVC_ID_RSVD_CTRL: + return "Control"; + case ATH10K_HTC_SVC_ID_WMI_CONTROL: + return "WMI"; + case ATH10K_HTC_SVC_ID_WMI_DATA_BE: + return "DATA BE"; + case ATH10K_HTC_SVC_ID_WMI_DATA_BK: + return "DATA BK"; + case ATH10K_HTC_SVC_ID_WMI_DATA_VI: + return "DATA VI"; + case ATH10K_HTC_SVC_ID_WMI_DATA_VO: + return "DATA VO"; + case ATH10K_HTC_SVC_ID_NMI_CONTROL: + return "NMI Control"; + case ATH10K_HTC_SVC_ID_NMI_DATA: + return "NMI Data"; + case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: + return "HTT Data"; + case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS: + return "RAW"; + } + + return "Unknown"; +} + +static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc) +{ + struct ath10k_htc_ep *ep; + int i; + + for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ep->service_id = ATH10K_HTC_SVC_ID_UNUSED; + ep->max_ep_message_len = 0; + ep->max_tx_queue_depth = 0; + ep->eid = i; + skb_queue_head_init(&ep->tx_queue); + ep->htc = htc; + ep->tx_credit_flow_enabled = true; + INIT_WORK(&ep->send_work, ath10k_htc_send_work); + } +} + +static void ath10k_htc_setup_target_buffer_assignments(struct ath10k_htc *htc) +{ + struct ath10k_htc_svc_tx_credits *entry; + + entry = &htc->service_tx_alloc[0]; + + /* + * for PCIE allocate all credists/HTC buffers to WMI. + * no buffers are used/required for data. data always + * remains on host. + */ + entry++; + entry->service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; + entry->credit_allocation = htc->total_transmit_credits; +} + +static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc, + u16 service_id) +{ + u8 allocation = 0; + int i; + + for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) { + if (htc->service_tx_alloc[i].service_id == service_id) + allocation = + htc->service_tx_alloc[i].credit_allocation; + } + + return allocation; +} + +int ath10k_htc_wait_target(struct ath10k_htc *htc) +{ + int status = 0; + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + struct ath10k_htc_msg *msg; + u16 message_id; + u16 credit_count; + u16 credit_size; + + INIT_COMPLETION(htc->ctl_resp); + + status = ath10k_hif_start(htc->ar); + if (status) { + ath10k_err("could not start HIF (%d)\n", status); + goto err_start; + } + + status = wait_for_completion_timeout(&htc->ctl_resp, + ATH10K_HTC_WAIT_TIMEOUT_HZ); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + + ath10k_err("ctl_resp never came in (%d)\n", status); + goto err_target; + } + + if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) { + ath10k_err("Invalid HTC ready msg len:%d\n", + htc->control_resp_len); + + status = -ECOMM; + goto err_target; + } + + msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; + message_id = __le16_to_cpu(msg->hdr.message_id); + credit_count = __le16_to_cpu(msg->ready.credit_count); + credit_size = __le16_to_cpu(msg->ready.credit_size); + + if (message_id != ATH10K_HTC_MSG_READY_ID) { + ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id); + status = -ECOMM; + goto err_target; + } + + htc->total_transmit_credits = credit_count; + htc->target_credit_size = credit_size; + + ath10k_dbg(ATH10K_DBG_HTC, + "Target ready! transmit resources: %d size:%d\n", + htc->total_transmit_credits, + htc->target_credit_size); + + if ((htc->total_transmit_credits == 0) || + (htc->target_credit_size == 0)) { + status = -ECOMM; + ath10k_err("Invalid credit size received\n"); + goto err_target; + } + + ath10k_htc_setup_target_buffer_assignments(htc); + + /* setup our pseudo HTC control endpoint connection */ + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete; + conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS; + conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL; + + /* connect fake service */ + status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp); + if (status) { + ath10k_err("could not connect to htc service (%d)\n", status); + goto err_target; + } + + return 0; +err_target: + ath10k_hif_stop(htc->ar); +err_start: + return status; +} + +int ath10k_htc_connect_service(struct ath10k_htc *htc, + struct ath10k_htc_svc_conn_req *conn_req, + struct ath10k_htc_svc_conn_resp *conn_resp) +{ + struct ath10k_htc_msg *msg; + struct ath10k_htc_conn_svc *req_msg; + struct ath10k_htc_conn_svc_response resp_msg_dummy; + struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy; + enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT; + struct ath10k_htc_ep *ep; + struct sk_buff *skb; + unsigned int max_msg_size = 0; + int length, status; + bool disable_credit_flow_ctrl = false; + u16 message_id, service_id, flags = 0; + u8 tx_alloc = 0; + + /* special case for HTC pseudo control service */ + if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) { + disable_credit_flow_ctrl = true; + assigned_eid = ATH10K_HTC_EP_0; + max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN; + memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); + goto setup; + } + + tx_alloc = ath10k_htc_get_credit_allocation(htc, + conn_req->service_id); + if (!tx_alloc) + ath10k_warn("HTC Service %s does not allocate target credits\n", + htc_service_name(conn_req->service_id)); + + skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); + if (!skb) { + ath10k_err("Failed to allocate HTC packet\n"); + return -ENOMEM; + } + + length = sizeof(msg->hdr) + sizeof(msg->connect_service); + skb_put(skb, length); + memset(skb->data, 0, length); + + msg = (struct ath10k_htc_msg *)skb->data; + msg->hdr.message_id = + __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID); + + flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC); + + req_msg = &msg->connect_service; + req_msg->flags = __cpu_to_le16(flags); + req_msg->service_id = __cpu_to_le16(conn_req->service_id); + + /* Only enable credit flow control for WMI ctrl service */ + if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) { + flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = true; + } + + INIT_COMPLETION(htc->ctl_resp); + + status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + /* wait for response */ + status = wait_for_completion_timeout(&htc->ctl_resp, + ATH10K_HTC_CONN_SVC_TIMEOUT_HZ); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + ath10k_err("Service connect timeout: %d\n", status); + return status; + } + + /* we controlled the buffer creation, it's aligned */ + msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; + resp_msg = &msg->connect_service_response; + message_id = __le16_to_cpu(msg->hdr.message_id); + service_id = __le16_to_cpu(resp_msg->service_id); + + if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || + (htc->control_resp_len < sizeof(msg->hdr) + + sizeof(msg->connect_service_response))) { + ath10k_err("Invalid resp message ID 0x%x", message_id); + return -EPROTO; + } + + ath10k_dbg(ATH10K_DBG_HTC, + "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n", + htc_service_name(service_id), + resp_msg->status, resp_msg->eid); + + conn_resp->connect_resp_code = resp_msg->status; + + /* check response status */ + if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) { + ath10k_err("HTC Service %s connect request failed: 0x%x)\n", + htc_service_name(service_id), + resp_msg->status); + return -EPROTO; + } + + assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid; + max_msg_size = __le16_to_cpu(resp_msg->max_msg_size); + +setup: + + if (assigned_eid >= ATH10K_HTC_EP_COUNT) + return -EPROTO; + + if (max_msg_size == 0) + return -EPROTO; + + ep = &htc->endpoint[assigned_eid]; + ep->eid = assigned_eid; + + if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED) + return -EPROTO; + + /* return assigned endpoint to caller */ + conn_resp->eid = assigned_eid; + conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size); + + /* setup the endpoint */ + ep->service_id = conn_req->service_id; + ep->max_tx_queue_depth = conn_req->max_send_queue_depth; + ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size); + ep->tx_credits = tx_alloc; + ep->tx_credit_size = htc->target_credit_size; + ep->tx_credits_per_max_message = ep->max_ep_message_len / + htc->target_credit_size; + + if (ep->max_ep_message_len % htc->target_credit_size) + ep->tx_credits_per_max_message++; + + /* copy all the callbacks */ + ep->ep_ops = conn_req->ep_ops; + + status = ath10k_hif_map_service_to_pipe(htc->ar, + ep->service_id, + &ep->ul_pipe_id, + &ep->dl_pipe_id, + &ep->ul_is_polled, + &ep->dl_is_polled); + if (status) + return status; + + ath10k_dbg(ATH10K_DBG_HTC, + "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n", + htc_service_name(ep->service_id), ep->ul_pipe_id, + ep->dl_pipe_id, ep->eid); + + ath10k_dbg(ATH10K_DBG_HTC, + "EP %d UL polled: %d, DL polled: %d\n", + ep->eid, ep->ul_is_polled, ep->dl_is_polled); + + if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { + ep->tx_credit_flow_enabled = false; + ath10k_dbg(ATH10K_DBG_HTC, + "HTC service: %s eid: %d TX flow control disabled\n", + htc_service_name(ep->service_id), assigned_eid); + } + + return status; +} + +struct sk_buff *ath10k_htc_alloc_skb(int size) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr)); + if (!skb) { + ath10k_warn("could not allocate HTC tx skb\n"); + return NULL; + } + + skb_reserve(skb, sizeof(struct ath10k_htc_hdr)); + + /* FW/HTC requires 4-byte aligned streams */ + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath10k_warn("Unaligned HTC tx skb\n"); + + return skb; +} + +int ath10k_htc_start(struct ath10k_htc *htc) +{ + struct sk_buff *skb; + int status = 0; + struct ath10k_htc_msg *msg; + + skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext)); + memset(skb->data, 0, skb->len); + + msg = (struct ath10k_htc_msg *)skb->data; + msg->hdr.message_id = + __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID); + + ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n"); + + status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + return 0; +} + +/* + * stop HTC communications, i.e. stop interrupt reception, and flush all + * queued buffers + */ +void ath10k_htc_stop(struct ath10k_htc *htc) +{ + int i; + struct ath10k_htc_ep *ep; + + spin_lock_bh(&htc->tx_lock); + htc->stopping = true; + spin_unlock_bh(&htc->tx_lock); + + for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ath10k_htc_flush_endpoint_tx(htc, ep); + } + + ath10k_hif_stop(htc->ar); + ath10k_htc_reset_endpoint_states(htc); +} + +/* registered target arrival callback from the HIF layer */ +struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, + struct ath10k_htc_ops *htc_ops) +{ + struct ath10k_hif_cb htc_callbacks; + struct ath10k_htc_ep *ep = NULL; + struct ath10k_htc *htc = NULL; + + /* FIXME: use struct ath10k instead */ + htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL); + if (!htc) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&htc->tx_lock); + + memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops)); + + ath10k_htc_reset_endpoint_states(htc); + + /* setup HIF layer callbacks */ + htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler; + htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler; + htc->ar = ar; + + /* Get HIF default pipe for HTC message exchange */ + ep = &htc->endpoint[ATH10K_HTC_EP_0]; + + ath10k_hif_init(ar, &htc_callbacks); + ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); + + init_completion(&htc->ctl_resp); + + return htc; +} + +void ath10k_htc_destroy(struct ath10k_htc *htc) +{ + kfree(htc); +} diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h new file mode 100644 index 00000000000..fa45844b59f --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HTC_H_ +#define _HTC_H_ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/bug.h> +#include <linux/skbuff.h> +#include <linux/semaphore.h> +#include <linux/timer.h> + +struct ath10k; + +/****************/ +/* HTC protocol */ +/****************/ + +/* + * HTC - host-target control protocol + * + * tx packets are generally <htc_hdr><payload> + * rx packets are more complex: <htc_hdr><payload><trailer> + * + * The payload + trailer length is stored in len. + * To get payload-only length one needs to payload - trailer_len. + * + * Trailer contains (possibly) multiple <htc_record>. + * Each record is a id-len-value. + * + * HTC header flags, control_byte0, control_byte1 + * have different meaning depending whether its tx + * or rx. + * + * Alignment: htc_hdr, payload and trailer are + * 4-byte aligned. + */ + +enum ath10k_htc_tx_flags { + ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, + ATH10K_HTC_FLAG_SEND_BUNDLE = 0x02 +}; + +enum ath10k_htc_rx_flags { + ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02, + ATH10K_HTC_FLAG_BUNDLE_MASK = 0xF0 +}; + +struct ath10k_htc_hdr { + u8 eid; /* @enum ath10k_htc_ep_id */ + u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */ + __le16 len; + union { + u8 trailer_len; /* for rx */ + u8 control_byte0; + } __packed; + union { + u8 seq_no; /* for tx */ + u8 control_byte1; + } __packed; + u8 pad0; + u8 pad1; +} __packed __aligned(4); + +enum ath10k_ath10k_htc_msg_id { + ATH10K_HTC_MSG_READY_ID = 1, + ATH10K_HTC_MSG_CONNECT_SERVICE_ID = 2, + ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3, + ATH10K_HTC_MSG_SETUP_COMPLETE_ID = 4, + ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5, + ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6 +}; + +enum ath10k_htc_version { + ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */ + ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */ +}; + +enum ath10k_htc_conn_flags { + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0, + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1, + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2, + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3, +#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3 + ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 1 << 2, + ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3 +#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00 +#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB 8 +}; + +enum ath10k_htc_conn_svc_status { + ATH10K_HTC_CONN_SVC_STATUS_SUCCESS = 0, + ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1, + ATH10K_HTC_CONN_SVC_STATUS_FAILED = 2, + ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3, + ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4 +}; + +struct ath10k_ath10k_htc_msg_hdr { + __le16 message_id; /* @enum htc_message_id */ +} __packed; + +struct ath10k_htc_unknown { + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_ready { + __le16 credit_count; + __le16 credit_size; + u8 max_endpoints; + u8 pad0; +} __packed; + +struct ath10k_htc_ready_extended { + struct ath10k_htc_ready base; + u8 htc_version; /* @enum ath10k_htc_version */ + u8 max_msgs_per_htc_bundle; + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_conn_svc { + __le16 service_id; + __le16 flags; /* @enum ath10k_htc_conn_flags */ + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_conn_svc_response { + __le16 service_id; + u8 status; /* @enum ath10k_htc_conn_svc_status */ + u8 eid; + __le16 max_msg_size; +} __packed; + +struct ath10k_htc_setup_complete_extended { + u8 pad0; + u8 pad1; + __le32 flags; /* @enum htc_setup_complete_flags */ + u8 max_msgs_per_bundled_recv; + u8 pad2; + u8 pad3; + u8 pad4; +} __packed; + +struct ath10k_htc_msg { + struct ath10k_ath10k_htc_msg_hdr hdr; + union { + /* host-to-target */ + struct ath10k_htc_conn_svc connect_service; + struct ath10k_htc_ready ready; + struct ath10k_htc_ready_extended ready_ext; + struct ath10k_htc_unknown unknown; + struct ath10k_htc_setup_complete_extended setup_complete_ext; + + /* target-to-host */ + struct ath10k_htc_conn_svc_response connect_service_response; + }; +} __packed __aligned(4); + +enum ath10k_ath10k_htc_record_id { + ATH10K_HTC_RECORD_NULL = 0, + ATH10K_HTC_RECORD_CREDITS = 1 +}; + +struct ath10k_ath10k_htc_record_hdr { + u8 id; /* @enum ath10k_ath10k_htc_record_id */ + u8 len; + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_credit_report { + u8 eid; /* @enum ath10k_htc_ep_id */ + u8 credits; + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_record { + struct ath10k_ath10k_htc_record_hdr hdr; + union { + struct ath10k_htc_credit_report credit_report[0]; + u8 pauload[0]; + }; +} __packed __aligned(4); + +/* + * note: the trailer offset is dynamic depending + * on payload length. this is only a struct layout draft + */ +struct ath10k_htc_frame { + struct ath10k_htc_hdr hdr; + union { + struct ath10k_htc_msg msg; + u8 payload[0]; + }; + struct ath10k_htc_record trailer[0]; +} __packed __aligned(4); + + +/*******************/ +/* Host-side stuff */ +/*******************/ + +enum ath10k_htc_svc_gid { + ATH10K_HTC_SVC_GRP_RSVD = 0, + ATH10K_HTC_SVC_GRP_WMI = 1, + ATH10K_HTC_SVC_GRP_NMI = 2, + ATH10K_HTC_SVC_GRP_HTT = 3, + + ATH10K_HTC_SVC_GRP_TEST = 254, + ATH10K_HTC_SVC_GRP_LAST = 255, +}; + +#define SVC(group, idx) \ + (int)(((int)(group) << 8) | (int)(idx)) + +enum ath10k_htc_svc_id { + /* NOTE: service ID of 0x0000 is reserved and should never be used */ + ATH10K_HTC_SVC_ID_RESERVED = 0x0000, + ATH10K_HTC_SVC_ID_UNUSED = ATH10K_HTC_SVC_ID_RESERVED, + + ATH10K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH10K_HTC_SVC_GRP_RSVD, 1), + ATH10K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH10K_HTC_SVC_GRP_WMI, 0), + ATH10K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH10K_HTC_SVC_GRP_WMI, 1), + ATH10K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH10K_HTC_SVC_GRP_WMI, 2), + ATH10K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH10K_HTC_SVC_GRP_WMI, 3), + ATH10K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH10K_HTC_SVC_GRP_WMI, 4), + + ATH10K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH10K_HTC_SVC_GRP_NMI, 0), + ATH10K_HTC_SVC_ID_NMI_DATA = SVC(ATH10K_HTC_SVC_GRP_NMI, 1), + + ATH10K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 0), + + /* raw stream service (i.e. flash, tcmd, calibration apps) */ + ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0), +}; + +#undef SVC + +enum ath10k_htc_ep_id { + ATH10K_HTC_EP_UNUSED = -1, + ATH10K_HTC_EP_0 = 0, + ATH10K_HTC_EP_1 = 1, + ATH10K_HTC_EP_2, + ATH10K_HTC_EP_3, + ATH10K_HTC_EP_4, + ATH10K_HTC_EP_5, + ATH10K_HTC_EP_6, + ATH10K_HTC_EP_7, + ATH10K_HTC_EP_8, + ATH10K_HTC_EP_COUNT, +}; + +struct ath10k_htc_ops { + void (*target_send_suspend_complete)(struct ath10k *ar); +}; + +struct ath10k_htc_ep_ops { + void (*ep_tx_complete)(struct ath10k *, struct sk_buff *); + void (*ep_rx_complete)(struct ath10k *, struct sk_buff *); +}; + +/* service connection information */ +struct ath10k_htc_svc_conn_req { + u16 service_id; + struct ath10k_htc_ep_ops ep_ops; + int max_send_queue_depth; +}; + +/* service connection response information */ +struct ath10k_htc_svc_conn_resp { + u8 buffer_len; + u8 actual_len; + enum ath10k_htc_ep_id eid; + unsigned int max_msg_len; + u8 connect_resp_code; +}; + +#define ATH10K_NUM_CONTROL_TX_BUFFERS 2 +#define ATH10K_HTC_MAX_LEN 4096 +#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256 +#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1*HZ) +#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \ + sizeof(struct ath10k_htc_hdr)) +#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1*HZ) + +struct ath10k_htc_ep { + struct ath10k_htc *htc; + enum ath10k_htc_ep_id eid; + enum ath10k_htc_svc_id service_id; + struct ath10k_htc_ep_ops ep_ops; + + int max_tx_queue_depth; + int max_ep_message_len; + u8 ul_pipe_id; + u8 dl_pipe_id; + int ul_is_polled; /* call HIF to get tx completions */ + int dl_is_polled; /* call HIF to fetch rx (not implemented) */ + + struct sk_buff_head tx_queue; + + u8 seq_no; /* for debugging */ + int tx_credits; + int tx_credit_size; + int tx_credits_per_max_message; + bool tx_credit_flow_enabled; + + struct work_struct send_work; +}; + +struct ath10k_htc_svc_tx_credits { + u16 service_id; + u8 credit_allocation; +}; + +struct ath10k_htc { + struct ath10k *ar; + struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT]; + + /* protects endpoint and stopping fields */ + spinlock_t tx_lock; + + struct ath10k_htc_ops htc_ops; + + u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN]; + int control_resp_len; + + struct completion ctl_resp; + + int total_transmit_credits; + struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT]; + int target_credit_size; + + bool stopping; +}; + +struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, + struct ath10k_htc_ops *htc_ops); +int ath10k_htc_wait_target(struct ath10k_htc *htc); +int ath10k_htc_start(struct ath10k_htc *htc); +int ath10k_htc_connect_service(struct ath10k_htc *htc, + struct ath10k_htc_svc_conn_req *conn_req, + struct ath10k_htc_svc_conn_resp *conn_resp); +int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, + struct sk_buff *packet); +void ath10k_htc_stop(struct ath10k_htc *htc); +void ath10k_htc_destroy(struct ath10k_htc *htc); +struct sk_buff *ath10k_htc_alloc_skb(int size); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c new file mode 100644 index 00000000000..185a5468a2f --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/slab.h> + +#include "htt.h" +#include "core.h" +#include "debug.h" + +static int ath10k_htt_htc_attach(struct ath10k_htt *htt) +{ + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler; + + /* connect to control service */ + conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG; + + status = ath10k_htc_connect_service(htt->ar->htc, &conn_req, + &conn_resp); + + if (status) + return status; + + htt->eid = conn_resp.eid; + + return 0; +} + +struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) +{ + struct ath10k_htt *htt; + int ret; + + htt = kzalloc(sizeof(*htt), GFP_KERNEL); + if (!htt) + return NULL; + + htt->ar = ar; + htt->max_throughput_mbps = 800; + + /* + * Connect to HTC service. + * This has to be done before calling ath10k_htt_rx_attach, + * since ath10k_htt_rx_attach involves sending a rx ring configure + * message to the target. + */ + if (ath10k_htt_htc_attach(htt)) + goto err_htc_attach; + + ret = ath10k_htt_tx_attach(htt); + if (ret) { + ath10k_err("could not attach htt tx (%d)\n", ret); + goto err_htc_attach; + } + + if (ath10k_htt_rx_attach(htt)) + goto err_rx_attach; + + /* + * Prefetch enough data to satisfy target + * classification engine. + * This is for LL chips. HL chips will probably + * transfer all frame in the tx fragment. + */ + htt->prefetch_len = + 36 + /* 802.11 + qos + ht */ + 4 + /* 802.1q */ + 8 + /* llc snap */ + 2; /* ip4 dscp or ip6 priority */ + + return htt; + +err_rx_attach: + ath10k_htt_tx_detach(htt); +err_htc_attach: + kfree(htt); + return NULL; +} + +#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) + +static int ath10k_htt_verify_version(struct ath10k_htt *htt) +{ + ath10k_dbg(ATH10K_DBG_HTT, + "htt target version %d.%d; host version %d.%d\n", + htt->target_version_major, + htt->target_version_minor, + HTT_CURRENT_VERSION_MAJOR, + HTT_CURRENT_VERSION_MINOR); + + if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) { + ath10k_err("htt major versions are incompatible!\n"); + return -ENOTSUPP; + } + + if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR) + ath10k_warn("htt minor version differ but still compatible\n"); + + return 0; +} + +int ath10k_htt_attach_target(struct ath10k_htt *htt) +{ + int status; + + init_completion(&htt->target_version_received); + + status = ath10k_htt_h2t_ver_req_msg(htt); + if (status) + return status; + + status = wait_for_completion_timeout(&htt->target_version_received, + HTT_TARGET_VERSION_TIMEOUT_HZ); + if (status <= 0) { + ath10k_warn("htt version request timed out\n"); + return -ETIMEDOUT; + } + + status = ath10k_htt_verify_version(htt); + if (status) + return status; + + return ath10k_htt_send_rx_ring_cfg_ll(htt); +} + +void ath10k_htt_detach(struct ath10k_htt *htt) +{ + ath10k_htt_rx_detach(htt); + ath10k_htt_tx_detach(htt); + kfree(htt); +} diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h new file mode 100644 index 00000000000..a7a7aa04053 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -0,0 +1,1338 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HTT_H_ +#define _HTT_H_ + +#include <linux/bug.h> + +#include "core.h" +#include "htc.h" +#include "rx_desc.h" + +#define HTT_CURRENT_VERSION_MAJOR 2 +#define HTT_CURRENT_VERSION_MINOR 1 + +enum htt_dbg_stats_type { + HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0, + HTT_DBG_STATS_RX_REORDER = 1 << 1, + HTT_DBG_STATS_RX_RATE_INFO = 1 << 2, + HTT_DBG_STATS_TX_PPDU_LOG = 1 << 3, + HTT_DBG_STATS_TX_RATE_INFO = 1 << 4, + /* bits 5-23 currently reserved */ + + HTT_DBG_NUM_STATS /* keep this last */ +}; + +enum htt_h2t_msg_type { /* host-to-target */ + HTT_H2T_MSG_TYPE_VERSION_REQ = 0, + HTT_H2T_MSG_TYPE_TX_FRM = 1, + HTT_H2T_MSG_TYPE_RX_RING_CFG = 2, + HTT_H2T_MSG_TYPE_STATS_REQ = 3, + HTT_H2T_MSG_TYPE_SYNC = 4, + HTT_H2T_MSG_TYPE_AGGR_CFG = 5, + HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6, + HTT_H2T_MSG_TYPE_MGMT_TX = 7, + + HTT_H2T_NUM_MSGS /* keep this last */ +}; + +struct htt_cmd_hdr { + u8 msg_type; +} __packed; + +struct htt_ver_req { + u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)]; +} __packed; + +/* + * HTT tx MSDU descriptor + * + * The HTT tx MSDU descriptor is created by the host HTT SW for each + * tx MSDU. The HTT tx MSDU descriptor contains the information that + * the target firmware needs for the FW's tx processing, particularly + * for creating the HW msdu descriptor. + * The same HTT tx descriptor is used for HL and LL systems, though + * a few fields within the tx descriptor are used only by LL or + * only by HL. + * The HTT tx descriptor is defined in two manners: by a struct with + * bitfields, and by a series of [dword offset, bit mask, bit shift] + * definitions. + * The target should use the struct def, for simplicitly and clarity, + * but the host shall use the bit-mast + bit-shift defs, to be endian- + * neutral. Specifically, the host shall use the get/set macros built + * around the mask + shift defs. + */ +struct htt_data_tx_desc_frag { + __le32 paddr; + __le32 len; +} __packed; + +enum htt_data_tx_desc_flags0 { + HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0, + HTT_DATA_TX_DESC_FLAGS0_NO_AGGR = 1 << 1, + HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT = 1 << 2, + HTT_DATA_TX_DESC_FLAGS0_NO_CLASSIFY = 1 << 3, + HTT_DATA_TX_DESC_FLAGS0_RSVD0 = 1 << 4 +#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_MASK 0xE0 +#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_LSB 5 +}; + +enum htt_data_tx_desc_flags1 { +#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_BITS 6 +#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_MASK 0x003F +#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_LSB 0 +#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_BITS 5 +#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_MASK 0x07C0 +#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_LSB 6 + HTT_DATA_TX_DESC_FLAGS1_POSTPONED = 1 << 11, + HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH = 1 << 12, + HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13, + HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14, + HTT_DATA_TX_DESC_FLAGS1_RSVD1 = 1 << 15 +}; + +enum htt_data_tx_ext_tid { + HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16, + HTT_DATA_TX_EXT_TID_MGMT = 17, + HTT_DATA_TX_EXT_TID_INVALID = 31 +}; + +#define HTT_INVALID_PEERID 0xFFFF + +/* + * htt_data_tx_desc - used for data tx path + * + * Note: vdev_id irrelevant for pkt_type == raw and no_classify == 1. + * ext_tid: for qos-data frames (0-15), see %HTT_DATA_TX_EXT_TID_ + * for special kinds of tids + * postponed: only for HL hosts. indicates if this is a resend + * (HL hosts manage queues on the host ) + * more_in_batch: only for HL hosts. indicates if more packets are + * pending. this allows target to wait and aggregate + */ +struct htt_data_tx_desc { + u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */ + __le16 flags1; /* %HTT_DATA_TX_DESC_FLAGS1_ */ + __le16 len; + __le16 id; + __le32 frags_paddr; + __le32 peerid; + u8 prefetch[0]; /* start of frame, for FW classification engine */ +} __packed; + +enum htt_rx_ring_flags { + HTT_RX_RING_FLAGS_MAC80211_HDR = 1 << 0, + HTT_RX_RING_FLAGS_MSDU_PAYLOAD = 1 << 1, + HTT_RX_RING_FLAGS_PPDU_START = 1 << 2, + HTT_RX_RING_FLAGS_PPDU_END = 1 << 3, + HTT_RX_RING_FLAGS_MPDU_START = 1 << 4, + HTT_RX_RING_FLAGS_MPDU_END = 1 << 5, + HTT_RX_RING_FLAGS_MSDU_START = 1 << 6, + HTT_RX_RING_FLAGS_MSDU_END = 1 << 7, + HTT_RX_RING_FLAGS_RX_ATTENTION = 1 << 8, + HTT_RX_RING_FLAGS_FRAG_INFO = 1 << 9, + HTT_RX_RING_FLAGS_UNICAST_RX = 1 << 10, + HTT_RX_RING_FLAGS_MULTICAST_RX = 1 << 11, + HTT_RX_RING_FLAGS_CTRL_RX = 1 << 12, + HTT_RX_RING_FLAGS_MGMT_RX = 1 << 13, + HTT_RX_RING_FLAGS_NULL_RX = 1 << 14, + HTT_RX_RING_FLAGS_PHY_DATA_RX = 1 << 15 +}; + +struct htt_rx_ring_setup_ring { + __le32 fw_idx_shadow_reg_paddr; + __le32 rx_ring_base_paddr; + __le16 rx_ring_len; /* in 4-byte words */ + __le16 rx_ring_bufsize; /* rx skb size - in bytes */ + __le16 flags; /* %HTT_RX_RING_FLAGS_ */ + __le16 fw_idx_init_val; + + /* the following offsets are in 4-byte units */ + __le16 mac80211_hdr_offset; + __le16 msdu_payload_offset; + __le16 ppdu_start_offset; + __le16 ppdu_end_offset; + __le16 mpdu_start_offset; + __le16 mpdu_end_offset; + __le16 msdu_start_offset; + __le16 msdu_end_offset; + __le16 rx_attention_offset; + __le16 frag_info_offset; +} __packed; + +struct htt_rx_ring_setup_hdr { + u8 num_rings; /* supported values: 1, 2 */ + __le16 rsvd0; +} __packed; + +struct htt_rx_ring_setup { + struct htt_rx_ring_setup_hdr hdr; + struct htt_rx_ring_setup_ring rings[0]; +} __packed; + +/* + * htt_stats_req - request target to send specified statistics + * + * @msg_type: hardcoded %HTT_H2T_MSG_TYPE_STATS_REQ + * @upload_types: see %htt_dbg_stats_type. this is 24bit field actually + * so make sure its little-endian. + * @reset_types: see %htt_dbg_stats_type. this is 24bit field actually + * so make sure its little-endian. + * @cfg_val: stat_type specific configuration + * @stat_type: see %htt_dbg_stats_type + * @cookie_lsb: used for confirmation message from target->host + * @cookie_msb: ditto as %cookie + */ +struct htt_stats_req { + u8 upload_types[3]; + u8 rsvd0; + u8 reset_types[3]; + struct { + u8 mpdu_bytes; + u8 mpdu_num_msdus; + u8 msdu_bytes; + } __packed; + u8 stat_type; + __le32 cookie_lsb; + __le32 cookie_msb; +} __packed; + +#define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff + +/* + * htt_oob_sync_req - request out-of-band sync + * + * The HTT SYNC tells the target to suspend processing of subsequent + * HTT host-to-target messages until some other target agent locally + * informs the target HTT FW that the current sync counter is equal to + * or greater than (in a modulo sense) the sync counter specified in + * the SYNC message. + * + * This allows other host-target components to synchronize their operation + * with HTT, e.g. to ensure that tx frames don't get transmitted until a + * security key has been downloaded to and activated by the target. + * In the absence of any explicit synchronization counter value + * specification, the target HTT FW will use zero as the default current + * sync value. + * + * The HTT target FW will suspend its host->target message processing as long + * as 0 < (in-band sync counter - out-of-band sync counter) & 0xff < 128. + */ +struct htt_oob_sync_req { + u8 sync_count; + __le16 rsvd0; +} __packed; + +#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F +#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB 0 + +struct htt_aggr_conf { + u8 max_num_ampdu_subframes; + union { + /* dont use bitfields; undefined behaviour */ + u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */ + u8 max_num_amsdu_subframes:5; + } __packed; +} __packed; + +#define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32 + +struct htt_mgmt_tx_desc { + u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)]; + __le32 msdu_paddr; + __le32 desc_id; + __le32 len; + __le32 vdev_id; + u8 hdr[HTT_MGMT_FRM_HDR_DOWNLOAD_LEN]; +} __packed; + +enum htt_mgmt_tx_status { + HTT_MGMT_TX_STATUS_OK = 0, + HTT_MGMT_TX_STATUS_RETRY = 1, + HTT_MGMT_TX_STATUS_DROP = 2 +}; + +/*=== target -> host messages ===============================================*/ + + +enum htt_t2h_msg_type { + HTT_T2H_MSG_TYPE_VERSION_CONF = 0x0, + HTT_T2H_MSG_TYPE_RX_IND = 0x1, + HTT_T2H_MSG_TYPE_RX_FLUSH = 0x2, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x3, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x4, + HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, + HTT_T2H_MSG_TYPE_RX_DELBA = 0x6, + HTT_T2H_MSG_TYPE_TX_COMPL_IND = 0x7, + HTT_T2H_MSG_TYPE_PKTLOG = 0x8, + HTT_T2H_MSG_TYPE_STATS_CONF = 0x9, + HTT_T2H_MSG_TYPE_RX_FRAG_IND = 0xa, + HTT_T2H_MSG_TYPE_SEC_IND = 0xb, + HTT_T2H_MSG_TYPE_RC_UPDATE_IND = 0xc, + HTT_T2H_MSG_TYPE_TX_INSPECT_IND = 0xd, + HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION = 0xe, + HTT_T2H_MSG_TYPE_TEST, + /* keep this last */ + HTT_T2H_NUM_MSGS +}; + +/* + * htt_resp_hdr - header for target-to-host messages + * + * msg_type: see htt_t2h_msg_type + */ +struct htt_resp_hdr { + u8 msg_type; +} __packed; + +#define HTT_RESP_HDR_MSG_TYPE_OFFSET 0 +#define HTT_RESP_HDR_MSG_TYPE_MASK 0xff +#define HTT_RESP_HDR_MSG_TYPE_LSB 0 + +/* htt_ver_resp - response sent for htt_ver_req */ +struct htt_ver_resp { + u8 minor; + u8 major; + u8 rsvd0; +} __packed; + +struct htt_mgmt_tx_completion { + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; + __le32 desc_id; + __le32 status; +} __packed; + +#define HTT_RX_INDICATION_INFO0_EXT_TID_MASK (0x3F) +#define HTT_RX_INDICATION_INFO0_EXT_TID_LSB (0) +#define HTT_RX_INDICATION_INFO0_FLUSH_VALID (1 << 6) +#define HTT_RX_INDICATION_INFO0_RELEASE_VALID (1 << 7) + +#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_MASK 0x0000003F +#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_LSB 0 +#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_MASK 0x00000FC0 +#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_LSB 6 +#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_MASK 0x0003F000 +#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_LSB 12 +#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_MASK 0x00FC0000 +#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_LSB 18 +#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK 0xFF000000 +#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB 24 + +struct htt_rx_indication_hdr { + u8 info0; /* %HTT_RX_INDICATION_INFO0_ */ + __le16 peer_id; + __le32 info1; /* %HTT_RX_INDICATION_INFO1_ */ +} __packed; + +#define HTT_RX_INDICATION_INFO0_PHY_ERR_VALID (1 << 0) +#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_MASK (0x1E) +#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_LSB (1) +#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK (1 << 5) +#define HTT_RX_INDICATION_INFO0_END_VALID (1 << 6) +#define HTT_RX_INDICATION_INFO0_START_VALID (1 << 7) + +#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_MASK 0x00FFFFFF +#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_LSB 0 +#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_MASK 0xFF000000 +#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_LSB 24 + +#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_MASK 0x00FFFFFF +#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_LSB 0 +#define HTT_RX_INDICATION_INFO2_SERVICE_MASK 0xFF000000 +#define HTT_RX_INDICATION_INFO2_SERVICE_LSB 24 + +enum htt_rx_legacy_rate { + HTT_RX_OFDM_48 = 0, + HTT_RX_OFDM_24 = 1, + HTT_RX_OFDM_12, + HTT_RX_OFDM_6, + HTT_RX_OFDM_54, + HTT_RX_OFDM_36, + HTT_RX_OFDM_18, + HTT_RX_OFDM_9, + + /* long preamble */ + HTT_RX_CCK_11_LP = 0, + HTT_RX_CCK_5_5_LP = 1, + HTT_RX_CCK_2_LP, + HTT_RX_CCK_1_LP, + /* short preamble */ + HTT_RX_CCK_11_SP, + HTT_RX_CCK_5_5_SP, + HTT_RX_CCK_2_SP +}; + +enum htt_rx_legacy_rate_type { + HTT_RX_LEGACY_RATE_OFDM = 0, + HTT_RX_LEGACY_RATE_CCK +}; + +enum htt_rx_preamble_type { + HTT_RX_LEGACY = 0x4, + HTT_RX_HT = 0x8, + HTT_RX_HT_WITH_TXBF = 0x9, + HTT_RX_VHT = 0xC, + HTT_RX_VHT_WITH_TXBF = 0xD, +}; + +/* + * Fields: phy_err_valid, phy_err_code, tsf, + * usec_timestamp, sub_usec_timestamp + * ..are valid only if end_valid == 1. + * + * Fields: rssi_chains, legacy_rate_type, + * legacy_rate_cck, preamble_type, service, + * vht_sig_* + * ..are valid only if start_valid == 1; + */ +struct htt_rx_indication_ppdu { + u8 combined_rssi; + u8 sub_usec_timestamp; + u8 phy_err_code; + u8 info0; /* HTT_RX_INDICATION_INFO0_ */ + struct { + u8 pri20_db; + u8 ext20_db; + u8 ext40_db; + u8 ext80_db; + } __packed rssi_chains[4]; + __le32 tsf; + __le32 usec_timestamp; + __le32 info1; /* HTT_RX_INDICATION_INFO1_ */ + __le32 info2; /* HTT_RX_INDICATION_INFO2_ */ +} __packed; + +enum htt_rx_mpdu_status { + HTT_RX_IND_MPDU_STATUS_UNKNOWN = 0x0, + HTT_RX_IND_MPDU_STATUS_OK, + HTT_RX_IND_MPDU_STATUS_ERR_FCS, + HTT_RX_IND_MPDU_STATUS_ERR_DUP, + HTT_RX_IND_MPDU_STATUS_ERR_REPLAY, + HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER, + /* only accept EAPOL frames */ + HTT_RX_IND_MPDU_STATUS_UNAUTH_PEER, + HTT_RX_IND_MPDU_STATUS_OUT_OF_SYNC, + /* Non-data in promiscous mode */ + HTT_RX_IND_MPDU_STATUS_MGMT_CTRL, + HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR, + HTT_RX_IND_MPDU_STATUS_DECRYPT_ERR, + HTT_RX_IND_MPDU_STATUS_MPDU_LENGTH_ERR, + HTT_RX_IND_MPDU_STATUS_ENCRYPT_REQUIRED_ERR, + HTT_RX_IND_MPDU_STATUS_PRIVACY_ERR, + + /* + * MISC: discard for unspecified reasons. + * Leave this enum value last. + */ + HTT_RX_IND_MPDU_STATUS_ERR_MISC = 0xFF +}; + +struct htt_rx_indication_mpdu_range { + u8 mpdu_count; + u8 mpdu_range_status; /* %htt_rx_mpdu_status */ + u8 pad0; + u8 pad1; +} __packed; + +struct htt_rx_indication_prefix { + __le16 fw_rx_desc_bytes; + u8 pad0; + u8 pad1; +}; + +struct htt_rx_indication { + struct htt_rx_indication_hdr hdr; + struct htt_rx_indication_ppdu ppdu; + struct htt_rx_indication_prefix prefix; + + /* + * the following fields are both dynamically sized, so + * take care addressing them + */ + + /* the size of this is %fw_rx_desc_bytes */ + struct fw_rx_desc_base fw_desc; + + /* + * %mpdu_ranges starts after &%prefix + roundup(%fw_rx_desc_bytes, 4) + * and has %num_mpdu_ranges elements. + */ + struct htt_rx_indication_mpdu_range mpdu_ranges[0]; +} __packed; + +static inline struct htt_rx_indication_mpdu_range * + htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind) +{ + void *ptr = rx_ind; + + ptr += sizeof(rx_ind->hdr) + + sizeof(rx_ind->ppdu) + + sizeof(rx_ind->prefix) + + roundup(__le16_to_cpu(rx_ind->prefix.fw_rx_desc_bytes), 4); + return ptr; +} + +enum htt_rx_flush_mpdu_status { + HTT_RX_FLUSH_MPDU_DISCARD = 0, + HTT_RX_FLUSH_MPDU_REORDER = 1, +}; + +/* + * htt_rx_flush - discard or reorder given range of mpdus + * + * Note: host must check if all sequence numbers between + * [seq_num_start, seq_num_end-1] are valid. + */ +struct htt_rx_flush { + __le16 peer_id; + u8 tid; + u8 rsvd0; + u8 mpdu_status; /* %htt_rx_flush_mpdu_status */ + u8 seq_num_start; /* it is 6 LSBs of 802.11 seq no */ + u8 seq_num_end; /* it is 6 LSBs of 802.11 seq no */ +}; + +struct htt_rx_peer_map { + u8 vdev_id; + __le16 peer_id; + u8 addr[6]; + u8 rsvd0; + u8 rsvd1; +} __packed; + +struct htt_rx_peer_unmap { + u8 rsvd0; + __le16 peer_id; +} __packed; + +enum htt_security_types { + HTT_SECURITY_NONE, + HTT_SECURITY_WEP128, + HTT_SECURITY_WEP104, + HTT_SECURITY_WEP40, + HTT_SECURITY_TKIP, + HTT_SECURITY_TKIP_NOMIC, + HTT_SECURITY_AES_CCMP, + HTT_SECURITY_WAPI, + + HTT_NUM_SECURITY_TYPES /* keep this last! */ +}; + +enum htt_security_flags { +#define HTT_SECURITY_TYPE_MASK 0x7F +#define HTT_SECURITY_TYPE_LSB 0 + HTT_SECURITY_IS_UNICAST = 1 << 7 +}; + +struct htt_security_indication { + union { + /* dont use bitfields; undefined behaviour */ + u8 flags; /* %htt_security_flags */ + struct { + u8 security_type:7, /* %htt_security_types */ + is_unicast:1; + } __packed; + } __packed; + __le16 peer_id; + u8 michael_key[8]; + u8 wapi_rsc[16]; +} __packed; + +#define HTT_RX_BA_INFO0_TID_MASK 0x000F +#define HTT_RX_BA_INFO0_TID_LSB 0 +#define HTT_RX_BA_INFO0_PEER_ID_MASK 0xFFF0 +#define HTT_RX_BA_INFO0_PEER_ID_LSB 4 + +struct htt_rx_addba { + u8 window_size; + __le16 info0; /* %HTT_RX_BA_INFO0_ */ +} __packed; + +struct htt_rx_delba { + u8 rsvd0; + __le16 info0; /* %HTT_RX_BA_INFO0_ */ +} __packed; + +enum htt_data_tx_status { + HTT_DATA_TX_STATUS_OK = 0, + HTT_DATA_TX_STATUS_DISCARD = 1, + HTT_DATA_TX_STATUS_NO_ACK = 2, + HTT_DATA_TX_STATUS_POSTPONE = 3, /* HL only */ + HTT_DATA_TX_STATUS_DOWNLOAD_FAIL = 128 +}; + +enum htt_data_tx_flags { +#define HTT_DATA_TX_STATUS_MASK 0x07 +#define HTT_DATA_TX_STATUS_LSB 0 +#define HTT_DATA_TX_TID_MASK 0x78 +#define HTT_DATA_TX_TID_LSB 3 + HTT_DATA_TX_TID_INVALID = 1 << 7 +}; + +#define HTT_TX_COMPL_INV_MSDU_ID 0xFFFF + +struct htt_data_tx_completion { + union { + u8 flags; + struct { + u8 status:3, + tid:4, + tid_invalid:1; + } __packed; + } __packed; + u8 num_msdus; + u8 rsvd0; + __le16 msdus[0]; /* variable length based on %num_msdus */ +} __packed; + +struct htt_tx_compl_ind_base { + u32 hdr; + u16 payload[1/*or more*/]; +} __packed; + +struct htt_rc_tx_done_params { + u32 rate_code; + u32 rate_code_flags; + u32 flags; + u32 num_enqued; /* 1 for non-AMPDU */ + u32 num_retries; + u32 num_failed; /* for AMPDU */ + u32 ack_rssi; + u32 time_stamp; + u32 is_probe; +}; + +struct htt_rc_update { + u8 vdev_id; + __le16 peer_id; + u8 addr[6]; + u8 num_elems; + u8 rsvd0; + struct htt_rc_tx_done_params params[0]; /* variable length %num_elems */ +} __packed; + +/* see htt_rx_indication for similar fields and descriptions */ +struct htt_rx_fragment_indication { + union { + u8 info0; /* %HTT_RX_FRAG_IND_INFO0_ */ + struct { + u8 ext_tid:5, + flush_valid:1; + } __packed; + } __packed; + __le16 peer_id; + __le32 info1; /* %HTT_RX_FRAG_IND_INFO1_ */ + __le16 fw_rx_desc_bytes; + __le16 rsvd0; + + u8 fw_msdu_rx_desc[0]; +} __packed; + +#define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK 0x1F +#define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB 0 +#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20 +#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_LSB 5 + +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_MASK 0x0000003F +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_LSB 0 +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK 0x00000FC0 +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB 6 + +/* + * target -> host test message definition + * + * The following field definitions describe the format of the test + * message sent from the target to the host. + * The message consists of a 4-octet header, followed by a variable + * number of 32-bit integer values, followed by a variable number + * of 8-bit character values. + * + * |31 16|15 8|7 0| + * |-----------------------------------------------------------| + * | num chars | num ints | msg type | + * |-----------------------------------------------------------| + * | int 0 | + * |-----------------------------------------------------------| + * | int 1 | + * |-----------------------------------------------------------| + * | ... | + * |-----------------------------------------------------------| + * | char 3 | char 2 | char 1 | char 0 | + * |-----------------------------------------------------------| + * | | | ... | char 4 | + * |-----------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a test message + * Value: HTT_MSG_TYPE_TEST + * - NUM_INTS + * Bits 15:8 + * Purpose: indicate how many 32-bit integers follow the message header + * - NUM_CHARS + * Bits 31:16 + * Purpose: indicate how many 8-bit charaters follow the series of integers + */ +struct htt_rx_test { + u8 num_ints; + __le16 num_chars; + + /* payload consists of 2 lists: + * a) num_ints * sizeof(__le32) + * b) num_chars * sizeof(u8) aligned to 4bytes */ + u8 payload[0]; +} __packed; + +static inline __le32 *htt_rx_test_get_ints(struct htt_rx_test *rx_test) +{ + return (__le32 *)rx_test->payload; +} + +static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test) +{ + return rx_test->payload + (rx_test->num_ints * sizeof(__le32)); +} + +/* + * target -> host packet log message + * + * The following field definitions describe the format of the packet log + * message sent from the target to the host. + * The message consists of a 4-octet header,followed by a variable number + * of 32-bit character values. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | | | | msg type | + * |-----------------------------------------------------------| + * | payload | + * |-----------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a test message + * Value: HTT_MSG_TYPE_PACKETLOG + */ +struct htt_pktlog_msg { + u8 pad[3]; + __le32 payload[1 /* or more */]; +} __packed; + +struct htt_dbg_stats_rx_reorder_stats { + /* Non QoS MPDUs received */ + __le32 deliver_non_qos; + + /* MPDUs received in-order */ + __le32 deliver_in_order; + + /* Flush due to reorder timer expired */ + __le32 deliver_flush_timeout; + + /* Flush due to move out of window */ + __le32 deliver_flush_oow; + + /* Flush due to DELBA */ + __le32 deliver_flush_delba; + + /* MPDUs dropped due to FCS error */ + __le32 fcs_error; + + /* MPDUs dropped due to monitor mode non-data packet */ + __le32 mgmt_ctrl; + + /* MPDUs dropped due to invalid peer */ + __le32 invalid_peer; + + /* MPDUs dropped due to duplication (non aggregation) */ + __le32 dup_non_aggr; + + /* MPDUs dropped due to processed before */ + __le32 dup_past; + + /* MPDUs dropped due to duplicate in reorder queue */ + __le32 dup_in_reorder; + + /* Reorder timeout happened */ + __le32 reorder_timeout; + + /* invalid bar ssn */ + __le32 invalid_bar_ssn; + + /* reorder reset due to bar ssn */ + __le32 ssn_reset; +}; + +struct htt_dbg_stats_wal_tx_stats { + /* Num HTT cookies queued to dispatch list */ + __le32 comp_queued; + + /* Num HTT cookies dispatched */ + __le32 comp_delivered; + + /* Num MSDU queued to WAL */ + __le32 msdu_enqued; + + /* Num MPDU queue to WAL */ + __le32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + __le32 wmm_drop; + + /* Num Local frames queued */ + __le32 local_enqued; + + /* Num Local frames done */ + __le32 local_freed; + + /* Num queued to HW */ + __le32 hw_queued; + + /* Num PPDU reaped from HW */ + __le32 hw_reaped; + + /* Num underruns */ + __le32 underrun; + + /* Num PPDUs cleaned up in TX abort */ + __le32 tx_abort; + + /* Num MPDUs requed by SW */ + __le32 mpdus_requed; + + /* excessive retries */ + __le32 tx_ko; + + /* data hw rate code */ + __le32 data_rc; + + /* Scheduler self triggers */ + __le32 self_triggers; + + /* frames dropped due to excessive sw retries */ + __le32 sw_retry_failure; + + /* illegal rate phy errors */ + __le32 illgl_rate_phy_err; + + /* wal pdev continous xretry */ + __le32 pdev_cont_xretry; + + /* wal pdev continous xretry */ + __le32 pdev_tx_timeout; + + /* wal pdev resets */ + __le32 pdev_resets; + + __le32 phy_underrun; + + /* MPDU is more than txop limit */ + __le32 txop_ovf; +} __packed; + +struct htt_dbg_stats_wal_rx_stats { + /* Cnts any change in ring routing mid-ppdu */ + __le32 mid_ppdu_route_change; + + /* Total number of statuses processed */ + __le32 status_rcvd; + + /* Extra frags on rings 0-3 */ + __le32 r0_frags; + __le32 r1_frags; + __le32 r2_frags; + __le32 r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + __le32 htt_msdus; + __le32 htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + __le32 loc_msdus; + __le32 loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + __le32 oversize_amsdu; + + /* Number of PHY errors */ + __le32 phy_errs; + + /* Number of PHY errors drops */ + __le32 phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + __le32 mpdu_errs; +} __packed; + +struct htt_dbg_stats_wal_peer_stats { + __le32 dummy; /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */ +} __packed; + +struct htt_dbg_stats_wal_pdev_txrx { + struct htt_dbg_stats_wal_tx_stats tx_stats; + struct htt_dbg_stats_wal_rx_stats rx_stats; + struct htt_dbg_stats_wal_peer_stats peer_stats; +} __packed; + +struct htt_dbg_stats_rx_rate_info { + __le32 mcs[10]; + __le32 sgi[10]; + __le32 nss[4]; + __le32 stbc[10]; + __le32 bw[3]; + __le32 pream[6]; + __le32 ldpc; + __le32 txbf; +}; + +/* + * htt_dbg_stats_status - + * present - The requested stats have been delivered in full. + * This indicates that either the stats information was contained + * in its entirety within this message, or else this message + * completes the delivery of the requested stats info that was + * partially delivered through earlier STATS_CONF messages. + * partial - The requested stats have been delivered in part. + * One or more subsequent STATS_CONF messages with the same + * cookie value will be sent to deliver the remainder of the + * information. + * error - The requested stats could not be delivered, for example due + * to a shortage of memory to construct a message holding the + * requested stats. + * invalid - The requested stat type is either not recognized, or the + * target is configured to not gather the stats type in question. + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * series_done - This special value indicates that no further stats info + * elements are present within a series of stats info elems + * (within a stats upload confirmation message). + */ +enum htt_dbg_stats_status { + HTT_DBG_STATS_STATUS_PRESENT = 0, + HTT_DBG_STATS_STATUS_PARTIAL = 1, + HTT_DBG_STATS_STATUS_ERROR = 2, + HTT_DBG_STATS_STATUS_INVALID = 3, + HTT_DBG_STATS_STATUS_SERIES_DONE = 7 +}; + +/* + * target -> host statistics upload + * + * The following field definitions describe the format of the HTT target + * to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a series of tag-length-value stats information elements. + * The tag-length header for each stats info element also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A special value of all 1's in this status field is used to indicate + * the end of the series of stats info elements. + * + * + * |31 16|15 8|7 5|4 0| + * |------------------------------------------------------------| + * | reserved | msg type | + * |------------------------------------------------------------| + * | cookie LSBs | + * |------------------------------------------------------------| + * | cookie MSBs | + * |------------------------------------------------------------| + * | stats entry length | reserved | S |stat type| + * |------------------------------------------------------------| + * | | + * | type-specific stats info | + * | | + * |------------------------------------------------------------| + * | stats entry length | reserved | S |stat type| + * |------------------------------------------------------------| + * | | + * | type-specific stats info | + * | | + * |------------------------------------------------------------| + * | n/a | reserved | 111 | n/a | + * |------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a statistics upload confirmation message + * Value: 0x9 + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 4:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: htt_dbg_stats_type + * - STATUS + * Bits 7:5 + * Purpose: indicate whether the requested stats are present + * Value: htt_dbg_stats_status, including a special value (0x7) to mark + * the completion of the stats entry series + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. Even if the length is not an integer multiple of 4, the + * subsequent stats entry header will begin on a 4-byte aligned + * boundary. + */ + +#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_MASK 0x1F +#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_LSB 0 +#define HTT_STATS_CONF_ITEM_INFO_STATUS_MASK 0xE0 +#define HTT_STATS_CONF_ITEM_INFO_STATUS_LSB 5 + +struct htt_stats_conf_item { + union { + u8 info; + struct { + u8 stat_type:5; /* %HTT_DBG_STATS_ */ + u8 status:3; /* %HTT_DBG_STATS_STATUS_ */ + } __packed; + } __packed; + u8 pad; + __le16 length; + u8 payload[0]; /* roundup(length, 4) long */ +} __packed; + +struct htt_stats_conf { + u8 pad[3]; + __le32 cookie_lsb; + __le32 cookie_msb; + + /* each item has variable length! */ + struct htt_stats_conf_item items[0]; +} __packed; + +static inline struct htt_stats_conf_item *htt_stats_conf_next_item( + const struct htt_stats_conf_item *item) +{ + return (void *)item + sizeof(*item) + roundup(item->length, 4); +} +/* + * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank + * + * The following field definitions describe the format of the HTT host + * to target frag_desc/msdu_ext bank configuration message. + * The message contains the based address and the min and max id of the + * MSDU_EXT/FRAG_DESC that will be used by the HTT to map MSDU DESC and + * MSDU_EXT/FRAG_DESC. + * HTT will use id in HTT descriptor instead sending the frag_desc_ptr. + * For QCA988X HW the firmware will use fragment_desc_ptr but in WIFI2.0 + * the hardware does the mapping/translation. + * + * Total banks that can be configured is configured to 16. + * + * This should be called before any TX has be initiated by the HTT + * + * |31 16|15 8|7 5|4 0| + * |------------------------------------------------------------| + * | DESC_SIZE | NUM_BANKS | RES |SWP|pdev| msg type | + * |------------------------------------------------------------| + * | BANK0_BASE_ADDRESS | + * |------------------------------------------------------------| + * | ... | + * |------------------------------------------------------------| + * | BANK15_BASE_ADDRESS | + * |------------------------------------------------------------| + * | BANK0_MAX_ID | BANK0_MIN_ID | + * |------------------------------------------------------------| + * | ... | + * |------------------------------------------------------------| + * | BANK15_MAX_ID | BANK15_MIN_ID | + * |------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Value: 0x6 + * - BANKx_BASE_ADDRESS + * Bits 31:0 + * Purpose: Provide a mechanism to specify the base address of the MSDU_EXT + * bank physical/bus address. + * - BANKx_MIN_ID + * Bits 15:0 + * Purpose: Provide a mechanism to specify the min index that needs to + * mapped. + * - BANKx_MAX_ID + * Bits 31:16 + * Purpose: Provide a mechanism to specify the max index that needs to + * + */ +struct htt_frag_desc_bank_id { + __le16 bank_min_id; + __le16 bank_max_id; +} __packed; + +/* real is 16 but it wouldn't fit in the max htt message size + * so we use a conservatively safe value for now */ +#define HTT_FRAG_DESC_BANK_MAX 4 + +#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03 +#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB 0 +#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP (1 << 2) + +struct htt_frag_desc_bank_cfg { + u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */ + u8 num_banks; + u8 desc_size; + __le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX]; + struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX]; +} __packed; + +union htt_rx_pn_t { + /* WEP: 24-bit PN */ + u32 pn24; + + /* TKIP or CCMP: 48-bit PN */ + u_int64_t pn48; + + /* WAPI: 128-bit PN */ + u_int64_t pn128[2]; +}; + +struct htt_cmd { + struct htt_cmd_hdr hdr; + union { + struct htt_ver_req ver_req; + struct htt_mgmt_tx_desc mgmt_tx; + struct htt_data_tx_desc data_tx; + struct htt_rx_ring_setup rx_setup; + struct htt_stats_req stats_req; + struct htt_oob_sync_req oob_sync_req; + struct htt_aggr_conf aggr_conf; + struct htt_frag_desc_bank_cfg frag_desc_bank_cfg; + }; +} __packed; + +struct htt_resp { + struct htt_resp_hdr hdr; + union { + struct htt_ver_resp ver_resp; + struct htt_mgmt_tx_completion mgmt_tx_completion; + struct htt_data_tx_completion data_tx_completion; + struct htt_rx_indication rx_ind; + struct htt_rx_fragment_indication rx_frag_ind; + struct htt_rx_peer_map peer_map; + struct htt_rx_peer_unmap peer_unmap; + struct htt_rx_flush rx_flush; + struct htt_rx_addba rx_addba; + struct htt_rx_delba rx_delba; + struct htt_security_indication security_indication; + struct htt_rc_update rc_update; + struct htt_rx_test rx_test; + struct htt_pktlog_msg pktlog_msg; + struct htt_stats_conf stats_conf; + }; +} __packed; + + +/*** host side structures follow ***/ + +struct htt_tx_done { + u32 msdu_id; + bool discard; + bool no_ack; +}; + +struct htt_peer_map_event { + u8 vdev_id; + u16 peer_id; + u8 addr[ETH_ALEN]; +}; + +struct htt_peer_unmap_event { + u16 peer_id; +}; + +struct htt_rx_info { + struct sk_buff *skb; + enum htt_rx_mpdu_status status; + enum htt_rx_mpdu_encrypt_type encrypt_type; + s8 signal; + struct { + u8 info0; + u32 info1; + u32 info2; + } rate; + bool fcs_err; +}; + +struct ath10k_htt { + struct ath10k *ar; + enum ath10k_htc_ep_id eid; + + int max_throughput_mbps; + u8 target_version_major; + u8 target_version_minor; + struct completion target_version_received; + + struct { + /* + * Ring of network buffer objects - This ring is + * used exclusively by the host SW. This ring + * mirrors the dev_addrs_ring that is shared + * between the host SW and the MAC HW. The host SW + * uses this netbufs ring to locate the network + * buffer objects whose data buffers the HW has + * filled. + */ + struct sk_buff **netbufs_ring; + /* + * Ring of buffer addresses - + * This ring holds the "physical" device address of the + * rx buffers the host SW provides for the MAC HW to + * fill. + */ + __le32 *paddrs_ring; + + /* + * Base address of ring, as a "physical" device address + * rather than a CPU address. + */ + dma_addr_t base_paddr; + + /* how many elems in the ring (power of 2) */ + int size; + + /* size - 1 */ + unsigned size_mask; + + /* how many rx buffers to keep in the ring */ + int fill_level; + + /* how many rx buffers (full+empty) are in the ring */ + int fill_cnt; + + /* + * alloc_idx - where HTT SW has deposited empty buffers + * This is allocated in consistent mem, so that the FW can + * read this variable, and program the HW's FW_IDX reg with + * the value of this shadow register. + */ + struct { + __le32 *vaddr; + dma_addr_t paddr; + } alloc_idx; + + /* where HTT SW has processed bufs filled by rx MAC DMA */ + struct { + unsigned msdu_payld; + } sw_rd_idx; + + /* + * refill_retry_timer - timer triggered when the ring is + * not refilled to the level expected + */ + struct timer_list refill_retry_timer; + + /* Protects access to all rx ring buffer state variables */ + spinlock_t lock; + } rx_ring; + + unsigned int prefetch_len; + + /* Protects access to %pending_tx, %used_msdu_ids */ + spinlock_t tx_lock; + int max_num_pending_tx; + int num_pending_tx; + struct sk_buff **pending_tx; + unsigned long *used_msdu_ids; /* bitmap */ + wait_queue_head_t empty_tx_wq; + + /* set if host-fw communication goes haywire + * used to avoid further failures */ + bool rx_confused; +}; + +#define RX_HTT_HDR_STATUS_LEN 64 + +/* This structure layout is programmed via rx ring setup + * so that FW knows how to transfer the rx descriptor to the host. + * Buffers like this are placed on the rx ring. */ +struct htt_rx_desc { + union { + /* This field is filled on the host using the msdu buffer + * from htt_rx_indication */ + struct fw_rx_desc_base fw_desc; + u32 pad; + } __packed; + struct { + struct rx_attention attention; + struct rx_frag_info frag_info; + struct rx_mpdu_start mpdu_start; + struct rx_msdu_start msdu_start; + struct rx_msdu_end msdu_end; + struct rx_mpdu_end mpdu_end; + struct rx_ppdu_start ppdu_start; + struct rx_ppdu_end ppdu_end; + } __packed; + u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN]; + u8 msdu_payload[0]; +}; + +#define HTT_RX_DESC_ALIGN 8 + +#define HTT_MAC_ADDR_LEN 6 + +/* + * FIX THIS + * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size, + * rounded up to a cache line size. + */ +#define HTT_RX_BUF_SIZE 1920 +#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) + +/* + * DMA_MAP expects the buffer to be an integral number of cache lines. + * Rather than checking the actual cache line size, this code makes a + * conservative estimate of what the cache line size could be. + */ +#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ +#define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) + +struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar); +int ath10k_htt_attach_target(struct ath10k_htt *htt); +void ath10k_htt_detach(struct ath10k_htt *htt); + +int ath10k_htt_tx_attach(struct ath10k_htt *htt); +void ath10k_htt_tx_detach(struct ath10k_htt *htt); +int ath10k_htt_rx_attach(struct ath10k_htt *htt); +void ath10k_htt_rx_detach(struct ath10k_htt *htt); +void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); +void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); +int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); +int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); + +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); +int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt); +void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); +int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); +int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *); +#endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c new file mode 100644 index 00000000000..de058d7adca --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -0,0 +1,1167 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" +#include "htt.h" +#include "txrx.h" +#include "debug.h" + +#include <linux/log2.h> + +/* slightly larger than one large A-MPDU */ +#define HTT_RX_RING_SIZE_MIN 128 + +/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */ +#define HTT_RX_RING_SIZE_MAX 2048 + +#define HTT_RX_AVG_FRM_BYTES 1000 + +/* ms, very conservative */ +#define HTT_RX_HOST_LATENCY_MAX_MS 20 + +/* ms, conservative */ +#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10 + +/* when under memory pressure rx ring refill may fail and needs a retry */ +#define HTT_RX_RING_REFILL_RETRY_MS 50 + +static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt) +{ + int size; + + /* + * It is expected that the host CPU will typically be able to + * service the rx indication from one A-MPDU before the rx + * indication from the subsequent A-MPDU happens, roughly 1-2 ms + * later. However, the rx ring should be sized very conservatively, + * to accomodate the worst reasonable delay before the host CPU + * services a rx indication interrupt. + * + * The rx ring need not be kept full of empty buffers. In theory, + * the htt host SW can dynamically track the low-water mark in the + * rx ring, and dynamically adjust the level to which the rx ring + * is filled with empty buffers, to dynamically meet the desired + * low-water mark. + * + * In contrast, it's difficult to resize the rx ring itself, once + * it's in use. Thus, the ring itself should be sized very + * conservatively, while the degree to which the ring is filled + * with empty buffers should be sized moderately conservatively. + */ + + /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ + size = + htt->max_throughput_mbps + + 1000 / + (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS; + + if (size < HTT_RX_RING_SIZE_MIN) + size = HTT_RX_RING_SIZE_MIN; + + if (size > HTT_RX_RING_SIZE_MAX) + size = HTT_RX_RING_SIZE_MAX; + + size = roundup_pow_of_two(size); + + return size; +} + +static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt) +{ + int size; + + /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ + size = + htt->max_throughput_mbps * + 1000 / + (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS; + + /* + * Make sure the fill level is at least 1 less than the ring size. + * Leaving 1 element empty allows the SW to easily distinguish + * between a full ring vs. an empty ring. + */ + if (size >= htt->rx_ring.size) + size = htt->rx_ring.size - 1; + + return size; +} + +static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *cb; + int i; + + for (i = 0; i < htt->rx_ring.fill_cnt; i++) { + skb = htt->rx_ring.netbufs_ring[i]; + cb = ATH10K_SKB_CB(skb); + dma_unmap_single(htt->ar->dev, cb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } + + htt->rx_ring.fill_cnt = 0; +} + +static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) +{ + struct htt_rx_desc *rx_desc; + struct sk_buff *skb; + dma_addr_t paddr; + int ret = 0, idx; + + idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr)); + while (num > 0) { + skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN); + if (!skb) { + ret = -ENOMEM; + goto fail; + } + + if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN)) + skb_pull(skb, + PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) - + skb->data); + + /* Clear rx_desc attention word before posting to Rx ring */ + rx_desc = (struct htt_rx_desc *)skb->data; + rx_desc->attention.flags = __cpu_to_le32(0); + + paddr = dma_map_single(htt->ar->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) { + dev_kfree_skb_any(skb); + ret = -ENOMEM; + goto fail; + } + + ATH10K_SKB_CB(skb)->paddr = paddr; + htt->rx_ring.netbufs_ring[idx] = skb; + htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr); + htt->rx_ring.fill_cnt++; + + num--; + idx++; + idx &= htt->rx_ring.size_mask; + } + +fail: + *(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx); + return ret; +} + +static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) +{ + lockdep_assert_held(&htt->rx_ring.lock); + return __ath10k_htt_rx_ring_fill_n(htt, num); +} + +static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) +{ + int ret, num_to_fill; + + spin_lock_bh(&htt->rx_ring.lock); + num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; + ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill); + if (ret == -ENOMEM) { + /* + * Failed to fill it to the desired level - + * we'll start a timer and try again next time. + * As long as enough buffers are left in the ring for + * another A-MPDU rx, no special recovery is needed. + */ + mod_timer(&htt->rx_ring.refill_retry_timer, jiffies + + msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS)); + } + spin_unlock_bh(&htt->rx_ring.lock); +} + +static void ath10k_htt_rx_ring_refill_retry(unsigned long arg) +{ + struct ath10k_htt *htt = (struct ath10k_htt *)arg; + ath10k_htt_rx_msdu_buff_replenish(htt); +} + +static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt) +{ + return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) - + htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask; +} + +void ath10k_htt_rx_detach(struct ath10k_htt *htt) +{ + int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; + + del_timer_sync(&htt->rx_ring.refill_retry_timer); + + while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { + struct sk_buff *skb = + htt->rx_ring.netbufs_ring[sw_rd_idx]; + struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); + + dma_unmap_single(htt->ar->dev, cb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]); + sw_rd_idx++; + sw_rd_idx &= htt->rx_ring.size_mask; + } + + dma_free_coherent(htt->ar->dev, + (htt->rx_ring.size * + sizeof(htt->rx_ring.paddrs_ring)), + htt->rx_ring.paddrs_ring, + htt->rx_ring.base_paddr); + + dma_free_coherent(htt->ar->dev, + sizeof(*htt->rx_ring.alloc_idx.vaddr), + htt->rx_ring.alloc_idx.vaddr, + htt->rx_ring.alloc_idx.paddr); + + kfree(htt->rx_ring.netbufs_ring); +} + +static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) +{ + int idx; + struct sk_buff *msdu; + + spin_lock_bh(&htt->rx_ring.lock); + + if (ath10k_htt_rx_ring_elems(htt) == 0) + ath10k_warn("htt rx ring is empty!\n"); + + idx = htt->rx_ring.sw_rd_idx.msdu_payld; + msdu = htt->rx_ring.netbufs_ring[idx]; + + idx++; + idx &= htt->rx_ring.size_mask; + htt->rx_ring.sw_rd_idx.msdu_payld = idx; + htt->rx_ring.fill_cnt--; + + spin_unlock_bh(&htt->rx_ring.lock); + return msdu; +} + +static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb) +{ + struct sk_buff *next; + + while (skb) { + next = skb->next; + dev_kfree_skb_any(skb); + skb = next; + } +} + +static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, + u8 **fw_desc, int *fw_desc_len, + struct sk_buff **head_msdu, + struct sk_buff **tail_msdu) +{ + int msdu_len, msdu_chaining = 0; + struct sk_buff *msdu; + struct htt_rx_desc *rx_desc; + + if (ath10k_htt_rx_ring_elems(htt) == 0) + ath10k_warn("htt rx ring is empty!\n"); + + if (htt->rx_confused) { + ath10k_warn("htt is confused. refusing rx\n"); + return 0; + } + + msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt); + while (msdu) { + int last_msdu, msdu_len_invalid, msdu_chained; + + dma_unmap_single(htt->ar->dev, + ATH10K_SKB_CB(msdu)->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", + msdu->data, msdu->len + skb_tailroom(msdu)); + + rx_desc = (struct htt_rx_desc *)msdu->data; + + /* FIXME: we must report msdu payload since this is what caller + * expects now */ + skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload)); + skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload)); + + /* + * Sanity check - confirm the HW is finished filling in the + * rx data. + * If the HW and SW are working correctly, then it's guaranteed + * that the HW's MAC DMA is done before this point in the SW. + * To prevent the case that we handle a stale Rx descriptor, + * just assert for now until we have a way to recover. + */ + if (!(__le32_to_cpu(rx_desc->attention.flags) + & RX_ATTENTION_FLAGS_MSDU_DONE)) { + ath10k_htt_rx_free_msdu_chain(*head_msdu); + *head_msdu = NULL; + msdu = NULL; + ath10k_err("htt rx stopped. cannot recover\n"); + htt->rx_confused = true; + break; + } + + /* + * Copy the FW rx descriptor for this MSDU from the rx + * indication message into the MSDU's netbuf. HL uses the + * same rx indication message definition as LL, and simply + * appends new info (fields from the HW rx desc, and the + * MSDU payload itself). So, the offset into the rx + * indication message only has to account for the standard + * offset of the per-MSDU FW rx desc info within the + * message, and how many bytes of the per-MSDU FW rx desc + * info have already been consumed. (And the endianness of + * the host, since for a big-endian host, the rx ind + * message contents, including the per-MSDU rx desc bytes, + * were byteswapped during upload.) + */ + if (*fw_desc_len > 0) { + rx_desc->fw_desc.info0 = **fw_desc; + /* + * The target is expected to only provide the basic + * per-MSDU rx descriptors. Just to be sure, verify + * that the target has not attached extension data + * (e.g. LRO flow ID). + */ + + /* or more, if there's extension data */ + (*fw_desc)++; + (*fw_desc_len)--; + } else { + /* + * When an oversized AMSDU happened, FW will lost + * some of MSDU status - in this case, the FW + * descriptors provided will be less than the + * actual MSDUs inside this MPDU. Mark the FW + * descriptors so that it will still deliver to + * upper stack, if no CRC error for this MPDU. + * + * FIX THIS - the FW descriptors are actually for + * MSDUs in the end of this A-MSDU instead of the + * beginning. + */ + rx_desc->fw_desc.info0 = 0; + } + + msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags) + & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR | + RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR)); + msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0), + RX_MSDU_START_INFO0_MSDU_LENGTH); + msdu_chained = rx_desc->frag_info.ring2_more_count; + + if (msdu_len_invalid) + msdu_len = 0; + + skb_trim(msdu, 0); + skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE)); + msdu_len -= msdu->len; + + /* FIXME: Do chained buffers include htt_rx_desc or not? */ + while (msdu_chained--) { + struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt); + + dma_unmap_single(htt->ar->dev, + ATH10K_SKB_CB(next)->paddr, + next->len + skb_tailroom(next), + DMA_FROM_DEVICE); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", + next->data, + next->len + skb_tailroom(next)); + + skb_trim(next, 0); + skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE)); + msdu_len -= next->len; + + msdu->next = next; + msdu = next; + msdu_chaining = 1; + } + + if (msdu_len > 0) { + /* This may suggest FW bug? */ + ath10k_warn("htt rx msdu len not consumed (%d)\n", + msdu_len); + } + + last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & + RX_MSDU_END_INFO0_LAST_MSDU; + + if (last_msdu) { + msdu->next = NULL; + break; + } else { + struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt); + msdu->next = next; + msdu = next; + } + } + *tail_msdu = msdu; + + /* + * Don't refill the ring yet. + * + * First, the elements popped here are still in use - it is not + * safe to overwrite them until the matching call to + * mpdu_desc_list_next. Second, for efficiency it is preferable to + * refill the rx ring with 1 PPDU's worth of rx buffers (something + * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers + * (something like 3 buffers). Consequently, we'll rely on the txrx + * SW to tell us when it is done pulling all the PPDU's rx buffers + * out of the rx ring, and then refill it just once. + */ + + return msdu_chaining; +} + +int ath10k_htt_rx_attach(struct ath10k_htt *htt) +{ + dma_addr_t paddr; + void *vaddr; + struct timer_list *timer = &htt->rx_ring.refill_retry_timer; + + htt->rx_ring.size = ath10k_htt_rx_ring_size(htt); + if (!is_power_of_2(htt->rx_ring.size)) { + ath10k_warn("htt rx ring size is not power of 2\n"); + return -EINVAL; + } + + htt->rx_ring.size_mask = htt->rx_ring.size - 1; + + /* + * Set the initial value for the level to which the rx ring + * should be filled, based on the max throughput and the + * worst likely latency for the host to fill the rx ring + * with new buffers. In theory, this fill level can be + * dynamically adjusted from the initial value set here, to + * reflect the actual host latency rather than a + * conservative assumption about the host latency. + */ + htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt); + + htt->rx_ring.netbufs_ring = + kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *), + GFP_KERNEL); + if (!htt->rx_ring.netbufs_ring) + goto err_netbuf; + + vaddr = dma_alloc_coherent(htt->ar->dev, + (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)), + &paddr, GFP_DMA); + if (!vaddr) + goto err_dma_ring; + + htt->rx_ring.paddrs_ring = vaddr; + htt->rx_ring.base_paddr = paddr; + + vaddr = dma_alloc_coherent(htt->ar->dev, + sizeof(*htt->rx_ring.alloc_idx.vaddr), + &paddr, GFP_DMA); + if (!vaddr) + goto err_dma_idx; + + htt->rx_ring.alloc_idx.vaddr = vaddr; + htt->rx_ring.alloc_idx.paddr = paddr; + htt->rx_ring.sw_rd_idx.msdu_payld = 0; + *htt->rx_ring.alloc_idx.vaddr = 0; + + /* Initialize the Rx refill retry timer */ + setup_timer(timer, ath10k_htt_rx_ring_refill_retry, (unsigned long)htt); + + spin_lock_init(&htt->rx_ring.lock); + + htt->rx_ring.fill_cnt = 0; + if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) + goto err_fill_ring; + + ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n", + htt->rx_ring.size, htt->rx_ring.fill_level); + return 0; + +err_fill_ring: + ath10k_htt_rx_ring_free(htt); + dma_free_coherent(htt->ar->dev, + sizeof(*htt->rx_ring.alloc_idx.vaddr), + htt->rx_ring.alloc_idx.vaddr, + htt->rx_ring.alloc_idx.paddr); +err_dma_idx: + dma_free_coherent(htt->ar->dev, + (htt->rx_ring.size * + sizeof(htt->rx_ring.paddrs_ring)), + htt->rx_ring.paddrs_ring, + htt->rx_ring.base_paddr); +err_dma_ring: + kfree(htt->rx_ring.netbufs_ring); +err_netbuf: + return -ENOMEM; +} + +static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type) +{ + switch (type) { + case HTT_RX_MPDU_ENCRYPT_WEP40: + case HTT_RX_MPDU_ENCRYPT_WEP104: + return 4; + case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC: + case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */ + case HTT_RX_MPDU_ENCRYPT_TKIP_WPA: + case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */ + case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2: + return 8; + case HTT_RX_MPDU_ENCRYPT_NONE: + return 0; + } + + ath10k_warn("unknown encryption type %d\n", type); + return 0; +} + +static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type) +{ + switch (type) { + case HTT_RX_MPDU_ENCRYPT_NONE: + case HTT_RX_MPDU_ENCRYPT_WEP40: + case HTT_RX_MPDU_ENCRYPT_WEP104: + case HTT_RX_MPDU_ENCRYPT_WEP128: + case HTT_RX_MPDU_ENCRYPT_WAPI: + return 0; + case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC: + case HTT_RX_MPDU_ENCRYPT_TKIP_WPA: + return 4; + case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2: + return 8; + } + + ath10k_warn("unknown encryption type %d\n", type); + return 0; +} + +/* Applies for first msdu in chain, before altering it. */ +static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + enum rx_msdu_decap_format fmt; + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + + if (fmt == RX_MSDU_DECAP_RAW) + return (void *)skb->data; + else + return (void *)skb->data - RX_HTT_HDR_STATUS_LEN; +} + +/* This function only applies for first msdu in an msdu chain */ +static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + if (qc[0] & 0x80) + return true; + } + return false; +} + +static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt, + struct htt_rx_info *info) +{ + struct htt_rx_desc *rxd; + struct sk_buff *amsdu; + struct sk_buff *first; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = info->skb; + enum rx_msdu_decap_format fmt; + enum htt_rx_mpdu_encrypt_type enctype; + unsigned int hdr_len; + int crypto_len; + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + + /* FIXME: No idea what assumptions are safe here. Need logs */ + if ((fmt == RX_MSDU_DECAP_RAW && skb->next) || + (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) { + ath10k_htt_rx_free_msdu_chain(skb->next); + skb->next = NULL; + return -ENOTSUPP; + } + + /* A-MSDU max is a little less than 8K */ + amsdu = dev_alloc_skb(8*1024); + if (!amsdu) { + ath10k_warn("A-MSDU allocation failed\n"); + ath10k_htt_rx_free_msdu_chain(skb->next); + skb->next = NULL; + return -ENOMEM; + } + + if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) { + int hdrlen; + + hdr = (void *)rxd->rx_hdr_status; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen); + } + + first = skb; + while (skb) { + void *decap_hdr; + int decap_len = 0; + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + decap_hdr = (void *)rxd->rx_hdr_status; + + if (skb == first) { + /* We receive linked A-MSDU subframe skbuffs. The + * first one contains the original 802.11 header (and + * possible crypto param) in the RX descriptor. The + * A-MSDU subframe header follows that. Each part is + * aligned to 4 byte boundary. */ + + hdr = (void *)amsdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath10k_htt_rx_crypto_param_len(enctype); + + decap_hdr += roundup(hdr_len, 4); + decap_hdr += roundup(crypto_len, 4); + } + + if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { + /* Ethernet2 decap inserts ethernet header in place of + * A-MSDU subframe header. */ + skb_pull(skb, 6 + 6 + 2); + + /* A-MSDU subframe header length */ + decap_len += 6 + 6 + 2; + + /* Ethernet2 decap also strips the LLC/SNAP so we need + * to re-insert it. The LLC/SNAP follows A-MSDU + * subframe header. */ + /* FIXME: Not all LLCs are 8 bytes long */ + decap_len += 8; + + memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); + } + + if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) { + /* Native Wifi decap inserts regular 802.11 header + * in place of A-MSDU subframe header. */ + hdr = (struct ieee80211_hdr *)skb->data; + skb_pull(skb, ieee80211_hdrlen(hdr->frame_control)); + + /* A-MSDU subframe header length */ + decap_len += 6 + 6 + 2; + + memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); + } + + if (fmt == RX_MSDU_DECAP_RAW) + skb_trim(skb, skb->len - 4); /* remove FCS */ + + memcpy(skb_put(amsdu, skb->len), skb->data, skb->len); + + /* A-MSDU subframes are padded to 4bytes + * but relative to first subframe, not the whole MPDU */ + if (skb->next && ((decap_len + skb->len) & 3)) { + int padlen = 4 - ((decap_len + skb->len) & 3); + memset(skb_put(amsdu, padlen), 0, padlen); + } + + skb = skb->next; + } + + info->skb = amsdu; + info->encrypt_type = enctype; + + ath10k_htt_rx_free_msdu_chain(first); + + return 0; +} + +static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) +{ + struct sk_buff *skb = info->skb; + struct htt_rx_desc *rxd; + struct ieee80211_hdr *hdr; + enum rx_msdu_decap_format fmt; + enum htt_rx_mpdu_encrypt_type enctype; + + /* This shouldn't happen. If it does than it may be a FW bug. */ + if (skb->next) { + ath10k_warn("received chained non A-MSDU frame\n"); + ath10k_htt_rx_free_msdu_chain(skb->next); + skb->next = NULL; + } + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN; + + switch (fmt) { + case RX_MSDU_DECAP_RAW: + /* remove trailing FCS */ + skb_trim(skb, skb->len - 4); + break; + case RX_MSDU_DECAP_NATIVE_WIFI: + /* nothing to do here */ + break; + case RX_MSDU_DECAP_ETHERNET2_DIX: + /* macaddr[6] + macaddr[6] + ethertype[2] */ + skb_pull(skb, 6 + 6 + 2); + break; + case RX_MSDU_DECAP_8023_SNAP_LLC: + /* macaddr[6] + macaddr[6] + len[2] */ + /* we don't need this for non-A-MSDU */ + skb_pull(skb, 6 + 6 + 2); + break; + } + + if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { + void *llc; + int llclen; + + llclen = 8; + llc = hdr; + llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4); + llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); + + skb_push(skb, llclen); + memcpy(skb->data, llc, llclen); + } + + if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) { + int len = ieee80211_hdrlen(hdr->frame_control); + skb_push(skb, len); + memcpy(skb->data, hdr, len); + } + + info->skb = skb; + info->encrypt_type = enctype; + return 0; +} + +static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR) + return true; + + return false; +} + +static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_FCS_ERR) + return true; + + return false; +} + +static void ath10k_htt_rx_handler(struct ath10k_htt *htt, + struct htt_rx_indication *rx) +{ + struct htt_rx_info info; + struct htt_rx_indication_mpdu_range *mpdu_ranges; + struct ieee80211_hdr *hdr; + int num_mpdu_ranges; + int fw_desc_len; + u8 *fw_desc; + int i, j; + int ret; + + memset(&info, 0, sizeof(info)); + + fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes); + fw_desc = (u8 *)&rx->fw_desc; + + num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), + HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", + rx, sizeof(*rx) + + (sizeof(struct htt_rx_indication_mpdu_range) * + num_mpdu_ranges)); + + for (i = 0; i < num_mpdu_ranges; i++) { + info.status = mpdu_ranges[i].mpdu_range_status; + + for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) { + struct sk_buff *msdu_head, *msdu_tail; + enum htt_rx_mpdu_status status; + int msdu_chaining; + + msdu_head = NULL; + msdu_tail = NULL; + msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, + &fw_desc, + &fw_desc_len, + &msdu_head, + &msdu_tail); + + if (!msdu_head) { + ath10k_warn("htt rx no data!\n"); + continue; + } + + if (msdu_head->len == 0) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx dropping due to zero-len\n"); + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + if (ath10k_htt_rx_has_decrypt_err(msdu_head)) { + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + status = info.status; + + /* Skip mgmt frames while we handle this in WMI */ + if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) { + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + if (status != HTT_RX_IND_MPDU_STATUS_OK && + status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR && + !htt->ar->monitor_enabled) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx ignoring frame w/ status %d\n", + status); + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + /* FIXME: we do not support chaining yet. + * this needs investigation */ + if (msdu_chaining) { + ath10k_warn("msdu_chaining is true\n"); + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + info.skb = msdu_head; + info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); + info.signal = ATH10K_DEFAULT_NOISE_FLOOR; + info.signal += rx->ppdu.combined_rssi; + + info.rate.info0 = rx->ppdu.info0; + info.rate.info1 = __le32_to_cpu(rx->ppdu.info1); + info.rate.info2 = __le32_to_cpu(rx->ppdu.info2); + + hdr = ath10k_htt_rx_skb_get_hdr(msdu_head); + + if (ath10k_htt_rx_hdr_is_amsdu(hdr)) + ret = ath10k_htt_rx_amsdu(htt, &info); + else + ret = ath10k_htt_rx_msdu(htt, &info); + + if (ret && !info.fcs_err) { + ath10k_warn("error processing msdus %d\n", ret); + dev_kfree_skb_any(info.skb); + continue; + } + + if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data)) + ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n"); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ", + info.skb->data, info.skb->len); + ath10k_process_rx(htt->ar, &info); + } + } + + ath10k_htt_rx_msdu_buff_replenish(htt); +} + +static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, + struct htt_rx_fragment_indication *frag) +{ + struct sk_buff *msdu_head, *msdu_tail; + struct htt_rx_desc *rxd; + enum rx_msdu_decap_format fmt; + struct htt_rx_info info = {}; + struct ieee80211_hdr *hdr; + int msdu_chaining; + bool tkip_mic_err; + bool decrypt_err; + u8 *fw_desc; + int fw_desc_len, hdrlen, paramlen; + int trim; + + fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes); + fw_desc = (u8 *)frag->fw_msdu_rx_desc; + + msdu_head = NULL; + msdu_tail = NULL; + msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, + &msdu_head, &msdu_tail); + + ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); + + if (!msdu_head) { + ath10k_warn("htt rx frag no data\n"); + return; + } + + if (msdu_chaining || msdu_head != msdu_tail) { + ath10k_warn("aggregation with fragmentation?!\n"); + ath10k_htt_rx_free_msdu_chain(msdu_head); + return; + } + + /* FIXME: implement signal strength */ + + hdr = (struct ieee80211_hdr *)msdu_head->data; + rxd = (void *)msdu_head->data - sizeof(*rxd); + tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) & + RX_ATTENTION_FLAGS_TKIP_MIC_ERR); + decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) & + RX_ATTENTION_FLAGS_DECRYPT_ERR); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + + if (fmt != RX_MSDU_DECAP_RAW) { + ath10k_warn("we dont support non-raw fragmented rx yet\n"); + dev_kfree_skb_any(msdu_head); + goto end; + } + + info.skb = msdu_head; + info.status = HTT_RX_IND_MPDU_STATUS_OK; + info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + + if (tkip_mic_err) { + ath10k_warn("tkip mic error\n"); + info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR; + } + + if (decrypt_err) { + ath10k_warn("decryption err in fragmented rx\n"); + dev_kfree_skb_any(info.skb); + goto end; + } + + if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) { + hdrlen = ieee80211_hdrlen(hdr->frame_control); + paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type); + + /* It is more efficient to move the header than the payload */ + memmove((void *)info.skb->data + paramlen, + (void *)info.skb->data, + hdrlen); + skb_pull(info.skb, paramlen); + hdr = (struct ieee80211_hdr *)info.skb->data; + } + + /* remove trailing FCS */ + trim = 4; + + /* remove crypto trailer */ + trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type); + + /* last fragment of TKIP frags has MIC */ + if (!ieee80211_has_morefrags(hdr->frame_control) && + info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) + trim += 8; + + if (trim > info.skb->len) { + ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n"); + dev_kfree_skb_any(info.skb); + goto end; + } + + skb_trim(info.skb, info.skb->len - trim); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ", + info.skb->data, info.skb->len); + ath10k_process_rx(htt->ar, &info); + +end: + if (fw_desc_len > 0) { + ath10k_dbg(ATH10K_DBG_HTT, + "expecting more fragmented rx in one indication %d\n", + fw_desc_len); + } +} + +void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_htt *htt = ar->htt; + struct htt_resp *resp = (struct htt_resp *)skb->data; + + /* confirm alignment */ + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath10k_warn("unaligned htt message, expect trouble\n"); + + ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n", + resp->hdr.msg_type); + switch (resp->hdr.msg_type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: { + htt->target_version_major = resp->ver_resp.major; + htt->target_version_minor = resp->ver_resp.minor; + complete(&htt->target_version_received); + break; + } + case HTT_T2H_MSG_TYPE_RX_IND: { + ath10k_htt_rx_handler(htt, &resp->rx_ind); + break; + } + case HTT_T2H_MSG_TYPE_PEER_MAP: { + struct htt_peer_map_event ev = { + .vdev_id = resp->peer_map.vdev_id, + .peer_id = __le16_to_cpu(resp->peer_map.peer_id), + }; + memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr)); + ath10k_peer_map_event(htt, &ev); + break; + } + case HTT_T2H_MSG_TYPE_PEER_UNMAP: { + struct htt_peer_unmap_event ev = { + .peer_id = __le16_to_cpu(resp->peer_unmap.peer_id), + }; + ath10k_peer_unmap_event(htt, &ev); + break; + } + case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: { + struct htt_tx_done tx_done = {}; + int status = __le32_to_cpu(resp->mgmt_tx_completion.status); + + tx_done.msdu_id = + __le32_to_cpu(resp->mgmt_tx_completion.desc_id); + + switch (status) { + case HTT_MGMT_TX_STATUS_OK: + break; + case HTT_MGMT_TX_STATUS_RETRY: + tx_done.no_ack = true; + break; + case HTT_MGMT_TX_STATUS_DROP: + tx_done.discard = true; + break; + } + + ath10k_txrx_tx_completed(htt, &tx_done); + break; + } + case HTT_T2H_MSG_TYPE_TX_COMPL_IND: { + struct htt_tx_done tx_done = {}; + int status = MS(resp->data_tx_completion.flags, + HTT_DATA_TX_STATUS); + __le16 msdu_id; + int i; + + switch (status) { + case HTT_DATA_TX_STATUS_NO_ACK: + tx_done.no_ack = true; + break; + case HTT_DATA_TX_STATUS_OK: + break; + case HTT_DATA_TX_STATUS_DISCARD: + case HTT_DATA_TX_STATUS_POSTPONE: + case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL: + tx_done.discard = true; + break; + default: + ath10k_warn("unhandled tx completion status %d\n", + status); + tx_done.discard = true; + break; + } + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n", + resp->data_tx_completion.num_msdus); + + for (i = 0; i < resp->data_tx_completion.num_msdus; i++) { + msdu_id = resp->data_tx_completion.msdus[i]; + tx_done.msdu_id = __le16_to_cpu(msdu_id); + ath10k_txrx_tx_completed(htt, &tx_done); + } + break; + } + case HTT_T2H_MSG_TYPE_SEC_IND: { + struct ath10k *ar = htt->ar; + struct htt_security_indication *ev = &resp->security_indication; + + ath10k_dbg(ATH10K_DBG_HTT, + "sec ind peer_id %d unicast %d type %d\n", + __le16_to_cpu(ev->peer_id), + !!(ev->flags & HTT_SECURITY_IS_UNICAST), + MS(ev->flags, HTT_SECURITY_TYPE)); + complete(&ar->install_key_done); + break; + } + case HTT_T2H_MSG_TYPE_RX_FRAG_IND: { + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", + skb->data, skb->len); + ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind); + break; + } + case HTT_T2H_MSG_TYPE_TEST: + /* FIX THIS */ + break; + case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: + case HTT_T2H_MSG_TYPE_STATS_CONF: + case HTT_T2H_MSG_TYPE_RX_ADDBA: + case HTT_T2H_MSG_TYPE_RX_DELBA: + case HTT_T2H_MSG_TYPE_RX_FLUSH: + default: + ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n", + resp->hdr.msg_type); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", + skb->data, skb->len); + break; + }; + + /* Free the indication buffer */ + dev_kfree_skb_any(skb); +} diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c new file mode 100644 index 00000000000..ef79106db24 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/etherdevice.h> +#include "htt.h" +#include "mac.h" +#include "hif.h" +#include "txrx.h" +#include "debug.h" + +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +{ + htt->num_pending_tx--; + if (htt->num_pending_tx == htt->max_num_pending_tx - 1) + ieee80211_wake_queues(htt->ar->hw); +} + +static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +{ + spin_lock_bh(&htt->tx_lock); + __ath10k_htt_tx_dec_pending(htt); + spin_unlock_bh(&htt->tx_lock); +} + +static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) +{ + int ret = 0; + + spin_lock_bh(&htt->tx_lock); + + if (htt->num_pending_tx >= htt->max_num_pending_tx) { + ret = -EBUSY; + goto exit; + } + + htt->num_pending_tx++; + if (htt->num_pending_tx == htt->max_num_pending_tx) + ieee80211_stop_queues(htt->ar->hw); + +exit: + spin_unlock_bh(&htt->tx_lock); + return ret; +} + +int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt) +{ + int msdu_id; + + lockdep_assert_held(&htt->tx_lock); + + msdu_id = find_first_zero_bit(htt->used_msdu_ids, + htt->max_num_pending_tx); + if (msdu_id == htt->max_num_pending_tx) + return -ENOBUFS; + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id); + __set_bit(msdu_id, htt->used_msdu_ids); + return msdu_id; +} + +void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) +{ + lockdep_assert_held(&htt->tx_lock); + + if (!test_bit(msdu_id, htt->used_msdu_ids)) + ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id); + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id); + __clear_bit(msdu_id, htt->used_msdu_ids); +} + +int ath10k_htt_tx_attach(struct ath10k_htt *htt) +{ + u8 pipe; + + spin_lock_init(&htt->tx_lock); + init_waitqueue_head(&htt->empty_tx_wq); + + /* At the beginning free queue number should hint us the maximum + * queue length */ + pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id; + htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar, + pipe); + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n", + htt->max_num_pending_tx); + + htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) * + htt->max_num_pending_tx, GFP_KERNEL); + if (!htt->pending_tx) + return -ENOMEM; + + htt->used_msdu_ids = kzalloc(sizeof(unsigned long) * + BITS_TO_LONGS(htt->max_num_pending_tx), + GFP_KERNEL); + if (!htt->used_msdu_ids) { + kfree(htt->pending_tx); + return -ENOMEM; + } + + return 0; +} + +static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) +{ + struct sk_buff *txdesc; + int msdu_id; + + /* No locks needed. Called after communication with the device has + * been stopped. */ + + for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) { + if (!test_bit(msdu_id, htt->used_msdu_ids)) + continue; + + txdesc = htt->pending_tx[msdu_id]; + if (!txdesc) + continue; + + ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", + msdu_id); + + if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) + ATH10K_SKB_CB(txdesc)->htt.refcount = 1; + + ATH10K_SKB_CB(txdesc)->htt.discard = true; + ath10k_txrx_tx_unref(htt, txdesc); + } +} + +void ath10k_htt_tx_detach(struct ath10k_htt *htt) +{ + ath10k_htt_tx_cleanup_pending(htt); + kfree(htt->pending_tx); + kfree(htt->used_msdu_ids); + return; +} + +void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + struct ath10k_htt *htt = ar->htt; + + if (skb_cb->htt.is_conf) { + dev_kfree_skb_any(skb); + return; + } + + if (skb_cb->is_aborted) { + skb_cb->htt.discard = true; + + /* if the skbuff is aborted we need to make sure we'll free up + * the tx resources, we can't simply run tx_unref() 2 times + * because if htt tx completion came in earlier we'd access + * unallocated memory */ + if (skb_cb->htt.refcount > 1) + skb_cb->htt.refcount = 1; + } + + ath10k_txrx_tx_unref(htt, skb); +} + +int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) +{ + struct sk_buff *skb; + struct htt_cmd *cmd; + int len = 0; + int ret; + + len += sizeof(cmd->hdr); + len += sizeof(cmd->ver_req); + + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_cmd *)skb->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ; + + ATH10K_SKB_CB(skb)->htt.is_conf = true; + + ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) +{ + struct sk_buff *skb; + struct htt_cmd *cmd; + struct htt_rx_ring_setup_ring *ring; + const int num_rx_ring = 1; + u16 flags; + u32 fw_idx; + int len; + int ret; + + /* + * the HW expects the buffer to be an integral number of 4-byte + * "words" + */ + BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4)); + BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0); + + len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr) + + (sizeof(*ring) * num_rx_ring); + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + + cmd = (struct htt_cmd *)skb->data; + ring = &cmd->rx_setup.rings[0]; + + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG; + cmd->rx_setup.hdr.num_rings = 1; + + /* FIXME: do we need all of this? */ + flags = 0; + flags |= HTT_RX_RING_FLAGS_MAC80211_HDR; + flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD; + flags |= HTT_RX_RING_FLAGS_PPDU_START; + flags |= HTT_RX_RING_FLAGS_PPDU_END; + flags |= HTT_RX_RING_FLAGS_MPDU_START; + flags |= HTT_RX_RING_FLAGS_MPDU_END; + flags |= HTT_RX_RING_FLAGS_MSDU_START; + flags |= HTT_RX_RING_FLAGS_MSDU_END; + flags |= HTT_RX_RING_FLAGS_RX_ATTENTION; + flags |= HTT_RX_RING_FLAGS_FRAG_INFO; + flags |= HTT_RX_RING_FLAGS_UNICAST_RX; + flags |= HTT_RX_RING_FLAGS_MULTICAST_RX; + flags |= HTT_RX_RING_FLAGS_CTRL_RX; + flags |= HTT_RX_RING_FLAGS_MGMT_RX; + flags |= HTT_RX_RING_FLAGS_NULL_RX; + flags |= HTT_RX_RING_FLAGS_PHY_DATA_RX; + + fw_idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr); + + ring->fw_idx_shadow_reg_paddr = + __cpu_to_le32(htt->rx_ring.alloc_idx.paddr); + ring->rx_ring_base_paddr = __cpu_to_le32(htt->rx_ring.base_paddr); + ring->rx_ring_len = __cpu_to_le16(htt->rx_ring.size); + ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE); + ring->flags = __cpu_to_le16(flags); + ring->fw_idx_init_val = __cpu_to_le16(fw_idx); + +#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4) + + ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); + ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); + ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); + ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); + ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); + ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); + ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); + ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); + ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); + ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); + +#undef desc_offset + + ATH10K_SKB_CB(skb)->htt.is_conf = true; + + ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) +{ + struct device *dev = htt->ar->dev; + struct ath10k_skb_cb *skb_cb; + struct sk_buff *txdesc = NULL; + struct htt_cmd *cmd; + u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + int len = 0; + int msdu_id = -1; + int res; + + + res = ath10k_htt_tx_inc_pending(htt); + if (res) + return res; + + len += sizeof(cmd->hdr); + len += sizeof(cmd->mgmt_tx); + + txdesc = ath10k_htc_alloc_skb(len); + if (!txdesc) { + res = -ENOMEM; + goto err; + } + + spin_lock_bh(&htt->tx_lock); + msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); + if (msdu_id < 0) { + spin_unlock_bh(&htt->tx_lock); + res = msdu_id; + goto err; + } + htt->pending_tx[msdu_id] = txdesc; + spin_unlock_bh(&htt->tx_lock); + + res = ath10k_skb_map(dev, msdu); + if (res) + goto err; + + skb_put(txdesc, len); + cmd = (struct htt_cmd *)txdesc->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_MGMT_TX; + cmd->mgmt_tx.msdu_paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr); + cmd->mgmt_tx.len = __cpu_to_le32(msdu->len); + cmd->mgmt_tx.desc_id = __cpu_to_le32(msdu_id); + cmd->mgmt_tx.vdev_id = __cpu_to_le32(vdev_id); + memcpy(cmd->mgmt_tx.hdr, msdu->data, + min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN)); + + /* refcount is decremented by HTC and HTT completions until it reaches + * zero and is freed */ + skb_cb = ATH10K_SKB_CB(txdesc); + skb_cb->htt.msdu_id = msdu_id; + skb_cb->htt.refcount = 2; + skb_cb->htt.msdu = msdu; + + res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + if (res) + goto err; + + return 0; + +err: + ath10k_skb_unmap(dev, msdu); + + if (txdesc) + dev_kfree_skb_any(txdesc); + if (msdu_id >= 0) { + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); + } + ath10k_htt_tx_dec_pending(htt); + return res; +} + +int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) +{ + struct device *dev = htt->ar->dev; + struct htt_cmd *cmd; + struct htt_data_tx_desc_frag *tx_frags; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ath10k_skb_cb *skb_cb; + struct sk_buff *txdesc = NULL; + struct sk_buff *txfrag = NULL; + u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + u8 tid; + int prefetch_len, desc_len, frag_len; + dma_addr_t frags_paddr; + int msdu_id = -1; + int res; + u8 flags0; + u16 flags1; + + res = ath10k_htt_tx_inc_pending(htt); + if (res) + return res; + + prefetch_len = min(htt->prefetch_len, msdu->len); + prefetch_len = roundup(prefetch_len, 4); + + desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len; + frag_len = sizeof(*tx_frags) * 2; + + txdesc = ath10k_htc_alloc_skb(desc_len); + if (!txdesc) { + res = -ENOMEM; + goto err; + } + + txfrag = dev_alloc_skb(frag_len); + if (!txfrag) { + res = -ENOMEM; + goto err; + } + + if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) { + ath10k_warn("htt alignment check failed. dropping packet.\n"); + res = -EIO; + goto err; + } + + spin_lock_bh(&htt->tx_lock); + msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); + if (msdu_id < 0) { + spin_unlock_bh(&htt->tx_lock); + res = msdu_id; + goto err; + } + htt->pending_tx[msdu_id] = txdesc; + spin_unlock_bh(&htt->tx_lock); + + res = ath10k_skb_map(dev, msdu); + if (res) + goto err; + + /* tx fragment list must be terminated with zero-entry */ + skb_put(txfrag, frag_len); + tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data; + tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr); + tx_frags[0].len = __cpu_to_le32(msdu->len); + tx_frags[1].paddr = __cpu_to_le32(0); + tx_frags[1].len = __cpu_to_le32(0); + + res = ath10k_skb_map(dev, txfrag); + if (res) + goto err; + + ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n", + (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr, + (unsigned long long) ATH10K_SKB_CB(msdu)->paddr); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ", + txfrag->data, frag_len); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ", + msdu->data, msdu->len); + + skb_put(txdesc, desc_len); + cmd = (struct htt_cmd *)txdesc->data; + memset(cmd, 0, desc_len); + + tid = ATH10K_SKB_CB(msdu)->htt.tid; + + ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid); + + flags0 = 0; + if (!ieee80211_has_protected(hdr->frame_control)) + flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; + flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; + flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + + flags1 = 0; + flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); + flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); + + frags_paddr = ATH10K_SKB_CB(txfrag)->paddr; + + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; + cmd->data_tx.flags0 = flags0; + cmd->data_tx.flags1 = __cpu_to_le16(flags1); + cmd->data_tx.len = __cpu_to_le16(msdu->len); + cmd->data_tx.id = __cpu_to_le16(msdu_id); + cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr); + cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); + + memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len); + + /* refcount is decremented by HTC and HTT completions until it reaches + * zero and is freed */ + skb_cb = ATH10K_SKB_CB(txdesc); + skb_cb->htt.msdu_id = msdu_id; + skb_cb->htt.refcount = 2; + skb_cb->htt.txfrag = txfrag; + skb_cb->htt.msdu = msdu; + + res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + if (res) + goto err; + + return 0; +err: + if (txfrag) + ath10k_skb_unmap(dev, txfrag); + if (txdesc) + dev_kfree_skb_any(txdesc); + if (txfrag) + dev_kfree_skb_any(txfrag); + if (msdu_id >= 0) { + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); + } + ath10k_htt_tx_dec_pending(htt); + ath10k_skb_unmap(dev, msdu); + return res; +} diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h new file mode 100644 index 00000000000..44ed5af0a20 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HW_H_ +#define _HW_H_ + +#include "targaddrs.h" + +/* Supported FW version */ +#define SUPPORTED_FW_MAJOR 1 +#define SUPPORTED_FW_MINOR 0 +#define SUPPORTED_FW_RELEASE 0 +#define SUPPORTED_FW_BUILD 629 + +/* QCA988X 1.0 definitions */ +#define QCA988X_HW_1_0_VERSION 0x4000002c +#define QCA988X_HW_1_0_FW_DIR "ath10k/QCA988X/hw1.0" +#define QCA988X_HW_1_0_FW_FILE "firmware.bin" +#define QCA988X_HW_1_0_OTP_FILE "otp.bin" +#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin" +#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234 + +/* QCA988X 2.0 definitions */ +#define QCA988X_HW_2_0_VERSION 0x4100016c +#define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0" +#define QCA988X_HW_2_0_FW_FILE "firmware.bin" +#define QCA988X_HW_2_0_OTP_FILE "otp.bin" +#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" +#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 + +/* Known pecularities: + * - current FW doesn't support raw rx mode (last tested v599) + * - current FW dumps upon raw tx mode (last tested v599) + * - raw appears in nwifi decap, raw and nwifi appear in ethernet decap + * - raw have FCS, nwifi doesn't + * - ethernet frames have 802.11 header decapped and parts (base hdr, cipher + * param, llc/snap) are aligned to 4byte boundaries each */ +enum ath10k_hw_txrx_mode { + ATH10K_HW_TXRX_RAW = 0, + ATH10K_HW_TXRX_NATIVE_WIFI = 1, + ATH10K_HW_TXRX_ETHERNET = 2, +}; + +enum ath10k_mcast2ucast_mode { + ATH10K_MCAST2UCAST_DISABLED = 0, + ATH10K_MCAST2UCAST_ENABLED = 1, +}; + +#define TARGET_NUM_VDEVS 8 +#define TARGET_NUM_PEER_AST 2 +#define TARGET_NUM_WDS_ENTRIES 32 +#define TARGET_DMA_BURST_SIZE 0 +#define TARGET_MAC_AGGR_DELIM 0 +#define TARGET_AST_SKID_LIMIT 16 +#define TARGET_NUM_PEERS 16 +#define TARGET_NUM_OFFLOAD_PEERS 0 +#define TARGET_NUM_OFFLOAD_REORDER_BUFS 0 +#define TARGET_NUM_PEER_KEYS 2 +#define TARGET_NUM_TIDS (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS))) +#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_RX_TIMEOUT_LO_PRI 100 +#define TARGET_RX_TIMEOUT_HI_PRI 40 +#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET +#define TARGET_SCAN_MAX_PENDING_REQS 4 +#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_NUM_MCAST_GROUPS 0 +#define TARGET_NUM_MCAST_TABLE_ELEMS 0 +#define TARGET_MCAST2UCAST_MODE ATH10K_MCAST2UCAST_DISABLED +#define TARGET_TX_DBG_LOG_SIZE 1024 +#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0 +#define TARGET_VOW_CONFIG 0 +#define TARGET_NUM_MSDU_DESC (1024 + 400) +#define TARGET_MAX_FRAG_ENTRIES 0 + + +/* Number of Copy Engines supported */ +#define CE_COUNT 8 + +/* + * Total number of PCIe MSI interrupts requested for all interrupt sources. + * PCIe standard forces this to be a power of 2. + * Some Host OS's limit MSI requests that can be granted to 8 + * so for now we abide by this limit and avoid requesting more + * than that. + */ +#define MSI_NUM_REQUEST_LOG2 3 +#define MSI_NUM_REQUEST (1<<MSI_NUM_REQUEST_LOG2) + +/* + * Granted MSIs are assigned as follows: + * Firmware uses the first + * Remaining MSIs, if any, are used by Copy Engines + * This mapping is known to both Target firmware and Host software. + * It may be changed as long as Host and Target are kept in sync. + */ +/* MSI for firmware (errors, etc.) */ +#define MSI_ASSIGN_FW 0 + +/* MSIs for Copy Engines */ +#define MSI_ASSIGN_CE_INITIAL 1 +#define MSI_ASSIGN_CE_MAX 7 + +/* as of IP3.7.1 */ +#define RTC_STATE_V_ON 3 + +#define RTC_STATE_COLD_RESET_MASK 0x00000400 +#define RTC_STATE_V_LSB 0 +#define RTC_STATE_V_MASK 0x00000007 +#define RTC_STATE_ADDRESS 0x0000 +#define PCIE_SOC_WAKE_V_MASK 0x00000001 +#define PCIE_SOC_WAKE_ADDRESS 0x0004 +#define PCIE_SOC_WAKE_RESET 0x00000000 +#define SOC_GLOBAL_RESET_ADDRESS 0x0008 + +#define RTC_SOC_BASE_ADDRESS 0x00004000 +#define RTC_WMAC_BASE_ADDRESS 0x00005000 +#define MAC_COEX_BASE_ADDRESS 0x00006000 +#define BT_COEX_BASE_ADDRESS 0x00007000 +#define SOC_PCIE_BASE_ADDRESS 0x00008000 +#define SOC_CORE_BASE_ADDRESS 0x00009000 +#define WLAN_UART_BASE_ADDRESS 0x0000c000 +#define WLAN_SI_BASE_ADDRESS 0x00010000 +#define WLAN_GPIO_BASE_ADDRESS 0x00014000 +#define WLAN_ANALOG_INTF_BASE_ADDRESS 0x0001c000 +#define WLAN_MAC_BASE_ADDRESS 0x00020000 +#define EFUSE_BASE_ADDRESS 0x00030000 +#define FPGA_REG_BASE_ADDRESS 0x00039000 +#define WLAN_UART2_BASE_ADDRESS 0x00054c00 +#define CE_WRAPPER_BASE_ADDRESS 0x00057000 +#define CE0_BASE_ADDRESS 0x00057400 +#define CE1_BASE_ADDRESS 0x00057800 +#define CE2_BASE_ADDRESS 0x00057c00 +#define CE3_BASE_ADDRESS 0x00058000 +#define CE4_BASE_ADDRESS 0x00058400 +#define CE5_BASE_ADDRESS 0x00058800 +#define CE6_BASE_ADDRESS 0x00058c00 +#define CE7_BASE_ADDRESS 0x00059000 +#define DBI_BASE_ADDRESS 0x00060000 +#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000 +#define PCIE_LOCAL_BASE_ADDRESS 0x00080000 + +#define SOC_RESET_CONTROL_OFFSET 0x00000000 +#define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001 +#define SOC_CPU_CLOCK_OFFSET 0x00000020 +#define SOC_CPU_CLOCK_STANDARD_LSB 0 +#define SOC_CPU_CLOCK_STANDARD_MASK 0x00000003 +#define SOC_CLOCK_CONTROL_OFFSET 0x00000028 +#define SOC_CLOCK_CONTROL_SI0_CLK_MASK 0x00000001 +#define SOC_SYSTEM_SLEEP_OFFSET 0x000000c4 +#define SOC_LPO_CAL_OFFSET 0x000000e0 +#define SOC_LPO_CAL_ENABLE_LSB 20 +#define SOC_LPO_CAL_ENABLE_MASK 0x00100000 + +#define WLAN_RESET_CONTROL_COLD_RST_MASK 0x00000008 +#define WLAN_RESET_CONTROL_WARM_RST_MASK 0x00000004 +#define WLAN_SYSTEM_SLEEP_DISABLE_LSB 0 +#define WLAN_SYSTEM_SLEEP_DISABLE_MASK 0x00000001 + +#define WLAN_GPIO_PIN0_ADDRESS 0x00000028 +#define WLAN_GPIO_PIN0_CONFIG_MASK 0x00007800 +#define WLAN_GPIO_PIN1_ADDRESS 0x0000002c +#define WLAN_GPIO_PIN1_CONFIG_MASK 0x00007800 +#define WLAN_GPIO_PIN10_ADDRESS 0x00000050 +#define WLAN_GPIO_PIN11_ADDRESS 0x00000054 +#define WLAN_GPIO_PIN12_ADDRESS 0x00000058 +#define WLAN_GPIO_PIN13_ADDRESS 0x0000005c + +#define CLOCK_GPIO_OFFSET 0xffffffff +#define CLOCK_GPIO_BT_CLK_OUT_EN_LSB 0 +#define CLOCK_GPIO_BT_CLK_OUT_EN_MASK 0 + +#define SI_CONFIG_OFFSET 0x00000000 +#define SI_CONFIG_BIDIR_OD_DATA_LSB 18 +#define SI_CONFIG_BIDIR_OD_DATA_MASK 0x00040000 +#define SI_CONFIG_I2C_LSB 16 +#define SI_CONFIG_I2C_MASK 0x00010000 +#define SI_CONFIG_POS_SAMPLE_LSB 7 +#define SI_CONFIG_POS_SAMPLE_MASK 0x00000080 +#define SI_CONFIG_INACTIVE_DATA_LSB 5 +#define SI_CONFIG_INACTIVE_DATA_MASK 0x00000020 +#define SI_CONFIG_INACTIVE_CLK_LSB 4 +#define SI_CONFIG_INACTIVE_CLK_MASK 0x00000010 +#define SI_CONFIG_DIVIDER_LSB 0 +#define SI_CONFIG_DIVIDER_MASK 0x0000000f +#define SI_CS_OFFSET 0x00000004 +#define SI_CS_DONE_ERR_MASK 0x00000400 +#define SI_CS_DONE_INT_MASK 0x00000200 +#define SI_CS_START_LSB 8 +#define SI_CS_START_MASK 0x00000100 +#define SI_CS_RX_CNT_LSB 4 +#define SI_CS_RX_CNT_MASK 0x000000f0 +#define SI_CS_TX_CNT_LSB 0 +#define SI_CS_TX_CNT_MASK 0x0000000f + +#define SI_TX_DATA0_OFFSET 0x00000008 +#define SI_TX_DATA1_OFFSET 0x0000000c +#define SI_RX_DATA0_OFFSET 0x00000010 +#define SI_RX_DATA1_OFFSET 0x00000014 + +#define CORE_CTRL_CPU_INTR_MASK 0x00002000 +#define CORE_CTRL_ADDRESS 0x0000 +#define PCIE_INTR_ENABLE_ADDRESS 0x0008 +#define PCIE_INTR_CLR_ADDRESS 0x0014 +#define SCRATCH_3_ADDRESS 0x0030 + +/* Firmware indications to the Host via SCRATCH_3 register. */ +#define FW_INDICATOR_ADDRESS (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS) +#define FW_IND_EVENT_PENDING 1 +#define FW_IND_INITIALIZED 2 + +/* HOST_REG interrupt from firmware */ +#define PCIE_INTR_FIRMWARE_MASK 0x00000400 +#define PCIE_INTR_CE_MASK_ALL 0x0007f800 + +#define DRAM_BASE_ADDRESS 0x00400000 + +#define MISSING 0 + +#define SYSTEM_SLEEP_OFFSET SOC_SYSTEM_SLEEP_OFFSET +#define WLAN_SYSTEM_SLEEP_OFFSET SOC_SYSTEM_SLEEP_OFFSET +#define WLAN_RESET_CONTROL_OFFSET SOC_RESET_CONTROL_OFFSET +#define CLOCK_CONTROL_OFFSET SOC_CLOCK_CONTROL_OFFSET +#define CLOCK_CONTROL_SI0_CLK_MASK SOC_CLOCK_CONTROL_SI0_CLK_MASK +#define RESET_CONTROL_MBOX_RST_MASK MISSING +#define RESET_CONTROL_SI0_RST_MASK SOC_RESET_CONTROL_SI0_RST_MASK +#define GPIO_BASE_ADDRESS WLAN_GPIO_BASE_ADDRESS +#define GPIO_PIN0_OFFSET WLAN_GPIO_PIN0_ADDRESS +#define GPIO_PIN1_OFFSET WLAN_GPIO_PIN1_ADDRESS +#define GPIO_PIN0_CONFIG_MASK WLAN_GPIO_PIN0_CONFIG_MASK +#define GPIO_PIN1_CONFIG_MASK WLAN_GPIO_PIN1_CONFIG_MASK +#define SI_BASE_ADDRESS WLAN_SI_BASE_ADDRESS +#define SCRATCH_BASE_ADDRESS SOC_CORE_BASE_ADDRESS +#define LOCAL_SCRATCH_OFFSET 0x18 +#define CPU_CLOCK_OFFSET SOC_CPU_CLOCK_OFFSET +#define LPO_CAL_OFFSET SOC_LPO_CAL_OFFSET +#define GPIO_PIN10_OFFSET WLAN_GPIO_PIN10_ADDRESS +#define GPIO_PIN11_OFFSET WLAN_GPIO_PIN11_ADDRESS +#define GPIO_PIN12_OFFSET WLAN_GPIO_PIN12_ADDRESS +#define GPIO_PIN13_OFFSET WLAN_GPIO_PIN13_ADDRESS +#define CPU_CLOCK_STANDARD_LSB SOC_CPU_CLOCK_STANDARD_LSB +#define CPU_CLOCK_STANDARD_MASK SOC_CPU_CLOCK_STANDARD_MASK +#define LPO_CAL_ENABLE_LSB SOC_LPO_CAL_ENABLE_LSB +#define LPO_CAL_ENABLE_MASK SOC_LPO_CAL_ENABLE_MASK +#define ANALOG_INTF_BASE_ADDRESS WLAN_ANALOG_INTF_BASE_ADDRESS +#define MBOX_BASE_ADDRESS MISSING +#define INT_STATUS_ENABLE_ERROR_LSB MISSING +#define INT_STATUS_ENABLE_ERROR_MASK MISSING +#define INT_STATUS_ENABLE_CPU_LSB MISSING +#define INT_STATUS_ENABLE_CPU_MASK MISSING +#define INT_STATUS_ENABLE_COUNTER_LSB MISSING +#define INT_STATUS_ENABLE_COUNTER_MASK MISSING +#define INT_STATUS_ENABLE_MBOX_DATA_LSB MISSING +#define INT_STATUS_ENABLE_MBOX_DATA_MASK MISSING +#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB MISSING +#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK MISSING +#define ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB MISSING +#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK MISSING +#define COUNTER_INT_STATUS_ENABLE_BIT_LSB MISSING +#define COUNTER_INT_STATUS_ENABLE_BIT_MASK MISSING +#define INT_STATUS_ENABLE_ADDRESS MISSING +#define CPU_INT_STATUS_ENABLE_BIT_LSB MISSING +#define CPU_INT_STATUS_ENABLE_BIT_MASK MISSING +#define HOST_INT_STATUS_ADDRESS MISSING +#define CPU_INT_STATUS_ADDRESS MISSING +#define ERROR_INT_STATUS_ADDRESS MISSING +#define ERROR_INT_STATUS_WAKEUP_MASK MISSING +#define ERROR_INT_STATUS_WAKEUP_LSB MISSING +#define ERROR_INT_STATUS_RX_UNDERFLOW_MASK MISSING +#define ERROR_INT_STATUS_RX_UNDERFLOW_LSB MISSING +#define ERROR_INT_STATUS_TX_OVERFLOW_MASK MISSING +#define ERROR_INT_STATUS_TX_OVERFLOW_LSB MISSING +#define COUNT_DEC_ADDRESS MISSING +#define HOST_INT_STATUS_CPU_MASK MISSING +#define HOST_INT_STATUS_CPU_LSB MISSING +#define HOST_INT_STATUS_ERROR_MASK MISSING +#define HOST_INT_STATUS_ERROR_LSB MISSING +#define HOST_INT_STATUS_COUNTER_MASK MISSING +#define HOST_INT_STATUS_COUNTER_LSB MISSING +#define RX_LOOKAHEAD_VALID_ADDRESS MISSING +#define WINDOW_DATA_ADDRESS MISSING +#define WINDOW_READ_ADDR_ADDRESS MISSING +#define WINDOW_WRITE_ADDR_ADDRESS MISSING + +#define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB) + +#endif /* _HW_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c new file mode 100644 index 00000000000..da5c333d0d4 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -0,0 +1,3069 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mac.h" + +#include <net/mac80211.h> +#include <linux/etherdevice.h> + +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "htt.h" +#include "txrx.h" + +/**********/ +/* Crypto */ +/**********/ + +static int ath10k_send_key(struct ath10k_vif *arvif, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + const u8 *macaddr) +{ + struct wmi_vdev_install_key_arg arg = { + .vdev_id = arvif->vdev_id, + .key_idx = key->keyidx, + .key_len = key->keylen, + .key_data = key->key, + .macaddr = macaddr, + }; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + arg.key_flags = WMI_KEY_PAIRWISE; + else + arg.key_flags = WMI_KEY_GROUP; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + arg.key_cipher = WMI_CIPHER_AES_CCM; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + break; + case WLAN_CIPHER_SUITE_TKIP: + arg.key_cipher = WMI_CIPHER_TKIP; + arg.key_txmic_len = 8; + arg.key_rxmic_len = 8; + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + arg.key_cipher = WMI_CIPHER_WEP; + /* AP/IBSS mode requires self-key to be groupwise + * Otherwise pairwise key must be set */ + if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN)) + arg.key_flags = WMI_KEY_PAIRWISE; + break; + default: + ath10k_warn("cipher %d is not supported\n", key->cipher); + return -EOPNOTSUPP; + } + + if (cmd == DISABLE_KEY) { + arg.key_cipher = WMI_CIPHER_NONE; + arg.key_data = NULL; + } + + return ath10k_wmi_vdev_install_key(arvif->ar, &arg); +} + +static int ath10k_install_key(struct ath10k_vif *arvif, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + const u8 *macaddr) +{ + struct ath10k *ar = arvif->ar; + int ret; + + INIT_COMPLETION(ar->install_key_done); + + ret = ath10k_send_key(arvif, key, cmd, macaddr); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&ar->install_key_done, 3*HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif, + const u8 *addr) +{ + struct ath10k *ar = arvif->ar; + struct ath10k_peer *peer; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, addr); + spin_unlock_bh(&ar->data_lock); + + if (!peer) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) { + if (arvif->wep_keys[i] == NULL) + continue; + + ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY, + addr); + if (ret) + return ret; + + peer->keys[i] = arvif->wep_keys[i]; + } + + return 0; +} + +static int ath10k_clear_peer_keys(struct ath10k_vif *arvif, + const u8 *addr) +{ + struct ath10k *ar = arvif->ar; + struct ath10k_peer *peer; + int first_errno = 0; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, addr); + spin_unlock_bh(&ar->data_lock); + + if (!peer) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { + if (peer->keys[i] == NULL) + continue; + + ret = ath10k_install_key(arvif, peer->keys[i], + DISABLE_KEY, addr); + if (ret && first_errno == 0) + first_errno = ret; + + if (ret) + ath10k_warn("could not remove peer wep key %d (%d)\n", + i, ret); + + peer->keys[i] = NULL; + } + + return first_errno; +} + +static int ath10k_clear_vdev_key(struct ath10k_vif *arvif, + struct ieee80211_key_conf *key) +{ + struct ath10k *ar = arvif->ar; + struct ath10k_peer *peer; + u8 addr[ETH_ALEN]; + int first_errno = 0; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + for (;;) { + /* since ath10k_install_key we can't hold data_lock all the + * time, so we try to remove the keys incrementally */ + spin_lock_bh(&ar->data_lock); + i = 0; + list_for_each_entry(peer, &ar->peers, list) { + for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { + if (peer->keys[i] == key) { + memcpy(addr, peer->addr, ETH_ALEN); + peer->keys[i] = NULL; + break; + } + } + + if (i < ARRAY_SIZE(peer->keys)) + break; + } + spin_unlock_bh(&ar->data_lock); + + if (i == ARRAY_SIZE(peer->keys)) + break; + + ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr); + if (ret && first_errno == 0) + first_errno = ret; + + if (ret) + ath10k_warn("could not remove key for %pM\n", addr); + } + + return first_errno; +} + + +/*********************/ +/* General utilities */ +/*********************/ + +static inline enum wmi_phy_mode +chan_to_phymode(const struct cfg80211_chan_def *chandef) +{ + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + switch (chandef->chan->band) { + case IEEE80211_BAND_2GHZ: + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + phymode = MODE_11G; + break; + case NL80211_CHAN_WIDTH_20: + phymode = MODE_11NG_HT20; + break; + case NL80211_CHAN_WIDTH_40: + phymode = MODE_11NG_HT40; + break; + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + phymode = MODE_UNKNOWN; + break; + } + break; + case IEEE80211_BAND_5GHZ: + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + phymode = MODE_11A; + break; + case NL80211_CHAN_WIDTH_20: + phymode = MODE_11NA_HT20; + break; + case NL80211_CHAN_WIDTH_40: + phymode = MODE_11NA_HT40; + break; + case NL80211_CHAN_WIDTH_80: + phymode = MODE_11AC_VHT80; + break; + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + phymode = MODE_UNKNOWN; + break; + } + break; + default: + break; + } + + WARN_ON(phymode == MODE_UNKNOWN); + return phymode; +} + +static u8 ath10k_parse_mpdudensity(u8 mpdudensity) +{ +/* + * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + 1 microsecond */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_peer_create(ar, vdev_id, addr); + if (ret) + return ret; + + ret = ath10k_wait_for_peer_created(ar, vdev_id, addr); + if (ret) + return ret; + + return 0; +} + +static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_peer_delete(ar, vdev_id, addr); + if (ret) + return ret; + + ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) + return ret; + + return 0; +} + +static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) +{ + struct ath10k_peer *peer, *tmp; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(peer, tmp, &ar->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + + ath10k_warn("removing stale peer %pM from vdev_id %d\n", + peer->addr, vdev_id); + + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ar->data_lock); +} + +/************************/ +/* Interface management */ +/************************/ + +static inline int ath10k_vdev_setup_sync(struct ath10k *ar) +{ + int ret; + + ret = wait_for_completion_timeout(&ar->vdev_setup_done, + ATH10K_VDEV_SETUP_TIMEOUT_HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int ath10k_vdev_start(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct ieee80211_conf *conf = &ar->hw->conf; + struct ieee80211_channel *channel = conf->chandef.chan; + struct wmi_vdev_start_request_arg arg = {}; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + INIT_COMPLETION(ar->vdev_setup_done); + + arg.vdev_id = arvif->vdev_id; + arg.dtim_period = arvif->dtim_period; + arg.bcn_intval = arvif->beacon_interval; + + arg.channel.freq = channel->center_freq; + + arg.channel.band_center_freq1 = conf->chandef.center_freq1; + + arg.channel.mode = chan_to_phymode(&conf->chandef); + + arg.channel.min_power = channel->max_power * 3; + arg.channel.max_power = channel->max_power * 4; + arg.channel.max_reg_power = channel->max_reg_power * 4; + arg.channel.max_antenna_gain = channel->max_antenna_gain; + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = arvif->u.ap.ssid; + arg.ssid_len = arvif->u.ap.ssid_len; + arg.hidden_ssid = arvif->u.ap.hidden_ssid; + } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { + arg.ssid = arvif->vif->bss_conf.ssid; + arg.ssid_len = arvif->vif->bss_conf.ssid_len; + } + + ret = ath10k_wmi_vdev_start(ar, &arg); + if (ret) { + ath10k_warn("WMI vdev start failed: ret %d\n", ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("vdev setup failed %d\n", ret); + return ret; + } + + return ret; +} + +static int ath10k_vdev_stop(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + INIT_COMPLETION(ar->vdev_setup_done); + + ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); + if (ret) { + ath10k_warn("WMI vdev stop failed: ret %d\n", ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("vdev setup failed %d\n", ret); + return ret; + } + + return ret; +} + +static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) +{ + struct ieee80211_channel *channel = ar->hw->conf.chandef.chan; + struct wmi_vdev_start_request_arg arg = {}; + enum nl80211_channel_type type; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + type = cfg80211_get_chandef_type(&ar->hw->conf.chandef); + + arg.vdev_id = vdev_id; + arg.channel.freq = channel->center_freq; + arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1; + + /* TODO setup this dynamically, what in case we + don't have any vifs? */ + arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef); + + arg.channel.min_power = channel->max_power * 3; + arg.channel.max_power = channel->max_power * 4; + arg.channel.max_reg_power = channel->max_reg_power * 4; + arg.channel.max_antenna_gain = channel->max_antenna_gain; + + ret = ath10k_wmi_vdev_start(ar, &arg); + if (ret) { + ath10k_warn("Monitor vdev start failed: ret %d\n", ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("Monitor vdev setup failed %d\n", ret); + return ret; + } + + ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); + if (ret) { + ath10k_warn("Monitor vdev up failed: %d\n", ret); + goto vdev_stop; + } + + ar->monitor_vdev_id = vdev_id; + ar->monitor_enabled = true; + + return 0; + +vdev_stop: + ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Monitor vdev stop failed: %d\n", ret); + + return ret; +} + +static int ath10k_monitor_stop(struct ath10k *ar) +{ + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + /* For some reasons, ath10k_wmi_vdev_down() here couse + * often ath10k_wmi_vdev_stop() to fail. Next we could + * not run monitor vdev and driver reload + * required. Don't see such problems we skip + * ath10k_wmi_vdev_down() here. + */ + + ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Monitor vdev stop failed: %d\n", ret); + + ret = ath10k_vdev_setup_sync(ar); + if (ret) + ath10k_warn("Monitor_down sync failed: %d\n", ret); + + ar->monitor_enabled = false; + return ret; +} + +static int ath10k_monitor_create(struct ath10k *ar) +{ + int bit, ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->monitor_present) { + ath10k_warn("Monitor mode already enabled\n"); + return 0; + } + + bit = ffs(ar->free_vdev_map); + if (bit == 0) { + ath10k_warn("No free VDEV slots\n"); + return -ENOMEM; + } + + ar->monitor_vdev_id = bit - 1; + ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id); + + ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id, + WMI_VDEV_TYPE_MONITOR, + 0, ar->mac_addr); + if (ret) { + ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret); + goto vdev_fail; + } + + ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n", + ar->monitor_vdev_id); + + ar->monitor_present = true; + return 0; + +vdev_fail: + /* + * Restore the ID to the global map. + */ + ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); + return ret; +} + +static int ath10k_monitor_destroy(struct ath10k *ar) +{ + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (!ar->monitor_present) + return 0; + + ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id); + if (ret) { + ath10k_warn("WMI vdev monitor delete failed: %d\n", ret); + return ret; + } + + ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); + ar->monitor_present = false; + + ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n", + ar->monitor_vdev_id); + return ret; +} + +static void ath10k_control_beaconing(struct ath10k_vif *arvif, + struct ieee80211_bss_conf *info) +{ + int ret = 0; + + if (!info->enable_beacon) { + ath10k_vdev_stop(arvif); + return; + } + + arvif->tx_seq_no = 0x1000; + + ret = ath10k_vdev_start(arvif); + if (ret) + return; + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid); + if (ret) { + ath10k_warn("Failed to bring up VDEV: %d\n", + arvif->vdev_id); + return; + } + ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id); +} + +static void ath10k_control_ibss(struct ath10k_vif *arvif, + struct ieee80211_bss_conf *info, + const u8 self_peer[ETH_ALEN]) +{ + int ret = 0; + + if (!info->ibss_joined) { + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); + if (ret) + ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n", + self_peer, arvif->vdev_id, ret); + + if (is_zero_ether_addr(arvif->u.ibss.bssid)) + return; + + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, + arvif->u.ibss.bssid); + if (ret) { + ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n", + arvif->u.ibss.bssid, arvif->vdev_id, ret); + return; + } + + memset(arvif->u.ibss.bssid, 0, ETH_ALEN); + + return; + } + + ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer); + if (ret) { + ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n", + self_peer, arvif->vdev_id, ret); + return; + } + + ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, + WMI_VDEV_PARAM_ATIM_WINDOW, + ATH10K_DEFAULT_ATIM); + if (ret) + ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n", + arvif->vdev_id, ret); +} + +/* + * Review this when mac80211 gains per-interface powersave support. + */ +static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath10k_generic_iter *ar_iter = data; + struct ieee80211_conf *conf = &ar_iter->ar->hw->conf; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + enum wmi_sta_powersave_param param; + enum wmi_sta_ps_mode psmode; + int ret; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (conf->flags & IEEE80211_CONF_PS) { + psmode = WMI_STA_PS_MODE_ENABLED; + param = WMI_STA_PS_PARAM_INACTIVITY_TIME; + + ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar, + arvif->vdev_id, + param, + conf->dynamic_ps_timeout); + if (ret) { + ath10k_warn("Failed to set inactivity time for VDEV: %d\n", + arvif->vdev_id); + return; + } + + ar_iter->ret = ret; + } else { + psmode = WMI_STA_PS_MODE_DISABLED; + } + + ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id, + psmode); + if (ar_iter->ret) + ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n", + psmode, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n", + psmode, arvif->vdev_id); +} + +/**********************/ +/* Station management */ +/**********************/ + +static void ath10k_peer_assoc_h_basic(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + memcpy(arg->addr, sta->addr, ETH_ALEN); + arg->vdev_id = arvif->vdev_id; + arg->peer_aid = sta->aid; + arg->peer_flags |= WMI_PEER_AUTH; + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA) + /* + * Seems FW have problems with Power Save in STA + * mode when we setup this parameter to high (eg. 5). + * Often we see that FW don't send NULL (with clean P flags) + * frame even there is info about buffered frames in beacons. + * Sometimes we have to wait more than 10 seconds before FW + * will wakeup. Often sending one ping from AP to our device + * just fail (more than 50%). + * + * Seems setting this FW parameter to 1 couse FW + * will check every beacon and will wakup immediately + * after detection buffered data. + */ + arg->peer_listen_intval = 1; + else + arg->peer_listen_intval = ar->hw->conf.listen_interval; + + arg->peer_num_spatial_streams = 1; + + /* + * The assoc capabilities are available only in managed mode. + */ + if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf) + arg->peer_caps = bss_conf->assoc_capability; +} + +static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, + struct ath10k_vif *arvif, + struct wmi_peer_assoc_complete_arg *arg) +{ + struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_bss_conf *info = &vif->bss_conf; + struct cfg80211_bss *bss; + const u8 *rsnie = NULL; + const u8 *wpaie = NULL; + + bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, + info->bssid, NULL, 0, 0, 0); + if (bss) { + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN); + + ies = rcu_dereference(bss->ies); + + wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + ies->data, + ies->len); + rcu_read_unlock(); + cfg80211_put_bss(ar->hw->wiphy, bss); + } + + /* FIXME: base on RSN IE/WPA IE is a correct idea? */ + if (rsnie || wpaie) { + ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__); + arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; + } + + if (wpaie) { + ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__); + arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; + } +} + +static void ath10k_peer_assoc_h_rates(struct ath10k *ar, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates; + const struct ieee80211_supported_band *sband; + const struct ieee80211_rate *rates; + u32 ratemask; + int i; + + sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band]; + ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band]; + rates = sband->bitrates; + + rateset->num_rates = 0; + + for (i = 0; i < 32; i++, ratemask >>= 1, rates++) { + if (!(ratemask & 1)) + continue; + + rateset->rates[rateset->num_rates] = rates->hw_value; + rateset->num_rates++; + } +} + +static void ath10k_peer_assoc_h_ht(struct ath10k *ar, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int smps; + int i, n; + + if (!ht_cap->ht_supported) + return; + + arg->peer_flags |= WMI_PEER_HT; + arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ht_cap->ampdu_factor)) - 1; + + arg->peer_mpdu_density = + ath10k_parse_mpdudensity(ht_cap->ampdu_density); + + arg->peer_ht_caps = ht_cap->cap; + arg->peer_rate_caps |= WMI_RC_HT_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) + arg->peer_flags |= WMI_PEER_LDPC; + + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { + arg->peer_flags |= WMI_PEER_40MHZ; + arg->peer_rate_caps |= WMI_RC_CW40_FLAG; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) + arg->peer_rate_caps |= WMI_RC_SGI_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) + arg->peer_rate_caps |= WMI_RC_SGI_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) { + arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG; + arg->peer_flags |= WMI_PEER_STBC; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) { + u32 stbc; + stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC; + stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc = stbc << WMI_RC_RX_STBC_FLAG_S; + arg->peer_rate_caps |= stbc; + arg->peer_flags |= WMI_PEER_STBC; + } + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (smps == WLAN_HT_CAP_SM_PS_STATIC) { + arg->peer_flags |= WMI_PEER_SPATIAL_MUX; + arg->peer_flags |= WMI_PEER_STATIC_MIMOPS; + } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) { + arg->peer_flags |= WMI_PEER_SPATIAL_MUX; + arg->peer_flags |= WMI_PEER_DYN_MIMOPS; + } + + if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) + arg->peer_rate_caps |= WMI_RC_TS_FLAG; + else if (ht_cap->mcs.rx_mask[1]) + arg->peer_rate_caps |= WMI_RC_DS_FLAG; + + for (i = 0, n = 0; i < IEEE80211_HT_MCS_MASK_LEN*8; i++) + if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8)) + arg->peer_ht_rates.rates[n++] = i; + + arg->peer_ht_rates.num_rates = n; + arg->peer_num_spatial_streams = max((n+7) / 8, 1); + + ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n", + arg->peer_ht_rates.num_rates, + arg->peer_num_spatial_streams); +} + +static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + u32 uapsd = 0; + u32 max_sp = 0; + + if (sta->wme) + arg->peer_flags |= WMI_PEER_QOS; + + if (sta->wme && sta->uapsd_queues) { + ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n", + sta->uapsd_queues, sta->max_sp); + + arg->peer_flags |= WMI_PEER_APSD; + arg->peer_flags |= WMI_RC_UAPSD_FLAG; + + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN; + + + if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP) + max_sp = sta->max_sp; + + ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_UAPSD, + uapsd); + + ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_MAX_SP, + max_sp); + + /* TODO setup this based on STA listen interval and + beacon interval. Currently we don't know + sta->listen_interval - mac80211 patch required. + Currently use 10 seconds */ + ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, + 10); + } +} + +static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + if (bss_conf->qos) + arg->peer_flags |= WMI_PEER_QOS; +} + +static void ath10k_peer_assoc_h_vht(struct ath10k *ar, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + + if (!vht_cap->vht_supported) + return; + + arg->peer_flags |= WMI_PEER_VHT; + + arg->peer_vht_caps = vht_cap->cap; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + arg->peer_flags |= WMI_PEER_80MHZ; + + arg->peer_vht_rates.rx_max_rate = + __le16_to_cpu(vht_cap->vht_mcs.rx_highest); + arg->peer_vht_rates.rx_mcs_set = + __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + arg->peer_vht_rates.tx_max_rate = + __le16_to_cpu(vht_cap->vht_mcs.tx_highest); + arg->peer_vht_rates.tx_mcs_set = + __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); + + ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n"); +} + +static void ath10k_peer_assoc_h_qos(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_AP: + ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg); + break; + case WMI_VDEV_TYPE_STA: + ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg); + break; + default: + break; + } +} + +static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + /* FIXME: add VHT */ + + switch (ar->hw->conf.chandef.chan->band) { + case IEEE80211_BAND_2GHZ: + if (sta->ht_cap.ht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11NG_HT40; + else + phymode = MODE_11NG_HT20; + } else { + phymode = MODE_11G; + } + + break; + case IEEE80211_BAND_5GHZ: + if (sta->ht_cap.ht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11NA_HT40; + else + phymode = MODE_11NA_HT20; + } else { + phymode = MODE_11A; + } + + break; + default: + break; + } + + arg->peer_phymode = phymode; + WARN_ON(phymode == MODE_UNKNOWN); +} + +static int ath10k_peer_assoc(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf) +{ + struct wmi_peer_assoc_complete_arg arg; + + memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); + + ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); + ath10k_peer_assoc_h_crypto(ar, arvif, &arg); + ath10k_peer_assoc_h_rates(ar, sta, &arg); + ath10k_peer_assoc_h_ht(ar, sta, &arg); + ath10k_peer_assoc_h_vht(ar, sta, &arg); + ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg); + ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg); + + return ath10k_wmi_peer_assoc(ar, &arg); +} + +/* can be called only in mac80211 callbacks due to `key_count` usage */ +static void ath10k_bss_assoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta *ap_sta; + int ret; + + rcu_read_lock(); + + ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!ap_sta) { + ath10k_warn("Failed to find station entry for %pM\n", + bss_conf->bssid); + rcu_read_unlock(); + return; + } + + ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf); + if (ret) { + ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid); + rcu_read_unlock(); + return; + } + + rcu_read_unlock(); + + ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid, + bss_conf->bssid); + if (ret) + ath10k_warn("VDEV: %d up failed: ret %d\n", + arvif->vdev_id, ret); + else + ath10k_dbg(ATH10K_DBG_MAC, + "VDEV: %d associated, BSSID: %pM, AID: %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); +} + +/* + * FIXME: flush TIDs + */ +static void ath10k_bss_disassoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret; + + /* + * For some reason, calling VDEV-DOWN before VDEV-STOP + * makes the FW to send frames via HTT after disassociation. + * No idea why this happens, even though VDEV-DOWN is supposed + * to be analogous to link down, so just stop the VDEV. + */ + ret = ath10k_vdev_stop(arvif); + if (!ret) + ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n", + arvif->vdev_id); + + /* + * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and + * report beacons from previously associated network through HTT. + * This in turn would spam mac80211 WARN_ON if we bring down all + * interfaces as it expects there is no rx when no interface is + * running. + */ + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n", + arvif->vdev_id, ret); + + ath10k_wmi_flush_tx(ar); + + arvif->def_wep_key_index = 0; +} + +static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, + struct ieee80211_sta *sta) +{ + int ret = 0; + + ret = ath10k_peer_assoc(ar, arvif, sta, NULL); + if (ret) { + ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); + return ret; + } + + ret = ath10k_install_peer_wep_keys(arvif, sta->addr); + if (ret) { + ath10k_warn("could not install peer wep keys (%d)\n", ret); + return ret; + } + + return ret; +} + +static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, + struct ieee80211_sta *sta) +{ + int ret = 0; + + ret = ath10k_clear_peer_keys(arvif, sta->addr); + if (ret) { + ath10k_warn("could not clear all peer wep keys (%d)\n", ret); + return ret; + } + + return ret; +} + +/**************/ +/* Regulatory */ +/**************/ + +static int ath10k_update_channel_list(struct ath10k *ar) +{ + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_supported_band **bands; + enum ieee80211_band band; + struct ieee80211_channel *channel; + struct wmi_scan_chan_list_arg arg = {0}; + struct wmi_channel_arg *ch; + bool passive; + int len; + int ret; + int i; + + bands = hw->wiphy->bands; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + if (bands[band]->channels[i].flags & + IEEE80211_CHAN_DISABLED) + continue; + + arg.n_channels++; + } + } + + len = sizeof(struct wmi_channel_arg) * arg.n_channels; + arg.channels = kzalloc(len, GFP_KERNEL); + if (!arg.channels) + return -ENOMEM; + + ch = arg.channels; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + channel = &bands[band]->channels[i]; + + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + + ch->allow_ht = true; + + /* FIXME: when should we really allow VHT? */ + ch->allow_vht = true; + + ch->allow_ibss = + !(channel->flags & IEEE80211_CHAN_NO_IBSS); + + ch->ht40plus = + !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS); + + passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN; + ch->passive = passive; + + ch->freq = channel->center_freq; + ch->min_power = channel->max_power * 3; + ch->max_power = channel->max_power * 4; + ch->max_reg_power = channel->max_reg_power * 4; + ch->max_antenna_gain = channel->max_antenna_gain; + ch->reg_class_id = 0; /* FIXME */ + + /* FIXME: why use only legacy modes, why not any + * HT/VHT modes? Would that even make any + * difference? */ + if (channel->band == IEEE80211_BAND_2GHZ) + ch->mode = MODE_11G; + else + ch->mode = MODE_11A; + + if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN)) + continue; + + ath10k_dbg(ATH10K_DBG_WMI, + "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", + __func__, ch - arg.channels, arg.n_channels, + ch->freq, ch->max_power, ch->max_reg_power, + ch->max_antenna_gain, ch->mode); + + ch++; + } + } + + ret = ath10k_wmi_scan_chan_list(ar, &arg); + kfree(arg.channels); + + return ret; +} + +static void ath10k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct reg_dmn_pair_mapping *regpair; + struct ath10k *ar = hw->priv; + int ret; + + ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + + ret = ath10k_update_channel_list(ar); + if (ret) + ath10k_warn("could not update channel list (%d)\n", ret); + + regpair = ar->ath_common.regulatory.regpair; + /* Target allows setting up per-band regdomain but ath_common provides + * a combined one only */ + ret = ath10k_wmi_pdev_set_regdomain(ar, + regpair->regDmnEnum, + regpair->regDmnEnum, /* 2ghz */ + regpair->regDmnEnum, /* 5ghz */ + regpair->reg_2ghz_ctl, + regpair->reg_5ghz_ctl); + if (ret) + ath10k_warn("could not set pdev regdomain (%d)\n", ret); +} + +/***************/ +/* TX handlers */ +/***************/ + +/* + * Frames sent to the FW have to be in "Native Wifi" format. + * Strip the QoS field from the 802.11 header. + */ +static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + u8 *qos_ctl; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + qos_ctl = ieee80211_get_qos_ctl(hdr); + memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN, + skb->len - ieee80211_hdrlen(hdr->frame_control)); + skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN); +} + +static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_key_conf *key = info->control.hw_key; + int ret; + + /* TODO AP mode should be implemented */ + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (!ieee80211_has_protected(hdr->frame_control)) + return; + + if (!key) + return; + + if (key->cipher != WLAN_CIPHER_SUITE_WEP40 && + key->cipher != WLAN_CIPHER_SUITE_WEP104) + return; + + if (key->keyidx == arvif->def_wep_key_index) + return; + + ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx); + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_DEF_KEYID, + key->keyidx); + if (ret) { + ath10k_warn("could not update wep keyidx (%d)\n", ret); + return; + } + + arvif->def_wep_key_index = key->keyidx; +} + +static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + /* This is case only for P2P_GO */ + if (arvif->vdev_type != WMI_VDEV_TYPE_AP || + arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + return; + + if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) { + spin_lock_bh(&ar->data_lock); + if (arvif->u.ap.noa_data) + if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len, + GFP_ATOMIC)) + memcpy(skb_put(skb, arvif->u.ap.noa_len), + arvif->u.ap.noa_data, + arvif->u.ap.noa_len); + spin_unlock_bh(&ar->data_lock); + } +} + +static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int ret; + + if (ieee80211_is_mgmt(hdr->frame_control)) + ret = ath10k_htt_mgmt_tx(ar->htt, skb); + else if (ieee80211_is_nullfunc(hdr->frame_control)) + /* FW does not report tx status properly for NullFunc frames + * unless they are sent through mgmt tx path. mac80211 sends + * those frames when it detects link/beacon loss and depends on + * the tx status to be correct. */ + ret = ath10k_htt_mgmt_tx(ar->htt, skb); + else + ret = ath10k_htt_tx(ar->htt, skb); + + if (ret) { + ath10k_warn("tx failed (%d). dropping packet.\n", ret); + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_offchan_tx_purge(struct ath10k *ar) +{ + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->offchan_tx_queue); + if (!skb) + break; + + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_offchan_tx_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work); + struct ath10k_peer *peer; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + const u8 *peer_addr; + int vdev_id; + int ret; + + /* FW requirement: We must create a peer before FW will send out + * an offchannel frame. Otherwise the frame will be stuck and + * never transmitted. We delete the peer upon tx completion. + * It is unlikely that a peer for offchannel tx will already be + * present. However it may be in some rare cases so account for that. + * Otherwise we might remove a legitimate peer and break stuff. */ + + for (;;) { + skb = skb_dequeue(&ar->offchan_tx_queue); + if (!skb) + break; + + mutex_lock(&ar->conf_mutex); + + ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n", + skb); + + hdr = (struct ieee80211_hdr *)skb->data; + peer_addr = ieee80211_get_DA(hdr); + vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id; + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, vdev_id, peer_addr); + spin_unlock_bh(&ar->data_lock); + + if (peer) + ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n", + peer_addr, vdev_id); + + if (!peer) { + ret = ath10k_peer_create(ar, vdev_id, peer_addr); + if (ret) + ath10k_warn("peer %pM on vdev %d not created (%d)\n", + peer_addr, vdev_id, ret); + } + + spin_lock_bh(&ar->data_lock); + INIT_COMPLETION(ar->offchan_tx_completed); + ar->offchan_tx_skb = skb; + spin_unlock_bh(&ar->data_lock); + + ath10k_tx_htt(ar, skb); + + ret = wait_for_completion_timeout(&ar->offchan_tx_completed, + 3 * HZ); + if (ret <= 0) + ath10k_warn("timed out waiting for offchannel skb %p\n", + skb); + + if (!peer) { + ret = ath10k_peer_delete(ar, vdev_id, peer_addr); + if (ret) + ath10k_warn("peer %pM on vdev %d not deleted (%d)\n", + peer_addr, vdev_id, ret); + } + + mutex_unlock(&ar->conf_mutex); + } +} + +/************/ +/* Scanning */ +/************/ + +/* + * This gets called if we dont get a heart-beat during scan. + * This may indicate the FW has hung and we need to abort the + * scan manually to prevent cancel_hw_scan() from deadlocking + */ +void ath10k_reset_scan(unsigned long ptr) +{ + struct ath10k *ar = (struct ath10k *)ptr; + + spin_lock_bh(&ar->data_lock); + if (!ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + return; + } + + ath10k_warn("scan timeout. resetting. fw issue?\n"); + + if (ar->scan.is_roc) + ieee80211_remain_on_channel_expired(ar->hw); + else + ieee80211_scan_completed(ar->hw, 1 /* aborted */); + + ar->scan.in_progress = false; + complete_all(&ar->scan.completed); + spin_unlock_bh(&ar->data_lock); +} + +static int ath10k_abort_scan(struct ath10k *ar) +{ + struct wmi_stop_scan_arg arg = { + .req_id = 1, /* FIXME */ + .req_type = WMI_SCAN_STOP_ONE, + .u.scan_id = ATH10K_SCAN_ID, + }; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + del_timer_sync(&ar->scan.timeout); + + spin_lock_bh(&ar->data_lock); + if (!ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + return 0; + } + + ar->scan.aborting = true; + spin_unlock_bh(&ar->data_lock); + + ret = ath10k_wmi_stop_scan(ar, &arg); + if (ret) { + ath10k_warn("could not submit wmi stop scan (%d)\n", ret); + return -EIO; + } + + ath10k_wmi_flush_tx(ar); + + ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ); + if (ret == 0) + ath10k_warn("timed out while waiting for scan to stop\n"); + + /* scan completion may be done right after we timeout here, so let's + * check the in_progress and tell mac80211 scan is completed. if we + * don't do that and FW fails to send us scan completion indication + * then userspace won't be able to scan anymore */ + ret = 0; + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + ath10k_warn("could not stop scan. its still in progress\n"); + ar->scan.in_progress = false; + ath10k_offchan_tx_purge(ar); + ret = -ETIMEDOUT; + } + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static int ath10k_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_start_scan(ar, arg); + if (ret) + return ret; + + /* make sure we submit the command so the completion + * timeout makes sense */ + ath10k_wmi_flush_tx(ar); + + ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ); + if (ret == 0) { + ath10k_abort_scan(ar); + return ret; + } + + /* the scan can complete earlier, before we even + * start the timer. in that case the timer handler + * checks ar->scan.in_progress and bails out if its + * false. Add a 200ms margin to account event/command + * processing. */ + mod_timer(&ar->scan.timeout, jiffies + + msecs_to_jiffies(arg->max_scan_time+200)); + return 0; +} + +/**********************/ +/* mac80211 callbacks */ +/**********************/ + +static void ath10k_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = NULL; + u32 vdev_id = 0; + u8 tid; + + if (info->control.vif) { + arvif = ath10k_vif_to_arvif(info->control.vif); + vdev_id = arvif->vdev_id; + } else if (ar->monitor_enabled) { + vdev_id = ar->monitor_vdev_id; + } + + /* We should disable CCK RATE due to P2P */ + if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) + ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); + + /* we must calculate tid before we apply qos workaround + * as we'd lose the qos control field */ + tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + if (ieee80211_is_data_qos(hdr->frame_control) && + is_unicast_ether_addr(ieee80211_get_DA(hdr))) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + } + + ath10k_tx_h_qos_workaround(hw, control, skb); + ath10k_tx_h_update_wep_key(skb); + ath10k_tx_h_add_p2p_noa_ie(ar, skb); + ath10k_tx_h_seq_no(skb); + + memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); + ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; + ATH10K_SKB_CB(skb)->htt.tid = tid; + + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { + spin_lock_bh(&ar->data_lock); + ATH10K_SKB_CB(skb)->htt.is_offchan = true; + ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id; + spin_unlock_bh(&ar->data_lock); + + ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); + + skb_queue_tail(&ar->offchan_tx_queue, skb); + ieee80211_queue_work(hw, &ar->offchan_tx_work); + return; + } + + ath10k_tx_htt(ar, skb); +} + +/* + * Initialize various parameters with default vaules. + */ +static int ath10k_start(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + int ret; + + ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); + if (ret) + ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", + ret); + + ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0); + if (ret) + ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", + ret); + + return 0; +} + +static void ath10k_stop(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + + /* avoid leaks in case FW never confirms scan for offchannel */ + cancel_work_sync(&ar->offchan_tx_work); + ath10k_offchan_tx_purge(ar); +} + +static int ath10k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath10k_generic_iter ar_iter; + struct ath10k *ar = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + int ret = 0; + u32 flags; + + mutex_lock(&ar->conf_mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n", + conf->chandef.chan->center_freq); + spin_lock_bh(&ar->data_lock); + ar->rx_channel = conf->chandef.chan; + spin_unlock_bh(&ar->data_lock); + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + flags = IEEE80211_IFACE_ITER_RESUME_ALL; + + ieee80211_iterate_active_interfaces_atomic(hw, + flags, + ath10k_ps_iter, + &ar_iter); + + ret = ar_iter.ret; + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (conf->flags & IEEE80211_CONF_MONITOR) + ret = ath10k_monitor_create(ar); + else + ret = ath10k_monitor_destroy(ar); + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +/* + * TODO: + * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE, + * because we will send mgmt frames without CCK. This requirement + * for P2P_FIND/GO_NEG should be handled by checking CCK flag + * in the TX packet. + */ +static int ath10k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + enum wmi_sta_powersave_param param; + int ret = 0; + u32 value; + int bit; + + mutex_lock(&ar->conf_mutex); + + arvif->ar = ar; + arvif->vif = vif; + + if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) { + ath10k_warn("Only one monitor interface allowed\n"); + ret = -EBUSY; + goto exit; + } + + bit = ffs(ar->free_vdev_map); + if (bit == 0) { + ret = -EBUSY; + goto exit; + } + + arvif->vdev_id = bit - 1; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + ar->free_vdev_map &= ~(1 << arvif->vdev_id); + + if (ar->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + + switch (vif->type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT; + break; + case NL80211_IFTYPE_ADHOC: + arvif->vdev_type = WMI_VDEV_TYPE_IBSS; + break; + case NL80211_IFTYPE_AP: + arvif->vdev_type = WMI_VDEV_TYPE_AP; + + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; + break; + default: + WARN_ON(1); + break; + } + + ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n", + arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype); + + ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type, + arvif->vdev_subtype, vif->addr); + if (ret) { + ath10k_warn("WMI vdev create failed: ret %d\n", ret); + goto exit; + } + + ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID, + arvif->def_wep_key_index); + if (ret) + ath10k_warn("Failed to set default keyid: %d\n", ret); + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_TX_ENCAP_TYPE, + ATH10K_HW_TXRX_NATIVE_WIFI); + if (ret) + ath10k_warn("Failed to set TX encap: %d\n", ret); + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr); + if (ret) { + ath10k_warn("Failed to create peer for AP: %d\n", ret); + goto exit; + } + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { + param = WMI_STA_PS_PARAM_RX_WAKE_POLICY; + value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) + ath10k_warn("Failed to set RX wake policy: %d\n", ret); + + param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) + ath10k_warn("Failed to set TX wake thresh: %d\n", ret); + + param = WMI_STA_PS_PARAM_PSPOLL_COUNT; + value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) + ath10k_warn("Failed to set PSPOLL count: %d\n", ret); + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + ar->monitor_present = true; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath10k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret; + + mutex_lock(&ar->conf_mutex); + + ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id); + + ar->free_vdev_map |= 1 << (arvif->vdev_id); + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); + if (ret) + ath10k_warn("Failed to remove peer for AP: %d\n", ret); + + kfree(arvif->u.ap.noa_data); + } + + ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) + ath10k_warn("WMI vdev delete failed: %d\n", ret); + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + ar->monitor_present = false; + + ath10k_peer_cleanup(ar, arvif->vdev_id); + + mutex_unlock(&ar->conf_mutex); +} + +/* + * FIXME: Has to be verified. + */ +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_PSPOLL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ + FIF_FCSFAIL) + +static void ath10k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + ar->filter_flags = *total_flags; + + if ((ar->filter_flags & FIF_PROMISC_IN_BSS) && + !ar->monitor_enabled) { + ret = ath10k_monitor_start(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Unable to start monitor mode\n"); + else + ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n"); + } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && + ar->monitor_enabled) { + ret = ath10k_monitor_stop(ar); + if (ret) + ath10k_warn("Unable to stop monitor mode\n"); + else + ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n"); + } + + mutex_unlock(&ar->conf_mutex); +} + +static void ath10k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (changed & BSS_CHANGED_IBSS) + ath10k_control_ibss(arvif, info, vif->addr); + + if (changed & BSS_CHANGED_BEACON_INT) { + arvif->beacon_interval = info->beacon_int; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_BEACON_INTERVAL, + arvif->beacon_interval); + if (ret) + ath10k_warn("Failed to set beacon interval for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Beacon interval: %d set for VDEV: %d\n", + arvif->beacon_interval, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_BEACON) { + ret = ath10k_wmi_pdev_set_param(ar, + WMI_PDEV_PARAM_BEACON_TX_MODE, + WMI_BEACON_STAGGERED_MODE); + if (ret) + ath10k_warn("Failed to set beacon mode for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set staggered beacon mode for VDEV: %d\n", + arvif->vdev_id); + } + + if (changed & BSS_CHANGED_BEACON_INFO) { + arvif->dtim_period = info->dtim_period; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_DTIM_PERIOD, + arvif->dtim_period); + if (ret) + ath10k_warn("Failed to set dtim period for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set dtim period: %d for VDEV: %d\n", + arvif->dtim_period, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_SSID && + vif->type == NL80211_IFTYPE_AP) { + arvif->u.ap.ssid_len = info->ssid_len; + if (info->ssid_len) + memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len); + arvif->u.ap.hidden_ssid = info->hidden_ssid; + } + + if (changed & BSS_CHANGED_BSSID) { + if (!is_zero_ether_addr(info->bssid)) { + ret = ath10k_peer_create(ar, arvif->vdev_id, + info->bssid); + if (ret) + ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", + info->bssid, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Added peer: %pM for VDEV: %d\n", + info->bssid, arvif->vdev_id); + + + if (vif->type == NL80211_IFTYPE_STATION) { + /* + * this is never erased as we it for crypto key + * clearing; this is FW requirement + */ + memcpy(arvif->u.sta.bssid, info->bssid, + ETH_ALEN); + + ret = ath10k_vdev_start(arvif); + if (!ret) + ath10k_dbg(ATH10K_DBG_MAC, + "VDEV: %d started with BSSID: %pM\n", + arvif->vdev_id, info->bssid); + } + + /* + * Mac80211 does not keep IBSS bssid when leaving IBSS, + * so driver need to store it. It is needed when leaving + * IBSS in order to remove BSSID peer. + */ + if (vif->type == NL80211_IFTYPE_ADHOC) + memcpy(arvif->u.ibss.bssid, info->bssid, + ETH_ALEN); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + ath10k_control_beaconing(arvif, info); + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + u32 cts_prot; + if (info->use_cts_prot) + cts_prot = 1; + else + cts_prot = 0; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + cts_prot); + if (ret) + ath10k_warn("Failed to set CTS prot for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set CTS prot: %d for VDEV: %d\n", + cts_prot, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + u32 slottime; + if (info->use_short_slot) + slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */ + + else + slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_SLOT_TIME, + slottime); + if (ret) + ath10k_warn("Failed to set erp slot for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set slottime: %d for VDEV: %d\n", + slottime, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + u32 preamble; + if (info->use_short_preamble) + preamble = WMI_VDEV_PREAMBLE_SHORT; + else + preamble = WMI_VDEV_PREAMBLE_LONG; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_PREAMBLE, + preamble); + if (ret) + ath10k_warn("Failed to set preamble for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set preamble: %d for VDEV: %d\n", + preamble, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ASSOC) { + if (info->assoc) + ath10k_bss_assoc(hw, vif, info); + } + + mutex_unlock(&ar->conf_mutex); +} + +static int ath10k_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_start_scan_arg arg; + int ret = 0; + int i; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + ret = -EBUSY; + goto exit; + } + + INIT_COMPLETION(ar->scan.started); + INIT_COMPLETION(ar->scan.completed); + ar->scan.in_progress = true; + ar->scan.aborting = false; + ar->scan.is_roc = false; + ar->scan.vdev_id = arvif->vdev_id; + spin_unlock_bh(&ar->data_lock); + + memset(&arg, 0, sizeof(arg)); + ath10k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; + arg.scan_id = ATH10K_SCAN_ID; + + if (!req->no_cck) + arg.scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES; + + if (req->ie_len) { + arg.ie_len = req->ie_len; + memcpy(arg.ie, req->ie, arg.ie_len); + } + + if (req->n_ssids) { + arg.n_ssids = req->n_ssids; + for (i = 0; i < arg.n_ssids; i++) { + arg.ssids[i].len = req->ssids[i].ssid_len; + arg.ssids[i].ssid = req->ssids[i].ssid; + } + } + + if (req->n_channels) { + arg.n_channels = req->n_channels; + for (i = 0; i < arg.n_channels; i++) + arg.channels[i] = req->channels[i]->center_freq; + } + + ret = ath10k_start_scan(ar, &arg); + if (ret) { + ath10k_warn("could not start hw scan (%d)\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.in_progress = false; + spin_unlock_bh(&ar->data_lock); + } + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = ath10k_abort_scan(ar); + if (ret) { + ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n", + ret); + ieee80211_scan_completed(hw, 1 /* aborted */); + } + mutex_unlock(&ar->conf_mutex); +} + +static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k_peer *peer; + const u8 *peer_addr; + bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104; + int ret = 0; + + if (key->keyidx > WMI_MAX_KEY_INDEX) + return -ENOSPC; + + mutex_lock(&ar->conf_mutex); + + if (sta) + peer_addr = sta->addr; + else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) + peer_addr = vif->bss_conf.bssid; + else + peer_addr = vif->addr; + + key->hw_key_idx = key->keyidx; + + /* the peer should not disappear in mid-way (unless FW goes awry) since + * we already hold conf_mutex. we just make sure its there now. */ + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr); + spin_unlock_bh(&ar->data_lock); + + if (!peer) { + if (cmd == SET_KEY) { + ath10k_warn("cannot install key for non-existent peer %pM\n", + peer_addr); + ret = -EOPNOTSUPP; + goto exit; + } else { + /* if the peer doesn't exist there is no key to disable + * anymore */ + goto exit; + } + } + + if (is_wep) { + if (cmd == SET_KEY) + arvif->wep_keys[key->keyidx] = key; + else + arvif->wep_keys[key->keyidx] = NULL; + + if (cmd == DISABLE_KEY) + ath10k_clear_vdev_key(arvif, key); + } + + ret = ath10k_install_key(arvif, key, cmd, peer_addr); + if (ret) { + ath10k_warn("ath10k_install_key failed (%d)\n", ret); + goto exit; + } + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr); + if (peer && cmd == SET_KEY) + peer->keys[key->keyidx] = key; + else if (peer && cmd == DISABLE_KEY) + peer->keys[key->keyidx] = NULL; + else if (peer == NULL) + /* impossible unless FW goes crazy */ + ath10k_warn("peer %pM disappeared!\n", peer_addr); + spin_unlock_bh(&ar->data_lock); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE && + vif->type != NL80211_IFTYPE_STATION) { + /* + * New station addition. + */ + ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); + if (ret) + ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Added peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + /* + * Existing station deletion. + */ + ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + if (vif->type == NL80211_IFTYPE_STATION) + ath10k_bss_disassoc(hw, vif); + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) { + /* + * New association. + */ + ret = ath10k_station_assoc(ar, arvif, sta); + if (ret) + ath10k_warn("Failed to associate station: %pM\n", + sta->addr); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Station %pM moved to assoc state\n", + sta->addr); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) { + /* + * Disassociation. + */ + ret = ath10k_station_disassoc(ar, arvif, sta); + if (ret) + ath10k_warn("Failed to disassociate station: %pM\n", + sta->addr); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Station %pM moved to disassociated state\n", + sta->addr); + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, + u16 ac, bool enable) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 value = 0; + int ret = 0; + + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + switch (ac) { + case IEEE80211_AC_VO: + value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; + break; + case IEEE80211_AC_VI: + value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; + break; + case IEEE80211_AC_BE: + value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; + break; + case IEEE80211_AC_BK: + value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; + break; + } + + if (enable) + arvif->u.sta.uapsd |= value; + else + arvif->u.sta.uapsd &= ~value; + + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_UAPSD, + arvif->u.sta.uapsd); + if (ret) { + ath10k_warn("could not set uapsd params %d\n", ret); + goto exit; + } + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; + else + value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_RX_WAKE_POLICY, + value); + if (ret) + ath10k_warn("could not set rx wake param %d\n", ret); + +exit: + return ret; +} + +static int ath10k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath10k *ar = hw->priv; + struct wmi_wmm_params_arg *p = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + switch (ac) { + case IEEE80211_AC_VO: + p = &ar->wmm_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &ar->wmm_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &ar->wmm_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &ar->wmm_params.ac_bk; + break; + } + + if (WARN_ON(!p)) { + ret = -EINVAL; + goto exit; + } + + p->cwmin = params->cw_min; + p->cwmax = params->cw_max; + p->aifs = params->aifs; + + /* + * The channel time duration programmed in the HW is in absolute + * microseconds, while mac80211 gives the txop in units of + * 32 microseconds. + */ + p->txop = params->txop * 32; + + /* FIXME: FW accepts wmm params per hw, not per vif */ + ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params); + if (ret) { + ath10k_warn("could not set wmm params %d\n", ret); + goto exit; + } + + ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd); + if (ret) + ath10k_warn("could not set sta uapsd %d\n", ret); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +#define ATH10K_ROC_TIMEOUT_HZ (2*HZ) + +static int ath10k_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_start_scan_arg arg; + int ret; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + ret = -EBUSY; + goto exit; + } + + INIT_COMPLETION(ar->scan.started); + INIT_COMPLETION(ar->scan.completed); + INIT_COMPLETION(ar->scan.on_channel); + ar->scan.in_progress = true; + ar->scan.aborting = false; + ar->scan.is_roc = true; + ar->scan.vdev_id = arvif->vdev_id; + ar->scan.roc_freq = chan->center_freq; + spin_unlock_bh(&ar->data_lock); + + memset(&arg, 0, sizeof(arg)); + ath10k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; + arg.scan_id = ATH10K_SCAN_ID; + arg.n_channels = 1; + arg.channels[0] = chan->center_freq; + arg.dwell_time_active = duration; + arg.dwell_time_passive = duration; + arg.max_scan_time = 2 * duration; + arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; + + ret = ath10k_start_scan(ar, &arg); + if (ret) { + ath10k_warn("could not start roc scan (%d)\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.in_progress = false; + spin_unlock_bh(&ar->data_lock); + goto exit; + } + + ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ); + if (ret == 0) { + ath10k_warn("could not switch to channel for roc scan\n"); + ath10k_abort_scan(ar); + ret = -ETIMEDOUT; + goto exit; + } + + ret = 0; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + ath10k_abort_scan(ar); + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +/* + * Both RTS and Fragmentation threshold are interface-specific + * in ath10k, but device-specific in mac80211. + */ +static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath10k_generic_iter *ar_iter = data; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; + + rts = min_t(u32, rts, ATH10K_RTS_MAX); + + ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + rts); + if (ar_iter->ret) + ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set RTS threshold: %d for VDEV: %d\n", + rts, arvif->vdev_id); +} + +static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath10k_generic_iter ar_iter; + struct ath10k *ar = hw->priv; + + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + + mutex_lock(&ar->conf_mutex); + ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_set_rts_iter, &ar_iter); + mutex_unlock(&ar->conf_mutex); + + return ar_iter.ret; +} + +static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath10k_generic_iter *ar_iter = data; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; + int ret; + + frag = clamp_t(u32, frag, + ATH10K_FRAGMT_THRESHOLD_MIN, + ATH10K_FRAGMT_THRESHOLD_MAX); + + ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + frag); + + ar_iter->ret = ret; + if (ar_iter->ret) + ath10k_warn("Failed to set frag threshold for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set frag threshold: %d for VDEV: %d\n", + frag, arvif->vdev_id); +} + +static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath10k_generic_iter ar_iter; + struct ath10k *ar = hw->priv; + + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + + mutex_lock(&ar->conf_mutex); + ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_set_frag_iter, &ar_iter); + mutex_unlock(&ar->conf_mutex); + + return ar_iter.ret; +} + +static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +{ + struct ath10k *ar = hw->priv; + int ret; + + /* mac80211 doesn't care if we really xmit queued frames or not + * we'll collect those frames either way if we stop/delete vdevs */ + if (drop) + return; + + ret = wait_event_timeout(ar->htt->empty_tx_wq, ({ + bool empty; + spin_lock_bh(&ar->htt->tx_lock); + empty = bitmap_empty(ar->htt->used_msdu_ids, + ar->htt->max_num_pending_tx); + spin_unlock_bh(&ar->htt->tx_lock); + (empty); + }), ATH10K_FLUSH_TIMEOUT_HZ); + if (ret <= 0) + ath10k_warn("tx not flushed\n"); +} + +/* TODO: Implement this function properly + * For now it is needed to reply to Probe Requests in IBSS mode. + * Propably we need this information from FW. + */ +static int ath10k_tx_last_beacon(struct ieee80211_hw *hw) +{ + return 1; +} + +static const struct ieee80211_ops ath10k_ops = { + .tx = ath10k_tx, + .start = ath10k_start, + .stop = ath10k_stop, + .config = ath10k_config, + .add_interface = ath10k_add_interface, + .remove_interface = ath10k_remove_interface, + .configure_filter = ath10k_configure_filter, + .bss_info_changed = ath10k_bss_info_changed, + .hw_scan = ath10k_hw_scan, + .cancel_hw_scan = ath10k_cancel_hw_scan, + .set_key = ath10k_set_key, + .sta_state = ath10k_sta_state, + .conf_tx = ath10k_conf_tx, + .remain_on_channel = ath10k_remain_on_channel, + .cancel_remain_on_channel = ath10k_cancel_remain_on_channel, + .set_rts_threshold = ath10k_set_rts_threshold, + .set_frag_threshold = ath10k_set_frag_threshold, + .flush = ath10k_flush, + .tx_last_beacon = ath10k_tx_last_beacon, +}; + +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .flags = (_flags), \ + .hw_value = (_rateid), \ +} + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static const struct ieee80211_channel ath10k_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_channel ath10k_5ghz_channels[] = { + CHAN5G(36, 5180, 0), + CHAN5G(40, 5200, 0), + CHAN5G(44, 5220, 0), + CHAN5G(48, 5240, 0), + CHAN5G(52, 5260, 0), + CHAN5G(56, 5280, 0), + CHAN5G(60, 5300, 0), + CHAN5G(64, 5320, 0), + CHAN5G(100, 5500, 0), + CHAN5G(104, 5520, 0), + CHAN5G(108, 5540, 0), + CHAN5G(112, 5560, 0), + CHAN5G(116, 5580, 0), + CHAN5G(120, 5600, 0), + CHAN5G(124, 5620, 0), + CHAN5G(128, 5640, 0), + CHAN5G(132, 5660, 0), + CHAN5G(136, 5680, 0), + CHAN5G(140, 5700, 0), + CHAN5G(149, 5745, 0), + CHAN5G(153, 5765, 0), + CHAN5G(157, 5785, 0), + CHAN5G(161, 5805, 0), + CHAN5G(165, 5825, 0), +}; + +static struct ieee80211_rate ath10k_rates[] = { + /* CCK */ + RATETAB_ENT(10, 0x82, 0), + RATETAB_ENT(20, 0x84, 0), + RATETAB_ENT(55, 0x8b, 0), + RATETAB_ENT(110, 0x96, 0), + /* OFDM */ + RATETAB_ENT(60, 0x0c, 0), + RATETAB_ENT(90, 0x12, 0), + RATETAB_ENT(120, 0x18, 0), + RATETAB_ENT(180, 0x24, 0), + RATETAB_ENT(240, 0x30, 0), + RATETAB_ENT(360, 0x48, 0), + RATETAB_ENT(480, 0x60, 0), + RATETAB_ENT(540, 0x6c, 0), +}; + +#define ath10k_a_rates (ath10k_rates + 4) +#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - 4) +#define ath10k_g_rates (ath10k_rates + 0) +#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates)) + +struct ath10k *ath10k_mac_create(void) +{ + struct ieee80211_hw *hw; + struct ath10k *ar; + + hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops); + if (!hw) + return NULL; + + ar = hw->priv; + ar->hw = hw; + + return ar; +} + +void ath10k_mac_destroy(struct ath10k *ar) +{ + ieee80211_free_hw(ar->hw); +} + +static const struct ieee80211_iface_limit ath10k_if_limits[] = { + { + .max = 8, + .types = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO) + | BIT(NL80211_IFTYPE_AP) + } +}; + +static const struct ieee80211_iface_combination ath10k_if_comb = { + .limits = ath10k_if_limits, + .n_limits = ARRAY_SIZE(ath10k_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .beacon_int_infra_match = true, +}; + +static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) +{ + struct ieee80211_sta_vht_cap vht_cap = {0}; + u16 mcs_map; + + vht_cap.vht_supported = 1; + vht_cap.cap = ar->vht_cap_info; + + /* FIXME: check dynamically how many streams board supports */ + mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 14; + + vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); + vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); + + return vht_cap; +} + +static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) +{ + int i; + struct ieee80211_sta_ht_cap ht_cap = {0}; + + if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED)) + return ht_cap; + + ht_cap.ht_supported = 1; + ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + + if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + + if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) { + u32 smps; + + smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + + ht_cap.cap |= smps; + } + + if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC) + ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) { + u32 stbc; + + stbc = ar->ht_cap_info; + stbc &= WMI_HT_CAP_RX_STBC; + stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT; + stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc &= IEEE80211_HT_CAP_RX_STBC; + + ht_cap.cap |= stbc; + } + + if (ar->ht_cap_info & WMI_HT_CAP_LDPC) + ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT) + ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; + + /* max AMSDU is implicitly taken from vht_cap_info */ + if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) + ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++) + ht_cap.mcs.rx_mask[i] = 0xFF; + + ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; + + return ht_cap; +} + + +static void ath10k_get_arvif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_vif_iter *arvif_iter = data; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + if (arvif->vdev_id == arvif_iter->vdev_id) + arvif_iter->arvif = arvif; +} + +struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id) +{ + struct ath10k_vif_iter arvif_iter; + u32 flags; + + memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter)); + arvif_iter.vdev_id = vdev_id; + + flags = IEEE80211_IFACE_ITER_RESUME_ALL; + ieee80211_iterate_active_interfaces_atomic(ar->hw, + flags, + ath10k_get_arvif_iter, + &arvif_iter); + if (!arvif_iter.arvif) { + ath10k_warn("No VIF found for VDEV: %d\n", vdev_id); + return NULL; + } + + return arvif_iter.arvif; +} + +int ath10k_mac_register(struct ath10k *ar) +{ + struct ieee80211_supported_band *band; + struct ieee80211_sta_vht_cap vht_cap; + struct ieee80211_sta_ht_cap ht_cap; + void *channels; + int ret; + + SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr); + + SET_IEEE80211_DEV(ar->hw, ar->dev); + + ht_cap = ath10k_get_ht_cap(ar); + vht_cap = ath10k_create_vht_cap(ar); + + if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { + channels = kmemdup(ath10k_2ghz_channels, + sizeof(ath10k_2ghz_channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; + band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels); + band->channels = channels; + band->n_bitrates = ath10k_g_rates_size; + band->bitrates = ath10k_g_rates; + band->ht_cap = ht_cap; + + /* vht is not supported in 2.4 GHz */ + + ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + + if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) { + channels = kmemdup(ath10k_5ghz_channels, + sizeof(ath10k_5ghz_channels), + GFP_KERNEL); + if (!channels) { + if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { + band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; + kfree(band->channels); + } + return -ENOMEM; + } + + band = &ar->mac.sbands[IEEE80211_BAND_5GHZ]; + band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels); + band->channels = channels; + band->n_bitrates = ath10k_a_rates_size; + band->bitrates = ath10k_a_rates; + band->ht_cap = ht_cap; + band->vht_cap = vht_cap; + ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } + + ar->hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_SUPPORTS_UAPSD | + IEEE80211_HW_MFP_CAPABLE | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_HAS_RATE_CONTROL | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_AP_LINK_PS; + + if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) + ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS; + + if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) { + ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + ar->hw->flags |= IEEE80211_HW_TX_AMPDU_SETUP_IN_HW; + } + + ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; + ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN; + + ar->hw->vif_data_size = sizeof(struct ath10k_vif); + + ar->hw->channel_change_time = 5000; + ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; + + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->max_remain_on_channel_duration = 5000; + + ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + /* + * on LL hardware queues are managed entirely by the FW + * so we only advertise to mac we can do the queues thing + */ + ar->hw->queues = 4; + + ar->hw->wiphy->iface_combinations = &ath10k_if_comb; + ar->hw->wiphy->n_iface_combinations = 1; + + ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, + ath10k_reg_notifier); + if (ret) { + ath10k_err("Regulatory initialization failed\n"); + return ret; + } + + ret = ieee80211_register_hw(ar->hw); + if (ret) { + ath10k_err("ieee80211 registration failed: %d\n", ret); + return ret; + } + + if (!ath_is_world_regd(&ar->ath_common.regulatory)) { + ret = regulatory_hint(ar->hw->wiphy, + ar->ath_common.regulatory.alpha2); + if (ret) + goto exit; + } + + return 0; +exit: + ieee80211_unregister_hw(ar->hw); + return ret; +} + +void ath10k_mac_unregister(struct ath10k *ar) +{ + ieee80211_unregister_hw(ar->hw); + + kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); +} diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h new file mode 100644 index 00000000000..27fc92e5882 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MAC_H_ +#define _MAC_H_ + +#include <net/mac80211.h> +#include "core.h" + +struct ath10k_generic_iter { + struct ath10k *ar; + int ret; +}; + +struct ath10k *ath10k_mac_create(void); +void ath10k_mac_destroy(struct ath10k *ar); +int ath10k_mac_register(struct ath10k *ar); +void ath10k_mac_unregister(struct ath10k *ar); +struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); +void ath10k_reset_scan(unsigned long ptr); +void ath10k_offchan_tx_purge(struct ath10k *ar); +void ath10k_offchan_tx_work(struct work_struct *work); + +static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) +{ + return (struct ath10k_vif *)vif->drv_priv; +} + +static inline void ath10k_tx_h_seq_no(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_vif *vif = info->control.vif; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + if (arvif->tx_seq_no == 0) + arvif->tx_seq_no = 0x1000; + + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + arvif->tx_seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no); + } +} + +#endif /* _MAC_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c new file mode 100644 index 00000000000..33af4672c90 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -0,0 +1,2507 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +#include "core.h" +#include "debug.h" + +#include "targaddrs.h" +#include "bmi.h" + +#include "hif.h" +#include "htc.h" + +#include "ce.h" +#include "pci.h" + +unsigned int ath10k_target_ps; +module_param(ath10k_target_ps, uint, 0644); +MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); + +#define QCA988X_1_0_DEVICE_ID (0xabcd) +#define QCA988X_2_0_DEVICE_ID (0x003c) + +static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { + { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */ + { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */ + {0} +}; + +static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, + u32 *data); + +static void ath10k_pci_process_ce(struct ath10k *ar); +static int ath10k_pci_post_rx(struct ath10k *ar); +static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, + int num); +static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); +static void ath10k_pci_stop_ce(struct ath10k *ar); + +static const struct ce_attr host_ce_config_wlan[] = { + /* host->target HTC control and raw streams */ + { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,}, + /* could be moved to share CE3 */ + /* target->host HTT + HTC control */ + { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,}, + /* target->host WMI */ + { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,}, + /* host->target WMI */ + { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,}, + /* host->target HTT */ + { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0, + CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,}, + /* unused */ + { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, + /* Target autonomous hif_memcpy */ + { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, + /* ce_diag, the Diagnostic Window */ + { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,}, +}; + +/* Target firmware's Copy Engine configuration. */ +static const struct ce_pipe_config target_ce_config_wlan[] = { + /* host->target HTC control and raw streams */ + { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,}, + /* target->host HTT + HTC control */ + { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,}, + /* target->host WMI */ + { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* host->target WMI */ + { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* host->target HTT */ + { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,}, + /* NB: 50% of src nentries, since tx has 2 frags */ + /* unused */ + { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* Reserved for target autonomous hif_memcpy */ + { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,}, + /* CE7 used only by Host */ +}; + +/* + * Diagnostic read/write access is provided for startup/config/debug usage. + * Caller must guarantee proper alignment, when applicable, and single user + * at any moment. + */ +static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, + int nbytes) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret = 0; + u32 buf; + unsigned int completed_nbytes, orig_nbytes, remaining_bytes; + unsigned int id; + unsigned int flags; + struct ce_state *ce_diag; + /* Host buffer address in CE space */ + u32 ce_data; + dma_addr_t ce_data_base = 0; + void *data_buf = NULL; + int i; + + /* + * This code cannot handle reads to non-memory space. Redirect to the + * register read fn but preserve the multi word read capability of + * this fn + */ + if (address < DRAM_BASE_ADDRESS) { + if (!IS_ALIGNED(address, 4) || + !IS_ALIGNED((unsigned long)data, 4)) + return -EIO; + + while ((nbytes >= 4) && ((ret = ath10k_pci_diag_read_access( + ar, address, (u32 *)data)) == 0)) { + nbytes -= sizeof(u32); + address += sizeof(u32); + data += sizeof(u32); + } + return ret; + } + + ce_diag = ar_pci->ce_diag; + + /* + * Allocate a temporary bounce buffer to hold caller's data + * to be DMA'ed from Target. This guarantees + * 1) 4-byte alignment + * 2) Buffer in DMA-able space + */ + orig_nbytes = nbytes; + data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev, + orig_nbytes, + &ce_data_base); + + if (!data_buf) { + ret = -ENOMEM; + goto done; + } + memset(data_buf, 0, orig_nbytes); + + remaining_bytes = orig_nbytes; + ce_data = ce_data_base; + while (remaining_bytes) { + nbytes = min_t(unsigned int, remaining_bytes, + DIAG_TRANSFER_LIMIT); + + ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data); + if (ret != 0) + goto done; + + /* Request CE to send from Target(!) address to Host buffer */ + /* + * The address supplied by the caller is in the + * Target CPU virtual address space. + * + * In order to use this address with the diagnostic CE, + * convert it from Target CPU virtual address space + * to CE address space + */ + ath10k_pci_wake(ar); + address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, + address); + ath10k_pci_sleep(ar); + + ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0, + 0); + if (ret) + goto done; + + i = 0; + while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id) != 0) { + mdelay(1); + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != (u32) address) { + ret = -EIO; + goto done; + } + + i = 0; + while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id, &flags) != 0) { + mdelay(1); + + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != ce_data) { + ret = -EIO; + goto done; + } + + remaining_bytes -= nbytes; + address += nbytes; + ce_data += nbytes; + } + +done: + if (ret == 0) { + /* Copy data from allocated DMA buf to caller's buf */ + WARN_ON_ONCE(orig_nbytes & 3); + for (i = 0; i < orig_nbytes / sizeof(__le32); i++) { + ((u32 *)data)[i] = + __le32_to_cpu(((__le32 *)data_buf)[i]); + } + } else + ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", + __func__, address); + + if (data_buf) + pci_free_consistent(ar_pci->pdev, orig_nbytes, + data_buf, ce_data_base); + + return ret; +} + +/* Read 4-byte aligned data from Target memory or register */ +static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, + u32 *data) +{ + /* Assume range doesn't cross this boundary */ + if (address >= DRAM_BASE_ADDRESS) + return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32)); + + ath10k_pci_wake(ar); + *data = ath10k_pci_read32(ar, address); + ath10k_pci_sleep(ar); + return 0; +} + +static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, + const void *data, int nbytes) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret = 0; + u32 buf; + unsigned int completed_nbytes, orig_nbytes, remaining_bytes; + unsigned int id; + unsigned int flags; + struct ce_state *ce_diag; + void *data_buf = NULL; + u32 ce_data; /* Host buffer address in CE space */ + dma_addr_t ce_data_base = 0; + int i; + + ce_diag = ar_pci->ce_diag; + + /* + * Allocate a temporary bounce buffer to hold caller's data + * to be DMA'ed to Target. This guarantees + * 1) 4-byte alignment + * 2) Buffer in DMA-able space + */ + orig_nbytes = nbytes; + data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev, + orig_nbytes, + &ce_data_base); + if (!data_buf) { + ret = -ENOMEM; + goto done; + } + + /* Copy caller's data to allocated DMA buf */ + WARN_ON_ONCE(orig_nbytes & 3); + for (i = 0; i < orig_nbytes / sizeof(__le32); i++) + ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]); + + /* + * The address supplied by the caller is in the + * Target CPU virtual address space. + * + * In order to use this address with the diagnostic CE, + * convert it from + * Target CPU virtual address space + * to + * CE address space + */ + ath10k_pci_wake(ar); + address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address); + ath10k_pci_sleep(ar); + + remaining_bytes = orig_nbytes; + ce_data = ce_data_base; + while (remaining_bytes) { + /* FIXME: check cast */ + nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT); + + /* Set up to receive directly into Target(!) address */ + ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address); + if (ret != 0) + goto done; + + /* + * Request CE to send caller-supplied data that + * was copied to bounce buffer to Target(!) address. + */ + ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data, + nbytes, 0, 0); + if (ret != 0) + goto done; + + i = 0; + while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id) != 0) { + mdelay(1); + + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != ce_data) { + ret = -EIO; + goto done; + } + + i = 0; + while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id, &flags) != 0) { + mdelay(1); + + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != address) { + ret = -EIO; + goto done; + } + + remaining_bytes -= nbytes; + address += nbytes; + ce_data += nbytes; + } + +done: + if (data_buf) { + pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf, + ce_data_base); + } + + if (ret != 0) + ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__, + address); + + return ret; +} + +/* Write 4B data to Target memory or register */ +static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address, + u32 data) +{ + /* Assume range doesn't cross this boundary */ + if (address >= DRAM_BASE_ADDRESS) + return ath10k_pci_diag_write_mem(ar, address, &data, + sizeof(u32)); + + ath10k_pci_wake(ar); + ath10k_pci_write32(ar, address, data); + ath10k_pci_sleep(ar); + return 0; +} + +static bool ath10k_pci_target_is_awake(struct ath10k *ar) +{ + void __iomem *mem = ath10k_pci_priv(ar)->mem; + u32 val; + val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + + RTC_STATE_ADDRESS); + return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON); +} + +static void ath10k_pci_wait(struct ath10k *ar) +{ + int n = 100; + + while (n-- && !ath10k_pci_target_is_awake(ar)) + msleep(10); + + if (n < 0) + ath10k_warn("Unable to wakeup target\n"); +} + +void ath10k_do_pci_wake(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *pci_addr = ar_pci->mem; + int tot_delay = 0; + int curr_delay = 5; + + if (atomic_read(&ar_pci->keep_awake_count) == 0) { + /* Force AWAKE */ + iowrite32(PCIE_SOC_WAKE_V_MASK, + pci_addr + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + } + atomic_inc(&ar_pci->keep_awake_count); + + if (ar_pci->verified_awake) + return; + + for (;;) { + if (ath10k_pci_target_is_awake(ar)) { + ar_pci->verified_awake = true; + break; + } + + if (tot_delay > PCIE_WAKE_TIMEOUT) { + ath10k_warn("target takes too long to wake up (awake count %d)\n", + atomic_read(&ar_pci->keep_awake_count)); + break; + } + + udelay(curr_delay); + tot_delay += curr_delay; + + if (curr_delay < 50) + curr_delay += 5; + } +} + +void ath10k_do_pci_sleep(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *pci_addr = ar_pci->mem; + + if (atomic_dec_and_test(&ar_pci->keep_awake_count)) { + /* Allow sleep */ + ar_pci->verified_awake = false; + iowrite32(PCIE_SOC_WAKE_RESET, + pci_addr + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + } +} + +/* + * FIXME: Handle OOM properly. + */ +static inline +struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info) +{ + struct ath10k_pci_compl *compl = NULL; + + spin_lock_bh(&pipe_info->pipe_lock); + if (list_empty(&pipe_info->compl_free)) { + ath10k_warn("Completion buffers are full\n"); + goto exit; + } + compl = list_first_entry(&pipe_info->compl_free, + struct ath10k_pci_compl, list); + list_del(&compl->list); +exit: + spin_unlock_bh(&pipe_info->pipe_lock); + return compl; +} + +/* Called by lower (CE) layer when a send to Target completes. */ +static void ath10k_pci_ce_send_done(struct ce_state *ce_state, + void *transfer_context, + u32 ce_data, + unsigned int nbytes, + unsigned int transfer_id) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_pci_compl *compl; + bool process = false; + + do { + /* + * For the send completion of an item in sendlist, just + * increment num_sends_allowed. The upper layer callback will + * be triggered when last fragment is done with send. + */ + if (transfer_context == CE_SENDLIST_ITEM_CTXT) { + spin_lock_bh(&pipe_info->pipe_lock); + pipe_info->num_sends_allowed++; + spin_unlock_bh(&pipe_info->pipe_lock); + continue; + } + + compl = get_free_compl(pipe_info); + if (!compl) + break; + + compl->send_or_recv = HIF_CE_COMPLETE_SEND; + compl->ce_state = ce_state; + compl->pipe_info = pipe_info; + compl->transfer_context = transfer_context; + compl->nbytes = nbytes; + compl->transfer_id = transfer_id; + compl->flags = 0; + + /* + * Add the completion to the processing queue. + */ + spin_lock_bh(&ar_pci->compl_lock); + list_add_tail(&compl->list, &ar_pci->compl_process); + spin_unlock_bh(&ar_pci->compl_lock); + + process = true; + } while (ath10k_ce_completed_send_next(ce_state, + &transfer_context, + &ce_data, &nbytes, + &transfer_id) == 0); + + /* + * If only some of the items within a sendlist have completed, + * don't invoke completion processing until the entire sendlist + * has been sent. + */ + if (!process) + return; + + ath10k_pci_process_ce(ar); +} + +/* Called by lower (CE) layer when data is received from the Target. */ +static void ath10k_pci_ce_recv_data(struct ce_state *ce_state, + void *transfer_context, u32 ce_data, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_pci_compl *compl; + struct sk_buff *skb; + + do { + compl = get_free_compl(pipe_info); + if (!compl) + break; + + compl->send_or_recv = HIF_CE_COMPLETE_RECV; + compl->ce_state = ce_state; + compl->pipe_info = pipe_info; + compl->transfer_context = transfer_context; + compl->nbytes = nbytes; + compl->transfer_id = transfer_id; + compl->flags = flags; + + skb = transfer_context; + dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + /* + * Add the completion to the processing queue. + */ + spin_lock_bh(&ar_pci->compl_lock); + list_add_tail(&compl->list, &ar_pci->compl_process); + spin_unlock_bh(&ar_pci->compl_lock); + + } while (ath10k_ce_completed_recv_next(ce_state, + &transfer_context, + &ce_data, &nbytes, + &transfer_id, + &flags) == 0); + + ath10k_pci_process_ce(ar); +} + +/* Send the first nbytes bytes of the buffer */ +static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, + unsigned int transfer_id, + unsigned int bytes, struct sk_buff *nbuf) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]); + struct ce_state *ce_hdl = pipe_info->ce_hdl; + struct ce_sendlist sendlist; + unsigned int len; + u32 flags = 0; + int ret; + + memset(&sendlist, 0, sizeof(struct ce_sendlist)); + + len = min(bytes, nbuf->len); + bytes -= len; + + if (len & 3) + ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len); + + ath10k_dbg(ATH10K_DBG_PCI, + "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n", + nbuf->data, (unsigned long long) skb_cb->paddr, + nbuf->len, len); + ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, + "ath10k tx: data: ", + nbuf->data, nbuf->len); + + ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags); + + /* Make sure we have resources to handle this request */ + spin_lock_bh(&pipe_info->pipe_lock); + if (!pipe_info->num_sends_allowed) { + ath10k_warn("Pipe: %d is full\n", pipe_id); + spin_unlock_bh(&pipe_info->pipe_lock); + return -ENOSR; + } + pipe_info->num_sends_allowed--; + spin_unlock_bh(&pipe_info->pipe_lock); + + ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id); + if (ret) + ath10k_warn("CE send failed: %p\n", nbuf); + + return ret; +} + +static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]); + int ret; + + spin_lock_bh(&pipe_info->pipe_lock); + ret = pipe_info->num_sends_allowed; + spin_unlock_bh(&pipe_info->pipe_lock); + + return ret; +} + +static void ath10k_pci_hif_dump_area(struct ath10k *ar) +{ + u32 reg_dump_area = 0; + u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {}; + u32 host_addr; + int ret; + u32 i; + + ath10k_err("firmware crashed!\n"); + ath10k_err("hardware name %s version 0x%x\n", + ar->hw_params.name, ar->target_version); + ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major, + ar->fw_version_minor, ar->fw_version_release, + ar->fw_version_build); + + host_addr = host_interest_item_address(HI_ITEM(hi_failure_state)); + if (ath10k_pci_diag_read_mem(ar, host_addr, + ®_dump_area, sizeof(u32)) != 0) { + ath10k_warn("could not read hi_failure_state\n"); + return; + } + + ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area); + + ret = ath10k_pci_diag_read_mem(ar, reg_dump_area, + ®_dump_values[0], + REG_DUMP_COUNT_QCA988X * sizeof(u32)); + if (ret != 0) { + ath10k_err("could not dump FW Dump Area\n"); + return; + } + + BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4); + + ath10k_err("target Register Dump\n"); + for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4) + ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n", + i, + reg_dump_values[i], + reg_dump_values[i + 1], + reg_dump_values[i + 2], + reg_dump_values[i + 3]); +} + +static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, + int force) +{ + if (!force) { + int resources; + /* + * Decide whether to actually poll for completions, or just + * wait for a later chance. + * If there seem to be plenty of resources left, then just wait + * since checking involves reading a CE register, which is a + * relatively expensive operation. + */ + resources = ath10k_pci_hif_get_free_queue_number(ar, pipe); + + /* + * If at least 50% of the total resources are still available, + * don't bother checking again yet. + */ + if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1)) + return; + } + ath10k_ce_per_engine_service(ar, pipe); +} + +static void ath10k_pci_hif_post_init(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + memcpy(&ar_pci->msg_callbacks_current, callbacks, + sizeof(ar_pci->msg_callbacks_current)); +} + +static int ath10k_pci_start_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_diag = ar_pci->ce_diag; + const struct ce_attr *attr; + struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_compl *compl; + int i, pipe_num, completions, disable_interrupts; + + spin_lock_init(&ar_pci->compl_lock); + INIT_LIST_HEAD(&ar_pci->compl_process); + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + + spin_lock_init(&pipe_info->pipe_lock); + INIT_LIST_HEAD(&pipe_info->compl_free); + + /* Handle Diagnostic CE specially */ + if (pipe_info->ce_hdl == ce_diag) + continue; + + attr = &host_ce_config_wlan[pipe_num]; + completions = 0; + + if (attr->src_nentries) { + disable_interrupts = attr->flags & CE_ATTR_DIS_INTR; + ath10k_ce_send_cb_register(pipe_info->ce_hdl, + ath10k_pci_ce_send_done, + disable_interrupts); + completions += attr->src_nentries; + pipe_info->num_sends_allowed = attr->src_nentries - 1; + } + + if (attr->dest_nentries) { + ath10k_ce_recv_cb_register(pipe_info->ce_hdl, + ath10k_pci_ce_recv_data); + completions += attr->dest_nentries; + } + + if (completions == 0) + continue; + + for (i = 0; i < completions; i++) { + compl = kmalloc(sizeof(struct ath10k_pci_compl), + GFP_KERNEL); + if (!compl) { + ath10k_warn("No memory for completion state\n"); + ath10k_pci_stop_ce(ar); + return -ENOMEM; + } + + compl->send_or_recv = HIF_CE_COMPLETE_FREE; + list_add_tail(&compl->list, &pipe_info->compl_free); + } + } + + return 0; +} + +static void ath10k_pci_stop_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_pci_compl *compl; + struct sk_buff *skb; + int i; + + ath10k_ce_disable_interrupts(ar); + + /* Cancel the pending tasklet */ + tasklet_kill(&ar_pci->intr_tq); + + for (i = 0; i < CE_COUNT; i++) + tasklet_kill(&ar_pci->pipe_info[i].intr); + + /* Mark pending completions as aborted, so that upper layers free up + * their associated resources */ + spin_lock_bh(&ar_pci->compl_lock); + list_for_each_entry(compl, &ar_pci->compl_process, list) { + skb = (struct sk_buff *)compl->transfer_context; + ATH10K_SKB_CB(skb)->is_aborted = true; + } + spin_unlock_bh(&ar_pci->compl_lock); +} + +static void ath10k_pci_cleanup_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_pci_compl *compl, *tmp; + struct hif_ce_pipe_info *pipe_info; + struct sk_buff *netbuf; + int pipe_num; + + /* Free pending completions. */ + spin_lock_bh(&ar_pci->compl_lock); + if (!list_empty(&ar_pci->compl_process)) + ath10k_warn("pending completions still present! possible memory leaks.\n"); + + list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) { + list_del(&compl->list); + netbuf = (struct sk_buff *)compl->transfer_context; + dev_kfree_skb_any(netbuf); + kfree(compl); + } + spin_unlock_bh(&ar_pci->compl_lock); + + /* Free unused completions for each pipe. */ + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + + spin_lock_bh(&pipe_info->pipe_lock); + list_for_each_entry_safe(compl, tmp, + &pipe_info->compl_free, list) { + list_del(&compl->list); + kfree(compl); + } + spin_unlock_bh(&pipe_info->pipe_lock); + } +} + +static void ath10k_pci_process_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ar->hif.priv; + struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; + struct ath10k_pci_compl *compl; + struct sk_buff *skb; + unsigned int nbytes; + int ret, send_done = 0; + + /* Upper layers aren't ready to handle tx/rx completions in parallel so + * we must serialize all completion processing. */ + + spin_lock_bh(&ar_pci->compl_lock); + if (ar_pci->compl_processing) { + spin_unlock_bh(&ar_pci->compl_lock); + return; + } + ar_pci->compl_processing = true; + spin_unlock_bh(&ar_pci->compl_lock); + + for (;;) { + spin_lock_bh(&ar_pci->compl_lock); + if (list_empty(&ar_pci->compl_process)) { + spin_unlock_bh(&ar_pci->compl_lock); + break; + } + compl = list_first_entry(&ar_pci->compl_process, + struct ath10k_pci_compl, list); + list_del(&compl->list); + spin_unlock_bh(&ar_pci->compl_lock); + + if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) { + cb->tx_completion(ar, + compl->transfer_context, + compl->transfer_id); + send_done = 1; + } else { + ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1); + if (ret) { + ath10k_warn("Unable to post recv buffer for pipe: %d\n", + compl->pipe_info->pipe_num); + break; + } + + skb = (struct sk_buff *)compl->transfer_context; + nbytes = compl->nbytes; + + ath10k_dbg(ATH10K_DBG_PCI, + "ath10k_pci_ce_recv_data netbuf=%p nbytes=%d\n", + skb, nbytes); + ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, + "ath10k rx: ", skb->data, nbytes); + + if (skb->len + skb_tailroom(skb) >= nbytes) { + skb_trim(skb, 0); + skb_put(skb, nbytes); + cb->rx_completion(ar, skb, + compl->pipe_info->pipe_num); + } else { + ath10k_warn("rxed more than expected (nbytes %d, max %d)", + nbytes, + skb->len + skb_tailroom(skb)); + } + } + + compl->send_or_recv = HIF_CE_COMPLETE_FREE; + + /* + * Add completion back to the pipe's free list. + */ + spin_lock_bh(&compl->pipe_info->pipe_lock); + list_add_tail(&compl->list, &compl->pipe_info->compl_free); + compl->pipe_info->num_sends_allowed += send_done; + spin_unlock_bh(&compl->pipe_info->pipe_lock); + } + + spin_lock_bh(&ar_pci->compl_lock); + ar_pci->compl_processing = false; + spin_unlock_bh(&ar_pci->compl_lock); +} + +/* TODO - temporary mapping while we have too few CE's */ +static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, + u16 service_id, u8 *ul_pipe, + u8 *dl_pipe, int *ul_is_polled, + int *dl_is_polled) +{ + int ret = 0; + + /* polling for received messages not supported */ + *dl_is_polled = 0; + + switch (service_id) { + case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: + /* + * Host->target HTT gets its own pipe, so it can be polled + * while other pipes are interrupt driven. + */ + *ul_pipe = 4; + /* + * Use the same target->host pipe for HTC ctrl, HTC raw + * streams, and HTT. + */ + *dl_pipe = 1; + break; + + case ATH10K_HTC_SVC_ID_RSVD_CTRL: + case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS: + /* + * Note: HTC_RAW_STREAMS_SVC is currently unused, and + * HTC_CTRL_RSVD_SVC could share the same pipe as the + * WMI services. So, if another CE is needed, change + * this to *ul_pipe = 3, which frees up CE 0. + */ + /* *ul_pipe = 3; */ + *ul_pipe = 0; + *dl_pipe = 1; + break; + + case ATH10K_HTC_SVC_ID_WMI_DATA_BK: + case ATH10K_HTC_SVC_ID_WMI_DATA_BE: + case ATH10K_HTC_SVC_ID_WMI_DATA_VI: + case ATH10K_HTC_SVC_ID_WMI_DATA_VO: + + case ATH10K_HTC_SVC_ID_WMI_CONTROL: + *ul_pipe = 3; + *dl_pipe = 2; + break; + + /* pipe 5 unused */ + /* pipe 6 reserved */ + /* pipe 7 reserved */ + + default: + ret = -1; + break; + } + *ul_is_polled = + (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0; + + return ret; +} + +static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + int ul_is_polled, dl_is_polled; + + (void)ath10k_pci_hif_map_service_to_pipe(ar, + ATH10K_HTC_SVC_ID_RSVD_CTRL, + ul_pipe, + dl_pipe, + &ul_is_polled, + &dl_is_polled); +} + +static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, + int num) +{ + struct ath10k *ar = pipe_info->hif_ce_state; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_state = pipe_info->ce_hdl; + struct sk_buff *skb; + dma_addr_t ce_data; + int i, ret = 0; + + if (pipe_info->buf_sz == 0) + return 0; + + for (i = 0; i < num; i++) { + skb = dev_alloc_skb(pipe_info->buf_sz); + if (!skb) { + ath10k_warn("could not allocate skbuff for pipe %d\n", + num); + ret = -ENOMEM; + goto err; + } + + WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb"); + + ce_data = dma_map_single(ar->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(ar->dev, ce_data))) { + ath10k_warn("could not dma map skbuff\n"); + dev_kfree_skb_any(skb); + ret = -EIO; + goto err; + } + + ATH10K_SKB_CB(skb)->paddr = ce_data; + + pci_dma_sync_single_for_device(ar_pci->pdev, ce_data, + pipe_info->buf_sz, + PCI_DMA_FROMDEVICE); + + ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb, + ce_data); + if (ret) { + ath10k_warn("could not enqueue to pipe %d (%d)\n", + num, ret); + goto err; + } + } + + return ret; + +err: + ath10k_pci_rx_pipe_cleanup(pipe_info); + return ret; +} + +static int ath10k_pci_post_rx(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info; + const struct ce_attr *attr; + int pipe_num, ret = 0; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + attr = &host_ce_config_wlan[pipe_num]; + + if (attr->dest_nentries == 0) + continue; + + ret = ath10k_pci_post_rx_pipe(pipe_info, + attr->dest_nentries - 1); + if (ret) { + ath10k_warn("Unable to replenish recv buffers for pipe: %d\n", + pipe_num); + + for (; pipe_num >= 0; pipe_num--) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + ath10k_pci_rx_pipe_cleanup(pipe_info); + } + return ret; + } + } + + return 0; +} + +static int ath10k_pci_hif_start(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = ath10k_pci_start_ce(ar); + if (ret) { + ath10k_warn("could not start CE (%d)\n", ret); + return ret; + } + + /* Post buffers once to start things off. */ + ret = ath10k_pci_post_rx(ar); + if (ret) { + ath10k_warn("could not post rx pipes (%d)\n", ret); + return ret; + } + + ar_pci->started = 1; + return 0; +} + +static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +{ + struct ath10k *ar; + struct ath10k_pci *ar_pci; + struct ce_state *ce_hdl; + u32 buf_sz; + struct sk_buff *netbuf; + u32 ce_data; + + buf_sz = pipe_info->buf_sz; + + /* Unused Copy Engine */ + if (buf_sz == 0) + return; + + ar = pipe_info->hif_ce_state; + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci->started) + return; + + ce_hdl = pipe_info->ce_hdl; + + while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf, + &ce_data) == 0) { + dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr, + netbuf->len + skb_tailroom(netbuf), + DMA_FROM_DEVICE); + dev_kfree_skb_any(netbuf); + } +} + +static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +{ + struct ath10k *ar; + struct ath10k_pci *ar_pci; + struct ce_state *ce_hdl; + struct sk_buff *netbuf; + u32 ce_data; + unsigned int nbytes; + unsigned int id; + u32 buf_sz; + + buf_sz = pipe_info->buf_sz; + + /* Unused Copy Engine */ + if (buf_sz == 0) + return; + + ar = pipe_info->hif_ce_state; + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci->started) + return; + + ce_hdl = pipe_info->ce_hdl; + + while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf, + &ce_data, &nbytes, &id) == 0) { + if (netbuf != CE_SENDLIST_ITEM_CTXT) + /* + * Indicate the completion to higer layer to free + * the buffer + */ + ATH10K_SKB_CB(netbuf)->is_aborted = true; + ar_pci->msg_callbacks_current.tx_completion(ar, + netbuf, + id); + } +} + +/* + * Cleanup residual buffers for device shutdown: + * buffers that were enqueued for receive + * buffers that were to be sent + * Note: Buffers that had completed but which were + * not yet processed are on a completion queue. They + * are handled when the completion thread shuts down. + */ +static void ath10k_pci_buffer_cleanup(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int pipe_num; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + struct hif_ce_pipe_info *pipe_info; + + pipe_info = &ar_pci->pipe_info[pipe_num]; + ath10k_pci_rx_pipe_cleanup(pipe_info); + ath10k_pci_tx_pipe_cleanup(pipe_info); + } +} + +static void ath10k_pci_ce_deinit(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info; + int pipe_num; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + if (pipe_info->ce_hdl) { + ath10k_ce_deinit(pipe_info->ce_hdl); + pipe_info->ce_hdl = NULL; + pipe_info->buf_sz = 0; + } + } +} + +static void ath10k_pci_hif_stop(struct ath10k *ar) +{ + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + ath10k_pci_stop_ce(ar); + + /* At this point, asynchronous threads are stopped, the target should + * not DMA nor interrupt. We process the leftovers and then free + * everything else up. */ + + ath10k_pci_process_ce(ar); + ath10k_pci_cleanup_ce(ar); + ath10k_pci_buffer_cleanup(ar); + ath10k_pci_ce_deinit(ar); +} + +static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, + void *req, u32 req_len, + void *resp, u32 *resp_len) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl; + struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl; + dma_addr_t req_paddr = 0; + dma_addr_t resp_paddr = 0; + struct bmi_xfer xfer = {}; + void *treq, *tresp = NULL; + int ret = 0; + + if (resp && !resp_len) + return -EINVAL; + + if (resp && resp_len && *resp_len == 0) + return -EINVAL; + + treq = kmemdup(req, req_len, GFP_KERNEL); + if (!treq) + return -ENOMEM; + + req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE); + ret = dma_mapping_error(ar->dev, req_paddr); + if (ret) + goto err_dma; + + if (resp && resp_len) { + tresp = kzalloc(*resp_len, GFP_KERNEL); + if (!tresp) { + ret = -ENOMEM; + goto err_req; + } + + resp_paddr = dma_map_single(ar->dev, tresp, *resp_len, + DMA_FROM_DEVICE); + ret = dma_mapping_error(ar->dev, resp_paddr); + if (ret) + goto err_req; + + xfer.wait_for_resp = true; + xfer.resp_len = 0; + + ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr); + } + + init_completion(&xfer.done); + + ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0); + if (ret) + goto err_resp; + + ret = wait_for_completion_timeout(&xfer.done, + BMI_COMMUNICATION_TIMEOUT_HZ); + if (ret <= 0) { + u32 unused_buffer; + unsigned int unused_nbytes; + unsigned int unused_id; + + ret = -ETIMEDOUT; + ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer, + &unused_nbytes, &unused_id); + } else { + /* non-zero means we did not time out */ + ret = 0; + } + +err_resp: + if (resp) { + u32 unused_buffer; + + ath10k_ce_revoke_recv_next(ce_rx, NULL, &unused_buffer); + dma_unmap_single(ar->dev, resp_paddr, + *resp_len, DMA_FROM_DEVICE); + } +err_req: + dma_unmap_single(ar->dev, req_paddr, req_len, DMA_TO_DEVICE); + + if (ret == 0 && resp_len) { + *resp_len = min(*resp_len, xfer.resp_len); + memcpy(resp, tresp, xfer.resp_len); + } +err_dma: + kfree(treq); + kfree(tresp); + + return ret; +} + +static void ath10k_pci_bmi_send_done(struct ce_state *ce_state, + void *transfer_context, + u32 data, + unsigned int nbytes, + unsigned int transfer_id) +{ + struct bmi_xfer *xfer = transfer_context; + + if (xfer->wait_for_resp) + return; + + complete(&xfer->done); +} + +static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state, + void *transfer_context, + u32 data, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct bmi_xfer *xfer = transfer_context; + + if (!xfer->wait_for_resp) { + ath10k_warn("unexpected: BMI data received; ignoring\n"); + return; + } + + xfer->resp_len = nbytes; + complete(&xfer->done); +} + +/* + * Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + ATH10K_HTC_SVC_ID_WMI_DATA_VO, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_VO, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BK, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BK, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BE, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BE, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_VI, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_VI, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_CONTROL, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_CONTROL, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_RSVD_CTRL, + PIPEDIR_OUT, /* out = UL = host -> target */ + 0, /* could be moved to 3 (share with WMI) */ + }, + { + ATH10K_HTC_SVC_ID_RSVD_CTRL, + PIPEDIR_IN, /* in = DL = target -> host */ + 1, + }, + { + ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */ + PIPEDIR_OUT, /* out = UL = host -> target */ + 0, + }, + { + ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */ + PIPEDIR_IN, /* in = DL = target -> host */ + 1, + }, + { + ATH10K_HTC_SVC_ID_HTT_DATA_MSG, + PIPEDIR_OUT, /* out = UL = host -> target */ + 4, + }, + { + ATH10K_HTC_SVC_ID_HTT_DATA_MSG, + PIPEDIR_IN, /* in = DL = target -> host */ + 1, + }, + + /* (Additions here) */ + + { /* Must be last */ + 0, + 0, + 0, + }, +}; + +/* + * Send an interrupt to the device to wake up the Target CPU + * so it has an opportunity to notice any changed state. + */ +static int ath10k_pci_wake_target_cpu(struct ath10k *ar) +{ + int ret; + u32 core_ctrl; + + ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS | + CORE_CTRL_ADDRESS, + &core_ctrl); + if (ret) { + ath10k_warn("Unable to read core ctrl\n"); + return ret; + } + + /* A_INUM_FIRMWARE interrupt to Target CPU */ + core_ctrl |= CORE_CTRL_CPU_INTR_MASK; + + ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS | + CORE_CTRL_ADDRESS, + core_ctrl); + if (ret) + ath10k_warn("Unable to set interrupt mask\n"); + + return ret; +} + +static int ath10k_pci_init_config(struct ath10k *ar) +{ + u32 interconnect_targ_addr; + u32 pcie_state_targ_addr = 0; + u32 pipe_cfg_targ_addr = 0; + u32 svc_to_pipe_map = 0; + u32 pcie_config_flags = 0; + u32 ealloc_value; + u32 ealloc_targ_addr; + u32 flag2_value; + u32 flag2_targ_addr; + int ret = 0; + + /* Download to Target the CE Config and the service-to-CE map */ + interconnect_targ_addr = + host_interest_item_address(HI_ITEM(hi_interconnect_state)); + + /* Supply Target-side CE configuration */ + ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr, + &pcie_state_targ_addr); + if (ret != 0) { + ath10k_err("Failed to get pcie state addr: %d\n", ret); + return ret; + } + + if (pcie_state_targ_addr == 0) { + ret = -EIO; + ath10k_err("Invalid pcie state addr\n"); + return ret; + } + + ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, + pipe_cfg_addr), + &pipe_cfg_targ_addr); + if (ret != 0) { + ath10k_err("Failed to get pipe cfg addr: %d\n", ret); + return ret; + } + + if (pipe_cfg_targ_addr == 0) { + ret = -EIO; + ath10k_err("Invalid pipe cfg addr\n"); + return ret; + } + + ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr, + target_ce_config_wlan, + sizeof(target_ce_config_wlan)); + + if (ret != 0) { + ath10k_err("Failed to write pipe cfg: %d\n", ret); + return ret; + } + + ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, + svc_to_pipe_map), + &svc_to_pipe_map); + if (ret != 0) { + ath10k_err("Failed to get svc/pipe map: %d\n", ret); + return ret; + } + + if (svc_to_pipe_map == 0) { + ret = -EIO; + ath10k_err("Invalid svc_to_pipe map\n"); + return ret; + } + + ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map, + target_service_to_ce_map_wlan, + sizeof(target_service_to_ce_map_wlan)); + if (ret != 0) { + ath10k_err("Failed to write svc/pipe map: %d\n", ret); + return ret; + } + + ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, + config_flags), + &pcie_config_flags); + if (ret != 0) { + ath10k_err("Failed to get pcie config_flags: %d\n", ret); + return ret; + } + + pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1; + + ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, config_flags), + &pcie_config_flags, + sizeof(pcie_config_flags)); + if (ret != 0) { + ath10k_err("Failed to write pcie config_flags: %d\n", ret); + return ret; + } + + /* configure early allocation */ + ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc)); + + ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value); + if (ret != 0) { + ath10k_err("Faile to get early alloc val: %d\n", ret); + return ret; + } + + /* first bank is switched to IRAM */ + ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) & + HI_EARLY_ALLOC_MAGIC_MASK); + ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) & + HI_EARLY_ALLOC_IRAM_BANKS_MASK); + + ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value); + if (ret != 0) { + ath10k_err("Failed to set early alloc val: %d\n", ret); + return ret; + } + + /* Tell Target to proceed with initialization */ + flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2)); + + ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value); + if (ret != 0) { + ath10k_err("Failed to get option val: %d\n", ret); + return ret; + } + + flag2_value |= HI_OPTION_EARLY_CFG_DONE; + + ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value); + if (ret != 0) { + ath10k_err("Failed to set option val: %d\n", ret); + return ret; + } + + return 0; +} + + + +static int ath10k_pci_ce_init(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info; + const struct ce_attr *attr; + int pipe_num; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + pipe_info->pipe_num = pipe_num; + pipe_info->hif_ce_state = ar; + attr = &host_ce_config_wlan[pipe_num]; + + pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr); + if (pipe_info->ce_hdl == NULL) { + ath10k_err("Unable to initialize CE for pipe: %d\n", + pipe_num); + + /* It is safe to call it here. It checks if ce_hdl is + * valid for each pipe */ + ath10k_pci_ce_deinit(ar); + return -1; + } + + if (pipe_num == ar_pci->ce_count - 1) { + /* + * Reserve the ultimate CE for + * diagnostic Window support + */ + ar_pci->ce_diag = + ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl; + continue; + } + + pipe_info->buf_sz = (size_t) (attr->src_sz_max); + } + + /* + * Initially, establish CE completion handlers for use with BMI. + * These are overwritten with generic handlers after we exit BMI phase. + */ + pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; + ath10k_ce_send_cb_register(pipe_info->ce_hdl, + ath10k_pci_bmi_send_done, 0); + + pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; + ath10k_ce_recv_cb_register(pipe_info->ce_hdl, + ath10k_pci_bmi_recv_data); + + return 0; +} + +static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 fw_indicator_address, fw_indicator; + + ath10k_pci_wake(ar); + + fw_indicator_address = ar_pci->fw_indicator_address; + fw_indicator = ath10k_pci_read32(ar, fw_indicator_address); + + if (fw_indicator & FW_IND_EVENT_PENDING) { + /* ACK: clear Target-side pending event */ + ath10k_pci_write32(ar, fw_indicator_address, + fw_indicator & ~FW_IND_EVENT_PENDING); + + if (ar_pci->started) { + ath10k_pci_hif_dump_area(ar); + } else { + /* + * Probable Target failure before we're prepared + * to handle it. Generally unexpected. + */ + ath10k_warn("early firmware event indicated\n"); + } + } + + ath10k_pci_sleep(ar); +} + +static const struct ath10k_hif_ops ath10k_pci_hif_ops = { + .send_head = ath10k_pci_hif_send_head, + .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, + .start = ath10k_pci_hif_start, + .stop = ath10k_pci_hif_stop, + .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, + .get_default_pipe = ath10k_pci_hif_get_default_pipe, + .send_complete_check = ath10k_pci_hif_send_complete_check, + .init = ath10k_pci_hif_post_init, + .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, +}; + +static void ath10k_pci_ce_tasklet(unsigned long ptr) +{ + struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr; + struct ath10k_pci *ar_pci = pipe->ar_pci; + + ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num); +} + +static void ath10k_msi_err_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + + ath10k_pci_fw_interrupt_handler(ar); +} + +/* + * Handler for a per-engine interrupt on a PARTICULAR CE. + * This is used in cases where each CE has a private MSI interrupt. + */ +static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL; + + if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_pci->pipe_info)) { + ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id); + return IRQ_HANDLED; + } + + /* + * NOTE: We are able to derive ce_id from irq because we + * use a one-to-one mapping for CE's 0..5. + * CE's 6 & 7 do not use interrupts at all. + * + * This mapping must be kept in sync with the mapping + * used by firmware. + */ + tasklet_schedule(&ar_pci->pipe_info[ce_id].intr); + return IRQ_HANDLED; +} + +static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + tasklet_schedule(&ar_pci->msi_fw_err); + return IRQ_HANDLED; +} + +/* + * Top-level interrupt handler for all PCI interrupts from a Target. + * When a block of MSI interrupts is allocated, this top-level handler + * is not used; instead, we directly call the correct sub-handler. + */ +static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (ar_pci->num_msi_intrs == 0) { + /* + * IMPORTANT: INTR_CLR regiser has to be set after + * INTR_ENABLE is set to 0, otherwise interrupt can not be + * really cleared. + */ + iowrite32(0, ar_pci->mem + + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_CLR_ADDRESS)); + /* + * IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. + */ + (void) ioread32(ar_pci->mem + + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + } + + tasklet_schedule(&ar_pci->intr_tq); + + return IRQ_HANDLED; +} + +static void ath10k_pci_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */ + ath10k_ce_per_engine_service_any(ar); + + if (ar_pci->num_msi_intrs == 0) { + /* Enable Legacy PCI line interrupts */ + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + /* + * IMPORTANT: this extra read transaction is required to + * flush the posted write buffer + */ + (void) ioread32(ar_pci->mem + + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + } +} + +static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + int i; + + ret = pci_enable_msi_block(ar_pci->pdev, num); + if (ret) + return ret; + + ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, + ath10k_pci_msi_fw_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret) + return ret; + + for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) { + ret = request_irq(ar_pci->pdev->irq + i, + ath10k_pci_per_engine_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret) { + ath10k_warn("request_irq(%d) failed %d\n", + ar_pci->pdev->irq + i, ret); + + for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--) + free_irq(ar_pci->pdev->irq + i, ar); + + free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar); + pci_disable_msi(ar_pci->pdev); + return ret; + } + } + + ath10k_info("MSI-X interrupt handling (%d intrs)\n", num); + return 0; +} + +static int ath10k_pci_start_intr_msi(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = pci_enable_msi(ar_pci->pdev); + if (ret < 0) + return ret; + + ret = request_irq(ar_pci->pdev->irq, + ath10k_pci_interrupt_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret < 0) { + pci_disable_msi(ar_pci->pdev); + return ret; + } + + ath10k_info("MSI interrupt handling\n"); + return 0; +} + +static int ath10k_pci_start_intr_legacy(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = request_irq(ar_pci->pdev->irq, + ath10k_pci_interrupt_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret < 0) + return ret; + + /* + * Make sure to wake the Target before enabling Legacy + * Interrupt. + */ + iowrite32(PCIE_SOC_WAKE_V_MASK, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ath10k_pci_wait(ar); + + /* + * A potential race occurs here: The CORE_BASE write + * depends on target correctly decoding AXI address but + * host won't know when target writes BAR to CORE_CTRL. + * This write might get lost if target has NOT written BAR. + * For now, fix the race by repeating the write in below + * synchronization checking. + */ + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ath10k_info("legacy interrupt handling\n"); + return 0; +} + +static int ath10k_pci_start_intr(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int num = MSI_NUM_REQUEST; + int ret; + int i; + + tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar); + tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, + (unsigned long) ar); + + for (i = 0; i < CE_COUNT; i++) { + ar_pci->pipe_info[i].ar_pci = ar_pci; + tasklet_init(&ar_pci->pipe_info[i].intr, + ath10k_pci_ce_tasklet, + (unsigned long)&ar_pci->pipe_info[i]); + } + + if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) + num = 1; + + if (num > 1) { + ret = ath10k_pci_start_intr_msix(ar, num); + if (ret == 0) + goto exit; + + ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret); + num = 1; + } + + if (num == 1) { + ret = ath10k_pci_start_intr_msi(ar); + if (ret == 0) + goto exit; + + ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n", + ret); + num = 0; + } + + ret = ath10k_pci_start_intr_legacy(ar); + +exit: + ar_pci->num_msi_intrs = num; + ar_pci->ce_count = CE_COUNT; + return ret; +} + +static void ath10k_pci_stop_intr(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + /* There's at least one interrupt irregardless whether its legacy INTR + * or MSI or MSI-X */ + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + free_irq(ar_pci->pdev->irq + i, ar); + + if (ar_pci->num_msi_intrs > 0) + pci_disable_msi(ar_pci->pdev); +} + +static int ath10k_pci_reset_target(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int wait_limit = 300; /* 3 sec */ + + /* Wait for Target to finish initialization before we proceed. */ + iowrite32(PCIE_SOC_WAKE_V_MASK, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ath10k_pci_wait(ar); + + while (wait_limit-- && + !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) & + FW_IND_INITIALIZED)) { + if (ar_pci->num_msi_intrs == 0) + /* Fix potential race by repeating CORE_BASE writes */ + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + mdelay(10); + } + + if (wait_limit < 0) { + ath10k_err("Target stalled\n"); + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + return -EIO; + } + + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + return 0; +} + +static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci) +{ + struct ath10k *ar = ar_pci->ar; + void __iomem *mem = ar_pci->mem; + int i; + u32 val; + + if (!SOC_GLOBAL_RESET_ADDRESS) + return; + + if (!mem) + return; + + ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, + PCIE_SOC_WAKE_V_MASK); + for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { + if (ath10k_pci_target_is_awake(ar)) + break; + msleep(1); + } + + /* Put Target, including PCIe, into RESET. */ + val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS); + val |= 1; + ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + + for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { + if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + RTC_STATE_COLD_RESET_MASK) + break; + msleep(1); + } + + /* Pull Target, including PCIe, out of RESET. */ + val &= ~1; + ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + + for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { + if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + RTC_STATE_COLD_RESET_MASK)) + break; + msleep(1); + } + + ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); +} + +static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) +{ + int i; + + for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) { + if (!test_bit(i, ar_pci->features)) + continue; + + switch (i) { + case ATH10K_PCI_FEATURE_MSI_X: + ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); + break; + case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND: + ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n"); + break; + } + } +} + +static int ath10k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_dev) +{ + void __iomem *mem; + int ret = 0; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + u32 lcr_val; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL); + if (ar_pci == NULL) + return -ENOMEM; + + ar_pci->pdev = pdev; + ar_pci->dev = &pdev->dev; + + switch (pci_dev->device) { + case QCA988X_1_0_DEVICE_ID: + set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features); + break; + case QCA988X_2_0_DEVICE_ID: + set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); + break; + default: + ret = -ENODEV; + ath10k_err("Unkown device ID: %d\n", pci_dev->device); + goto err_ar_pci; + } + + ath10k_pci_dump_features(ar_pci); + + ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI, + &ath10k_pci_hif_ops); + if (!ar) { + ath10k_err("ath10k_core_create failed!\n"); + ret = -EINVAL; + goto err_ar_pci; + } + + /* Enable QCA988X_1.0 HW workarounds */ + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) + spin_lock_init(&ar_pci->hw_v1_workaround_lock); + + ar_pci->ar = ar; + ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS; + atomic_set(&ar_pci->keep_awake_count, 0); + + pci_set_drvdata(pdev, ar); + + /* + * Without any knowledge of the Host, the Target may have been reset or + * power cycled and its Config Space may no longer reflect the PCI + * address space that was assigned earlier by the PCI infrastructure. + * Refresh it now. + */ + ret = pci_assign_resource(pdev, BAR_NUM); + if (ret) { + ath10k_err("cannot assign PCI space: %d\n", ret); + goto err_ar; + } + + ret = pci_enable_device(pdev); + if (ret) { + ath10k_err("cannot enable PCI device: %d\n", ret); + goto err_ar; + } + + /* Request MMIO resources */ + ret = pci_request_region(pdev, BAR_NUM, "ath"); + if (ret) { + ath10k_err("PCI MMIO reservation error: %d\n", ret); + goto err_device; + } + + /* + * Target structures have a limit of 32 bit DMA pointers. + * DMA pointers can be wider than 32 bits by default on some systems. + */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + ath10k_err("32-bit DMA not available: %d\n", ret); + goto err_region; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + ath10k_err("cannot enable 32-bit consistent DMA\n"); + goto err_region; + } + + /* Set bus master bit in PCI_COMMAND to enable DMA */ + pci_set_master(pdev); + + /* + * Temporary FIX: disable ASPM + * Will be removed after the OTP is programmed + */ + pci_read_config_dword(pdev, 0x80, &lcr_val); + pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00)); + + /* Arrange for access to Target SoC registers. */ + mem = pci_iomap(pdev, BAR_NUM, 0); + if (!mem) { + ath10k_err("PCI iomap error\n"); + ret = -EIO; + goto err_master; + } + + ar_pci->mem = mem; + + spin_lock_init(&ar_pci->ce_lock); + + ar_pci->cacheline_sz = dma_get_cache_alignment(); + + ret = ath10k_pci_start_intr(ar); + if (ret) { + ath10k_err("could not start interrupt handling (%d)\n", ret); + goto err_iomap; + } + + /* + * Bring the target up cleanly. + * + * The target may be in an undefined state with an AUX-powered Target + * and a Host in WoW mode. If the Host crashes, loses power, or is + * restarted (without unloading the driver) then the Target is left + * (aux) powered and running. On a subsequent driver load, the Target + * is in an unexpected state. We try to catch that here in order to + * reset the Target and retry the probe. + */ + ath10k_pci_device_reset(ar_pci); + + ret = ath10k_pci_reset_target(ar); + if (ret) + goto err_intr; + + if (ath10k_target_ps) { + ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n"); + } else { + /* Force AWAKE forever */ + ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n"); + ath10k_do_pci_wake(ar); + } + + ret = ath10k_pci_ce_init(ar); + if (ret) + goto err_intr; + + ret = ath10k_pci_init_config(ar); + if (ret) + goto err_ce; + + ret = ath10k_pci_wake_target_cpu(ar); + if (ret) { + ath10k_err("could not wake up target CPU (%d)\n", ret); + goto err_ce; + } + + ret = ath10k_core_register(ar); + if (ret) { + ath10k_err("could not register driver core (%d)\n", ret); + goto err_ce; + } + + return 0; + +err_ce: + ath10k_pci_ce_deinit(ar); +err_intr: + ath10k_pci_stop_intr(ar); +err_iomap: + pci_iounmap(pdev, mem); +err_master: + pci_clear_master(pdev); +err_region: + pci_release_region(pdev, BAR_NUM); +err_device: + pci_disable_device(pdev); +err_ar: + pci_set_drvdata(pdev, NULL); + ath10k_core_destroy(ar); +err_ar_pci: + /* call HIF PCI free here */ + kfree(ar_pci); + + return ret; +} + +static void ath10k_pci_remove(struct pci_dev *pdev) +{ + struct ath10k *ar = pci_get_drvdata(pdev); + struct ath10k_pci *ar_pci; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + if (!ar) + return; + + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci) + return; + + tasklet_kill(&ar_pci->msi_fw_err); + + ath10k_core_unregister(ar); + ath10k_pci_stop_intr(ar); + + pci_set_drvdata(pdev, NULL); + pci_iounmap(pdev, ar_pci->mem); + pci_release_region(pdev, BAR_NUM); + pci_clear_master(pdev); + pci_disable_device(pdev); + + ath10k_core_destroy(ar); + kfree(ar_pci); +} + +#if defined(CONFIG_PM_SLEEP) + +#define ATH10K_PCI_PM_CONTROL 0x44 + +static int ath10k_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct ath10k *ar = pci_get_drvdata(pdev); + struct ath10k_pci *ar_pci; + u32 val; + int ret, retval; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + if (!ar) + return -ENODEV; + + ar_pci = ath10k_pci_priv(ar); + if (!ar_pci) + return -ENODEV; + + if (ath10k_core_target_suspend(ar)) + return -EBUSY; + + ret = wait_event_interruptible_timeout(ar->event_queue, + ar->is_target_paused == true, + 1 * HZ); + if (ret < 0) { + ath10k_warn("suspend interrupted (%d)\n", ret); + retval = ret; + goto resume; + } else if (ret == 0) { + ath10k_warn("suspend timed out - target pause event never came\n"); + retval = EIO; + goto resume; + } + + /* + * reset is_target_paused and host can check that in next time, + * or it will always be TRUE and host just skip the waiting + * condition, it causes target assert due to host already + * suspend + */ + ar->is_target_paused = false; + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0x3) { + pci_save_state(pdev); + pci_disable_device(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + (val & 0xffffff00) | 0x03); + } + + return 0; +resume: + ret = ath10k_core_target_resume(ar); + if (ret) + ath10k_warn("could not resume (%d)\n", ret); + + return retval; +} + +static int ath10k_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct ath10k *ar = pci_get_drvdata(pdev); + struct ath10k_pci *ar_pci; + int ret; + u32 val; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + if (!ar) + return -ENODEV; + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci) + return -ENODEV; + + ret = pci_enable_device(pdev); + if (ret) { + ath10k_warn("cannot enable PCI device: %d\n", ret); + return ret; + } + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0) { + pci_restore_state(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + val & 0xffffff00); + /* + * Suspend/Resume resets the PCI configuration space, + * so we have to re-disable the RETRY_TIMEOUT register (0x41) + * to keep PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + } + + ret = ath10k_core_target_resume(ar); + if (ret) + ath10k_warn("target resume failed: %d\n", ret); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops, + ath10k_pci_suspend, + ath10k_pci_resume); + +#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops) + +#else + +#define ATH10K_PCI_PM_OPS NULL + +#endif /* CONFIG_PM_SLEEP */ + +MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table); + +static struct pci_driver ath10k_pci_driver = { + .name = "ath10k_pci", + .id_table = ath10k_pci_id_table, + .probe = ath10k_pci_probe, + .remove = ath10k_pci_remove, + .driver.pm = ATH10K_PCI_PM_OPS, +}; + +static int __init ath10k_pci_init(void) +{ + int ret; + + ret = pci_register_driver(&ath10k_pci_driver); + if (ret) + ath10k_err("pci_register_driver failed [%d]\n", ret); + + return ret; +} +module_init(ath10k_pci_init); + +static void __exit ath10k_pci_exit(void) +{ + pci_unregister_driver(&ath10k_pci_driver); +} + +module_exit(ath10k_pci_exit); + +MODULE_AUTHOR("Qualcomm Atheros"); +MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE); +MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE); +MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h new file mode 100644 index 00000000000..d2a055a07dc --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PCI_H_ +#define _PCI_H_ + +#include <linux/interrupt.h> + +#include "hw.h" +#include "ce.h" + +/* FW dump area */ +#define REG_DUMP_COUNT_QCA988X 60 + +/* + * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite + */ +#define DIAG_TRANSFER_LIMIT 2048 + +/* + * maximum number of bytes that can be + * handled atomically by DiagRead/DiagWrite + */ +#define DIAG_TRANSFER_LIMIT 2048 + +struct bmi_xfer { + struct completion done; + bool wait_for_resp; + u32 resp_len; +}; + +struct ath10k_pci_compl { + struct list_head list; + int send_or_recv; + struct ce_state *ce_state; + struct hif_ce_pipe_info *pipe_info; + void *transfer_context; + unsigned int nbytes; + unsigned int transfer_id; + unsigned int flags; +}; + +/* compl_state.send_or_recv */ +#define HIF_CE_COMPLETE_FREE 0 +#define HIF_CE_COMPLETE_SEND 1 +#define HIF_CE_COMPLETE_RECV 2 + +/* + * PCI-specific Target state + * + * NOTE: Structure is shared between Host software and Target firmware! + * + * Much of this may be of interest to the Host so + * HOST_INTEREST->hi_interconnect_state points here + * (and all members are 32-bit quantities in order to + * facilitate Host access). In particular, Host software is + * required to initialize pipe_cfg_addr and svc_to_pipe_map. + */ +struct pcie_state { + /* Pipe configuration Target address */ + /* NB: ce_pipe_config[CE_COUNT] */ + u32 pipe_cfg_addr; + + /* Service to pipe map Target address */ + /* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */ + u32 svc_to_pipe_map; + + /* number of MSI interrupts requested */ + u32 msi_requested; + + /* number of MSI interrupts granted */ + u32 msi_granted; + + /* Message Signalled Interrupt address */ + u32 msi_addr; + + /* Base data */ + u32 msi_data; + + /* + * Data for firmware interrupt; + * MSI data for other interrupts are + * in various SoC registers + */ + u32 msi_fw_intr_data; + + /* PCIE_PWR_METHOD_* */ + u32 power_mgmt_method; + + /* PCIE_CONFIG_FLAG_* */ + u32 config_flags; +}; + +/* PCIE_CONFIG_FLAG definitions */ +#define PCIE_CONFIG_FLAG_ENABLE_L1 0x0000001 + +/* Host software's Copy Engine configuration. */ +#define CE_ATTR_FLAGS 0 + +/* + * Configuration information for a Copy Engine pipe. + * Passed from Host to Target during startup (one per CE). + * + * NOTE: Structure is shared between Host software and Target firmware! + */ +struct ce_pipe_config { + u32 pipenum; + u32 pipedir; + u32 nentries; + u32 nbytes_max; + u32 flags; + u32 reserved; +}; + +/* + * Directions for interconnect pipe configuration. + * These definitions may be used during configuration and are shared + * between Host and Target. + * + * Pipe Directions are relative to the Host, so PIPEDIR_IN means + * "coming IN over air through Target to Host" as with a WiFi Rx operation. + * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" + * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" + * Target since things that are "PIPEDIR_OUT" are coming IN to the Target + * over the interconnect. + */ +#define PIPEDIR_NONE 0 +#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ +#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ +#define PIPEDIR_INOUT 3 /* bidirectional */ + +/* Establish a mapping between a service/direction and a pipe. */ +struct service_to_pipe { + u32 service_id; + u32 pipedir; + u32 pipenum; +}; + +enum ath10k_pci_features { + ATH10K_PCI_FEATURE_MSI_X = 0, + ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1, + + /* keep last */ + ATH10K_PCI_FEATURE_COUNT +}; + +/* Per-pipe state. */ +struct hif_ce_pipe_info { + /* Handle of underlying Copy Engine */ + struct ce_state *ce_hdl; + + /* Our pipe number; facilitiates use of pipe_info ptrs. */ + u8 pipe_num; + + /* Convenience back pointer to hif_ce_state. */ + struct ath10k *hif_ce_state; + + size_t buf_sz; + + /* protects compl_free and num_send_allowed */ + spinlock_t pipe_lock; + + /* List of free CE completion slots */ + struct list_head compl_free; + + /* Limit the number of outstanding send requests. */ + int num_sends_allowed; + + struct ath10k_pci *ar_pci; + struct tasklet_struct intr; +}; + +struct ath10k_pci { + struct pci_dev *pdev; + struct device *dev; + struct ath10k *ar; + void __iomem *mem; + int cacheline_sz; + + DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT); + + /* + * Number of MSI interrupts granted, 0 --> using legacy PCI line + * interrupts. + */ + int num_msi_intrs; + + struct tasklet_struct intr_tq; + struct tasklet_struct msi_fw_err; + + /* Number of Copy Engines supported */ + unsigned int ce_count; + + int started; + + atomic_t keep_awake_count; + bool verified_awake; + + /* List of CE completions to be processed */ + struct list_head compl_process; + + /* protects compl_processing and compl_process */ + spinlock_t compl_lock; + + bool compl_processing; + + struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX]; + + struct ath10k_hif_cb msg_callbacks_current; + + /* Target address used to signal a pending firmware event */ + u32 fw_indicator_address; + + /* Copy Engine used for Diagnostic Accesses */ + struct ce_state *ce_diag; + + /* FIXME: document what this really protects */ + spinlock_t ce_lock; + + /* Map CE id to ce_state */ + struct ce_state *ce_id_to_state[CE_COUNT_MAX]; + + /* makes sure that dummy reads are atomic */ + spinlock_t hw_v1_workaround_lock; +}; + +static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) +{ + return ar->hif.priv; +} + +static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr) +{ + return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr); +} + +static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val) +{ + iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr); +} + +#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ +#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */ + +#define BAR_NUM 0 + +#define CDC_WAR_MAGIC_STR 0xceef0000 +#define CDC_WAR_DATA_CE 4 + +/* + * TODO: Should be a function call specific to each Target-type. + * This convoluted macro converts from Target CPU Virtual Address Space to CE + * Address Space. As part of this process, we conservatively fetch the current + * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space + * for this device; but that's not guaranteed. + */ +#define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr) \ + (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS| \ + CORE_CTRL_ADDRESS)) & 0x7ff) << 21) | \ + 0x100000 | ((addr) & 0xfffff)) + +/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ +#define DIAG_ACCESS_CE_TIMEOUT_MS 10 + +/* + * This API allows the Host to access Target registers directly + * and relatively efficiently over PCIe. + * This allows the Host to avoid extra overhead associated with + * sending a message to firmware and waiting for a response message + * from firmware, as is done on other interconnects. + * + * Yet there is some complexity with direct accesses because the + * Target's power state is not known a priori. The Host must issue + * special PCIe reads/writes in order to explicitly wake the Target + * and to verify that it is awake and will remain awake. + * + * Usage: + * + * Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space. + * These calls must be bracketed by ath10k_pci_wake and + * ath10k_pci_sleep. A single BEGIN/END pair is adequate for + * multiple READ/WRITE operations. + * + * Use ath10k_pci_wake to put the Target in a state in + * which it is legal for the Host to directly access it. This + * may involve waking the Target from a low power state, which + * may take up to 2Ms! + * + * Use ath10k_pci_sleep to tell the Target that as far as + * this code path is concerned, it no longer needs to remain + * directly accessible. BEGIN/END is under a reference counter; + * multiple code paths may issue BEGIN/END on a single targid. + */ +static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, + u32 value) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *addr = ar_pci->mem; + + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + unsigned long irq_flags; + + spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); + + ioread32(addr+offset+4); /* 3rd read prior to write */ + ioread32(addr+offset+4); /* 2nd read prior to write */ + ioread32(addr+offset+4); /* 1st read prior to write */ + iowrite32(value, addr+offset); + + spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock, + irq_flags); + } else { + iowrite32(value, addr+offset); + } +} + +static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + return ioread32(ar_pci->mem + offset); +} + +extern unsigned int ath10k_target_ps; + +void ath10k_do_pci_wake(struct ath10k *ar); +void ath10k_do_pci_sleep(struct ath10k *ar); + +static inline void ath10k_pci_wake(struct ath10k *ar) +{ + if (ath10k_target_ps) + ath10k_do_pci_wake(ar); +} + +static inline void ath10k_pci_sleep(struct ath10k *ar) +{ + if (ath10k_target_ps) + ath10k_do_pci_sleep(ar); +} + +#endif /* _PCI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h new file mode 100644 index 00000000000..bfec6c8f2ec --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -0,0 +1,990 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _RX_DESC_H_ +#define _RX_DESC_H_ + +enum rx_attention_flags { + RX_ATTENTION_FLAGS_FIRST_MPDU = 1 << 0, + RX_ATTENTION_FLAGS_LAST_MPDU = 1 << 1, + RX_ATTENTION_FLAGS_MCAST_BCAST = 1 << 2, + RX_ATTENTION_FLAGS_PEER_IDX_INVALID = 1 << 3, + RX_ATTENTION_FLAGS_PEER_IDX_TIMEOUT = 1 << 4, + RX_ATTENTION_FLAGS_POWER_MGMT = 1 << 5, + RX_ATTENTION_FLAGS_NON_QOS = 1 << 6, + RX_ATTENTION_FLAGS_NULL_DATA = 1 << 7, + RX_ATTENTION_FLAGS_MGMT_TYPE = 1 << 8, + RX_ATTENTION_FLAGS_CTRL_TYPE = 1 << 9, + RX_ATTENTION_FLAGS_MORE_DATA = 1 << 10, + RX_ATTENTION_FLAGS_EOSP = 1 << 11, + RX_ATTENTION_FLAGS_U_APSD_TRIGGER = 1 << 12, + RX_ATTENTION_FLAGS_FRAGMENT = 1 << 13, + RX_ATTENTION_FLAGS_ORDER = 1 << 14, + RX_ATTENTION_FLAGS_CLASSIFICATION = 1 << 15, + RX_ATTENTION_FLAGS_OVERFLOW_ERR = 1 << 16, + RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR = 1 << 17, + RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL = 1 << 18, + RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL = 1 << 19, + RX_ATTENTION_FLAGS_SA_IDX_INVALID = 1 << 20, + RX_ATTENTION_FLAGS_DA_IDX_INVALID = 1 << 21, + RX_ATTENTION_FLAGS_SA_IDX_TIMEOUT = 1 << 22, + RX_ATTENTION_FLAGS_DA_IDX_TIMEOUT = 1 << 23, + RX_ATTENTION_FLAGS_ENCRYPT_REQUIRED = 1 << 24, + RX_ATTENTION_FLAGS_DIRECTED = 1 << 25, + RX_ATTENTION_FLAGS_BUFFER_FRAGMENT = 1 << 26, + RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR = 1 << 27, + RX_ATTENTION_FLAGS_TKIP_MIC_ERR = 1 << 28, + RX_ATTENTION_FLAGS_DECRYPT_ERR = 1 << 29, + RX_ATTENTION_FLAGS_FCS_ERR = 1 << 30, + RX_ATTENTION_FLAGS_MSDU_DONE = 1 << 31, +}; + +struct rx_attention { + __le32 flags; /* %RX_ATTENTION_FLAGS_ */ +} __packed; + +/* + * first_mpdu + * Indicates the first MSDU of the PPDU. If both first_mpdu + * and last_mpdu are set in the MSDU then this is a not an + * A-MPDU frame but a stand alone MPDU. Interior MPDU in an + * A-MPDU shall have both first_mpdu and last_mpdu bits set to + * 0. The PPDU start status will only be valid when this bit + * is set. + * + * last_mpdu + * Indicates the last MSDU of the last MPDU of the PPDU. The + * PPDU end status will only be valid when this bit is set. + * + * mcast_bcast + * Multicast / broadcast indicator. Only set when the MAC + * address 1 bit 0 is set indicating mcast/bcast and the BSSID + * matches one of the 4 BSSID registers. Only set when + * first_msdu is set. + * + * peer_idx_invalid + * Indicates no matching entries within the the max search + * count. Only set when first_msdu is set. + * + * peer_idx_timeout + * Indicates an unsuccessful search for the peer index due to + * timeout. Only set when first_msdu is set. + * + * power_mgmt + * Power management bit set in the 802.11 header. Only set + * when first_msdu is set. + * + * non_qos + * Set if packet is not a non-QoS data frame. Only set when + * first_msdu is set. + * + * null_data + * Set if frame type indicates either null data or QoS null + * data format. Only set when first_msdu is set. + * + * mgmt_type + * Set if packet is a management packet. Only set when + * first_msdu is set. + * + * ctrl_type + * Set if packet is a control packet. Only set when first_msdu + * is set. + * + * more_data + * Set if more bit in frame control is set. Only set when + * first_msdu is set. + * + * eosp + * Set if the EOSP (end of service period) bit in the QoS + * control field is set. Only set when first_msdu is set. + * + * u_apsd_trigger + * Set if packet is U-APSD trigger. Key table will have bits + * per TID to indicate U-APSD trigger. + * + * fragment + * Indicates that this is an 802.11 fragment frame. This is + * set when either the more_frag bit is set in the frame + * control or the fragment number is not zero. Only set when + * first_msdu is set. + * + * order + * Set if the order bit in the frame control is set. Only set + * when first_msdu is set. + * + * classification + * Indicates that this status has a corresponding MSDU that + * requires FW processing. The OLE will have classification + * ring mask registers which will indicate the ring(s) for + * packets and descriptors which need FW attention. + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * msdu_length_err + * Indicates that the MSDU length from the 802.3 encapsulated + * length field extends beyond the MPDU boundary. + * + * tcp_udp_chksum_fail + * Indicates that the computed checksum (tcp_udp_chksum) did + * not match the checksum in the TCP/UDP header. + * + * ip_chksum_fail + * Indicates that the computed checksum did not match the + * checksum in the IP header. + * + * sa_idx_invalid + * Indicates no matching entry was found in the address search + * table for the source MAC address. + * + * da_idx_invalid + * Indicates no matching entry was found in the address search + * table for the destination MAC address. + * + * sa_idx_timeout + * Indicates an unsuccessful search for the source MAC address + * due to the expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful search for the destination MAC + * address due to the expiring of the search timer. + * + * encrypt_required + * Indicates that this data type frame is not encrypted even if + * the policy for this MPDU requires encryption as indicated in + * the peer table key type. + * + * directed + * MPDU is a directed packet which means that the RA matched + * our STA addresses. In proxySTA it means that the TA matched + * an entry in our address search table with the corresponding + * 'no_ack' bit is the address search entry cleared. + * + * buffer_fragment + * Indicates that at least one of the rx buffers has been + * fragmented. If set the FW should look at the rx_frag_info + * descriptor described below. + * + * mpdu_length_err + * Indicates that the MPDU was pre-maturely terminated + * resulting in a truncated MPDU. Don't trust the MPDU length + * field. + * + * tkip_mic_err + * Indicates that the MPDU Michael integrity check failed + * + * decrypt_err + * Indicates that the MPDU decrypt integrity check failed + * + * fcs_err + * Indicates that the MPDU FCS check failed + * + * msdu_done + * If set indicates that the RX packet data, RX header data, RX + * PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU + * start/end descriptors and RX Attention descriptor are all + * valid. This bit must be in the last octet of the + * descriptor. + */ + +struct rx_frag_info { + u8 ring0_more_count; + u8 ring1_more_count; + u8 ring2_more_count; + u8 ring3_more_count; +} __packed; + +/* + * ring0_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 0. Field is filled in by the RX_DMA. + * + * ring1_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 1. Field is filled in by the RX_DMA. + * + * ring2_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 2. Field is filled in by the RX_DMA. + * + * ring3_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 3. Field is filled in by the RX_DMA. + */ + +enum htt_rx_mpdu_encrypt_type { + HTT_RX_MPDU_ENCRYPT_WEP40 = 0, + HTT_RX_MPDU_ENCRYPT_WEP104 = 1, + HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC = 2, + HTT_RX_MPDU_ENCRYPT_WEP128 = 3, + HTT_RX_MPDU_ENCRYPT_TKIP_WPA = 4, + HTT_RX_MPDU_ENCRYPT_WAPI = 5, + HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2 = 6, + HTT_RX_MPDU_ENCRYPT_NONE = 7, +}; + +#define RX_MPDU_START_INFO0_PEER_IDX_MASK 0x000007ff +#define RX_MPDU_START_INFO0_PEER_IDX_LSB 0 +#define RX_MPDU_START_INFO0_SEQ_NUM_MASK 0x0fff0000 +#define RX_MPDU_START_INFO0_SEQ_NUM_LSB 16 +#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_MASK 0xf0000000 +#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_LSB 28 +#define RX_MPDU_START_INFO0_FROM_DS (1 << 11) +#define RX_MPDU_START_INFO0_TO_DS (1 << 12) +#define RX_MPDU_START_INFO0_ENCRYPTED (1 << 13) +#define RX_MPDU_START_INFO0_RETRY (1 << 14) +#define RX_MPDU_START_INFO0_TXBF_H_INFO (1 << 15) + +#define RX_MPDU_START_INFO1_TID_MASK 0xf0000000 +#define RX_MPDU_START_INFO1_TID_LSB 28 +#define RX_MPDU_START_INFO1_DIRECTED (1 << 16) + +struct rx_mpdu_start { + __le32 info0; + union { + struct { + __le32 pn31_0; + __le32 info1; /* %RX_MPDU_START_INFO1_ */ + } __packed; + struct { + u8 pn[6]; + } __packed; + } __packed; +} __packed; + +/* + * peer_idx + * The index of the address search table which associated with + * the peer table entry corresponding to this MPDU. Only valid + * when first_msdu is set. + * + * fr_ds + * Set if the from DS bit is set in the frame control. Only + * valid when first_msdu is set. + * + * to_ds + * Set if the to DS bit is set in the frame control. Only + * valid when first_msdu is set. + * + * encrypted + * Protected bit from the frame control. Only valid when + * first_msdu is set. + * + * retry + * Retry bit from the frame control. Only valid when + * first_msdu is set. + * + * txbf_h_info + * The MPDU data will contain H information. Primarily used + * for debug. + * + * seq_num + * The sequence number from the 802.11 header. Only valid when + * first_msdu is set. + * + * encrypt_type + * Indicates type of decrypt cipher used (as defined in the + * peer table) + * 0: WEP40 + * 1: WEP104 + * 2: TKIP without MIC + * 3: WEP128 + * 4: TKIP (WPA) + * 5: WAPI + * 6: AES-CCM (WPA2) + * 7: No cipher + * Only valid when first_msdu_is set + * + * pn_31_0 + * Bits [31:0] of the PN number extracted from the IV field + * WEP: IV = {key_id_octet, pn2, pn1, pn0}. Only pn[23:0] is + * valid. + * TKIP: IV = {pn5, pn4, pn3, pn2, key_id_octet, pn0, + * WEPSeed[1], pn1}. Only pn[47:0] is valid. + * AES-CCM: IV = {pn5, pn4, pn3, pn2, key_id_octet, 0x0, pn1, + * pn0}. Only pn[47:0] is valid. + * WAPI: IV = {key_id_octet, 0x0, pn15, pn14, pn13, pn12, pn11, + * pn10, pn9, pn8, pn7, pn6, pn5, pn4, pn3, pn2, pn1, pn0}. + * The ext_wapi_pn[127:48] in the rx_msdu_misc descriptor and + * pn[47:0] are valid. + * Only valid when first_msdu is set. + * + * pn_47_32 + * Bits [47:32] of the PN number. See description for + * pn_31_0. The remaining PN fields are in the rx_msdu_end + * descriptor + * + * pn + * Use this field to access the pn without worrying about + * byte-order and bitmasking/bitshifting. + * + * directed + * See definition in RX attention descriptor + * + * reserved_2 + * Reserved: HW should fill with zero. FW should ignore. + * + * tid + * The TID field in the QoS control field + */ + +#define RX_MPDU_END_INFO0_RESERVED_0_MASK 0x00001fff +#define RX_MPDU_END_INFO0_RESERVED_0_LSB 0 +#define RX_MPDU_END_INFO0_POST_DELIM_CNT_MASK 0x0fff0000 +#define RX_MPDU_END_INFO0_POST_DELIM_CNT_LSB 16 +#define RX_MPDU_END_INFO0_OVERFLOW_ERR (1 << 13) +#define RX_MPDU_END_INFO0_LAST_MPDU (1 << 14) +#define RX_MPDU_END_INFO0_POST_DELIM_ERR (1 << 15) +#define RX_MPDU_END_INFO0_MPDU_LENGTH_ERR (1 << 28) +#define RX_MPDU_END_INFO0_TKIP_MIC_ERR (1 << 29) +#define RX_MPDU_END_INFO0_DECRYPT_ERR (1 << 30) +#define RX_MPDU_END_INFO0_FCS_ERR (1 << 31) + +struct rx_mpdu_end { + __le32 info0; +} __packed; + +/* + * reserved_0 + * Reserved + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * last_mpdu + * Indicates that this is the last MPDU of a PPDU. + * + * post_delim_err + * Indicates that a delimiter FCS error occurred after this + * MPDU before the next MPDU. Only valid when last_msdu is + * set. + * + * post_delim_cnt + * Count of the delimiters after this MPDU. This requires the + * last MPDU to be held until all the EOF descriptors have been + * received. This may be inefficient in the future when + * ML-MIMO is used. Only valid when last_mpdu is set. + * + * mpdu_length_err + * See definition in RX attention descriptor + * + * tkip_mic_err + * See definition in RX attention descriptor + * + * decrypt_err + * See definition in RX attention descriptor + * + * fcs_err + * See definition in RX attention descriptor + */ + +#define RX_MSDU_START_INFO0_MSDU_LENGTH_MASK 0x00003fff +#define RX_MSDU_START_INFO0_MSDU_LENGTH_LSB 0 +#define RX_MSDU_START_INFO0_IP_OFFSET_MASK 0x000fc000 +#define RX_MSDU_START_INFO0_IP_OFFSET_LSB 14 +#define RX_MSDU_START_INFO0_RING_MASK_MASK 0x00f00000 +#define RX_MSDU_START_INFO0_RING_MASK_LSB 20 +#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_MASK 0x7f000000 +#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_LSB 24 + +#define RX_MSDU_START_INFO1_MSDU_NUMBER_MASK 0x000000ff +#define RX_MSDU_START_INFO1_MSDU_NUMBER_LSB 0 +#define RX_MSDU_START_INFO1_DECAP_FORMAT_MASK 0x00000300 +#define RX_MSDU_START_INFO1_DECAP_FORMAT_LSB 8 +#define RX_MSDU_START_INFO1_SA_IDX_MASK 0x07ff0000 +#define RX_MSDU_START_INFO1_SA_IDX_LSB 16 +#define RX_MSDU_START_INFO1_IPV4_PROTO (1 << 10) +#define RX_MSDU_START_INFO1_IPV6_PROTO (1 << 11) +#define RX_MSDU_START_INFO1_TCP_PROTO (1 << 12) +#define RX_MSDU_START_INFO1_UDP_PROTO (1 << 13) +#define RX_MSDU_START_INFO1_IP_FRAG (1 << 14) +#define RX_MSDU_START_INFO1_TCP_ONLY_ACK (1 << 15) + +enum rx_msdu_decap_format { + RX_MSDU_DECAP_RAW = 0, + RX_MSDU_DECAP_NATIVE_WIFI = 1, + RX_MSDU_DECAP_ETHERNET2_DIX = 2, + RX_MSDU_DECAP_8023_SNAP_LLC = 3 +}; + +struct rx_msdu_start { + __le32 info0; /* %RX_MSDU_START_INFO0_ */ + __le32 flow_id_crc; + __le32 info1; /* %RX_MSDU_START_INFO1_ */ +} __packed; + +/* + * msdu_length + * MSDU length in bytes after decapsulation. This field is + * still valid for MPDU frames without A-MSDU. It still + * represents MSDU length after decapsulation + * + * ip_offset + * Indicates the IP offset in bytes from the start of the + * packet after decapsulation. Only valid if ipv4_proto or + * ipv6_proto is set. + * + * ring_mask + * Indicates the destination RX rings for this MSDU. + * + * tcp_udp_offset + * Indicates the offset in bytes to the start of TCP or UDP + * header from the start of the IP header after decapsulation. + * Only valid if tcp_prot or udp_prot is set. The value 0 + * indicates that the offset is longer than 127 bytes. + * + * reserved_0c + * Reserved: HW should fill with zero. FW should ignore. + * + * flow_id_crc + * The flow_id_crc runs CRC32 on the following information: + * IPv4 option: dest_addr[31:0], src_addr [31:0], {24'b0, + * protocol[7:0]}. + * IPv6 option: dest_addr[127:0], src_addr [127:0], {24'b0, + * next_header[7:0]} + * UDP case: sort_port[15:0], dest_port[15:0] + * TCP case: sort_port[15:0], dest_port[15:0], + * {header_length[3:0], 6'b0, flags[5:0], window_size[15:0]}, + * {16'b0, urgent_ptr[15:0]}, all options except 32-bit + * timestamp. + * + * msdu_number + * Indicates the MSDU number within a MPDU. This value is + * reset to zero at the start of each MPDU. If the number of + * MSDU exceeds 255 this number will wrap using modulo 256. + * + * decap_format + * Indicates the format after decapsulation: + * 0: RAW: No decapsulation + * 1: Native WiFi + * 2: Ethernet 2 (DIX) + * 3: 802.3 (SNAP/LLC) + * + * ipv4_proto + * Set if L2 layer indicates IPv4 protocol. + * + * ipv6_proto + * Set if L2 layer indicates IPv6 protocol. + * + * tcp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP + * protocol indicates TCP. + * + * udp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP + * protocol indicates UDP. + * + * ip_frag + * Indicates that either the IP More frag bit is set or IP frag + * number is non-zero. If set indicates that this is a + * fragmented IP packet. + * + * tcp_only_ack + * Set if only the TCP Ack bit is set in the TCP flags and if + * the TCP payload is 0. + * + * sa_idx + * The offset in the address table which matches the MAC source + * address. + * + * reserved_2b + * Reserved: HW should fill with zero. FW should ignore. + */ + +#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_MASK 0x00003fff +#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB 0 +#define RX_MSDU_END_INFO0_FIRST_MSDU (1 << 14) +#define RX_MSDU_END_INFO0_LAST_MSDU (1 << 15) +#define RX_MSDU_END_INFO0_PRE_DELIM_ERR (1 << 30) +#define RX_MSDU_END_INFO0_RESERVED_3B (1 << 31) + +struct rx_msdu_end { + __le16 ip_hdr_cksum; + __le16 tcp_hdr_cksum; + u8 key_id_octet; + u8 classification_filter; + u8 wapi_pn[10]; + __le32 info0; +} __packed; + +/* + *ip_hdr_chksum + * This can include the IP header checksum or the pseudo header + * checksum used by TCP/UDP checksum. + * + *tcp_udp_chksum + * The value of the computed TCP/UDP checksum. A mode bit + * selects whether this checksum is the full checksum or the + * partial checksum which does not include the pseudo header. + * + *key_id_octet + * The key ID octet from the IV. Only valid when first_msdu is + * set. + * + *classification_filter + * Indicates the number classification filter rule + * + *ext_wapi_pn_63_48 + * Extension PN (packet number) which is only used by WAPI. + * This corresponds to WAPI PN bits [63:48] (pn6 and pn7). The + * WAPI PN bits [63:0] are in the pn field of the rx_mpdu_start + * descriptor. + * + *ext_wapi_pn_95_64 + * Extension PN (packet number) which is only used by WAPI. + * This corresponds to WAPI PN bits [95:64] (pn8, pn9, pn10 and + * pn11). + * + *ext_wapi_pn_127_96 + * Extension PN (packet number) which is only used by WAPI. + * This corresponds to WAPI PN bits [127:96] (pn12, pn13, pn14, + * pn15). + * + *reported_mpdu_length + * MPDU length before decapsulation. Only valid when + * first_msdu is set. This field is taken directly from the + * length field of the A-MPDU delimiter or the preamble length + * field for non-A-MPDU frames. + * + *first_msdu + * Indicates the first MSDU of A-MSDU. If both first_msdu and + * last_msdu are set in the MSDU then this is a non-aggregated + * MSDU frame: normal MPDU. Interior MSDU in an A-MSDU shall + * have both first_mpdu and last_mpdu bits set to 0. + * + *last_msdu + * Indicates the last MSDU of the A-MSDU. MPDU end status is + * only valid when last_msdu is set. + * + *reserved_3a + * Reserved: HW should fill with zero. FW should ignore. + * + *pre_delim_err + * Indicates that the first delimiter had a FCS failure. Only + * valid when first_mpdu and first_msdu are set. + * + *reserved_3b + * Reserved: HW should fill with zero. FW should ignore. + */ + +#define RX_PPDU_START_SIG_RATE_SELECT_OFDM 0 +#define RX_PPDU_START_SIG_RATE_SELECT_CCK 1 + +#define RX_PPDU_START_SIG_RATE_OFDM_48 0 +#define RX_PPDU_START_SIG_RATE_OFDM_24 1 +#define RX_PPDU_START_SIG_RATE_OFDM_12 2 +#define RX_PPDU_START_SIG_RATE_OFDM_6 3 +#define RX_PPDU_START_SIG_RATE_OFDM_54 4 +#define RX_PPDU_START_SIG_RATE_OFDM_36 5 +#define RX_PPDU_START_SIG_RATE_OFDM_18 6 +#define RX_PPDU_START_SIG_RATE_OFDM_9 7 + +#define RX_PPDU_START_SIG_RATE_CCK_LP_11 0 +#define RX_PPDU_START_SIG_RATE_CCK_LP_5_5 1 +#define RX_PPDU_START_SIG_RATE_CCK_LP_2 2 +#define RX_PPDU_START_SIG_RATE_CCK_LP_1 3 +#define RX_PPDU_START_SIG_RATE_CCK_SP_11 4 +#define RX_PPDU_START_SIG_RATE_CCK_SP_5_5 5 +#define RX_PPDU_START_SIG_RATE_CCK_SP_2 6 + +#define HTT_RX_PPDU_START_PREAMBLE_LEGACY 0x04 +#define HTT_RX_PPDU_START_PREAMBLE_HT 0x08 +#define HTT_RX_PPDU_START_PREAMBLE_HT_WITH_TXBF 0x09 +#define HTT_RX_PPDU_START_PREAMBLE_VHT 0x0C +#define HTT_RX_PPDU_START_PREAMBLE_VHT_WITH_TXBF 0x0D + +#define RX_PPDU_START_INFO0_IS_GREENFIELD (1 << 0) + +#define RX_PPDU_START_INFO1_L_SIG_RATE_MASK 0x0000000f +#define RX_PPDU_START_INFO1_L_SIG_RATE_LSB 0 +#define RX_PPDU_START_INFO1_L_SIG_LENGTH_MASK 0x0001ffe0 +#define RX_PPDU_START_INFO1_L_SIG_LENGTH_LSB 5 +#define RX_PPDU_START_INFO1_L_SIG_TAIL_MASK 0x00fc0000 +#define RX_PPDU_START_INFO1_L_SIG_TAIL_LSB 18 +#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_MASK 0xff000000 +#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_LSB 24 +#define RX_PPDU_START_INFO1_L_SIG_RATE_SELECT (1 << 4) +#define RX_PPDU_START_INFO1_L_SIG_PARITY (1 << 17) + +#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_MASK 0x00ffffff +#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_LSB 0 + +#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_MASK 0x00ffffff +#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_LSB 0 +#define RX_PPDU_START_INFO3_TXBF_H_INFO (1 << 24) + +#define RX_PPDU_START_INFO4_VHT_SIG_B_MASK 0x1fffffff +#define RX_PPDU_START_INFO4_VHT_SIG_B_LSB 0 + +#define RX_PPDU_START_INFO5_SERVICE_MASK 0x0000ffff +#define RX_PPDU_START_INFO5_SERVICE_LSB 0 + +struct rx_ppdu_start { + struct { + u8 pri20_mhz; + u8 ext20_mhz; + u8 ext40_mhz; + u8 ext80_mhz; + } rssi_chains[4]; + u8 rssi_comb; + __le16 rsvd0; + u8 info0; /* %RX_PPDU_START_INFO0_ */ + __le32 info1; /* %RX_PPDU_START_INFO1_ */ + __le32 info2; /* %RX_PPDU_START_INFO2_ */ + __le32 info3; /* %RX_PPDU_START_INFO3_ */ + __le32 info4; /* %RX_PPDU_START_INFO4_ */ + __le32 info5; /* %RX_PPDU_START_INFO5_ */ +} __packed; + +/* + * rssi_chain0_pri20 + * RSSI of RX PPDU on chain 0 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain0_sec20 + * RSSI of RX PPDU on chain 0 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain0_sec40 + * RSSI of RX PPDU on chain 0 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain0_sec80 + * RSSI of RX PPDU on chain 0 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_pri20 + * RSSI of RX PPDU on chain 1 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_sec20 + * RSSI of RX PPDU on chain 1 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_sec40 + * RSSI of RX PPDU on chain 1 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_sec80 + * RSSI of RX PPDU on chain 1 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_pri20 + * RSSI of RX PPDU on chain 2 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_sec20 + * RSSI of RX PPDU on chain 2 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_sec40 + * RSSI of RX PPDU on chain 2 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_sec80 + * RSSI of RX PPDU on chain 2 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_pri20 + * RSSI of RX PPDU on chain 3 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_sec20 + * RSSI of RX PPDU on chain 3 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_sec40 + * RSSI of RX PPDU on chain 3 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_sec80 + * RSSI of RX PPDU on chain 3 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_comb + * The combined RSSI of RX PPDU of all active chains and + * bandwidths. Value of 0x80 indicates invalid. + * + * reserved_4a + * Reserved: HW should fill with 0, FW should ignore. + * + * is_greenfield + * Do we really support this? + * + * reserved_4b + * Reserved: HW should fill with 0, FW should ignore. + * + * l_sig_rate + * If l_sig_rate_select is 0: + * 0x8: OFDM 48 Mbps + * 0x9: OFDM 24 Mbps + * 0xA: OFDM 12 Mbps + * 0xB: OFDM 6 Mbps + * 0xC: OFDM 54 Mbps + * 0xD: OFDM 36 Mbps + * 0xE: OFDM 18 Mbps + * 0xF: OFDM 9 Mbps + * If l_sig_rate_select is 1: + * 0x8: CCK 11 Mbps long preamble + * 0x9: CCK 5.5 Mbps long preamble + * 0xA: CCK 2 Mbps long preamble + * 0xB: CCK 1 Mbps long preamble + * 0xC: CCK 11 Mbps short preamble + * 0xD: CCK 5.5 Mbps short preamble + * 0xE: CCK 2 Mbps short preamble + * + * l_sig_rate_select + * Legacy signal rate select. If set then l_sig_rate indicates + * CCK rates. If clear then l_sig_rate indicates OFDM rates. + * + * l_sig_length + * Length of legacy frame in octets. + * + * l_sig_parity + * Odd parity over l_sig_rate and l_sig_length + * + * l_sig_tail + * Tail bits for Viterbi decoder + * + * preamble_type + * Indicates the type of preamble ahead: + * 0x4: Legacy (OFDM/CCK) + * 0x8: HT + * 0x9: HT with TxBF + * 0xC: VHT + * 0xD: VHT with TxBF + * 0x80 - 0xFF: Reserved for special baseband data types such + * as radar and spectral scan. + * + * ht_sig_vht_sig_a_1 + * If preamble_type == 0x8 or 0x9 + * HT-SIG (first 24 bits) + * If preamble_type == 0xC or 0xD + * VHT-SIG A (first 24 bits) + * Else + * Reserved + * + * reserved_6 + * Reserved: HW should fill with 0, FW should ignore. + * + * ht_sig_vht_sig_a_2 + * If preamble_type == 0x8 or 0x9 + * HT-SIG (last 24 bits) + * If preamble_type == 0xC or 0xD + * VHT-SIG A (last 24 bits) + * Else + * Reserved + * + * txbf_h_info + * Indicates that the packet data carries H information which + * is used for TxBF debug. + * + * reserved_7 + * Reserved: HW should fill with 0, FW should ignore. + * + * vht_sig_b + * WiFi 1.0 and WiFi 2.0 will likely have this field to be all + * 0s since the BB does not plan on decoding VHT SIG-B. + * + * reserved_8 + * Reserved: HW should fill with 0, FW should ignore. + * + * service + * Service field from BB for OFDM, HT and VHT packets. CCK + * packets will have service field of 0. + * + * reserved_9 + * Reserved: HW should fill with 0, FW should ignore. +*/ + + +#define RX_PPDU_END_FLAGS_PHY_ERR (1 << 0) +#define RX_PPDU_END_FLAGS_RX_LOCATION (1 << 1) +#define RX_PPDU_END_FLAGS_TXBF_H_INFO (1 << 2) + +#define RX_PPDU_END_INFO0_RX_ANTENNA_MASK 0x00ffffff +#define RX_PPDU_END_INFO0_RX_ANTENNA_LSB 0 +#define RX_PPDU_END_INFO0_FLAGS_TX_HT_VHT_ACK (1 << 24) +#define RX_PPDU_END_INFO0_BB_CAPTURED_CHANNEL (1 << 25) + +#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15) + +struct rx_ppdu_end { + __le32 evm_p0; + __le32 evm_p1; + __le32 evm_p2; + __le32 evm_p3; + __le32 evm_p4; + __le32 evm_p5; + __le32 evm_p6; + __le32 evm_p7; + __le32 evm_p8; + __le32 evm_p9; + __le32 evm_p10; + __le32 evm_p11; + __le32 evm_p12; + __le32 evm_p13; + __le32 evm_p14; + __le32 evm_p15; + __le32 tsf_timestamp; + __le32 wb_timestamp; + u8 locationing_timestamp; + u8 phy_err_code; + __le16 flags; /* %RX_PPDU_END_FLAGS_ */ + __le32 info0; /* %RX_PPDU_END_INFO0_ */ + __le16 bb_length; + __le16 info1; /* %RX_PPDU_END_INFO1_ */ +} __packed; + +/* + * evm_p0 + * EVM for pilot 0. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p1 + * EVM for pilot 1. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p2 + * EVM for pilot 2. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p3 + * EVM for pilot 3. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p4 + * EVM for pilot 4. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p5 + * EVM for pilot 5. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p6 + * EVM for pilot 6. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p7 + * EVM for pilot 7. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p8 + * EVM for pilot 8. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p9 + * EVM for pilot 9. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p10 + * EVM for pilot 10. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p11 + * EVM for pilot 11. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p12 + * EVM for pilot 12. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p13 + * EVM for pilot 13. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p14 + * EVM for pilot 14. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p15 + * EVM for pilot 15. Contain EVM for streams: 0, 1, 2 and 3. + * + * tsf_timestamp + * Receive TSF timestamp sampled on the rising edge of + * rx_clear. For PHY errors this may be the current TSF when + * phy_error is asserted if the rx_clear does not assert before + * the end of the PHY error. + * + * wb_timestamp + * WLAN/BT timestamp is a 1 usec resolution timestamp which + * does not get updated based on receive beacon like TSF. The + * same rules for capturing tsf_timestamp are used to capture + * the wb_timestamp. + * + * locationing_timestamp + * Timestamp used for locationing. This timestamp is used to + * indicate fractions of usec. For example if the MAC clock is + * running at 80 MHz, the timestamp will increment every 12.5 + * nsec. The value starts at 0 and increments to 79 and + * returns to 0 and repeats. This information is valid for + * every PPDU. This information can be used in conjunction + * with wb_timestamp to capture large delta times. + * + * phy_err_code + * See the 1.10.8.1.2 for the list of the PHY error codes. + * + * phy_err + * Indicates a PHY error was detected for this PPDU. + * + * rx_location + * Indicates that location information was requested. + * + * txbf_h_info + * Indicates that the packet data carries H information which + * is used for TxBF debug. + * + * reserved_18 + * Reserved: HW should fill with 0, FW should ignore. + * + * rx_antenna + * Receive antenna value + * + * tx_ht_vht_ack + * Indicates that a HT or VHT Ack/BA frame was transmitted in + * response to this receive packet. + * + * bb_captured_channel + * Indicates that the BB has captured a channel dump. FW can + * then read the channel dump memory. This may indicate that + * the channel was captured either based on PCU setting the + * capture_channel bit BB descriptor or FW setting the + * capture_channel mode bit. + * + * reserved_19 + * Reserved: HW should fill with 0, FW should ignore. + * + * bb_length + * Indicates the number of bytes of baseband information for + * PPDUs where the BB descriptor preamble type is 0x80 to 0xFF + * which indicates that this is not a normal PPDU but rather + * contains baseband debug information. + * + * reserved_20 + * Reserved: HW should fill with 0, FW should ignore. + * + * ppdu_done + * PPDU end status is only valid when ppdu_done bit is set. + * Every time HW sets this bit in memory FW/SW must clear this + * bit in memory. FW will initialize all the ppdu_done dword + * to 0. +*/ + +#define FW_RX_DESC_INFO0_DISCARD (1 << 0) +#define FW_RX_DESC_INFO0_FORWARD (1 << 1) +#define FW_RX_DESC_INFO0_INSPECT (1 << 5) +#define FW_RX_DESC_INFO0_EXT_MASK 0xC0 +#define FW_RX_DESC_INFO0_EXT_LSB 6 + +struct fw_rx_desc_base { + u8 info0; +} __packed; + +#endif /* _RX_DESC_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h new file mode 100644 index 00000000000..be7ba1e78af --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __TARGADDRS_H__ +#define __TARGADDRS_H__ + +/* + * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the + * host_interest structure. It must match the address of the _host_interest + * symbol (see linker script). + * + * Host Interest is shared between Host and Target in order to coordinate + * between the two, and is intended to remain constant (with additions only + * at the end) across software releases. + * + * All addresses are available here so that it's possible to + * write a single binary that works with all Target Types. + * May be used in assembler code as well as C. + */ +#define QCA988X_HOST_INTEREST_ADDRESS 0x00400800 +#define HOST_INTEREST_MAX_SIZE 0x200 + +/* + * These are items that the Host may need to access via BMI or via the + * Diagnostic Window. The position of items in this structure must remain + * constant across firmware revisions! Types for each item must be fixed + * size across target and host platforms. More items may be added at the end. + */ +struct host_interest { + /* + * Pointer to application-defined area, if any. + * Set by Target application during startup. + */ + u32 hi_app_host_interest; /* 0x00 */ + + /* Pointer to register dump area, valid after Target crash. */ + u32 hi_failure_state; /* 0x04 */ + + /* Pointer to debug logging header */ + u32 hi_dbglog_hdr; /* 0x08 */ + + u32 hi_unused0c; /* 0x0c */ + + /* + * General-purpose flag bits, similar to SOC_OPTION_* flags. + * Can be used by application rather than by OS. + */ + u32 hi_option_flag; /* 0x10 */ + + /* + * Boolean that determines whether or not to + * display messages on the serial port. + */ + u32 hi_serial_enable; /* 0x14 */ + + /* Start address of DataSet index, if any */ + u32 hi_dset_list_head; /* 0x18 */ + + /* Override Target application start address */ + u32 hi_app_start; /* 0x1c */ + + /* Clock and voltage tuning */ + u32 hi_skip_clock_init; /* 0x20 */ + u32 hi_core_clock_setting; /* 0x24 */ + u32 hi_cpu_clock_setting; /* 0x28 */ + u32 hi_system_sleep_setting; /* 0x2c */ + u32 hi_xtal_control_setting; /* 0x30 */ + u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */ + u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */ + u32 hi_ref_voltage_trim_setting; /* 0x3c */ + u32 hi_clock_info; /* 0x40 */ + + /* Host uses BE CPU or not */ + u32 hi_be; /* 0x44 */ + + u32 hi_stack; /* normal stack */ /* 0x48 */ + u32 hi_err_stack; /* error stack */ /* 0x4c */ + u32 hi_desired_cpu_speed_hz; /* 0x50 */ + + /* Pointer to Board Data */ + u32 hi_board_data; /* 0x54 */ + + /* + * Indication of Board Data state: + * 0: board data is not yet initialized. + * 1: board data is initialized; unknown size + * >1: number of bytes of initialized board data + */ + u32 hi_board_data_initialized; /* 0x58 */ + + u32 hi_dset_ram_index_table; /* 0x5c */ + + u32 hi_desired_baud_rate; /* 0x60 */ + u32 hi_dbglog_config; /* 0x64 */ + u32 hi_end_ram_reserve_sz; /* 0x68 */ + u32 hi_mbox_io_block_sz; /* 0x6c */ + + u32 hi_num_bpatch_streams; /* 0x70 -- unused */ + u32 hi_mbox_isr_yield_limit; /* 0x74 */ + + u32 hi_refclk_hz; /* 0x78 */ + u32 hi_ext_clk_detected; /* 0x7c */ + u32 hi_dbg_uart_txpin; /* 0x80 */ + u32 hi_dbg_uart_rxpin; /* 0x84 */ + u32 hi_hci_uart_baud; /* 0x88 */ + u32 hi_hci_uart_pin_assignments; /* 0x8C */ + + u32 hi_hci_uart_baud_scale_val; /* 0x90 */ + u32 hi_hci_uart_baud_step_val; /* 0x94 */ + + u32 hi_allocram_start; /* 0x98 */ + u32 hi_allocram_sz; /* 0x9c */ + u32 hi_hci_bridge_flags; /* 0xa0 */ + u32 hi_hci_uart_support_pins; /* 0xa4 */ + + u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */ + + /* + * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high + * [31:16]: wakeup timeout in ms + */ + /* Pointer to extended board Data */ + u32 hi_board_ext_data; /* 0xac */ + u32 hi_board_ext_data_config; /* 0xb0 */ + /* + * Bit [0] : valid + * Bit[31:16: size + */ + /* + * hi_reset_flag is used to do some stuff when target reset. + * such as restore app_start after warm reset or + * preserve host Interest area, or preserve ROM data, literals etc. + */ + u32 hi_reset_flag; /* 0xb4 */ + /* indicate hi_reset_flag is valid */ + u32 hi_reset_flag_valid; /* 0xb8 */ + u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */ + /* 0xbc - [31:0]: idle timeout in ms */ + /* ACS flags */ + u32 hi_acs_flags; /* 0xc0 */ + u32 hi_console_flags; /* 0xc4 */ + u32 hi_nvram_state; /* 0xc8 */ + u32 hi_option_flag2; /* 0xcc */ + + /* If non-zero, override values sent to Host in WMI_READY event. */ + u32 hi_sw_version_override; /* 0xd0 */ + u32 hi_abi_version_override; /* 0xd4 */ + + /* + * Percentage of high priority RX traffic to total expected RX traffic + * applicable only to ar6004 + */ + u32 hi_hp_rx_traffic_ratio; /* 0xd8 */ + + /* test applications flags */ + u32 hi_test_apps_related; /* 0xdc */ + /* location of test script */ + u32 hi_ota_testscript; /* 0xe0 */ + /* location of CAL data */ + u32 hi_cal_data; /* 0xe4 */ + + /* Number of packet log buffers */ + u32 hi_pktlog_num_buffers; /* 0xe8 */ + + /* wow extension configuration */ + u32 hi_wow_ext_config; /* 0xec */ + u32 hi_pwr_save_flags; /* 0xf0 */ + + /* Spatial Multiplexing Power Save (SMPS) options */ + u32 hi_smps_options; /* 0xf4 */ + + /* Interconnect-specific state */ + u32 hi_interconnect_state; /* 0xf8 */ + + /* Coex configuration flags */ + u32 hi_coex_config; /* 0xfc */ + + /* Early allocation support */ + u32 hi_early_alloc; /* 0x100 */ + /* FW swap field */ + /* + * Bits of this 32bit word will be used to pass specific swap + * instruction to FW + */ + /* + * Bit 0 -- AP Nart descriptor no swap. When this bit is set + * FW will not swap TX descriptor. Meaning packets are formed + * on the target processor. + */ + /* Bit 1 - unused */ + u32 hi_fw_swap; /* 0x104 */ +} __packed; + +#define HI_ITEM(item) offsetof(struct host_interest, item) + +/* Bits defined in hi_option_flag */ + +/* Enable timer workaround */ +#define HI_OPTION_TIMER_WAR 0x01 +/* Limit BMI command credits */ +#define HI_OPTION_BMI_CRED_LIMIT 0x02 +/* Relay Dot11 hdr to/from host */ +#define HI_OPTION_RELAY_DOT11_HDR 0x04 +/* MAC addr method 0-locally administred 1-globally unique addrs */ +#define HI_OPTION_MAC_ADDR_METHOD 0x08 +/* Firmware Bridging */ +#define HI_OPTION_FW_BRIDGE 0x10 +/* Enable CPU profiling */ +#define HI_OPTION_ENABLE_PROFILE 0x20 +/* Disable debug logging */ +#define HI_OPTION_DISABLE_DBGLOG 0x40 +/* Skip Era Tracking */ +#define HI_OPTION_SKIP_ERA_TRACKING 0x80 +/* Disable PAPRD (debug) */ +#define HI_OPTION_PAPRD_DISABLE 0x100 +#define HI_OPTION_NUM_DEV_LSB 0x200 +#define HI_OPTION_NUM_DEV_MSB 0x800 +#define HI_OPTION_DEV_MODE_LSB 0x1000 +#define HI_OPTION_DEV_MODE_MSB 0x8000000 +/* Disable LowFreq Timer Stabilization */ +#define HI_OPTION_NO_LFT_STBL 0x10000000 +/* Skip regulatory scan */ +#define HI_OPTION_SKIP_REG_SCAN 0x20000000 +/* + * Do regulatory scan during init before + * sending WMI ready event to host + */ +#define HI_OPTION_INIT_REG_SCAN 0x40000000 + +/* REV6: Do not adjust memory map */ +#define HI_OPTION_SKIP_MEMMAP 0x80000000 + +#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3 + +/* 2 bits of hi_option_flag are used to represent 3 modes */ +#define HI_OPTION_FW_MODE_IBSS 0x0 /* IBSS Mode */ +#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */ +#define HI_OPTION_FW_MODE_AP 0x2 /* AP Mode */ +#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */ + +/* 2 bits of hi_option flag are usedto represent 4 submodes */ +#define HI_OPTION_FW_SUBMODE_NONE 0x0 /* Normal mode */ +#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1 /* p2p device mode */ +#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */ +#define HI_OPTION_FW_SUBMODE_P2PGO 0x3 /* p2p go mode */ + +/* Num dev Mask */ +#define HI_OPTION_NUM_DEV_MASK 0x7 +#define HI_OPTION_NUM_DEV_SHIFT 0x9 + +/* firmware bridging */ +#define HI_OPTION_FW_BRIDGE_SHIFT 0x04 + +/* +Fw Mode/SubMode Mask +|-----------------------------------------------------------------------------| +| SUB | SUB | SUB | SUB | | | | | +|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]| +| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2) | +|-----------------------------------------------------------------------------| +*/ +#define HI_OPTION_FW_MODE_BITS 0x2 +#define HI_OPTION_FW_MODE_MASK 0x3 +#define HI_OPTION_FW_MODE_SHIFT 0xC +#define HI_OPTION_ALL_FW_MODE_MASK 0xFF + +#define HI_OPTION_FW_SUBMODE_BITS 0x2 +#define HI_OPTION_FW_SUBMODE_MASK 0x3 +#define HI_OPTION_FW_SUBMODE_SHIFT 0x14 +#define HI_OPTION_ALL_FW_SUBMODE_MASK 0xFF00 +#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8 + + +/* hi_option_flag2 options */ +#define HI_OPTION_OFFLOAD_AMSDU 0x01 +#define HI_OPTION_DFS_SUPPORT 0x02 /* Enable DFS support */ +#define HI_OPTION_ENABLE_RFKILL 0x04 /* RFKill Enable Feature*/ +#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */ +#define HI_OPTION_EARLY_CFG_DONE 0x10 /* Early configuration is complete */ + +#define HI_OPTION_RF_KILL_SHIFT 0x2 +#define HI_OPTION_RF_KILL_MASK 0x1 + +/* hi_reset_flag */ +/* preserve App Start address */ +#define HI_RESET_FLAG_PRESERVE_APP_START 0x01 +/* preserve host interest */ +#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST 0x02 +/* preserve ROM data */ +#define HI_RESET_FLAG_PRESERVE_ROMDATA 0x04 +#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE 0x08 +#define HI_RESET_FLAG_PRESERVE_BOOT_INFO 0x10 +#define HI_RESET_FLAG_WARM_RESET 0x20 + +/* define hi_fw_swap bits */ +#define HI_DESC_IN_FW_BIT 0x01 + +/* indicate the reset flag is valid */ +#define HI_RESET_FLAG_IS_VALID 0x12345678 + +/* ACS is enabled */ +#define HI_ACS_FLAGS_ENABLED (1 << 0) +/* Use physical WWAN device */ +#define HI_ACS_FLAGS_USE_WWAN (1 << 1) +/* Use test VAP */ +#define HI_ACS_FLAGS_TEST_VAP (1 << 2) + +/* + * CONSOLE FLAGS + * + * Bit Range Meaning + * --------- -------------------------------- + * 2..0 UART ID (0 = Default) + * 3 Baud Select (0 = 9600, 1 = 115200) + * 30..4 Reserved + * 31 Enable Console + * + */ + +#define HI_CONSOLE_FLAGS_ENABLE (1 << 31) +#define HI_CONSOLE_FLAGS_UART_MASK (0x7) +#define HI_CONSOLE_FLAGS_UART_SHIFT 0 +#define HI_CONSOLE_FLAGS_BAUD_SELECT (1 << 3) + +/* SM power save options */ +#define HI_SMPS_ALLOW_MASK (0x00000001) +#define HI_SMPS_MODE_MASK (0x00000002) +#define HI_SMPS_MODE_STATIC (0x00000000) +#define HI_SMPS_MODE_DYNAMIC (0x00000002) +#define HI_SMPS_DISABLE_AUTO_MODE (0x00000004) +#define HI_SMPS_DATA_THRESH_MASK (0x000007f8) +#define HI_SMPS_DATA_THRESH_SHIFT (3) +#define HI_SMPS_RSSI_THRESH_MASK (0x0007f800) +#define HI_SMPS_RSSI_THRESH_SHIFT (11) +#define HI_SMPS_LOWPWR_CM_MASK (0x00380000) +#define HI_SMPS_LOWPWR_CM_SHIFT (15) +#define HI_SMPS_HIPWR_CM_MASK (0x03c00000) +#define HI_SMPS_HIPWR_CM_SHIFT (19) + +/* + * WOW Extension configuration + * + * Bit Range Meaning + * --------- -------------------------------- + * 8..0 Size of each WOW pattern (max 511) + * 15..9 Number of patterns per list (max 127) + * 17..16 Number of lists (max 4) + * 30..18 Reserved + * 31 Enabled + * + * set values (except enable) to zeros for default settings + */ + +#define HI_WOW_EXT_ENABLED_MASK (1 << 31) +#define HI_WOW_EXT_NUM_LIST_SHIFT 16 +#define HI_WOW_EXT_NUM_LIST_MASK (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT) +#define HI_WOW_EXT_NUM_PATTERNS_SHIFT 9 +#define HI_WOW_EXT_NUM_PATTERNS_MASK (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT) +#define HI_WOW_EXT_PATTERN_SIZE_SHIFT 0 +#define HI_WOW_EXT_PATTERN_SIZE_MASK (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT) + +#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \ + ((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \ + HI_WOW_EXT_NUM_LIST_MASK) | \ + (((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \ + HI_WOW_EXT_NUM_PATTERNS_MASK) | \ + (((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \ + HI_WOW_EXT_PATTERN_SIZE_MASK)) + +#define HI_WOW_EXT_GET_NUM_LISTS(config) \ + (((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT) +#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \ + (((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \ + HI_WOW_EXT_NUM_PATTERNS_SHIFT) +#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \ + (((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \ + HI_WOW_EXT_PATTERN_SIZE_SHIFT) + +/* + * Early allocation configuration + * Support RAM bank configuration before BMI done and this eases the memory + * allocation at very early stage + * Bit Range Meaning + * --------- ---------------------------------- + * [0:3] number of bank assigned to be IRAM + * [4:15] reserved + * [16:31] magic number + * + * Note: + * 1. target firmware would check magic number and if it's a match, firmware + * would consider the bits[0:15] are valid and base on that to calculate + * the end of DRAM. Early allocation would be located at that area and + * may be reclaimed when necesary + * 2. if no magic number is found, early allocation would happen at "_end" + * symbol of ROM which is located before the app-data and might NOT be + * re-claimable. If this is adopted, link script should keep this in + * mind to avoid data corruption. + */ +#define HI_EARLY_ALLOC_MAGIC 0x6d8a +#define HI_EARLY_ALLOC_MAGIC_MASK 0xffff0000 +#define HI_EARLY_ALLOC_MAGIC_SHIFT 16 +#define HI_EARLY_ALLOC_IRAM_BANKS_MASK 0x0000000f +#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT 0 + +#define HI_EARLY_ALLOC_VALID() \ + ((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \ + HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC)) +#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \ + (((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \ + >> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) + +/*power save flag bit definitions*/ +#define HI_PWR_SAVE_LPL_ENABLED 0x1 +/*b1-b3 reserved*/ +/*b4-b5 : dev0 LPL type : 0 - none + 1- Reduce Pwr Search + 2- Reduce Pwr Listen*/ +/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/ +#define HI_PWR_SAVE_LPL_DEV0_LSB 4 +#define HI_PWR_SAVE_LPL_DEV_MASK 0x3 +/*power save related utility macros*/ +#define HI_LPL_ENABLED() \ + ((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED)) +#define HI_DEV_LPL_TYPE_GET(_devix) \ + (HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \ + (HI_PWR_SAVE_LPL_DEV0_LSB + (_devix)*2))) + +#define HOST_INTEREST_SMPS_IS_ALLOWED() \ + ((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK)) + +/* Reserve 1024 bytes for extended board data */ +#define QCA988X_BOARD_DATA_SZ 7168 +#define QCA988X_BOARD_EXT_DATA_SZ 0 + +#endif /* __TARGADDRS_H__ */ diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c new file mode 100644 index 00000000000..4a31e2c6fbd --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/trace.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h new file mode 100644 index 00000000000..85e806bf725 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + +#include <linux/tracepoint.h> + +#define _TRACE_H_ + +/* create empty functions when tracing is disabled */ +#if !defined(CONFIG_ATH10K_TRACING) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ath10k + +#define ATH10K_MSG_MAX 200 + +DECLARE_EVENT_CLASS(ath10k_log_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, ATH10K_MSG_MAX) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + ATH10K_MSG_MAX, + vaf->fmt, + *vaf->va) >= ATH10K_MSG_MAX); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(ath10k_log_event, ath10k_log_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(ath10k_log_event, ath10k_log_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(ath10k_log_event, ath10k_log_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(ath10k_log_dbg, + TP_PROTO(unsigned int level, struct va_format *vaf), + TP_ARGS(level, vaf), + TP_STRUCT__entry( + __field(unsigned int, level) + __dynamic_array(char, msg, ATH10K_MSG_MAX) + ), + TP_fast_assign( + __entry->level = level; + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + ATH10K_MSG_MAX, + vaf->fmt, + *vaf->va) >= ATH10K_MSG_MAX); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(ath10k_log_dbg_dump, + TP_PROTO(const char *msg, const char *prefix, + const void *buf, size_t buf_len), + + TP_ARGS(msg, prefix, buf, buf_len), + + TP_STRUCT__entry( + __string(msg, msg) + __string(prefix, prefix) + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __assign_str(msg, msg); + __assign_str(prefix, prefix); + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "%s/%s\n", __get_str(prefix), __get_str(msg) + ) +); + +TRACE_EVENT(ath10k_wmi_cmd, + TP_PROTO(int id, void *buf, size_t buf_len), + + TP_ARGS(id, buf, buf_len), + + TP_STRUCT__entry( + __field(unsigned int, id) + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->id = id; + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "id %d len %zu", + __entry->id, + __entry->buf_len + ) +); + +TRACE_EVENT(ath10k_wmi_event, + TP_PROTO(int id, void *buf, size_t buf_len), + + TP_ARGS(id, buf, buf_len), + + TP_STRUCT__entry( + __field(unsigned int, id) + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->id = id; + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "id %d len %zu", + __entry->id, + __entry->buf_len + ) +); + +#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ + +/* we don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c new file mode 100644 index 00000000000..68b6faefd1d --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "txrx.h" +#include "htt.h" +#include "mac.h" +#include "debug.h" + +static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) +{ + if (!ATH10K_SKB_CB(skb)->htt.is_offchan) + return; + + /* If the original wait_for_completion() timed out before + * {data,mgmt}_tx_completed() was called then we could complete + * offchan_tx_completed for a different skb. Prevent this by using + * offchan_tx_skb. */ + spin_lock_bh(&ar->data_lock); + if (ar->offchan_tx_skb != skb) { + ath10k_warn("completed old offchannel frame\n"); + goto out; + } + + complete(&ar->offchan_tx_completed); + ar->offchan_tx_skb = NULL; /* just for sanity */ + + ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb); +out: + spin_unlock_bh(&ar->data_lock); +} + +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) +{ + struct device *dev = htt->ar->dev; + struct ieee80211_tx_info *info; + struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag; + struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu; + int ret; + + if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0) + return; + + ATH10K_SKB_CB(txdesc)->htt.refcount--; + + if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) + return; + + if (txfrag) { + ret = ath10k_skb_unmap(dev, txfrag); + if (ret) + ath10k_warn("txfrag unmap failed (%d)\n", ret); + + dev_kfree_skb_any(txfrag); + } + + ret = ath10k_skb_unmap(dev, msdu); + if (ret) + ath10k_warn("data skb unmap failed (%d)\n", ret); + + ath10k_report_offchan_tx(htt->ar, msdu); + + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + + if (ATH10K_SKB_CB(txdesc)->htt.discard) { + ieee80211_free_txskb(htt->ar->hw, msdu); + goto exit; + } + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + if (ATH10K_SKB_CB(txdesc)->htt.no_ack) + info->flags &= ~IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status(htt->ar->hw, msdu); + /* we do not own the msdu anymore */ + +exit: + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id); + __ath10k_htt_tx_dec_pending(htt); + if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx)) + wake_up(&htt->empty_tx_wq); + spin_unlock_bh(&htt->tx_lock); + + dev_kfree_skb_any(txdesc); +} + +void ath10k_txrx_tx_completed(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done) +{ + struct sk_buff *txdesc; + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n", + tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack); + + if (tx_done->msdu_id >= htt->max_num_pending_tx) { + ath10k_warn("warning: msdu_id %d too big, ignoring\n", + tx_done->msdu_id); + return; + } + + txdesc = htt->pending_tx[tx_done->msdu_id]; + + ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard; + ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack; + + ath10k_txrx_tx_unref(htt, txdesc); +} + +static const u8 rx_legacy_rate_idx[] = { + 3, /* 0x00 - 11Mbps */ + 2, /* 0x01 - 5.5Mbps */ + 1, /* 0x02 - 2Mbps */ + 0, /* 0x03 - 1Mbps */ + 3, /* 0x04 - 11Mbps */ + 2, /* 0x05 - 5.5Mbps */ + 1, /* 0x06 - 2Mbps */ + 0, /* 0x07 - 1Mbps */ + 10, /* 0x08 - 48Mbps */ + 8, /* 0x09 - 24Mbps */ + 6, /* 0x0A - 12Mbps */ + 4, /* 0x0B - 6Mbps */ + 11, /* 0x0C - 54Mbps */ + 9, /* 0x0D - 36Mbps */ + 7, /* 0x0E - 18Mbps */ + 5, /* 0x0F - 9Mbps */ +}; + +static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info, + enum ieee80211_band band, + struct ieee80211_rx_status *status) +{ + u8 cck, rate, rate_idx, bw, sgi, mcs, nss; + u8 info0 = info->rate.info0; + u32 info1 = info->rate.info1; + u32 info2 = info->rate.info2; + u8 preamble = 0; + + /* Check if valid fields */ + if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID)) + return; + + preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE); + + switch (preamble) { + case HTT_RX_LEGACY: + cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK; + rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE); + rate_idx = 0; + + if (rate < 0x08 || rate > 0x0F) + break; + + switch (band) { + case IEEE80211_BAND_2GHZ: + if (cck) + rate &= ~BIT(3); + rate_idx = rx_legacy_rate_idx[rate]; + break; + case IEEE80211_BAND_5GHZ: + rate_idx = rx_legacy_rate_idx[rate]; + /* We are using same rate table registering + HW - ath10k_rates[]. In case of 5GHz skip + CCK rates, so -4 here */ + rate_idx -= 4; + break; + default: + break; + } + + status->rate_idx = rate_idx; + break; + case HTT_RX_HT: + case HTT_RX_HT_WITH_TXBF: + /* HT-SIG - Table 20-11 in info1 and info2 */ + mcs = info1 & 0x1F; + nss = mcs >> 3; + bw = (info1 >> 7) & 1; + sgi = (info2 >> 7) & 1; + + status->rate_idx = mcs; + status->flag |= RX_FLAG_HT; + if (sgi) + status->flag |= RX_FLAG_SHORT_GI; + if (bw) + status->flag |= RX_FLAG_40MHZ; + break; + case HTT_RX_VHT: + case HTT_RX_VHT_WITH_TXBF: + /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2 + TODO check this */ + mcs = (info2 >> 4) & 0x0F; + nss = (info1 >> 10) & 0x07; + bw = info1 & 3; + sgi = info2 & 1; + + status->rate_idx = mcs; + status->vht_nss = nss; + + if (sgi) + status->flag |= RX_FLAG_SHORT_GI; + + switch (bw) { + /* 20MHZ */ + case 0: + break; + /* 40MHZ */ + case 1: + status->flag |= RX_FLAG_40MHZ; + break; + /* 80MHZ */ + case 2: + status->flag |= RX_FLAG_80MHZ; + } + + status->flag |= RX_FLAG_VHT; + break; + default: + break; + } +} + +void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) +{ + struct ieee80211_rx_status *status; + struct ieee80211_channel *ch; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data; + + status = IEEE80211_SKB_RXCB(info->skb); + memset(status, 0, sizeof(*status)); + + if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) { + status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16( + __le16_to_cpu(hdr->frame_control) & + ~IEEE80211_FCTL_PROTECTED); + } + + if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (info->fcs_err) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + status->signal = info->signal; + + spin_lock_bh(&ar->data_lock); + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + spin_unlock_bh(&ar->data_lock); + + if (!ch) { + ath10k_warn("no channel configured; ignoring frame!\n"); + dev_kfree_skb_any(info->skb); + return; + } + + process_rx_rates(ar, info, ch->band, status); + status->band = ch->band; + status->freq = ch->center_freq; + + ath10k_dbg(ATH10K_DBG_DATA, + "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n", + info->skb, + info->skb->len, + status->flag == 0 ? "legacy" : "", + status->flag & RX_FLAG_HT ? "ht" : "", + status->flag & RX_FLAG_VHT ? "vht" : "", + status->flag & RX_FLAG_40MHZ ? "40" : "", + status->flag & RX_FLAG_80MHZ ? "80" : "", + status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", + status->rate_idx, + status->vht_nss, + status->freq, + status->band); + + ieee80211_rx(ar->hw, info->skb); +} + +struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, + const u8 *addr) +{ + struct ath10k_peer *peer; + + lockdep_assert_held(&ar->data_lock); + + list_for_each_entry(peer, &ar->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + if (memcmp(peer->addr, addr, ETH_ALEN)) + continue; + + return peer; + } + + return NULL; +} + +static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, + int peer_id) +{ + struct ath10k_peer *peer; + + lockdep_assert_held(&ar->data_lock); + + list_for_each_entry(peer, &ar->peers, list) + if (test_bit(peer_id, peer->peer_ids)) + return peer; + + return NULL; +} + +static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id, + const u8 *addr, bool expect_mapped) +{ + int ret; + + ret = wait_event_timeout(ar->peer_mapping_wq, ({ + bool mapped; + + spin_lock_bh(&ar->data_lock); + mapped = !!ath10k_peer_find(ar, vdev_id, addr); + spin_unlock_bh(&ar->data_lock); + + mapped == expect_mapped; + }), 3*HZ); + + if (ret <= 0) + return -ETIMEDOUT; + + return 0; +} + +int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr) +{ + return ath10k_wait_for_peer_common(ar, vdev_id, addr, true); +} + +int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, const u8 *addr) +{ + return ath10k_wait_for_peer_common(ar, vdev_id, addr, false); +} + +void ath10k_peer_map_event(struct ath10k_htt *htt, + struct htt_peer_map_event *ev) +{ + struct ath10k *ar = htt->ar; + struct ath10k_peer *peer; + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr); + if (!peer) { + peer = kzalloc(sizeof(*peer), GFP_ATOMIC); + if (!peer) + goto exit; + + peer->vdev_id = ev->vdev_id; + memcpy(peer->addr, ev->addr, ETH_ALEN); + list_add(&peer->list, &ar->peers); + wake_up(&ar->peer_mapping_wq); + } + + ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n", + ev->vdev_id, ev->addr, ev->peer_id); + + set_bit(ev->peer_id, peer->peer_ids); +exit: + spin_unlock_bh(&ar->data_lock); +} + +void ath10k_peer_unmap_event(struct ath10k_htt *htt, + struct htt_peer_unmap_event *ev) +{ + struct ath10k *ar = htt->ar; + struct ath10k_peer *peer; + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, ev->peer_id); + if (!peer) { + ath10k_warn("unknown peer id %d\n", ev->peer_id); + goto exit; + } + + ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n", + peer->vdev_id, peer->addr, ev->peer_id); + + clear_bit(ev->peer_id, peer->peer_ids); + + if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) { + list_del(&peer->list); + kfree(peer); + wake_up(&ar->peer_mapping_wq); + } + +exit: + spin_unlock_bh(&ar->data_lock); +} diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h new file mode 100644 index 00000000000..e78632a76df --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/txrx.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _TXRX_H_ +#define _TXRX_H_ + +#include "htt.h" + +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc); +void ath10k_txrx_tx_completed(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done); +void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info); + +struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, + const u8 *addr); +int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, + const u8 *addr); +int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, + const u8 *addr); + +void ath10k_peer_map_event(struct ath10k_htt *htt, + struct htt_peer_map_event *ev); +void ath10k_peer_unmap_event(struct ath10k_htt *htt, + struct htt_peer_unmap_event *ev); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c new file mode 100644 index 00000000000..7d4b7987422 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -0,0 +1,2081 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/skbuff.h> + +#include "core.h" +#include "htc.h" +#include "debug.h" +#include "wmi.h" +#include "mac.h" + +void ath10k_wmi_flush_tx(struct ath10k *ar) +{ + int ret; + + ret = wait_event_timeout(ar->wmi.wq, + atomic_read(&ar->wmi.pending_tx_count) == 0, + 5*HZ); + if (atomic_read(&ar->wmi.pending_tx_count) == 0) + return; + + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret < 0) + ath10k_warn("wmi flush failed (%d)\n", ret); +} + +int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) +{ + int ret; + ret = wait_for_completion_timeout(&ar->wmi.service_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + return ret; +} + +int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar) +{ + int ret; + ret = wait_for_completion_timeout(&ar->wmi.unified_ready, + WMI_UNIFIED_READY_TIMEOUT_HZ); + return ret; +} + +static struct sk_buff *ath10k_wmi_alloc_skb(u32 len) +{ + struct sk_buff *skb; + u32 round_len = roundup(len, 4); + + skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len); + if (!skb) + return NULL; + + skb_reserve(skb, WMI_SKB_HEADROOM); + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath10k_warn("Unaligned WMI skb\n"); + + skb_put(skb, round_len); + memset(skb->data, 0, round_len); + + return skb; +} + +static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + dev_kfree_skb(skb); + + if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0) + wake_up(&ar->wmi.wq); +} + +/* WMI command API */ +static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, + enum wmi_cmd_id cmd_id) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + struct wmi_cmd_hdr *cmd_hdr; + int status; + u32 cmd = 0; + + if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return -ENOMEM; + + cmd |= SM(cmd_id, WMI_CMD_HDR_CMD_ID); + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + cmd_hdr->cmd_id = __cpu_to_le32(cmd); + + if (atomic_add_return(1, &ar->wmi.pending_tx_count) > + WMI_MAX_PENDING_TX_COUNT) { + /* avoid using up memory when FW hangs */ + atomic_dec(&ar->wmi.pending_tx_count); + return -EBUSY; + } + + memset(skb_cb, 0, sizeof(*skb_cb)); + + trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); + + status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb); + if (status) { + dev_kfree_skb_any(skb); + atomic_dec(&ar->wmi.pending_tx_count); + return status; + } + + return 0; +} + +static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; + enum wmi_scan_event_type event_type; + enum wmi_scan_completion_reason reason; + u32 freq; + u32 req_id; + u32 scan_id; + u32 vdev_id; + + event_type = __le32_to_cpu(event->event_type); + reason = __le32_to_cpu(event->reason); + freq = __le32_to_cpu(event->channel_freq); + req_id = __le32_to_cpu(event->scan_req_id); + scan_id = __le32_to_cpu(event->scan_id); + vdev_id = __le32_to_cpu(event->vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n"); + ath10k_dbg(ATH10K_DBG_WMI, + "scan event type %d reason %d freq %d req_id %d " + "scan_id %d vdev_id %d\n", + event_type, reason, freq, req_id, scan_id, vdev_id); + + spin_lock_bh(&ar->data_lock); + + switch (event_type) { + case WMI_SCAN_EVENT_STARTED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n"); + if (ar->scan.in_progress && ar->scan.is_roc) + ieee80211_ready_on_channel(ar->hw); + + complete(&ar->scan.started); + break; + case WMI_SCAN_EVENT_COMPLETED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n"); + switch (reason) { + case WMI_SCAN_REASON_COMPLETED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n"); + break; + case WMI_SCAN_REASON_CANCELLED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n"); + break; + case WMI_SCAN_REASON_PREEMPTED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n"); + break; + case WMI_SCAN_REASON_TIMEDOUT: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n"); + break; + default: + break; + } + + ar->scan_channel = NULL; + if (!ar->scan.in_progress) { + ath10k_warn("no scan requested, ignoring\n"); + break; + } + + if (ar->scan.is_roc) { + ath10k_offchan_tx_purge(ar); + + if (!ar->scan.aborting) + ieee80211_remain_on_channel_expired(ar->hw); + } else { + ieee80211_scan_completed(ar->hw, ar->scan.aborting); + } + + del_timer(&ar->scan.timeout); + complete_all(&ar->scan.completed); + ar->scan.in_progress = false; + break; + case WMI_SCAN_EVENT_BSS_CHANNEL: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n"); + ar->scan_channel = NULL; + break; + case WMI_SCAN_EVENT_FOREIGN_CHANNEL: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n"); + ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); + if (ar->scan.in_progress && ar->scan.is_roc && + ar->scan.roc_freq == freq) { + complete(&ar->scan.on_channel); + } + break; + case WMI_SCAN_EVENT_DEQUEUED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n"); + break; + case WMI_SCAN_EVENT_PREEMPTED: + ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n"); + break; + case WMI_SCAN_EVENT_START_FAILED: + ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n"); + break; + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + return 0; +} + +static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode) +{ + enum ieee80211_band band; + + switch (phy_mode) { + case MODE_11A: + case MODE_11NA_HT20: + case MODE_11NA_HT40: + case MODE_11AC_VHT20: + case MODE_11AC_VHT40: + case MODE_11AC_VHT80: + band = IEEE80211_BAND_5GHZ; + break; + case MODE_11G: + case MODE_11B: + case MODE_11GONLY: + case MODE_11NG_HT20: + case MODE_11NG_HT40: + case MODE_11AC_VHT20_2G: + case MODE_11AC_VHT40_2G: + case MODE_11AC_VHT80_2G: + default: + band = IEEE80211_BAND_2GHZ; + } + + return band; +} + +static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band) +{ + u8 rate_idx = 0; + + /* rate in Kbps */ + switch (rate) { + case 1000: + rate_idx = 0; + break; + case 2000: + rate_idx = 1; + break; + case 5500: + rate_idx = 2; + break; + case 11000: + rate_idx = 3; + break; + case 6000: + rate_idx = 4; + break; + case 9000: + rate_idx = 5; + break; + case 12000: + rate_idx = 6; + break; + case 18000: + rate_idx = 7; + break; + case 24000: + rate_idx = 8; + break; + case 36000: + rate_idx = 9; + break; + case 48000: + rate_idx = 10; + break; + case 54000: + rate_idx = 11; + break; + default: + break; + } + + if (band == IEEE80211_BAND_5GHZ) { + if (rate_idx > 3) + /* Omit CCK rates */ + rate_idx -= 4; + else + rate_idx = 0; + } + + return rate_idx; +} + +static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *hdr; + u32 rx_status; + u32 channel; + u32 phy_mode; + u32 snr; + u32 rate; + u32 buf_len; + u16 fc; + + channel = __le32_to_cpu(event->hdr.channel); + buf_len = __le32_to_cpu(event->hdr.buf_len); + rx_status = __le32_to_cpu(event->hdr.status); + snr = __le32_to_cpu(event->hdr.snr); + phy_mode = __le32_to_cpu(event->hdr.phy_mode); + rate = __le32_to_cpu(event->hdr.rate); + + memset(status, 0, sizeof(*status)); + + ath10k_dbg(ATH10K_DBG_MGMT, + "event mgmt rx status %08x\n", rx_status); + + if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) { + dev_kfree_skb(skb); + return 0; + } + + if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) { + dev_kfree_skb(skb); + return 0; + } + + if (rx_status & WMI_RX_STATUS_ERR_CRC) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rx_status & WMI_RX_STATUS_ERR_MIC) + status->flag |= RX_FLAG_MMIC_ERROR; + + status->band = phy_mode_to_band(phy_mode); + status->freq = ieee80211_channel_to_frequency(channel, status->band); + status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; + status->rate_idx = get_rate_idx(rate, status->band); + + skb_pull(skb, sizeof(event->hdr)); + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (fc & IEEE80211_FCTL_PROTECTED) { + status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & + ~IEEE80211_FCTL_PROTECTED); + } + + ath10k_dbg(ATH10K_DBG_MGMT, + "event mgmt rx skb %p len %d ftype %02x stype %02x\n", + skb, skb->len, + fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); + + ath10k_dbg(ATH10K_DBG_MGMT, + "event mgmt rx freq %d band %d snr %d, rate_idx %d\n", + status->freq, status->band, status->signal, + status->rate_idx); + + /* + * packets from HTC come aligned to 4byte boundaries + * because they can originally come in along with a trailer + */ + skb_trim(skb, buf_len); + + ieee80211_rx(ar->hw, skb); + return 0; +} + +static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n"); +} + +static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); +} + +static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n"); +} + +static void ath10k_wmi_event_update_stats(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data; + + ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); + + ath10k_debug_read_target_stats(ar, ev); +} + +static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_vdev_start_response_event *ev; + + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); + + ev = (struct wmi_vdev_start_response_event *)skb->data; + + if (WARN_ON(__le32_to_cpu(ev->status))) + return; + + complete(&ar->vdev_setup_done); +} + +static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n"); + complete(&ar->vdev_setup_done); +} + +static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n"); +} + +/* + * FIXME + * + * We don't report to mac80211 sleep state of connected + * stations. Due to this mac80211 can't fill in TIM IE + * correctly. + * + * I know of no way of getting nullfunc frames that contain + * sleep transition from connected stations - these do not + * seem to be sent from the target to the host. There also + * doesn't seem to be a dedicated event for that. So the + * only way left to do this would be to read tim_bitmap + * during SWBA. + * + * We could probably try using tim_bitmap from SWBA to tell + * mac80211 which stations are asleep and which are not. The + * problem here is calling mac80211 functions so many times + * could take too long and make us miss the time to submit + * the beacon to the target. + * + * So as a workaround we try to extend the TIM IE if there + * is unicast buffered for stations with aid > 7 and fill it + * in ourselves. + */ +static void ath10k_wmi_update_tim(struct ath10k *ar, + struct ath10k_vif *arvif, + struct sk_buff *bcn, + struct wmi_bcn_info *bcn_info) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data; + struct ieee80211_tim_ie *tim; + u8 *ies, *ie; + u8 ie_len, pvm_len; + + /* if next SWBA has no tim_changed the tim_bitmap is garbage. + * we must copy the bitmap upon change and reuse it later */ + if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) { + int i; + + BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) != + sizeof(bcn_info->tim_info.tim_bitmap)); + + for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) { + __le32 t = bcn_info->tim_info.tim_bitmap[i / 4]; + u32 v = __le32_to_cpu(t); + arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF; + } + + /* FW reports either length 0 or 16 + * so we calculate this on our own */ + arvif->u.ap.tim_len = 0; + for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) + if (arvif->u.ap.tim_bitmap[i]) + arvif->u.ap.tim_len = i; + + arvif->u.ap.tim_len++; + } + + ies = bcn->data; + ies += ieee80211_hdrlen(hdr->frame_control); + ies += 12; /* fixed parameters */ + + ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies, + (u8 *)skb_tail_pointer(bcn) - ies); + if (!ie) { + /* highly unlikely for mac80211 */ + ath10k_warn("no tim ie found;\n"); + return; + } + + tim = (void *)ie + 2; + ie_len = ie[1]; + pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */ + + if (pvm_len < arvif->u.ap.tim_len) { + int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len; + int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len); + void *next_ie = ie + 2 + ie_len; + + if (skb_put(bcn, expand_size)) { + memmove(next_ie + expand_size, next_ie, move_size); + + ie[1] += expand_size; + ie_len += expand_size; + pvm_len += expand_size; + } else { + ath10k_warn("tim expansion failed\n"); + } + } + + if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) { + ath10k_warn("tim pvm length is too great (%d)\n", pvm_len); + return; + } + + tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast); + memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); + + ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", + tim->dtim_count, tim->dtim_period, + tim->bitmap_ctrl, pvm_len); +} + +static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len, + struct wmi_p2p_noa_info *noa) +{ + struct ieee80211_p2p_noa_attr *noa_attr; + u8 ctwindow_oppps = noa->ctwindow_oppps; + u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET; + bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT); + __le16 *noa_attr_len; + u16 attr_len; + u8 noa_descriptors = noa->num_descriptors; + int i; + + /* P2P IE */ + data[0] = WLAN_EID_VENDOR_SPECIFIC; + data[1] = len - 2; + data[2] = (WLAN_OUI_WFA >> 16) & 0xff; + data[3] = (WLAN_OUI_WFA >> 8) & 0xff; + data[4] = (WLAN_OUI_WFA >> 0) & 0xff; + data[5] = WLAN_OUI_TYPE_WFA_P2P; + + /* NOA ATTR */ + data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE; + noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */ + noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9]; + + noa_attr->index = noa->index; + noa_attr->oppps_ctwindow = ctwindow; + if (oppps) + noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; + + for (i = 0; i < noa_descriptors; i++) { + noa_attr->desc[i].count = + __le32_to_cpu(noa->descriptors[i].type_count); + noa_attr->desc[i].duration = noa->descriptors[i].duration; + noa_attr->desc[i].interval = noa->descriptors[i].interval; + noa_attr->desc[i].start_time = noa->descriptors[i].start_time; + } + + attr_len = 2; /* index + oppps_ctwindow */ + attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); + *noa_attr_len = __cpu_to_le16(attr_len); +} + +static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa) +{ + u32 len = 0; + u8 noa_descriptors = noa->num_descriptors; + u8 opp_ps_info = noa->ctwindow_oppps; + bool opps_enabled = !!(opp_ps_info & WMI_P2P_OPPPS_ENABLE_BIT); + + + if (!noa_descriptors && !opps_enabled) + return len; + + len += 1 + 1 + 4; /* EID + len + OUI */ + len += 1 + 2; /* noa attr + attr len */ + len += 1 + 1; /* index + oppps_ctwindow */ + len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); + + return len; +} + +static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, + struct sk_buff *bcn, + struct wmi_bcn_info *bcn_info) +{ + struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info; + u8 *new_data, *old_data = arvif->u.ap.noa_data; + u32 new_len; + + if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + return; + + ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed); + if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) { + new_len = ath10k_p2p_calc_noa_ie_len(noa); + if (!new_len) + goto cleanup; + + new_data = kmalloc(new_len, GFP_ATOMIC); + if (!new_data) + goto cleanup; + + ath10k_p2p_fill_noa_ie(new_data, new_len, noa); + + spin_lock_bh(&ar->data_lock); + arvif->u.ap.noa_data = new_data; + arvif->u.ap.noa_len = new_len; + spin_unlock_bh(&ar->data_lock); + kfree(old_data); + } + + if (arvif->u.ap.noa_data) + if (!pskb_expand_head(bcn, 0, arvif->u.ap.noa_len, GFP_ATOMIC)) + memcpy(skb_put(bcn, arvif->u.ap.noa_len), + arvif->u.ap.noa_data, + arvif->u.ap.noa_len); + return; + +cleanup: + spin_lock_bh(&ar->data_lock); + arvif->u.ap.noa_data = NULL; + arvif->u.ap.noa_len = 0; + spin_unlock_bh(&ar->data_lock); + kfree(old_data); +} + + +static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_host_swba_event *ev; + u32 map; + int i = -1; + struct wmi_bcn_info *bcn_info; + struct ath10k_vif *arvif; + struct wmi_bcn_tx_arg arg; + struct sk_buff *bcn; + int vdev_id = 0; + int ret; + + ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n"); + + ev = (struct wmi_host_swba_event *)skb->data; + map = __le32_to_cpu(ev->vdev_map); + + ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n" + "-vdev map 0x%x\n", + ev->vdev_map); + + for (; map; map >>= 1, vdev_id++) { + if (!(map & 0x1)) + continue; + + i++; + + if (i >= WMI_MAX_AP_VDEV) { + ath10k_warn("swba has corrupted vdev map\n"); + break; + } + + bcn_info = &ev->bcn_info[i]; + + ath10k_dbg(ATH10K_DBG_MGMT, + "-bcn_info[%d]:\n" + "--tim_len %d\n" + "--tim_mcast %d\n" + "--tim_changed %d\n" + "--tim_num_ps_pending %d\n" + "--tim_bitmap 0x%08x%08x%08x%08x\n", + i, + __le32_to_cpu(bcn_info->tim_info.tim_len), + __le32_to_cpu(bcn_info->tim_info.tim_mcast), + __le32_to_cpu(bcn_info->tim_info.tim_changed), + __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0])); + + arvif = ath10k_get_arvif(ar, vdev_id); + if (arvif == NULL) { + ath10k_warn("no vif for vdev_id %d found\n", vdev_id); + continue; + } + + bcn = ieee80211_beacon_get(ar->hw, arvif->vif); + if (!bcn) { + ath10k_warn("could not get mac80211 beacon\n"); + continue; + } + + ath10k_tx_h_seq_no(bcn); + ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); + ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); + + arg.vdev_id = arvif->vdev_id; + arg.tx_rate = 0; + arg.tx_power = 0; + arg.bcn = bcn->data; + arg.bcn_len = bcn->len; + + ret = ath10k_wmi_beacon_send(ar, &arg); + if (ret) + ath10k_warn("could not send beacon (%d)\n", ret); + + dev_kfree_skb_any(bcn); + } +} + +static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); +} + +static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n"); +} + +static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n"); +} + +static void ath10k_wmi_event_profile_match(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n"); +} + +static void ath10k_wmi_event_debug_print(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n"); +} + +static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n"); +} + +static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n"); +} + +static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n"); +} + +static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n"); +} + +static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n"); +} + +static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); +} + +static void ath10k_wmi_event_dcs_interference(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); +} + +static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); +} + +static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); +} + +static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n"); +} + +static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n"); +} + +static void ath10k_wmi_event_delba_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n"); +} + +static void ath10k_wmi_event_addba_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); +} + +static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_service_ready_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) { + ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + skb->len, sizeof(*ev)); + return; + } + + ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power); + ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power); + ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info); + ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info); + ar->fw_version_major = + (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24; + ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff); + ar->fw_version_release = + (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16; + ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff); + ar->phy_capability = __le32_to_cpu(ev->phy_capability); + + ar->ath_common.regulatory.current_rd = + __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); + + ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, + sizeof(ev->wmi_service_bitmap)); + + if (strlen(ar->hw->wiphy->fw_version) == 0) { + snprintf(ar->hw->wiphy->fw_version, + sizeof(ar->hw->wiphy->fw_version), + "%u.%u.%u.%u", + ar->fw_version_major, + ar->fw_version_minor, + ar->fw_version_release, + ar->fw_version_build); + } + + /* FIXME: it probably should be better to support this */ + if (__le32_to_cpu(ev->num_mem_reqs) > 0) { + ath10k_warn("target requested %d memory chunks; ignoring\n", + __le32_to_cpu(ev->num_mem_reqs)); + } + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->sw_version_1), + __le32_to_cpu(ev->abi_version), + __le32_to_cpu(ev->phy_capability), + __le32_to_cpu(ev->ht_cap_info), + __le32_to_cpu(ev->vht_cap_info), + __le32_to_cpu(ev->vht_supp_mcs), + __le32_to_cpu(ev->sys_cap_info), + __le32_to_cpu(ev->num_mem_reqs)); + + complete(&ar->wmi.service_ready); +} + +static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; + + if (WARN_ON(skb->len < sizeof(*ev))) + return -EINVAL; + + memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->abi_version), + ev->mac_addr.addr, + __le32_to_cpu(ev->status)); + + complete(&ar->wmi.unified_ready); + return 0; +} + +static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_event_id id; + u16 len; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; + + len = skb->len; + + trace_ath10k_wmi_event(id, skb->data, skb->len); + + switch (id) { + case WMI_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ + return; + case WMI_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_PDEV_FTM_INTG_EVENTID: + ath10k_wmi_event_pdev_ftm_intg(ar, skb); + break; + case WMI_GTK_OFFLOAD_STATUS_EVENTID: + ath10k_wmi_event_gtk_offload_status(ar, skb); + break; + case WMI_GTK_REKEY_FAIL_EVENTID: + ath10k_wmi_event_gtk_rekey_fail(ar, skb); + break; + case WMI_TX_DELBA_COMPLETE_EVENTID: + ath10k_wmi_event_delba_complete(ar, skb); + break; + case WMI_TX_ADDBA_COMPLETE_EVENTID: + ath10k_wmi_event_addba_complete(ar, skb); + break; + case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + ath10k_wmi_event_vdev_install_key_complete(ar, skb); + break; + case WMI_SERVICE_READY_EVENTID: + ath10k_wmi_service_ready_event_rx(ar, skb); + break; + case WMI_READY_EVENTID: + ath10k_wmi_ready_event_rx(ar, skb); + break; + default: + ath10k_warn("Unknown eventid: %d\n", id); + break; + } + + dev_kfree_skb(skb); +} + +static void ath10k_wmi_event_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + wmi.wmi_event_work); + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->wmi.wmi_event_list); + if (!skb) + break; + + ath10k_wmi_event_process(ar, skb); + } +} + +static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + enum wmi_event_id event_id; + + event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + /* some events require to be handled ASAP + * thus can't be defered to a worker thread */ + switch (event_id) { + case WMI_HOST_SWBA_EVENTID: + case WMI_MGMT_RX_EVENTID: + ath10k_wmi_event_process(ar, skb); + return; + default: + break; + } + + skb_queue_tail(&ar->wmi.wmi_event_list, skb); + queue_work(ar->workqueue, &ar->wmi.wmi_event_work); +} + +/* WMI Initialization functions */ +int ath10k_wmi_attach(struct ath10k *ar) +{ + init_completion(&ar->wmi.service_ready); + init_completion(&ar->wmi.unified_ready); + init_waitqueue_head(&ar->wmi.wq); + + skb_queue_head_init(&ar->wmi.wmi_event_list); + INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work); + + return 0; +} + +void ath10k_wmi_detach(struct ath10k *ar) +{ + /* HTC should've drained the packets already */ + if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0)) + ath10k_warn("there are still pending packets\n"); + + cancel_work_sync(&ar->wmi.wmi_event_work); + skb_queue_purge(&ar->wmi.wmi_event_list); +} + +int ath10k_wmi_connect_htc_service(struct ath10k *ar) +{ + int status; + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + /* these fields are the same for all service endpoints */ + conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx; + + /* connect to control service */ + conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; + + status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp); + if (status) { + ath10k_warn("failed to connect to WMI CONTROL service status: %d\n", + status); + return status; + } + + ar->wmi.eid = conn_resp.eid; + return 0; +} + +int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g) +{ + struct wmi_pdev_set_regdomain_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data; + cmd->reg_domain = __cpu_to_le32(rd); + cmd->reg_domain_2G = __cpu_to_le32(rd2g); + cmd->reg_domain_5G = __cpu_to_le32(rd5g); + cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g); + cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", + rd, rd2g, rd5g, ctl2g, ctl5g); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); +} + +int ath10k_wmi_pdev_set_channel(struct ath10k *ar, + const struct wmi_channel_arg *arg) +{ + struct wmi_set_channel_cmd *cmd; + struct sk_buff *skb; + + if (arg->passive) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_channel_cmd *)skb->data; + cmd->chan.mhz = __cpu_to_le32(arg->freq); + cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq); + cmd->chan.mode = arg->mode; + cmd->chan.min_power = arg->min_power; + cmd->chan.max_power = arg->max_power; + cmd->chan.reg_power = arg->max_reg_power; + cmd->chan.reg_classid = arg->reg_class_id; + cmd->chan.antenna_max = arg->max_antenna_gain; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi set channel mode %d freq %d\n", + arg->mode, arg->freq); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID); +} + +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) +{ + struct wmi_pdev_suspend_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_suspend_cmd *)skb->data; + cmd->suspend_opt = WMI_PDEV_SUSPEND; + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID); +} + +int ath10k_wmi_pdev_resume_target(struct ath10k *ar) +{ + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(0); + if (skb == NULL) + return -ENOMEM; + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID); +} + +int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, + u32 value) +{ + struct wmi_pdev_set_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_param_cmd *)skb->data; + cmd->param_id = __cpu_to_le32(id); + cmd->param_value = __cpu_to_le32(value); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", + id, value); + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID); +} + +int ath10k_wmi_cmd_init(struct ath10k *ar) +{ + struct wmi_init_cmd *cmd; + struct sk_buff *buf; + struct wmi_resource_config config = {}; + u32 val; + + config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); + config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS); + + config.num_offload_reorder_bufs = + __cpu_to_le32(TARGET_NUM_OFFLOAD_REORDER_BUFS); + + config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE); + + config.scan_max_pending_reqs = + __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS); + + config.bmiss_offload_max_vdev = + __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_vdev = + __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_ap_profiles = + __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES); + + config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS); + config.num_mcast_table_elems = + __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS); + + config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM); + + val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); + + config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG); + + config.gtk_offload_max_vdev = + __cpu_to_le32(TARGET_GTK_OFFLOAD_MAX_VDEV); + + config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); + + buf = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!buf) + return -ENOMEM; + + cmd = (struct wmi_init_cmd *)buf->data; + cmd->num_host_mem_chunks = 0; + memcpy(&cmd->resource_config, &config, sizeof(config)); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); + return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID); +} + +static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) +{ + int len; + + len = sizeof(struct wmi_start_scan_cmd); + + if (arg->ie_len) { + if (!arg->ie) + return -EINVAL; + if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN) + return -EINVAL; + + len += sizeof(struct wmi_ie_data); + len += roundup(arg->ie_len, 4); + } + + if (arg->n_channels) { + if (!arg->channels) + return -EINVAL; + if (arg->n_channels > ARRAY_SIZE(arg->channels)) + return -EINVAL; + + len += sizeof(struct wmi_chan_list); + len += sizeof(__le32) * arg->n_channels; + } + + if (arg->n_ssids) { + if (!arg->ssids) + return -EINVAL; + if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID) + return -EINVAL; + + len += sizeof(struct wmi_ssid_list); + len += sizeof(struct wmi_ssid) * arg->n_ssids; + } + + if (arg->n_bssids) { + if (!arg->bssids) + return -EINVAL; + if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID) + return -EINVAL; + + len += sizeof(struct wmi_bssid_list); + len += sizeof(struct wmi_mac_addr) * arg->n_bssids; + } + + return len; +} + +int ath10k_wmi_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct wmi_start_scan_cmd *cmd; + struct sk_buff *skb; + struct wmi_ie_data *ie; + struct wmi_chan_list *channels; + struct wmi_ssid_list *ssids; + struct wmi_bssid_list *bssids; + u32 scan_id; + u32 scan_req_id; + int off; + int len = 0; + int i; + + len = ath10k_wmi_start_scan_calc_len(arg); + if (len < 0) + return len; /* len contains error code here */ + + skb = ath10k_wmi_alloc_skb(len); + if (!skb) + return -ENOMEM; + + scan_id = WMI_HOST_SCAN_REQ_ID_PREFIX; + scan_id |= arg->scan_id; + + scan_req_id = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX; + scan_req_id |= arg->scan_req_id; + + cmd = (struct wmi_start_scan_cmd *)skb->data; + cmd->scan_id = __cpu_to_le32(scan_id); + cmd->scan_req_id = __cpu_to_le32(scan_req_id); + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->scan_priority = __cpu_to_le32(arg->scan_priority); + cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events); + cmd->dwell_time_active = __cpu_to_le32(arg->dwell_time_active); + cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive); + cmd->min_rest_time = __cpu_to_le32(arg->min_rest_time); + cmd->max_rest_time = __cpu_to_le32(arg->max_rest_time); + cmd->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time); + cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time); + cmd->idle_time = __cpu_to_le32(arg->idle_time); + cmd->max_scan_time = __cpu_to_le32(arg->max_scan_time); + cmd->probe_delay = __cpu_to_le32(arg->probe_delay); + cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags); + + /* TLV list starts after fields included in the struct */ + off = sizeof(*cmd); + + if (arg->n_channels) { + channels = (void *)skb->data + off; + channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG); + channels->num_chan = __cpu_to_le32(arg->n_channels); + + for (i = 0; i < arg->n_channels; i++) + channels->channel_list[i] = + __cpu_to_le32(arg->channels[i]); + + off += sizeof(*channels); + off += sizeof(__le32) * arg->n_channels; + } + + if (arg->n_ssids) { + ssids = (void *)skb->data + off; + ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG); + ssids->num_ssids = __cpu_to_le32(arg->n_ssids); + + for (i = 0; i < arg->n_ssids; i++) { + ssids->ssids[i].ssid_len = + __cpu_to_le32(arg->ssids[i].len); + memcpy(&ssids->ssids[i].ssid, + arg->ssids[i].ssid, + arg->ssids[i].len); + } + + off += sizeof(*ssids); + off += sizeof(struct wmi_ssid) * arg->n_ssids; + } + + if (arg->n_bssids) { + bssids = (void *)skb->data + off; + bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG); + bssids->num_bssid = __cpu_to_le32(arg->n_bssids); + + for (i = 0; i < arg->n_bssids; i++) + memcpy(&bssids->bssid_list[i], + arg->bssids[i].bssid, + ETH_ALEN); + + off += sizeof(*bssids); + off += sizeof(struct wmi_mac_addr) * arg->n_bssids; + } + + if (arg->ie_len) { + ie = (void *)skb->data + off; + ie->tag = __cpu_to_le32(WMI_IE_TAG); + ie->ie_len = __cpu_to_le32(arg->ie_len); + memcpy(ie->ie_data, arg->ie, arg->ie_len); + + off += sizeof(*ie); + off += roundup(arg->ie_len, 4); + } + + if (off != skb->len) { + dev_kfree_skb(skb); + return -EINVAL; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n"); + return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID); +} + +void ath10k_wmi_start_scan_init(struct ath10k *ar, + struct wmi_start_scan_arg *arg) +{ + /* setup commonly used values */ + arg->scan_req_id = 1; + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; + arg->dwell_time_active = 50; + arg->dwell_time_passive = 150; + arg->min_rest_time = 50; + arg->max_rest_time = 500; + arg->repeat_probe_time = 0; + arg->probe_spacing_time = 0; + arg->idle_time = 0; + arg->max_scan_time = 5000; + arg->probe_delay = 5; + arg->notify_scan_events = WMI_SCAN_EVENT_STARTED + | WMI_SCAN_EVENT_COMPLETED + | WMI_SCAN_EVENT_BSS_CHANNEL + | WMI_SCAN_EVENT_FOREIGN_CHANNEL + | WMI_SCAN_EVENT_DEQUEUED; + arg->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES; + arg->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT; + arg->n_bssids = 1; + arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF"; +} + +int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) +{ + struct wmi_stop_scan_cmd *cmd; + struct sk_buff *skb; + u32 scan_id; + u32 req_id; + + if (arg->req_id > 0xFFF) + return -EINVAL; + if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + scan_id = arg->u.scan_id; + scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX; + + req_id = arg->req_id; + req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX; + + cmd = (struct wmi_stop_scan_cmd *)skb->data; + cmd->req_type = __cpu_to_le32(arg->req_type); + cmd->vdev_id = __cpu_to_le32(arg->u.vdev_id); + cmd->scan_id = __cpu_to_le32(scan_id); + cmd->scan_req_id = __cpu_to_le32(req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", + arg->req_id, arg->req_type, arg->u.scan_id); + return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID); +} + +int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]) +{ + struct wmi_vdev_create_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_create_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_type = __cpu_to_le32(type); + cmd->vdev_subtype = __cpu_to_le32(subtype); + memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", + vdev_id, type, subtype, macaddr); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID); +} + +int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_delete_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_delete_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "WMI vdev delete id %d\n", vdev_id); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID); +} + +static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + enum wmi_cmd_id cmd_id) +{ + struct wmi_vdev_start_request_cmd *cmd; + struct sk_buff *skb; + const char *cmdname; + u32 flags = 0; + + if (cmd_id != WMI_VDEV_START_REQUEST_CMDID && + cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID) + return -EINVAL; + if (WARN_ON(arg->ssid && arg->ssid_len == 0)) + return -EINVAL; + if (WARN_ON(arg->hidden_ssid && !arg->ssid)) + return -EINVAL; + if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) + return -EINVAL; + + if (cmd_id == WMI_VDEV_START_REQUEST_CMDID) + cmdname = "start"; + else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID) + cmdname = "restart"; + else + return -EINVAL; /* should not happen, we already check cmd_id */ + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + if (arg->hidden_ssid) + flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + flags |= WMI_VDEV_START_PMF_ENABLED; + + cmd = (struct wmi_vdev_start_request_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->disable_hw_ack = __cpu_to_le32(arg->disable_hw_ack); + cmd->beacon_interval = __cpu_to_le32(arg->bcn_intval); + cmd->dtim_period = __cpu_to_le32(arg->dtim_period); + cmd->flags = __cpu_to_le32(flags); + cmd->bcn_tx_rate = __cpu_to_le32(arg->bcn_tx_rate); + cmd->bcn_tx_power = __cpu_to_le32(arg->bcn_tx_power); + + if (arg->ssid) { + cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len); + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + + cmd->chan.mhz = __cpu_to_le32(arg->channel.freq); + + cmd->chan.band_center_freq1 = + __cpu_to_le32(arg->channel.band_center_freq1); + + cmd->chan.mode = arg->channel.mode; + cmd->chan.min_power = arg->channel.min_power; + cmd->chan.max_power = arg->channel.max_power; + cmd->chan.reg_power = arg->channel.max_reg_power; + cmd->chan.reg_classid = arg->channel.reg_class_id; + cmd->chan.antenna_max = arg->channel.max_antenna_gain; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X," + "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq, + arg->channel.mode, flags, arg->channel.max_power); + + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +int ath10k_wmi_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + return ath10k_wmi_vdev_start_restart(ar, arg, + WMI_VDEV_START_REQUEST_CMDID); +} + +int ath10k_wmi_vdev_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + return ath10k_wmi_vdev_start_restart(ar, arg, + WMI_VDEV_RESTART_REQUEST_CMDID); +} + +int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_stop_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_stop_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID); +} + +int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +{ + struct wmi_vdev_up_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_up_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_assoc_id = __cpu_to_le32(aid); + memcpy(&cmd->vdev_bssid.addr, bssid, 6); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", + vdev_id, aid, bssid); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID); +} + +int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_down_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_down_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mgmt vdev down id 0x%x\n", vdev_id); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID); +} + +int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_param param_id, u32 param_value) +{ + struct wmi_vdev_set_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_set_param_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev id 0x%x set param %d value %d\n", + vdev_id, param_id, param_value); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID); +} + +int ath10k_wmi_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) +{ + struct wmi_vdev_install_key_cmd *cmd; + struct sk_buff *skb; + + if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL) + return -EINVAL; + if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_install_key_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->key_idx = __cpu_to_le32(arg->key_idx); + cmd->key_flags = __cpu_to_le32(arg->key_flags); + cmd->key_cipher = __cpu_to_le32(arg->key_cipher); + cmd->key_len = __cpu_to_le32(arg->key_len); + cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len); + cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len); + + if (arg->macaddr) + memcpy(cmd->peer_macaddr.addr, arg->macaddr, ETH_ALEN); + if (arg->key_data) + memcpy(cmd->key_data, arg->key_data, arg->key_len); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); +} + +int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_peer_create_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_create_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer create vdev_id %d peer_addr %pM\n", + vdev_id, peer_addr); + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID); +} + +int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_peer_delete_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_delete_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer delete vdev_id %d peer_addr %pM\n", + vdev_id, peer_addr); + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID); +} + +int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +{ + struct wmi_peer_flush_tids_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_flush_tids_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap); + memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", + vdev_id, peer_addr, tid_bitmap); + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID); +} + +int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, enum wmi_peer_param param_id, + u32 param_value) +{ + struct wmi_peer_set_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_set_param_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + memcpy(&cmd->peer_macaddr.addr, peer_addr, 6); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev %d peer 0x%pM set param %d value %d\n", + vdev_id, peer_addr, param_id, param_value); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID); +} + +int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct wmi_sta_powersave_mode_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->sta_ps_mode = __cpu_to_le32(psmode); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi set powersave id 0x%x mode %d\n", + vdev_id, psmode); + + return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID); +} + +int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value) +{ + struct wmi_sta_powersave_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_powersave_param_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(value); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi sta ps param vdev_id 0x%x param %d value %d\n", + vdev_id, param_id, value); + return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID); +} + +int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) +{ + struct wmi_ap_ps_peer_cmd *cmd; + struct sk_buff *skb; + + if (!mac) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_ps_peer_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(value); + memcpy(&cmd->peer_macaddr, mac, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", + vdev_id, param_id, value, mac); + + return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID); +} + +int ath10k_wmi_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) +{ + struct wmi_scan_chan_list_cmd *cmd; + struct sk_buff *skb; + struct wmi_channel_arg *ch; + struct wmi_channel *ci; + int len; + int i; + + len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel); + + skb = ath10k_wmi_alloc_skb(len); + if (!skb) + return -EINVAL; + + cmd = (struct wmi_scan_chan_list_cmd *)skb->data; + cmd->num_scan_chans = __cpu_to_le32(arg->n_channels); + + for (i = 0; i < arg->n_channels; i++) { + u32 flags = 0; + + ch = &arg->channels[i]; + ci = &cmd->chan_info[i]; + + if (ch->passive) + flags |= WMI_CHAN_FLAG_PASSIVE; + if (ch->allow_ibss) + flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED; + if (ch->allow_ht) + flags |= WMI_CHAN_FLAG_ALLOW_HT; + if (ch->allow_vht) + flags |= WMI_CHAN_FLAG_ALLOW_VHT; + if (ch->ht40plus) + flags |= WMI_CHAN_FLAG_HT40_PLUS; + + ci->mhz = __cpu_to_le32(ch->freq); + ci->band_center_freq1 = __cpu_to_le32(ch->freq); + ci->band_center_freq2 = 0; + ci->min_power = ch->min_power; + ci->max_power = ch->max_power; + ci->reg_power = ch->max_reg_power; + ci->antenna_max = ch->max_antenna_gain; + ci->antenna_max = 0; + + /* mode & flags share storage */ + ci->mode = ch->mode; + ci->flags |= __cpu_to_le32(flags); + } + + return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID); +} + +int ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_peer_assoc_complete_cmd *cmd; + struct sk_buff *skb; + + if (arg->peer_mpdu_density > 16) + return -EINVAL; + if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) + return -EINVAL; + if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->peer_new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1); + cmd->peer_associd = __cpu_to_le32(arg->peer_aid); + cmd->peer_flags = __cpu_to_le32(arg->peer_flags); + cmd->peer_caps = __cpu_to_le32(arg->peer_caps); + cmd->peer_listen_intval = __cpu_to_le32(arg->peer_listen_intval); + cmd->peer_ht_caps = __cpu_to_le32(arg->peer_ht_caps); + cmd->peer_max_mpdu = __cpu_to_le32(arg->peer_max_mpdu); + cmd->peer_mpdu_density = __cpu_to_le32(arg->peer_mpdu_density); + cmd->peer_rate_caps = __cpu_to_le32(arg->peer_rate_caps); + cmd->peer_nss = __cpu_to_le32(arg->peer_num_spatial_streams); + cmd->peer_vht_caps = __cpu_to_le32(arg->peer_vht_caps); + cmd->peer_phymode = __cpu_to_le32(arg->peer_phymode); + + memcpy(cmd->peer_macaddr.addr, arg->addr, ETH_ALEN); + + cmd->peer_legacy_rates.num_rates = + __cpu_to_le32(arg->peer_legacy_rates.num_rates); + memcpy(cmd->peer_legacy_rates.rates, arg->peer_legacy_rates.rates, + arg->peer_legacy_rates.num_rates); + + cmd->peer_ht_rates.num_rates = + __cpu_to_le32(arg->peer_ht_rates.num_rates); + memcpy(cmd->peer_ht_rates.rates, arg->peer_ht_rates.rates, + arg->peer_ht_rates.num_rates); + + cmd->peer_vht_rates.rx_max_rate = + __cpu_to_le32(arg->peer_vht_rates.rx_max_rate); + cmd->peer_vht_rates.rx_mcs_set = + __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set); + cmd->peer_vht_rates.tx_max_rate = + __cpu_to_le32(arg->peer_vht_rates.tx_max_rate); + cmd->peer_vht_rates.tx_mcs_set = + __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); +} + +int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg) +{ + struct wmi_bcn_tx_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bcn_tx_cmd *)skb->data; + cmd->hdr.vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->hdr.tx_rate = __cpu_to_le32(arg->tx_rate); + cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power); + cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len); + memcpy(cmd->bcn, arg->bcn, arg->bcn_len); + + return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID); +} + +static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, + const struct wmi_wmm_params_arg *arg) +{ + params->cwmin = __cpu_to_le32(arg->cwmin); + params->cwmax = __cpu_to_le32(arg->cwmax); + params->aifs = __cpu_to_le32(arg->aifs); + params->txop = __cpu_to_le32(arg->txop); + params->acm = __cpu_to_le32(arg->acm); + params->no_ack = __cpu_to_le32(arg->no_ack); +} + +int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg) +{ + struct wmi_pdev_set_wmm_params *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_wmm_params *)skb->data; + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be); + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk); + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi); + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID); +} + +int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) +{ + struct wmi_request_stats_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_request_stats_cmd *)skb->data; + cmd->stats_id = __cpu_to_le32(stats_id); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); + return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h new file mode 100644 index 00000000000..9555f5a0e04 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -0,0 +1,3052 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WMI_H_ +#define _WMI_H_ + +#include <linux/types.h> +#include <net/mac80211.h> + +/* + * This file specifies the WMI interface for the Unified Software + * Architecture. + * + * It includes definitions of all the commands and events. Commands are + * messages from the host to the target. Events and Replies are messages + * from the target to the host. + * + * Ownership of correctness in regards to WMI commands belongs to the host + * driver and the target is not required to validate parameters for value, + * proper range, or any other checking. + * + * Guidelines for extending this interface are below. + * + * 1. Add new WMI commands ONLY within the specified range - 0x9000 - 0x9fff + * + * 2. Use ONLY u32 type for defining member variables within WMI + * command/event structures. Do not use u8, u16, bool or + * enum types within these structures. + * + * 3. DO NOT define bit fields within structures. Implement bit fields + * using masks if necessary. Do not use the programming language's bit + * field definition. + * + * 4. Define macros for encode/decode of u8, u16 fields within + * the u32 variables. Use these macros for set/get of these fields. + * Try to use this to optimize the structure without bloating it with + * u32 variables for every lower sized field. + * + * 5. Do not use PACK/UNPACK attributes for the structures as each member + * variable is already 4-byte aligned by virtue of being a u32 + * type. + * + * 6. Comment each parameter part of the WMI command/event structure by + * using the 2 stars at the begining of C comment instead of one star to + * enable HTML document generation using Doxygen. + * + */ + +/* Control Path */ +struct wmi_cmd_hdr { + __le32 cmd_id; +} __packed; + +#define WMI_CMD_HDR_CMD_ID_MASK 0x00FFFFFF +#define WMI_CMD_HDR_CMD_ID_LSB 0 +#define WMI_CMD_HDR_PLT_PRIV_MASK 0xFF000000 +#define WMI_CMD_HDR_PLT_PRIV_LSB 24 + +#define HTC_PROTOCOL_VERSION 0x0002 +#define WMI_PROTOCOL_VERSION 0x0002 + +enum wmi_service_id { + WMI_SERVICE_BEACON_OFFLOAD = 0, /* beacon offload */ + WMI_SERVICE_SCAN_OFFLOAD, /* scan offload */ + WMI_SERVICE_ROAM_OFFLOAD, /* roam offload */ + WMI_SERVICE_BCN_MISS_OFFLOAD, /* beacon miss offload */ + WMI_SERVICE_STA_PWRSAVE, /* fake sleep + basic power save */ + WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */ + WMI_SERVICE_AP_UAPSD, /* uapsd on AP */ + WMI_SERVICE_AP_DFS, /* DFS on AP */ + WMI_SERVICE_11AC, /* supports 11ac */ + WMI_SERVICE_BLOCKACK, /* Supports triggering ADDBA/DELBA from host*/ + WMI_SERVICE_PHYERR, /* PHY error */ + WMI_SERVICE_BCN_FILTER, /* Beacon filter support */ + WMI_SERVICE_RTT, /* RTT (round trip time) support */ + WMI_SERVICE_RATECTRL, /* Rate-control */ + WMI_SERVICE_WOW, /* WOW Support */ + WMI_SERVICE_RATECTRL_CACHE, /* Rate-control caching */ + WMI_SERVICE_IRAM_TIDS, /* TIDs in IRAM */ + WMI_SERVICE_ARPNS_OFFLOAD, /* ARP NS Offload support */ + WMI_SERVICE_NLO, /* Network list offload service */ + WMI_SERVICE_GTK_OFFLOAD, /* GTK offload */ + WMI_SERVICE_SCAN_SCH, /* Scan Scheduler Service */ + WMI_SERVICE_CSA_OFFLOAD, /* CSA offload service */ + WMI_SERVICE_CHATTER, /* Chatter service */ + WMI_SERVICE_COEX_FREQAVOID, /* FW report freq range to avoid */ + WMI_SERVICE_PACKET_POWER_SAVE, /* packet power save service */ + WMI_SERVICE_FORCE_FW_HANG, /* To test fw recovery mechanism */ + WMI_SERVICE_GPIO, /* GPIO service */ + WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */ + WMI_STA_UAPSD_BASIC_AUTO_TRIG, /* UAPSD AC Trigger Generation */ + WMI_STA_UAPSD_VAR_AUTO_TRIG, /* -do- */ + WMI_SERVICE_STA_KEEP_ALIVE, /* STA keep alive mechanism support */ + WMI_SERVICE_TX_ENCAP, /* Packet type for TX encapsulation */ + + WMI_SERVICE_LAST, + WMI_MAX_SERVICE = 64 /* max service */ +}; + +static inline char *wmi_service_name(int service_id) +{ + switch (service_id) { + case WMI_SERVICE_BEACON_OFFLOAD: + return "BEACON_OFFLOAD"; + case WMI_SERVICE_SCAN_OFFLOAD: + return "SCAN_OFFLOAD"; + case WMI_SERVICE_ROAM_OFFLOAD: + return "ROAM_OFFLOAD"; + case WMI_SERVICE_BCN_MISS_OFFLOAD: + return "BCN_MISS_OFFLOAD"; + case WMI_SERVICE_STA_PWRSAVE: + return "STA_PWRSAVE"; + case WMI_SERVICE_STA_ADVANCED_PWRSAVE: + return "STA_ADVANCED_PWRSAVE"; + case WMI_SERVICE_AP_UAPSD: + return "AP_UAPSD"; + case WMI_SERVICE_AP_DFS: + return "AP_DFS"; + case WMI_SERVICE_11AC: + return "11AC"; + case WMI_SERVICE_BLOCKACK: + return "BLOCKACK"; + case WMI_SERVICE_PHYERR: + return "PHYERR"; + case WMI_SERVICE_BCN_FILTER: + return "BCN_FILTER"; + case WMI_SERVICE_RTT: + return "RTT"; + case WMI_SERVICE_RATECTRL: + return "RATECTRL"; + case WMI_SERVICE_WOW: + return "WOW"; + case WMI_SERVICE_RATECTRL_CACHE: + return "RATECTRL CACHE"; + case WMI_SERVICE_IRAM_TIDS: + return "IRAM TIDS"; + case WMI_SERVICE_ARPNS_OFFLOAD: + return "ARPNS_OFFLOAD"; + case WMI_SERVICE_NLO: + return "NLO"; + case WMI_SERVICE_GTK_OFFLOAD: + return "GTK_OFFLOAD"; + case WMI_SERVICE_SCAN_SCH: + return "SCAN_SCH"; + case WMI_SERVICE_CSA_OFFLOAD: + return "CSA_OFFLOAD"; + case WMI_SERVICE_CHATTER: + return "CHATTER"; + case WMI_SERVICE_COEX_FREQAVOID: + return "COEX_FREQAVOID"; + case WMI_SERVICE_PACKET_POWER_SAVE: + return "PACKET_POWER_SAVE"; + case WMI_SERVICE_FORCE_FW_HANG: + return "FORCE FW HANG"; + case WMI_SERVICE_GPIO: + return "GPIO"; + case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM: + return "MODULATED DTIM"; + case WMI_STA_UAPSD_BASIC_AUTO_TRIG: + return "BASIC UAPSD"; + case WMI_STA_UAPSD_VAR_AUTO_TRIG: + return "VAR UAPSD"; + case WMI_SERVICE_STA_KEEP_ALIVE: + return "STA KEEP ALIVE"; + case WMI_SERVICE_TX_ENCAP: + return "TX ENCAP"; + default: + return "UNKNOWN SERVICE\n"; + } +} + + +#define WMI_SERVICE_BM_SIZE \ + ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32)) + +/* 2 word representation of MAC addr */ +struct wmi_mac_addr { + union { + u8 addr[6]; + struct { + u32 word0; + u32 word1; + } __packed; + } __packed; +} __packed; + +/* macro to convert MAC address from WMI word format to char array */ +#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \ + (c_macaddr)[0] = ((pwmi_mac_addr)->word0) & 0xff; \ + (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \ + (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \ + (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \ + (c_macaddr)[4] = ((pwmi_mac_addr)->word1) & 0xff; \ + (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \ + } while (0) + +/* + * wmi command groups. + */ +enum wmi_cmd_group { + /* 0 to 2 are reserved */ + WMI_GRP_START = 0x3, + WMI_GRP_SCAN = WMI_GRP_START, + WMI_GRP_PDEV, + WMI_GRP_VDEV, + WMI_GRP_PEER, + WMI_GRP_MGMT, + WMI_GRP_BA_NEG, + WMI_GRP_STA_PS, + WMI_GRP_DFS, + WMI_GRP_ROAM, + WMI_GRP_OFL_SCAN, + WMI_GRP_P2P, + WMI_GRP_AP_PS, + WMI_GRP_RATE_CTRL, + WMI_GRP_PROFILE, + WMI_GRP_SUSPEND, + WMI_GRP_BCN_FILTER, + WMI_GRP_WOW, + WMI_GRP_RTT, + WMI_GRP_SPECTRAL, + WMI_GRP_STATS, + WMI_GRP_ARP_NS_OFL, + WMI_GRP_NLO_OFL, + WMI_GRP_GTK_OFL, + WMI_GRP_CSA_OFL, + WMI_GRP_CHATTER, + WMI_GRP_TID_ADDBA, + WMI_GRP_MISC, + WMI_GRP_GPIO, +}; + +#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) + +/* Command IDs and commande events. */ +enum wmi_cmd_id { + WMI_INIT_CMDID = 0x1, + + /* Scan specific commands */ + WMI_START_SCAN_CMDID = WMI_CMD_GRP(WMI_GRP_SCAN), + WMI_STOP_SCAN_CMDID, + WMI_SCAN_CHAN_LIST_CMDID, + WMI_SCAN_SCH_PRIO_TBL_CMDID, + + /* PDEV (physical device) specific commands */ + WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_CMD_GRP(WMI_GRP_PDEV), + WMI_PDEV_SET_CHANNEL_CMDID, + WMI_PDEV_SET_PARAM_CMDID, + WMI_PDEV_PKTLOG_ENABLE_CMDID, + WMI_PDEV_PKTLOG_DISABLE_CMDID, + WMI_PDEV_SET_WMM_PARAMS_CMDID, + WMI_PDEV_SET_HT_CAP_IE_CMDID, + WMI_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_PDEV_SET_QUIET_MODE_CMDID, + WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_PDEV_GET_TPC_CONFIG_CMDID, + WMI_PDEV_SET_BASE_MACADDR_CMDID, + + /* VDEV (virtual device) specific commands */ + WMI_VDEV_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_VDEV), + WMI_VDEV_DELETE_CMDID, + WMI_VDEV_START_REQUEST_CMDID, + WMI_VDEV_RESTART_REQUEST_CMDID, + WMI_VDEV_UP_CMDID, + WMI_VDEV_STOP_CMDID, + WMI_VDEV_DOWN_CMDID, + WMI_VDEV_SET_PARAM_CMDID, + WMI_VDEV_INSTALL_KEY_CMDID, + + /* peer specific commands */ + WMI_PEER_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_PEER), + WMI_PEER_DELETE_CMDID, + WMI_PEER_FLUSH_TIDS_CMDID, + WMI_PEER_SET_PARAM_CMDID, + WMI_PEER_ASSOC_CMDID, + WMI_PEER_ADD_WDS_ENTRY_CMDID, + WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_PEER_MCAST_GROUP_CMDID, + + /* beacon/management specific commands */ + WMI_BCN_TX_CMDID = WMI_CMD_GRP(WMI_GRP_MGMT), + WMI_PDEV_SEND_BCN_CMDID, + WMI_BCN_TMPL_CMDID, + WMI_BCN_FILTER_RX_CMDID, + WMI_PRB_REQ_FILTER_RX_CMDID, + WMI_MGMT_TX_CMDID, + WMI_PRB_TMPL_CMDID, + + /* commands to directly control BA negotiation directly from host. */ + WMI_ADDBA_CLEAR_RESP_CMDID = WMI_CMD_GRP(WMI_GRP_BA_NEG), + WMI_ADDBA_SEND_CMDID, + WMI_ADDBA_STATUS_CMDID, + WMI_DELBA_SEND_CMDID, + WMI_ADDBA_SET_RESP_CMDID, + WMI_SEND_SINGLEAMSDU_CMDID, + + /* Station power save specific config */ + WMI_STA_POWERSAVE_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_STA_PS), + WMI_STA_POWERSAVE_PARAM_CMDID, + WMI_STA_MIMO_PS_MODE_CMDID, + + /** DFS-specific commands */ + WMI_PDEV_DFS_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_DFS), + WMI_PDEV_DFS_DISABLE_CMDID, + + /* Roaming specific commands */ + WMI_ROAM_SCAN_MODE = WMI_CMD_GRP(WMI_GRP_ROAM), + WMI_ROAM_SCAN_RSSI_THRESHOLD, + WMI_ROAM_SCAN_PERIOD, + WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_ROAM_AP_PROFILE, + + /* offload scan specific commands */ + WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_CMD_GRP(WMI_GRP_OFL_SCAN), + WMI_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_OFL_SCAN_PERIOD, + + /* P2P specific commands */ + WMI_P2P_DEV_SET_DEVICE_INFO = WMI_CMD_GRP(WMI_GRP_P2P), + WMI_P2P_DEV_SET_DISCOVERABILITY, + WMI_P2P_GO_SET_BEACON_IE, + WMI_P2P_GO_SET_PROBE_RESP_IE, + WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + + /* AP power save specific config */ + WMI_AP_PS_PEER_PARAM_CMDID = WMI_CMD_GRP(WMI_GRP_AP_PS), + WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + + /* Rate-control specific commands */ + WMI_PEER_RATE_RETRY_SCHED_CMDID = + WMI_CMD_GRP(WMI_GRP_RATE_CTRL), + + /* WLAN Profiling commands. */ + WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_CMD_GRP(WMI_GRP_PROFILE), + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + + /* Suspend resume command Ids */ + WMI_PDEV_SUSPEND_CMDID = WMI_CMD_GRP(WMI_GRP_SUSPEND), + WMI_PDEV_RESUME_CMDID, + + /* Beacon filter commands */ + WMI_ADD_BCN_FILTER_CMDID = WMI_CMD_GRP(WMI_GRP_BCN_FILTER), + WMI_RMV_BCN_FILTER_CMDID, + + /* WOW Specific WMI commands*/ + WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_CMD_GRP(WMI_GRP_WOW), + WMI_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_WOW_ENABLE_CMDID, + WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + + /* RTT measurement related cmd */ + WMI_RTT_MEASREQ_CMDID = WMI_CMD_GRP(WMI_GRP_RTT), + WMI_RTT_TSF_CMDID, + + /* spectral scan commands */ + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_CMD_GRP(WMI_GRP_SPECTRAL), + WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + + /* F/W stats */ + WMI_REQUEST_STATS_CMDID = WMI_CMD_GRP(WMI_GRP_STATS), + + /* ARP OFFLOAD REQUEST*/ + WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_ARP_NS_OFL), + + /* NS offload confid*/ + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_NLO_OFL), + + /* GTK offload Specific WMI commands*/ + WMI_GTK_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_GTK_OFL), + + /* CSA offload Specific WMI commands*/ + WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_CSA_OFL), + WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + + /* Chatter commands*/ + WMI_CHATTER_SET_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_CHATTER), + + /* addba specific commands */ + WMI_PEER_TID_ADDBA_CMDID = WMI_CMD_GRP(WMI_GRP_TID_ADDBA), + WMI_PEER_TID_DELBA_CMDID, + + /* set station mimo powersave method */ + WMI_STA_DTIM_PS_METHOD_CMDID, + /* Configure the Station UAPSD AC Auto Trigger Parameters */ + WMI_STA_UAPSD_AUTO_TRIG_CMDID, + + /* STA Keep alive parameter configuration, + Requires WMI_SERVICE_STA_KEEP_ALIVE */ + WMI_STA_KEEPALIVE_CMD, + + /* misc command group */ + WMI_ECHO_CMDID = WMI_CMD_GRP(WMI_GRP_MISC), + WMI_PDEV_UTF_CMDID, + WMI_DBGLOG_CFG_CMDID, + WMI_PDEV_QVIT_CMDID, + WMI_PDEV_FTM_INTG_CMDID, + WMI_VDEV_SET_KEEPALIVE_CMDID, + WMI_VDEV_GET_KEEPALIVE_CMDID, + + /* GPIO Configuration */ + WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO), + WMI_GPIO_OUTPUT_CMDID, +}; + +enum wmi_event_id { + WMI_SERVICE_READY_EVENTID = 0x1, + WMI_READY_EVENTID, + + /* Scan specific events */ + WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN), + + /* PDEV specific events */ + WMI_PDEV_TPC_CONFIG_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PDEV), + WMI_CHAN_INFO_EVENTID, + WMI_PHYERR_EVENTID, + + /* VDEV specific events */ + WMI_VDEV_START_RESP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_VDEV), + WMI_VDEV_STOPPED_EVENTID, + WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + + /* peer specific events */ + WMI_PEER_STA_KICKOUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PEER), + + /* beacon/mgmt specific events */ + WMI_MGMT_RX_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MGMT), + WMI_HOST_SWBA_EVENTID, + WMI_TBTTOFFSET_UPDATE_EVENTID, + + /* ADDBA Related WMI Events*/ + WMI_TX_DELBA_COMPLETE_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_BA_NEG), + WMI_TX_ADDBA_COMPLETE_EVENTID, + + /* Roam event to trigger roaming on host */ + WMI_ROAM_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_ROAM), + WMI_PROFILE_MATCH, + + /* WoW */ + WMI_WOW_WAKEUP_HOST_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_WOW), + + /* RTT */ + WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_RTT), + WMI_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_RTT_ERROR_REPORT_EVENTID, + + /* GTK offload */ + WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GTK_OFL), + WMI_GTK_REKEY_FAIL_EVENTID, + + /* CSA IE received event */ + WMI_CSA_HANDLING_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_CSA_OFL), + + /* Misc events */ + WMI_ECHO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MISC), + WMI_PDEV_UTF_EVENTID, + WMI_DEBUG_MESG_EVENTID, + WMI_UPDATE_STATS_EVENTID, + WMI_DEBUG_PRINT_EVENTID, + WMI_DCS_INTERFERENCE_EVENTID, + WMI_PDEV_QVIT_EVENTID, + WMI_WLAN_PROFILE_DATA_EVENTID, + WMI_PDEV_FTM_INTG_EVENTID, + WMI_WLAN_FREQ_AVOID_EVENTID, + WMI_VDEV_GET_KEEPALIVE_EVENTID, + + /* GPIO Event */ + WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO), +}; + +enum wmi_phy_mode { + MODE_11A = 0, /* 11a Mode */ + MODE_11G = 1, /* 11b/g Mode */ + MODE_11B = 2, /* 11b Mode */ + MODE_11GONLY = 3, /* 11g only Mode */ + MODE_11NA_HT20 = 4, /* 11a HT20 mode */ + MODE_11NG_HT20 = 5, /* 11g HT20 mode */ + MODE_11NA_HT40 = 6, /* 11a HT40 mode */ + MODE_11NG_HT40 = 7, /* 11g HT40 mode */ + MODE_11AC_VHT20 = 8, + MODE_11AC_VHT40 = 9, + MODE_11AC_VHT80 = 10, + /* MODE_11AC_VHT160 = 11, */ + MODE_11AC_VHT20_2G = 11, + MODE_11AC_VHT40_2G = 12, + MODE_11AC_VHT80_2G = 13, + MODE_UNKNOWN = 14, + MODE_MAX = 14 +}; + +#define WMI_CHAN_LIST_TAG 0x1 +#define WMI_SSID_LIST_TAG 0x2 +#define WMI_BSSID_LIST_TAG 0x3 +#define WMI_IE_TAG 0x4 + +struct wmi_channel { + __le32 mhz; + __le32 band_center_freq1; + __le32 band_center_freq2; /* valid for 11ac, 80plus80 */ + union { + __le32 flags; /* WMI_CHAN_FLAG_ */ + struct { + u8 mode; /* only 6 LSBs */ + } __packed; + } __packed; + union { + __le32 reginfo0; + struct { + u8 min_power; + u8 max_power; + u8 reg_power; + u8 reg_classid; + } __packed; + } __packed; + union { + __le32 reginfo1; + struct { + u8 antenna_max; + } __packed; + } __packed; +} __packed; + +struct wmi_channel_arg { + u32 freq; + u32 band_center_freq1; + bool passive; + bool allow_ibss; + bool allow_ht; + bool allow_vht; + bool ht40plus; + /* note: power unit is 1/4th of dBm */ + u32 min_power; + u32 max_power; + u32 max_reg_power; + u32 max_antenna_gain; + u32 reg_class_id; + enum wmi_phy_mode mode; +}; + +enum wmi_channel_change_cause { + WMI_CHANNEL_CHANGE_CAUSE_NONE = 0, + WMI_CHANNEL_CHANGE_CAUSE_CSA, +}; + +#define WMI_CHAN_FLAG_HT40_PLUS (1 << 6) +#define WMI_CHAN_FLAG_PASSIVE (1 << 7) +#define WMI_CHAN_FLAG_ADHOC_ALLOWED (1 << 8) +#define WMI_CHAN_FLAG_AP_DISABLED (1 << 9) +#define WMI_CHAN_FLAG_DFS (1 << 10) +#define WMI_CHAN_FLAG_ALLOW_HT (1 << 11) +#define WMI_CHAN_FLAG_ALLOW_VHT (1 << 12) + +/* Indicate reason for channel switch */ +#define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13) + +#define WMI_MAX_SPATIAL_STREAM 3 + +/* HT Capabilities*/ +#define WMI_HT_CAP_ENABLED 0x0001 /* HT Enabled/ disabled */ +#define WMI_HT_CAP_HT20_SGI 0x0002 /* Short Guard Interval with HT20 */ +#define WMI_HT_CAP_DYNAMIC_SMPS 0x0004 /* Dynamic MIMO powersave */ +#define WMI_HT_CAP_TX_STBC 0x0008 /* B3 TX STBC */ +#define WMI_HT_CAP_TX_STBC_MASK_SHIFT 3 +#define WMI_HT_CAP_RX_STBC 0x0030 /* B4-B5 RX STBC */ +#define WMI_HT_CAP_RX_STBC_MASK_SHIFT 4 +#define WMI_HT_CAP_LDPC 0x0040 /* LDPC supported */ +#define WMI_HT_CAP_L_SIG_TXOP_PROT 0x0080 /* L-SIG TXOP Protection */ +#define WMI_HT_CAP_MPDU_DENSITY 0x0700 /* MPDU Density */ +#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8 +#define WMI_HT_CAP_HT40_SGI 0x0800 + +#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED | \ + WMI_HT_CAP_HT20_SGI | \ + WMI_HT_CAP_HT40_SGI | \ + WMI_HT_CAP_TX_STBC | \ + WMI_HT_CAP_RX_STBC | \ + WMI_HT_CAP_LDPC) + + +/* + * WMI_VHT_CAP_* these maps to ieee 802.11ac vht capability information + * field. The fields not defined here are not supported, or reserved. + * Do not change these masks and if you have to add new one follow the + * bitmask as specified by 802.11ac draft. + */ + +#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK 0x00000003 +#define WMI_VHT_CAP_RX_LDPC 0x00000010 +#define WMI_VHT_CAP_SGI_80MHZ 0x00000020 +#define WMI_VHT_CAP_TX_STBC 0x00000080 +#define WMI_VHT_CAP_RX_STBC_MASK 0x00000300 +#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT 8 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP 0x03800000 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT 23 +#define WMI_VHT_CAP_RX_FIXED_ANT 0x10000000 +#define WMI_VHT_CAP_TX_FIXED_ANT 0x20000000 + +/* The following also refer for max HT AMSDU */ +#define WMI_VHT_CAP_MAX_MPDU_LEN_3839 0x00000000 +#define WMI_VHT_CAP_MAX_MPDU_LEN_7935 0x00000001 +#define WMI_VHT_CAP_MAX_MPDU_LEN_11454 0x00000002 + +#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454 | \ + WMI_VHT_CAP_RX_LDPC | \ + WMI_VHT_CAP_SGI_80MHZ | \ + WMI_VHT_CAP_TX_STBC | \ + WMI_VHT_CAP_RX_STBC_MASK | \ + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP | \ + WMI_VHT_CAP_RX_FIXED_ANT | \ + WMI_VHT_CAP_TX_FIXED_ANT) + +/* + * Interested readers refer to Rx/Tx MCS Map definition as defined in + * 802.11ac + */ +#define WMI_VHT_MAX_MCS_4_SS_MASK(r, ss) ((3 & (r)) << (((ss) - 1) << 1)) +#define WMI_VHT_MAX_SUPP_RATE_MASK 0x1fff0000 +#define WMI_VHT_MAX_SUPP_RATE_MASK_SHIFT 16 + +enum { + REGDMN_MODE_11A = 0x00001, /* 11a channels */ + REGDMN_MODE_TURBO = 0x00002, /* 11a turbo-only channels */ + REGDMN_MODE_11B = 0x00004, /* 11b channels */ + REGDMN_MODE_PUREG = 0x00008, /* 11g channels (OFDM only) */ + REGDMN_MODE_11G = 0x00008, /* XXX historical */ + REGDMN_MODE_108G = 0x00020, /* 11a+Turbo channels */ + REGDMN_MODE_108A = 0x00040, /* 11g+Turbo channels */ + REGDMN_MODE_XR = 0x00100, /* XR channels */ + REGDMN_MODE_11A_HALF_RATE = 0x00200, /* 11A half rate channels */ + REGDMN_MODE_11A_QUARTER_RATE = 0x00400, /* 11A quarter rate channels */ + REGDMN_MODE_11NG_HT20 = 0x00800, /* 11N-G HT20 channels */ + REGDMN_MODE_11NA_HT20 = 0x01000, /* 11N-A HT20 channels */ + REGDMN_MODE_11NG_HT40PLUS = 0x02000, /* 11N-G HT40 + channels */ + REGDMN_MODE_11NG_HT40MINUS = 0x04000, /* 11N-G HT40 - channels */ + REGDMN_MODE_11NA_HT40PLUS = 0x08000, /* 11N-A HT40 + channels */ + REGDMN_MODE_11NA_HT40MINUS = 0x10000, /* 11N-A HT40 - channels */ + REGDMN_MODE_11AC_VHT20 = 0x20000, /* 5Ghz, VHT20 */ + REGDMN_MODE_11AC_VHT40PLUS = 0x40000, /* 5Ghz, VHT40 + channels */ + REGDMN_MODE_11AC_VHT40MINUS = 0x80000, /* 5Ghz VHT40 - channels */ + REGDMN_MODE_11AC_VHT80 = 0x100000, /* 5Ghz, VHT80 channels */ + REGDMN_MODE_ALL = 0xffffffff +}; + +#define REGDMN_CAP1_CHAN_HALF_RATE 0x00000001 +#define REGDMN_CAP1_CHAN_QUARTER_RATE 0x00000002 +#define REGDMN_CAP1_CHAN_HAL49GHZ 0x00000004 + +/* regulatory capabilities */ +#define REGDMN_EEPROM_EEREGCAP_EN_FCC_MIDBAND 0x0040 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_EVEN 0x0080 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_U2 0x0100 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_MIDBAND 0x0200 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_ODD 0x0400 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_NEW_11A 0x0800 + +struct hal_reg_capabilities { + /* regdomain value specified in EEPROM */ + __le32 eeprom_rd; + /*regdomain */ + __le32 eeprom_rd_ext; + /* CAP1 capabilities bit map. */ + __le32 regcap1; + /* REGDMN EEPROM CAP. */ + __le32 regcap2; + /* REGDMN MODE */ + __le32 wireless_modes; + __le32 low_2ghz_chan; + __le32 high_2ghz_chan; + __le32 low_5ghz_chan; + __le32 high_5ghz_chan; +} __packed; + +enum wlan_mode_capability { + WHAL_WLAN_11A_CAPABILITY = 0x1, + WHAL_WLAN_11G_CAPABILITY = 0x2, + WHAL_WLAN_11AG_CAPABILITY = 0x3, +}; + +/* structure used by FW for requesting host memory */ +struct wlan_host_mem_req { + /* ID of the request */ + __le32 req_id; + /* size of the of each unit */ + __le32 unit_size; + /* flags to indicate that + * the number units is dependent + * on number of resources(num vdevs num peers .. etc) + */ + __le32 num_unit_info; + /* + * actual number of units to allocate . if flags in the num_unit_info + * indicate that number of units is tied to number of a particular + * resource to allocate then num_units filed is set to 0 and host + * will derive the number units from number of the resources it is + * requesting. + */ + __le32 num_units; +} __packed; + +#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \ + ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \ + (1 << ((svc_id)%(sizeof(u32))))) != 0) + +/* + * The following struct holds optional payload for + * wmi_service_ready_event,e.g., 11ac pass some of the + * device capability to the host. + */ +struct wmi_service_ready_event { + __le32 sw_version; + __le32 sw_version_1; + __le32 abi_version; + /* WMI_PHY_CAPABILITY */ + __le32 phy_capability; + /* Maximum number of frag table entries that SW will populate less 1 */ + __le32 max_frag_entry; + __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + __le32 num_rf_chains; + /* + * The following field is only valid for service type + * WMI_SERVICE_11AC + */ + __le32 ht_cap_info; /* WMI HT Capability */ + __le32 vht_cap_info; /* VHT capability info field of 802.11ac */ + __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */ + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + struct hal_reg_capabilities hal_reg_capabilities; + __le32 sys_cap_info; + __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */ + /* + * Max beacon and Probe Response IE offload size + * (includes optional P2P IEs) + */ + __le32 max_bcn_ie_size; + /* + * request to host to allocate a chuck of memory and pss it down to FW + * via WM_INIT. FW uses this as FW extesnsion memory for saving its + * data structures. Only valid for low latency interfaces like PCIE + * where FW can access this memory directly (or) by DMA. + */ + __le32 num_mem_reqs; + struct wlan_host_mem_req mem_reqs[1]; +} __packed; + +/* + * status consists of upper 16 bits fo int status and lower 16 bits of + * module ID that retuned status + */ +#define WLAN_INIT_STATUS_SUCCESS 0x0 +#define WLAN_GET_INIT_STATUS_REASON(status) ((status) & 0xffff) +#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff) + +#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ) +#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ) + +struct wmi_ready_event { + __le32 sw_version; + __le32 abi_version; + struct wmi_mac_addr mac_addr; + __le32 status; +} __packed; + +struct wmi_resource_config { + /* number of virtual devices (VAPs) to support */ + __le32 num_vdevs; + + /* number of peer nodes to support */ + __le32 num_peers; + + /* + * In offload mode target supports features like WOW, chatter and + * other protocol offloads. In order to support them some + * functionalities like reorder buffering, PN checking need to be + * done in target. This determines maximum number of peers suported + * by target in offload mode + */ + __le32 num_offload_peers; + + /* For target-based RX reordering */ + __le32 num_offload_reorder_bufs; + + /* number of keys per peer */ + __le32 num_peer_keys; + + /* total number of TX/RX data TIDs */ + __le32 num_tids; + + /* + * max skid for resolving hash collisions + * + * The address search table is sparse, so that if two MAC addresses + * result in the same hash value, the second of these conflicting + * entries can slide to the next index in the address search table, + * and use it, if it is unoccupied. This ast_skid_limit parameter + * specifies the upper bound on how many subsequent indices to search + * over to find an unoccupied space. + */ + __le32 ast_skid_limit; + + /* + * the nominal chain mask for transmit + * + * The chain mask may be modified dynamically, e.g. to operate AP + * tx with a reduced number of chains if no clients are associated. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of tx chains. + */ + __le32 tx_chain_mask; + + /* + * the nominal chain mask for receive + * + * The chain mask may be modified dynamically, e.g. for a client + * to use a reduced number of chains for receive if the traffic to + * the client is low enough that it doesn't require downlink MIMO + * or antenna diversity. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of rx chains. + */ + __le32 rx_chain_mask; + + /* + * what rx reorder timeout (ms) to use for the AC + * + * Each WMM access class (voice, video, best-effort, background) will + * have its own timeout value to dictate how long to wait for missing + * rx MPDUs to arrive before flushing subsequent MPDUs that have + * already been received. + * This parameter specifies the timeout in milliseconds for each + * class. + */ + __le32 rx_timeout_pri_vi; + __le32 rx_timeout_pri_vo; + __le32 rx_timeout_pri_be; + __le32 rx_timeout_pri_bk; + + /* + * what mode the rx should decap packets to + * + * MAC can decap to RAW (no decap), native wifi or Ethernet types + * THis setting also determines the default TX behavior, however TX + * behavior can be modified on a per VAP basis during VAP init + */ + __le32 rx_decap_mode; + + /* what is the maximum scan requests than can be queued */ + __le32 scan_max_pending_reqs; + + /* maximum VDEV that could use BMISS offload */ + __le32 bmiss_offload_max_vdev; + + /* maximum VDEV that could use offload roaming */ + __le32 roam_offload_max_vdev; + + /* maximum AP profiles that would push to offload roaming */ + __le32 roam_offload_max_ap_profiles; + + /* + * how many groups to use for mcast->ucast conversion + * + * The target's WAL maintains a table to hold information regarding + * which peers belong to a given multicast group, so that if + * multicast->unicast conversion is enabled, the target can convert + * multicast tx frames to a series of unicast tx frames, to each + * peer within the multicast group. + This num_mcast_groups configuration parameter tells the target how + * many multicast groups to provide storage for within its multicast + * group membership table. + */ + __le32 num_mcast_groups; + + /* + * size to alloc for the mcast membership table + * + * This num_mcast_table_elems configuration parameter tells the + * target how many peer elements it needs to provide storage for in + * its multicast group membership table. + * These multicast group membership table elements are shared by the + * multicast groups stored within the table. + */ + __le32 num_mcast_table_elems; + + /* + * whether/how to do multicast->unicast conversion + * + * This configuration parameter specifies whether the target should + * perform multicast --> unicast conversion on transmit, and if so, + * what to do if it finds no entries in its multicast group + * membership table for the multicast IP address in the tx frame. + * Configuration value: + * 0 -> Do not perform multicast to unicast conversion. + * 1 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * drop the frame. + * 2 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * transmit the frame as multicast. + */ + __le32 mcast2ucast_mode; + + /* + * how much memory to allocate for a tx PPDU dbg log + * + * This parameter controls how much memory the target will allocate + * to store a log of tx PPDU meta-information (how large the PPDU + * was, when it was sent, whether it was successful, etc.) + */ + __le32 tx_dbg_log_size; + + /* how many AST entries to be allocated for WDS */ + __le32 num_wds_entries; + + /* + * MAC DMA burst size, e.g., For target PCI limit can be + * 0 -default, 1 256B + */ + __le32 dma_burst_size; + + /* + * Fixed delimiters to be inserted after every MPDU to + * account for interface latency to avoid underrun. + */ + __le32 mac_aggr_delim; + + /* + * determine whether target is responsible for detecting duplicate + * non-aggregate MPDU and timing out stale fragments. + * + * A-MPDU reordering is always performed on the target. + * + * 0: target responsible for frag timeout and dup checking + * 1: host responsible for frag timeout and dup checking + */ + __le32 rx_skip_defrag_timeout_dup_detection_check; + + /* + * Configuration for VoW : + * No of Video Nodes to be supported + * and Max no of descriptors for each Video link (node). + */ + __le32 vow_config; + + /* maximum VDEV that could use GTK offload */ + __le32 gtk_offload_max_vdev; + + /* Number of msdu descriptors target should use */ + __le32 num_msdu_desc; + + /* + * Max. number of Tx fragments per MSDU + * This parameter controls the max number of Tx fragments per MSDU. + * This is sent by the target as part of the WMI_SERVICE_READY event + * and is overriden by the OS shim as required. + */ + __le32 max_frag_entries; +} __packed; + +/* strucutre describing host memory chunk. */ +struct host_memory_chunk { + /* id of the request that is passed up in service ready */ + __le32 req_id; + /* the physical address the memory chunk */ + __le32 ptr; + /* size of the chunk */ + __le32 size; +} __packed; + +struct wmi_init_cmd { + struct wmi_resource_config resource_config; + __le32 num_host_mem_chunks; + + /* + * variable number of host memory chunks. + * This should be the last element in the structure + */ + struct host_memory_chunk host_mem_chunks[1]; +} __packed; + +/* TLV for channel list */ +struct wmi_chan_list { + __le32 tag; /* WMI_CHAN_LIST_TAG */ + __le32 num_chan; + __le32 channel_list[0]; +} __packed; + +struct wmi_bssid_list { + __le32 tag; /* WMI_BSSID_LIST_TAG */ + __le32 num_bssid; + struct wmi_mac_addr bssid_list[0]; +} __packed; + +struct wmi_ie_data { + __le32 tag; /* WMI_IE_TAG */ + __le32 ie_len; + u8 ie_data[0]; +} __packed; + +struct wmi_ssid { + __le32 ssid_len; + u8 ssid[32]; +} __packed; + +struct wmi_ssid_list { + __le32 tag; /* WMI_SSID_LIST_TAG */ + __le32 num_ssids; + struct wmi_ssid ssids[0]; +} __packed; + +/* prefix used by scan requestor ids on the host */ +#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000 + +/* prefix used by scan request ids generated on the host */ +/* host cycles through the lower 12 bits to generate ids */ +#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +/* Scan priority numbers must be sequential, starting with 0 */ +enum wmi_scan_priority { + WMI_SCAN_PRIORITY_VERY_LOW = 0, + WMI_SCAN_PRIORITY_LOW, + WMI_SCAN_PRIORITY_MEDIUM, + WMI_SCAN_PRIORITY_HIGH, + WMI_SCAN_PRIORITY_VERY_HIGH, + WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */ +}; + +struct wmi_start_scan_cmd { + /* Scan ID */ + __le32 scan_id; + /* Scan requestor ID */ + __le32 scan_req_id; + /* VDEV id(interface) that is requesting scan */ + __le32 vdev_id; + /* Scan Priority, input to scan scheduler */ + __le32 scan_priority; + /* Scan events subscription */ + __le32 notify_scan_events; + /* dwell time in msec on active channels */ + __le32 dwell_time_active; + /* dwell time in msec on passive channels */ + __le32 dwell_time_passive; + /* + * min time in msec on the BSS channel,only valid if atleast one + * VDEV is active + */ + __le32 min_rest_time; + /* + * max rest time in msec on the BSS channel,only valid if at least + * one VDEV is active + */ + /* + * the scanner will rest on the bss channel at least min_rest_time + * after min_rest_time the scanner will start checking for tx/rx + * activity on all VDEVs. if there is no activity the scanner will + * switch to off channel. if there is activity the scanner will let + * the radio on the bss channel until max_rest_time expires.at + * max_rest_time scanner will switch to off channel irrespective of + * activity. activity is determined by the idle_time parameter. + */ + __le32 max_rest_time; + /* + * time before sending next set of probe requests. + * The scanner keeps repeating probe requests transmission with + * period specified by repeat_probe_time. + * The number of probe requests specified depends on the ssid_list + * and bssid_list + */ + __le32 repeat_probe_time; + /* time in msec between 2 consequetive probe requests with in a set. */ + __le32 probe_spacing_time; + /* + * data inactivity time in msec on bss channel that will be used by + * scanner for measuring the inactivity. + */ + __le32 idle_time; + /* maximum time in msec allowed for scan */ + __le32 max_scan_time; + /* + * delay in msec before sending first probe request after switching + * to a channel + */ + __le32 probe_delay; + /* Scan control flags */ + __le32 scan_ctrl_flags; + + /* Burst duration time in msecs */ + __le32 burst_duration; + /* + * TLV (tag length value ) paramerters follow the scan_cmd structure. + * TLV can contain channel list, bssid list, ssid list and + * ie. the TLV tags are defined above; + */ +} __packed; + +struct wmi_ssid_arg { + int len; + const u8 *ssid; +}; + +struct wmi_bssid_arg { + const u8 *bssid; +}; + +struct wmi_start_scan_arg { + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 scan_priority; + u32 notify_scan_events; + u32 dwell_time_active; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + u32 scan_ctrl_flags; + + u32 ie_len; + u32 n_channels; + u32 n_ssids; + u32 n_bssids; + + u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; + u32 channels[64]; + struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; + struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; +}; + +/* scan control flags */ + +/* passively scan all channels including active channels */ +#define WMI_SCAN_FLAG_PASSIVE 0x1 +/* add wild card ssid probe request even though ssid_list is specified. */ +#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2 +/* add cck rates to rates/xrate ie for the generated probe request */ +#define WMI_SCAN_ADD_CCK_RATES 0x4 +/* add ofdm rates to rates/xrate ie for the generated probe request */ +#define WMI_SCAN_ADD_OFDM_RATES 0x8 +/* To enable indication of Chan load and Noise floor to host */ +#define WMI_SCAN_CHAN_STAT_EVENT 0x10 +/* Filter Probe request frames */ +#define WMI_SCAN_FILTER_PROBE_REQ 0x20 +/* When set, DFS channels will not be scanned */ +#define WMI_SCAN_BYPASS_DFS_CHN 0x40 +/* Different FW scan engine may choose to bail out on errors. + * Allow the driver to have influence over that. */ +#define WMI_SCAN_CONTINUE_ON_ERROR 0x80 + +/* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */ +#define WMI_SCAN_CLASS_MASK 0xFF000000 + + +enum wmi_stop_scan_type { + WMI_SCAN_STOP_ONE = 0x00000000, /* stop by scan_id */ + WMI_SCAN_STOP_VDEV_ALL = 0x01000000, /* stop by vdev_id */ + WMI_SCAN_STOP_ALL = 0x04000000, /* stop all scans */ +}; + +struct wmi_stop_scan_cmd { + __le32 scan_req_id; + __le32 scan_id; + __le32 req_type; + __le32 vdev_id; +} __packed; + +struct wmi_stop_scan_arg { + u32 req_id; + enum wmi_stop_scan_type req_type; + union { + u32 scan_id; + u32 vdev_id; + } u; +}; + +struct wmi_scan_chan_list_cmd { + __le32 num_scan_chans; + struct wmi_channel chan_info[0]; +} __packed; + +struct wmi_scan_chan_list_arg { + u32 n_channels; + struct wmi_channel_arg *channels; +}; + +enum wmi_bss_filter { + WMI_BSS_FILTER_NONE = 0, /* no beacons forwarded */ + WMI_BSS_FILTER_ALL, /* all beacons forwarded */ + WMI_BSS_FILTER_PROFILE, /* only beacons matching profile */ + WMI_BSS_FILTER_ALL_BUT_PROFILE, /* all but beacons matching profile */ + WMI_BSS_FILTER_CURRENT_BSS, /* only beacons matching current BSS */ + WMI_BSS_FILTER_ALL_BUT_BSS, /* all but beacons matching BSS */ + WMI_BSS_FILTER_PROBED_SSID, /* beacons matching probed ssid */ + WMI_BSS_FILTER_LAST_BSS, /* marker only */ +}; + +enum wmi_scan_event_type { + WMI_SCAN_EVENT_STARTED = 0x1, + WMI_SCAN_EVENT_COMPLETED = 0x2, + WMI_SCAN_EVENT_BSS_CHANNEL = 0x4, + WMI_SCAN_EVENT_FOREIGN_CHANNEL = 0x8, + WMI_SCAN_EVENT_DEQUEUED = 0x10, + WMI_SCAN_EVENT_PREEMPTED = 0x20, /* possibly by high-prio scan */ + WMI_SCAN_EVENT_START_FAILED = 0x40, + WMI_SCAN_EVENT_RESTARTED = 0x80, + WMI_SCAN_EVENT_MAX = 0x8000 +}; + +enum wmi_scan_completion_reason { + WMI_SCAN_REASON_COMPLETED, + WMI_SCAN_REASON_CANCELLED, + WMI_SCAN_REASON_PREEMPTED, + WMI_SCAN_REASON_TIMEDOUT, + WMI_SCAN_REASON_MAX, +}; + +struct wmi_scan_event { + __le32 event_type; /* %WMI_SCAN_EVENT_ */ + __le32 reason; /* %WMI_SCAN_REASON_ */ + __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + __le32 scan_req_id; + __le32 scan_id; + __le32 vdev_id; +} __packed; + +/* + * This defines how much headroom is kept in the + * receive frame between the descriptor and the + * payload, in order for the WMI PHY error and + * management handler to insert header contents. + * + * This is in bytes. + */ +#define WMI_MGMT_RX_HDR_HEADROOM 52 + +/* + * This event will be used for sending scan results + * as well as rx mgmt frames to the host. The rx buffer + * will be sent as part of this WMI event. It would be a + * good idea to pass all the fields in the RX status + * descriptor up to the host. + */ +struct wmi_mgmt_rx_hdr { + __le32 channel; + __le32 snr; + __le32 rate; + __le32 phy_mode; + __le32 buf_len; + __le32 status; /* %WMI_RX_STATUS_ */ +} __packed; + +struct wmi_mgmt_rx_event { + struct wmi_mgmt_rx_hdr hdr; + u8 buf[0]; +} __packed; + +#define WMI_RX_STATUS_OK 0x00 +#define WMI_RX_STATUS_ERR_CRC 0x01 +#define WMI_RX_STATUS_ERR_DECRYPT 0x08 +#define WMI_RX_STATUS_ERR_MIC 0x10 +#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20 + +struct wmi_single_phyerr_rx_hdr { + /* TSF timestamp */ + __le32 tsf_timestamp; + + /* + * Current freq1, freq2 + * + * [7:0]: freq1[lo] + * [15:8] : freq1[hi] + * [23:16]: freq2[lo] + * [31:24]: freq2[hi] + */ + __le16 freq1; + __le16 freq2; + + /* + * Combined RSSI over all chains and channel width for this PHY error + * + * [7:0]: RSSI combined + * [15:8]: Channel width (MHz) + * [23:16]: PHY error code + * [24:16]: reserved (future use) + */ + u8 rssi_combined; + u8 chan_width_mhz; + u8 phy_err_code; + u8 rsvd0; + + /* + * RSSI on chain 0 through 3 + * + * This is formatted the same as the PPDU_START RX descriptor + * field: + * + * [7:0]: pri20 + * [15:8]: sec20 + * [23:16]: sec40 + * [31:24]: sec80 + */ + + __le32 rssi_chain0; + __le32 rssi_chain1; + __le32 rssi_chain2; + __le32 rssi_chain3; + + /* + * Last calibrated NF value for chain 0 through 3 + * + * nf_list_1: + * + * + [15:0] - chain 0 + * + [31:16] - chain 1 + * + * nf_list_2: + * + * + [15:0] - chain 2 + * + [31:16] - chain 3 + */ + __le32 nf_list_1; + __le32 nf_list_2; + + + /* Length of the frame */ + __le32 buf_len; +} __packed; + +struct wmi_single_phyerr_rx_event { + /* Phy error event header */ + struct wmi_single_phyerr_rx_hdr hdr; + /* frame buffer */ + u8 bufp[0]; +} __packed; + +struct wmi_comb_phyerr_rx_hdr { + /* Phy error phy error count */ + __le32 num_phyerr_events; + __le32 tsf_l32; + __le32 tsf_u32; +} __packed; + +struct wmi_comb_phyerr_rx_event { + /* Phy error phy error count */ + struct wmi_comb_phyerr_rx_hdr hdr; + /* + * frame buffer - contains multiple payloads in the order: + * header - payload, header - payload... + * (The header is of type: wmi_single_phyerr_rx_hdr) + */ + u8 bufp[0]; +} __packed; + +struct wmi_mgmt_tx_hdr { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 tx_rate; + __le32 tx_power; + __le32 buf_len; +} __packed; + +struct wmi_mgmt_tx_cmd { + struct wmi_mgmt_tx_hdr hdr; + u8 buf[0]; +} __packed; + +struct wmi_echo_event { + __le32 value; +} __packed; + +struct wmi_echo_cmd { + __le32 value; +} __packed; + + +struct wmi_pdev_set_regdomain_cmd { + __le32 reg_domain; + __le32 reg_domain_2G; + __le32 reg_domain_5G; + __le32 conformance_test_limit_2G; + __le32 conformance_test_limit_5G; +} __packed; + +/* Command to set/unset chip in quiet mode */ +struct wmi_pdev_set_quiet_cmd { + /* period in TUs */ + __le32 period; + + /* duration in TUs */ + __le32 duration; + + /* offset in TUs */ + __le32 next_start; + + /* enable/disable */ + __le32 enabled; +} __packed; + + +/* + * 802.11g protection mode. + */ +enum ath10k_protmode { + ATH10K_PROT_NONE = 0, /* no protection */ + ATH10K_PROT_CTSONLY = 1, /* CTS to self */ + ATH10K_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +enum wmi_beacon_gen_mode { + WMI_BEACON_STAGGERED_MODE = 0, + WMI_BEACON_BURST_MODE = 1 +}; + +enum wmi_csa_event_ies_present_flag { + WMI_CSA_IE_PRESENT = 0x00000001, + WMI_XCSA_IE_PRESENT = 0x00000002, + WMI_WBW_IE_PRESENT = 0x00000004, + WMI_CSWARP_IE_PRESENT = 0x00000008, +}; + +/* wmi CSA receive event from beacon frame */ +struct wmi_csa_event { + __le32 i_fc_dur; + /* Bit 0-15: FC */ + /* Bit 16-31: DUR */ + struct wmi_mac_addr i_addr1; + struct wmi_mac_addr i_addr2; + __le32 csa_ie[2]; + __le32 xcsa_ie[2]; + __le32 wb_ie[2]; + __le32 cswarp_ie; + __le32 ies_present_flag; /* wmi_csa_event_ies_present_flag */ +} __packed; + +/* the definition of different PDEV parameters */ +#define PDEV_DEFAULT_STATS_UPDATE_PERIOD 500 +#define VDEV_DEFAULT_STATS_UPDATE_PERIOD 500 +#define PEER_DEFAULT_STATS_UPDATE_PERIOD 500 + +enum wmi_pdev_param { + /* TX chian mask */ + WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + /* RX chian mask */ + WMI_PDEV_PARAM_RX_CHAIN_MASK, + /* TX power limit for 2G Radio */ + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + /* TX power limit for 5G Radio */ + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + /* TX power scale */ + WMI_PDEV_PARAM_TXPOWER_SCALE, + /* Beacon generation mode . 0: host, 1: target */ + WMI_PDEV_PARAM_BEACON_GEN_MODE, + /* Beacon generation mode . 0: staggered 1: bursted */ + WMI_PDEV_PARAM_BEACON_TX_MODE, + /* + * Resource manager off chan mode . + * 0: turn off off chan mode. 1: turn on offchan mode + */ + WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + /* + * Protection mode: + * 0: no protection 1:use CTS-to-self 2: use RTS/CTS + */ + WMI_PDEV_PARAM_PROTECTION_MODE, + /* Dynamic bandwidth 0: disable 1: enable */ + WMI_PDEV_PARAM_DYNAMIC_BW, + /* Non aggregrate/ 11g sw retry threshold.0-disable */ + WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + /* aggregrate sw retry threshold. 0-disable*/ + WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + /* Station kickout threshold (non of consecutive failures).0-disable */ + WMI_PDEV_PARAM_STA_KICKOUT_TH, + /* Aggerate size scaling configuration per AC */ + WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + /* LTR enable */ + WMI_PDEV_PARAM_LTR_ENABLE, + /* LTR latency for BE, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + /* LTR latency for BK, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + /* LTR latency for VI, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + /* LTR latency for VO, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + /* LTR AC latency timeout, in ms */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + /* LTR platform latency override, in us */ + WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + /* LTR-RX override, in us */ + WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + /* Tx activity timeout for LTR, in us */ + WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + /* L1SS state machine enable */ + WMI_PDEV_PARAM_L1SS_ENABLE, + /* Deep sleep state machine enable */ + WMI_PDEV_PARAM_DSLEEP_ENABLE, + /* RX buffering flush enable */ + WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + /* RX buffering matermark */ + WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + /* RX buffering timeout enable */ + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + /* RX buffering timeout value */ + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + /* pdev level stats update period in ms */ + WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + /* vdev level stats update period in ms */ + WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + /* peer level stats update period in ms */ + WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + /* beacon filter status update period */ + WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */ + WMI_PDEV_PARAM_PMF_QOS, + /* Access category on which ARP frames are sent */ + WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + /* DCS configuration */ + WMI_PDEV_PARAM_DCS, + /* Enable/Disable ANI on target */ + WMI_PDEV_PARAM_ANI_ENABLE, + /* configure the ANI polling period */ + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + /* configure the ANI listening period */ + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + /* configure OFDM immunity level */ + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + /* configure CCK immunity level */ + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + /* Enable/Disable CDD for 1x1 STAs in rate control module */ + WMI_PDEV_PARAM_DYNTXCHAIN, + /* Enable/Disable proxy STA */ + WMI_PDEV_PARAM_PROXY_STA, + /* Enable/Disable low power state when all VDEVs are inactive/idle. */ + WMI_PDEV_PARAM_IDLE_PS_CONFIG, + /* Enable/Disable power gating sleep */ + WMI_PDEV_PARAM_POWER_GATING_SLEEP, +}; + +struct wmi_pdev_set_param_cmd { + __le32 param_id; + __le32 param_value; +} __packed; + +struct wmi_pdev_get_tpc_config_cmd { + /* parameter */ + __le32 param; +} __packed; + +#define WMI_TPC_RATE_MAX 160 +#define WMI_TPC_TX_N_CHAIN 4 + +enum wmi_tpc_config_event_flag { + WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD = 0x1, + WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC = 0x2, + WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF = 0x4, +}; + +struct wmi_pdev_tpc_config_event { + __le32 reg_domain; + __le32 chan_freq; + __le32 phy_mode; + __le32 twice_antenna_reduction; + __le32 twice_max_rd_power; + s32 twice_antenna_gain; + __le32 power_limit; + __le32 rate_max; + __le32 num_tx_chain; + __le32 ctl; + __le32 flags; + s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + u8 rates_array[WMI_TPC_RATE_MAX]; +} __packed; + +/* Transmit power scale factor. */ +enum wmi_tp_scale { + WMI_TP_SCALE_MAX = 0, /* no scaling (default) */ + WMI_TP_SCALE_50 = 1, /* 50% of max (-3 dBm) */ + WMI_TP_SCALE_25 = 2, /* 25% of max (-6 dBm) */ + WMI_TP_SCALE_12 = 3, /* 12% of max (-9 dBm) */ + WMI_TP_SCALE_MIN = 4, /* min, but still on */ + WMI_TP_SCALE_SIZE = 5, /* max num of enum */ +}; + +struct wmi_set_channel_cmd { + /* channel (only frequency and mode info are used) */ + struct wmi_channel chan; +} __packed; + +struct wmi_pdev_chanlist_update_event { + /* number of channels */ + __le32 num_chan; + /* array of channels */ + struct wmi_channel channel_list[1]; +} __packed; + +#define WMI_MAX_DEBUG_MESG (sizeof(u32) * 32) + +struct wmi_debug_mesg_event { + /* message buffer, NULL terminated */ + char bufp[WMI_MAX_DEBUG_MESG]; +} __packed; + +enum { + /* P2P device */ + VDEV_SUBTYPE_P2PDEV = 0, + /* P2P client */ + VDEV_SUBTYPE_P2PCLI, + /* P2P GO */ + VDEV_SUBTYPE_P2PGO, + /* BT3.0 HS */ + VDEV_SUBTYPE_BT, +}; + +struct wmi_pdev_set_channel_cmd { + /* idnore power , only use flags , mode and freq */ + struct wmi_channel chan; +} __packed; + +/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */ +#define WMI_DSCP_MAP_MAX (64) +struct wmi_pdev_set_dscp_tid_map_cmd { + /* map indicating DSCP to TID conversion */ + __le32 dscp_to_tid_map[WMI_DSCP_MAP_MAX]; +} __packed; + +enum mcast_bcast_rate_id { + WMI_SET_MCAST_RATE, + WMI_SET_BCAST_RATE +}; + +struct mcast_bcast_rate { + enum mcast_bcast_rate_id rate_id; + __le32 rate; +} __packed; + +struct wmi_wmm_params { + __le32 cwmin; + __le32 cwmax; + __le32 aifs; + __le32 txop; + __le32 acm; + __le32 no_ack; +} __packed; + +struct wmi_pdev_set_wmm_params { + struct wmi_wmm_params ac_be; + struct wmi_wmm_params ac_bk; + struct wmi_wmm_params ac_vi; + struct wmi_wmm_params ac_vo; +} __packed; + +struct wmi_wmm_params_arg { + u32 cwmin; + u32 cwmax; + u32 aifs; + u32 txop; + u32 acm; + u32 no_ack; +}; + +struct wmi_pdev_set_wmm_params_arg { + struct wmi_wmm_params_arg ac_be; + struct wmi_wmm_params_arg ac_bk; + struct wmi_wmm_params_arg ac_vi; + struct wmi_wmm_params_arg ac_vo; +}; + +struct wal_dbg_tx_stats { + /* Num HTT cookies queued to dispatch list */ + __le32 comp_queued; + + /* Num HTT cookies dispatched */ + __le32 comp_delivered; + + /* Num MSDU queued to WAL */ + __le32 msdu_enqued; + + /* Num MPDU queue to WAL */ + __le32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + __le32 wmm_drop; + + /* Num Local frames queued */ + __le32 local_enqued; + + /* Num Local frames done */ + __le32 local_freed; + + /* Num queued to HW */ + __le32 hw_queued; + + /* Num PPDU reaped from HW */ + __le32 hw_reaped; + + /* Num underruns */ + __le32 underrun; + + /* Num PPDUs cleaned up in TX abort */ + __le32 tx_abort; + + /* Num MPDUs requed by SW */ + __le32 mpdus_requed; + + /* excessive retries */ + __le32 tx_ko; + + /* data hw rate code */ + __le32 data_rc; + + /* Scheduler self triggers */ + __le32 self_triggers; + + /* frames dropped due to excessive sw retries */ + __le32 sw_retry_failure; + + /* illegal rate phy errors */ + __le32 illgl_rate_phy_err; + + /* wal pdev continous xretry */ + __le32 pdev_cont_xretry; + + /* wal pdev continous xretry */ + __le32 pdev_tx_timeout; + + /* wal pdev resets */ + __le32 pdev_resets; + + __le32 phy_underrun; + + /* MPDU is more than txop limit */ + __le32 txop_ovf; +} __packed; + +struct wal_dbg_rx_stats { + /* Cnts any change in ring routing mid-ppdu */ + __le32 mid_ppdu_route_change; + + /* Total number of statuses processed */ + __le32 status_rcvd; + + /* Extra frags on rings 0-3 */ + __le32 r0_frags; + __le32 r1_frags; + __le32 r2_frags; + __le32 r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + __le32 htt_msdus; + __le32 htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + __le32 loc_msdus; + __le32 loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + __le32 oversize_amsdu; + + /* Number of PHY errors */ + __le32 phy_errs; + + /* Number of PHY errors drops */ + __le32 phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + __le32 mpdu_errs; +} __packed; + +struct wal_dbg_peer_stats { + /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */ + __le32 dummy; +} __packed; + +struct wal_dbg_stats { + struct wal_dbg_tx_stats tx; + struct wal_dbg_rx_stats rx; + struct wal_dbg_peer_stats peer; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PEER_STAT = 0x01, + WMI_REQUEST_AP_STAT = 0x02 +}; + +struct wmi_request_stats_cmd { + __le32 stats_id; + + /* + * Space to add parameters like + * peer mac addr + */ +} __packed; + +/* Suspend option */ +enum { + /* suspend */ + WMI_PDEV_SUSPEND, + + /* suspend and disable all interrupts */ + WMI_PDEV_SUSPEND_AND_DISABLE_INTR, +}; + +struct wmi_pdev_suspend_cmd { + /* suspend option sent to target */ + __le32 suspend_opt; +} __packed; + +struct wmi_stats_event { + __le32 stats_id; /* %WMI_REQUEST_ */ + /* + * number of pdev stats event structures + * (wmi_pdev_stats) 0 or 1 + */ + __le32 num_pdev_stats; + /* + * number of vdev stats event structures + * (wmi_vdev_stats) 0 or max vdevs + */ + __le32 num_vdev_stats; + /* + * number of peer stats event structures + * (wmi_peer_stats) 0 or max peers + */ + __le32 num_peer_stats; + __le32 num_bcnflt_stats; + /* + * followed by + * num_pdev_stats * size of(struct wmi_pdev_stats) + * num_vdev_stats * size of(struct wmi_vdev_stats) + * num_peer_stats * size of(struct wmi_peer_stats) + * + * By having a zero sized array, the pointer to data area + * becomes available without increasing the struct size + */ + u8 data[0]; +} __packed; + +/* + * PDEV statistics + * TODO: add all PDEV stats here + */ +struct wmi_pdev_stats { + __le32 chan_nf; /* Channel noise floor */ + __le32 tx_frame_count; /* TX frame count */ + __le32 rx_frame_count; /* RX frame count */ + __le32 rx_clear_count; /* rx clear count */ + __le32 cycle_count; /* cycle count */ + __le32 phy_err_count; /* Phy error count */ + __le32 chan_tx_pwr; /* channel tx power */ + struct wal_dbg_stats wal; /* WAL dbg stats */ +} __packed; + +/* + * VDEV statistics + * TODO: add all VDEV stats here + */ +struct wmi_vdev_stats { + __le32 vdev_id; +} __packed; + +/* + * peer statistics. + * TODO: add more stats + */ +struct wmi_peer_stats { + struct wmi_mac_addr peer_macaddr; + __le32 peer_rssi; + __le32 peer_tx_rate; +} __packed; + +struct wmi_vdev_create_cmd { + __le32 vdev_id; + __le32 vdev_type; + __le32 vdev_subtype; + struct wmi_mac_addr vdev_macaddr; +} __packed; + +enum wmi_vdev_type { + WMI_VDEV_TYPE_AP = 1, + WMI_VDEV_TYPE_STA = 2, + WMI_VDEV_TYPE_IBSS = 3, + WMI_VDEV_TYPE_MONITOR = 4, +}; + +enum wmi_vdev_subtype { + WMI_VDEV_SUBTYPE_NONE = 0, + WMI_VDEV_SUBTYPE_P2P_DEVICE = 1, + WMI_VDEV_SUBTYPE_P2P_CLIENT = 2, + WMI_VDEV_SUBTYPE_P2P_GO = 3, +}; + +/* values for vdev_subtype */ + +/* values for vdev_start_request flags */ +/* + * Indicates that AP VDEV uses hidden ssid. only valid for + * AP/GO */ +#define WMI_VDEV_START_HIDDEN_SSID (1<<0) +/* + * Indicates if robust management frame/management frame + * protection is enabled. For GO/AP vdevs, it indicates that + * it may support station/client associations with RMF enabled. + * For STA/client vdevs, it indicates that sta will + * associate with AP with RMF enabled. */ +#define WMI_VDEV_START_PMF_ENABLED (1<<1) + +struct wmi_p2p_noa_descriptor { + __le32 type_count; /* 255: continuous schedule, 0: reserved */ + __le32 duration; /* Absent period duration in micro seconds */ + __le32 interval; /* Absent period interval in micro seconds */ + __le32 start_time; /* 32 bit tsf time when in starts */ +} __packed; + +struct wmi_vdev_start_request_cmd { + /* WMI channel */ + struct wmi_channel chan; + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* requestor id identifying the caller module */ + __le32 requestor_id; + /* beacon interval from received beacon */ + __le32 beacon_interval; + /* DTIM Period from the received beacon */ + __le32 dtim_period; + /* Flags */ + __le32 flags; + /* ssid field. Only valid for AP/GO/IBSS/BTAmp VDEV type. */ + struct wmi_ssid ssid; + /* beacon/probe reponse xmit rate. Applicable for SoftAP. */ + __le32 bcn_tx_rate; + /* beacon/probe reponse xmit power. Applicable for SoftAP. */ + __le32 bcn_tx_power; + /* number of p2p NOA descriptor(s) from scan entry */ + __le32 num_noa_descriptors; + /* + * Disable H/W ack. This used by WMI_VDEV_RESTART_REQUEST_CMDID. + * During CAC, Our HW shouldn't ack ditected frames + */ + __le32 disable_hw_ack; + /* actual p2p NOA descriptor from scan entry */ + struct wmi_p2p_noa_descriptor noa_descriptors[2]; +} __packed; + +struct wmi_vdev_restart_request_cmd { + struct wmi_vdev_start_request_cmd vdev_start_request_cmd; +} __packed; + +struct wmi_vdev_start_request_arg { + u32 vdev_id; + struct wmi_channel_arg channel; + u32 bcn_intval; + u32 dtim_period; + u8 *ssid; + u32 ssid_len; + u32 bcn_tx_rate; + u32 bcn_tx_power; + bool disable_hw_ack; + bool hidden_ssid; + bool pmf_enabled; +}; + +struct wmi_vdev_delete_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_up_cmd { + __le32 vdev_id; + __le32 vdev_assoc_id; + struct wmi_mac_addr vdev_bssid; +} __packed; + +struct wmi_vdev_stop_cmd { + __le32 vdev_id; +} __packed; + +struct wmi_vdev_down_cmd { + __le32 vdev_id; +} __packed; + +struct wmi_vdev_standby_response_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_resume_response_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_set_param_cmd { + __le32 vdev_id; + __le32 param_id; + __le32 param_value; +} __packed; + +#define WMI_MAX_KEY_INDEX 3 +#define WMI_MAX_KEY_LEN 32 + +#define WMI_KEY_PAIRWISE 0x00 +#define WMI_KEY_GROUP 0x01 +#define WMI_KEY_TX_USAGE 0x02 /* default tx key - static wep */ + +struct wmi_key_seq_counter { + __le32 key_seq_counter_l; + __le32 key_seq_counter_h; +} __packed; + +#define WMI_CIPHER_NONE 0x0 /* clear key */ +#define WMI_CIPHER_WEP 0x1 +#define WMI_CIPHER_TKIP 0x2 +#define WMI_CIPHER_AES_OCB 0x3 +#define WMI_CIPHER_AES_CCM 0x4 +#define WMI_CIPHER_WAPI 0x5 +#define WMI_CIPHER_CKIP 0x6 +#define WMI_CIPHER_AES_CMAC 0x7 + +struct wmi_vdev_install_key_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 key_idx; + __le32 key_flags; + __le32 key_cipher; /* %WMI_CIPHER_ */ + struct wmi_key_seq_counter key_rsc_counter; + struct wmi_key_seq_counter key_global_rsc_counter; + struct wmi_key_seq_counter key_tsc_counter; + u8 wpi_key_rsc_counter[16]; + u8 wpi_key_tsc_counter[16]; + __le32 key_len; + __le32 key_txmic_len; + __le32 key_rxmic_len; + + /* contains key followed by tx mic followed by rx mic */ + u8 key_data[0]; +} __packed; + +struct wmi_vdev_install_key_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; + u32 key_flags; + u32 key_cipher; + u32 key_len; + u32 key_txmic_len; + u32 key_rxmic_len; + const void *key_data; +}; + +/* Preamble types to be used with VDEV fixed rate configuration */ +enum wmi_rate_preamble { + WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_HT, + WMI_RATE_PREAMBLE_VHT, +}; + +/* Value to disable fixed rate setting */ +#define WMI_FIXED_RATE_NONE (0xff) + +/* the definition of different VDEV parameters */ +enum wmi_vdev_param { + /* RTS Threshold */ + WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1, + /* Fragmentation threshold */ + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + /* beacon interval in TUs */ + WMI_VDEV_PARAM_BEACON_INTERVAL, + /* Listen interval in TUs */ + WMI_VDEV_PARAM_LISTEN_INTERVAL, + /* muticast rate in Mbps */ + WMI_VDEV_PARAM_MULTICAST_RATE, + /* management frame rate in Mbps */ + WMI_VDEV_PARAM_MGMT_TX_RATE, + /* slot time (long vs short) */ + WMI_VDEV_PARAM_SLOT_TIME, + /* preamble (long vs short) */ + WMI_VDEV_PARAM_PREAMBLE, + /* SWBA time (time before tbtt in msec) */ + WMI_VDEV_PARAM_SWBA_TIME, + /* time period for updating VDEV stats */ + WMI_VDEV_STATS_UPDATE_PERIOD, + /* age out time in msec for frames queued for station in power save */ + WMI_VDEV_PWRSAVE_AGEOUT_TIME, + /* + * Host SWBA interval (time in msec before tbtt for SWBA event + * generation). + */ + WMI_VDEV_HOST_SWBA_INTERVAL, + /* DTIM period (specified in units of num beacon intervals) */ + WMI_VDEV_PARAM_DTIM_PERIOD, + /* + * scheduler air time limit for this VDEV. used by off chan + * scheduler. + */ + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + /* enable/dsiable WDS for this VDEV */ + WMI_VDEV_PARAM_WDS, + /* ATIM Window */ + WMI_VDEV_PARAM_ATIM_WINDOW, + /* BMISS max */ + WMI_VDEV_PARAM_BMISS_COUNT_MAX, + /* BMISS first time */ + WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + /* BMISS final time */ + WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + /* WMM enables/disabled */ + WMI_VDEV_PARAM_FEATURE_WMM, + /* Channel width */ + WMI_VDEV_PARAM_CHWIDTH, + /* Channel Offset */ + WMI_VDEV_PARAM_CHEXTOFFSET, + /* Disable HT Protection */ + WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + /* Quick STA Kickout */ + WMI_VDEV_PARAM_STA_QUICKKICKOUT, + /* Rate to be used with Management frames */ + WMI_VDEV_PARAM_MGMT_RATE, + /* Protection Mode */ + WMI_VDEV_PARAM_PROTECTION_MODE, + /* Fixed rate setting */ + WMI_VDEV_PARAM_FIXED_RATE, + /* Short GI Enable/Disable */ + WMI_VDEV_PARAM_SGI, + /* Enable LDPC */ + WMI_VDEV_PARAM_LDPC, + /* Enable Tx STBC */ + WMI_VDEV_PARAM_TX_STBC, + /* Enable Rx STBC */ + WMI_VDEV_PARAM_RX_STBC, + /* Intra BSS forwarding */ + WMI_VDEV_PARAM_INTRA_BSS_FWD, + /* Setting Default xmit key for Vdev */ + WMI_VDEV_PARAM_DEF_KEYID, + /* NSS width */ + WMI_VDEV_PARAM_NSS, + /* Set the custom rate for the broadcast data frames */ + WMI_VDEV_PARAM_BCAST_DATA_RATE, + /* Set the custom rate (rate-code) for multicast data frames */ + WMI_VDEV_PARAM_MCAST_DATA_RATE, + /* Tx multicast packet indicate Enable/Disable */ + WMI_VDEV_PARAM_MCAST_INDICATE, + /* Tx DHCP packet indicate Enable/Disable */ + WMI_VDEV_PARAM_DHCP_INDICATE, + /* Enable host inspection of Tx unicast packet to unknown destination */ + WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + + /* The minimum amount of time AP begins to consider STA inactive */ + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered inactive when there is no recent + * TX/RX activity and no downlink frames are buffered for it. Once a + * STA exceeds the maximum idle inactive time, the AP will send an + * 802.11 data-null as a keep alive to verify the STA is still + * associated. If the STA does ACK the data-null, or if the data-null + * is buffered and the STA does not retrieve it, the STA will be + * considered unresponsive + * (see WMI_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS). + */ + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered unresponsive if there is no recent + * TX/RX activity and downlink frames are buffered for it. Once a STA + * exceeds the maximum unresponsive time, the AP will send a + * WMI_STA_KICKOUT event to the host so the STA can be deleted. */ + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + + /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + /* Enable/Disable RTS-CTS */ + WMI_VDEV_PARAM_ENABLE_RTSCTS, + /* Enable TXBFee/er */ + WMI_VDEV_PARAM_TXBF, + + /* Set packet power save */ + WMI_VDEV_PARAM_PACKET_POWERSAVE, + + /* + * Drops un-encrypted packets if eceived in an encrypted connection + * otherwise forwards to host. + */ + WMI_VDEV_PARAM_DROP_UNENCRY, + + /* + * Set the encapsulation type for frames. + */ + WMI_VDEV_PARAM_TX_ENCAP_TYPE, +}; + +/* slot time long */ +#define WMI_VDEV_SLOT_TIME_LONG 0x1 +/* slot time short */ +#define WMI_VDEV_SLOT_TIME_SHORT 0x2 +/* preablbe long */ +#define WMI_VDEV_PREAMBLE_LONG 0x1 +/* preablbe short */ +#define WMI_VDEV_PREAMBLE_SHORT 0x2 + +enum wmi_start_event_param { + WMI_VDEV_RESP_START_EVENT = 0, + WMI_VDEV_RESP_RESTART_EVENT, +}; + +struct wmi_vdev_start_response_event { + __le32 vdev_id; + __le32 req_id; + __le32 resp_type; /* %WMI_VDEV_RESP_ */ + __le32 status; +} __packed; + +struct wmi_vdev_standby_req_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_resume_req_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_stopped_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +/* + * common structure used for simple events + * (stopped, resume_req, standby response) + */ +struct wmi_vdev_simple_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +/* VDEV start response status codes */ +/* VDEV succesfully started */ +#define WMI_INIFIED_VDEV_START_RESPONSE_STATUS_SUCCESS 0x0 + +/* requested VDEV not found */ +#define WMI_INIFIED_VDEV_START_RESPONSE_INVALID_VDEVID 0x1 + +/* unsupported VDEV combination */ +#define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED 0x2 + +/* Beacon processing related command and event structures */ +struct wmi_bcn_tx_hdr { + __le32 vdev_id; + __le32 tx_rate; + __le32 tx_power; + __le32 bcn_len; +} __packed; + +struct wmi_bcn_tx_cmd { + struct wmi_bcn_tx_hdr hdr; + u8 *bcn[0]; +} __packed; + +struct wmi_bcn_tx_arg { + u32 vdev_id; + u32 tx_rate; + u32 tx_power; + u32 bcn_len; + const void *bcn; +}; + +/* Beacon filter */ +#define WMI_BCN_FILTER_ALL 0 /* Filter all beacons */ +#define WMI_BCN_FILTER_NONE 1 /* Pass all beacons */ +#define WMI_BCN_FILTER_RSSI 2 /* Pass Beacons RSSI >= RSSI threshold */ +#define WMI_BCN_FILTER_BSSID 3 /* Pass Beacons with matching BSSID */ +#define WMI_BCN_FILTER_SSID 4 /* Pass Beacons with matching SSID */ + +struct wmi_bcn_filter_rx_cmd { + /* Filter ID */ + __le32 bcn_filter_id; + /* Filter type - wmi_bcn_filter */ + __le32 bcn_filter; + /* Buffer len */ + __le32 bcn_filter_len; + /* Filter info (threshold, BSSID, RSSI) */ + u8 *bcn_filter_buf; +} __packed; + +/* Capabilities and IEs to be passed to firmware */ +struct wmi_bcn_prb_info { + /* Capabilities */ + __le32 caps; + /* ERP info */ + __le32 erp; + /* Advanced capabilities */ + /* HT capabilities */ + /* HT Info */ + /* ibss_dfs */ + /* wpa Info */ + /* rsn Info */ + /* rrm info */ + /* ath_ext */ + /* app IE */ +} __packed; + +struct wmi_bcn_tmpl_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* TIM IE offset from the beginning of the template. */ + __le32 tim_ie_offset; + /* beacon probe capabilities and IEs */ + struct wmi_bcn_prb_info bcn_prb_info; + /* beacon buffer length */ + __le32 buf_len; + /* variable length data */ + u8 data[1]; +} __packed; + +struct wmi_prb_tmpl_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* beacon probe capabilities and IEs */ + struct wmi_bcn_prb_info bcn_prb_info; + /* beacon buffer length */ + __le32 buf_len; + /* Variable length data */ + u8 data[1]; +} __packed; + +enum wmi_sta_ps_mode { + /* enable power save for the given STA VDEV */ + WMI_STA_PS_MODE_DISABLED = 0, + /* disable power save for a given STA VDEV */ + WMI_STA_PS_MODE_ENABLED = 1, +}; + +struct wmi_sta_powersave_mode_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + + /* + * Power save mode + * (see enum wmi_sta_ps_mode) + */ + __le32 sta_ps_mode; +} __packed; + +enum wmi_csa_offload_en { + WMI_CSA_OFFLOAD_DISABLE = 0, + WMI_CSA_OFFLOAD_ENABLE = 1, +}; + +struct wmi_csa_offload_enable_cmd { + __le32 vdev_id; + __le32 csa_offload_enable; +} __packed; + +struct wmi_csa_offload_chanswitch_cmd { + __le32 vdev_id; + struct wmi_channel chan; +} __packed; + +/* + * This parameter controls the policy for retrieving frames from AP while the + * STA is in sleep state. + * + * Only takes affect if the sta_ps_mode is enabled + */ +enum wmi_sta_ps_param_rx_wake_policy { + /* + * Wake up when ever there is an RX activity on the VDEV. In this mode + * the Power save SM(state machine) will come out of sleep by either + * sending null frame (or) a data frame (with PS==0) in response to TIM + * bit set in the received beacon frame from AP. + */ + WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0, + + /* + * Here the power save state machine will not wakeup in response to TIM + * bit, instead it will send a PSPOLL (or) UASPD trigger based on UAPSD + * configuration setup by WMISET_PS_SET_UAPSD WMI command. When all + * access categories are delivery-enabled, the station will send a + * UAPSD trigger frame, otherwise it will send a PS-Poll. + */ + WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1, +}; + +/* + * Number of tx frames/beacon that cause the power save SM to wake up. + * + * Value 1 causes the SM to wake up for every TX. Value 0 has a special + * meaning, It will cause the SM to never wake up. This is useful if you want + * to keep the system to sleep all the time for some kind of test mode . host + * can change this parameter any time. It will affect at the next tx frame. + */ +enum wmi_sta_ps_param_tx_wake_threshold { + WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0, + WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1, + + /* + * Values greater than one indicate that many TX attempts per beacon + * interval before the STA will wake up + */ +}; + +/* + * The maximum number of PS-Poll frames the FW will send in response to + * traffic advertised in TIM before waking up (by sending a null frame with PS + * = 0). Value 0 has a special meaning: there is no maximum count and the FW + * will send as many PS-Poll as are necessary to retrieve buffered BU. This + * parameter is used when the RX wake policy is + * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake + * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE. + */ +enum wmi_sta_ps_param_pspoll_count { + WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0, + /* + * Values greater than 0 indicate the maximum numer of PS-Poll frames + * FW will send before waking up. + */ +}; + +/* + * This will include the delivery and trigger enabled state for every AC. + * This is the negotiated state with AP. The host MLME needs to set this based + * on AP capability and the state Set in the association request by the + * station MLME.Lower 8 bits of the value specify the UAPSD configuration. + */ +#define WMI_UAPSD_AC_TYPE_DELI 0 +#define WMI_UAPSD_AC_TYPE_TRIG 1 + +#define WMI_UAPSD_AC_BIT_MASK(ac, type) \ + ((type == WMI_UAPSD_AC_TYPE_DELI) ? (1<<(ac<<1)) : (1<<((ac<<1)+1))) + +enum wmi_sta_ps_param_uapsd { + WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +enum wmi_sta_powersave_param { + /* + * Controls how frames are retrievd from AP while STA is sleeping + * + * (see enum wmi_sta_ps_param_rx_wake_policy) + */ + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + + /* + * The STA will go active after this many TX + * + * (see enum wmi_sta_ps_param_tx_wake_threshold) + */ + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, + + /* + * Number of PS-Poll to send before STA wakes up + * + * (see enum wmi_sta_ps_param_pspoll_count) + * + */ + WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, + + /* + * TX/RX inactivity time in msec before going to sleep. + * + * The power save SM will monitor tx/rx activity on the VDEV, if no + * activity for the specified msec of the parameter the Power save + * SM will go to sleep. + */ + WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, + + /* + * Set uapsd configuration. + * + * (see enum wmi_sta_ps_param_uapsd) + */ + WMI_STA_PS_PARAM_UAPSD = 4, +}; + +struct wmi_sta_powersave_param_cmd { + __le32 vdev_id; + __le32 param_id; /* %WMI_STA_PS_PARAM_ */ + __le32 param_value; +} __packed; + +/* No MIMO power save */ +#define WMI_STA_MIMO_PS_MODE_DISABLE +/* mimo powersave mode static*/ +#define WMI_STA_MIMO_PS_MODE_STATIC +/* mimo powersave mode dynamic */ +#define WMI_STA_MIMO_PS_MODE_DYNAMIC + +struct wmi_sta_mimo_ps_mode_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* mimo powersave mode as defined above */ + __le32 mimo_pwrsave_mode; +} __packed; + +/* U-APSD configuration of peer station from (re)assoc request and TSPECs */ +enum wmi_ap_ps_param_uapsd { + WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +/* U-APSD maximum service period of peer station */ +enum wmi_ap_ps_peer_param_max_sp { + WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0, + WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1, + WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2, + WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3, + MAX_WMI_AP_PS_PEER_PARAM_MAX_SP, +}; + +/* + * AP power save parameter + * Set a power save specific parameter for a peer station + */ +enum wmi_ap_ps_peer_param { + /* Set uapsd configuration for a given peer. + * + * Include the delivery and trigger enabled state for every AC. + * The host MLME needs to set this based on AP capability and stations + * request Set in the association request received from the station. + * + * Lower 8 bits of the value specify the UAPSD configuration. + * + * (see enum wmi_ap_ps_param_uapsd) + * The default value is 0. + */ + WMI_AP_PS_PEER_PARAM_UAPSD = 0, + + /* + * Set the service period for a UAPSD capable station + * + * The service period from wme ie in the (re)assoc request frame. + * + * (see enum wmi_ap_ps_peer_param_max_sp) + */ + WMI_AP_PS_PEER_PARAM_MAX_SP = 1, + + /* Time in seconds for aging out buffered frames for STA in PS */ + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2, +}; + +struct wmi_ap_ps_peer_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + + /* AP powersave param (see enum wmi_ap_ps_peer_param) */ + __le32 param_id; + + /* AP powersave param value */ + __le32 param_value; +} __packed; + +/* 128 clients = 4 words */ +#define WMI_TIM_BITMAP_ARRAY_SIZE 4 + +struct wmi_tim_info { + __le32 tim_len; + __le32 tim_mcast; + __le32 tim_bitmap[WMI_TIM_BITMAP_ARRAY_SIZE]; + __le32 tim_changed; + __le32 tim_num_ps_pending; +} __packed; + +/* Maximum number of NOA Descriptors supported */ +#define WMI_P2P_MAX_NOA_DESCRIPTORS 4 +#define WMI_P2P_OPPPS_ENABLE_BIT BIT(0) +#define WMI_P2P_OPPPS_CTWINDOW_OFFSET 1 +#define WMI_P2P_NOA_CHANGED_BIT BIT(0) + +struct wmi_p2p_noa_info { + /* Bit 0 - Flag to indicate an update in NOA schedule + Bits 7-1 - Reserved */ + u8 changed; + /* NOA index */ + u8 index; + /* Bit 0 - Opp PS state of the AP + Bits 1-7 - Ctwindow in TUs */ + u8 ctwindow_oppps; + /* Number of NOA descriptors */ + u8 num_descriptors; + + struct wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS]; +} __packed; + +struct wmi_bcn_info { + struct wmi_tim_info tim_info; + struct wmi_p2p_noa_info p2p_noa_info; +} __packed; + +struct wmi_host_swba_event { + __le32 vdev_map; + struct wmi_bcn_info bcn_info[1]; +} __packed; + +#define WMI_MAX_AP_VDEV 16 + +struct wmi_tbtt_offset_event { + __le32 vdev_map; + __le32 tbttoffset_list[WMI_MAX_AP_VDEV]; +} __packed; + + +struct wmi_peer_create_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_delete_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_flush_tids_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 peer_tid_bitmap; +} __packed; + +struct wmi_fixed_rate { + /* + * rate mode . 0: disable fixed rate (auto rate) + * 1: legacy (non 11n) rate specified as ieee rate 2*Mbps + * 2: ht20 11n rate specified as mcs index + * 3: ht40 11n rate specified as mcs index + */ + __le32 rate_mode; + /* + * 4 rate values for 4 rate series. series 0 is stored in byte 0 (LSB) + * and series 3 is stored at byte 3 (MSB) + */ + __le32 rate_series; + /* + * 4 retry counts for 4 rate series. retry count for rate 0 is stored + * in byte 0 (LSB) and retry count for rate 3 is stored at byte 3 + * (MSB) + */ + __le32 rate_retries; +} __packed; + +struct wmi_peer_fixed_rate_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* fixed rate */ + struct wmi_fixed_rate peer_fixed_rate; +} __packed; + +#define WMI_MGMT_TID 17 + +struct wmi_addba_clear_resp_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_addba_send_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; + /* Buffer/Window size*/ + __le32 buffersize; +} __packed; + +struct wmi_delba_send_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; + /* Is Initiator */ + __le32 initiator; + /* Reason code */ + __le32 reasoncode; +} __packed; + +struct wmi_addba_setresponse_cmd { + /* unique id identifying the vdev, generated by the caller */ + __le32 vdev_id; + /* peer mac address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; + /* status code */ + __le32 statuscode; +} __packed; + +struct wmi_send_singleamsdu_cmd { + /* unique id identifying the vdev, generated by the caller */ + __le32 vdev_id; + /* peer mac address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; +} __packed; + +enum wmi_peer_smps_state { + WMI_PEER_SMPS_PS_NONE = 0x0, + WMI_PEER_SMPS_STATIC = 0x1, + WMI_PEER_SMPS_DYNAMIC = 0x2 +}; + +enum wmi_peer_param { + WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */ + WMI_PEER_AMPDU = 0x2, + WMI_PEER_AUTHORIZE = 0x3, + WMI_PEER_CHAN_WIDTH = 0x4, + WMI_PEER_NSS = 0x5, + WMI_PEER_USE_4ADDR = 0x6 +}; + +struct wmi_peer_set_param_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 param_id; + __le32 param_value; +} __packed; + +#define MAX_SUPPORTED_RATES 128 + +struct wmi_rate_set { + /* total number of rates */ + __le32 num_rates; + /* + * rates (each 8bit value) packed into a 32 bit word. + * the rates are filled from least significant byte to most + * significant byte. + */ + __le32 rates[(MAX_SUPPORTED_RATES/4)+1]; +} __packed; + +struct wmi_rate_set_arg { + unsigned int num_rates; + u8 rates[MAX_SUPPORTED_RATES]; +}; + +/* + * NOTE: It would bea good idea to represent the Tx MCS + * info in one word and Rx in another word. This is split + * into multiple words for convenience + */ +struct wmi_vht_rate_set { + __le32 rx_max_rate; /* Max Rx data rate */ + __le32 rx_mcs_set; /* Negotiated RX VHT rates */ + __le32 tx_max_rate; /* Max Tx data rate */ + __le32 tx_mcs_set; /* Negotiated TX VHT rates */ +} __packed; + +struct wmi_vht_rate_set_arg { + u32 rx_max_rate; + u32 rx_mcs_set; + u32 tx_max_rate; + u32 tx_mcs_set; +}; + +struct wmi_peer_set_rates_cmd { + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* legacy rate set */ + struct wmi_rate_set peer_legacy_rates; + /* ht rate set */ + struct wmi_rate_set peer_ht_rates; +} __packed; + +struct wmi_peer_set_q_empty_callback_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + __le32 callback_enable; +} __packed; + +#define WMI_PEER_AUTH 0x00000001 +#define WMI_PEER_QOS 0x00000002 +#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 +#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 +#define WMI_PEER_APSD 0x00000800 +#define WMI_PEER_HT 0x00001000 +#define WMI_PEER_40MHZ 0x00002000 +#define WMI_PEER_STBC 0x00008000 +#define WMI_PEER_LDPC 0x00010000 +#define WMI_PEER_DYN_MIMOPS 0x00020000 +#define WMI_PEER_STATIC_MIMOPS 0x00040000 +#define WMI_PEER_SPATIAL_MUX 0x00200000 +#define WMI_PEER_VHT 0x02000000 +#define WMI_PEER_80MHZ 0x04000000 +#define WMI_PEER_PMF 0x08000000 + +/* + * Peer rate capabilities. + * + * This is of interest to the ratecontrol + * module which resides in the firmware. The bit definitions are + * consistent with that defined in if_athrate.c. + */ +#define WMI_RC_DS_FLAG 0x01 +#define WMI_RC_CW40_FLAG 0x02 +#define WMI_RC_SGI_FLAG 0x04 +#define WMI_RC_HT_FLAG 0x08 +#define WMI_RC_RTSCTS_FLAG 0x10 +#define WMI_RC_TX_STBC_FLAG 0x20 +#define WMI_RC_RX_STBC_FLAG 0xC0 +#define WMI_RC_RX_STBC_FLAG_S 6 +#define WMI_RC_WEP_TKIP_FLAG 0x100 +#define WMI_RC_TS_FLAG 0x200 +#define WMI_RC_UAPSD_FLAG 0x400 + +/* Maximum listen interval supported by hw in units of beacon interval */ +#define ATH10K_MAX_HW_LISTEN_INTERVAL 5 + +struct wmi_peer_assoc_complete_cmd { + struct wmi_mac_addr peer_macaddr; + __le32 vdev_id; + __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */ + __le32 peer_associd; /* 16 LSBs */ + __le32 peer_flags; + __le32 peer_caps; /* 16 LSBs */ + __le32 peer_listen_intval; + __le32 peer_ht_caps; + __le32 peer_max_mpdu; + __le32 peer_mpdu_density; /* 0..16 */ + __le32 peer_rate_caps; + struct wmi_rate_set peer_legacy_rates; + struct wmi_rate_set peer_ht_rates; + __le32 peer_nss; /* num of spatial streams */ + __le32 peer_vht_caps; + __le32 peer_phymode; + struct wmi_vht_rate_set peer_vht_rates; + /* HT Operation Element of the peer. Five bytes packed in 2 + * INT32 array and filled from lsb to msb. */ + __le32 peer_ht_info[2]; +} __packed; + +struct wmi_peer_assoc_complete_arg { + u8 addr[ETH_ALEN]; + u32 vdev_id; + bool peer_reassoc; + u16 peer_aid; + u32 peer_flags; /* see %WMI_PEER_ */ + u16 peer_caps; + u32 peer_listen_intval; + u32 peer_ht_caps; + u32 peer_max_mpdu; + u32 peer_mpdu_density; /* 0..16 */ + u32 peer_rate_caps; /* see %WMI_RC_ */ + struct wmi_rate_set_arg peer_legacy_rates; + struct wmi_rate_set_arg peer_ht_rates; + u32 peer_num_spatial_streams; + u32 peer_vht_caps; + enum wmi_phy_mode peer_phymode; + struct wmi_vht_rate_set_arg peer_vht_rates; +}; + +struct wmi_peer_add_wds_entry_cmd { + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* wds MAC addr */ + struct wmi_mac_addr wds_macaddr; +} __packed; + +struct wmi_peer_remove_wds_entry_cmd { + /* wds MAC addr */ + struct wmi_mac_addr wds_macaddr; +} __packed; + +struct wmi_peer_q_empty_callback_event { + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; +} __packed; + +/* + * Channel info WMI event + */ +struct wmi_chan_info_event { + __le32 err_code; + __le32 freq; + __le32 cmd_flags; + __le32 noise_floor; + __le32 rx_clear_count; + __le32 cycle_count; +} __packed; + +/* Beacon filter wmi command info */ +#define BCN_FLT_MAX_SUPPORTED_IES 256 +#define BCN_FLT_MAX_ELEMS_IE_LIST (BCN_FLT_MAX_SUPPORTED_IES / 32) + +struct bss_bcn_stats { + __le32 vdev_id; + __le32 bss_bcnsdropped; + __le32 bss_bcnsdelivered; +} __packed; + +struct bcn_filter_stats { + __le32 bcns_dropped; + __le32 bcns_delivered; + __le32 activefilters; + struct bss_bcn_stats bss_stats; +} __packed; + +struct wmi_add_bcn_filter_cmd { + u32 vdev_id; + u32 ie_map[BCN_FLT_MAX_ELEMS_IE_LIST]; +} __packed; + +enum wmi_sta_keepalive_method { + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1, + WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2, +}; + +/* note: ip4 addresses are in network byte order, i.e. big endian */ +struct wmi_sta_keepalive_arp_resp { + __be32 src_ip4_addr; + __be32 dest_ip4_addr; + struct wmi_mac_addr dest_mac_addr; +} __packed; + +struct wmi_sta_keepalive_cmd { + __le32 vdev_id; + __le32 enabled; + __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */ + __le32 interval; /* in seconds */ + struct wmi_sta_keepalive_arp_resp arp_resp; +} __packed; + +#define ATH10K_RTS_MAX 2347 +#define ATH10K_FRAGMT_THRESHOLD_MIN 540 +#define ATH10K_FRAGMT_THRESHOLD_MAX 2346 + +#define WMI_MAX_EVENT 0x1000 +/* Maximum number of pending TXed WMI packets */ +#define WMI_MAX_PENDING_TX_COUNT 128 +#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr) + +/* By default disable power save for IBSS */ +#define ATH10K_DEFAULT_ATIM 0 + +struct ath10k; +struct ath10k_vif; + +int ath10k_wmi_attach(struct ath10k *ar); +void ath10k_wmi_detach(struct ath10k *ar); +int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); +int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); +void ath10k_wmi_flush_tx(struct ath10k *ar); + +int ath10k_wmi_connect_htc_service(struct ath10k *ar); +int ath10k_wmi_pdev_set_channel(struct ath10k *ar, + const struct wmi_channel_arg *); +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar); +int ath10k_wmi_pdev_resume_target(struct ath10k *ar); +int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g); +int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, + u32 value); +int ath10k_wmi_cmd_init(struct ath10k *ar); +int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); +void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); +int ath10k_wmi_stop_scan(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg); +int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]); +int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id); +int ath10k_wmi_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *); +int ath10k_wmi_vdev_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *); +int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id); +int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid); +int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); +int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_param param_id, u32 param_value); +int ath10k_wmi_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg); +int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); +int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); +int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap); +int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, u32 param_value); +int ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg); +int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode); +int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value); +int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value); +int ath10k_wmi_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg); +int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); +int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg); +int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); + +#endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index 8e8bcc7a480..e9bc9e616b6 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -185,7 +185,6 @@ static int ath_ahb_probe(struct platform_device *pdev) err_free_hw: ieee80211_free_hw(hw); - platform_set_drvdata(pdev, NULL); err_iounmap: iounmap(mem); err_out: @@ -221,7 +220,6 @@ static int ath_ahb_remove(struct platform_device *pdev) ath5k_deinit_ah(ah); iounmap(ah->iobase); - platform_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); return 0; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 7f702fe3ecc..ce67ab791ea 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -60,6 +60,7 @@ #include <asm/unaligned.h> +#include <net/mac80211.h> #include "base.h" #include "reg.h" #include "debug.h" @@ -666,9 +667,46 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) return htype; } +static struct ieee80211_rate * +ath5k_get_rate(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *info, + struct ath5k_buf *bf, int idx) +{ + /* + * convert a ieee80211_tx_rate RC-table entry to + * the respective ieee80211_rate struct + */ + if (bf->rates[idx].idx < 0) { + return NULL; + } + + return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ]; +} + +static u16 +ath5k_get_rate_hw_value(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *info, + struct ath5k_buf *bf, int idx) +{ + struct ieee80211_rate *rate; + u16 hw_rate; + u8 rc_flags; + + rate = ath5k_get_rate(hw, info, bf, idx); + if (!rate) + return 0; + + rc_flags = bf->rates[idx].flags; + hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? + rate->hw_value_short : rate->hw_value; + + return hw_rate; +} + static int ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, - struct ath5k_txq *txq, int padsize) + struct ath5k_txq *txq, int padsize, + struct ieee80211_tx_control *control) { struct ath5k_desc *ds = bf->desc; struct sk_buff *skb = bf->skb; @@ -688,7 +726,11 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, DMA_TO_DEVICE); - rate = ieee80211_get_tx_rate(ah->hw, info); + ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates, + ARRAY_SIZE(bf->rates)); + + rate = ath5k_get_rate(ah->hw, info, bf, 0); + if (!rate) { ret = -EINVAL; goto err_unmap; @@ -698,8 +740,8 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, flags |= AR5K_TXDESC_NOACK; rc_flags = info->control.rates[0].flags; - hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? - rate->hw_value_short : rate->hw_value; + + hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0); pktlen = skb->len; @@ -722,12 +764,13 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw, info->control.vif, pktlen, info)); } + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, ieee80211_get_hdrlen_from_skb(skb), padsize, get_hw_packet_type(skb), (ah->ah_txpower.txp_requested * 2), hw_rate, - info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, + bf->rates[0].count, keyidx, ah->ah_tx_ant, flags, cts_rate, duration); if (ret) goto err_unmap; @@ -736,13 +779,15 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, if (ah->ah_capabilities.cap_has_mrr_support) { memset(mrr_rate, 0, sizeof(mrr_rate)); memset(mrr_tries, 0, sizeof(mrr_tries)); + for (i = 0; i < 3; i++) { - rate = ieee80211_get_alt_retry_rate(ah->hw, info, i); + + rate = ath5k_get_rate(ah->hw, info, bf, i); if (!rate) break; - mrr_rate[i] = rate->hw_value; - mrr_tries[i] = info->control.rates[i + 1].count; + mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i); + mrr_tries[i] = bf->rates[i].count; } ath5k_hw_setup_mrr_tx_desc(ah, ds, @@ -1515,7 +1560,7 @@ unlock: void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq) + struct ath5k_txq *txq, struct ieee80211_tx_control *control) { struct ath5k_hw *ah = hw->priv; struct ath5k_buf *bf; @@ -1555,7 +1600,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, bf->skb = skb; - if (ath5k_txbuf_setup(ah, bf, txq, padsize)) { + if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) { bf->skb = NULL; spin_lock_irqsave(&ah->txbuflock, flags); list_add_tail(&bf->list, &ah->txbuf); @@ -1571,11 +1616,13 @@ drop_packet: static void ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, - struct ath5k_txq *txq, struct ath5k_tx_status *ts) + struct ath5k_txq *txq, struct ath5k_tx_status *ts, + struct ath5k_buf *bf) { struct ieee80211_tx_info *info; u8 tries[3]; int i; + int size = 0; ah->stats.tx_all_count++; ah->stats.tx_bytes_count += skb->len; @@ -1587,6 +1634,9 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, ieee80211_tx_info_clear_status(info); + size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); + memcpy(info->status.rates, bf->rates, size); + for (i = 0; i < ts->ts_final_idx; i++) { struct ieee80211_tx_rate *r = &info->status.rates[i]; @@ -1663,7 +1713,7 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq) dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); - ath5k_tx_frame_completed(ah, skb, txq, &ts); + ath5k_tx_frame_completed(ah, skb, txq, &ts, bf); } /* @@ -1917,7 +1967,7 @@ ath5k_beacon_send(struct ath5k_hw *ah) skb = ieee80211_get_buffered_bc(ah->hw, vif); while (skb) { - ath5k_tx_queue(ah->hw, skb, ah->cabq); + ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL); if (ah->cabq->txq_len >= ah->cabq->txq_max) break; @@ -2442,7 +2492,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_MFP_CAPABLE | - IEEE80211_HW_REPORTS_TX_ACK_STATUS; + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_RC_TABLE; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index 6c94c7ff235..ca9a83ceeee 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -47,6 +47,7 @@ struct ath5k_hw; struct ath5k_txq; struct ieee80211_channel; struct ath_bus_ops; +struct ieee80211_tx_control; enum nl80211_iftype; enum ath5k_srev_type { @@ -61,11 +62,12 @@ struct ath5k_srev_name { }; struct ath5k_buf { - struct list_head list; - struct ath5k_desc *desc; /* virtual addr of desc */ - dma_addr_t daddr; /* physical addr of desc */ - struct sk_buff *skb; /* skbuff for buf */ - dma_addr_t skbaddr;/* physical addr of skb data */ + struct list_head list; + struct ath5k_desc *desc; /* virtual addr of desc */ + dma_addr_t daddr; /* physical addr of desc */ + struct sk_buff *skb; /* skbuff for buf */ + dma_addr_t skbaddr; /* physical addr of skb data */ + struct ieee80211_tx_rate rates[4]; /* number of multi-rate stages */ }; struct ath5k_vif { @@ -103,7 +105,7 @@ int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan); void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq); + struct ath5k_txq *txq, struct ieee80211_tx_control *control); const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val); diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 06f86f43571..81b686c6a37 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -66,7 +66,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, return; } - ath5k_tx_queue(hw, skb, &ah->txqs[qnum]); + ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control); } diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 5c9736a94e5..2437ad26949 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3175,10 +3175,21 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); - u32 id; + u32 id, freq; const struct ieee80211_mgmt *mgmt; bool more_data, queued; + /* default to the current channel, but use the one specified as argument + * if any + */ + freq = vif->ch_hint; + if (chan) + freq = chan->center_freq; + + /* never send freq zero to the firmware */ + if (WARN_ON(freq == 0)) + return -EINVAL; + mgmt = (const struct ieee80211_mgmt *) buf; if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && ieee80211_is_probe_resp(mgmt->frame_control) && @@ -3188,8 +3199,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, * command to allow the target to fill in the generic IEs. */ *cookie = 0; /* TX status not supported */ - return ath6kl_send_go_probe_resp(vif, buf, len, - chan->center_freq); + return ath6kl_send_go_probe_resp(vif, buf, len, freq); } id = vif->send_action_id++; @@ -3205,17 +3215,14 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { - queued = ath6kl_mgmt_powersave_ap(vif, - id, chan->center_freq, - wait, buf, - len, &more_data, no_cck); + queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len, + &more_data, no_cck); if (queued) return 0; } - return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, - chan->center_freq, wait, - buf, len, no_cck); + return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq, + wait, buf, len, no_cck); } static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, @@ -3679,6 +3686,20 @@ err: return NULL; } +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support ath6kl_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_4WAY_HANDSHAKE, + .n_patterns = WOW_MAX_FILTERS_PER_LIST, + .pattern_min_len = 1, + .pattern_max_len = WOW_PATTERN_SIZE, +}; +#endif + int ath6kl_cfg80211_init(struct ath6kl *ar) { struct wiphy *wiphy = ar->wiphy; @@ -3772,15 +3793,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); #ifdef CONFIG_PM - wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_GTK_REKEY_FAILURE | - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_4WAY_HANDSHAKE; - wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; - wiphy->wowlan.pattern_min_len = 1; - wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; + wiphy->wowlan = &ath6kl_wowlan_support; #endif wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index fe38b836cb2..dbfd17d0a5f 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -1240,20 +1240,14 @@ static ssize_t ath6kl_force_roam_write(struct file *file, char buf[20]; size_t len; u8 bssid[ETH_ALEN]; - int i; - int addr[ETH_ALEN]; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; - if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", - &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) - != ETH_ALEN) + if (!mac_pton(buf, bssid)) return -EINVAL; - for (i = 0; i < ETH_ALEN; i++) - bssid[i] = addr[i]; ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid); if (ret) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 40ffee6184f..6a67881f94d 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1696,10 +1696,16 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar) test_bit(WMI_READY, &ar->flag), WMI_TIMEOUT); + if (timeleft <= 0) { + clear_bit(WMI_READY, &ar->flag); + ath6kl_err("wmi is not ready or wait was interrupted: %ld\n", + timeleft); + ret = -EIO; + goto err_htc_stop; + } ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); - if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { ath6kl_info("%s %s fw %s api %d%s\n", ar->hw.name, @@ -1718,12 +1724,6 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar) goto err_htc_stop; } - if (!timeleft || signal_pending(current)) { - ath6kl_err("wmi is not ready or wait was interrupted\n"); - ret = -EIO; - goto err_htc_stop; - } - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); /* communicate the wmi protocol verision to the target */ diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index fb141454c6d..7126bdd4236 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -345,17 +345,17 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, { struct hif_scatter_req *s_req; struct bus_request *bus_req; - int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz; + int i, scat_req_sz, scat_list_sz, size; u8 *virt_buf; scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item); scat_req_sz = sizeof(*s_req) + scat_list_sz; if (!virt_scat) - sg_sz = sizeof(struct scatterlist) * n_scat_entry; + size = sizeof(struct scatterlist) * n_scat_entry; else - buf_sz = 2 * L1_CACHE_BYTES + - ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER; + size = 2 * L1_CACHE_BYTES + + ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER; for (i = 0; i < n_scat_req; i++) { /* allocate the scatter request */ @@ -364,7 +364,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, return -ENOMEM; if (virt_scat) { - virt_buf = kzalloc(buf_sz, GFP_KERNEL); + virt_buf = kzalloc(size, GFP_KERNEL); if (!virt_buf) { kfree(s_req); return -ENOMEM; @@ -374,7 +374,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf); } else { /* allocate sglist */ - s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL); + s_req->sgentries = kzalloc(size, GFP_KERNEL); if (!s_req->sgentries) { kfree(s_req); diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index bed0d337712..f38ff6a6255 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1061,6 +1061,22 @@ static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar) return; } +static int ath6kl_usb_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + /* + * cfg80211 suspend/WOW currently not supported for USB. + */ + return 0; +} + +static int ath6kl_usb_resume(struct ath6kl *ar) +{ + /* + * cfg80211 resume currently not supported for USB. + */ + return 0; +} + static const struct ath6kl_hif_ops ath6kl_usb_ops = { .diag_read32 = ath6kl_usb_diag_read32, .diag_write32 = ath6kl_usb_diag_write32, @@ -1074,6 +1090,8 @@ static const struct ath6kl_hif_ops ath6kl_usb_ops = { .pipe_map_service = ath6kl_usb_map_service_pipe, .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number, .cleanup_scatter = ath6kl_usb_cleanup_scatter, + .suspend = ath6kl_usb_suspend, + .resume = ath6kl_usb_resume, }; /* ath6kl usb driver registered functions */ @@ -1152,7 +1170,7 @@ static void ath6kl_usb_remove(struct usb_interface *interface) #ifdef CONFIG_PM -static int ath6kl_usb_suspend(struct usb_interface *interface, +static int ath6kl_usb_pm_suspend(struct usb_interface *interface, pm_message_t message) { struct ath6kl_usb *device; @@ -1162,7 +1180,7 @@ static int ath6kl_usb_suspend(struct usb_interface *interface, return 0; } -static int ath6kl_usb_resume(struct usb_interface *interface) +static int ath6kl_usb_pm_resume(struct usb_interface *interface) { struct ath6kl_usb *device; device = usb_get_intfdata(interface); @@ -1175,7 +1193,7 @@ static int ath6kl_usb_resume(struct usb_interface *interface) return 0; } -static int ath6kl_usb_reset_resume(struct usb_interface *intf) +static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf) { if (usb_get_intfdata(intf)) ath6kl_usb_remove(intf); @@ -1184,9 +1202,9 @@ static int ath6kl_usb_reset_resume(struct usb_interface *intf) #else -#define ath6kl_usb_suspend NULL -#define ath6kl_usb_resume NULL -#define ath6kl_usb_reset_resume NULL +#define ath6kl_usb_pm_suspend NULL +#define ath6kl_usb_pm_resume NULL +#define ath6kl_usb_pm_reset_resume NULL #endif @@ -1201,9 +1219,9 @@ MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids); static struct usb_driver ath6kl_usb_driver = { .name = "ath6kl_usb", .probe = ath6kl_usb_probe, - .suspend = ath6kl_usb_suspend, - .resume = ath6kl_usb_resume, - .reset_resume = ath6kl_usb_reset_resume, + .suspend = ath6kl_usb_pm_suspend, + .resume = ath6kl_usb_pm_resume, + .reset_resume = ath6kl_usb_pm_reset_resume, .disconnect = ath6kl_usb_remove, .id_table = ath6kl_usb_ids, .supports_autosuspend = true, diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 3c2cbc9d629..d491a317898 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -28,7 +28,7 @@ config ATH9K Atheros IEEE 802.11n AR5008, AR9001 and AR9002 family of chipsets. For a specific list of supported external cards, laptops that already ship with these cards and - APs that come with these cards refer to to ath9k wiki + APs that come with these cards refer to ath9k wiki products page: http://wireless.kernel.org/en/users/Drivers/ath9k/products @@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. -config ATH9K_MAC_DEBUG - bool "Atheros MAC statistics" - depends on ATH9K_DEBUGFS - default y - ---help--- - This option enables collection of statistics for Rx/Tx status - data and some other MAC related statistics - config ATH9K_LEGACY_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index d1ff3c246a1..072e4b53106 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -150,7 +150,6 @@ static int ath_ahb_probe(struct platform_device *pdev) free_irq(irq, sc); err_free_hw: ieee80211_free_hw(hw); - platform_set_drvdata(pdev, NULL); return ret; } @@ -164,7 +163,6 @@ static int ath_ahb_remove(struct platform_device *pdev) ath9k_deinit_device(sc); free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); - platform_set_drvdata(pdev, NULL); } return 0; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 7ecd40f07a7..4994bea809e 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = { { 5, 4, 1 }, /* lvl 5 */ { 6, 5, 1 }, /* lvl 6 */ { 7, 6, 1 }, /* lvl 7 */ - { 7, 6, 0 }, /* lvl 8 */ - { 7, 7, 0 } /* lvl 9 */ + { 7, 7, 1 }, /* lvl 8 */ + { 7, 8, 0 } /* lvl 9 */ }; #define ATH9K_ANI_OFDM_NUM_LEVEL \ ARRAY_SIZE(ofdm_level_table) @@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = { { 4, 0 }, /* lvl 4 */ { 5, 0 }, /* lvl 5 */ { 6, 0 }, /* lvl 6 */ - { 6, 0 }, /* lvl 7 (only for high rssi) */ - { 7, 0 } /* lvl 8 (only for high rssi) */ + { 7, 0 }, /* lvl 7 (only for high rssi) */ + { 8, 0 } /* lvl 8 (only for high rssi) */ }; #define ATH9K_ANI_CCK_NUM_LEVEL \ @@ -118,10 +118,10 @@ static void ath9k_ani_restart(struct ath_hw *ah) { struct ar5416AniState *aniState; - if (!DO_ANI(ah)) + if (!ah->curchan) return; - aniState = &ah->curchan->ani; + aniState = &ah->ani; aniState->listenTime = 0; ENABLE_REGWRITE_BUFFER(ah); @@ -143,7 +143,7 @@ static void ath9k_ani_restart(struct ath_hw *ah) static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, bool scan) { - struct ar5416AniState *aniState = &ah->curchan->ani; + struct ar5416AniState *aniState = &ah->ani; struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; @@ -177,10 +177,15 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH) weak_sig = true; - if (aniState->ofdmWeakSigDetect != weak_sig) - ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - entry_ofdm->ofdm_weak_signal_on); + /* + * OFDM Weak signal detection is always enabled for AP mode. + */ + if (ah->opmode != NL80211_IFTYPE_AP && + aniState->ofdmWeakSigDetect != weak_sig) { + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + entry_ofdm->ofdm_weak_signal_on); + } if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) { ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; @@ -195,10 +200,10 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) { struct ar5416AniState *aniState; - if (!DO_ANI(ah)) + if (!ah->curchan) return; - aniState = &ah->curchan->ani; + aniState = &ah->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false); @@ -210,7 +215,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, bool scan) { - struct ar5416AniState *aniState = &ah->curchan->ani; + struct ar5416AniState *aniState = &ah->ani; struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; @@ -251,10 +256,10 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) { struct ar5416AniState *aniState; - if (!DO_ANI(ah)) + if (!ah->curchan) return; - aniState = &ah->curchan->ani; + aniState = &ah->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1, @@ -269,7 +274,7 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) { struct ar5416AniState *aniState; - aniState = &ah->curchan->ani; + aniState = &ah->ani; /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && @@ -292,12 +297,12 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) */ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) { - struct ar5416AniState *aniState = &ah->curchan->ani; + struct ar5416AniState *aniState = &ah->ani; struct ath9k_channel *chan = ah->curchan; struct ath_common *common = ath9k_hw_common(ah); int ofdm_nil, cck_nil; - if (!DO_ANI(ah)) + if (!ah->curchan) return; BUG_ON(aniState == NULL); @@ -363,24 +368,13 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning); ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning); - /* - * enable phy counters if hw supports or if not, enable phy - * interrupts (so we can count each one) - */ ath9k_ani_restart(ah); - - ENABLE_REGWRITE_BUFFER(ah); - - REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); - REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); - - REGWRITE_BUFFER_FLUSH(ah); } static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - struct ar5416AniState *aniState = &ah->curchan->ani; + struct ar5416AniState *aniState = &ah->ani; u32 phyCnt1, phyCnt2; int32_t listenTime; @@ -415,10 +409,10 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) struct ath_common *common = ath9k_hw_common(ah); u32 ofdmPhyErrRate, cckPhyErrRate; - if (!DO_ANI(ah)) + if (!ah->curchan) return; - aniState = &ah->curchan->ani; + aniState = &ah->ani; if (!ath9k_hw_ani_read_counters(ah)) return; @@ -490,32 +484,22 @@ EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); void ath9k_hw_ani_init(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - int i; + struct ar5416AniState *ani = &ah->ani; ath_dbg(common, ANI, "Initialize ANI\n"); ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; - ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH; ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW; - for (i = 0; i < ARRAY_SIZE(ah->channels); i++) { - struct ath9k_channel *chan = &ah->channels[i]; - struct ar5416AniState *ani = &chan->ani; - - ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; - - ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; - - ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false; - - ani->ofdmsTurn = true; - - ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; - ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; - ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; - } + ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; + ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; + ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false; + ani->ofdmsTurn = true; + ani->ofdmWeakSigDetect = true; + ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; + ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; /* * since we expect some ongoing maintenance on the tables, let's sanity @@ -524,9 +508,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ah->aniperiod = ATH9K_ANI_PERIOD; ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL; - if (ah->config.enable_ani) - ah->proc_phyerr |= HAL_PROCESS_ANI; - ath9k_ani_restart(ah); ath9k_enable_mib_counters(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index dddb1361039..b54a3fb0188 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -17,32 +17,19 @@ #ifndef ANI_H #define ANI_H -#define HAL_PROCESS_ANI 0x00000001 - -#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan) - #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi) /* units are errors per second */ -#define ATH9K_ANI_OFDM_TRIG_HIGH 3500 +#define ATH9K_ANI_OFDM_TRIG_HIGH 3500 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000 -/* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_LOW 400 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900 -/* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_HIGH 600 - -/* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_LOW 300 -#define ATH9K_ANI_NOISE_IMMUNE_LVL 4 -#define ATH9K_ANI_USE_OFDM_WEAK_SIG true -#define ATH9K_ANI_CCK_WEAK_SIG_THR false - #define ATH9K_ANI_SPUR_IMMUNE_LVL 3 - #define ATH9K_ANI_FIRSTEP_LVL 2 #define ATH9K_ANI_RSSI_THR_HIGH 40 @@ -53,10 +40,6 @@ /* in ms */ #define ATH9K_ANI_POLLINTERVAL 1000 -#define HAL_NOISE_IMMUNE_MAX 4 -#define HAL_SPUR_IMMUNE_MAX 7 -#define HAL_FIRST_STEP_MAX 2 - #define ATH9K_SIG_FIRSTEP_SETTING_MIN 0 #define ATH9K_SIG_FIRSTEP_SETTING_MAX 20 #define ATH9K_SIG_SPUR_IMM_SETTING_MIN 0 @@ -111,7 +94,7 @@ struct ar5416AniState { u8 mrcCCK; u8 spurImmunityLevel; u8 firstepLevel; - u8 ofdmWeakSigDetect; + bool ofdmWeakSigDetect; u32 listenTime; u32 ofdmPhyErrCount; u32 cckPhyErrCount; @@ -119,8 +102,6 @@ struct ar5416AniState { }; struct ar5416Stats { - u32 ast_ani_niup; - u32 ast_ani_nidown; u32 ast_ani_spurup; u32 ast_ani_spurdown; u32 ast_ani_ofdmon; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 391da5ad6a9..d1acfe98918 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -931,7 +931,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; - struct ar5416AniState *aniState = &chan->ani; + struct ar5416AniState *aniState = &ah->ani; s32 value, value2; switch (cmd & ah->ani_function) { @@ -1207,7 +1207,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; - struct ar5416AniState *aniState = &chan->ani; + struct ar5416AniState *aniState = &ah->ani; struct ath9k_ani_default *iniDef; u32 val; @@ -1251,7 +1251,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; - aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = true; aniState->mrcCCK = false; /* not available on pre AR9003 */ } diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 830daa12feb..8dc2d089cde 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -38,10 +38,6 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah) else INIT_INI_ARRAY(&ah->iniPcieSerdes, ar9280PciePhy_clkreq_always_on_L1_9280); -#ifdef CONFIG_PM_SLEEP - INIT_INI_ARRAY(&ah->iniPcieSerdesWow, - ar9280PciePhy_awow); -#endif if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h index beb6162cf97..4d18c66a679 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h @@ -925,20 +925,6 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { {0x00004044, 0x00000000}, }; -static const u32 ar9280PciePhy_awow[][2] = { - /* Addr allmodes */ - {0x00004040, 0x9248fd00}, - {0x00004040, 0x24924924}, - {0x00004040, 0xa8000019}, - {0x00004040, 0x13160820}, - {0x00004040, 0xe5980560}, - {0x00004040, 0xc01dcffd}, - {0x00004040, 0x1aaabe41}, - {0x00004040, 0xbe105554}, - {0x00004040, 0x00043007}, - {0x00004044, 0x00000000}, -}; - static const u32 ar9285Modes_9285_1_2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index e6b92ff265f..d105e43d22e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3563,14 +3563,24 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) { struct ath9k_hw_capabilities *pCap = &ah->caps; int chain; - u32 regval; + u32 regval, value, gpio; static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = { AR_PHY_SWITCH_CHAIN_0, AR_PHY_SWITCH_CHAIN_1, AR_PHY_SWITCH_CHAIN_2, }; - u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz); + if (AR_SREV_9485(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) { + if (ah->config.xlna_gpio) + gpio = ah->config.xlna_gpio; + else + gpio = AR9300_EXT_LNA_CTL_GPIO_AR9485; + + ath9k_hw_cfg_output(ah, gpio, + AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED); + } + + value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, @@ -3596,7 +3606,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) * 7:4 R/W SWITCH_TABLE_COM_SPDT_WLAN_IDLE * SWITCH_TABLE_COM_SPDT_WLAN_IDLE */ - if (AR_SREV_9462_20(ah) || AR_SREV_9565(ah)) { + if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565(ah)) { value = ar9003_switch_com_spdt_get(ah, is2ghz); REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_SWITCH_TABLE_COM_SPDT_ALL, value); @@ -3796,7 +3806,13 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan) REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); - value = ar9003_hw_atten_chain_get_margin(ah, i, chan); + if (AR_SREV_9485(ah) && + (ar9003_hw_get_rx_gain_idx(ah) == 0) && + ah->config.xatten_margin_cfg) + value = 5; + else + value = ar9003_hw_atten_chain_get_margin(ah, i, chan); + REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); @@ -4043,8 +4059,9 @@ static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah) { u32 data, ko, kg; - if (!AR_SREV_9462_20(ah)) + if (!AR_SREV_9462_20_OR_LATER(ah)) return; + ar9300_otp_read_word(ah, 1, &data); ko = data & 0xff; kg = (data >> 8) & 0xff; @@ -4546,7 +4563,7 @@ static void ar9003_hw_get_target_power_eeprom(struct ath_hw *ah, is2GHz); for (i = 0; i < ar9300RateSize; i++) { - ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", + ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); } } @@ -4736,7 +4753,7 @@ tempslope: AR_PHY_TPC_19_ALPHA_THERM, temp_slope); } - if (AR_SREV_9462_20(ah)) + if (AR_SREV_9462_20_OR_LATER(ah)) REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, AR_PHY_TPC_19_B1_ALPHA_THERM, temp_slope); @@ -5272,7 +5289,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, return; for (i = 0; i < ar9300RateSize; i++) { - ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", + ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index a3523c969a3..d402cb32283 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -24,6 +24,7 @@ #include "ar955x_1p0_initvals.h" #include "ar9580_1p0_initvals.h" #include "ar9462_2p0_initvals.h" +#include "ar9462_2p1_initvals.h" #include "ar9565_1p0_initvals.h" /* General hardware code for the AR9003 hadware family */ @@ -197,6 +198,31 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, ar9485_1_1_pcie_phy_clkreq_disable_L1); + } else if (AR_SREV_9462_21(ah)) { + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], + ar9462_2p1_mac_core); + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], + ar9462_2p1_mac_postamble); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + ar9462_2p1_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + ar9462_2p1_baseband_postamble); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], + ar9462_2p1_radio_core); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], + ar9462_2p1_radio_postamble); + INIT_INI_ARRAY(&ah->ini_radio_post_sys2ant, + ar9462_2p1_radio_postamble_sys2ant); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], + ar9462_2p1_soc_preamble); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], + ar9462_2p1_soc_postamble); + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_2p1_common_rx_gain); + INIT_INI_ARRAY(&ah->iniModesFastClock, + ar9462_2p1_modes_fast_clock); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9462_2p1_baseband_core_txfir_coeff_japan_2484); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core); @@ -407,6 +433,9 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_lowest_ob_db_tx_gain_table); + else if (AR_SREV_9462_21(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9462_2p1_modes_low_ob_db_tx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_modes_low_ob_db_tx_gain_table_2p0); @@ -438,6 +467,9 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah) else if (AR_SREV_9550(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar955x_1p0_modes_no_xpa_tx_gain_table); + else if (AR_SREV_9462_21(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9462_2p1_modes_high_ob_db_tx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_modes_high_ob_db_tx_gain_table_2p0); @@ -507,6 +539,12 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_mixed_ob_db_tx_gain_table); + else if (AR_SREV_9462_21(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9462_2p1_modes_mix_ob_db_tx_gain); + else if (AR_SREV_9462_20(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9462_modes_mix_ob_db_tx_gain_table_2p0); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_mixed_ob_db_tx_gain_table_2p2); @@ -584,6 +622,9 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_rx_gain_table); + else if (AR_SREV_9462_21(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_2p1_common_rx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_rx_gain_table_2p0); @@ -606,6 +647,9 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9485Common_wo_xlna_rx_gain_1_1); + else if (AR_SREV_9462_21(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_2p1_common_wo_xlna_rx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_wo_xlna_rx_gain_table_2p0); @@ -627,9 +671,40 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) static void ar9003_rx_gain_table_mode2(struct ath_hw *ah) { - if (AR_SREV_9462_20(ah)) + if (AR_SREV_9462_21(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_2p1_common_mixed_rx_gain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core, + ar9462_2p1_baseband_core_mix_rxgain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble, + ar9462_2p1_baseband_postamble_mix_rxgain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + ar9462_2p1_baseband_postamble_5g_xlna); + } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_mixed_rx_gain_table_2p0); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core, + ar9462_2p0_baseband_core_mix_rxgain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble, + ar9462_2p0_baseband_postamble_mix_rxgain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + ar9462_2p0_baseband_postamble_5g_xlna); + } +} + +static void ar9003_rx_gain_table_mode3(struct ath_hw *ah) +{ + if (AR_SREV_9462_21(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_2p1_common_5g_xlna_only_rx_gain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + ar9462_2p1_baseband_postamble_5g_xlna); + } else if (AR_SREV_9462_20(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9462_2p0_5g_xlna_only_rxgain); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + ar9462_2p0_baseband_postamble_5g_xlna); + } } static void ar9003_rx_gain_table_apply(struct ath_hw *ah) @@ -645,6 +720,9 @@ static void ar9003_rx_gain_table_apply(struct ath_hw *ah) case 2: ar9003_rx_gain_table_mode2(ah); break; + case 3: + ar9003_rx_gain_table_mode3(ah); + break; } } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 301bf72c53b..5163abd3937 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -469,6 +469,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status = 0; rxs->rs_flags = 0; + rxs->flag = 0; rxs->rs_datalen = rxsp->status2 & AR_DataLen; rxs->rs_tstamp = rxsp->status3; @@ -493,8 +494,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0; rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7); - rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0; - rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0; + rxs->flag |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0; + rxs->flag |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0; rxs->evm0 = rxsp->status6; rxs->evm1 = rxsp->status7; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 09c1f9da67a..6343cc91953 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) if (accum_cnt <= thresh_accum_cnt) continue; + max_index++; + /* sum(tx amplitude) */ accum_tx = ((data_L[i] >> 16) & 0xffff) | ((data_U[i] & 0x7ff) << 16); @@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) accum_tx <<= scale_factor; accum_rx <<= scale_factor; - x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> - scale_factor; + x_est[max_index] = + (((accum_tx + accum_cnt) / accum_cnt) + 32) >> + scale_factor; - Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> + Y[max_index] = + ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> scale_factor) + - (1 << scale_factor) * max_index + 16; + (1 << scale_factor) * i + 16; if (accum_ang >= (1 << 26)) accum_ang -= 1 << 27; - theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / - accum_cnt; - - max_index++; + theta[max_index] = + ((accum_ang * (1 << scale_factor)) + accum_cnt) / + accum_cnt; } /* diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index e1714d7c9ee..1f694ab3cc7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -735,22 +735,53 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, return -EINVAL; } + /* + * SOC, MAC, BB, RADIO initvals. + */ for (i = 0; i < ATH_INI_NUM_SPLIT; i++) { ar9003_hw_prog_ini(ah, &ah->iniSOC[i], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniMac[i], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniBB[i], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniRadio[i], modesIndex); - if (i == ATH_INI_POST && AR_SREV_9462_20(ah)) + if (i == ATH_INI_POST && AR_SREV_9462_20_OR_LATER(ah)) ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant, modesIndex); } + /* + * RXGAIN initvals. + */ REG_WRITE_ARRAY(&ah->iniModesRxGain, 1, regWrites); + + if (AR_SREV_9462_20_OR_LATER(ah)) { + /* + * CUS217 mix LNA mode. + */ + if (ar9003_hw_get_rx_gain_idx(ah) == 2) { + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core, + 1, regWrites); + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble, + modesIndex, regWrites); + } + + /* + * 5G-XLNA + */ + if ((ar9003_hw_get_rx_gain_idx(ah) == 2) || + (ar9003_hw_get_rx_gain_idx(ah) == 3)) { + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + modesIndex, regWrites); + } + } + if (AR_SREV_9550(ah)) REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex, regWrites); + /* + * TXGAIN initvals. + */ if (AR_SREV_9550(ah)) { int modes_txgain_index; @@ -772,8 +803,14 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, REG_WRITE_ARRAY(&ah->iniModesFastClock, modesIndex, regWrites); + /* + * Clock frequency initvals. + */ REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); + /* + * JAPAN regulatory. + */ if (chan->channel == 2484) ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1); @@ -905,7 +942,12 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; - struct ar5416AniState *aniState = &chan->ani; + struct ar5416AniState *aniState = &ah->ani; + int m1ThreshLow, m2ThreshLow; + int m1Thresh, m2Thresh; + int m2CountThr, m2CountThrLow; + int m1ThreshLowExt, m2ThreshLowExt; + int m1ThreshExt, m2ThreshExt; s32 value, value2; switch (cmd & ah->ani_function) { @@ -919,6 +961,61 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, */ u32 on = param ? 1 : 0; + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) + goto skip_ws_det; + + m1ThreshLow = on ? + aniState->iniDef.m1ThreshLow : m1ThreshLow_off; + m2ThreshLow = on ? + aniState->iniDef.m2ThreshLow : m2ThreshLow_off; + m1Thresh = on ? + aniState->iniDef.m1Thresh : m1Thresh_off; + m2Thresh = on ? + aniState->iniDef.m2Thresh : m2Thresh_off; + m2CountThr = on ? + aniState->iniDef.m2CountThr : m2CountThr_off; + m2CountThrLow = on ? + aniState->iniDef.m2CountThrLow : m2CountThrLow_off; + m1ThreshLowExt = on ? + aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off; + m2ThreshLowExt = on ? + aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off; + m1ThreshExt = on ? + aniState->iniDef.m1ThreshExt : m1ThreshExt_off; + m2ThreshExt = on ? + aniState->iniDef.m2ThreshExt : m2ThreshExt_off; + + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M1_THRESH_LOW, + m1ThreshLow); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2_THRESH_LOW, + m2ThreshLow); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M1_THRESH, + m1Thresh); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2_THRESH, + m2Thresh); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2COUNT_THR, + m2CountThr); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, + m2CountThrLow); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH_LOW, + m1ThreshLowExt); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH_LOW, + m2ThreshLowExt); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH, + m1ThreshExt); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH, + m2ThreshExt); +skip_ws_det: if (on) REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); @@ -1173,7 +1270,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) struct ath9k_ani_default *iniDef; u32 val; - aniState = &ah->curchan->ani; + aniState = &ah->ani; iniDef = &aniState->iniDef; ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", @@ -1214,7 +1311,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; - aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = true; aniState->mrcCCK = true; } @@ -1415,7 +1512,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniRadio[ATH_INI_POST], modesIndex); - if (AR_SREV_9462_20(ah)) + if (AR_SREV_9462_20_OR_LATER(ah)) ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant, modesIndex); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index e71774196c0..d4d39f305a0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -351,6 +351,8 @@ #define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118 +#define AR9300_EXT_LNA_CTL_GPIO_AR9485 9 + /* * AGC Field Definitions */ @@ -952,7 +954,7 @@ #define AR_PHY_TPC_5_B1 (AR_SM1_BASE + 0x208) #define AR_PHY_TPC_6_B1 (AR_SM1_BASE + 0x20c) #define AR_PHY_TPC_11_B1 (AR_SM1_BASE + 0x220) -#define AR_PHY_PDADC_TAB_1 (AR_SM1_BASE + (AR_SREV_AR9462(ah) ? \ +#define AR_PHY_PDADC_TAB_1 (AR_SM1_BASE + (AR_SREV_9462_20_OR_LATER(ah) ? \ 0x280 : 0x240)) #define AR_PHY_TPC_19_B1 (AR_SM1_BASE + 0x240) #define AR_PHY_TPC_19_B1_ALPHA_THERM 0xff @@ -1046,7 +1048,7 @@ #define AR_GLB_GPIO_CONTROL (AR_GLB_BASE) #define AR_PHY_GLB_CONTROL (AR_GLB_BASE + 0x44) #define AR_GLB_SCRATCH(_ah) (AR_GLB_BASE + \ - (AR_SREV_9462_20(_ah) ? 0x4c : 0x50)) + (AR_SREV_9462_20_OR_LATER(_ah) ? 0x4c : 0x50)) #define AR_GLB_STATUS (AR_GLB_BASE + 0x48) /* diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 999ab08c34e..092b9d412e7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -78,7 +78,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, - {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -879,6 +879,69 @@ static const u32 ar9462_2p0_radio_postamble[][5] = { {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, }; +static const u32 ar9462_modes_mix_ob_db_tx_gain_table_2p0[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x0000d0da, 0x0000d0da, 0x0000d0de, 0x0000d0de}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x18022622, 0x18022622, 0x12000400, 0x12000400}, + {0x0000a518, 0x1b022822, 0x1b022822, 0x16000402, 0x16000402}, + {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, + {0x0000a520, 0x22022c41, 0x22022c41, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x28023042, 0x28023042, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x2c023044, 0x2c023044, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x2f023644, 0x2f023644, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x34025643, 0x34025643, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x38025a44, 0x38025a44, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x3b025e45, 0x3b025e45, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x38001640, 0x38001640}, + {0x0000a540, 0x48025e6c, 0x48025e6c, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x55025eb3, 0x55025eb3, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x62025f56, 0x62025f56, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x66027f56, 0x66027f56, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x70049f56, 0x70049f56, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x751ffff6, 0x751ffff6, 0x5c001eec, 0x5c001eec}, + {0x0000a568, 0x751ffff6, 0x751ffff6, 0x5e001ef0, 0x5e001ef0}, + {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x60001ef4, 0x60001ef4}, + {0x0000a570, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a574, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a578, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, +}; + static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, @@ -1449,4 +1512,284 @@ static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = { {0x0000b1fc, 0x00000196}, }; +static const u32 ar9462_2p0_baseband_postamble_5g_xlna[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, +}; + +static const u32 ar9462_2p0_5g_xlna_only_rxgain[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x03820190}, + {0x0000a030, 0x03840383}, + {0x0000a034, 0x03880385}, + {0x0000a038, 0x038a0389}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x29292929}, + {0x0000a084, 0x29292929}, + {0x0000a088, 0x29292929}, + {0x0000a08c, 0x29292929}, + {0x0000a090, 0x22292929}, + {0x0000a094, 0x1d1d2222}, + {0x0000a098, 0x0c111117}, + {0x0000a09c, 0x00030303}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x2a2d2f32}, + {0x0000b084, 0x21232328}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_2p0_baseband_core_mix_rxgain[][2] = { + /* Addr allmodes */ + {0x00009fd0, 0x0a2d6b93}, +}; + +static const u32 ar9462_2p0_baseband_postamble_mix_rxgain[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae}, + {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da}, + {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8}, + {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e}, +}; + #endif /* INITVALS_9462_2P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h new file mode 100644 index 00000000000..4dbc294df7e --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h @@ -0,0 +1,1774 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INITVALS_9462_2P1_H +#define INITVALS_9462_2P1_H + +/* AR9462 2.1 */ + +static const u32 ar9462_2p1_mac_core[][2] = { + /* Addr allmodes */ + {0x00000008, 0x00000000}, + {0x00000030, 0x000e0085}, + {0x00000034, 0x00000005}, + {0x00000040, 0x00000000}, + {0x00000044, 0x00000000}, + {0x00000048, 0x00000008}, + {0x0000004c, 0x00000010}, + {0x00000050, 0x00000000}, + {0x00001040, 0x002ffc0f}, + {0x00001044, 0x002ffc0f}, + {0x00001048, 0x002ffc0f}, + {0x0000104c, 0x002ffc0f}, + {0x00001050, 0x002ffc0f}, + {0x00001054, 0x002ffc0f}, + {0x00001058, 0x002ffc0f}, + {0x0000105c, 0x002ffc0f}, + {0x00001060, 0x002ffc0f}, + {0x00001064, 0x002ffc0f}, + {0x000010f0, 0x00000100}, + {0x00001270, 0x00000000}, + {0x000012b0, 0x00000000}, + {0x000012f0, 0x00000000}, + {0x0000143c, 0x00000000}, + {0x0000147c, 0x00000000}, + {0x00001810, 0x0f000003}, + {0x00008000, 0x00000000}, + {0x00008004, 0x00000000}, + {0x00008008, 0x00000000}, + {0x0000800c, 0x00000000}, + {0x00008018, 0x00000000}, + {0x00008020, 0x00000000}, + {0x00008038, 0x00000000}, + {0x0000803c, 0x00080000}, + {0x00008040, 0x00000000}, + {0x00008044, 0x00000000}, + {0x00008048, 0x00000000}, + {0x0000804c, 0xffffffff}, + {0x00008054, 0x00000000}, + {0x00008058, 0x00000000}, + {0x0000805c, 0x000fc78f}, + {0x00008060, 0x0000000f}, + {0x00008064, 0x00000000}, + {0x00008070, 0x00000310}, + {0x00008074, 0x00000020}, + {0x00008078, 0x00000000}, + {0x0000809c, 0x0000000f}, + {0x000080a0, 0x00000000}, + {0x000080a4, 0x02ff0000}, + {0x000080a8, 0x0e070605}, + {0x000080ac, 0x0000000d}, + {0x000080b0, 0x00000000}, + {0x000080b4, 0x00000000}, + {0x000080b8, 0x00000000}, + {0x000080bc, 0x00000000}, + {0x000080c0, 0x2a800000}, + {0x000080c4, 0x06900168}, + {0x000080c8, 0x13881c20}, + {0x000080cc, 0x01f40000}, + {0x000080d0, 0x00252500}, + {0x000080d4, 0x00b00005}, + {0x000080d8, 0x00400002}, + {0x000080dc, 0x00000000}, + {0x000080e0, 0xffffffff}, + {0x000080e4, 0x0000ffff}, + {0x000080e8, 0x3f3f3f3f}, + {0x000080ec, 0x00000000}, + {0x000080f0, 0x00000000}, + {0x000080f4, 0x00000000}, + {0x000080fc, 0x00020000}, + {0x00008100, 0x00000000}, + {0x00008108, 0x00000052}, + {0x0000810c, 0x00000000}, + {0x00008110, 0x00000000}, + {0x00008114, 0x000007ff}, + {0x00008118, 0x000000aa}, + {0x0000811c, 0x00003210}, + {0x00008124, 0x00000000}, + {0x00008128, 0x00000000}, + {0x0000812c, 0x00000000}, + {0x00008130, 0x00000000}, + {0x00008134, 0x00000000}, + {0x00008138, 0x00000000}, + {0x0000813c, 0x0000ffff}, + {0x00008144, 0xffffffff}, + {0x00008168, 0x00000000}, + {0x0000816c, 0x00000000}, + {0x00008170, 0x18486e00}, + {0x00008174, 0x33332210}, + {0x00008178, 0x00000000}, + {0x0000817c, 0x00020000}, + {0x000081c4, 0x33332210}, + {0x000081c8, 0x00000000}, + {0x000081cc, 0x00000000}, + {0x000081d4, 0x00000000}, + {0x000081ec, 0x00000000}, + {0x000081f0, 0x00000000}, + {0x000081f4, 0x00000000}, + {0x000081f8, 0x00000000}, + {0x000081fc, 0x00000000}, + {0x00008240, 0x00100000}, + {0x00008244, 0x0010f400}, + {0x00008248, 0x00000800}, + {0x0000824c, 0x0001e800}, + {0x00008250, 0x00000000}, + {0x00008254, 0x00000000}, + {0x00008258, 0x00000000}, + {0x0000825c, 0x40000000}, + {0x00008260, 0x00080922}, + {0x00008264, 0x99c00010}, + {0x00008268, 0xffffffff}, + {0x0000826c, 0x0000ffff}, + {0x00008270, 0x00000000}, + {0x00008274, 0x40000000}, + {0x00008278, 0x003e4180}, + {0x0000827c, 0x00000004}, + {0x00008284, 0x0000002c}, + {0x00008288, 0x0000002c}, + {0x0000828c, 0x000000ff}, + {0x00008294, 0x00000000}, + {0x00008298, 0x00000000}, + {0x0000829c, 0x00000000}, + {0x00008300, 0x00000140}, + {0x00008314, 0x00000000}, + {0x0000831c, 0x0000010d}, + {0x00008328, 0x00000000}, + {0x0000832c, 0x0000001f}, + {0x00008330, 0x00000302}, + {0x00008334, 0x00000700}, + {0x00008338, 0xffff0000}, + {0x0000833c, 0x02400000}, + {0x00008340, 0x000107ff}, + {0x00008344, 0xaa48107b}, + {0x00008348, 0x008f0000}, + {0x0000835c, 0x00000000}, + {0x00008360, 0xffffffff}, + {0x00008364, 0xffffffff}, + {0x00008368, 0x00000000}, + {0x00008370, 0x00000000}, + {0x00008374, 0x000000ff}, + {0x00008378, 0x00000000}, + {0x0000837c, 0x00000000}, + {0x00008380, 0xffffffff}, + {0x00008384, 0xffffffff}, + {0x00008390, 0xffffffff}, + {0x00008394, 0xffffffff}, + {0x00008398, 0x00000000}, + {0x0000839c, 0x00000000}, + {0x000083a4, 0x0000fa14}, + {0x000083a8, 0x000f0c00}, + {0x000083ac, 0x33332210}, + {0x000083b0, 0x33332210}, + {0x000083b4, 0x33332210}, + {0x000083b8, 0x33332210}, + {0x000083bc, 0x00000000}, + {0x000083c0, 0x00000000}, + {0x000083c4, 0x00000000}, + {0x000083c8, 0x00000000}, + {0x000083cc, 0x00000200}, + {0x000083d0, 0x000301ff}, +}; + +static const u32 ar9462_2p1_mac_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, + {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, + {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, + {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, + {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, + {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, + {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, + {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, +}; + +static const u32 ar9462_2p1_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a9f6b}, + {0x0000980c, 0x04900000}, + {0x00009814, 0x9280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x6400a290}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x0d000600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32440bbe}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098e4, 0x01ffffff}, + {0x000098e8, 0x01ffffff}, + {0x000098ec, 0x01ffffff}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009bf0, 0x80000000}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x9883800a}, + {0x00009d10, 0x01834061}, + {0x00009d14, 0x00c0040b}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x990bb515}, + {0x00009e28, 0x0c6f0000}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x15262820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009e54, 0xe4c555c2}, + {0x00009e58, 0xfd857722}, + {0x00009e5c, 0xe9198724}, + {0x00009fc0, 0x803e4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x00009fd0, 0x0a193b93}, + {0x0000a20c, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a2a0, 0x00000001}, + {0x0000a2c0, 0x00000001}, + {0x0000a2c8, 0x00000000}, + {0x0000a2cc, 0x18c43433}, + {0x0000a2d4, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000006}, + {0x0000a3f8, 0x0c9bd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce739ce}, + {0x0000a418, 0x2d001dce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00100000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x05000080}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a454, 0x07000000}, + {0x0000a644, 0xbfad9d74}, + {0x0000a648, 0x0048060a}, + {0x0000a64c, 0x00002037}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x09080504}, + {0x0000a678, 0x0d0c0b0a}, + {0x0000a67c, 0x13121110}, + {0x0000a680, 0x31301514}, + {0x0000a684, 0x35343332}, + {0x0000a688, 0x00000036}, + {0x0000a690, 0x00000838}, + {0x0000a6b0, 0x0000000a}, + {0x0000a6b4, 0x00512c01}, + {0x0000a7c0, 0x00000000}, + {0x0000a7c4, 0xfffffffc}, + {0x0000a7c8, 0x00000000}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000000}, + {0x0000a7f0, 0x80000000}, + {0x0000a8d0, 0x004b6a8e}, + {0x0000a8d4, 0x00000820}, + {0x0000a8dc, 0x00000000}, + {0x0000a8f0, 0x00000000}, + {0x0000a8f4, 0x00000000}, + {0x0000abf0, 0x80000000}, + {0x0000b2d0, 0x00000080}, + {0x0000b2d4, 0x00000000}, + {0x0000b2ec, 0x00000000}, + {0x0000b2f0, 0x00000000}, + {0x0000b2f4, 0x00000000}, + {0x0000b2f8, 0x00000000}, + {0x0000b408, 0x0e79e5c0}, + {0x0000b40c, 0x00820820}, + {0x0000b420, 0x00000000}, + {0x0000b6b0, 0x0000000a}, + {0x0000b6b4, 0x00000001}, +}; + +static const u32 ar9462_2p1_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae}, + {0x00009824, 0x63c640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da}, + {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x09143e81}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a2}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8}, + {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, + {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x01318fc0, 0x01318fc4, 0x01318fc4, 0x01318fc0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f}, + {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, + {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, + {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a3a4, 0x00000050, 0x00000050, 0x00000000, 0x00000000}, + {0x0000a3a8, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa}, + {0x0000a3ac, 0xaaaaaa00, 0xaa30aa30, 0xaaaaaa00, 0xaaaaaa00}, + {0x0000a41c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, + {0x0000a420, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce}, + {0x0000a424, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, + {0x0000a428, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce}, + {0x0000a42c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce}, + {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, +}; + +static const u32 ar9462_2p1_radio_core[][2] = { + /* Addr allmodes */ + {0x00016000, 0x36db6db6}, + {0x00016004, 0x6db6db40}, + {0x00016008, 0x73f00000}, + {0x0001600c, 0x00000000}, + {0x00016010, 0x6d820001}, + {0x00016040, 0x7f80fff8}, + {0x0001604c, 0x2699e04f}, + {0x00016050, 0x6db6db6c}, + {0x00016058, 0x6c200000}, + {0x00016080, 0x000c0000}, + {0x00016084, 0x9a68048c}, + {0x00016088, 0x54214514}, + {0x0001608c, 0x1203040b}, + {0x00016090, 0x24926490}, + {0x00016098, 0xd2888888}, + {0x000160a0, 0x0a108ffe}, + {0x000160a4, 0x812fc491}, + {0x000160a8, 0x423c8000}, + {0x000160b4, 0x92000000}, + {0x000160b8, 0x0285dddc}, + {0x000160bc, 0x02908888}, + {0x000160c0, 0x00adb6d0}, + {0x000160c4, 0x6db6db60}, + {0x000160c8, 0x6db6db6c}, + {0x000160cc, 0x0de6c1b0}, + {0x00016100, 0x3fffbe04}, + {0x00016104, 0xfff80000}, + {0x00016108, 0x00200400}, + {0x00016110, 0x00000000}, + {0x00016144, 0x02084080}, + {0x00016148, 0x000080c0}, + {0x00016280, 0x050a0001}, + {0x00016284, 0x3d841418}, + {0x00016288, 0x00000000}, + {0x0001628c, 0xe3000000}, + {0x00016290, 0xa1005080}, + {0x00016294, 0x00000020}, + {0x00016298, 0x54a82900}, + {0x00016340, 0x121e4276}, + {0x00016344, 0x00300000}, + {0x00016400, 0x36db6db6}, + {0x00016404, 0x6db6db40}, + {0x00016408, 0x73f00000}, + {0x0001640c, 0x00000000}, + {0x00016410, 0x6c800001}, + {0x00016440, 0x7f80fff8}, + {0x0001644c, 0x4699e04f}, + {0x00016450, 0x6db6db6c}, + {0x00016500, 0x3fffbe04}, + {0x00016504, 0xfff80000}, + {0x00016508, 0x00200400}, + {0x00016510, 0x00000000}, + {0x00016544, 0x02084080}, + {0x00016548, 0x000080c0}, +}; + +static const u32 ar9462_2p1_radio_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524}, + {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70}, + {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, + {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, +}; + +static const u32 ar9462_2p1_soc_preamble[][2] = { + /* Addr allmodes */ + {0x000040a4, 0x00a0c1c9}, + {0x00007020, 0x00000000}, + {0x00007034, 0x00000002}, + {0x00007038, 0x000004c2}, +}; + +static const u32 ar9462_2p1_soc_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00007010, 0x00000033, 0x00000033, 0x00000033, 0x00000033}, +}; + +static const u32 ar9462_2p1_radio_postamble_sys2ant[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, + {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, + {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, +}; + +static const u32 ar9462_2p1_common_rx_gain[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x01910190}, + {0x0000a030, 0x01930192}, + {0x0000a034, 0x01950194}, + {0x0000a038, 0x038a0196}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x22222229}, + {0x0000a084, 0x1d1d1d1d}, + {0x0000a088, 0x1d1d1d1d}, + {0x0000a08c, 0x1d1d1d1d}, + {0x0000a090, 0x171d1d1d}, + {0x0000a094, 0x11111717}, + {0x0000a098, 0x00030311}, + {0x0000a09c, 0x00000000}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x2a2d2f32}, + {0x0000b084, 0x21232328}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_2p1_common_mixed_rx_gain[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x03820190}, + {0x0000a030, 0x03840383}, + {0x0000a034, 0x03880385}, + {0x0000a038, 0x038a0389}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x29292929}, + {0x0000a084, 0x29292929}, + {0x0000a088, 0x29292929}, + {0x0000a08c, 0x29292929}, + {0x0000a090, 0x22292929}, + {0x0000a094, 0x1d1d2222}, + {0x0000a098, 0x0c111117}, + {0x0000a09c, 0x00030303}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x2a2d2f32}, + {0x0000b084, 0x21232328}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_2p1_baseband_core_mix_rxgain[][2] = { + /* Addr allmodes */ + {0x00009fd0, 0x0a2d6b93}, +}; + +static const u32 ar9462_2p1_baseband_postamble_mix_rxgain[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae}, + {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da}, + {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8}, + {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e}, +}; + +static const u32 ar9462_2p1_baseband_postamble_5g_xlna[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, +}; + +static const u32 ar9462_2p1_common_wo_xlna_rx_gain[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x03820190}, + {0x0000a030, 0x03840383}, + {0x0000a034, 0x03880385}, + {0x0000a038, 0x038a0389}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x29292929}, + {0x0000a084, 0x29292929}, + {0x0000a088, 0x29292929}, + {0x0000a08c, 0x29292929}, + {0x0000a090, 0x22292929}, + {0x0000a094, 0x1d1d2222}, + {0x0000a098, 0x0c111117}, + {0x0000a09c, 0x00030303}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x32323232}, + {0x0000b084, 0x2f2f3232}, + {0x0000b088, 0x23282a2d}, + {0x0000b08c, 0x1c1e2123}, + {0x0000b090, 0x14171919}, + {0x0000b094, 0x0e0e1214}, + {0x0000b098, 0x03050707}, + {0x0000b09c, 0x00030303}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_2p1_common_5g_xlna_only_rx_gain[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x03820190}, + {0x0000a030, 0x03840383}, + {0x0000a034, 0x03880385}, + {0x0000a038, 0x038a0389}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x29292929}, + {0x0000a084, 0x29292929}, + {0x0000a088, 0x29292929}, + {0x0000a08c, 0x29292929}, + {0x0000a090, 0x22292929}, + {0x0000a094, 0x1d1d2222}, + {0x0000a098, 0x0c111117}, + {0x0000a09c, 0x00030303}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x2a2d2f32}, + {0x0000b084, 0x21232328}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 ar9462_2p1_modes_low_ob_db_tx_gain[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, + {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, + {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, + {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, + {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, + {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060}, + {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, + {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, + {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000}, + {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, +}; + +static const u32 ar9462_2p1_modes_high_ob_db_tx_gain[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050da, 0x000050da, 0x000050de, 0x000050de}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400}, + {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402}, + {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, + {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640}, + {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, + {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x55025eb3, 0x55025eb3, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001a84, 0x44001a84}, + {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x751ffff6, 0x751ffff6, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x751ffff6, 0x751ffff6, 0x58001ef0, 0x58001ef0}, + {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x5a001ef4, 0x5a001ef4}, + {0x0000a570, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, + {0x0000a574, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, + {0x0000a578, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, + {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, + {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060}, + {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, + {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, + {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000}, + {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, +}; + +static const u32 ar9462_2p1_modes_mix_ob_db_tx_gain[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x0000d0da, 0x0000d0da, 0x0000d0de, 0x0000d0de}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x18022622, 0x18022622, 0x12000400, 0x12000400}, + {0x0000a518, 0x1b022822, 0x1b022822, 0x16000402, 0x16000402}, + {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, + {0x0000a520, 0x22022c41, 0x22022c41, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x28023042, 0x28023042, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x2c023044, 0x2c023044, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x2f023644, 0x2f023644, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x34025643, 0x34025643, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x38025a44, 0x38025a44, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x3b025e45, 0x3b025e45, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x38001640, 0x38001640}, + {0x0000a540, 0x48025e6c, 0x48025e6c, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x55025eb3, 0x55025eb3, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x62025f56, 0x62025f56, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x66027f56, 0x66027f56, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x70049f56, 0x70049f56, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x751ffff6, 0x751ffff6, 0x5c001eec, 0x5c001eec}, + {0x0000a568, 0x751ffff6, 0x751ffff6, 0x5e001ef0, 0x5e001ef0}, + {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x60001ef4, 0x60001ef4}, + {0x0000a570, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a574, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a578, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, +}; + +static const u32 ar9462_2p1_modes_fast_clock[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x00001030, 0x00000268, 0x000004d0}, + {0x00001070, 0x0000018c, 0x00000318}, + {0x000010b0, 0x00000fd0, 0x00001fa0}, + {0x00008014, 0x044c044c, 0x08980898}, + {0x0000801c, 0x148ec02b, 0x148ec057}, + {0x00008318, 0x000044c0, 0x00008980}, + {0x00009e00, 0x0372131c, 0x0372131c}, + {0x0000a230, 0x0000400b, 0x00004016}, + {0x0000a254, 0x00000898, 0x00001130}, +}; + +static const u32 ar9462_2p1_baseband_core_txfir_coeff_japan_2484[][2] = { + /* Addr allmodes */ + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x6f7f0301}, + {0x0000a3a0, 0xca9228ee}, +}; + +#endif /* INITVALS_9462_2P1_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 42b03dc39d1..c1224b5a257 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -296,6 +296,7 @@ struct ath_tx { struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; struct ath_descdma txdma; struct ath_txq *txq_map[IEEE80211_NUM_ACS]; + struct ath_txq *uapsdq; u32 txq_max_pending[IEEE80211_NUM_ACS]; u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32]; }; @@ -343,6 +344,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum, void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_tx_control *txctl); +void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct sk_buff *skb); void ath_tx_tasklet(struct ath_softc *sc); void ath_tx_edma_tasklet(struct ath_softc *sc); int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, @@ -353,6 +356,11 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_node *an); +void ath9k_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data); /********/ /* VIFs */ @@ -623,6 +631,11 @@ void ath_ant_comb_update(struct ath_softc *sc); /* Main driver core */ /********************/ +#define ATH9K_PCI_CUS198 0x0001 +#define ATH9K_PCI_CUS230 0x0002 +#define ATH9K_PCI_CUS217 0x0004 +#define ATH9K_PCI_WOW 0x0008 + /* * Default cache line size, in bytes. * Used when PCI device not fully initialized by bootrom/BIOS @@ -642,6 +655,7 @@ enum sc_op_flags { SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, SC_OP_HW_RESET, + SC_OP_SCANNING, }; /* Powersave flags */ @@ -706,6 +720,7 @@ struct ath_softc { unsigned int hw_busy_count; unsigned long sc_flags; + unsigned long driver_data; u32 intrstatus; u16 ps_flags; /* PS_* */ @@ -755,7 +770,6 @@ struct ath_softc { struct rchan *rfs_chan_spec_scan; enum spectral_mode spectral_mode; struct ath_spec_scan spec_config; - int scanning; #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 2ff570f7f8f..1a17732bb08 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc) ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); - if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || + sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) { /* Always burst out beacon and CAB traffic. */ qi.tqi_aifs = 1; qi.tqi_cwmin = 0; @@ -107,23 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); } -static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct ath_softc *sc = hw->priv; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_tx_control txctl; - - memset(&txctl, 0, sizeof(struct ath_tx_control)); - txctl.txq = sc->beacon.cabq; - - ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb); - - if (ath_tx_start(hw, skb, &txctl) != 0) { - ath_dbg(common, XMIT, "CABQ TX failed\n"); - ieee80211_free_txskb(hw, skb); - } -} - static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -205,10 +189,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx); - while (skb) { - ath9k_tx_cabq(hw, skb); - skb = ieee80211_get_buffered_bc(hw, vif); - } + if (skb) + ath_tx_cabq(hw, vif, skb); return bf; } @@ -273,7 +255,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) u64 tsf; int slot; - if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) { + if (sc->sc_ah->opmode != NL80211_IFTYPE_AP && + sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) { ath_dbg(common, BEACON, "slot 0, tsf: %llu\n", ath9k_hw_gettsf64(sc->sc_ah)); return 0; @@ -765,10 +748,10 @@ void ath9k_set_beacon(struct ath_softc *sc) switch (sc->sc_ah->opmode) { case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: ath9k_beacon_config_ap(sc, cur_conf); break; case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: ath9k_beacon_config_adhoc(sc, cur_conf); break; case NL80211_IFTYPE_STATION: diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 7304e758500..5e8219a91e2 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -387,7 +387,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) if (!caldata) { chan->noisefloor = nf; - ah->noise = ath9k_hw_getchan_noise(ah, chan); return false; } diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index b37eb8d3881..87454f6c7b4 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -69,7 +69,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &mask)) + if (kstrtoul(buf, 0, &mask)) return -EINVAL; common->debug_mask = mask; @@ -114,7 +114,7 @@ static ssize_t write_file_tx_chainmask(struct file *file, const char __user *use return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &mask)) + if (kstrtoul(buf, 0, &mask)) return -EINVAL; ah->txchainmask = mask; @@ -157,7 +157,7 @@ static ssize_t write_file_rx_chainmask(struct file *file, const char __user *use return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &mask)) + if (kstrtoul(buf, 0, &mask)) return -EINVAL; ah->rxchainmask = mask; @@ -173,25 +173,69 @@ static const struct file_operations fops_rx_chainmask = { .llseek = default_llseek, }; -static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf, +static ssize_t read_file_ani(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - char buf[32]; - unsigned int len; + struct ath_hw *ah = sc->sc_ah; + unsigned int len = 0, size = 1024; + ssize_t retval = 0; + char *buf; - len = sprintf(buf, "%d\n", common->disable_ani); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (common->disable_ani) { + len += snprintf(buf + len, size - len, "%s: %s\n", + "ANI", "DISABLED"); + goto exit; + } + + len += snprintf(buf + len, size - len, "%15s: %s\n", + "ANI", "ENABLED"); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "ANI RESET", ah->stats.ast_ani_reset); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "SPUR UP", ah->stats.ast_ani_spurup); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "SPUR DOWN", ah->stats.ast_ani_spurup); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "MRC-CCK ON", ah->stats.ast_ani_ccklow); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "MRC-CCK OFF", ah->stats.ast_ani_cckhigh); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "FIR-STEP UP", ah->stats.ast_ani_stepup); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "FIR-STEP DOWN", ah->stats.ast_ani_stepdown); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs); + len += snprintf(buf + len, size - len, "%15s: %u\n", + "CCK ERRORS", ah->stats.ast_ani_cckerrs); +exit: + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; } -static ssize_t write_file_disable_ani(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t write_file_ani(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - unsigned long disable_ani; + unsigned long ani; char buf[32]; ssize_t len; @@ -200,12 +244,15 @@ static ssize_t write_file_disable_ani(struct file *file, return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &disable_ani)) + if (kstrtoul(buf, 0, &ani)) return -EINVAL; - common->disable_ani = !!disable_ani; + if (ani < 0 || ani > 1) + return -EINVAL; + + common->disable_ani = !ani; - if (disable_ani) { + if (common->disable_ani) { clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_stop_ani(sc); } else { @@ -215,9 +262,9 @@ static ssize_t write_file_disable_ani(struct file *file, return count; } -static const struct file_operations fops_disable_ani = { - .read = read_file_disable_ani, - .write = write_file_disable_ani, +static const struct file_operations fops_ani = { + .read = read_file_ani, + .write = write_file_ani, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -253,7 +300,7 @@ static ssize_t write_file_ant_diversity(struct file *file, goto exit; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &antenna_diversity)) + if (kstrtoul(buf, 0, &antenna_diversity)) return -EINVAL; common->antenna_diversity = !!antenna_diversity; @@ -738,8 +785,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags) { -#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\ - [sc->debug.tsidx].c) int qnum = txq->axq_qnum; TX_STAT_INC(qnum, tx_pkts_all); @@ -771,37 +816,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, TX_STAT_INC(qnum, data_underrun); if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) TX_STAT_INC(qnum, delim_underrun); - -#ifdef CONFIG_ATH9K_MAC_DEBUG - spin_lock(&sc->debug.samp_lock); - TX_SAMP_DBG(jiffies) = jiffies; - TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0; - TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1; - TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2; - TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0; - TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1; - TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2; - TX_SAMP_DBG(rateindex) = ts->ts_rateindex; - TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK); - TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry; - TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry; - TX_SAMP_DBG(rssi) = ts->ts_rssi; - TX_SAMP_DBG(tid) = ts->tid; - TX_SAMP_DBG(qid) = ts->qid; - - if (ts->ts_flags & ATH9K_TX_BA) { - TX_SAMP_DBG(ba_low) = ts->ba_low; - TX_SAMP_DBG(ba_high) = ts->ba_high; - } else { - TX_SAMP_DBG(ba_low) = 0; - TX_SAMP_DBG(ba_high) = 0; - } - - sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES; - spin_unlock(&sc->debug.samp_lock); -#endif - -#undef TX_SAMP_DBG } static const struct file_operations fops_xmit = { @@ -915,8 +929,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { #define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ -#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\ - [sc->debug.rsidx].c) RX_STAT_INC(rx_pkts_all); sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; @@ -940,27 +952,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) RX_PHY_ERR_INC(rs->rs_phyerr); } -#ifdef CONFIG_ATH9K_MAC_DEBUG - spin_lock(&sc->debug.samp_lock); - RX_SAMP_DBG(jiffies) = jiffies; - RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0; - RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1; - RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2; - RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0; - RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1; - RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2; - RX_SAMP_DBG(antenna) = rs->rs_antenna; - RX_SAMP_DBG(rssi) = rs->rs_rssi; - RX_SAMP_DBG(rate) = rs->rs_rate; - RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon; - - sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES; - spin_unlock(&sc->debug.samp_lock); - -#endif - #undef RX_PHY_ERR_INC -#undef RX_SAMP_DBG } static const struct file_operations fops_recv = { @@ -1278,7 +1270,7 @@ static ssize_t write_file_regidx(struct file *file, const char __user *user_buf, return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, ®idx)) + if (kstrtoul(buf, 0, ®idx)) return -EINVAL; sc->debug.regidx = regidx; @@ -1323,7 +1315,7 @@ static ssize_t write_file_regval(struct file *file, const char __user *user_buf, return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, ®val)) + if (kstrtoul(buf, 0, ®val)) return -EINVAL; ath9k_ps_wakeup(sc); @@ -1485,283 +1477,6 @@ static const struct file_operations fops_modal_eeprom = { .llseek = default_llseek, }; -#ifdef CONFIG_ATH9K_MAC_DEBUG - -void ath9k_debug_samp_bb_mac(struct ath_softc *sc) -{ -#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c) - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - unsigned long flags; - int i; - - ath9k_ps_wakeup(sc); - - spin_lock_bh(&sc->debug.samp_lock); - - spin_lock_irqsave(&common->cc_lock, flags); - ath_hw_cycle_counters_update(common); - - ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles; - ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy; - ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame; - ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame; - spin_unlock_irqrestore(&common->cc_lock, flags); - - ATH_SAMP_DBG(noise) = ah->noise; - - REG_WRITE_D(ah, AR_MACMISC, - ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | - (AR_MACMISC_MISC_OBS_BUS_1 << - AR_MACMISC_MISC_OBS_BUS_MSB_S))); - - for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) - ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah, - AR_DMADBG_0 + (i * sizeof(u32))); - - ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1); - ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR); - - memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist, - sizeof(ATH_SAMP_DBG(nfCalHist))); - - sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES; - spin_unlock_bh(&sc->debug.samp_lock); - ath9k_ps_restore(sc); - -#undef ATH_SAMP_DBG -} - -static int open_file_bb_mac_samps(struct inode *inode, struct file *file) -{ -#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c - struct ath_softc *sc = inode->i_private; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; - struct ath_dbg_bb_mac_samp *bb_mac_samp; - struct ath9k_nfcal_hist *h; - int i, j, qcuOffset = 0, dcuOffset = 0; - u32 *qcuBase, *dcuBase, size = 30000, len = 0; - u32 sampidx = 0; - u8 *buf; - u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; - u8 nread; - - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) - return -EAGAIN; - - buf = vmalloc(size); - if (!buf) - return -ENOMEM; - bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES); - if (!bb_mac_samp) { - vfree(buf); - return -ENOMEM; - } - /* Account the current state too */ - ath9k_debug_samp_bb_mac(sc); - - spin_lock_bh(&sc->debug.samp_lock); - memcpy(bb_mac_samp, sc->debug.bb_mac_samp, - sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES); - len += snprintf(buf + len, size - len, - "Current Sample Index: %d\n", sc->debug.sampidx); - spin_unlock_bh(&sc->debug.samp_lock); - - len += snprintf(buf + len, size - len, - "Raw DMA Debug Dump:\n"); - len += snprintf(buf + len, size - len, "Sample |\t"); - for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) - len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i); - len += snprintf(buf + len, size - len, "\n"); - - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - len += snprintf(buf + len, size - len, "%d\t", sampidx); - - for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) - len += snprintf(buf + len, size - len, " %08x\t", - ATH_SAMP_DBG(dma_dbg_reg_vals[i])); - len += snprintf(buf + len, size - len, "\n"); - } - len += snprintf(buf + len, size - len, "\n"); - - len += snprintf(buf + len, size - len, - "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]); - dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]); - - for (i = 0; i < ATH9K_NUM_QUEUES; i++, - qcuOffset += 4, dcuOffset += 5) { - if (i == 8) { - qcuOffset = 0; - qcuBase++; - } - - if (i == 6) { - dcuOffset = 0; - dcuBase++; - } - if (!sc->debug.stats.txstats[i].queued) - continue; - - len += snprintf(buf + len, size - len, - "%4d %7d %2x %1x %2x %2x\n", - sampidx, i, - (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, - (*qcuBase & (0x8 << qcuOffset)) >> - (qcuOffset + 3), - ATH_SAMP_DBG(dma_dbg_reg_vals[2]) & - (0x7 << (i * 3)) >> (i * 3), - (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); - } - len += snprintf(buf + len, size - len, "\n"); - } - len += snprintf(buf + len, size - len, - "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp " - "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 " - "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n"); - - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]); - dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]); - - len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx, - (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18, - (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22); - len += snprintf(buf + len, size - len, "%7x %8x ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3)); - len += snprintf(buf + len, size - len, "%7x %7x ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25, - (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27); - len += snprintf(buf + len, size - len, "%7d %12d ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10); - len += snprintf(buf + len, size - len, "%12d %12d ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12); - len += snprintf(buf + len, size - len, "%12d %12d ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17); - len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n", - ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr)); - } - - len += snprintf(buf + len, size - len, - "Sample ChNoise Chain privNF #Reading Readings\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - h = ATH_SAMP_DBG(nfCalHist); - if (!ATH_SAMP_DBG(noise)) - continue; - - for (i = 0; i < NUM_NF_READINGS; i++) { - if (!(chainmask & (1 << i)) || - ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) - continue; - - nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - - h[i].invalidNFcount; - len += snprintf(buf + len, size - len, - "%4d %5d %4d\t %d\t %d\t", - sampidx, ATH_SAMP_DBG(noise), - i, h[i].privNF, nread); - for (j = 0; j < nread; j++) - len += snprintf(buf + len, size - len, - " %d", h[i].nfCalBuffer[j]); - len += snprintf(buf + len, size - len, "\n"); - } - } - len += snprintf(buf + len, size - len, "\nCycle counters:\n" - "Sample Total Rxbusy Rxframes Txframes\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - if (!ATH_SAMP_DBG(cc.cycles)) - continue; - len += snprintf(buf + len, size - len, - "%4d %08x %08x %08x %08x\n", - sampidx, ATH_SAMP_DBG(cc.cycles), - ATH_SAMP_DBG(cc.rx_busy), - ATH_SAMP_DBG(cc.rx_frame), - ATH_SAMP_DBG(cc.tx_frame)); - } - - len += snprintf(buf + len, size - len, "Tx status Dump :\n"); - len += snprintf(buf + len, size - len, - "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb " - "isok rts_fail data_fail rate tid qid " - "ba_low ba_high tx_before(ms)\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { - if (!ATH_SAMP_DBG(ts[i].jiffies)) - continue; - len += snprintf(buf + len, size - len, "%-14d" - "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d " - "%-9d %-4d %-3d %-3d %08x %08x %-11d\n", - sampidx, - ATH_SAMP_DBG(ts[i].rssi_ctl0), - ATH_SAMP_DBG(ts[i].rssi_ctl1), - ATH_SAMP_DBG(ts[i].rssi_ctl2), - ATH_SAMP_DBG(ts[i].rssi_ext0), - ATH_SAMP_DBG(ts[i].rssi_ext1), - ATH_SAMP_DBG(ts[i].rssi_ext2), - ATH_SAMP_DBG(ts[i].rssi), - ATH_SAMP_DBG(ts[i].isok), - ATH_SAMP_DBG(ts[i].rts_fail_cnt), - ATH_SAMP_DBG(ts[i].data_fail_cnt), - ATH_SAMP_DBG(ts[i].rateindex), - ATH_SAMP_DBG(ts[i].tid), - ATH_SAMP_DBG(ts[i].qid), - ATH_SAMP_DBG(ts[i].ba_low), - ATH_SAMP_DBG(ts[i].ba_high), - jiffies_to_msecs(jiffies - - ATH_SAMP_DBG(ts[i].jiffies))); - } - } - - len += snprintf(buf + len, size - len, "Rx status Dump :\n"); - len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 " - "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { - if (!ATH_SAMP_DBG(rs[i].jiffies)) - continue; - len += snprintf(buf + len, size - len, "%-14d" - "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n", - sampidx, - ATH_SAMP_DBG(rs[i].rssi_ctl0), - ATH_SAMP_DBG(rs[i].rssi_ctl1), - ATH_SAMP_DBG(rs[i].rssi_ctl2), - ATH_SAMP_DBG(rs[i].rssi_ext0), - ATH_SAMP_DBG(rs[i].rssi_ext1), - ATH_SAMP_DBG(rs[i].rssi_ext2), - ATH_SAMP_DBG(rs[i].rssi), - ATH_SAMP_DBG(rs[i].is_mybeacon) ? - "True" : "False", - ATH_SAMP_DBG(rs[i].antenna), - ATH_SAMP_DBG(rs[i].rate), - jiffies_to_msecs(jiffies - - ATH_SAMP_DBG(rs[i].jiffies))); - } - } - - vfree(bb_mac_samp); - file->private_data = buf; - - return 0; -#undef ATH_SAMP_DBG -} - -static const struct file_operations fops_samps = { - .open = open_file_bb_mac_samps, - .read = ath9k_debugfs_read_buf, - .release = ath9k_debugfs_release_buf, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -#endif - #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -2059,8 +1774,8 @@ int ath9k_init_debug(struct ath_hw *ah) sc->debug.debugfs_phy, sc, &fops_rx_chainmask); debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_tx_chainmask); - debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_disable_ani); + debugfs_create_file("ani", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_ani); debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->config.enable_paprd); debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, @@ -2095,11 +1810,6 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_spectral_fft_period); - -#ifdef CONFIG_ATH9K_MAC_DEBUG - debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_samps); -#endif debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 9d49aab8b98..fc679198a0f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -251,56 +251,10 @@ struct ath_stats { u32 reset[__RESET_TYPE_MAX]; }; -#define ATH_DBG_MAX_SAMPLES 10 -struct ath_dbg_bb_mac_samp { - u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS]; - u32 pcu_obs, pcu_cr, noise; - struct { - u64 jiffies; - int8_t rssi_ctl0; - int8_t rssi_ctl1; - int8_t rssi_ctl2; - int8_t rssi_ext0; - int8_t rssi_ext1; - int8_t rssi_ext2; - int8_t rssi; - bool isok; - u8 rts_fail_cnt; - u8 data_fail_cnt; - u8 rateindex; - u8 qid; - u8 tid; - u32 ba_low; - u32 ba_high; - } ts[ATH_DBG_MAX_SAMPLES]; - struct { - u64 jiffies; - int8_t rssi_ctl0; - int8_t rssi_ctl1; - int8_t rssi_ctl2; - int8_t rssi_ext0; - int8_t rssi_ext1; - int8_t rssi_ext2; - int8_t rssi; - bool is_mybeacon; - u8 antenna; - u8 rate; - } rs[ATH_DBG_MAX_SAMPLES]; - struct ath_cycle_counters cc; - struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; -}; - struct ath9k_debug { struct dentry *debugfs_phy; u32 regidx; struct ath_stats stats; -#ifdef CONFIG_ATH9K_MAC_DEBUG - spinlock_t samp_lock; - struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES]; - u8 sampidx; - u8 tsidx; - u8 rsidx; -#endif }; int ath9k_init_debug(struct ath_hw *ah); @@ -364,17 +318,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc, #endif /* CONFIG_ATH9K_DEBUGFS */ -#ifdef CONFIG_ATH9K_MAC_DEBUG - -void ath9k_debug_samp_bb_mac(struct ath_softc *sc); - -#else - -static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc) -{ -} - -#endif - - #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index b7611b7bbe4..3c6e4138a95 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -96,7 +96,7 @@ static ssize_t write_file_dfs(struct file *file, const char __user *user_buf, return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val == DFS_STATS_RESET_MAGIC) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index f5dda84176c..9e582e14da7 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -234,10 +234,15 @@ static inline void ath9k_skb_queue_complete(struct hif_device_usb *hif_dev, struct sk_buff *skb; while ((skb = __skb_dequeue(queue)) != NULL) { +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + int ln = skb->len; +#endif ath9k_htc_txcompletion_cb(hif_dev->htc_handle, skb, txok); - if (txok) + if (txok) { TX_STAT_INC(skb_success); + TX_STAT_ADD(skb_success_bytes, ln); + } else TX_STAT_INC(skb_failed); } @@ -620,6 +625,7 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, err: for (i = 0; i < pool_index; i++) { + RX_STAT_ADD(skb_completed_bytes, skb_pool[i]->len); ath9k_htc_rx_msg(hif_dev->htc_handle, skb_pool[i], skb_pool[i]->len, USB_WLAN_RX_PIPE); RX_STAT_INC(skb_completed); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index d3b099d7898..055d7c25e09 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -142,6 +142,7 @@ struct ath9k_htc_target_aggr { #define WLAN_RC_40_FLAG 0x02 #define WLAN_RC_SGI_FLAG 0x04 #define WLAN_RC_HT_FLAG 0x08 +#define ATH_RC_TX_STBC_FLAG 0x20 struct ath9k_htc_rateset { u8 rs_nrates; @@ -208,6 +209,9 @@ struct ath9k_htc_target_rx_stats { case NL80211_IFTYPE_AP: \ _priv->num_ap_vif++; \ break; \ + case NL80211_IFTYPE_MESH_POINT: \ + _priv->num_mbss_vif++; \ + break; \ default: \ break; \ } \ @@ -224,6 +228,9 @@ struct ath9k_htc_target_rx_stats { case NL80211_IFTYPE_AP: \ _priv->num_ap_vif--; \ break; \ + case NL80211_IFTYPE_MESH_POINT: \ + _priv->num_mbss_vif--; \ + break; \ default: \ break; \ } \ @@ -317,7 +324,9 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb) #ifdef CONFIG_ATH9K_HTC_DEBUGFS #define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) +#define TX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a) #define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++) +#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c += a) #define CAB_STAT_INC priv->debug.tx_stats.cab_queued++ #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++) @@ -330,6 +339,7 @@ struct ath_tx_stats { u32 buf_completed; u32 skb_queued; u32 skb_success; + u32 skb_success_bytes; u32 skb_failed; u32 cab_queued; u32 queue_stats[IEEE80211_NUM_ACS]; @@ -338,6 +348,7 @@ struct ath_tx_stats { struct ath_rx_stats { u32 skb_allocated; u32 skb_completed; + u32 skb_completed_bytes; u32 skb_dropped; u32 err_crc; u32 err_decrypt_crc; @@ -355,10 +366,20 @@ struct ath9k_debug { struct ath_rx_stats rx_stats; }; +void ath9k_htc_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data); +int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset); +void ath9k_htc_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data); #else #define TX_STAT_INC(c) do { } while (0) +#define TX_STAT_ADD(c, a) do { } while (0) #define RX_STAT_INC(c) do { } while (0) +#define RX_STAT_ADD(c, a) do { } while (0) #define CAB_STAT_INC do { } while (0) #define TX_QSTAT_INC(c) do { } while (0) @@ -450,6 +471,7 @@ struct ath9k_htc_priv { u8 sta_slot; u8 vif_sta_pos[ATH9K_HTC_MAX_VIF]; u8 num_ibss_vif; + u8 num_mbss_vif; u8 num_sta_vif; u8 num_sta_assoc_vif; u8 num_ap_vif; @@ -575,6 +597,8 @@ bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv); void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw); +struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv); + #ifdef CONFIG_MAC80211_LEDS void ath9k_init_leds(struct ath9k_htc_priv *priv); void ath9k_deinit_leds(struct ath9k_htc_priv *priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index f13f458dd65..e0c03bd6418 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); - if (priv->ah->opmode == NL80211_IFTYPE_AP) { + if (priv->ah->opmode == NL80211_IFTYPE_AP || + priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) { qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; @@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: ath9k_htc_beacon_config_ap(priv, cur_conf); break; @@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: ath9k_htc_beacon_config_ap(priv, cur_conf); break; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index 87110de577e..c1b45e2f848 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -471,7 +471,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf, return -EFAULT; buf[len] = '\0'; - if (strict_strtoul(buf, 0, &mask)) + if (kstrtoul(buf, 0, &mask)) return -EINVAL; common->debug_mask = mask; @@ -496,21 +496,7 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, ssize_t retval = 0; char *buf; - /* - * This can be done since all the 3 EEPROM families have the - * same base header upto a certain point, and we are interested in - * the data only upto that point. - */ - - if (AR_SREV_9271(priv->ah)) - pBase = (struct base_eep_header *) - &priv->ah->eeprom.map4k.baseEepHeader; - else if (priv->ah->hw_version.usbdev == AR9280_USB) - pBase = (struct base_eep_header *) - &priv->ah->eeprom.def.baseEepHeader; - else if (priv->ah->hw_version.usbdev == AR9287_USB) - pBase = (struct base_eep_header *) - &priv->ah->eeprom.map9287.baseEepHeader; + pBase = ath9k_htc_get_eeprom_base(priv); if (pBase == NULL) { ath_err(common, "Unknown EEPROM type\n"); @@ -916,6 +902,87 @@ static const struct file_operations fops_modal_eeprom = { .llseek = default_llseek, }; + +/* Ethtool support for get-stats */ +#define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" +static const char ath9k_htc_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_pkts_nic", + "tx_bytes_nic", + "rx_pkts_nic", + "rx_bytes_nic", + + AMKSTR(d_tx_pkts), + + "d_rx_crc_err", + "d_rx_decrypt_crc_err", + "d_rx_phy_err", + "d_rx_mic_err", + "d_rx_pre_delim_crc_err", + "d_rx_post_delim_crc_err", + "d_rx_decrypt_busy_err", + + "d_rx_phyerr_radar", + "d_rx_phyerr_ofdm_timing", + "d_rx_phyerr_cck_timing", + +}; +#define ATH9K_HTC_SSTATS_LEN ARRAY_SIZE(ath9k_htc_gstrings_stats) + +void ath9k_htc_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, *ath9k_htc_gstrings_stats, + sizeof(ath9k_htc_gstrings_stats)); +} + +int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return ATH9K_HTC_SSTATS_LEN; + return 0; +} + +#define STXBASE priv->debug.tx_stats +#define SRXBASE priv->debug.rx_stats +#define ASTXQ(a) \ + data[i++] = STXBASE.a[IEEE80211_AC_BE]; \ + data[i++] = STXBASE.a[IEEE80211_AC_BK]; \ + data[i++] = STXBASE.a[IEEE80211_AC_VI]; \ + data[i++] = STXBASE.a[IEEE80211_AC_VO] + +void ath9k_htc_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct ath9k_htc_priv *priv = hw->priv; + int i = 0; + + data[i++] = STXBASE.skb_success; + data[i++] = STXBASE.skb_success_bytes; + data[i++] = SRXBASE.skb_completed; + data[i++] = SRXBASE.skb_completed_bytes; + + ASTXQ(queue_stats); + + data[i++] = SRXBASE.err_crc; + data[i++] = SRXBASE.err_decrypt_crc; + data[i++] = SRXBASE.err_phy; + data[i++] = SRXBASE.err_mic; + data[i++] = SRXBASE.err_pre_delim; + data[i++] = SRXBASE.err_post_delim; + data[i++] = SRXBASE.err_decrypt_busy; + + data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_RADAR]; + data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_OFDM_TIMING]; + data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_CCK_TIMING]; + + WARN_ON(i != ATH9K_HTC_SSTATS_LEN); +} + + int ath9k_htc_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index a47f5e05fc0..71a183ffc77 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -517,6 +517,9 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv, ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", tx_streams, rx_streams); + if (tx_streams >= 2) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + if (tx_streams != rx_streams) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_streams - 1) << @@ -698,6 +701,9 @@ static const struct ieee80211_iface_limit if_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) }, { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_GO) }, }; @@ -712,6 +718,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw) { struct ath_common *common = ath9k_hw_common(priv->ah); + struct base_eep_header *pBase; hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | @@ -721,6 +728,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; hw->wiphy->interface_modes = @@ -728,7 +736,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT); + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; @@ -765,6 +774,12 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap); } + pBase = ath9k_htc_get_eeprom_base(priv); + if (pBase) { + hw->wiphy->available_antennas_rx = pBase->rxMask; + hw->wiphy->available_antennas_tx = pBase->txMask; + } + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 62f1b7636c9..5c1bec18c9e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath9k_htc_priv *priv = data; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon) + if ((vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT) && + bss_conf->enable_beacon) priv->reconfig_beacon = true; if (bss_conf->assoc) { @@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv) priv->ah->opmode = NL80211_IFTYPE_ADHOC; else if (priv->num_ap_vif) priv->ah->opmode = NL80211_IFTYPE_AP; + else if (priv->num_mbss_vif) + priv->ah->opmode = NL80211_IFTYPE_MESH_POINT; else priv->ah->opmode = NL80211_IFTYPE_STATION; @@ -623,6 +627,8 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv, trate->rates.ht_rates.rs_nrates = j; caps = WLAN_RC_HT_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + caps |= ATH_RC_TX_STBC_FLAG; if (sta->ht_cap.mcs.rx_mask[1]) caps |= WLAN_RC_DS_FLAG; if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && @@ -810,8 +816,7 @@ void ath9k_htc_ani_work(struct work_struct *work) } /* Verify whether we must check ANI */ - if (ah->config.enable_ani && - (timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { aniflag = true; common->ani.checkani_timer = timestamp; } @@ -841,8 +846,7 @@ set_timer: * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; - if (ah->config.enable_ani) - cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); + cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); @@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; + case NL80211_IFTYPE_MESH_POINT: + hvif.opmode = HTC_M_WDS; /* close enough */ + break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); @@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || + (vif->type == NL80211_IFTYPE_MESH_POINT) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); @@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, DEC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || + vif->type == NL80211_IFTYPE_MESH_POINT || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_remove_bslot(priv, vif); @@ -1525,9 +1534,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { /* * Disable SWBA interrupt only if there are no - * AP/IBSS interfaces. + * concurrent AP/mesh or IBSS interfaces. */ - if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { + if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) || + priv->num_ibss_vif) { ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); @@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_INT) { /* - * Reset the HW TSF for the first AP interface. + * Reset the HW TSF for the first AP or mesh interface. */ - if ((priv->ah->opmode == NL80211_IFTYPE_AP) && - (priv->nvifs == 1) && - (priv->num_ap_vif == 1) && - (vif->type == NL80211_IFTYPE_AP)) { + if (priv->nvifs == 1 && + ((priv->ah->opmode == NL80211_IFTYPE_AP && + vif->type == NL80211_IFTYPE_AP && + priv->num_ap_vif == 1) || + (priv->ah->opmode == NL80211_IFTYPE_MESH_POINT && + vif->type == NL80211_IFTYPE_MESH_POINT && + priv->num_mbss_vif == 1))) { set_bit(OP_TSF_RESET, &priv->op_flags); } ath_dbg(common, CONFIG, @@ -1761,6 +1774,43 @@ static int ath9k_htc_get_stats(struct ieee80211_hw *hw, return 0; } +struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv) +{ + struct base_eep_header *pBase = NULL; + /* + * This can be done since all the 3 EEPROM families have the + * same base header upto a certain point, and we are interested in + * the data only upto that point. + */ + + if (AR_SREV_9271(priv->ah)) + pBase = (struct base_eep_header *) + &priv->ah->eeprom.map4k.baseEepHeader; + else if (priv->ah->hw_version.usbdev == AR9280_USB) + pBase = (struct base_eep_header *) + &priv->ah->eeprom.def.baseEepHeader; + else if (priv->ah->hw_version.usbdev == AR9287_USB) + pBase = (struct base_eep_header *) + &priv->ah->eeprom.map9287.baseEepHeader; + return pBase; +} + + +static int ath9k_htc_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, + u32 *rx_ant) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct base_eep_header *pBase = ath9k_htc_get_eeprom_base(priv); + if (pBase) { + *tx_ant = pBase->txMask; + *rx_ant = pBase->rxMask; + } else { + *tx_ant = 0; + *rx_ant = 0; + } + return 0; +} + struct ieee80211_ops ath9k_htc_ops = { .tx = ath9k_htc_tx, .start = ath9k_htc_start, @@ -1786,4 +1836,11 @@ struct ieee80211_ops ath9k_htc_ops = { .set_coverage_class = ath9k_htc_set_coverage_class, .set_bitrate_mask = ath9k_htc_set_bitrate_mask, .get_stats = ath9k_htc_get_stats, + .get_antenna = ath9k_htc_get_antenna, + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + .get_et_sset_count = ath9k_htc_get_et_sset_count, + .get_et_stats = ath9k_htc_get_et_stats, + .get_et_strings = ath9k_htc_get_et_strings, +#endif }; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 6bd0e92ea2a..e602c951970 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) if (priv->rxfilter & FIF_PSPOLL) rfilt |= ATH9K_RX_FILTER_PSPOLL; - if (priv->nvifs > 1) + if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS) rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; return rfilt; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 15dfefcf2d0..4ca0cb06010 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -452,7 +452,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.pcie_clock_req = 0; ah->config.pcie_waen = 0; ah->config.analog_shiftreg = 1; - ah->config.enable_ani = true; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { ah->config.spurchans[i][0] = AR_NO_SPUR; @@ -549,8 +548,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah) ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); - if (ah->config.enable_ani) - ath9k_hw_ani_init(ah); + ath9k_hw_ani_init(ah); return 0; } @@ -1250,10 +1248,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) switch (opmode) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: set |= AR_STA_ID1_ADHOC; REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; /* fall through */ @@ -1872,7 +1870,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ah->caldata = caldata; if (caldata && (chan->channel != caldata->channel || - chan->channelFlags != caldata->channelFlags)) { + chan->channelFlags != caldata->channelFlags || + chan->chanmode != caldata->chanmode)) { /* Operating channel changed, reset channel calibration data */ memset(caldata, 0, sizeof(*caldata)); ath9k_init_nfcal_hist_buffer(ah, chan); @@ -2255,12 +2254,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) switch (ah->opmode) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); flags |= AR_NDP_TIMER_EN; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon - @@ -2600,17 +2599,12 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (!(ah->ent_mode & AR_ENT_OTP_49GHZ_DISABLE)) pCap->hw_caps |= ATH9K_HW_CAP_MCI; - if (AR_SREV_9462_20(ah)) + if (AR_SREV_9462_20_OR_LATER(ah)) pCap->hw_caps |= ATH9K_HW_CAP_RTT; } - if (AR_SREV_9280_20_OR_LATER(ah)) { - pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE | - ATH9K_HW_WOW_PATTERN_MATCH_EXACT; - - if (AR_SREV_9280(ah)) - pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD; - } + if (AR_SREV_9462(ah)) + pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE; if (AR_SREV_9300_20_OR_LATER(ah) && ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) @@ -3048,7 +3042,7 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah, timer_next = tsf + trig_timeout; - ath_dbg(ath9k_hw_common(ah), HWTIMER, + ath_dbg(ath9k_hw_common(ah), BTCOEX, "current tsf %x period %x timer_next %x\n", tsf, timer_period, timer_next); @@ -3147,7 +3141,7 @@ void ath_gen_timer_isr(struct ath_hw *ah) index = rightmost_index(timer_table, &thresh_mask); timer = timer_table->timers[index]; BUG_ON(!timer); - ath_dbg(common, HWTIMER, "TSF overflow for Gen timer %d\n", + ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n", index); timer->overflow(timer->arg); } @@ -3156,7 +3150,7 @@ void ath_gen_timer_isr(struct ath_hw *ah) index = rightmost_index(timer_table, &trigger_mask); timer = timer_table->timers[index]; BUG_ON(!timer); - ath_dbg(common, HWTIMER, + ath_dbg(common, BTCOEX, "Gen timer[%d] trigger\n", index); timer->trigger(timer->arg); } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index ae3034374bc..cd74b3afef7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -246,9 +246,7 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_MCI = BIT(15), ATH9K_HW_CAP_DFS = BIT(16), ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17), - ATH9K_HW_WOW_PATTERN_MATCH_EXACT = BIT(18), - ATH9K_HW_WOW_PATTERN_MATCH_DWORD = BIT(19), - ATH9K_HW_CAP_PAPRD = BIT(20), + ATH9K_HW_CAP_PAPRD = BIT(18), }; /* @@ -291,7 +289,6 @@ struct ath9k_ops_config { u32 ofdm_trig_high; u32 cck_trig_high; u32 cck_trig_low; - u32 enable_ani; u32 enable_paprd; int serialize_regmode; bool rx_intr_mitigation; @@ -310,6 +307,10 @@ struct ath9k_ops_config { u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; u8 max_txtrig_level; u16 ani_poll_interval; /* ANI poll interval in ms */ + + /* Platform specific config */ + u32 xlna_gpio; + bool xatten_margin_cfg; }; enum ath9k_int { @@ -423,7 +424,6 @@ struct ath9k_hw_cal_data { struct ath9k_channel { struct ieee80211_channel *chan; - struct ar5416AniState ani; u16 channel; u32 channelFlags; u32 chanmode; @@ -854,10 +854,10 @@ struct ath_hw { u32 globaltxtimeout; /* ANI */ - u32 proc_phyerr; u32 aniperiod; enum ath9k_ani_cmd ani_function; u32 ani_skip_count; + struct ar5416AniState ani; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex_hw btcoex_hw; @@ -882,9 +882,6 @@ struct ath_hw { struct ar5416IniArray iniBank6; struct ar5416IniArray iniAddac; struct ar5416IniArray iniPcieSerdes; -#ifdef CONFIG_PM_SLEEP - struct ar5416IniArray iniPcieSerdesWow; -#endif struct ar5416IniArray iniPcieSerdesLowPower; struct ar5416IniArray iniModesFastClock; struct ar5416IniArray iniAdditional; @@ -895,6 +892,9 @@ struct ath_hw { struct ar5416IniArray iniCckfirJapan2484; struct ar5416IniArray iniModes_9271_ANI_reg; struct ar5416IniArray ini_radio_post_sys2ant; + struct ar5416IniArray ini_modes_rxgain_5g_xlna; + struct ar5416IniArray ini_modes_rxgain_bb_core; + struct ar5416IniArray ini_modes_rxgain_bb_postamble; struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT]; struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT]; @@ -1165,8 +1165,6 @@ static inline void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) } #endif - - #define ATH9K_CLOCK_RATE_CCK 22 #define ATH9K_CLOCK_RATE_5GHZ_OFDM 40 #define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 2ba49456777..16f8b201642 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -21,6 +21,7 @@ #include <linux/ath9k_platform.h> #include <linux/module.h> #include <linux/relay.h> +#include <net/ieee80211_radiotap.h> #include "ath9k.h" @@ -431,6 +432,8 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); + sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); sc->tx.txq_map[i]->mac80211_qnum = i; @@ -510,6 +513,27 @@ static void ath9k_init_misc(struct ath_softc *sc) sc->spec_config.fft_period = 0xF; } +static void ath9k_init_platform(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + if (common->bus_ops->ath_bus_type != ATH_PCI) + return; + + if (sc->driver_data & (ATH9K_PCI_CUS198 | + ATH9K_PCI_CUS230)) { + ah->config.xlna_gpio = 9; + ah->config.xatten_margin_cfg = true; + + ath_info(common, "Set parameters for %s\n", + (sc->driver_data & ATH9K_PCI_CUS198) ? + "CUS198" : "CUS230"); + } else if (sc->driver_data & ATH9K_PCI_CUS217) { + ath_info(common, "CUS217 card detected\n"); + } +} + static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob, void *ctx) { @@ -602,6 +626,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, common->disable_ani = false; /* + * Platform quirks. + */ + ath9k_init_platform(sc); + + /* * Enable Antenna diversity only when BTCOEX is disabled * and the user manually requests the feature. */ @@ -613,9 +642,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); -#ifdef CONFIG_ATH9K_MAC_DEBUG - spin_lock_init(&sc->debug.samp_lock); -#endif tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, (unsigned long)sc); @@ -755,6 +781,15 @@ static const struct ieee80211_iface_combination if_comb[] = { } }; +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support ath9k_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = MAX_NUM_USER_PATTERN, + .pattern_min_len = 1, + .pattern_max_len = MAX_PATTERN_SIZE, +}; +#endif + void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; @@ -769,12 +804,19 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_RC_TABLE; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) - hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + + if (AR_SREV_9280_20_OR_LATER(ah)) + hw->radiotap_mcs_details |= + IEEE80211_RADIOTAP_MCS_HAVE_STBC; + } if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) hw->flags |= IEEE80211_HW_MFP_CAPABLE; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | @@ -794,21 +836,13 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; #ifdef CONFIG_PM_SLEEP - if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && - device_can_wakeup(sc->dev)) { - - hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT; - hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN; - hw->wiphy->wowlan.pattern_min_len = 1; - hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE; - - } + (sc->driver_data & ATH9K_PCI_WOW) && + device_can_wakeup(sc->dev)) + hw->wiphy->wowlan = &ath9k_wowlan_support; atomic_set(&sc->wow_sleep_proc_intr, -1); atomic_set(&sc->wow_got_bmiss_intr, -1); - #endif hw->queues = 4; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 849259b0737..fff5d3ccc66 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -390,9 +390,7 @@ void ath_ani_calibrate(unsigned long data) } /* Verify whether we must check ANI */ - if (sc->sc_ah->config.enable_ani - && (timestamp - common->ani.checkani_timer) >= - ah->config.ani_poll_interval) { + if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) { aniflag = true; common->ani.checkani_timer = timestamp; } @@ -418,7 +416,6 @@ void ath_ani_calibrate(unsigned long data) longcal ? "long" : "", shortcal ? "short" : "", aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); - ath9k_debug_samp_bb_mac(sc); ath9k_ps_restore(sc); set_timer: @@ -428,9 +425,7 @@ set_timer: * short calibration and long calibration. */ cal_interval = ATH_LONG_CALINTERVAL; - if (sc->sc_ah->config.enable_ani) - cal_interval = min(cal_interval, - (u32)ah->config.ani_poll_interval); + cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval); if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 566109a40fb..2ef05ebffbc 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -547,6 +547,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status = 0; rs->rs_flags = 0; + rs->flag = 0; rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen; rs->rs_tstamp = ads.AR_RcvTimestamp; @@ -586,10 +587,17 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); - rs->rs_flags = - (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; - rs->rs_flags |= - (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; + + /* directly mapped flags for ieee80211_rx_status */ + rs->flag |= + (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0; + rs->flag |= + (ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0; + if (AR_SREV_9280_20_OR_LATER(ah)) + rs->flag |= + (ads.ds_rxstatus3 & AR_STBC) ? + /* we can only Nss=1 STBC */ + (1 << RX_FLAG_STBC_SHIFT) : 0; if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 5865f92998e..b02dfce964b 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -149,6 +149,7 @@ struct ath_rx_status { u32 evm2; u32 evm3; u32 evm4; + u32 flag; /* see enum mac80211_rx_flags */ }; struct ath_htc_rx_status { @@ -533,7 +534,8 @@ struct ar5416_desc { #define AR_2040 0x00000002 #define AR_Parallel40 0x00000004 #define AR_Parallel40_S 2 -#define AR_RxStatusRsvd30 0x000000f8 +#define AR_STBC 0x00000008 /* on ar9280 and later */ +#define AR_RxStatusRsvd30 0x000000f0 #define AR_RxAntenna 0xffffff00 #define AR_RxAntenna_S 8 diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5092ecae770..1737a3e3368 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc) ath_stop_ani(sc); del_timer_sync(&sc->rx_poll_timer); - ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); if (!ath_drain_all_txq(sc)) @@ -1211,13 +1210,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); - /* - * Preserve the current channel values, before updating - * the same channel - */ - if (ah->curchan && (old_pos == pos)) - ath9k_hw_getnf(ah, ah->curchan); - ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], curchan, channel_type); @@ -1273,7 +1265,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) curchan->center_freq); } else { /* perform spectral scan if requested. */ - if (sc->scanning && + if (test_bit(SC_OP_SCANNING, &sc->sc_flags) && sc->spectral_mode == SPECTRAL_CHANSCAN) ath9k_spectral_scan_trigger(hw); } @@ -1690,7 +1682,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, bool flush = false; int ret = 0; - local_bh_disable(); + mutex_lock(&sc->mutex); switch (action) { case IEEE80211_AMPDU_RX_START: @@ -1723,7 +1715,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); } - local_bh_enable(); + mutex_unlock(&sc->mutex); return ret; } @@ -2007,7 +1999,6 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - struct ath9k_hw_capabilities *pcaps = &ah->caps; int pattern_count = 0; int i, byte_cnt; u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; @@ -2077,36 +2068,9 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) /* Create Disassociate pattern mask */ - if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) { - - if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) { - /* - * for AR9280, because of hardware limitation, the - * first 4 bytes have to be matched for all patterns. - * the mask for disassociation and de-auth pattern - * matching need to enable the first 4 bytes. - * also the duration field needs to be filled. - */ - dis_deauth_mask[0] = 0xf0; - - /* - * fill in duration field - FIXME: what is the exact value ? - */ - dis_deauth_pattern[2] = 0xff; - dis_deauth_pattern[3] = 0xff; - } else { - dis_deauth_mask[0] = 0xfe; - } - - dis_deauth_mask[1] = 0x03; - dis_deauth_mask[2] = 0xc0; - } else { - dis_deauth_mask[0] = 0xef; - dis_deauth_mask[1] = 0x3f; - dis_deauth_mask[2] = 0x00; - dis_deauth_mask[3] = 0xfc; - } + dis_deauth_mask[0] = 0xfe; + dis_deauth_mask[1] = 0x03; + dis_deauth_mask[2] = 0xc0; ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); @@ -2342,15 +2306,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) static void ath9k_sw_scan_start(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - - sc->scanning = 1; + set_bit(SC_OP_SCANNING, &sc->sc_flags); } static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - - sc->scanning = 0; + clear_bit(SC_OP_SCANNING, &sc->sc_flags); } struct ieee80211_ops ath9k_ops = { @@ -2378,6 +2340,7 @@ struct ieee80211_ops ath9k_ops = { .flush = ath9k_flush, .tx_frames_pending = ath9k_tx_frames_pending, .tx_last_beacon = ath9k_tx_last_beacon, + .release_buffered_frames = ath9k_release_buffered_frames, .get_stats = ath9k_get_stats, .set_antenna = ath9k_set_antenna, .get_antenna = ath9k_get_antenna, diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 0e0d3958383..c585c9b3597 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -34,8 +34,108 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E AR9300 */ + + /* PCI-E CUS198 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2086), + .driver_data = ATH9K_PCI_CUS198 }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1237), + .driver_data = ATH9K_PCI_CUS198 }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2126), + .driver_data = ATH9K_PCI_CUS198 }, + + /* PCI-E CUS230 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2152), + .driver_data = ATH9K_PCI_CUS230 }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE075), + .driver_data = ATH9K_PCI_CUS230 }, + { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */ { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */ + + /* PCI-E CUS217 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_AZWAVE, + 0x2116), + .driver_data = ATH9K_PCI_CUS217 }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6661), + .driver_data = ATH9K_PCI_CUS217 }, + + /* AR9462 with WoW support */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ATHEROS, + 0x3117), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_LENOVO, + 0x3214), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ATTANSIC, + 0x0091), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_AZWAVE, + 0x2110), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ASUSTEK, + 0x850E), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6631), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6641), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_HP, + 0x1864), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x14CD, /* USI */ + 0x0063), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x14CD, /* USI */ + 0x0064), + .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x10CF, /* Fujitsu */ + 0x1783), + .driver_data = ATH9K_PCI_WOW }, + { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */ { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E AR9565 */ @@ -221,6 +321,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->hw = hw; sc->dev = &pdev->dev; sc->mem = pcim_iomap_table(pdev)[0]; + sc->driver_data = id->driver_data; /* Will be cleared in ath9k_start() */ set_bit(SC_OP_INVALID, &sc->sc_flags); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 8be2b5d8c15..865e043e8aa 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -868,10 +868,7 @@ static int ath9k_process_rate(struct ath_common *common, if (rx_stats->rs_rate & 0x80) { /* HT rate */ rxs->flag |= RX_FLAG_HT; - if (rx_stats->rs_flags & ATH9K_RX_2040) - rxs->flag |= RX_FLAG_40MHZ; - if (rx_stats->rs_flags & ATH9K_RX_GI) - rxs->flag |= RX_FLAG_SHORT_GI; + rxs->flag |= rx_stats->flag; rxs->rate_idx = rx_stats->rs_rate & 0x7f; return 0; } @@ -958,11 +955,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, if (rx_stats->rs_more) return 0; - ath9k_process_rssi(common, hw, hdr, rx_stats); - if (ath9k_process_rate(common, hw, rx_stats, rx_status)) return -EINVAL; + ath9k_process_rssi(common, hw, hdr, rx_stats); + rx_status->band = hw->conf.chandef.chan->band; rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->signal = ah->noise + rx_stats->rs_rssi; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index f7c90cc58d5..5af97442ac3 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -806,6 +806,7 @@ #define AR_SREV_REVISION_9580_10 4 /* AR9580 1.0 */ #define AR_SREV_VERSION_9462 0x280 #define AR_SREV_REVISION_9462_20 2 +#define AR_SREV_REVISION_9462_21 3 #define AR_SREV_VERSION_9565 0x2C0 #define AR_SREV_REVISION_9565_10 0 #define AR_SREV_VERSION_9550 0x400 @@ -911,10 +912,18 @@ #define AR_SREV_9462(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462)) - #define AR_SREV_9462_20(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20)) + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20)) +#define AR_SREV_9462_21(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_21)) +#define AR_SREV_9462_20_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20)) +#define AR_SREV_9462_21_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_21)) #define AR_SREV_9565(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565)) diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 9f8563091be..81c88dd606d 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -34,17 +34,6 @@ const char *ath9k_hw_wow_event_to_string(u32 wow_event) } EXPORT_SYMBOL(ath9k_hw_wow_event_to_string); -static void ath9k_hw_config_serdes_wow_sleep(struct ath_hw *ah) -{ - int i; - - for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++) - REG_WRITE(ah, INI_RA(&ah->iniPcieSerdesWow, i, 0), - INI_RA(&ah->iniPcieSerdesWow, i, 1)); - - usleep_range(1000, 1500); -} - static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -58,15 +47,8 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); return; - } else { - if (!AR_SREV_9300_20_OR_LATER(ah)) - REG_WRITE(ah, AR_RXDP, 0x0); } - /* AR9280 WoW has sleep issue, do not set it to sleep */ - if (AR_SREV_9280_20(ah)) - return; - REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } @@ -84,27 +66,16 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) /* set the transmit buffer */ ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16)); - - if (!(AR_SREV_9300_20_OR_LATER(ah))) - ctl[0] += (KAL_ANTENNA_MODE << 25); - ctl[1] = 0; ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */ ctl[4] = 0; ctl[7] = (ah->txchainmask) << 2; - - if (AR_SREV_9300_20_OR_LATER(ah)) - ctl[2] = 0xf << 16; /* tx_tries 0 */ - else - ctl[2] = 0x7 << 16; /* tx_tries 0 */ - + ctl[2] = 0xf << 16; /* tx_tries 0 */ for (i = 0; i < KAL_NUM_DESC_WORDS; i++) REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); - /* for AR9300 family 13 descriptor words */ - if (AR_SREV_9300_20_OR_LATER(ah)) - REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); + REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) | (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16); @@ -183,9 +154,6 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); - if (!AR_SREV_9285_12_OR_LATER(ah)) - return; - if (pattern_count < 4) { /* Pattern 0-3 uses AR_WOW_LENGTH1 register */ set = (pattern_len & AR_WOW_LENGTH_MAX) << @@ -207,6 +175,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) { u32 wow_status = 0; u32 val = 0, rval; + /* * read the WoW status register to know * the wakeup reason @@ -223,19 +192,14 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) val &= ah->wow_event_mask; if (val) { - if (val & AR_WOW_MAGIC_PAT_FOUND) wow_status |= AH_WOW_MAGIC_PATTERN_EN; - if (AR_WOW_PATTERN_FOUND(val)) wow_status |= AH_WOW_USER_PATTERN_EN; - if (val & AR_WOW_KEEP_ALIVE_FAIL) wow_status |= AH_WOW_LINK_CHANGE; - if (val & AR_WOW_BEACON_FAIL) wow_status |= AH_WOW_BEACON_MISS; - } /* @@ -255,17 +219,6 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN))); /* - * tie reset register for AR9002 family of chipsets - * NB: not tieing it back might have some repurcussions. - */ - - if (!AR_SREV_9300_20_OR_LATER(ah)) { - REG_SET_BIT(ah, AR_WA, AR_WA_UNTIE_RESET_EN | - AR_WA_POR_SHORT | AR_WA_RESET_EN); - } - - - /* * restore the beacon threshold to init value */ REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); @@ -277,8 +230,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) * reset to our Chip's Power On Reset so that any PCI-E * reset from the bus will not reset our chip */ - - if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress) + if (ah->is_pciexpress) ath9k_hw_configpcipowersave(ah, false); ah->wow_event_mask = 0; @@ -298,7 +250,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) * are from the 'pattern_enable' in this function and * 'pattern_count' of ath9k_hw_wow_apply_pattern() */ - wow_event_mask = ah->wow_event_mask; /* @@ -306,50 +257,15 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) * WOW sleep, we do want the Reset from the PCI-E to disturb * our hw state */ - if (ah->is_pciexpress) { - /* * we need to untie the internal POR (power-on-reset) * to the external PCI-E reset. We also need to tie * the PCI-E Phy reset to the PCI-E reset. */ - - if (AR_SREV_9300_20_OR_LATER(ah)) { - set = AR_WA_RESET_EN | AR_WA_POR_SHORT; - clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE; - REG_RMW(ah, AR_WA, set, clr); - } else { - if (AR_SREV_9285(ah) || AR_SREV_9287(ah)) - set = AR9285_WA_DEFAULT; - else - set = AR9280_WA_DEFAULT; - - /* - * In AR9280 and AR9285, bit 14 in WA register - * (disable L1) should only be set when device - * enters D3 state and be cleared when device - * comes back to D0 - */ - - if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE) - set |= AR_WA_D3_L1_DISABLE; - - clr = AR_WA_UNTIE_RESET_EN; - set |= AR_WA_RESET_EN | AR_WA_POR_SHORT; - REG_RMW(ah, AR_WA, set, clr); - - /* - * for WoW sleep, we reprogram the SerDes so that the - * PLL and CLK REQ are both enabled. This uses more - * power but otherwise WoW sleep is unstable and the - * chip may disappear. - */ - - if (AR_SREV_9285_12_OR_LATER(ah)) - ath9k_hw_config_serdes_wow_sleep(ah); - - } + set = AR_WA_RESET_EN | AR_WA_POR_SHORT; + clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE; + REG_RMW(ah, AR_WA, set, clr); } /* @@ -378,7 +294,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) * Program default values for pattern backoff, aifs/slot/KAL count, * beacon miss timeout, KAL timeout, etc. */ - set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF); REG_SET_BIT(ah, AR_WOW_PATTERN, set); @@ -398,7 +313,7 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) /* * Keep alive timo in ms except AR9280 */ - if (!pattern_enable || AR_SREV_9280(ah)) + if (!pattern_enable) set = AR_WOW_KEEP_ALIVE_NEVER; else set = KAL_TIMEOUT * 32; @@ -420,7 +335,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) /* * Configure MAC WoW Registers */ - set = 0; /* Send keep alive timeouts anyway */ clr = AR_WOW_KEEP_ALIVE_AUTO_DIS; @@ -430,16 +344,9 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) else set = AR_WOW_KEEP_ALIVE_FAIL_DIS; - /* - * FIXME: For now disable keep alive frame - * failure. This seems to sometimes trigger - * unnecessary wake up with AR9485 chipsets. - */ set = AR_WOW_KEEP_ALIVE_FAIL_DIS; - REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr); - /* * we are relying on a bmiss failure. ensure we have * enough threshold to prevent false positives @@ -473,14 +380,8 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) set |= AR_WOW_MAC_INTR_EN; REG_RMW(ah, AR_WOW_PATTERN, set, clr); - /* - * For AR9285 and later version of chipsets - * enable WoW pattern match for packets less - * than 256 bytes for all patterns - */ - if (AR_SREV_9285_12_OR_LATER(ah)) - REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B, - AR_WOW_PATTERN_SUPPORTED); + REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B, + AR_WOW_PATTERN_SUPPORTED); /* * Set the power states appropriately and enable PME @@ -488,43 +389,32 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) clr = 0; set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA; - /* - * This is needed for AR9300 chipsets to wake-up - * the host. - */ - if (AR_SREV_9300_20_OR_LATER(ah)) - clr = AR_PCIE_PM_CTRL_ENA; + clr = AR_PCIE_PM_CTRL_ENA; REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr); - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { - /* - * this is needed to prevent the chip waking up - * the host within 3-4 seconds with certain - * platform/BIOS. The fix is to enable - * D1 & D3 to match original definition and - * also match the OTP value. Anyway this - * is more related to SW WOW. - */ - clr = AR_PMCTRL_PWR_STATE_D1D3; - REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); - - set = AR_PMCTRL_PWR_STATE_D1D3_REAL; - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); - } - + /* + * this is needed to prevent the chip waking up + * the host within 3-4 seconds with certain + * platform/BIOS. The fix is to enable + * D1 & D3 to match original definition and + * also match the OTP value. Anyway this + * is more related to SW WOW. + */ + clr = AR_PMCTRL_PWR_STATE_D1D3; + REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); + set = AR_PMCTRL_PWR_STATE_D1D3_REAL; + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); - if (AR_SREV_9300_20_OR_LATER(ah)) { - /* to bring down WOW power low margin */ - set = BIT(13); - REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); - /* HW WoW */ - clr = BIT(5); - REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr); - } + /* to bring down WOW power low margin */ + set = BIT(13); + REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); + /* HW WoW */ + clr = BIT(5); + REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr); ath9k_hw_set_powermode_wow_sleep(ah); ah->wow_event_mask = wow_event_mask; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 83ab6be3fe6..c59ae43b9b3 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -518,6 +518,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, !txfail); } else { + if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) { + tx_info->flags &= ~IEEE80211_TX_STATUS_EOSP; + ieee80211_sta_eosp(sta); + } /* retry the un-acked ones */ if (bf->bf_next == NULL && bf_last->bf_stale) { struct ath_buf *tbf; @@ -786,25 +790,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, return ndelim; } -static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, - struct ath_txq *txq, - struct ath_atx_tid *tid, - struct list_head *bf_q, - int *aggr_len) +static struct ath_buf * +ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid) { -#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) - struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; - int rl = 0, nframes = 0, ndelim, prev_al = 0; - u16 aggr_limit = 0, al = 0, bpad = 0, - al_delta, h_baw = tid->baw_size / 2; - enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; - struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; struct sk_buff *skb; + struct ath_buf *bf; u16 seqno; - do { + while (1) { skb = skb_peek(&tid->buf_q); + if (!skb) + break; + fi = get_frame_info(skb); bf = fi->bf; if (!fi->bf) @@ -820,10 +819,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, seqno = bf->bf_state.seqno; /* do not step over block-ack window */ - if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { - status = ATH_AGGR_BAW_CLOSED; + if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) break; - } if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) { struct ath_tx_status ts = {}; @@ -837,6 +834,40 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, continue; } + bf->bf_next = NULL; + bf->bf_lastbf = bf; + return bf; + } + + return NULL; +} + +static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_atx_tid *tid, + struct list_head *bf_q, + int *aggr_len) +{ +#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) + struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; + int rl = 0, nframes = 0, ndelim, prev_al = 0; + u16 aggr_limit = 0, al = 0, bpad = 0, + al_delta, h_baw = tid->baw_size / 2; + enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; + struct ieee80211_tx_info *tx_info; + struct ath_frame_info *fi; + struct sk_buff *skb; + + do { + bf = ath_tx_get_tid_subframe(sc, txq, tid); + if (!bf) { + status = ATH_AGGR_BAW_CLOSED; + break; + } + + skb = bf->bf_mpdu; + fi = get_frame_info(skb); + if (!bf_first) bf_first = bf; @@ -882,7 +913,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, /* link buffers of this frame to the aggregate */ if (!fi->retries) - ath_tx_addto_baw(sc, tid, seqno); + ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); bf->bf_state.ndelim = ndelim; __skb_unlink(skb, &tid->buf_q); @@ -1090,10 +1121,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, int len) { struct ath_hw *ah = sc->sc_ah; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); - struct ath_buf *bf_first = bf; + struct ath_buf *bf_first = NULL; struct ath_tx_info info; - bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR); memset(&info, 0, sizeof(info)); info.is_first = true; @@ -1101,24 +1130,11 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, info.txpower = MAX_RATE_POWER; info.qcu = txq->axq_qnum; - info.flags = ATH9K_TXDESC_INTREQ; - if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) - info.flags |= ATH9K_TXDESC_NOACK; - if (tx_info->flags & IEEE80211_TX_CTL_LDPC) - info.flags |= ATH9K_TXDESC_LDPC; - - ath_buf_set_rate(sc, bf, &info, len); - - if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) - info.flags |= ATH9K_TXDESC_CLRDMASK; - - if (bf->bf_state.bfs_paprd) - info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S; - - while (bf) { struct sk_buff *skb = bf->bf_mpdu; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_frame_info *fi = get_frame_info(skb); + bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR); info.type = get_hw_packet_type(skb); if (bf->bf_next) @@ -1126,6 +1142,26 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, else info.link = 0; + if (!bf_first) { + bf_first = bf; + + info.flags = ATH9K_TXDESC_INTREQ; + if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) || + txq == sc->tx.uapsdq) + info.flags |= ATH9K_TXDESC_CLRDMASK; + + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + info.flags |= ATH9K_TXDESC_NOACK; + if (tx_info->flags & IEEE80211_TX_CTL_LDPC) + info.flags |= ATH9K_TXDESC_LDPC; + + if (bf->bf_state.bfs_paprd) + info.flags |= (u32) bf->bf_state.bfs_paprd << + ATH9K_TXDESC_PAPRD_S; + + ath_buf_set_rate(sc, bf, &info, len); + } + info.buf_addr[0] = bf->bf_buf_addr; info.buf_len[0] = skb->len; info.pkt_len = fi->framelen; @@ -1135,7 +1171,7 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (aggr) { if (bf == bf_first) info.aggr = AGGR_BUF_FIRST; - else if (!bf->bf_next) + else if (bf == bf_first->bf_lastbf) info.aggr = AGGR_BUF_LAST; else info.aggr = AGGR_BUF_MIDDLE; @@ -1144,6 +1180,9 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, info.aggr_len = len; } + if (bf == bf_first->bf_lastbf) + bf_first = NULL; + ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); bf = bf->bf_next; } @@ -1328,6 +1367,70 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, ath_txq_unlock_complete(sc, txq); } +void ath9k_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct ath_softc *sc = hw->priv; + struct ath_node *an = (struct ath_node *)sta->drv_priv; + struct ath_txq *txq = sc->tx.uapsdq; + struct ieee80211_tx_info *info; + struct list_head bf_q; + struct ath_buf *bf_tail = NULL, *bf; + int sent = 0; + int i; + + INIT_LIST_HEAD(&bf_q); + for (i = 0; tids && nframes; i++, tids >>= 1) { + struct ath_atx_tid *tid; + + if (!(tids & 1)) + continue; + + tid = ATH_AN_2_TID(an, i); + if (tid->paused) + continue; + + ath_txq_lock(sc, tid->ac->txq); + while (!skb_queue_empty(&tid->buf_q) && nframes > 0) { + bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid); + if (!bf) + break; + + __skb_unlink(bf->bf_mpdu, &tid->buf_q); + list_add_tail(&bf->list, &bf_q); + ath_set_rates(tid->an->vif, tid->an->sta, bf); + ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); + bf->bf_state.bf_type &= ~BUF_AGGR; + if (bf_tail) + bf_tail->bf_next = bf; + + bf_tail = bf; + nframes--; + sent++; + TX_STAT_INC(txq->axq_qnum, a_queued_hw); + + if (skb_queue_empty(&tid->buf_q)) + ieee80211_sta_set_buffered(an->sta, i, false); + } + ath_txq_unlock_complete(sc, tid->ac->txq); + } + + if (list_empty(&bf_q)) + return; + + info = IEEE80211_SKB_CB(bf_tail->bf_mpdu); + info->flags |= IEEE80211_TX_STATUS_EOSP; + + bf = list_first_entry(&bf_q, struct ath_buf, list); + ath_txq_lock(sc, txq); + ath_tx_fill_desc(sc, bf, txq, 0); + ath_tx_txqaddbuf(sc, txq, &bf_q, false); + ath_txq_unlock(sc, txq); +} + /********************/ /* Queue Management */ /********************/ @@ -1679,14 +1782,19 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, } if (!internal) { - txq->axq_depth++; - if (bf_is_ampdu_not_probing(bf)) - txq->axq_ampdu_depth++; + while (bf) { + txq->axq_depth++; + if (bf_is_ampdu_not_probing(bf)) + txq->axq_ampdu_depth++; + + bf = bf->bf_lastbf->bf_next; + } } } -static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, - struct sk_buff *skb, struct ath_tx_control *txctl) +static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, struct sk_buff *skb, + struct ath_tx_control *txctl) { struct ath_frame_info *fi = get_frame_info(skb); struct list_head bf_head; @@ -1699,21 +1807,22 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, * - seqno is not within block-ack window * - h/w queue depth exceeds low water mark */ - if (!skb_queue_empty(&tid->buf_q) || tid->paused || - !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) || - txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) { + if ((!skb_queue_empty(&tid->buf_q) || tid->paused || + !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) || + txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) && + txq != sc->tx.uapsdq) { /* * Add this frame to software queue for scheduling later * for aggregation. */ - TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw); + TX_STAT_INC(txq->axq_qnum, a_queued_sw); __skb_queue_tail(&tid->buf_q, skb); if (!txctl->an || !txctl->an->sleeping) - ath_tx_queue_tid(txctl->txq, tid); + ath_tx_queue_tid(txq, tid); return; } - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txq, tid, skb); if (!bf) { ieee80211_free_txskb(sc->hw, skb); return; @@ -1728,10 +1837,10 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); /* Queue to h/w without aggregation */ - TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw); + TX_STAT_INC(txq->axq_qnum, a_queued_hw); bf->bf_lastbf = bf; - ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen); - ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false); + ath_tx_fill_desc(sc, bf, txq, fi->framelen); + ath_tx_txqaddbuf(sc, txq, &bf_head, false); } static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, @@ -1869,22 +1978,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, return bf; } -/* Upon failure caller should free skb */ -int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath_tx_control *txctl) +static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; struct ath_softc *sc = hw->priv; - struct ath_txq *txq = txctl->txq; - struct ath_atx_tid *tid = NULL; - struct ath_buf *bf; - int padpos, padsize; int frmlen = skb->len + FCS_LEN; - u8 tidno; - int q; + int padpos, padsize; /* NOTE: sta can be NULL according to net/mac80211.h */ if (sta) @@ -1905,6 +2008,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } + if ((vif && vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_AP_VLAN) || + !ieee80211_is_data(hdr->frame_control)) + info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; + /* Add the padding after the header if this is not already done */ padpos = ieee80211_hdrlen(hdr->frame_control); padsize = padpos & 3; @@ -1914,16 +2022,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); - hdr = (struct ieee80211_hdr *) skb->data; } - if ((vif && vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_AP_VLAN) || - !ieee80211_is_data(hdr->frame_control)) - info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - setup_frame_info(hw, sta, skb, frmlen); + return 0; +} + + +/* Upon failure caller should free skb */ +int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = txctl->sta; + struct ieee80211_vif *vif = info->control.vif; + struct ath_softc *sc = hw->priv; + struct ath_txq *txq = txctl->txq; + struct ath_atx_tid *tid = NULL; + struct ath_buf *bf; + u8 tidno; + int q; + int ret; + + ret = ath_tx_prepare(hw, skb, txctl); + if (ret) + return ret; + hdr = (struct ieee80211_hdr *) skb->data; /* * At this point, the vif, hw_key and sta pointers in the tx control * info are no longer valid (overwritten by the ath_frame_info data. @@ -1939,6 +2065,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, txq->stopped = true; } + if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { + ath_txq_unlock(sc, txq); + txq = sc->tx.uapsdq; + ath_txq_lock(sc, txq); + } + if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; @@ -1952,11 +2084,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, * Try aggregation if it's a unicast data frame * and the destination is HT capable. */ - ath_tx_send_ampdu(sc, tid, skb, txctl); + ath_tx_send_ampdu(sc, txq, tid, skb, txctl); goto out; } - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txq, tid, skb); if (!bf) { if (txctl->paprd) dev_kfree_skb_any(skb); @@ -1971,7 +2103,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, bf->bf_state.bfs_paprd_timestamp = jiffies; ath_set_rates(vif, sta, bf); - ath_tx_send_normal(sc, txctl->txq, tid, skb); + ath_tx_send_normal(sc, txq, tid, skb); out: ath_txq_unlock(sc, txq); @@ -1979,6 +2111,74 @@ out: return 0; } +void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct ath_softc *sc = hw->priv; + struct ath_tx_control txctl = { + .txq = sc->beacon.cabq + }; + struct ath_tx_info info = {}; + struct ieee80211_hdr *hdr; + struct ath_buf *bf_tail = NULL; + struct ath_buf *bf; + LIST_HEAD(bf_q); + int duration = 0; + int max_duration; + + max_duration = + sc->cur_beacon_conf.beacon_interval * 1000 * + sc->cur_beacon_conf.dtim_period / ATH_BCBUF; + + do { + struct ath_frame_info *fi = get_frame_info(skb); + + if (ath_tx_prepare(hw, skb, &txctl)) + break; + + bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb); + if (!bf) + break; + + bf->bf_lastbf = bf; + ath_set_rates(vif, NULL, bf); + ath_buf_set_rate(sc, bf, &info, fi->framelen); + duration += info.rates[0].PktDuration; + if (bf_tail) + bf_tail->bf_next = bf; + + list_add_tail(&bf->list, &bf_q); + bf_tail = bf; + skb = NULL; + + if (duration > max_duration) + break; + + skb = ieee80211_get_buffered_bc(hw, vif); + } while(skb); + + if (skb) + ieee80211_free_txskb(hw, skb); + + if (list_empty(&bf_q)) + return; + + bf = list_first_entry(&bf_q, struct ath_buf, list); + hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data; + + if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) { + hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA; + dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, + sizeof(*hdr), DMA_TO_DEVICE); + } + + ath_txq_lock(sc, txctl.txq); + ath_tx_fill_desc(sc, bf, txctl.txq, 0); + ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false); + TX_STAT_INC(txctl.txq->axq_qnum, queued); + ath_txq_unlock(sc, txctl.txq); +} + /*****************/ /* TX Completion */ /*****************/ @@ -2024,7 +2224,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + __skb_queue_tail(&txq->complete_q, skb); + q = skb_get_queue_mapping(skb); + if (txq == sc->tx.uapsdq) + txq = sc->tx.txq_map[q]; + if (txq == sc->tx.txq_map[q]) { if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; @@ -2035,8 +2240,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, txq->stopped = false; } } - - __skb_queue_tail(&txq->complete_q, skb); } static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 9dce106cd6d..8596aba34f9 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -133,6 +133,9 @@ struct carl9170_sta_tid { /* Preaggregation reorder queue */ struct sk_buff_head queue; + + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; }; #define CARL9170_QUEUE_TIMEOUT 256 diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index e9010a481df..4a33c6e39ca 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1448,6 +1448,8 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, tid_info->state = CARL9170_TID_STATE_PROGRESS; tid_info->tid = tid; tid_info->max = sta_info->ampdu_max_len; + tid_info->sta = sta; + tid_info->vif = vif; INIT_LIST_HEAD(&tid_info->list); INIT_LIST_HEAD(&tid_info->tmp_list); @@ -1857,6 +1859,7 @@ void *carl9170_alloc(size_t priv_size) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | + IEEE80211_HW_SUPPORTS_RC_TABLE | IEEE80211_HW_SIGNAL_DBM; if (!modparam_noht) { diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index c61cafa2665..e3f696ee4d2 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -625,7 +625,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar) msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT))) goto unlock; - sta = __carl9170_get_tx_sta(ar, skb); + sta = iter->sta; if (WARN_ON(!sta)) goto unlock; @@ -866,6 +866,93 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar, return false; } +static void carl9170_tx_get_rates(struct ar9170 *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info; + + BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES); + BUILD_BUG_ON(IEEE80211_TX_MAX_RATES > IEEE80211_TX_RATE_TABLE_SIZE); + + info = IEEE80211_SKB_CB(skb); + + ieee80211_get_tx_rates(vif, sta, skb, + info->control.rates, + IEEE80211_TX_MAX_RATES); +} + +static void carl9170_tx_apply_rateset(struct ar9170 *ar, + struct ieee80211_tx_info *sinfo, + struct sk_buff *skb) +{ + struct ieee80211_tx_rate *txrate; + struct ieee80211_tx_info *info; + struct _carl9170_tx_superframe *txc = (void *) skb->data; + int i; + bool ampdu; + bool no_ack; + + info = IEEE80211_SKB_CB(skb); + ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU); + no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK); + + /* Set the rate control probe flag for all (sub-) frames. + * This is because the TX_STATS_AMPDU flag is only set on + * the last frame, so it has to be inherited. + */ + info->flags |= (sinfo->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); + + /* NOTE: For the first rate, the ERP & AMPDU flags are directly + * taken from mac_control. For all fallback rate, the firmware + * updates the mac_control flags from the rate info field. + */ + for (i = 0; i < CARL9170_TX_MAX_RATES; i++) { + __le32 phy_set; + + txrate = &sinfo->control.rates[i]; + if (txrate->idx < 0) + break; + + phy_set = carl9170_tx_physet(ar, info, txrate); + if (i == 0) { + __le16 mac_tmp = cpu_to_le16(0); + + /* first rate - part of the hw's frame header */ + txc->f.phy_control = phy_set; + + if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS) + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR); + + if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); + else if (carl9170_tx_cts_check(ar, txrate)) + mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); + + txc->f.mac_control |= mac_tmp; + } else { + /* fallback rates are stored in the firmware's + * retry rate set array. + */ + txc->s.rr[i - 1] = phy_set; + } + + SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i], + txrate->count); + + if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) + txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS << + CARL9170_TX_SUPER_RI_ERP_PROT_S); + else if (carl9170_tx_cts_check(ar, txrate)) + txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS << + CARL9170_TX_SUPER_RI_ERP_PROT_S); + + if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS)) + txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU; + } +} + static int carl9170_tx_prepare(struct ar9170 *ar, struct ieee80211_sta *sta, struct sk_buff *skb) @@ -874,13 +961,10 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct _carl9170_tx_superframe *txc; struct carl9170_vif_info *cvif; struct ieee80211_tx_info *info; - struct ieee80211_tx_rate *txrate; struct carl9170_tx_info *arinfo; unsigned int hw_queue; - int i; __le16 mac_tmp; u16 len; - bool ampdu, no_ack; BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data)); BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) != @@ -889,8 +973,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar, BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN); - BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES); - BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC > ((CARL9170_TX_SUPER_MISC_VIF_ID >> CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1)); @@ -932,8 +1014,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar, mac_tmp |= cpu_to_le16((hw_queue << AR9170_TX_MAC_QOS_S) & AR9170_TX_MAC_QOS); - no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK); - if (unlikely(no_ack)) + if (unlikely(info->flags & IEEE80211_TX_CTL_NO_ACK)) mac_tmp |= cpu_to_le16(AR9170_TX_MAC_NO_ACK); if (info->control.hw_key) { @@ -954,8 +1035,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar, } } - ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU); - if (ampdu) { + if (info->flags & IEEE80211_TX_CTL_AMPDU) { unsigned int density, factor; if (unlikely(!sta || !cvif)) @@ -982,50 +1062,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar, txc->s.ampdu_settings, factor); } - /* - * NOTE: For the first rate, the ERP & AMPDU flags are directly - * taken from mac_control. For all fallback rate, the firmware - * updates the mac_control flags from the rate info field. - */ - for (i = 0; i < CARL9170_TX_MAX_RATES; i++) { - __le32 phy_set; - txrate = &info->control.rates[i]; - if (txrate->idx < 0) - break; - - phy_set = carl9170_tx_physet(ar, info, txrate); - if (i == 0) { - /* first rate - part of the hw's frame header */ - txc->f.phy_control = phy_set; - - if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS) - mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR); - if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) - mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS); - else if (carl9170_tx_cts_check(ar, txrate)) - mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS); - - } else { - /* fallback rates are stored in the firmware's - * retry rate set array. - */ - txc->s.rr[i - 1] = phy_set; - } - - SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i], - txrate->count); - - if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack)) - txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS << - CARL9170_TX_SUPER_RI_ERP_PROT_S); - else if (carl9170_tx_cts_check(ar, txrate)) - txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS << - CARL9170_TX_SUPER_RI_ERP_PROT_S); - - if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS)) - txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU; - } - txc->s.len = cpu_to_le16(skb->len); txc->f.length = cpu_to_le16(len + FCS_LEN); txc->f.mac_control = mac_tmp; @@ -1086,31 +1122,12 @@ static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb) } } -static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest, - struct sk_buff *_src) -{ - struct _carl9170_tx_superframe *dest, *src; - - dest = (void *) _dest->data; - src = (void *) _src->data; - - /* - * The mac80211 rate control algorithm expects that all MPDUs in - * an AMPDU share the same tx vectors. - * This is not really obvious right now, because the hardware - * does the AMPDU setup according to its own rulebook. - * Our nicely assembled, strictly monotonic increasing mpdu - * chains will be broken up, mashed back together... - */ - - return (dest->f.phy_control == src->f.phy_control); -} - static void carl9170_tx_ampdu(struct ar9170 *ar) { struct sk_buff_head agg; struct carl9170_sta_tid *tid_info; struct sk_buff *skb, *first; + struct ieee80211_tx_info *tx_info_first; unsigned int i = 0, done_ampdus = 0; u16 seq, queue, tmpssn; @@ -1156,6 +1173,7 @@ retry: goto processed; } + tx_info_first = NULL; while ((skb = skb_peek(&tid_info->queue))) { /* strict 0, 1, ..., n - 1, n frame sequence order */ if (unlikely(carl9170_get_seq(skb) != seq)) @@ -1166,8 +1184,13 @@ retry: (tid_info->max - 1))) break; - if (!carl9170_tx_rate_check(ar, skb, first)) - break; + if (!tx_info_first) { + carl9170_tx_get_rates(ar, tid_info->vif, + tid_info->sta, first); + tx_info_first = IEEE80211_SKB_CB(first); + } + + carl9170_tx_apply_rateset(ar, tx_info_first, skb); atomic_inc(&ar->tx_ampdu_upload); tid_info->snx = seq = SEQ_NEXT(seq); @@ -1182,8 +1205,7 @@ retry: if (skb_queue_empty(&tid_info->queue) || carl9170_get_seq(skb_peek(&tid_info->queue)) != tid_info->snx) { - /* - * stop TID, if A-MPDU frames are still missing, + /* stop TID, if A-MPDU frames are still missing, * or whenever the queue is empty. */ @@ -1450,12 +1472,14 @@ void carl9170_op_tx(struct ieee80211_hw *hw, struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; struct ieee80211_sta *sta = control->sta; + struct ieee80211_vif *vif; bool run; if (unlikely(!IS_STARTED(ar))) goto err_free; info = IEEE80211_SKB_CB(skb); + vif = info->control.vif; if (unlikely(carl9170_tx_prepare(ar, sta, skb))) goto err_free; @@ -1486,6 +1510,8 @@ void carl9170_op_tx(struct ieee80211_hw *hw, } else { unsigned int queue = skb_get_queue_mapping(skb); + carl9170_tx_get_rates(ar, vif, sta, skb); + carl9170_tx_apply_rateset(ar, info, skb); skb_queue_tail(&ar->tx_pending[queue], skb); } diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index ccc4c718f12..7d077c752dd 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -42,11 +42,11 @@ static int __ath_regd_init(struct ath_regulatory *reg); NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ -#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\ +#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) -#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\ +#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) -#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\ +#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index bac3d98a0cf..ce8c0381825 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -27,3 +27,15 @@ config WIL6210_ISR_COR self-clear when accessed for debug purposes, it makes such monitoring impossible. Say y unless you debug interrupts + +config WIL6210_TRACING + bool "wil6210 tracing support" + depends on WIL6210 + depends on EVENT_TRACING + default y + ---help--- + Say Y here to enable tracepoints for the wil6210 driver + using the kernel tracing infrastructure. Select this + option if you are interested in debugging the driver. + + If unsure, say Y to make it easier to debug problems. diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index d288eea0a26..f891d514d88 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -1,15 +1,20 @@ obj-$(CONFIG_WIL6210) += wil6210.o -wil6210-objs := main.o -wil6210-objs += netdev.o -wil6210-objs += cfg80211.o -wil6210-objs += pcie_bus.o -wil6210-objs += debugfs.o -wil6210-objs += wmi.o -wil6210-objs += interrupt.o -wil6210-objs += txrx.o +wil6210-y := main.o +wil6210-y += netdev.o +wil6210-y += cfg80211.o +wil6210-y += pcie_bus.o +wil6210-y += debugfs.o +wil6210-y += wmi.o +wil6210-y += interrupt.o +wil6210-y += txrx.o +wil6210-y += debug.o +wil6210-$(CONFIG_WIL6210_TRACING) += trace.o ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) subdir-ccflags-y += -Werror endif +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) + subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index c5d4a87abaa..61c302a6bde 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, * FW don't support scan after connection attempt */ set_bit(wil_status_dontscan, &wil->status); + set_bit(wil_status_fwconnecting, &wil->status); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); if (rc == 0) { /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); + } else { + clear_bit(wil_status_dontscan, &wil->status); + clear_bit(wil_status_fwconnecting, &wil->status); } out: @@ -398,6 +402,30 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy, return 0; } +static int wil_fix_bcon(struct wil6210_priv *wil, + struct cfg80211_beacon_data *bcon) +{ + struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp; + size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + int rc = 0; + + if (bcon->probe_resp_len <= hlen) + return 0; + + if (!bcon->proberesp_ies) { + bcon->proberesp_ies = f->u.probe_resp.variable; + bcon->proberesp_ies_len = bcon->probe_resp_len - hlen; + rc = 1; + } + if (!bcon->assocresp_ies) { + bcon->assocresp_ies = f->u.probe_resp.variable; + bcon->assocresp_ies_len = bcon->probe_resp_len - hlen; + rc = 1; + } + + return rc; +} + static int wil_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_ap_settings *info) @@ -419,10 +447,18 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, info->ssid, info->ssid_len); + if (wil_fix_bcon(wil, bcon)) + wil_dbg_misc(wil, "Fixed bcon\n"); + rc = wil_reset(wil); if (rc) return rc; + /* Rx VRING. */ + rc = wil_rx_init(wil); + if (rc) + return rc; + rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); if (rc) return rc; @@ -451,8 +487,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, if (rc) return rc; - /* Rx VRING. After MAC and beacon */ - rc = wil_rx_init(wil); netif_carrier_on(ndev); diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c new file mode 100644 index 00000000000..9eeabf4a587 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debug.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wil6210.h" +#include "trace.h" + +int wil_err(struct wil6210_priv *wil, const char *fmt, ...) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = netdev_err(ndev, "%pV", &vaf); + trace_wil6210_log_err(&vaf); + va_end(args); + + return ret; +} + +int wil_info(struct wil6210_priv *wil, const char *fmt, ...) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = netdev_info(ndev, "%pV", &vaf); + trace_wil6210_log_info(&vaf); + va_end(args); + + return ret; +} + +int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + trace_wil6210_log_dbg(&vaf); + va_end(args); + + return 0; +} diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 727b1f53e6a..e8308ec3097 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) if (skb) { unsigned char printbuf[16 * 3 + 2]; int i = 0; - int len = skb_headlen(skb); + int len = le16_to_cpu(d->dma.length); void *p = skb->data; + if (len != skb_headlen(skb)) { + seq_printf(s, "!!! len: desc = %d skb = %d\n", + len, skb_headlen(skb)); + len = min_t(int, len, skb_headlen(skb)); + } + seq_printf(s, " len = %d\n", len); while (i < len) { diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index e3c1e7684f9..8205d3e4ab6 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -17,6 +17,7 @@ #include <linux/interrupt.h> #include "wil6210.h" +#include "trace.h" /** * Theory of operation: @@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) clear_bit(wil_status_irqen, &wil->status); } -static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +void wil6210_unmask_irq_tx(struct wil6210_priv *wil) { iowrite32(WIL6210_IMC_TX, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, IMC)); } -static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +void wil6210_unmask_irq_rx(struct wil6210_priv *wil) { iowrite32(WIL6210_IMC_RX, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + @@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_RX_ICR) + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_rx(isr); wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); if (!isr) { @@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { wil_dbg_irq(wil, "RX done\n"); isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; - wil_rx_handle(wil); + wil_dbg_txrx(wil, "NAPI schedule\n"); + napi_schedule(&wil->napi_rx); } if (isr) wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); - wil6210_unmask_irq_rx(wil); + /* Rx IRQ will be enabled when NAPI processing finished */ return IRQ_HANDLED; } @@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_tx(isr); wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); if (!isr) { @@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) wil6210_mask_irq_tx(wil); if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { - uint i; wil_dbg_irq(wil, "TX done\n"); + napi_schedule(&wil->napi_tx); isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; - for (i = 0; i < 24; i++) { - u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); - if (isr & mask) { - isr &= ~mask; - wil_dbg_irq(wil, "TX done(%i)\n", i); - wil_tx_complete(wil, i); - } - } + /* clear also all VRING interrupts */ + isr &= ~(BIT(25) - 1UL); } if (isr) wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); - wil6210_unmask_irq_tx(wil); + /* Tx IRQ will be enabled when NAPI processing finished */ return IRQ_HANDLED; } @@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_misc(isr); wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); if (!isr) { @@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) struct wil6210_priv *wil = cookie; u32 isr = wil->isr_misc; + trace_wil6210_irq_misc_thread(isr); wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); if (isr & ISR_MISC_FW_ERROR) { @@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) if (wil6210_debug_irq_mask(wil, pseudo_cause)) return IRQ_NONE; + trace_wil6210_irq_pseudo(pseudo_cause); wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); wil6210_mask_irq_pseudo(wil); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index a0478e2f686..0a2844c48a6 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) { uint i; struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; wil_dbg_misc(wil, "%s()\n", __func__); wil_link_off(wil); - clear_bit(wil_status_fwconnected, &wil->status); - - switch (wdev->sme_state) { - case CFG80211_SME_CONNECTED: - cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + if (test_bit(wil_status_fwconnected, &wil->status)) { + clear_bit(wil_status_fwconnected, &wil->status); + cfg80211_disconnected(ndev, + WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, 0, GFP_KERNEL); - break; - case CFG80211_SME_CONNECTING: + } else if (test_bit(wil_status_fwconnecting, &wil->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); - break; - default: - break; } - + clear_bit(wil_status_fwconnecting, &wil->status); for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) wil_vring_fini_tx(wil, i); @@ -292,41 +286,36 @@ static int __wil_up(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; - struct ieee80211_channel *channel = wdev->preset_chandef.chan; int rc; - int bi; - u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); rc = wil_reset(wil); if (rc) return rc; - /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ - wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + /* Rx VRING. After MAC and beacon */ + rc = wil_rx_init(wil); + if (rc) + return rc; + switch (wdev->iftype) { case NL80211_IFTYPE_STATION: wil_dbg_misc(wil, "type: STATION\n"); - bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_AP: wil_dbg_misc(wil, "type: AP\n"); - bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_CLIENT: wil_dbg_misc(wil, "type: P2P_CLIENT\n"); - bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_GO: wil_dbg_misc(wil, "type: P2P_GO\n"); - bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_MONITOR: wil_dbg_misc(wil, "type: Monitor\n"); - bi = 0; ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ break; @@ -334,36 +323,12 @@ static int __wil_up(struct wil6210_priv *wil) return -EOPNOTSUPP; } - /* Apply profile in the following order: */ - /* SSID and channel for the AP */ - switch (wdev->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - if (wdev->ssid_len == 0) { - wil_err(wil, "SSID not set\n"); - return -EINVAL; - } - rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); - if (rc) - return rc; - break; - default: - break; - } - /* MAC address - pre-requisite for other commands */ wmi_set_mac_address(wil, ndev->dev_addr); - /* Set up beaconing if required. */ - if (bi > 0) { - rc = wmi_pcp_start(wil, bi, wmi_nettype, - (channel ? channel->hw_value : 0)); - if (rc) - return rc; - } - /* Rx VRING. After MAC and beacon */ - wil_rx_init(wil); + napi_enable(&wil->napi_rx); + napi_enable(&wil->napi_tx); return 0; } @@ -381,6 +346,9 @@ int wil_up(struct wil6210_priv *wil) static int __wil_down(struct wil6210_priv *wil) { + napi_disable(&wil->napi_rx); + napi_disable(&wil->napi_tx); + if (wil->scan_request) { cfg80211_scan_done(wil->scan_request, true); wil->scan_request = NULL; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 098a8ec6b84..29dd1e58cb1 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; +static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) +{ + struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, + napi_rx); + int quota = budget; + int done; + + wil_rx_handle(wil, "a); + done = budget - quota; + + if (done <= 1) { /* burst ends - only one packet processed */ + napi_complete(napi); + wil6210_unmask_irq_rx(wil); + wil_dbg_txrx(wil, "NAPI RX complete\n"); + } + + wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done); + + return done; +} + +static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) +{ + struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, + napi_tx); + int tx_done = 0; + uint i; + + /* always process ALL Tx complete, regardless budget - it is fast */ + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + struct vring *vring = &wil->vring_tx[i]; + + if (!vring->va) + continue; + + tx_done += wil_tx_complete(wil, i); + } + + if (tx_done <= 1) { /* burst ends - only one packet processed */ + napi_complete(napi); + wil6210_unmask_irq_tx(wil); + wil_dbg_txrx(wil, "NAPI TX complete\n"); + } + + wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done); + + return min(tx_done, budget); +} + void *wil_if_alloc(struct device *dev, void __iomem *csr) { struct net_device *ndev; @@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; + netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx, + WIL6210_NAPI_BUDGET); + netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, + WIL6210_NAPI_BUDGET); + wil_link_off(wil); return wil; diff --git a/drivers/net/wireless/ath/wil6210/trace.c b/drivers/net/wireless/ath/wil6210/trace.c new file mode 100644 index 00000000000..cd2534b9c5a --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/trace.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h new file mode 100644 index 00000000000..eff1239be53 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/trace.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM wil6210 +#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define WIL6210_TRACE_H + +#include <linux/tracepoint.h> +#include "wil6210.h" +#include "txrx.h" + +/* create empty functions when tracing is disabled */ +#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__) + +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */ + +DECLARE_EVENT_CLASS(wil6210_wmi, + TP_PROTO(u16 id, void *buf, u16 buf_len), + + TP_ARGS(id, buf, buf_len), + + TP_STRUCT__entry( + __field(u16, id) + __field(u16, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->id = id; + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "id 0x%04x len %d", + __entry->id, __entry->buf_len + ) +); + +DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd, + TP_PROTO(u16 id, void *buf, u16 buf_len), + TP_ARGS(id, buf, buf_len) +); + +DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event, + TP_PROTO(u16 id, void *buf, u16 buf_len), + TP_ARGS(id, buf, buf_len) +); + +#define WIL6210_MSG_MAX (200) + +DECLARE_EVENT_CLASS(wil6210_log_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, WIL6210_MSG_MAX) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + WIL6210_MSG_MAX, + vaf->fmt, + *vaf->va) >= WIL6210_MSG_MAX); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(wil6210_log_event, wil6210_log_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(wil6210_log_event, wil6210_log_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \ + {BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \ + {BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \ + {BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" }) + +TRACE_EVENT(wil6210_irq_pseudo, + TP_PROTO(u32 x), + TP_ARGS(x), + TP_STRUCT__entry( + __field(u32, x) + ), + TP_fast_assign( + __entry->x = x; + ), + TP_printk("cause 0x%08x : %s", __entry->x, + wil_pseudo_irq_cause(__entry->x)) +); + +DECLARE_EVENT_CLASS(wil6210_irq, + TP_PROTO(u32 x), + TP_ARGS(x), + TP_STRUCT__entry( + __field(u32, x) + ), + TP_fast_assign( + __entry->x = x; + ), + TP_printk("cause 0x%08x", __entry->x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_rx, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_tx, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_misc, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +TRACE_EVENT(wil6210_rx, + TP_PROTO(u16 index, struct vring_rx_desc *d), + TP_ARGS(index, d), + TP_STRUCT__entry( + __field(u16, index) + __field(unsigned int, len) + __field(u8, mid) + __field(u8, cid) + __field(u8, tid) + __field(u8, type) + __field(u8, subtype) + __field(u16, seq) + __field(u8, mcs) + ), + TP_fast_assign( + __entry->index = index; + __entry->len = d->dma.length; + __entry->mid = wil_rxdesc_mid(d); + __entry->cid = wil_rxdesc_cid(d); + __entry->tid = wil_rxdesc_tid(d); + __entry->type = wil_rxdesc_ftype(d); + __entry->subtype = wil_rxdesc_subtype(d); + __entry->seq = wil_rxdesc_seq(d); + __entry->mcs = wil_rxdesc_mcs(d); + ), + TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x" + " type 0x%1x subtype 0x%1x", __entry->index, __entry->len, + __entry->mid, __entry->cid, __entry->tid, __entry->mcs, + __entry->seq, __entry->type, __entry->subtype) +); + +TRACE_EVENT(wil6210_tx, + TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags), + TP_ARGS(vring, index, len, frags), + TP_STRUCT__entry( + __field(u8, vring) + __field(u8, frags) + __field(u16, index) + __field(unsigned int, len) + ), + TP_fast_assign( + __entry->vring = vring; + __entry->frags = frags; + __entry->index = index; + __entry->len = len; + ), + TP_printk("vring %d index %d len %d frags %d", + __entry->vring, __entry->index, __entry->len, __entry->frags) +); + +TRACE_EVENT(wil6210_tx_done, + TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err), + TP_ARGS(vring, index, len, err), + TP_STRUCT__entry( + __field(u8, vring) + __field(u8, err) + __field(u16, index) + __field(unsigned int, len) + ), + TP_fast_assign( + __entry->vring = vring; + __entry->index = index; + __entry->len = len; + __entry->err = err; + ), + TP_printk("vring %d index %d len %d err 0x%02x", + __entry->vring, __entry->index, __entry->len, + __entry->err) +); + +#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/ + +#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) +/* we don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include <trace/define_trace.h> +#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */ diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 797024507c7..d240b24e1cc 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -22,6 +22,7 @@ #include "wil6210.h" #include "wmi.h" #include "txrx.h" +#include "trace.h" static bool rtap_include_phy_info; module_param(rtap_include_phy_info, bool, S_IRUGO); @@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) * we can use any */ for (i = 0; i < vring->size; i++) { - volatile struct vring_tx_desc *d = &(vring->va[i].tx); - d->dma.status = TX_DMA_STATUS_DU; + volatile struct vring_tx_desc *_d = &(vring->va[i].tx); + _d->dma.status = TX_DMA_STATUS_DU; } wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, @@ -106,30 +107,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, size_t sz = vring->size * sizeof(vring->va[0]); while (!wil_vring_is_empty(vring)) { + dma_addr_t pa; + struct sk_buff *skb; + u16 dmalen; + if (tx) { - volatile struct vring_tx_desc *d = + struct vring_tx_desc dd, *d = ⅆ + volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx; - dma_addr_t pa = d->dma.addr_low | - ((u64)d->dma.addr_high << 32); - struct sk_buff *skb = vring->ctx[vring->swtail]; + + *d = *_d; + pa = wil_desc_addr(&d->dma.addr); + dmalen = le16_to_cpu(d->dma.length); + skb = vring->ctx[vring->swtail]; if (skb) { - dma_unmap_single(dev, pa, d->dma.length, + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); dev_kfree_skb_any(skb); vring->ctx[vring->swtail] = NULL; } else { - dma_unmap_page(dev, pa, d->dma.length, + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } vring->swtail = wil_vring_next_tail(vring); } else { /* rx */ - volatile struct vring_rx_desc *d = + struct vring_rx_desc dd, *d = ⅆ + volatile struct vring_rx_desc *_d = &vring->va[vring->swtail].rx; - dma_addr_t pa = d->dma.addr_low | - ((u64)d->dma.addr_high << 32); - struct sk_buff *skb = vring->ctx[vring->swhead]; - dma_unmap_single(dev, pa, d->dma.length, - DMA_FROM_DEVICE); + + *d = *_d; + pa = wil_desc_addr(&d->dma.addr); + dmalen = le16_to_cpu(d->dma.length); + skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); kfree_skb(skb); wil_vring_advance_head(vring, 1); } @@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, { struct device *dev = wil_to_dev(wil); unsigned int sz = RX_BUF_LEN; - volatile struct vring_rx_desc *d = &(vring->va[i].rx); + struct vring_rx_desc dd, *d = ⅆ + volatile struct vring_rx_desc *_d = &(vring->va[i].rx); dma_addr_t pa; /* TODO align */ @@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, } d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; - d->dma.addr_low = lower_32_bits(pa); - d->dma.addr_high = (u16)upper_32_bits(pa); + wil_desc_addr_set(&d->dma.addr, pa); /* ip_length don't care */ /* b11 don't care */ /* error don't care */ d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ - d->dma.length = sz; + d->dma.length = cpu_to_le16(sz); + *_d = *d; vring->ctx[i] = skb; return 0; @@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, { struct device *dev = wil_to_dev(wil); struct net_device *ndev = wil_to_ndev(wil); - volatile struct vring_rx_desc *d; - struct vring_rx_desc *d1; + volatile struct vring_rx_desc *_d; + struct vring_rx_desc *d; struct sk_buff *skb; dma_addr_t pa; unsigned int sz = RX_BUF_LEN; + u16 dmalen; u8 ftype; u8 ds_bits; @@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, if (wil_vring_is_empty(vring)) return NULL; - d = &(vring->va[vring->swhead].rx); - if (!(d->dma.status & RX_DMA_STATUS_DU)) { + _d = &(vring->va[vring->swhead].rx); + if (!(_d->dma.status & RX_DMA_STATUS_DU)) { /* it is not error, we just reached end of Rx done area */ return NULL; } - pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); skb = vring->ctx[vring->swhead]; + d = wil_skb_rxdesc(skb); + *d = *_d; + pa = wil_desc_addr(&d->dma.addr); + vring->ctx[vring->swhead] = NULL; + wil_vring_advance_head(vring, 1); + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); - skb_trim(skb, d->dma.length); + dmalen = le16_to_cpu(d->dma.length); + + trace_wil6210_rx(vring->swhead, d); + wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + if (dmalen > sz) { + wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + kfree_skb(skb); + return NULL; + } + skb_trim(skb, dmalen); + + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); - d1 = wil_skb_rxdesc(skb); - *d1 = *d; - wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1); + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); /* use radiotap header only if required */ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) wil_rx_add_radiotap_header(wil, skb); - wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); - wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, - (const void *)d, sizeof(*d), false); - - wil_vring_advance_head(vring, 1); - /* no extra checks if in sniffer mode */ if (ndev->type != ARPHRD_ETHER) return skb; @@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, * Driver should recognize it by frame type, that is found * in Rx descriptor. If type is not data, it is 802.11 frame as is */ - ftype = wil_rxdesc_ftype(d1) << 2; + ftype = wil_rxdesc_ftype(d) << 2; if (ftype != IEEE80211_FTYPE_DATA) { wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); /* TODO: process it */ @@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, return NULL; } - ds_bits = wil_rxdesc_ds_bits(d1); + ds_bits = wil_rxdesc_ds_bits(d); if (ds_bits == 1) { /* * HW bug - in ToDS mode, i.e. Rx on AP side, @@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) /* * Pass Rx packet to the netif. Update statistics. + * Called in softirq context (NAPI poll). */ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) { @@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) skb_orphan(skb); - if (in_interrupt()) - rc = netif_rx(skb); - else - rc = netif_rx_ni(skb); + rc = netif_receive_skb(skb); if (likely(rc == NET_RX_SUCCESS)) { ndev->stats.rx_packets++; @@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) /** * Proceed all completed skb's from Rx VRING * - * Safe to call from IRQ + * Safe to call from NAPI poll, i.e. softirq with interrupts enabled */ -void wil_rx_handle(struct wil6210_priv *wil) +void wil_rx_handle(struct wil6210_priv *wil, int *quota) { struct net_device *ndev = wil_to_ndev(wil); struct vring *v = &wil->vring_rx; @@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil) return; } wil_dbg_txrx(wil, "%s()\n", __func__); - while (NULL != (skb = wil_vring_reap_rx(wil, v))) { - wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, - skb->data, skb_headlen(skb), false); + while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) { + (*quota)--; if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { skb->dev = ndev; @@ -600,18 +621,17 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, return NULL; } -static int wil_tx_desc_map(volatile struct vring_tx_desc *d, - dma_addr_t pa, u32 len) +static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, + int vring_index) { - d->dma.addr_low = lower_32_bits(pa); - d->dma.addr_high = (u16)upper_32_bits(pa); + wil_desc_addr_set(&d->dma.addr, pa); d->dma.ip_length = 0; /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ d->dma.b11 = 0/*14 | BIT(7)*/; d->dma.error = 0; d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ - d->dma.length = len; - d->dma.d0 = 0; + d->dma.length = cpu_to_le16((u16)len); + d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS); d->mac.d[0] = 0; d->mac.d[1] = 0; d->mac.d[2] = 0; @@ -630,7 +650,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, struct sk_buff *skb) { struct device *dev = wil_to_dev(wil); - volatile struct vring_tx_desc *d; + struct vring_tx_desc dd, *d = ⅆ + volatile struct vring_tx_desc *_d; u32 swhead = vring->swhead; int avail = wil_vring_avail_tx(vring); int nr_frags = skb_shinfo(skb)->nr_frags; @@ -648,7 +669,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, 1 + nr_frags); return -ENOMEM; } - d = &(vring->va[i].tx); + _d = &(vring->va[i].tx); /* FIXME FW can accept only unicast frames for the peer */ memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); @@ -664,28 +685,32 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, if (unlikely(dma_mapping_error(dev, pa))) return -EINVAL; /* 1-st segment */ - wil_tx_desc_map(d, pa, skb_headlen(skb)); + wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index); d->mac.d[2] |= ((nr_frags + 1) << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + if (nr_frags) + *_d = *d; + /* middle segments */ for (f = 0; f < nr_frags; f++) { const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[f]; int len = skb_frag_size(frag); i = (swhead + f + 1) % vring->size; - d = &(vring->va[i].tx); + _d = &(vring->va[i].tx); pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev, pa))) goto dma_error; - wil_tx_desc_map(d, pa, len); + wil_tx_desc_map(d, pa, len, vring_index); vring->ctx[i] = NULL; + *_d = *d; } /* for the last seg only */ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); - d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); - d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + *_d = *d; wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); @@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* advance swhead */ wil_vring_advance_head(vring, nr_frags + 1); wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); /* hold reference to skb * to prevent skb release before accounting @@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* unmap what we have mapped */ /* Note: increment @f to operate with positive index */ for (f++; f > 0; f--) { + u16 dmalen; + i = (swhead + f) % vring->size; - d = &(vring->va[i].tx); - d->dma.status = TX_DMA_STATUS_DU; - pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + _d = &(vring->va[i].tx); + *d = *_d; + _d->dma.status = TX_DMA_STATUS_DU; + pa = wil_desc_addr(&d->dma.addr); + dmalen = le16_to_cpu(d->dma.length); if (vring->ctx[i]) - dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); else - dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } return -EINVAL; @@ -738,18 +768,16 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) wil_err(wil, "Xmit in monitor mode not supported\n"); goto drop; } - if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { - rc = wmi_tx_eapol(wil, skb); - } else { - /* find vring */ - vring = wil_find_tx_vring(wil, skb); - if (!vring) { - wil_err(wil, "No Tx VRING available\n"); - goto drop; - } - /* set up vring entry */ - rc = wil_tx_vring(wil, vring, skb); + + /* find vring */ + vring = wil_find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; } + /* set up vring entry */ + rc = wil_tx_vring(wil, vring, skb); + switch (rc) { case 0: /* statistics will be updated on the tx_complete */ @@ -761,7 +789,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) break; /* goto drop; */ } drop: - netif_tx_stop_all_queues(ndev); ndev->stats.tx_dropped++; dev_kfree_skb_any(skb); @@ -771,41 +798,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /** * Clean up transmitted skb's from the Tx VRING * + * Return number of descriptors cleared + * * Safe to call from IRQ */ -void wil_tx_complete(struct wil6210_priv *wil, int ringid) +int wil_tx_complete(struct wil6210_priv *wil, int ringid) { struct net_device *ndev = wil_to_ndev(wil); struct device *dev = wil_to_dev(wil); struct vring *vring = &wil->vring_tx[ringid]; + int done = 0; if (!vring->va) { wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); - return; + return 0; } wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); while (!wil_vring_is_empty(vring)) { - volatile struct vring_tx_desc *d1 = + volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx; struct vring_tx_desc dd, *d = ⅆ dma_addr_t pa; struct sk_buff *skb; + u16 dmalen; - dd = *d1; + *d = *_d; if (!(d->dma.status & TX_DMA_STATUS_DU)) break; + dmalen = le16_to_cpu(d->dma.length); + trace_wil6210_tx_done(ringid, vring->swtail, dmalen, + d->dma.error); wil_dbg_txrx(wil, "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", - vring->swtail, d->dma.length, d->dma.status, + vring->swtail, dmalen, d->dma.status, d->dma.error); wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); - pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + pa = wil_desc_addr(&d->dma.addr); skb = vring->ctx[vring->swtail]; if (skb) { if (d->dma.error == 0) { @@ -815,18 +849,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) ndev->stats.tx_errors++; } - dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); dev_kfree_skb_any(skb); vring->ctx[vring->swtail] = NULL; } else { - dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } - d->dma.addr_low = 0; - d->dma.addr_high = 0; + d->dma.addr.addr_low = 0; + d->dma.addr.addr_high = 0; d->dma.length = 0; d->dma.status = TX_DMA_STATUS_DU; vring->swtail = wil_vring_next_tail(vring); + done++; } if (wil_vring_avail_tx(vring) > vring->size/4) netif_tx_wake_all_queues(wil_to_ndev(wil)); + + return done; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index adef12fb2ae..859aea68a1f 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -27,6 +27,28 @@ #define WIL6210_RTAP_SIZE (128) /* Tx/Rx path */ + +/* + * Common representation of physical address in Vring + */ +struct vring_dma_addr { + __le32 addr_low; + __le16 addr_high; +} __packed; + +static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr) +{ + return le32_to_cpu(addr->addr_low) | + ((u64)le16_to_cpu(addr->addr_high) << 32); +} + +static inline void wil_desc_addr_set(struct vring_dma_addr *addr, + dma_addr_t pa) +{ + addr->addr_low = cpu_to_le32(lower_32_bits(pa)); + addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa)); +} + /* * Tx descriptor - MAC part * [dword 0] @@ -179,6 +201,10 @@ struct vring_tx_mac { #define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 #define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 +#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS 9 +#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_MSK 0x200 + #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 @@ -216,13 +242,12 @@ struct vring_tx_mac { struct vring_tx_dma { u32 d0; - u32 addr_low; - u16 addr_high; + struct vring_dma_addr addr; u8 ip_length; u8 b11; /* 0..6: mac_length; 7:ip_version */ u8 error; /* 0..2: err; 3..7: reserved; */ u8 status; /* 0: used; 1..7; reserved */ - u16 length; + __le16 length; } __packed; /* @@ -315,13 +340,12 @@ struct vring_rx_mac { struct vring_rx_dma { u32 d0; - u32 addr_low; - u16 addr_high; + struct vring_dma_addr addr; u8 ip_length; u8 b11; u8 error; u8 status; - u16 length; + __le16 length; } __packed; struct vring_tx_desc { diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 8f76ecd8a7e..44fdab51de7 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MEM_SIZE (2*1024*1024UL) -#define WIL6210_RX_RING_SIZE (128) -#define WIL6210_TX_RING_SIZE (128) -#define WIL6210_MAX_TX_RINGS (24) +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) /* HW limit */ +#define WIL6210_MAX_CID (8) /* HW limit */ +#define WIL6210_NAPI_BUDGET (16) /* arbitrary */ /* Hardware definitions begin */ @@ -184,6 +186,7 @@ struct vring { enum { /* for wil6210_priv.status */ wil_status_fwready = 0, + wil_status_fwconnecting, wil_status_fwconnected, wil_status_dontscan, wil_status_reset_done, @@ -239,6 +242,8 @@ struct wil6210_priv { * - consumed in thread by wmi_event_worker */ spinlock_t wmi_ev_lock; + struct napi_struct napi_rx; + struct napi_struct napi_tx; /* DMA related */ struct vring vring_rx; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; @@ -267,9 +272,13 @@ struct wil6210_priv { #define wil_to_ndev(i) (wil_to_wdev(i)->netdev) #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) -#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) -#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) -#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) +int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...); +int wil_err(struct wil6210_priv *wil, const char *fmt, ...); +int wil_info(struct wil6210_priv *wil, const char *fmt, ...); +#define wil_dbg(wil, fmt, arg...) do { \ + netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \ + wil_dbg_trace(wil, fmt, ##arg); \ +} while (0) #define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) #define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) @@ -320,7 +329,6 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); int wmi_set_channel(struct wil6210_priv *wil, int channel); int wmi_get_channel(struct wil6210_priv *wil, int *channel); -int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, const void *mac_addr); int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, @@ -356,10 +364,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, void wil_vring_fini_tx(struct wil6210_priv *wil, int id); netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); -void wil_tx_complete(struct wil6210_priv *wil, int ringid); +int wil_tx_complete(struct wil6210_priv *wil, int ringid); +void wil6210_unmask_irq_tx(struct wil6210_priv *wil); /* RX API */ -void wil_rx_handle(struct wil6210_priv *wil); +void wil_rx_handle(struct wil6210_priv *wil, int *quota); +void wil6210_unmask_irq_rx(struct wil6210_priv *wil); int wil_iftype_nl2wmi(enum nl80211_iftype type); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 45b04e383f9..dc8059ad4ba 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -20,6 +20,7 @@ #include "wil6210.h" #include "txrx.h" #include "wmi.h" +#include "trace.h" /** * WMI event receiving - theory of operations @@ -74,10 +75,11 @@ static const struct { {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ {0x880000, 0x88a000, 0x880000}, /* various RGF */ - {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ + {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */ /* * 920000..930000 ucode code RAM * 930000..932000 ucode data RAM + * 932000..949000 back-door debug data */ }; @@ -246,6 +248,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) iowrite32(r->head = next_head, wil->csr + HOST_MBOX + offsetof(struct wil6210_mbox_ctl, tx.head)); + trace_wil6210_wmi_cmd(cmdid, buf, len); + /* interrupt to FW */ iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); @@ -311,8 +315,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n", data->info.channel, data->info.mcs, data->info.snr); - wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, - le16_to_cpu(data->info.stype)); + wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len, + le16_to_cpu(fc)); wil_dbg_wmi(wil, "qid %d mid %d cid %d\n", data->info.qid, data->info.mid, data->info.cid); @@ -406,7 +410,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { - if (wdev->sme_state != CFG80211_SME_CONNECTING) { + if (!test_bit(wil_status_fwconnecting, &wil->status)) { wil_err(wil, "Not in connecting state\n"); return; } @@ -430,6 +434,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); } + clear_bit(wil_status_fwconnecting, &wil->status); set_bit(wil_status_fwconnected, &wil->status); /* FIXME FW can transmit only ucast frames to peer */ @@ -635,8 +640,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil) hdr.flags); if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { - wil_dbg_wmi(wil, "WMI event 0x%04x\n", - evt->event.wmi.id); + u16 id = le16_to_cpu(evt->event.wmi.id); + wil_dbg_wmi(wil, "WMI event 0x%04x\n", id); + trace_wil6210_wmi_event(id, &evt->event.wmi, len); } wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1, &evt->event.hdr, sizeof(hdr) + len, true); @@ -724,7 +730,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) .bcon_interval = cpu_to_le16(bi), .network_type = wmi_nettype, .disable_sec_offload = 1, - .channel = chan, + .channel = chan - 1, }; struct { struct wil6210_mbox_hdr_wmi wmi; @@ -734,8 +740,12 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) if (!wil->secure_pcp) cmd.disable_sec = 1; + /* + * Processing time may be huge, in case of secure AP it takes about + * 3500ms for FW to start AP + */ rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd), - WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 100); + WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000); if (rc) return rc; @@ -829,40 +839,6 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel) return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd)); } -int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) -{ - struct wmi_eapol_tx_cmd *cmd; - struct ethhdr *eth; - u16 eapol_len = skb->len - ETH_HLEN; - void *eapol = skb->data + ETH_HLEN; - uint i; - int rc; - - skb_set_mac_header(skb, 0); - eth = eth_hdr(skb); - wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); - for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { - if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) - goto found_dest; - } - - return -EINVAL; - - found_dest: - /* find out eapol data & len */ - cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); - if (!cmd) - return -EINVAL; - - memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); - cmd->eapol_len = cpu_to_le16(eapol_len); - memcpy(cmd->eapol, eapol, eapol_len); - rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); - kfree(cmd); - - return rc; -} - int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, const void *mac_addr) { diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 078e6f3477a..51ff0b198d0 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -28,18 +28,12 @@ config B43 config B43_BCMA bool "Support for BCMA bus" - depends on B43 && BCMA - default y - -config B43_BCMA_EXTRA - bool "Hardware support that overlaps with the brcmsmac driver" - depends on B43_BCMA - default n if BRCMSMAC + depends on B43 && (BCMA = y || BCMA = B43) default y config B43_SSB bool - depends on B43 && SSB + depends on B43 && (SSB = y || SSB = B43) default y # Auto-select SSB PCI-HOST support, if possible @@ -111,6 +105,7 @@ config B43_PIO config B43_PHY_N bool "Support for 802.11n (N-PHY) devices" depends on B43 + default y ---help--- Support for the N-PHY. @@ -132,6 +127,7 @@ config B43_PHY_LP config B43_PHY_HT bool "Support for HT-PHY (high throughput) devices" depends on B43 && B43_BCMA + default y ---help--- Support for the HT-PHY. diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index a95b77ab360..0e933bb7154 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -113,13 +113,15 @@ static int b43_modparam_pio = 0; module_param_named(pio, b43_modparam_pio, int, 0644); MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO"); +static int modparam_allhwsupport = !IS_ENABLED(CONFIG_BRCMSMAC); +module_param_named(allhwsupport, modparam_allhwsupport, int, 0444); +MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the brcmsmac driver)"); + #ifdef CONFIG_B43_BCMA static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), -#ifdef CONFIG_B43_BCMA_EXTRA BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), -#endif BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS), BCMA_CORETABLE_END }; @@ -5396,6 +5398,12 @@ static int b43_bcma_probe(struct bcma_device *core) struct b43_wl *wl; int err; + if (!modparam_allhwsupport && + (core->id.rev == 0x17 || core->id.rev == 0x18)) { + pr_err("Support for cores revisions 0x17 and 0x18 disabled by module param allhwsupport=0. Try b43.allhwsupport=1\n"); + return -ENOTSUPP; + } + dev = b43_bus_dev_bcma_init(core); if (!dev) return -ENODEV; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 4891e3df205..e3f3c48f86d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -22,9 +22,11 @@ #include <linux/pci_ids.h> #include <linux/sched.h> #include <linux/completion.h> +#include <linux/scatterlist.h> #include <linux/mmc/sdio.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> +#include <linux/mmc/host.h> #include <linux/platform_data/brcmfmac-sdio.h> #include <defs.h> @@ -160,7 +162,7 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) return 0; } -int +static int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) { int err = 0, i; @@ -191,12 +193,33 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) return err; } +static int +brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) +{ + uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; + int err = 0; + + if (bar0 != sdiodev->sbwad) { + err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); + if (err) + return err; + + sdiodev->sbwad = bar0; + } + + *addr &= SBSDIO_SB_OFT_ADDR_MASK; + + if (width == 4) + *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + return 0; +} + int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, void *data, bool write) { u8 func_num, reg_size; - u32 bar; s32 retry = 0; int ret; @@ -216,18 +239,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, func_num = SDIO_FUNC_1; reg_size = 4; - /* Set the window for SB core register */ - bar = addr & ~SBSDIO_SB_OFT_ADDR_MASK; - if (bar != sdiodev->sbwad) { - ret = brcmf_sdcard_set_sbaddr_window(sdiodev, bar); - if (ret != 0) { - memset(data, 0xFF, reg_size); - return ret; - } - sdiodev->sbwad = bar; - } - addr &= SBSDIO_SB_OFT_ADDR_MASK; - addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + brcmf_sdio_addrprep(sdiodev, reg_size, &addr); } do { @@ -303,30 +315,207 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, *ret = retval; } -static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn, - uint flags, uint width, u32 *addr) +/** + * brcmf_sdio_buffrw - SDIO interface function for block data access + * @sdiodev: brcmfmac sdio device + * @fn: SDIO function number + * @write: direction flag + * @addr: dongle memory address as source/destination + * @pkt: skb pointer + * + * This function takes the respbonsibility as the interface function to MMC + * stack for block data access. It assumes that the skb passed down by the + * caller has already been padded and aligned. + */ +static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, struct sk_buff_head *pktlist) { - uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; - int err = 0; + unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; + unsigned int max_blks, max_req_sz, orig_offset, dst_offset; + unsigned short max_seg_sz, seg_sz; + unsigned char *pkt_data, *orig_data, *dst_data; + struct sk_buff *pkt_next = NULL, *local_pkt_next; + struct sk_buff_head local_list, *target_list; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + struct sg_table st; + struct scatterlist *sgl; + struct mmc_host *host; + int ret = 0; - /* Async not implemented yet */ - if (flags & SDIO_REQ_ASYNC) - return -ENOTSUPP; + if (!pktlist->qlen) + return -EINVAL; - if (bar0 != sdiodev->sbwad) { - err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); - if (err) - return err; + brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_pm_resume_error(sdiodev)) + return -EIO; - sdiodev->sbwad = bar0; + /* Single skb use the standard mmc interface */ + if (pktlist->qlen == 1) { + pkt_next = pktlist->next; + req_sz = pkt_next->len + 3; + req_sz &= (uint)~3; + + if (write) + return sdio_memcpy_toio(sdiodev->func[fn], addr, + ((u8 *)(pkt_next->data)), + req_sz); + else if (fn == 1) + return sdio_memcpy_fromio(sdiodev->func[fn], + ((u8 *)(pkt_next->data)), + addr, req_sz); + else + /* function 2 read is FIFO operation */ + return sdio_readsb(sdiodev->func[fn], + ((u8 *)(pkt_next->data)), addr, + req_sz); } - *addr &= SBSDIO_SB_OFT_ADDR_MASK; + target_list = pktlist; + /* for host with broken sg support, prepare a page aligned list */ + __skb_queue_head_init(&local_list); + if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + req_sz = 0; + skb_queue_walk(pktlist, pkt_next) + req_sz += pkt_next->len; + req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize); + while (req_sz > PAGE_SIZE) { + pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE); + if (pkt_next == NULL) { + ret = -ENOMEM; + goto exit; + } + __skb_queue_tail(&local_list, pkt_next); + req_sz -= PAGE_SIZE; + } + pkt_next = brcmu_pkt_buf_get_skb(req_sz); + if (pkt_next == NULL) { + ret = -ENOMEM; + goto exit; + } + __skb_queue_tail(&local_list, pkt_next); + target_list = &local_list; + } - if (width == 4) - *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + host = sdiodev->func[fn]->card->host; + func_blk_sz = sdiodev->func[fn]->cur_blksize; + /* Blocks per command is limited by host count, host transfer + * size and the maximum for IO_RW_EXTENDED of 511 blocks. + */ + max_blks = min_t(unsigned int, host->max_blk_count, 511u); + max_req_sz = min_t(unsigned int, host->max_req_size, + max_blks * func_blk_sz); + max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC); + max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen); + seg_sz = target_list->qlen; + pkt_offset = 0; + pkt_next = target_list->next; + + if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) { + ret = -ENOMEM; + goto exit; + } - return 0; + while (seg_sz) { + req_sz = 0; + sg_cnt = 0; + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + sgl = st.sgl; + /* prep sg table */ + while (pkt_next != (struct sk_buff *)target_list) { + pkt_data = pkt_next->data + pkt_offset; + sg_data_sz = pkt_next->len - pkt_offset; + if (sg_data_sz > host->max_seg_size) + sg_data_sz = host->max_seg_size; + if (sg_data_sz > max_req_sz - req_sz) + sg_data_sz = max_req_sz - req_sz; + + sg_set_buf(sgl, pkt_data, sg_data_sz); + + sg_cnt++; + sgl = sg_next(sgl); + req_sz += sg_data_sz; + pkt_offset += sg_data_sz; + if (pkt_offset == pkt_next->len) { + pkt_offset = 0; + pkt_next = pkt_next->next; + } + + if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz) + break; + } + seg_sz -= sg_cnt; + + if (req_sz % func_blk_sz != 0) { + brcmf_err("sg request length %u is not %u aligned\n", + req_sz, func_blk_sz); + ret = -ENOTBLK; + goto exit; + } + mmc_dat.sg = st.sgl; + mmc_dat.sg_len = sg_cnt; + mmc_dat.blksz = func_blk_sz; + mmc_dat.blocks = req_sz / func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1<<27; /* block mode */ + /* incrementing addr for function 1 */ + mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ + mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + if (fn == 1) + addr += req_sz; + + mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card); + mmc_wait_for_req(host, &mmc_req); + + ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; + if (ret != 0) { + brcmf_err("CMD53 sg block %s failed %d\n", + write ? "write" : "read", ret); + ret = -EIO; + break; + } + } + + if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + local_pkt_next = local_list.next; + orig_offset = 0; + skb_queue_walk(pktlist, pkt_next) { + dst_offset = 0; + do { + req_sz = local_pkt_next->len - orig_offset; + req_sz = min_t(uint, pkt_next->len - dst_offset, + req_sz); + orig_data = local_pkt_next->data + orig_offset; + dst_data = pkt_next->data + dst_offset; + memcpy(dst_data, orig_data, req_sz); + orig_offset += req_sz; + dst_offset += req_sz; + if (orig_offset == local_pkt_next->len) { + orig_offset = 0; + local_pkt_next = local_pkt_next->next; + } + if (dst_offset == pkt_next->len) + break; + } while (!skb_queue_empty(&local_list)); + } + } + +exit: + sg_free_table(&st); + while ((pkt_next = __skb_dequeue(&local_list)) != NULL) + brcmu_pkt_buf_free_skb(pkt_next); + + return ret; } int @@ -355,21 +544,22 @@ int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff *pkt) { - uint incr_fix; uint width; int err = 0; + struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); + err = brcmf_sdio_addrprep(sdiodev, width, &addr); if (err) goto done; - incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; - err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ, - fn, addr, pkt); + skb_queue_head_init(&pkt_list); + skb_queue_tail(&pkt_list, pkt); + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list); + skb_dequeue_tail(&pkt_list); done: return err; @@ -386,13 +576,12 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); + err = brcmf_sdio_addrprep(sdiodev, width, &addr); if (err) goto done; incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; - err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr, - pktq); + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq); done: return err; @@ -424,37 +613,21 @@ int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff *pkt) { - uint incr_fix; uint width; - uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; + struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); - /* Async not implemented yet */ - if (flags & SDIO_REQ_ASYNC) - return -ENOTSUPP; - - if (bar0 != sdiodev->sbwad) { - err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); - if (err) - goto done; - - sdiodev->sbwad = bar0; - } - - addr &= SBSDIO_SB_OFT_ADDR_MASK; - - incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - if (width == 4) - addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + brcmf_sdio_addrprep(sdiodev, width, &addr); - err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, - addr, pkt); + skb_queue_head_init(&pkt_list); + skb_queue_tail(&pkt_list, pkt); + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list); + skb_dequeue_tail(&pkt_list); -done: return err; } @@ -466,6 +639,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, struct sk_buff *pkt; u32 sdaddr; uint dsize; + struct sk_buff_head pkt_list; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); pkt = dev_alloc_skb(dsize); @@ -474,6 +648,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, return -EIO; } pkt->priority = 0; + skb_queue_head_init(&pkt_list); /* Determine initial transfer parameters */ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; @@ -501,9 +676,10 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, skb_put(pkt, dsize); if (write) memcpy(pkt->data, data, dsize); - bcmerror = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, - write, SDIO_FUNC_1, - sdaddr, pkt); + skb_queue_tail(&pkt_list, pkt); + bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write, + sdaddr, &pkt_list); + skb_dequeue_tail(&pkt_list); if (bcmerror) { brcmf_err("membytes transfer failed\n"); break; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 44fa0cdbf97..289e386f01f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; -static bool +bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) { bool is_err = false; @@ -76,7 +76,7 @@ brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) return is_err; } -static void +void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq) { #ifdef CONFIG_PM_SLEEP @@ -211,115 +211,6 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, return err_ret; } -/* precondition: host controller is claimed */ -static int -brcmf_sdioh_request_data(struct brcmf_sdio_dev *sdiodev, uint write, bool fifo, - uint func, uint addr, struct sk_buff *pkt, uint pktlen) -{ - int err_ret = 0; - - if ((write) && (!fifo)) { - err_ret = sdio_memcpy_toio(sdiodev->func[func], addr, - ((u8 *) (pkt->data)), pktlen); - } else if (write) { - err_ret = sdio_memcpy_toio(sdiodev->func[func], addr, - ((u8 *) (pkt->data)), pktlen); - } else if (fifo) { - err_ret = sdio_readsb(sdiodev->func[func], - ((u8 *) (pkt->data)), addr, pktlen); - } else { - err_ret = sdio_memcpy_fromio(sdiodev->func[func], - ((u8 *) (pkt->data)), - addr, pktlen); - } - - return err_ret; -} - -/* - * This function takes a queue of packets. The packets on the queue - * are assumed to be properly aligned by the caller. - */ -int -brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, - uint write, uint func, uint addr, - struct sk_buff_head *pktq) -{ - bool fifo = (fix_inc == SDIOH_DATA_FIX); - u32 SGCount = 0; - int err_ret = 0; - - struct sk_buff *pkt; - - brcmf_dbg(SDIO, "Enter\n"); - - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait); - if (brcmf_pm_resume_error(sdiodev)) - return -EIO; - - skb_queue_walk(pktq, pkt) { - uint pkt_len = pkt->len; - pkt_len += 3; - pkt_len &= 0xFFFFFFFC; - - err_ret = brcmf_sdioh_request_data(sdiodev, write, fifo, func, - addr, pkt, pkt_len); - if (err_ret) { - brcmf_err("%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", - write ? "TX" : "RX", pkt, SGCount, addr, - pkt_len, err_ret); - } else { - brcmf_dbg(SDIO, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n", - write ? "TX" : "RX", pkt, SGCount, addr, - pkt_len); - } - if (!fifo) - addr += pkt_len; - - SGCount++; - } - - brcmf_dbg(SDIO, "Exit\n"); - return err_ret; -} - -/* - * This function takes a single DMA-able packet. - */ -int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, - uint fix_inc, uint write, uint func, uint addr, - struct sk_buff *pkt) -{ - int status; - uint pkt_len; - bool fifo = (fix_inc == SDIOH_DATA_FIX); - - brcmf_dbg(SDIO, "Enter\n"); - - if (pkt == NULL) - return -EINVAL; - pkt_len = pkt->len; - - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_pm_resume_error(sdiodev)) - return -EIO; - - pkt_len += 3; - pkt_len &= (uint)~3; - - status = brcmf_sdioh_request_data(sdiodev, write, fifo, func, - addr, pkt, pkt_len); - if (status) { - brcmf_err("%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", - write ? "TX" : "RX", pkt, addr, pkt_len, status); - } else { - brcmf_dbg(SDIO, "%s xfr'd %p, addr=0x%05x, len=%d\n", - write ? "TX" : "RX", pkt, addr, pkt_len); - } - - return status; -} - static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr) { /* read 24 bits and return valid 17 bit addr */ @@ -468,7 +359,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->request_byte_wait); init_waitqueue_head(&sdiodev->request_word_wait); - init_waitqueue_head(&sdiodev->request_chain_wait); init_waitqueue_head(&sdiodev->request_buffer_wait); brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n"); @@ -606,7 +496,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev) static struct platform_driver brcmf_sdio_pd = { .remove = brcmf_sdio_pd_remove, .driver = { - .name = BRCMFMAC_SDIO_PDATA_NAME + .name = BRCMFMAC_SDIO_PDATA_NAME, + .owner = THIS_MODULE, } }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 28db9cf3967..86cbfe2c7c6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -583,6 +583,7 @@ enum brcmf_netif_stop_reason { * @bssidx: index of bss associated with this interface. * @mac_addr: assigned mac address. * @netif_stop: bitmap indicates reason why netif queues are stopped. + * @netif_stop_lock: spinlock for update netif_stop from multiple sources. * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. * @pend_8021x_wait: used for signalling change in count. */ @@ -598,6 +599,7 @@ struct brcmf_if { s32 bssidx; u8 mac_addr[ETH_ALEN]; u8 netif_stop; + spinlock_t netif_stop_lock; atomic_t pend_8021x_cnt; wait_queue_head_t pend_8021x_wait; }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index 59c77aa3b95..dd85401063c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -30,6 +30,7 @@ #include "dhd_bus.h" #include "fwsignal.h" #include "dhd_dbg.h" +#include "tracepoint.h" struct brcmf_proto_cdc_dcmd { __le32 cmd; /* dongle command value */ @@ -292,6 +293,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, h->flags2 = 0; h->data_offset = offset; BDC_SET_IF_IDX(h, ifidx); + trace_brcmf_bdchdr(pktbuf->data); } int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, @@ -309,6 +311,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, return -EBADE; } + trace_brcmf_bdchdr(pktbuf->data); h = (struct brcmf_proto_bdc_header *)(pktbuf->data); *ifidx = BDC_GET_IF_IDX(h); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 202869cd093..c37b9d68e45 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -156,8 +156,11 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, "txs_suppr_core: %u\n" "txs_suppr_ps: %u\n" "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", fwstats->header_pulls, fwstats->header_only_pkt, fwstats->tlv_parse_failed, @@ -176,14 +179,17 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, fwstats->txs_supp_core, fwstats->txs_supp_ps, fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, fwstats->send_pkts[0], fwstats->send_pkts[1], fwstats->send_pkts[2], fwstats->send_pkts[3], fwstats->send_pkts[4], - fwstats->fifo_credits_sent[0], - fwstats->fifo_credits_sent[1], - fwstats->fifo_credits_sent[2], - fwstats->fifo_credits_sent[3], - fwstats->fifo_credits_sent[4]); + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); return simple_read_from_buffer(data, count, ppos, buf, res); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 009c87bfd9a..0af1f5dc583 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -141,8 +141,7 @@ struct brcmf_fws_stats { u32 header_pulls; u32 pkt2bus; u32 send_pkts[5]; - u32 fifo_credits_sent[5]; - u32 fifo_credits_back[6]; + u32 requested_sent[5]; u32 generic_error; u32 mac_update_failed; u32 mac_ps_update_failed; @@ -158,6 +157,9 @@ struct brcmf_fws_stats { u32 txs_supp_core; u32 txs_supp_ps; u32 txs_tossed; + u32 txs_host_tossed; + u32 bus_flow_block; + u32 fws_flow_block; }; struct brcmf_pub; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 2c593570497..8e8975562ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -179,7 +179,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct brcmf_pub *drvr = ifp->drvr; struct ethhdr *eh; - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); /* Can the device send data? */ if (drvr->bus_if->state != BRCMF_BUS_DATA) { @@ -240,11 +240,15 @@ done: void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state) { + unsigned long flags; + if (!ifp) return; brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", ifp->bssidx, ifp->netif_stop, reason, state); + + spin_lock_irqsave(&ifp->netif_stop_lock, flags); if (state) { if (!ifp->netif_stop) netif_stop_queue(ifp->ndev); @@ -254,6 +258,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, if (!ifp->netif_stop) netif_wake_queue(ifp->ndev); } + spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); } void brcmf_txflowblock(struct device *dev, bool state) @@ -264,15 +269,18 @@ void brcmf_txflowblock(struct device *dev, bool state) brcmf_dbg(TRACE, "Enter\n"); - for (i = 0; i < BRCMF_MAX_IFS; i++) - brcmf_txflowblock_if(drvr->iflist[i], - BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state); + if (brcmf_fws_fc_active(drvr->fws)) { + brcmf_fws_bus_blocked(drvr, state); + } else { + for (i = 0; i < BRCMF_MAX_IFS; i++) + brcmf_txflowblock_if(drvr->iflist[i], + BRCMF_NETIF_STOP_REASON_BLOCK_BUS, + state); + } } void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) { - unsigned char *eth; - uint len; struct sk_buff *skb, *pnext; struct brcmf_if *ifp; struct brcmf_bus *bus_if = dev_get_drvdata(dev); @@ -280,7 +288,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) u8 ifidx; int ret; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(DATA, "Enter\n"); skb_queue_walk_safe(skb_list, skb, pnext) { skb_unlink(skb, skb_list); @@ -296,33 +304,12 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) continue; } - /* Get the protocol, maintain skb around eth_type_trans() - * The main reason for this hack is for the limitation of - * Linux 2.4 where 'eth_type_trans' uses the - * 'net->hard_header_len' - * to perform skb_pull inside vs ETH_HLEN. Since to avoid - * coping of the packet coming from the network stack to add - * BDC, Hardware header etc, during network interface - * registration - * we set the 'net->hard_header_len' to ETH_HLEN + extra space - * required - * for BDC, Hardware header etc. and not just the ETH_HLEN - */ - eth = skb->data; - len = skb->len; - skb->dev = ifp->ndev; skb->protocol = eth_type_trans(skb, skb->dev); if (skb->pkt_type == PACKET_MULTICAST) ifp->stats.multicast++; - skb->data = eth; - skb->len = len; - - /* Strip header, count, deliver upward */ - skb_pull(skb, ETH_HLEN); - /* Process special event packets */ brcmf_fweh_process_skb(drvr, skb); @@ -338,10 +325,8 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) netif_rx(skb); else /* If the receive is not processed inside an ISR, - * the softirqd must be woken explicitly to service - * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled - * by netif_rx_ni(), but in earlier kernels, we need - * to do it manually. + * the softirqd must be woken explicitly to service the + * NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). */ netif_rx_ni(skb); } @@ -630,7 +615,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) /* set appropriate operations */ ndev->netdev_ops = &brcmf_netdev_ops_pri; - ndev->hard_header_len = ETH_HLEN + drvr->hdrlen; + ndev->hard_header_len += drvr->hdrlen; ndev->ethtool_ops = &brcmf_ethtool_ops; drvr->rxsz = ndev->mtu + ndev->hard_header_len + @@ -779,6 +764,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp->bssidx = bssidx; init_waitqueue_head(&ifp->pend_8021x_wait); + spin_lock_init(&ifp->netif_stop_lock); if (mac_addr != NULL) memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index d2487518bd2..26411196832 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -448,8 +448,6 @@ struct brcmf_sdio { uint rxblen; /* Allocated length of rxbuf */ u8 *rxctl; /* Aligned pointer into rxbuf */ u8 *rxctl_orig; /* pointer for freeing rxctl */ - u8 *databuf; /* Buffer for receiving big glom packet */ - u8 *dataptr; /* Aligned pointer into databuf */ uint rxlen; /* Length of valid data in buffer */ spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */ @@ -473,8 +471,6 @@ struct brcmf_sdio { s32 idletime; /* Control for activity timeout */ s32 idlecount; /* Activity timeout counter */ s32 idleclock; /* How to set bus driver when idle */ - s32 sd_rxchain; - bool use_rxchain; /* If brcmf should use PKT chains */ bool rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ bool alp_only; /* Don't use HT clock (ALP only) */ @@ -495,8 +491,7 @@ struct brcmf_sdio { struct workqueue_struct *brcmf_wq; struct work_struct datawork; - struct list_head dpc_tsklst; - spinlock_t dpc_tl_lock; + atomic_t dpc_tskcnt; const struct firmware *firmware; u32 fw_ptr; @@ -1026,29 +1021,6 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } -/* copy a buffer into a pkt buffer chain */ -static uint brcmf_sdbrcm_glom_from_buf(struct brcmf_sdio *bus, uint len) -{ - uint n, ret = 0; - struct sk_buff *p; - u8 *buf; - - buf = bus->dataptr; - - /* copy the data */ - skb_queue_walk(&bus->glom, p) { - n = min_t(uint, p->len, len); - memcpy(p->data, buf, n); - buf += n; - len -= n; - ret += n; - if (!len) - break; - } - - return ret; -} - /* return total length of buffer chain */ static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) { @@ -1202,8 +1174,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) int errcode; u8 doff, sfdoff; - bool usechain = bus->use_rxchain; - struct brcmf_sdio_read rd_new; /* If packets, issue read(s) and send up packet chain */ @@ -1238,7 +1208,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (sublen % BRCMF_SDALIGN) { brcmf_err("sublen %d not multiple of %d\n", sublen, BRCMF_SDALIGN); - usechain = false; } totlen += sublen; @@ -1305,27 +1274,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) * packet and and copy into the chain. */ sdio_claim_host(bus->sdiodev->func[1]); - if (usechain) { - errcode = brcmf_sdcard_recv_chain(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, &bus->glom); - } else if (bus->dataptr) { - errcode = brcmf_sdcard_recv_buf(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, - bus->dataptr, dlen); - sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen); - if (sublen != dlen) { - brcmf_err("FAILED TO COPY, dlen %d sublen %d\n", - dlen, sublen); - errcode = -1; - } - pnext = NULL; - } else { - brcmf_err("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", - dlen); - errcode = -1; - } + errcode = brcmf_sdcard_recv_chain(bus->sdiodev, + bus->sdiodev->sbwad, + SDIO_FUNC_2, F2SYNC, &bus->glom); sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2rxdata++; @@ -2061,23 +2012,6 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) } } -static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus) -{ - struct list_head *new_hd; - unsigned long flags; - - if (in_interrupt()) - new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC); - else - new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL); - if (new_hd == NULL) - return; - - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - list_add_tail(new_hd, &bus->dpc_tsklst); - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); -} - static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { u8 idx; @@ -2312,7 +2246,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus)) || PKT_AVAILABLE()) { - brcmf_sdbrcm_adddpctsk(bus); + atomic_inc(&bus->dpc_tskcnt); } /* If we're done for now, turn off clock request. */ @@ -2342,7 +2276,6 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - unsigned long flags; brcmf_dbg(TRACE, "Enter\n"); @@ -2369,26 +2302,21 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) } else { ret = 0; } - spin_unlock_bh(&bus->txqlock); if (pktq_len(&bus->txq) >= TXHI) { bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } + spin_unlock_bh(&bus->txqlock); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) qcount[prec] = pktq_plen(&bus->txq, prec); #endif - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - if (list_empty(&bus->dpc_tsklst)) { - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); - - brcmf_sdbrcm_adddpctsk(bus); + if (atomic_read(&bus->dpc_tskcnt) == 0) { + atomic_inc(&bus->dpc_tskcnt); queue_work(bus->brcmf_wq, &bus->datawork); - } else { - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } return ret; @@ -2525,7 +2453,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - unsigned long flags; brcmf_dbg(TRACE, "Enter\n"); @@ -2612,18 +2539,13 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } while (ret < 0 && retries++ < TXRETRIES); } - spin_lock_irqsave(&bus->dpc_tl_lock, flags); if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && - list_empty(&bus->dpc_tsklst)) { - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); - + atomic_read(&bus->dpc_tskcnt) == 0) { bus->activity = false; sdio_claim_host(bus->sdiodev->func[1]); brcmf_dbg(INFO, "idle\n"); brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); sdio_release_host(bus->sdiodev->func[1]); - } else { - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } if (ret) @@ -3451,7 +3373,7 @@ void brcmf_sdbrcm_isr(void *arg) if (!bus->intr) brcmf_err("isr w/o interrupt configured!\n"); - brcmf_sdbrcm_adddpctsk(bus); + atomic_inc(&bus->dpc_tskcnt); queue_work(bus->brcmf_wq, &bus->datawork); } @@ -3460,7 +3382,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) #ifdef DEBUG struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); #endif /* DEBUG */ - unsigned long flags; brcmf_dbg(TIMER, "Enter\n"); @@ -3476,11 +3397,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) if (!bus->intr || (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - if (list_empty(&bus->dpc_tsklst)) { + if (atomic_read(&bus->dpc_tskcnt) == 0) { u8 devpend; - spin_unlock_irqrestore(&bus->dpc_tl_lock, - flags); + sdio_claim_host(bus->sdiodev->func[1]); devpend = brcmf_sdio_regrb(bus->sdiodev, SDIO_CCCR_INTx, @@ -3489,9 +3408,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); - } else { - spin_unlock_irqrestore(&bus->dpc_tl_lock, - flags); } /* If there is something, make like the ISR and @@ -3500,7 +3416,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->sdcnt.pollcnt++; atomic_set(&bus->ipend, 1); - brcmf_sdbrcm_adddpctsk(bus); + atomic_inc(&bus->dpc_tskcnt); queue_work(bus->brcmf_wq, &bus->datawork); } } @@ -3545,41 +3461,15 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) return (atomic_read(&bus->ipend) > 0); } -static bool brcmf_sdbrcm_chipmatch(u16 chipid) -{ - if (chipid == BCM43143_CHIP_ID) - return true; - if (chipid == BCM43241_CHIP_ID) - return true; - if (chipid == BCM4329_CHIP_ID) - return true; - if (chipid == BCM4330_CHIP_ID) - return true; - if (chipid == BCM4334_CHIP_ID) - return true; - if (chipid == BCM4335_CHIP_ID) - return true; - return false; -} - static void brcmf_sdio_dataworker(struct work_struct *work) { struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, datawork); - struct list_head *cur_hd, *tmp_hd; - unsigned long flags; - - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) { - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + while (atomic_read(&bus->dpc_tskcnt)) { brcmf_sdbrcm_dpc(bus); - - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - list_del(cur_hd); - kfree(cur_hd); + atomic_dec(&bus->dpc_tskcnt); } - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) @@ -3589,9 +3479,6 @@ static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) kfree(bus->rxbuf); bus->rxctl = bus->rxbuf = NULL; bus->rxlen = 0; - - kfree(bus->databuf); - bus->databuf = NULL; } static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) @@ -3604,29 +3491,10 @@ static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) ALIGNMENT) + BRCMF_SDALIGN; bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); if (!(bus->rxbuf)) - goto fail; - } - - /* Allocate buffer to receive glomed packet */ - bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC); - if (!(bus->databuf)) { - /* release rxbuf which was already located as above */ - if (!bus->rxblen) - kfree(bus->rxbuf); - goto fail; + return false; } - /* Align the buffer */ - if ((unsigned long)bus->databuf % BRCMF_SDALIGN) - bus->dataptr = bus->databuf + (BRCMF_SDALIGN - - ((unsigned long)bus->databuf % BRCMF_SDALIGN)); - else - bus->dataptr = bus->databuf; - return true; - -fail: - return false; } static bool @@ -3667,11 +3535,6 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) goto fail; } - if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) { - brcmf_err("unsupported chip: 0x%04x\n", bus->ci->chip); - goto fail; - } - if (brcmf_sdbrcm_kso_init(bus)) { brcmf_err("error enabling KSO\n"); goto fail; @@ -3770,10 +3633,6 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) bus->blocksize = bus->sdiodev->func[2]->cur_blksize; bus->roundup = min(max_roundup, bus->blocksize); - /* bus module does not support packet chaining */ - bus->use_rxchain = false; - bus->sd_rxchain = false; - /* SR state */ bus->sleeping = false; bus->sr_enabled = false; @@ -3927,8 +3786,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->watchdog_tsk = NULL; } /* Initialize DPC thread */ - INIT_LIST_HEAD(&bus->dpc_tsklst); - spin_lock_init(&bus->dpc_tl_lock); + atomic_set(&bus->dpc_tskcnt, 0); /* Assign bus interface call back */ bus->sdiodev->bus_if->dev = bus->sdiodev->dev; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index 6ec5db9c60a..e679214b3c9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -101,7 +101,8 @@ struct brcmf_event; BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \ BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ - BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) + BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5352dc1fdf3..f0d9f7f6c83 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -22,7 +22,6 @@ #include <linux/etherdevice.h> #include <linux/err.h> #include <linux/jiffies.h> -#include <uapi/linux/nl80211.h> #include <net/cfg80211.h> #include <brcmu_utils.h> @@ -142,7 +141,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) #define BRCMF_FWS_FLOWCONTROL_HIWATER 128 #define BRCMF_FWS_FLOWCONTROL_LOWATER 64 -#define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) +#define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2) #define BRCMF_FWS_PSQ_LEN 256 #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 @@ -157,11 +156,13 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver. * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue. * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware. + * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info. */ enum brcmf_fws_skb_state { BRCMF_FWS_SKBSTATE_NEW, BRCMF_FWS_SKBSTATE_DELAYED, - BRCMF_FWS_SKBSTATE_SUPPRESSED + BRCMF_FWS_SKBSTATE_SUPPRESSED, + BRCMF_FWS_SKBSTATE_TIM }; /** @@ -193,9 +194,8 @@ struct brcmf_skbuff_cb { * b[11] - packet sent upon firmware request. * b[10] - packet only contains signalling data. * b[9] - packet is a tx packet. - * b[8] - packet uses FIFO credit (non-pspoll). + * b[8] - packet used requested credit * b[7] - interface in AP mode. - * b[6:4] - AC FIFO number. * b[3:0] - interface index. */ #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 @@ -204,12 +204,10 @@ struct brcmf_skbuff_cb { #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9 -#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK 0x0100 -#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT 8 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8 #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080 #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7 -#define BRCMF_SKB_IF_FLAGS_FIFO_MASK 0x0070 -#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT 4 #define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0 @@ -246,7 +244,7 @@ struct brcmf_skbuff_cb { #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00 #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8 #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff -#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 +#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 #define brcmf_skb_htod_tag_set_field(skb, field, value) \ brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \ @@ -278,6 +276,7 @@ struct brcmf_skbuff_cb { /** * enum brcmf_fws_fifo - fifo indices used by dongle firmware. * + * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background. * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic. * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic. * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic. @@ -287,7 +286,8 @@ struct brcmf_skbuff_cb { * @BRCMF_FWS_FIFO_COUNT: number of fifos. */ enum brcmf_fws_fifo { - BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_FIRST, + BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST, BRCMF_FWS_FIFO_AC_BE, BRCMF_FWS_FIFO_AC_VI, BRCMF_FWS_FIFO_AC_VO, @@ -307,12 +307,15 @@ enum brcmf_fws_fifo { * firmware suppress the packet as device is already in PS mode. * @BRCMF_FWS_TXSTATUS_FW_TOSSED: * firmware tossed the packet. + * @BRCMF_FWS_TXSTATUS_HOST_TOSSED: + * host tossed the packet. */ enum brcmf_fws_txstatus { BRCMF_FWS_TXSTATUS_DISCARD, BRCMF_FWS_TXSTATUS_CORE_SUPPRESS, BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS, - BRCMF_FWS_TXSTATUS_FW_TOSSED + BRCMF_FWS_TXSTATUS_FW_TOSSED, + BRCMF_FWS_TXSTATUS_HOST_TOSSED }; enum brcmf_fws_fcmode { @@ -343,6 +346,7 @@ enum brcmf_fws_mac_desc_state { * @transit_count: packet in transit to firmware. */ struct brcmf_fws_mac_descriptor { + char name[16]; u8 occupied; u8 mac_handle; u8 interface_id; @@ -356,7 +360,6 @@ struct brcmf_fws_mac_descriptor { u8 seq[BRCMF_FWS_FIFO_COUNT]; struct pktq psq; int transit_count; - int suppress_count; int suppr_transit_count; bool send_tim_signal; u8 traffic_pending_bmp; @@ -383,12 +386,10 @@ enum brcmf_fws_hanger_item_state { * struct brcmf_fws_hanger_item - single entry for tx pending packet. * * @state: entry is either free or occupied. - * @gen: generation. * @pkt: packet itself. */ struct brcmf_fws_hanger_item { enum brcmf_fws_hanger_item_state state; - u8 gen; struct sk_buff *pkt; }; @@ -424,6 +425,7 @@ struct brcmf_fws_info { struct brcmf_fws_stats stats; struct brcmf_fws_hanger hanger; enum brcmf_fws_fcmode fcmode; + bool bcmc_credit_check; struct brcmf_fws_macdesc_table desc; struct workqueue_struct *fws_wq; struct work_struct fws_dequeue_work; @@ -434,6 +436,8 @@ struct brcmf_fws_info { u32 fifo_credit_map; u32 fifo_delay_map; unsigned long borrow_defer_timestamp; + bool bus_flow_blocked; + bool creditmap_received; }; /* @@ -507,7 +511,6 @@ static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) { int i; - brcmf_dbg(TRACE, "enter\n"); memset(hanger, 0, sizeof(*hanger)); for (i = 0; i < ARRAY_SIZE(hanger->items); i++) hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; @@ -517,7 +520,6 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) { u32 i; - brcmf_dbg(TRACE, "enter\n"); i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; while (i != h->slot_pos) { @@ -533,14 +535,12 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) h->failed_slotfind++; i = BRCMF_FWS_HANGER_MAXITEMS; done: - brcmf_dbg(TRACE, "exit: %d\n", i); return i; } static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, - struct sk_buff *pkt, u32 slot_id) + struct sk_buff *pkt, u32 slot_id) { - brcmf_dbg(TRACE, "enter\n"); if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; @@ -560,7 +560,6 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, u32 slot_id, struct sk_buff **pktout, bool remove_item) { - brcmf_dbg(TRACE, "enter\n"); if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; @@ -574,23 +573,18 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, if (remove_item) { h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; h->items[slot_id].pkt = NULL; - h->items[slot_id].gen = 0xff; h->popped++; } return 0; } static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, - u32 slot_id, u8 gen) + u32 slot_id) { - brcmf_dbg(TRACE, "enter\n"); - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; - h->items[slot_id].gen = gen; - - if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) { + if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { brcmf_err("entry not in use\n"); return -EINVAL; } @@ -599,25 +593,6 @@ static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, return 0; } -static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, - struct sk_buff *pkt, u32 slot_id, - int *gen) -{ - brcmf_dbg(TRACE, "enter\n"); - *gen = 0xff; - - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) - return -ENOENT; - - if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { - brcmf_err("slot not in use\n"); - return -EINVAL; - } - - *gen = hanger->items[slot_id].gen; - return 0; -} - static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, bool (*fn)(struct sk_buff *, void *), int ifidx) @@ -627,7 +602,6 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, int i; enum brcmf_fws_hanger_item_state s; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); for (i = 0; i < ARRAY_SIZE(h->items); i++) { s = h->items[i].state; if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE || @@ -644,14 +618,28 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, } } -static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, - u8 *addr, u8 ifidx) +static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *desc) +{ + if (desc == &fws->desc.other) + strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name)); + else if (desc->mac_handle) + scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d", + desc->mac_handle, desc->interface_id); + else + scnprintf(desc->name, sizeof(desc->name), "MACIF:%d", + desc->interface_id); +} + +static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc, + u8 *addr, u8 ifidx) { brcmf_dbg(TRACE, "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx); desc->occupied = 1; desc->state = BRCMF_FWS_STATE_OPEN; desc->requested_credit = 0; + desc->requested_packet = 0; /* depending on use may need ifp->bssidx instead */ desc->interface_id = ifidx; desc->ac_bitmap = 0xff; /* update this when handling APSD */ @@ -660,22 +648,22 @@ static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, } static -void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc) +void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc) { brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id); desc->occupied = 0; desc->state = BRCMF_FWS_STATE_CLOSE; desc->requested_credit = 0; + desc->requested_packet = 0; } static struct brcmf_fws_mac_descriptor * -brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) +brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea) { struct brcmf_fws_mac_descriptor *entry; int i; - brcmf_dbg(TRACE, "enter: ea=%pM\n", ea); if (ea == NULL) return ERR_PTR(-EINVAL); @@ -690,42 +678,33 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) } static struct brcmf_fws_mac_descriptor* -brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp, - u8 *da) +brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da) { struct brcmf_fws_mac_descriptor *entry = &fws->desc.other; bool multicast; - enum nl80211_iftype iftype; - - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); multicast = is_multicast_ether_addr(da); - iftype = brcmf_cfg80211_get_iftype(ifp); - /* Multicast destination and P2P clients get the interface entry. - * STA gets the interface entry if there is no exact match. For - * example, TDLS destinations have their own entry. + /* Multicast destination, STA and P2P clients get the interface entry. + * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations + * have their own entry. */ - entry = NULL; - if ((multicast || iftype == NL80211_IFTYPE_STATION || - iftype == NL80211_IFTYPE_P2P_CLIENT) && ifp->fws_desc) + if (multicast && ifp->fws_desc) { entry = ifp->fws_desc; - - if (entry != NULL && iftype != NL80211_IFTYPE_STATION) goto done; + } - entry = brcmf_fws_mac_descriptor_lookup(fws, da); + entry = brcmf_fws_macdesc_lookup(fws, da); if (IS_ERR(entry)) - entry = &fws->desc.other; + entry = ifp->fws_desc; done: - brcmf_dbg(TRACE, "exit: entry=%p\n", entry); return entry; } -static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws, - struct brcmf_fws_mac_descriptor *entry, - int fifo) +static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int fifo) { struct brcmf_fws_mac_descriptor *if_entry; bool closed; @@ -748,15 +727,11 @@ static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws, return closed || !(entry->ac_bitmap & BIT(fifo)); } -static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws, - struct brcmf_fws_mac_descriptor *entry, - int ifidx) +static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int ifidx) { - brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n", - entry->ea, entry->interface_id, ifidx); if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) { - brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n", - ifidx, entry->psq.len); brcmf_fws_psq_flush(fws, &entry->psq, ifidx); entry->occupied = !!(entry->psq.len); } @@ -772,7 +747,6 @@ static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws, int prec; u32 hslot; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); txq = brcmf_bus_gettxq(fws->drvr->bus_if); if (IS_ERR(txq)) { brcmf_dbg(TRACE, "no txq to clean up\n"); @@ -798,7 +772,6 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) struct brcmf_fws_mac_descriptor *table; bool (*matchfn)(struct sk_buff *, void *) = NULL; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); if (fws == NULL) return; @@ -808,51 +781,121 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) /* cleanup individual nodes */ table = &fws->desc.nodes[0]; for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) - brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx); + brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx); - brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx); + brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx); brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); } -static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx, - struct brcmf_fws_mac_descriptor *entry, - int prec) +static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) { - brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea); - if (entry->state == BRCMF_FWS_STATE_CLOSE) { - /* check delayedQ and suppressQ in one call using bitmap */ - if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0) - entry->traffic_pending_bmp = - entry->traffic_pending_bmp & ~NBITVAL(prec); - else - entry->traffic_pending_bmp = - entry->traffic_pending_bmp | NBITVAL(prec); + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + u8 *wlh; + u16 data_offset = 0; + u8 fillers; + __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); + + brcmf_dbg(TRACE, "enter: %s, idx=%d pkttag=0x%08X, hslot=%d\n", + entry->name, brcmf_skb_if_flags_get_field(skb, INDEX), + le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff); + if (entry->send_tim_signal) + data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + + /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ + data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; + fillers = round_up(data_offset, 4) - data_offset; + data_offset += fillers; + + skb_push(skb, data_offset); + wlh = skb->data; + + wlh[0] = BRCMF_FWS_TYPE_PKTTAG; + wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; + memcpy(&wlh[2], &pkttag, sizeof(pkttag)); + wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; + + if (entry->send_tim_signal) { + entry->send_tim_signal = 0; + wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; + wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + wlh[2] = entry->mac_handle; + wlh[3] = entry->traffic_pending_bmp; + brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n", + entry->mac_handle, entry->traffic_pending_bmp); + wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; } - /* request a TIM update to firmware at the next piggyback opportunity */ + if (fillers) + memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); + + brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), + data_offset >> 2, skb); + return 0; +} + +static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int fifo, bool send_immediately) +{ + struct sk_buff *skb; + struct brcmf_bus *bus; + struct brcmf_skbuff_cb *skcb; + s32 err; + u32 len; + + /* check delayedQ and suppressQ in one call using bitmap */ + if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0) + entry->traffic_pending_bmp &= ~NBITVAL(fifo); + else + entry->traffic_pending_bmp |= NBITVAL(fifo); + + entry->send_tim_signal = false; if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) entry->send_tim_signal = true; + if (send_immediately && entry->send_tim_signal && + entry->state == BRCMF_FWS_STATE_CLOSE) { + /* create a dummy packet and sent that. The traffic */ + /* bitmap info will automatically be attached to that packet */ + len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 + + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 + + 4 + fws->drvr->hdrlen; + skb = brcmu_pkt_buf_get_skb(len); + if (skb == NULL) + return false; + skb_pull(skb, len); + skcb = brcmf_skbcb(skb); + skcb->mac = entry; + skcb->state = BRCMF_FWS_SKBSTATE_TIM; + bus = fws->drvr->bus_if; + err = brcmf_fws_hdrpush(fws, skb); + if (err == 0) + err = brcmf_bus_txdata(bus, skb); + if (err) + brcmu_pkt_buf_free_skb(skb); + return true; + } + return false; } static void brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, u8 if_id) { - struct brcmf_if *ifp = fws->drvr->iflist[if_id]; + struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1]; if (WARN_ON(!ifp)) return; - brcmf_dbg(TRACE, - "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx); - if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER) brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, false); if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && - pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) + pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) { + fws->stats.fws_flow_block++; brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true); + } return; } @@ -862,10 +905,26 @@ static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) return 0; } +/* using macro so sparse checking does not complain + * about locking imbalance. + */ +#define brcmf_fws_lock(drvr, flags) \ +do { \ + flags = 0; \ + spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \ +} while (0) + +/* using macro so sparse checking does not complain + * about locking imbalance. + */ +#define brcmf_fws_unlock(drvr, flags) \ + spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags)) + static int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry, *existing; + ulong flags; u8 mac_handle; u8 ifidx; u8 *addr; @@ -876,34 +935,44 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) entry = &fws->desc.nodes[mac_handle & 0x1F]; if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { - brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); if (entry->occupied) { - brcmf_fws_mac_desc_cleanup(fws, entry, -1); - brcmf_fws_clear_mac_descriptor(entry); + brcmf_dbg(TRACE, "deleting %s mac %pM\n", + entry->name, addr); + brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_macdesc_cleanup(fws, entry, -1); + brcmf_fws_macdesc_deinit(entry); + brcmf_fws_unlock(fws->drvr, flags); } else fws->stats.mac_update_failed++; return 0; } - brcmf_dbg(TRACE, - "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx); - existing = brcmf_fws_mac_descriptor_lookup(fws, addr); + existing = brcmf_fws_macdesc_lookup(fws, addr); if (IS_ERR(existing)) { if (!entry->occupied) { + brcmf_fws_lock(fws->drvr, flags); entry->mac_handle = mac_handle; - brcmf_fws_init_mac_descriptor(entry, addr, ifidx); + brcmf_fws_macdesc_init(entry, addr, ifidx); + brcmf_fws_macdesc_set_name(fws, entry); brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); + brcmf_fws_unlock(fws->drvr, flags); + brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr); } else { fws->stats.mac_update_failed++; } } else { if (entry != existing) { - brcmf_dbg(TRACE, "relocate mac\n"); + brcmf_dbg(TRACE, "copy mac %s\n", existing->name); + brcmf_fws_lock(fws->drvr, flags); memcpy(entry, existing, offsetof(struct brcmf_fws_mac_descriptor, psq)); entry->mac_handle = mac_handle; - brcmf_fws_clear_mac_descriptor(existing); + brcmf_fws_macdesc_deinit(existing); + brcmf_fws_macdesc_set_name(fws, entry); + brcmf_fws_unlock(fws->drvr, flags); + brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name, + addr); } else { brcmf_dbg(TRACE, "use existing\n"); WARN_ON(entry->mac_handle != mac_handle); @@ -917,8 +986,9 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry; + ulong flags; u8 mac_handle; - int i; + int ret; mac_handle = data[0]; entry = &fws->desc.nodes[mac_handle & 0x1F]; @@ -926,30 +996,35 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, fws->stats.mac_ps_update_failed++; return -ESRCH; } - - /* a state update should wipe old credits? */ + brcmf_fws_lock(fws->drvr, flags); + /* a state update should wipe old credits */ entry->requested_credit = 0; + entry->requested_packet = 0; if (type == BRCMF_FWS_TYPE_MAC_OPEN) { entry->state = BRCMF_FWS_STATE_OPEN; - return BRCMF_FWS_RET_OK_SCHEDULE; + ret = BRCMF_FWS_RET_OK_SCHEDULE; } else { entry->state = BRCMF_FWS_STATE_CLOSE; - for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++) - brcmf_fws_tim_update(fws, entry, i); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true); + ret = BRCMF_FWS_RET_OK_NOSCHEDULE; } - return BRCMF_FWS_RET_OK_NOSCHEDULE; + brcmf_fws_unlock(fws->drvr, flags); + return ret; } static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry; + ulong flags; u8 ifidx; int ret; ifidx = data[0]; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); if (ifidx >= BRCMF_MAX_IFS) { ret = -ERANGE; goto fail; @@ -961,17 +1036,26 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, goto fail; } + brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, + entry->name); + brcmf_fws_lock(fws->drvr, flags); switch (type) { case BRCMF_FWS_TYPE_INTERFACE_OPEN: entry->state = BRCMF_FWS_STATE_OPEN; - return BRCMF_FWS_RET_OK_SCHEDULE; + ret = BRCMF_FWS_RET_OK_SCHEDULE; + break; case BRCMF_FWS_TYPE_INTERFACE_CLOSE: entry->state = BRCMF_FWS_STATE_CLOSE; - return BRCMF_FWS_RET_OK_NOSCHEDULE; + ret = BRCMF_FWS_RET_OK_NOSCHEDULE; + break; default: ret = -EINVAL; - break; + brcmf_fws_unlock(fws->drvr, flags); + goto fail; } + brcmf_fws_unlock(fws->drvr, flags); + return ret; + fail: fws->stats.if_update_failed++; return ret; @@ -981,6 +1065,7 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) { struct brcmf_fws_mac_descriptor *entry; + ulong flags; entry = &fws->desc.nodes[data[1] & 0x1F]; if (!entry->occupied) { @@ -991,15 +1076,51 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, return -ESRCH; } + brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n", + brcmf_fws_get_tlv_name(type), type, entry->name, + data[0], data[2]); + brcmf_fws_lock(fws->drvr, flags); if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) entry->requested_credit = data[0]; else entry->requested_packet = data[0]; entry->ac_bitmap = data[2]; + brcmf_fws_unlock(fws->drvr, flags); return BRCMF_FWS_RET_OK_SCHEDULE; } +static void +brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry, + struct sk_buff *skb) +{ + if (entry->requested_credit > 0) { + entry->requested_credit--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1); + if (entry->state != BRCMF_FWS_STATE_CLOSE) + brcmf_err("requested credit set while mac not closed!\n"); + } else if (entry->requested_packet > 0) { + entry->requested_packet--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); + if (entry->state != BRCMF_FWS_STATE_CLOSE) + brcmf_err("requested packet set while mac not closed!\n"); + } else { + brcmf_skb_if_flags_set_field(skb, REQUESTED, 0); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); + } +} + +static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + + if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) && + (entry->state == BRCMF_FWS_STATE_CLOSE)) + entry->requested_credit++; +} + static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, u8 fifo, u8 credits) { @@ -1010,6 +1131,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, if (!credits) return; + fws->fifo_credit_map |= 1 << fifo; + if ((fifo == BRCMF_FWS_FIFO_AC_BE) && (fws->credits_borrowed[0])) { for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0; @@ -1031,7 +1154,6 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, } } - fws->fifo_credit_map |= 1 << fifo; fws->fifo_credit[fifo] += credits; } @@ -1042,27 +1164,6 @@ static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) queue_work(fws->fws_wq, &fws->fws_dequeue_work); } -static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *p) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac; - - if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { - if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT) - return; - brcmf_fws_return_credits(fws, fifo, 1); - } else { - /* - * if this packet did not count against FIFO credit, it - * must have taken a requested_credit from the destination - * entry (for pspoll etc.) - */ - if (!brcmf_skb_if_flags_get_field(p, REQUESTED)) - entry->requested_credit++; - } - brcmf_fws_schedule_deq(fws); -} - static int brcmf_fws_enq(struct brcmf_fws_info *fws, enum brcmf_fws_skb_state state, int fifo, struct sk_buff *p) @@ -1078,7 +1179,7 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, return -ENOENT; } - brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len); + brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p); if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { prec += 1; qfull_stat = &fws->stats.supprq_full_error; @@ -1095,14 +1196,12 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, /* update the sk_buff state */ brcmf_skbcb(p)->state = state; - if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) - entry->suppress_count++; /* * A packet has been pushed so update traffic * availability bitmap, if applicable */ - brcmf_fws_tim_update(fws, entry, fifo); + brcmf_fws_tim_update(fws, entry, fifo, true); brcmf_fws_flow_control_check(fws, &entry->psq, brcmf_skb_if_flags_get_field(p, INDEX)); return 0; @@ -1113,7 +1212,6 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) struct brcmf_fws_mac_descriptor *table; struct brcmf_fws_mac_descriptor *entry; struct sk_buff *p; - int use_credit = 1; int num_nodes; int node_pos; int prec_out; @@ -1127,7 +1225,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) for (i = 0; i < num_nodes; i++) { entry = &table[(node_pos + i) % num_nodes]; if (!entry->occupied || - brcmf_fws_mac_desc_closed(fws, entry, fifo)) + brcmf_fws_macdesc_closed(fws, entry, fifo)) continue; if (entry->suppressed) @@ -1137,9 +1235,8 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out); if (p == NULL) { if (entry->suppressed) { - if (entry->suppr_transit_count > - entry->suppress_count) - return NULL; + if (entry->suppr_transit_count) + continue; entry->suppressed = false; p = brcmu_pktq_mdeq(&entry->psq, 1 << (fifo * 2), &prec_out); @@ -1148,26 +1245,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) if (p == NULL) continue; - /* did the packet come from suppress sub-queue? */ - if (entry->requested_credit > 0) { - entry->requested_credit--; - /* - * if the packet was pulled out while destination is in - * closed state but had a non-zero packets requested, - * then this should not count against the FIFO credit. - * That is due to the fact that the firmware will - * most likely hold onto this packet until a suitable - * time later to push it to the appropriate AC FIFO. - */ - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } else if (entry->requested_packet > 0) { - entry->requested_packet--; - brcmf_skb_if_flags_set_field(p, REQUESTED, 1); - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } - brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit); + brcmf_fws_macdesc_use_req_credit(entry, p); /* move dequeue position to ensure fair round-robin */ fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; @@ -1179,7 +1257,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) * A packet has been picked up, update traffic * availability bitmap, if applicable */ - brcmf_fws_tim_update(fws, entry, fifo); + brcmf_fws_tim_update(fws, entry, fifo, false); /* * decrement total enqueued fifo packets and @@ -1192,7 +1270,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) } p = NULL; done: - brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p); + brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p); return p; } @@ -1202,22 +1280,26 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u32 hslot; int ret; + u8 ifidx; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); /* this packet was suppressed */ - if (!entry->suppressed || entry->generation != genbit) { + if (!entry->suppressed) { entry->suppressed = true; - entry->suppress_count = brcmu_pktq_mlen(&entry->psq, - 1 << (fifo * 2 + 1)); entry->suppr_transit_count = entry->transit_count; + brcmf_dbg(DATA, "suppress %s: transit %d\n", + entry->name, entry->transit_count); } entry->generation = genbit; - ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); + if (ret == 0) + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, + skb); if (ret != 0) { - /* suppress q is full, drop this packet */ + /* suppress q is full or hdrpull failed, drop this packet */ brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); } else { @@ -1225,26 +1307,24 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, * Mark suppressed to avoid a double free during * wlfc cleanup */ - brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot, - genbit); - entry->suppress_count++; + brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); } return ret; } static int -brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, +brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, u32 genbit) { u32 fifo; int ret; bool remove_from_hanger = true; struct sk_buff *skb; + struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; - brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n", - flags, hslot); + brcmf_dbg(DATA, "flags %d\n", flags); if (flags == BRCMF_FWS_TXSTATUS_DISCARD) fws->stats.txs_discard++; @@ -1256,6 +1336,8 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, remove_from_hanger = false; } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) fws->stats.txs_tossed++; + else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) + fws->stats.txs_host_tossed++; else brcmf_err("unexpected txstatus\n"); @@ -1266,32 +1348,42 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, return ret; } - entry = brcmf_skbcb(skb)->mac; + skcb = brcmf_skbcb(skb); + entry = skcb->mac; if (WARN_ON(!entry)) { brcmu_pkt_buf_free_skb(skb); return -EINVAL; } + entry->transit_count--; + if (entry->suppressed && entry->suppr_transit_count) + entry->suppr_transit_count--; + + brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, + skcb->htod); /* pick up the implicit credit from this packet */ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); - brcmf_skb_pick_up_credit(fws, fifo, skb); + if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) || + (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) || + (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) { + brcmf_fws_return_credits(fws, fifo, 1); + brcmf_fws_schedule_deq(fws); + } + brcmf_fws_macdesc_return_req_credit(skb); if (!remove_from_hanger) ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); - if (remove_from_hanger || ret) { - entry->transit_count--; - if (entry->suppressed) - entry->suppr_transit_count--; - + if (remove_from_hanger || ret) brcmf_txfinalize(fws->drvr, skb, true); - } + return 0; } static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, u8 *data) { + ulong flags; int i; if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { @@ -1299,17 +1391,20 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, return BRCMF_FWS_RET_OK_NOSCHEDULE; } - brcmf_dbg(TRACE, "enter: data %pM\n", data); + brcmf_dbg(DATA, "enter: data %pM\n", data); + brcmf_fws_lock(fws->drvr, flags); for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) brcmf_fws_return_credits(fws, i, data[i]); - brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map, + brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map, fws->fifo_delay_map); + brcmf_fws_unlock(fws->drvr, flags); return BRCMF_FWS_RET_OK_SCHEDULE; } static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) { + ulong lflags; __le32 status_le; u32 status; u32 hslot; @@ -1323,7 +1418,10 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) hslot = brcmf_txstatus_get_field(status, HSLOT); genbit = brcmf_txstatus_get_field(status, GENERATION); - return brcmf_fws_txstatus_process(fws, flags, hslot, genbit); + brcmf_fws_lock(fws->drvr, lflags); + brcmf_fws_txs_process(fws, flags, hslot, genbit); + brcmf_fws_unlock(fws->drvr, lflags); + return BRCMF_FWS_RET_OK_NOSCHEDULE; } static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) @@ -1331,26 +1429,11 @@ static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) __le32 timestamp; memcpy(×tamp, &data[2], sizeof(timestamp)); - brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1], + brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1], le32_to_cpu(timestamp)); return 0; } -/* using macro so sparse checking does not complain - * about locking imbalance. - */ -#define brcmf_fws_lock(drvr, flags) \ -do { \ - flags = 0; \ - spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \ -} while (0) - -/* using macro so sparse checking does not complain - * about locking imbalance. - */ -#define brcmf_fws_unlock(drvr, flags) \ - spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags)) - static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) @@ -1364,6 +1447,10 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, brcmf_err("event payload too small (%d)\n", e->datalen); return -EINVAL; } + if (fws->creditmap_received) + return 0; + + fws->creditmap_received = true; brcmf_dbg(TRACE, "enter: credits %pM\n", credits); brcmf_fws_lock(ifp->drvr, flags); @@ -1379,11 +1466,24 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, return 0; } +static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_fws_info *fws = ifp->drvr->fws; + ulong flags; + + brcmf_fws_lock(ifp->drvr, flags); + if (fws) + fws->bcmc_credit_check = true; + brcmf_fws_unlock(ifp->drvr, flags); + return 0; +} + int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, struct sk_buff *skb) { struct brcmf_fws_info *fws = drvr->fws; - ulong flags; u8 *signal_data; s16 data_len; u8 type; @@ -1392,7 +1492,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, s32 status; s32 err; - brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n", + brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", ifidx, skb->len, signal_len); WARN_ON(signal_len > skb->len); @@ -1403,9 +1503,6 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, return 0; } - /* lock during tlv parsing */ - brcmf_fws_lock(drvr, flags); - fws->stats.header_pulls++; data_len = signal_len; signal_data = skb->data; @@ -1426,14 +1523,15 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, len = signal_data[1]; data = signal_data + 2; - brcmf_dbg(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type, - brcmf_fws_get_tlv_name(type), len, *data); + brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n", + brcmf_fws_get_tlv_name(type), type, len, + brcmf_fws_get_tlv_len(fws, type)); /* abort parsing when length invalid */ if (data_len < len + 2) break; - if (len != brcmf_fws_get_tlv_len(fws, type)) + if (len < brcmf_fws_get_tlv_len(fws, type)) break; err = BRCMF_FWS_RET_OK_NOSCHEDULE; @@ -1498,203 +1596,74 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, if (skb->len == 0) fws->stats.header_only_pkt++; - brcmf_fws_unlock(drvr, flags); - return 0; -} - -static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; - u8 *wlh; - u16 data_offset = 0; - u8 fillers; - __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); - - brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n", - entry->ea, entry->interface_id, le32_to_cpu(pkttag)); - if (entry->send_tim_signal) - data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - - /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ - data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; - fillers = round_up(data_offset, 4) - data_offset; - data_offset += fillers; - - skb_push(skb, data_offset); - wlh = skb->data; - - wlh[0] = BRCMF_FWS_TYPE_PKTTAG; - wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; - memcpy(&wlh[2], &pkttag, sizeof(pkttag)); - wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; - - if (entry->send_tim_signal) { - entry->send_tim_signal = 0; - wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; - wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - wlh[2] = entry->mac_handle; - wlh[3] = entry->traffic_pending_bmp; - wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; - entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; - } - if (fillers) - memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); - - brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), - data_offset >> 2, skb); return 0; } -static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, +static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, struct sk_buff *p) { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); struct brcmf_fws_mac_descriptor *entry = skcb->mac; - int rc = 0; - bool header_needed; - int hslot = BRCMF_FWS_HANGER_MAXITEMS; - u8 free_ctr; - u8 ifidx; u8 flags; - header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED; - - if (header_needed) { - /* obtaining free slot may fail, but that will be caught - * by the hanger push. This assures the packet has a BDC - * header upon return. - */ - hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); - free_ctr = entry->seq[fifo]; - brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); - brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr); - brcmf_skb_htod_tag_set_field(p, GENERATION, 1); - entry->transit_count++; - } brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); - brcmf_skb_htod_tag_set_field(p, FIFO, fifo); - + brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; - if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) { + if (brcmf_skb_if_flags_get_field(p, REQUESTED)) { /* - Indicate that this packet is being sent in response to an - explicit request from the firmware side. - */ + * Indicate that this packet is being sent in response to an + * explicit request from the firmware side. + */ flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; } brcmf_skb_htod_tag_set_field(p, FLAGS, flags); - if (header_needed) { - brcmf_fws_hdrpush(fws, p); - rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); - if (rc) - brcmf_err("hanger push failed: rc=%d\n", rc); - } else { - int gen; - - /* remove old header */ - rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p); - if (rc == 0) { - hslot = brcmf_skb_htod_tag_get_field(p, HSLOT); - brcmf_fws_hanger_get_genbit(&fws->hanger, p, - hslot, &gen); - brcmf_skb_htod_tag_set_field(p, GENERATION, gen); - - /* push new header */ - brcmf_fws_hdrpush(fws, p); - } - } - - return rc; + brcmf_fws_hdrpush(fws, p); } -static void -brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb) +static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, + struct sk_buff *skb, int fifo) { - /* - put the packet back to the head of queue - - - suppressed packet goes back to suppress sub-queue - - pull out the header, if new or delayed packet - - Note: hslot is used only when header removal is done. - */ struct brcmf_fws_mac_descriptor *entry; - enum brcmf_fws_skb_state state; struct sk_buff *pktout; + int qidx, hslot; int rc = 0; - int fifo; - int hslot; - u8 ifidx; - fifo = brcmf_skb_if_flags_get_field(skb, FIFO); - state = brcmf_skbcb(skb)->state; entry = brcmf_skbcb(skb)->mac; - - if (entry != NULL) { - if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { - /* wl-header is saved for suppressed packets */ - pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo + 1, - skb); - if (pktout == NULL) { - brcmf_err("suppress queue full\n"); - rc = -ENOSPC; - } - } else { - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - - /* remove header first */ - rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (rc) { - brcmf_err("header removal failed\n"); - /* free the hanger slot */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, - &pktout, true); - rc = -EINVAL; - goto fail; - } - - /* delay-q packets are going to delay-q */ - pktout = brcmu_pktq_penq_head(&entry->psq, - 2 * fifo, skb); - if (pktout == NULL) { - brcmf_err("delay queue full\n"); - rc = -ENOSPC; - } - - /* free the hanger slot */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &pktout, - true); - - /* decrement sequence count */ - entry->seq[fifo]--; + if (entry->occupied) { + qidx = 2 * fifo; + if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED) + qidx++; + + pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb); + if (pktout == NULL) { + brcmf_err("%s queue %d full\n", entry->name, qidx); + rc = -ENOSPC; } - /* - if this packet did not count against FIFO credit, it must have - taken a requested_credit from the firmware (for pspoll etc.) - */ - if (!(brcmf_skbcb(skb)->if_flags & - BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) - entry->requested_credit++; } else { - brcmf_err("no mac entry linked\n"); + brcmf_err("%s entry removed\n", entry->name); rc = -ENOENT; } - -fail: if (rc) { - brcmf_txfinalize(fws->drvr, skb, false); fws->stats.rollback_failed++; - } else + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, + hslot, 0); + } else { fws->stats.rollback_success++; + brcmf_fws_return_credits(fws, fifo, 1); + brcmf_fws_macdesc_return_req_credit(skb); + } } static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) { int lender_ac; - if (time_after(fws->borrow_defer_timestamp, jiffies)) + if (time_after(fws->borrow_defer_timestamp, jiffies)) { + fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); return -ENAVAIL; + } for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) { if (fws->fifo_credit[lender_ac]) { @@ -1702,66 +1671,15 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) fws->fifo_credit[lender_ac]--; if (fws->fifo_credit[lender_ac] == 0) fws->fifo_credit_map &= ~(1 << lender_ac); - brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac); + fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE); + brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac); return 0; } } + fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); return -ENAVAIL; } -static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; - int *credit = &fws->fifo_credit[fifo]; - int use_credit = 1; - - brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit); - - if (entry->requested_credit > 0) { - /* - * if the packet was pulled out while destination is in - * closed state but had a non-zero packets requested, - * then this should not count against the FIFO credit. - * That is due to the fact that the firmware will - * most likely hold onto this packet until a suitable - * time later to push it to the appropriate AC FIFO. - */ - entry->requested_credit--; - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } else if (entry->requested_packet > 0) { - entry->requested_packet--; - brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } - brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit); - if (!use_credit) { - brcmf_dbg(TRACE, "exit: no creditcheck set\n"); - return 0; - } - - if (fifo != BRCMF_FWS_FIFO_AC_BE) - fws->borrow_defer_timestamp = jiffies + - BRCMF_FWS_BORROW_DEFER_PERIOD; - - if (!(*credit)) { - /* Try to borrow a credit from other queue */ - if (fifo == BRCMF_FWS_FIFO_AC_BE && - brcmf_fws_borrow_credit(fws) == 0) - return 0; - - brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo); - return -ENAVAIL; - } - (*credit)--; - if (!(*credit)) - fws->fifo_credit_map &= ~(1 << fifo); - brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit); - return 0; -} - static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, struct sk_buff *skb) { @@ -1769,32 +1687,51 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, struct brcmf_fws_mac_descriptor *entry; struct brcmf_bus *bus = fws->drvr->bus_if; int rc; + u8 ifidx; entry = skcb->mac; if (IS_ERR(entry)) return PTR_ERR(entry); - rc = brcmf_fws_precommit_skb(fws, fifo, skb); + brcmf_fws_precommit_skb(fws, fifo, skb); + rc = brcmf_bus_txdata(bus, skb); + brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, + skcb->if_flags, skcb->htod, rc); if (rc < 0) { - fws->stats.generic_error++; + brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); goto rollback; } - rc = brcmf_bus_txdata(bus, skb); - if (rc < 0) - goto rollback; - - entry->seq[fifo]++; + entry->transit_count++; + if (entry->suppressed) + entry->suppr_transit_count++; fws->stats.pkt2bus++; - if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { - fws->stats.send_pkts[fifo]++; - fws->stats.fifo_credits_sent[fifo]++; - } + fws->stats.send_pkts[fifo]++; + if (brcmf_skb_if_flags_get_field(skb, REQUESTED)) + fws->stats.requested_sent[fifo]++; return rc; rollback: - brcmf_fws_rollback_toq(fws, skb); + brcmf_fws_rollback_toq(fws, skb, fifo); + return rc; +} + +static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p, + int fifo) +{ + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); + int rc, hslot; + + hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); + brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); + brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]); + brcmf_skb_htod_tag_set_field(p, FIFO, fifo); + rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); + if (!rc) + skcb->mac->seq[fifo]++; + else + fws->stats.generic_error++; return rc; } @@ -1826,29 +1763,25 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) /* set control buffer information */ skcb->if_flags = 0; - skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest); skcb->state = BRCMF_FWS_SKBSTATE_NEW; brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); if (!multicast) fifo = brcmf_fws_prio2fifo[skb->priority]; - brcmf_skb_if_flags_set_field(skb, FIFO, fifo); - - brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, - multicast, fifo); brcmf_fws_lock(drvr, flags); - if (skcb->mac->suppressed || - brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) || - brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) || - (!multicast && - brcmf_fws_consume_credit(fws, fifo, skb) < 0)) { - /* enqueue the packet in delayQ */ - drvr->fws->fifo_delay_map |= 1 << fifo; + if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC) + fws->borrow_defer_timestamp = jiffies + + BRCMF_FWS_BORROW_DEFER_PERIOD; + + skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest); + brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name, + eh->h_dest, multicast, fifo); + if (!brcmf_fws_assign_htod(fws, skb, fifo)) { brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); + brcmf_fws_schedule_deq(fws); } else { - if (brcmf_fws_commit_skb(fws, fifo, skb)) - if (!multicast) - brcmf_skb_pick_up_credit(fws, fifo, skb); + brcmf_err("drop skb: no hanger slot\n"); + brcmu_pkt_buf_free_skb(skb); } brcmf_fws_unlock(drvr, flags); return 0; @@ -1862,7 +1795,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp) if (!entry) return; - brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); + brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx); } void brcmf_fws_add_interface(struct brcmf_if *ifp) @@ -1870,16 +1803,16 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) struct brcmf_fws_info *fws = ifp->drvr->fws; struct brcmf_fws_mac_descriptor *entry; - brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n", - ifp->bssidx, ifp->mac_addr); if (!ifp->ndev || !ifp->drvr->fw_signals) return; entry = &fws->desc.iface[ifp->ifidx]; ifp->fws_desc = entry; - brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); + brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx); + brcmf_fws_macdesc_set_name(fws, entry); brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); + brcmf_dbg(TRACE, "added %s\n", entry->name); } void brcmf_fws_del_interface(struct brcmf_if *ifp) @@ -1887,13 +1820,13 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; ulong flags; - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); if (!entry) return; brcmf_fws_lock(ifp->drvr, flags); ifp->fws_desc = NULL; - brcmf_fws_clear_mac_descriptor(entry); + brcmf_dbg(TRACE, "deleting %s\n", entry->name); + brcmf_fws_macdesc_deinit(entry); brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); brcmf_fws_unlock(ifp->drvr, flags); } @@ -1904,39 +1837,37 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) struct sk_buff *skb; ulong flags; int fifo; - int credit; fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); - brcmf_dbg(TRACE, "enter: fws=%p\n", fws); brcmf_fws_lock(fws->drvr, flags); - for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) { - brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo, - fws->fifo_credit[fifo]); - for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) { + for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; + fifo--) { + while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) && + (fifo == BRCMF_FWS_FIFO_BCMC))) { skb = brcmf_fws_deq(fws, fifo); - if (!skb || brcmf_fws_commit_skb(fws, fifo, skb)) + if (!skb) + break; + fws->fifo_credit[fifo]--; + if (brcmf_fws_commit_skb(fws, fifo, skb)) + break; + if (fws->bus_flow_blocked) break; - if (brcmf_skbcb(skb)->if_flags & - BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) - credit++; } if ((fifo == BRCMF_FWS_FIFO_AC_BE) && - (credit == fws->fifo_credit[fifo])) { - fws->fifo_credit[fifo] -= credit; + (fws->fifo_credit[fifo] == 0) && + (!fws->bus_flow_blocked)) { while (brcmf_fws_borrow_credit(fws) == 0) { skb = brcmf_fws_deq(fws, fifo); if (!skb) { brcmf_fws_return_credits(fws, fifo, 1); break; } - if (brcmf_fws_commit_skb(fws, fifo, skb)) { - brcmf_fws_return_credits(fws, fifo, 1); + if (brcmf_fws_commit_skb(fws, fifo, skb)) + break; + if (fws->bus_flow_blocked) break; - } } - } else { - fws->fifo_credit[fifo] -= credit; } } brcmf_fws_unlock(fws->drvr, flags); @@ -1982,6 +1913,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr) brcmf_err("register credit map handler failed\n"); goto fail; } + rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT, + brcmf_fws_notify_bcmc_credit_support); + if (rc < 0) { + brcmf_err("register bcmc credit handler failed\n"); + brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP); + goto fail; + } /* setting the iovar may fail if feature is unsupported * so leave the rc as is so driver initialization can @@ -1993,19 +1931,20 @@ int brcmf_fws_init(struct brcmf_pub *drvr) } brcmf_fws_hanger_init(&drvr->fws->hanger); - brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0); + brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0); + brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other); brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); /* create debugfs file for statistics */ brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); - /* TODO: remove upon feature delivery */ - brcmf_err("%s bdcv2 tlv signaling [%x]\n", + brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", drvr->fw_signals ? "enabled" : "disabled", tlv); return 0; fail_event: + brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT); brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP); fail: brcmf_fws_deinit(drvr); @@ -2043,25 +1982,31 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) if (!fws) return false; - brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode); return fws->fcmode != BRCMF_FWS_FCMODE_NONE; } void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) { ulong flags; + u32 hslot; - brcmf_fws_lock(fws->drvr, flags); - brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, - brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); - /* the packet never reached firmware so reclaim credit */ - if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT && - brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { - brcmf_fws_return_credits(fws, - brcmf_skb_htod_tag_get_field(skb, - FIFO), - 1); - brcmf_fws_schedule_deq(fws); + if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) { + brcmu_pkt_buf_free_skb(skb); + return; } + brcmf_fws_lock(fws->drvr, flags); + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0); brcmf_fws_unlock(fws->drvr, flags); } + +void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) +{ + struct brcmf_fws_info *fws = drvr->fws; + + fws->bus_flow_blocked = flow_blocked; + if (!flow_blocked) + brcmf_fws_schedule_deq(fws); + else + fws->stats.bus_flow_block++; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index fbe483d2375..9fc860910bd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h @@ -29,5 +29,6 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp); void brcmf_fws_add_interface(struct brcmf_if *ifp); void brcmf_fws_del_interface(struct brcmf_if *ifp); void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); +void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); #endif /* FWSIGNAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 7c1b6332747..09786a53995 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -170,7 +170,6 @@ struct brcmf_sdio_dev { atomic_t suspend; /* suspend flag */ wait_queue_head_t request_byte_wait; wait_queue_head_t request_word_wait; - wait_queue_head_t request_chain_wait; wait_queue_head_t request_buffer_wait; struct device *dev; struct brcmf_bus *bus_if; @@ -230,8 +229,6 @@ brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, #define SDIO_REQ_4BYTE 0x1 /* Fixed address (FIFO) (vs. incrementing address) */ #define SDIO_REQ_FIXED 0x2 -/* Async request (vs. sync request) */ -#define SDIO_REQ_ASYNC 0x4 /* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). * rw: read or write (0/1) @@ -252,9 +249,6 @@ extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn); extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev); -extern int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, - u32 address); - /* attach, return handler on success, NULL if failed. * The handler shall be provided by all subsequent calls. No local cache * cfghdl points to the starting address of pci device mapped memory @@ -272,16 +266,6 @@ brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, uint addr, u32 *word, uint nbyte); -/* read or write any buffer using cmd53 */ -extern int -brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, - uint fix_inc, uint rw, uint fnc_num, u32 addr, - struct sk_buff *pkt); -extern int -brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, - uint write, uint func, uint addr, - struct sk_buff_head *pktq); - /* Watchdog timer interface for pm ops */ extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable); @@ -291,4 +275,8 @@ extern void brcmf_sdbrcm_disconnect(void *ptr); extern void brcmf_sdbrcm_isr(void *arg); extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick); + +extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, + wait_queue_head_t *wq); +extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev); #endif /* _BRCM_SDH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h index 9df1f7a681e..bc291711289 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h @@ -87,6 +87,27 @@ TRACE_EVENT(brcmf_hexdump, TP_printk("hexdump [length=%lu]", __entry->len) ); +TRACE_EVENT(brcmf_bdchdr, + TP_PROTO(void *data), + TP_ARGS(data), + TP_STRUCT__entry( + __field(u8, flags) + __field(u8, prio) + __field(u8, flags2) + __field(u32, siglen) + __dynamic_array(u8, signal, *((u8 *)data + 3) * 4) + ), + TP_fast_assign( + __entry->flags = *(u8 *)data; + __entry->prio = *((u8 *)data + 1); + __entry->flags2 = *((u8 *)data + 2); + __entry->siglen = *((u8 *)data + 3) * 4; + memcpy(__get_dynamic_array(signal), + (u8 *)data + 4, __entry->siglen); + ), + TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) +); + #ifdef CONFIG_BRCM_TRACING #undef TRACE_INCLUDE_PATH diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 01aed7ad6be..322cadc51de 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -82,6 +82,7 @@ struct brcmf_usbdev_info { int tx_high_watermark; int tx_freecount; bool tx_flowblock; + spinlock_t tx_flowblock_lock; struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; @@ -411,6 +412,7 @@ static void brcmf_usb_tx_complete(struct urb *urb) { struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; + unsigned long flags; brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status, req->skb); @@ -419,11 +421,13 @@ static void brcmf_usb_tx_complete(struct urb *urb) brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0); req->skb = NULL; brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); + spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); if (devinfo->tx_freecount > devinfo->tx_high_watermark && devinfo->tx_flowblock) { brcmf_txflowblock(devinfo->dev, false); devinfo->tx_flowblock = false; } + spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); } static void brcmf_usb_rx_complete(struct urb *urb) @@ -568,6 +572,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbreq *req; int ret; + unsigned long flags; brcmf_dbg(USB, "Enter, skb=%p\n", skb); if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { @@ -599,11 +604,13 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) goto fail; } + spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); if (devinfo->tx_freecount < devinfo->tx_low_watermark && !devinfo->tx_flowblock) { brcmf_txflowblock(dev, true); devinfo->tx_flowblock = true; } + spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); return 0; fail: @@ -1164,6 +1171,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, /* Initialize the spinlocks */ spin_lock_init(&devinfo->qlock); + spin_lock_init(&devinfo->tx_flowblock_lock); INIT_LIST_HEAD(&devinfo->rx_freeq); INIT_LIST_HEAD(&devinfo->rx_postq); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 301e572e892..277b37ae712 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3982,6 +3982,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct brcmf_fil_af_params_le *af_params; bool ack; s32 chan_nr; + u32 freq; brcmf_dbg(TRACE, "Enter\n"); @@ -3994,6 +3995,8 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, return -EPERM; } + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + if (ieee80211_is_probe_resp(mgmt->frame_control)) { /* Right now the only reason to get a probe response */ /* is for p2p listen response or for p2p GO from */ @@ -4009,7 +4012,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ie_len = len - ie_offset; - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; err = brcmf_vif_set_mgmt_ie(vif, @@ -4033,16 +4035,22 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); /* Add the length exepted for 802.11 header */ action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); - /* Add the channel */ - chan_nr = ieee80211_frequency_to_channel(chan->center_freq); + /* Add the channel. Use the one specified as parameter if any or + * the current one (got from the firmware) otherwise + */ + if (chan) + freq = chan->center_freq; + else + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &freq); + chan_nr = ieee80211_frequency_to_channel(freq); af_params->channel = cpu_to_le32(chan_nr); memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], le16_to_cpu(action_frame->len)); brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", - *cookie, le16_to_cpu(action_frame->len), - chan->center_freq); + *cookie, le16_to_cpu(action_frame->len), freq); ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), af_params); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c index 1585cc5bf86..bd982856d38 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c @@ -900,7 +900,7 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, if (supr_status) { update_rate = false; if (supr_status == TX_STATUS_SUPR_BADCH) { - brcms_err(wlc->hw->d11core, + brcms_dbg_ht(wlc->hw->d11core, "%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL( wlc->default_bss->chanspec)); diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig new file mode 100644 index 00000000000..0880742eab1 --- /dev/null +++ b/drivers/net/wireless/cw1200/Kconfig @@ -0,0 +1,30 @@ +config CW1200 + tristate "CW1200 WLAN support" + depends on MAC80211 && CFG80211 + help + This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. + This option just enables the driver core, see below for + specific bus support. + +if CW1200 + +config CW1200_WLAN_SDIO + tristate "Support SDIO platforms" + depends on CW1200 && MMC + help + Enable support for the CW1200 connected via an SDIO bus. + By default this driver only supports the Sagrad SG901-1091/1098 EVK + and similar designs that utilize a hardware reset circuit. To + support different CW1200 SDIO designs you will need to override + the default platform data by calling cw1200_sdio_set_platform_data() + in your board setup file. + +config CW1200_WLAN_SPI + tristate "Support SPI platforms" + depends on CW1200 && SPI + help + Enables support for the CW1200 connected via a SPI bus. You will + need to add appropriate platform data glue in your board setup + file. + +endif diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile new file mode 100644 index 00000000000..b086aac6547 --- /dev/null +++ b/drivers/net/wireless/cw1200/Makefile @@ -0,0 +1,21 @@ +cw1200_core-y := \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + scan.o \ + debug.o +cw1200_core-$(CONFIG_PM) += pm.o + +# CFLAGS_sta.o += -DDEBUG + +cw1200_wlan_sdio-y := cw1200_sdio.o +cw1200_wlan_spi-y := cw1200_spi.o + +obj-$(CONFIG_CW1200) += cw1200_core.o +obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o +obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c new file mode 100644 index 00000000000..c1ec2a4dd8c --- /dev/null +++ b/drivers/net/wireless/cw1200/bh.c @@ -0,0 +1,619 @@ +/* + * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <net/mac80211.h> +#include <linux/kthread.h> +#include <linux/timer.h> + +#include "cw1200.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "hwbus.h" +#include "debug.h" +#include "fwio.h" + +static int cw1200_bh(void *arg); + +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes + */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +/* Suspend state privates */ +enum cw1200_bh_pm_state { + CW1200_BH_RESUMED = 0, + CW1200_BH_SUSPEND, + CW1200_BH_SUSPENDED, + CW1200_BH_RESUME, +}; + +typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, + u8 *data, size_t size); + +static void cw1200_bh_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bh_work); + cw1200_bh(priv); +} + +int cw1200_register_bh(struct cw1200_common *priv) +{ + int err = 0; + /* Realtime workqueue */ + priv->bh_workqueue = alloc_workqueue("cw1200_bh", + WQ_MEM_RECLAIM | WQ_HIGHPRI + | WQ_CPU_INTENSIVE, 1); + + if (!priv->bh_workqueue) + return -ENOMEM; + + INIT_WORK(&priv->bh_work, cw1200_bh_work); + + pr_debug("[BH] register.\n"); + + atomic_set(&priv->bh_rx, 0); + atomic_set(&priv->bh_tx, 0); + atomic_set(&priv->bh_term, 0); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + priv->bh_error = 0; + priv->hw_bufs_used = 0; + priv->buf_id_tx = 0; + priv->buf_id_rx = 0; + init_waitqueue_head(&priv->bh_wq); + init_waitqueue_head(&priv->bh_evt_wq); + + err = !queue_work(priv->bh_workqueue, &priv->bh_work); + WARN_ON(err); + return err; +} + +void cw1200_unregister_bh(struct cw1200_common *priv) +{ + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + + flush_workqueue(priv->bh_workqueue); + + destroy_workqueue(priv->bh_workqueue); + priv->bh_workqueue = NULL; + + pr_debug("[BH] unregistered.\n"); +} + +void cw1200_irq_handler(struct cw1200_common *priv) +{ + pr_debug("[BH] irq.\n"); + + /* Disable Interrupts! */ + /* NOTE: hwbus_ops->lock already held */ + __cw1200_irq_enable(priv, 0); + + if (/* WARN_ON */(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_rx) == 1) + wake_up(&priv->bh_wq); +} +EXPORT_SYMBOL_GPL(cw1200_irq_handler); + +void cw1200_bh_wakeup(struct cw1200_common *priv) +{ + pr_debug("[BH] wakeup.\n"); + if (priv->bh_error) { + pr_err("[BH] wakeup failed (BH error)\n"); + return; + } + + if (atomic_add_return(1, &priv->bh_tx) == 1) + wake_up(&priv->bh_wq); +} + +int cw1200_bh_suspend(struct cw1200_common *priv) +{ + pr_debug("[BH] suspend.\n"); + if (priv->bh_error) { + wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n"); + return -EINVAL; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +int cw1200_bh_resume(struct cw1200_common *priv) +{ + pr_debug("[BH] resume.\n"); + if (priv->bh_error) { + wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n"); + return -EINVAL; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) +{ + ++priv->hw_bufs_used; +} + +int wsm_release_tx_buffer(struct cw1200_common *priv, int count) +{ + int ret = 0; + int hw_bufs_used = priv->hw_bufs_used; + + priv->hw_bufs_used -= count; + if (WARN_ON(priv->hw_bufs_used < 0)) + ret = -1; + else if (hw_bufs_used >= priv->wsm_caps.input_buffers) + ret = 1; + if (!priv->hw_bufs_used) + wake_up(&priv->bh_evt_wq); + return ret; +} + +static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, + u16 *ctrl_reg) +{ + int ret; + + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) + pr_err("[BH] Failed to read control register.\n"); + } + + return ret; +} + +static int cw1200_device_wakeup(struct cw1200_common *priv) +{ + u16 ctrl_reg; + int ret; + + pr_debug("[BH] Device wakeup.\n"); + + /* First, set the dpll register */ + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, + cw1200_dpll_from_clk(priv->hw_refclk)); + if (WARN_ON(ret)) + return ret; + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT); + if (WARN_ON(ret)) + return ret; + + ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); + if (WARN_ON(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. + */ + if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { + pr_debug("[BH] Device awake.\n"); + return 1; + } + + return 0; +} + +/* Must be called from BH thraed. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable) +{ + pr_debug("[BH] Powerave is %s.\n", + enable ? "enabled" : "disabled"); + priv->powersave_enabled = enable; +} + +static int cw1200_bh_rx_helper(struct cw1200_common *priv, + uint16_t *ctrl_reg, + int *tx) +{ + size_t read_len = 0; + struct sk_buff *skb_rx = NULL; + struct wsm_hdr *wsm; + size_t wsm_len; + u16 wsm_id; + u8 wsm_seq; + int rx_resync = 1; + + size_t alloc_len; + u8 *data; + + read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; + if (!read_len) + return 0; /* No more work */ + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + pr_debug("Invalid read len: %zu (%04x)", + read_len, *ctrl_reg); + goto err; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB + */ + read_len = read_len + 2; + + alloc_len = priv->hwbus_ops->align_size( + priv->hwbus_priv, read_len); + + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + pr_debug("Read aligned len: %zu\n", + alloc_len); + } + + skb_rx = dev_alloc_skb(alloc_len); + if (WARN_ON(!skb_rx)) + goto err; + + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (WARN_ON(!data)) + goto err; + + if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) { + pr_err("rx blew up, len %zu\n", alloc_len); + goto err; + } + + /* Piggyback */ + *ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + wsm = (struct wsm_hdr *)data; + wsm_len = __le16_to_cpu(wsm->len); + if (WARN_ON(wsm_len > read_len)) + goto err; + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("<-- ", + DUMP_PREFIX_NONE, + data, wsm_len); + + wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb_rx, wsm_len); + + if (wsm_id == 0x0800) { + wsm_handle_exception(priv, + &data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + goto err; + } else if (!rx_resync) { + if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) + goto err; + } + priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; + + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(priv, 1); + if (WARN_ON(rc < 0)) + return rc; + else if (rc > 0) + *tx = 1; + } + + /* cw1200_wsm_rx takes care on SKB livetime */ + if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) + goto err; + + if (skb_rx) { + dev_kfree_skb(skb_rx); + skb_rx = NULL; + } + + return 0; + +err: + if (skb_rx) { + dev_kfree_skb(skb_rx); + skb_rx = NULL; + } + return -1; +} + +static int cw1200_bh_tx_helper(struct cw1200_common *priv, + int *pending_tx, + int *tx_burst) +{ + size_t tx_len; + u8 *data; + int ret; + struct wsm_hdr *wsm; + + if (priv->device_can_sleep) { + ret = cw1200_device_wakeup(priv); + if (WARN_ON(ret < 0)) { /* Error in wakeup */ + *pending_tx = 1; + return 0; + } else if (ret) { /* Woke up */ + priv->device_can_sleep = false; + } else { /* Did not awake */ + *pending_tx = 1; + return 0; + } + } + + wsm_alloc_tx_buffer(priv); + ret = wsm_get_tx(priv, &data, &tx_len, tx_burst); + if (ret <= 0) { + wsm_release_tx_buffer(priv, 1); + if (WARN_ON(ret < 0)) + return ret; /* Error */ + return 0; /* No work */ + } + + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le16_to_cpu(wsm->len) != tx_len); + + atomic_add(1, &priv->bh_tx); + + tx_len = priv->hwbus_ops->align_size( + priv->hwbus_priv, tx_len); + + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) + pr_debug("Write aligned len: %zu\n", tx_len); + + wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq)); + + if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { + pr_err("tx blew up, len %zu\n", tx_len); + wsm_release_tx_buffer(priv, 1); + return -1; /* Error */ + } + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("--> ", + DUMP_PREFIX_NONE, + data, + __le16_to_cpu(wsm->len)); + + wsm_txed(priv, data); + priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; + + if (*tx_burst > 1) { + cw1200_debug_tx_burst(priv); + return 1; /* Work remains */ + } + + return 0; +} + +static int cw1200_bh(void *arg) +{ + struct cw1200_common *priv = arg; + int rx, tx, term, suspend; + u16 ctrl_reg = 0; + int tx_allowed; + int pending_tx = 0; + int tx_burst; + long status; + u32 dummy; + int ret; + + for (;;) { + if (!priv->hw_bufs_used && + priv->powersave_enabled && + !priv->device_can_sleep && + !atomic_read(&priv->recent_scan)) { + status = 1 * HZ; + pr_debug("[BH] Device wakedown. No data.\n"); + cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } else if (priv->hw_bufs_used) { + /* Interrupt loss detection */ + status = 1 * HZ; + } else { + status = MAX_SCHEDULE_TIMEOUT; + } + + /* Dummy Read for SDIO retry mechanism*/ + if ((priv->hw_type != -1) && + (atomic_read(&priv->bh_rx) == 0) && + (atomic_read(&priv->bh_tx) == 0)) + cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, + &dummy, sizeof(dummy)); + + pr_debug("[BH] waiting ...\n"); + status = wait_event_interruptible_timeout(priv->bh_wq, ({ + rx = atomic_xchg(&priv->bh_rx, 0); + tx = atomic_xchg(&priv->bh_tx, 0); + term = atomic_xchg(&priv->bh_term, 0); + suspend = pending_tx ? + 0 : atomic_read(&priv->bh_suspend); + (rx || tx || term || suspend || priv->bh_error); + }), status); + + pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n", + rx, tx, term, suspend, status); + + /* Did an error occur? */ + if ((status < 0 && status != -ERESTARTSYS) || + term || priv->bh_error) { + break; + } + if (!status) { /* wait_event timed out */ + unsigned long timestamp = jiffies; + long timeout; + int pending = 0; + int i; + + /* Check to see if we have any outstanding frames */ + if (priv->hw_bufs_used && (!rx || !tx)) { + wiphy_warn(priv->hw->wiphy, + "Missed interrupt? (%d frames outstanding)\n", + priv->hw_bufs_used); + rx = 1; + + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending += cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp, + priv->pending_frame_id); + + /* Check if frame transmission is timed out. + * Add an extra second with respect to possible + * interrupt loss. + */ + timeout = timestamp + + WSM_CMD_LAST_CHANCE_TIMEOUT + + 1 * HZ - + jiffies; + + /* And terminate BH thread if the frame is "stuck" */ + if (pending && timeout < 0) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n", + priv->hw_bufs_used, pending, + timestamp, jiffies); + break; + } + } else if (!priv->device_can_sleep && + !atomic_read(&priv->recent_scan)) { + pr_debug("[BH] Device wakedown. Timeout.\n"); + cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } + goto done; + } else if (suspend) { + pr_debug("[BH] Device suspend.\n"); + if (priv->powersave_enabled) { + pr_debug("[BH] Device wakedown. Suspend.\n"); + cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); + wake_up(&priv->bh_evt_wq); + status = wait_event_interruptible(priv->bh_wq, + CW1200_BH_RESUME == atomic_read(&priv->bh_suspend)); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "Failed to wait for resume: %ld.\n", + status); + break; + } + pr_debug("[BH] Device resume.\n"); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + wake_up(&priv->bh_evt_wq); + atomic_add(1, &priv->bh_rx); + goto done; + } + + rx: + tx += pending_tx; + pending_tx = 0; + + if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; + + /* Don't bother trying to rx unless we have data to read */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { + ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); + if (ret < 0) + break; + /* Double up here if there's more data.. */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { + ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); + if (ret < 0) + break; + } + } + + tx: + if (tx) { + tx = 0; + + BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers); + tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used; + tx_allowed = tx_burst > 0; + + if (!tx_allowed) { + /* Buffers full. Ensure we process tx + * after we handle rx.. + */ + pending_tx = tx; + goto done_rx; + } + ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst); + if (ret < 0) + break; + if (ret > 0) /* More to transmit */ + tx = ret; + + /* Re-read ctrl reg */ + if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; + } + + done_rx: + if (priv->bh_error) + break; + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + goto rx; + if (tx) + goto tx; + + done: + /* Re-enable device interrupts */ + priv->hwbus_ops->lock(priv->hwbus_priv); + __cw1200_irq_enable(priv, 1); + priv->hwbus_ops->unlock(priv->hwbus_priv); + } + + /* Explicitly disable device interrupts */ + priv->hwbus_ops->lock(priv->hwbus_priv); + __cw1200_irq_enable(priv, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + + if (!term) { + pr_err("[BH] Fatal error, exiting.\n"); + priv->bh_error = 1; + /* TODO: schedule_work(recovery) */ + } + return 0; +} diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h new file mode 100644 index 00000000000..af6a4853728 --- /dev/null +++ b/drivers/net/wireless/cw1200/bh.h @@ -0,0 +1,28 @@ +/* + * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_BH_H +#define CW1200_BH_H + +/* extern */ struct cw1200_common; + +int cw1200_register_bh(struct cw1200_common *priv); +void cw1200_unregister_bh(struct cw1200_common *priv); +void cw1200_irq_handler(struct cw1200_common *priv); +void cw1200_bh_wakeup(struct cw1200_common *priv); +int cw1200_bh_suspend(struct cw1200_common *priv); +int cw1200_bh_resume(struct cw1200_common *priv); +/* Must be called from BH thread. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable); +int wsm_release_tx_buffer(struct cw1200_common *priv, int count); + +#endif /* CW1200_BH_H */ diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h new file mode 100644 index 00000000000..1ad7d360252 --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200.h @@ -0,0 +1,323 @@ +/* + * Common private data for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_H +#define CW1200_H + +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <net/mac80211.h> + +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "pm.h" + +/* Forward declarations */ +struct hwbus_ops; +struct task_struct; +struct cw1200_debug_priv; +struct firmware; + +#define CW1200_MAX_CTRL_FRAME_LEN (0x1000) + +#define CW1200_MAX_STA_IN_AP_MODE (5) +#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) +#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) +#define CW1200_MAX_REQUEUE_ATTEMPTS (5) + +#define CW1200_MAX_TID (8) + +#define CW1200_BLOCK_ACK_CNT (30) +#define CW1200_BLOCK_ACK_THLD (800) +#define CW1200_BLOCK_ACK_HIST (3) +#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) + +#define CW1200_JOIN_TIMEOUT (1 * HZ) +#define CW1200_AUTH_TIMEOUT (5 * HZ) + +struct cw1200_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +/* Please keep order */ +enum cw1200_join_status { + CW1200_JOIN_STATUS_PASSIVE = 0, + CW1200_JOIN_STATUS_MONITOR, + CW1200_JOIN_STATUS_JOINING, + CW1200_JOIN_STATUS_PRE_STA, + CW1200_JOIN_STATUS_STA, + CW1200_JOIN_STATUS_IBSS, + CW1200_JOIN_STATUS_AP, +}; + +enum cw1200_link_status { + CW1200_LINK_OFF, + CW1200_LINK_RESERVE, + CW1200_LINK_SOFT, + CW1200_LINK_HARD, + CW1200_LINK_RESET, + CW1200_LINK_RESET_REMAP, +}; + +extern int cw1200_power_mode; +extern const char * const cw1200_fw_types[]; + +struct cw1200_link_entry { + unsigned long timestamp; + enum cw1200_link_status status; + enum cw1200_link_status prev_status; + u8 mac[ETH_ALEN]; + u8 buffered[CW1200_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +struct cw1200_common { + /* interfaces to the rest of the stack */ + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + struct device *pdev; + + /* Statistics */ + struct ieee80211_low_level_stats stats; + + /* Our macaddr */ + u8 mac_addr[ETH_ALEN]; + + /* Hardware interface */ + const struct hwbus_ops *hwbus_ops; + struct hwbus_priv *hwbus_priv; + + /* Hardware information */ + enum { + HIF_9000_SILICON_VERSATILE = 0, + HIF_8601_VERSATILE, + HIF_8601_SILICON, + } hw_type; + enum { + CW1200_HW_REV_CUT10 = 10, + CW1200_HW_REV_CUT11 = 11, + CW1200_HW_REV_CUT20 = 20, + CW1200_HW_REV_CUT22 = 22, + CW1X60_HW_REV = 40, + } hw_revision; + int hw_refclk; + bool hw_have_5ghz; + const struct firmware *sdd; + char *sdd_path; + + struct cw1200_debug_priv *debug; + + struct workqueue_struct *workqueue; + struct mutex conf_mutex; + + struct cw1200_queue tx_queue[4]; + struct cw1200_queue_stats tx_queue_stats; + int tx_burst_idx; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + + /* BBP/MAC state */ + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; + struct ieee80211_channel *channel; + struct wsm_edca_params edca; + struct wsm_tx_queue_params tx_queue_params; + struct wsm_mib_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct cw1200_ht_info ht_info; + struct wsm_set_pm powersave_mode; + struct wsm_set_pm firmware_ps_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + bool cqm_use_rssi; + int cqm_beacon_loss_count; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + int mode; + bool enable_beacon; + int beacon_int; + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_mib_multicast_filter multicast_filter; + bool has_multicast_subscription; + bool disable_beacon_filter; + struct work_struct update_filtering_work; + struct work_struct set_beacon_wakeup_period_work; + + u8 ba_rx_tid_mask; + u8 ba_tx_tid_mask; + + struct cw1200_pm_state pm_state; + + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + bool bt_present; + u8 conf_listen_interval; + u32 listen_interval; + u32 erp_info; + u32 rts_threshold; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + atomic_t bh_suspend; + + struct workqueue_struct *bh_workqueue; + struct work_struct bh_work; + + int bh_error; + wait_queue_head_t bh_wq; + wait_queue_head_t bh_evt_wq; + u8 buf_id_tx; + u8 buf_id_rx; + u8 wsm_rx_seq; + u8 wsm_tx_seq; + int hw_bufs_used; + bool powersave_enabled; + bool device_can_sleep; + + /* Scan status */ + struct cw1200_scan scan; + /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid + * FW issue with sleeping/waking up. + */ + atomic_t recent_scan; + struct delayed_work clear_recent_scan_work; + + /* WSM */ + struct wsm_startup_ind wsm_caps; + struct mutex wsm_cmd_mux; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + int firmware_ready; + atomic_t tx_lock; + + /* WSM debug */ + int wsm_enable_wsm_dumps; + + /* WSM Join */ + enum cw1200_join_status join_status; + u32 pending_frame_id; + bool join_pending; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + struct work_struct join_complete_work; + int join_complete_status; + int join_dtim_period; + bool delayed_unjoin; + + /* TX/RX and security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; + + /* AP powersave */ + u32 link_id_map; + struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + bool aid0_bit_set; + spinlock_t ps_state_lock; /* Protect power save state */ + bool buffered_multicasts; + bool tx_multicast; + struct work_struct set_tim_work; + struct work_struct set_cts_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; + + /* WSM events and CQM implementation */ + spinlock_t event_queue_lock; /* Protect event queue */ + struct list_head event_queue; + struct work_struct event_handler; + + struct delayed_work bss_loss_work; + spinlock_t bss_loss_lock; /* Protect BSS loss state */ + int bss_loss_state; + u32 bss_loss_confirm_id; + int delayed_link_loss; + struct work_struct bss_params_work; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + /* legacy PS mode switch in suspend */ + int ps_mode_switch_in_progress; + wait_queue_head_t ps_mode_switch_done; + + /* Workaround for WFD testcase 6.1.10*/ + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; +}; + +struct cw1200_sta_priv { + int link_id; +}; + +/* interfaces for the drivers */ +int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, + struct hwbus_priv *hwbus, + struct device *pdev, + struct cw1200_common **pself, + int ref_clk, const u8 *macaddr, + const char *sdd_path, bool have_5ghz); +void cw1200_core_release(struct cw1200_common *self); + +#define FWLOAD_BLOCK_SIZE (1024) + +static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) +{ + return cw1200_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) +{ + if (!cw1200_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* CW1200_H */ diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c new file mode 100644 index 00000000000..ebdcdf44f15 --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -0,0 +1,425 @@ +/* + * Mac80211 SDIO driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sdio.h> +#include <net/mac80211.h> + +#include "cw1200.h" +#include "hwbus.h" +#include <linux/platform_data/net-cw1200.h> +#include "hwio.h" + +MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>"); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); +MODULE_LICENSE("GPL"); + +#define SDIO_BLOCK_SIZE (512) + +/* Default platform data for Sagrad modules */ +static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = { + .ref_clk = 38400, + .have_5ghz = false, + .sdd_file = "sdd_sagrad_1091_1098.bin", +}; + +/* Allow platform data to be overridden */ +static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data; + +void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata) +{ + global_plat_data = pdata; +} + +struct hwbus_priv { + struct sdio_func *func; + struct cw1200_common *core; + const struct cw1200_platform_data_sdio *pdata; +}; + +#ifndef SDIO_VENDOR_ID_STE +#define SDIO_VENDOR_ID_STE 0x0020 +#endif + +#ifndef SDIO_DEVICE_ID_STE_CW1200 +#define SDIO_DEVICE_ID_STE_CW1200 0x2280 +#endif + +static const struct sdio_device_id cw1200_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, + { /* end: all zeroes */ }, +}; + +/* hwbus_ops implemetation */ + +static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + return sdio_memcpy_fromio(self->func, dst, addr, count); +} + +static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return sdio_memcpy_toio(self->func, addr, (void *)src, count); +} + +static void cw1200_sdio_lock(struct hwbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void cw1200_sdio_unlock(struct hwbus_priv *self) +{ + sdio_release_host(self->func); +} + +static void cw1200_sdio_irq_handler(struct sdio_func *func) +{ + struct hwbus_priv *self = sdio_get_drvdata(func); + + /* note: sdio_host already claimed here. */ + if (self->core) + cw1200_irq_handler(self->core); +} + +static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id) +{ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) +{ + struct hwbus_priv *self = dev_id; + + if (self->core) { + sdio_claim_host(self->func); + cw1200_irq_handler(self->core); + sdio_release_host(self->func); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int cw1200_request_irq(struct hwbus_priv *self) +{ + int ret; + u8 cccr; + + cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto err; + + /* Master interrupt enable ... */ + cccr |= BIT(0); + + /* ... for our function */ + cccr |= BIT(self->func->num); + + sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto err; + + ret = enable_irq_wake(self->pdata->irq); + if (WARN_ON(ret)) + goto err; + + /* Request the IRQ */ + ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq, + cw1200_gpio_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "cw1200_wlan_irq", self); + if (WARN_ON(ret)) + goto err; + + return 0; + +err: + return ret; +} + +static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ subscribe\n"); + sdio_claim_host(self->func); + if (self->pdata->irq) + ret = cw1200_request_irq(self); + else + ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); + + sdio_release_host(self->func); + return ret; +} + +static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ unsubscribe\n"); + + if (self->pdata->irq) { + disable_irq_wake(self->pdata->irq); + free_irq(self->pdata->irq, self); + } else { + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); + } + return ret; +} + +static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) +{ + if (pdata->reset) { + gpio_set_value(pdata->reset, 0); + msleep(30); /* Min is 2 * CLK32K cycles */ + gpio_free(pdata->reset); + } + + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); + + return 0; +} + +static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) +{ + /* Ensure I/Os are pulled low */ + if (pdata->reset) { + gpio_request(pdata->reset, "cw1200_wlan_reset"); + gpio_direction_output(pdata->reset, 0); + } + if (pdata->powerup) { + gpio_request(pdata->powerup, "cw1200_wlan_powerup"); + gpio_direction_output(pdata->powerup, 0); + } + if (pdata->reset || pdata->powerup) + msleep(10); /* Settle time? */ + + /* Enable 3v3 and 1v8 to hardware */ + if (pdata->power_ctrl) { + if (pdata->power_ctrl(pdata, true)) { + pr_err("power_ctrl() failed!\n"); + return -1; + } + } + + /* Enable CLK32K */ + if (pdata->clk_ctrl) { + if (pdata->clk_ctrl(pdata, true)) { + pr_err("clk_ctrl() failed!\n"); + return -1; + } + msleep(10); /* Delay until clock is stable for 2 cycles */ + } + + /* Enable POWERUP signal */ + if (pdata->powerup) { + gpio_set_value(pdata->powerup, 1); + msleep(250); /* or more..? */ + } + /* Enable RSTn signal */ + if (pdata->reset) { + gpio_set_value(pdata->reset, 1); + msleep(50); /* Or more..? */ + } + return 0; +} + +static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size) +{ + if (self->pdata->no_nptb) + size = round_up(size, SDIO_BLOCK_SIZE); + else + size = sdio_align_size(self->func, size); + + return size; +} + +static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend) +{ + int ret = 0; + + if (self->pdata->irq) + ret = irq_set_irq_wake(self->pdata->irq, suspend); + return ret; +} + +static struct hwbus_ops cw1200_sdio_hwbus_ops = { + .hwbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, + .hwbus_memcpy_toio = cw1200_sdio_memcpy_toio, + .lock = cw1200_sdio_lock, + .unlock = cw1200_sdio_unlock, + .align_size = cw1200_sdio_align_size, + .power_mgmt = cw1200_sdio_pm, +}; + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int cw1200_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct hwbus_priv *self; + int status; + + pr_info("cw1200_wlan_sdio: Probe called\n"); + + /* We are only able to handle the wlan function */ + if (func->num != 0x01) + return -ENODEV; + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + pr_err("Can't allocate SDIO hwbus_priv.\n"); + return -ENOMEM; + } + + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + + self->pdata = global_plat_data; /* FIXME */ + self->func = func; + sdio_set_drvdata(func, self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + status = cw1200_sdio_irq_subscribe(self); + + status = cw1200_core_probe(&cw1200_sdio_hwbus_ops, + self, &func->dev, &self->core, + self->pdata->ref_clk, + self->pdata->macaddr, + self->pdata->sdd_file, + self->pdata->have_5ghz); + if (status) { + cw1200_sdio_irq_unsubscribe(self); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SDIO stack when + * device is disconnected + */ +static void cw1200_sdio_disconnect(struct sdio_func *func) +{ + struct hwbus_priv *self = sdio_get_drvdata(func); + + if (self) { + cw1200_sdio_irq_unsubscribe(self); + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } +} + +#ifdef CONFIG_PM +static int cw1200_sdio_suspend(struct device *dev) +{ + int ret; + struct sdio_func *func = dev_to_sdio_func(dev); + struct hwbus_priv *self = sdio_get_drvdata(func); + + if (!cw1200_can_suspend(self->core)) + return -EAGAIN; + + /* Notify SDIO that CW1200 will remain powered during suspend */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + pr_err("Error setting SDIO pm flags: %i\n", ret); + + return ret; +} + +static int cw1200_sdio_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops cw1200_pm_ops = { + .suspend = cw1200_sdio_suspend, + .resume = cw1200_sdio_resume, +}; +#endif + +static struct sdio_driver sdio_driver = { + .name = "cw1200_wlan_sdio", + .id_table = cw1200_sdio_ids, + .probe = cw1200_sdio_probe, + .remove = cw1200_sdio_disconnect, +#ifdef CONFIG_PM + .drv = { + .pm = &cw1200_pm_ops, + } +#endif +}; + +/* Init Module function -> Called by insmod */ +static int __init cw1200_sdio_init(void) +{ + const struct cw1200_platform_data_sdio *pdata; + int ret; + + /* FIXME -- this won't support multiple devices */ + pdata = global_plat_data; + + if (cw1200_sdio_on(pdata)) { + ret = -1; + goto err; + } + + ret = sdio_register_driver(&sdio_driver); + if (ret) + goto err; + + return 0; + +err: + cw1200_sdio_off(pdata); + return ret; +} + +/* Called at Driver Unloading */ +static void __exit cw1200_sdio_exit(void) +{ + const struct cw1200_platform_data_sdio *pdata; + + /* FIXME -- this won't support multiple devices */ + pdata = global_plat_data; + sdio_unregister_driver(&sdio_driver); + cw1200_sdio_off(pdata); +} + + +module_init(cw1200_sdio_init); +module_exit(cw1200_sdio_exit); diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c new file mode 100644 index 00000000000..d06376014bc --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -0,0 +1,471 @@ +/* + * Mac80211 SPI driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2011, Sagrad Inc. + * Author: Solomon Peachy <speachy@sagrad.com> + * + * Based on cw1200_sdio.c + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <net/mac80211.h> + +#include <linux/spi/spi.h> +#include <linux/device.h> + +#include "cw1200.h" +#include "hwbus.h" +#include <linux/platform_data/net-cw1200.h> +#include "hwio.h" + +MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>"); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:cw1200_wlan_spi"); + +/* #define SPI_DEBUG */ + +struct hwbus_priv { + struct spi_device *func; + struct cw1200_common *core; + const struct cw1200_platform_data_spi *pdata; + spinlock_t lock; /* Serialize all bus operations */ + int claimed; +}; + +#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2) +#define SET_WRITE 0x7FFF /* usage: and operation */ +#define SET_READ 0x8000 /* usage: or operation */ + +/* Notes on byte ordering: + LE: B0 B1 B2 B3 + BE: B3 B2 B1 B0 + + Hardware expects 32-bit data to be written as 16-bit BE words: + + B1 B0 B3 B2 +*/ + +static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + int ret, i; + u16 regaddr; + struct spi_message m; + + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .rx_buf = dst, + .len = count, + }; + + regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; + regaddr |= SET_READ; + regaddr |= (count>>1); + +#ifdef SPI_DEBUG + pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr); +#endif + + /* Header is LE16 */ + regaddr = cpu_to_le16(regaddr); + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + if (self->func->bits_per_word == 8) +#endif + regaddr = swab16(regaddr); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(self->func, &m); + +#ifdef SPI_DEBUG + pr_info("READ : "); + for (i = 0; i < t_addr.len; i++) + printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); + printk(" : "); + for (i = 0; i < t_msg.len; i++) + printk("%02x ", ((u8 *)t_msg.rx_buf)[i]); + printk("\n"); +#endif + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)dst; + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + + return ret; +} + +static int cw1200_spi_memcpy_toio(struct hwbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + int rval, i; + u16 regaddr; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .tx_buf = src, + .len = count, + }; + struct spi_message m; + + regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; + regaddr &= SET_WRITE; + regaddr |= (count>>1); + +#ifdef SPI_DEBUG + pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, regaddr); +#endif + + /* Header is LE16 */ + regaddr = cpu_to_le16(regaddr); + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)src; + regaddr = swab16(regaddr); + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + +#ifdef SPI_DEBUG + pr_info("WRITE: "); + for (i = 0; i < t_addr.len; i++) + printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); + printk(" : "); + for (i = 0; i < t_msg.len; i++) + printk("%02x ", ((u8 *)t_msg.tx_buf)[i]); + printk("\n"); +#endif + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + rval = spi_sync(self->func, &m); + +#ifdef SPI_DEBUG + pr_info("WROTE: %d\n", m.actual_length); +#endif + +#if defined(__LITTLE_ENDIAN) + /* We have to byteswap if the SPI bus is limited to 8b operation */ + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)src; + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + return rval; +} + +static void cw1200_spi_lock(struct hwbus_priv *self) +{ + unsigned long flags; + + might_sleep(); + + spin_lock_irqsave(&self->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!self->claimed) + break; + spin_unlock_irqrestore(&self->lock, flags); + schedule(); + spin_lock_irqsave(&self->lock, flags); + } + set_current_state(TASK_RUNNING); + self->claimed = 1; + spin_unlock_irqrestore(&self->lock, flags); + + return; +} + +static void cw1200_spi_unlock(struct hwbus_priv *self) +{ + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + self->claimed = 0; + spin_unlock_irqrestore(&self->lock, flags); + return; +} + +static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) +{ + struct hwbus_priv *self = dev_id; + + if (self->core) { + cw1200_irq_handler(self->core); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int cw1200_spi_irq_subscribe(struct hwbus_priv *self) +{ + int ret; + + pr_debug("SW IRQ subscribe\n"); + + ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler, + IRQF_TRIGGER_HIGH, + "cw1200_wlan_irq", self); + if (WARN_ON(ret < 0)) + goto exit; + + ret = enable_irq_wake(self->func->irq); + if (WARN_ON(ret)) + goto free_irq; + + return 0; + +free_irq: + free_irq(self->func->irq, self); +exit: + return ret; +} + +static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ unsubscribe\n"); + disable_irq_wake(self->func->irq); + free_irq(self->func->irq, self); + + return ret; +} + +static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) +{ + if (pdata->reset) { + gpio_set_value(pdata->reset, 0); + msleep(30); /* Min is 2 * CLK32K cycles */ + gpio_free(pdata->reset); + } + + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); + + return 0; +} + +static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) +{ + /* Ensure I/Os are pulled low */ + if (pdata->reset) { + gpio_request(pdata->reset, "cw1200_wlan_reset"); + gpio_direction_output(pdata->reset, 0); + } + if (pdata->powerup) { + gpio_request(pdata->powerup, "cw1200_wlan_powerup"); + gpio_direction_output(pdata->powerup, 0); + } + if (pdata->reset || pdata->powerup) + msleep(10); /* Settle time? */ + + /* Enable 3v3 and 1v8 to hardware */ + if (pdata->power_ctrl) { + if (pdata->power_ctrl(pdata, true)) { + pr_err("power_ctrl() failed!\n"); + return -1; + } + } + + /* Enable CLK32K */ + if (pdata->clk_ctrl) { + if (pdata->clk_ctrl(pdata, true)) { + pr_err("clk_ctrl() failed!\n"); + return -1; + } + msleep(10); /* Delay until clock is stable for 2 cycles */ + } + + /* Enable POWERUP signal */ + if (pdata->powerup) { + gpio_set_value(pdata->powerup, 1); + msleep(250); /* or more..? */ + } + /* Enable RSTn signal */ + if (pdata->reset) { + gpio_set_value(pdata->reset, 1); + msleep(50); /* Or more..? */ + } + return 0; +} + +static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size) +{ + return size & 1 ? size + 1 : size; +} + +static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend) +{ + return irq_set_irq_wake(self->func->irq, suspend); +} + +static struct hwbus_ops cw1200_spi_hwbus_ops = { + .hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio, + .hwbus_memcpy_toio = cw1200_spi_memcpy_toio, + .lock = cw1200_spi_lock, + .unlock = cw1200_spi_unlock, + .align_size = cw1200_spi_align_size, + .power_mgmt = cw1200_spi_pm, +}; + +/* Probe Function to be called by SPI stack when device is discovered */ +static int cw1200_spi_probe(struct spi_device *func) +{ + const struct cw1200_platform_data_spi *plat_data = + func->dev.platform_data; + struct hwbus_priv *self; + int status; + + /* Sanity check speed */ + if (func->max_speed_hz > 52000000) + func->max_speed_hz = 52000000; + if (func->max_speed_hz < 1000000) + func->max_speed_hz = 1000000; + + /* Fix up transfer size */ + if (plat_data->spi_bits_per_word) + func->bits_per_word = plat_data->spi_bits_per_word; + if (!func->bits_per_word) + func->bits_per_word = 16; + + /* And finally.. */ + func->mode = SPI_MODE_0; + + pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n", + func->chip_select, func->mode, func->bits_per_word, + func->max_speed_hz); + + if (cw1200_spi_on(plat_data)) { + pr_err("spi_on() failed!\n"); + return -1; + } + + if (spi_setup(func)) { + pr_err("spi_setup() failed!\n"); + return -1; + } + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + pr_err("Can't allocate SPI hwbus_priv."); + return -ENOMEM; + } + + self->pdata = plat_data; + self->func = func; + spin_lock_init(&self->lock); + + spi_set_drvdata(func, self); + + status = cw1200_spi_irq_subscribe(self); + + status = cw1200_core_probe(&cw1200_spi_hwbus_ops, + self, &func->dev, &self->core, + self->pdata->ref_clk, + self->pdata->macaddr, + self->pdata->sdd_file, + self->pdata->have_5ghz); + + if (status) { + cw1200_spi_irq_unsubscribe(self); + cw1200_spi_off(plat_data); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SPI stack when device is disconnected */ +static int cw1200_spi_disconnect(struct spi_device *func) +{ + struct hwbus_priv *self = spi_get_drvdata(func); + + if (self) { + cw1200_spi_irq_unsubscribe(self); + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + kfree(self); + } + cw1200_spi_off(func->dev.platform_data); + + return 0; +} + +#ifdef CONFIG_PM +static int cw1200_spi_suspend(struct device *dev, pm_message_t state) +{ + struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); + + if (!cw1200_can_suspend(self->core)) + return -EAGAIN; + + /* XXX notify host that we have to keep CW1200 powered on? */ + return 0; +} + +static int cw1200_spi_resume(struct device *dev) +{ + return 0; +} +#endif + +static struct spi_driver spi_driver = { + .probe = cw1200_spi_probe, + .remove = cw1200_spi_disconnect, + .driver = { + .name = "cw1200_wlan_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .suspend = cw1200_spi_suspend, + .resume = cw1200_spi_resume, +#endif + }, +}; + +module_spi_driver(spi_driver); diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c new file mode 100644 index 00000000000..e323b4d5433 --- /dev/null +++ b/drivers/net/wireless/cw1200/debug.c @@ -0,0 +1,428 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * DebugFS code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include "cw1200.h" +#include "debug.h" +#include "fwio.h" + +/* join_status */ +static const char * const cw1200_debug_join_status[] = { + "passive", + "monitor", + "station (joining)", + "station (not authenticated yet)", + "station", + "adhoc", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const cw1200_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + + +static const char * const cw1200_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", +}; + +static const char *cw1200_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "adhoc"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void cw1200_queue_status_show(struct seq_file *seq, + struct cw1200_queue *q) +{ + int i; + seq_printf(seq, "Queue %d:\n", q->queue_id); + seq_printf(seq, " capacity: %zu\n", q->capacity); + seq_printf(seq, " queued: %zu\n", q->num_queued); + seq_printf(seq, " pending: %zu\n", q->num_pending); + seq_printf(seq, " sent: %zu\n", q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[i]); + seq_printf(seq, "<-%zu\n", q->stats->map_capacity); +} + +static void cw1200_debug_print_map(struct seq_file *seq, + struct cw1200_common *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1); +} + +static int cw1200_status_show(struct seq_file *seq, void *v) +{ + int i; + struct list_head *item; + struct cw1200_common *priv = seq->private; + struct cw1200_debug_priv *d = priv->debug; + + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + priv->wsm_caps.hw_id, + priv->wsm_caps.hw_subid); + seq_printf(seq, "Firmware: %s %d.%d\n", + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build); + seq_printf(seq, "FW API: %d\n", + priv->wsm_caps.fw_api); + seq_printf(seq, "FW caps: 0x%.4X\n", + priv->wsm_caps.fw_cap); + seq_printf(seq, "FW label: '%s'\n", + priv->wsm_caps.fw_label); + seq_printf(seq, "Mode: %s%s\n", + cw1200_debug_mode(priv->mode), + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Join state: %s\n", + cw1200_debug_join_status[priv->join_status]); + if (priv->channel) + seq_printf(seq, "Channel: %d%s\n", + priv->channel->hw_value, + priv->channel_switch_in_progress ? + " (switching)" : ""); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) + seq_puts(seq, "Filter: fcs\n"); + if (priv->rx_filter.bssid) + seq_puts(seq, "Filter: bssid\n"); + if (!priv->disable_beacon_filter) + seq_puts(seq, "Filter: beacons\n"); + + if (priv->enable_beacon || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "Beaconing: %s\n", + priv->enable_beacon ? + "enabled" : "disabled"); + + for (i = 0; i < 4; ++i) + seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, + priv->edca.params[i].cwmin, + priv->edca.params[i].cwmax, + priv->edca.params[i].aifns, + priv->edca.params[i].txop_limit, + priv->edca.params[i].max_rx_lifetime); + + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + static const char *pm_mode = "unknown"; + switch (priv->powersave_mode.mode) { + case WSM_PSM_ACTIVE: + pm_mode = "off"; + break; + case WSM_PSM_PS: + pm_mode = "on"; + break; + case WSM_PSM_FAST_PS: + pm_mode = "dynamic"; + break; + } + seq_printf(seq, "Preamble: %s\n", + cw1200_debug_preamble[priv->association_mode.preamble]); + seq_printf(seq, "AMPDU spcn: %d\n", + priv->association_mode.mpdu_start_spacing); + seq_printf(seq, "Basic rate: 0x%.8X\n", + le32_to_cpu(priv->association_mode.basic_rate_set)); + seq_printf(seq, "Bss lost: %d beacons\n", + priv->bss_params.beacon_lost_count); + seq_printf(seq, "AID: %d\n", + priv->bss_params.aid); + seq_printf(seq, "Rates: 0x%.8X\n", + priv->bss_params.operational_rate_set); + seq_printf(seq, "Powersave: %s\n", pm_mode); + } + seq_printf(seq, "HT: %s\n", + cw1200_is_ht(&priv->ht_info) ? "on" : "off"); + if (cw1200_is_ht(&priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + cw1200_ht_ampdu_density(&priv->ht_info)); + } + seq_printf(seq, "RSSI thold: %d\n", + priv->cqm_rssi_thold); + seq_printf(seq, "RSSI hyst: %d\n", + priv->cqm_rssi_hyst); + seq_printf(seq, "Long retr: %d\n", + priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + priv->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + cw1200_queue_status_show(seq, &priv->tx_queue[i]); + seq_puts(seq, "\n"); + } + + cw1200_debug_print_map(seq, priv, "Link map: ", + priv->link_id_map); + cw1200_debug_print_map(seq, priv, "Asleep map: ", + priv->sta_asleep_mask); + cw1200_debug_print_map(seq, priv, "PSPOLL map: ", + priv->pspoll_mask); + + seq_puts(seq, "\n"); + + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (priv->link_id_db[i].status) { + seq_printf(seq, "Link %d: %s, %pM\n", + i + 1, + cw1200_debug_link_id[priv->link_id_db[i].status], + priv->link_id_db[i].mac); + } + } + + seq_puts(seq, "\n"); + + seq_printf(seq, "BH status: %s\n", + atomic_read(&priv->bh_term) ? "terminated" : "alive"); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&priv->bh_tx)); + if (priv->bh_error) + seq_printf(seq, "BH errcode: %d\n", + priv->bh_error); + seq_printf(seq, "TX bufs: %d x %d bytes\n", + priv->wsm_caps.input_buffers, + priv->wsm_caps.input_buffer_size); + seq_printf(seq, "Used bufs: %d\n", + priv->hw_bufs_used); + seq_printf(seq, "Powermgmt: %s\n", + priv->powersave_enabled ? "on" : "off"); + seq_printf(seq, "Device: %s\n", + priv->device_can_sleep ? "asleep" : "awake"); + + spin_lock(&priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n", + priv->wsm_cmd.cmd, priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + priv->wsm_cmd.ret); + spin_unlock(&priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&priv->tx_lock)); + + seq_printf(seq, "TXed: %d\n", + d->tx); + seq_printf(seq, "AGG TXed: %d\n", + d->tx_agg); + seq_printf(seq, "MULTI TXed: %d (%d)\n", + d->tx_multi, d->tx_multi_frames); + seq_printf(seq, "RXed: %d\n", + d->rx); + seq_printf(seq, "AGG RXed: %d\n", + d->rx_agg); + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "TX align: %d\n", + d->tx_align); + seq_printf(seq, "TX burst: %d\n", + d->tx_burst); + seq_printf(seq, "TX TTL: %d\n", + d->tx_ttl); + seq_printf(seq, "Scan: %s\n", + atomic_read(&priv->scan.in_progress) ? "active" : "idle"); + + return 0; +} + +static int cw1200_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_status_show, + inode->i_private); +} + +static const struct file_operations fops_status = { + .open = cw1200_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int cw1200_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct cw1200_common *priv = seq->private; + struct wsm_mib_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + if (ret) + return ret; + +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.name)) + + PUT_COUNTER("\t\t", plcp_errors); + PUT_COUNTER("\t\t", fcs_errors); + PUT_COUNTER("\t\t", tx_packets); + PUT_COUNTER("\t\t", rx_packets); + PUT_COUNTER("\t\t", rx_packet_errors); + PUT_COUNTER("\t", rx_decryption_failures); + PUT_COUNTER("\t\t", rx_mic_failures); + PUT_COUNTER("\t", rx_no_key_failures); + PUT_COUNTER("\t", tx_multicast_frames); + PUT_COUNTER("\t", tx_frames_success); + PUT_COUNTER("\t", tx_frame_failures); + PUT_COUNTER("\t", tx_frames_retried); + PUT_COUNTER("\t", tx_frames_multi_retried); + PUT_COUNTER("\t", rx_frame_duplicates); + PUT_COUNTER("\t\t", rts_success); + PUT_COUNTER("\t\t", rts_failures); + PUT_COUNTER("\t\t", ack_failures); + PUT_COUNTER("\t", rx_multicast_frames); + PUT_COUNTER("\t", rx_frames_success); + PUT_COUNTER("\t", rx_cmac_icv_errors); + PUT_COUNTER("\t\t", rx_cmac_replays); + PUT_COUNTER("\t", rx_mgmt_ccmp_replays); + +#undef PUT_COUNTER + + return 0; +} + +static int cw1200_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = cw1200_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static ssize_t cw1200_wsm_dumps(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (buf[0] == '1') + priv->wsm_enable_wsm_dumps = 1; + else + priv->wsm_enable_wsm_dumps = 0; + + return count; +} + +static const struct file_operations fops_wsm_dumps = { + .open = simple_open, + .write = cw1200_wsm_dumps, + .llseek = default_llseek, +}; + +int cw1200_debug_init(struct cw1200_common *priv) +{ + int ret = -ENOMEM; + struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), + GFP_KERNEL); + priv->debug = d; + if (!d) + return ret; + + d->debugfs_phy = debugfs_create_dir("cw1200", + priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + goto err; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status)) + goto err; + + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + priv, &fops_counters)) + goto err; + + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + priv, &fops_wsm_dumps)) + goto err; + + return 0; + +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; +} + +void cw1200_debug_release(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = priv->debug; + if (d) { + debugfs_remove_recursive(d->debugfs_phy); + priv->debug = NULL; + kfree(d); + } +} diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h new file mode 100644 index 00000000000..b525aba53bf --- /dev/null +++ b/drivers/net/wireless/cw1200/debug.h @@ -0,0 +1,93 @@ +/* + * DebugFS code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_DEBUG_H_INCLUDED +#define CW1200_DEBUG_H_INCLUDED + +struct cw1200_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int rx; + int rx_agg; + int tx_multi; + int tx_multi_frames; + int tx_cache_miss; + int tx_align; + int tx_ttl; + int tx_burst; + int ba_cnt; + int ba_acc; + int ba_cnt_rx; + int ba_acc_rx; +}; + +int cw1200_debug_init(struct cw1200_common *priv); +void cw1200_debug_release(struct cw1200_common *priv); + +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ + ++priv->debug->tx; +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ + ++priv->debug->tx_agg; +} + +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ + ++priv->debug->rx; +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ + ++priv->debug->rx_agg; +} + +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ + ++priv->debug->tx_cache_miss; +} + +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) +{ + ++priv->debug->tx_align; +} + +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ + ++priv->debug->tx_ttl; +} + +static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) +{ + ++priv->debug->tx_burst; +} + +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc, + int ba_cnt_rx, int ba_acc_rx) +{ + priv->debug->ba_cnt = ba_cnt; + priv->debug->ba_acc = ba_acc; + priv->debug->ba_cnt_rx = ba_cnt_rx; + priv->debug->ba_acc_rx = ba_acc_rx; +} + +#endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c new file mode 100644 index 00000000000..acdff0f7f95 --- /dev/null +++ b/drivers/net/wireless/cw1200/fwio.c @@ -0,0 +1,520 @@ +/* + * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/firmware.h> + +#include "cw1200.h" +#include "fwio.h" +#include "hwio.h" +#include "hwbus.h" +#include "bh.h" + +static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) +{ + int hw_type = -1; + u32 silicon_type = (config_reg_val >> 24) & 0x7; + u32 silicon_vers = (config_reg_val >> 31) & 0x1; + + switch (silicon_type) { + case 0x00: + *major_revision = 1; + hw_type = HIF_9000_SILICON_VERSATILE; + break; + case 0x01: + case 0x02: /* CW1x00 */ + case 0x04: /* CW1x60 */ + *major_revision = silicon_type; + if (silicon_vers) + hw_type = HIF_8601_VERSATILE; + else + hw_type = HIF_8601_SILICON; + break; + default: + break; + } + + return hw_type; +} + +static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) +{ + int ret, block, num_blocks; + unsigned i; + u32 val32; + u32 put = 0, get = 0; + u8 *buf = NULL; + const char *fw_path; + const struct firmware *firmware = NULL; + + /* Macroses are local. */ +#define APB_WRITE(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ + if (ret < 0) \ + goto error; \ + } while (0) +#define APB_READ(reg, val) \ + do { \ + ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ + if (ret < 0) \ + goto error; \ + } while (0) +#define REG_WRITE(reg, val) \ + do { \ + ret = cw1200_reg_write_32(priv, (reg), (val)); \ + if (ret < 0) \ + goto error; \ + } while (0) +#define REG_READ(reg, val) \ + do { \ + ret = cw1200_reg_read_32(priv, (reg), &(val)); \ + if (ret < 0) \ + goto error; \ + } while (0) + + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + fw_path = FIRMWARE_CUT10; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + fw_path = FIRMWARE_CUT11; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + fw_path = FIRMWARE_CUT20; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_20; + break; + case CW1200_HW_REV_CUT22: + fw_path = FIRMWARE_CUT22; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_22; + break; + case CW1X60_HW_REV: + fw_path = FIRMWARE_CW1X60; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_CW1X60; + break; + default: + pr_err("Invalid silicon revision %d.\n", priv->hw_revision); + return -EINVAL; + } + + /* Initialize common registers */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); + APB_WRITE(DOWNLOAD_PUT_REG, 0); + APB_WRITE(DOWNLOAD_GET_REG, 0); + APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); + APB_WRITE(DOWNLOAD_FLAGS_REG, 0); + + /* Write the NOP Instruction */ + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + + /* Release CPU from RESET */ + REG_READ(ST90TDS_CONFIG_REG_ID, val32); + val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Enable Clock */ + val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Load a firmware file */ + ret = request_firmware(&firmware, fw_path, priv->pdev); + if (ret) { + pr_err("Can't load firmware file %s.\n", fw_path); + goto error; + } + + buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + if (!buf) { + pr_err("Can't allocate firmware load buffer.\n"); + ret = -ENOMEM; + goto error; + } + + /* Check if the bootloader is ready */ + for (i = 0; i < 100; i += 1 + i / 2) { + APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); + if (val32 == DOWNLOAD_I_AM_HERE) + break; + mdelay(i); + } /* End of for loop */ + + if (val32 != DOWNLOAD_I_AM_HERE) { + pr_err("Bootloader is not ready.\n"); + ret = -ETIMEDOUT; + goto error; + } + + /* Calculcate number of download blocks */ + num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; + + /* Updating the length in Download Ctrl Area */ + val32 = firmware->size; /* Explicit cast from size_t to u32 */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); + + /* Firmware downloading loop */ + for (block = 0; block < num_blocks; block++) { + size_t tx_size; + size_t block_size; + + /* check the download status */ + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) { + pr_err("Bootloader reported error %d.\n", val32); + ret = -EIO; + goto error; + } + + /* loop until put - get <= 24K */ + for (i = 0; i < 100; i++) { + APB_READ(DOWNLOAD_GET_REG, get); + if ((put - get) <= + (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) + break; + mdelay(i); + } + + if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { + pr_err("Timeout waiting for FIFO.\n"); + ret = -ETIMEDOUT; + goto error; + } + + /* calculate the block size */ + tx_size = block_size = min((size_t)(firmware->size - put), + (size_t)DOWNLOAD_BLOCK_SIZE); + + memcpy(buf, &firmware->data[put], block_size); + if (block_size < DOWNLOAD_BLOCK_SIZE) { + memset(&buf[block_size], 0, + DOWNLOAD_BLOCK_SIZE - block_size); + tx_size = DOWNLOAD_BLOCK_SIZE; + } + + /* send the block to sram */ + ret = cw1200_apb_write(priv, + CW1200_APB(DOWNLOAD_FIFO_OFFSET + + (put & (DOWNLOAD_FIFO_SIZE - 1))), + buf, tx_size); + if (ret < 0) { + pr_err("Can't write firmware block @ %d!\n", + put & (DOWNLOAD_FIFO_SIZE - 1)); + goto error; + } + + /* update the put register */ + put += block_size; + APB_WRITE(DOWNLOAD_PUT_REG, put); + } /* End of firmware download loop */ + + /* Wait for the download completion */ + for (i = 0; i < 300; i += 1 + i / 2) { + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) + break; + mdelay(i); + } + if (val32 != DOWNLOAD_SUCCESS) { + pr_err("Wait for download completion failed: 0x%.8X\n", val32); + ret = -ETIMEDOUT; + goto error; + } else { + pr_info("Firmware download completed.\n"); + ret = 0; + } + +error: + kfree(buf); + if (firmware) + release_firmware(firmware); + return ret; + +#undef APB_WRITE +#undef APB_READ +#undef REG_WRITE +#undef REG_READ +} + + +static int config_reg_read(struct cw1200_common *priv, u32 *val) +{ + switch (priv->hw_type) { + case HIF_9000_SILICON_VERSATILE: { + u16 val16; + int ret = cw1200_reg_read_16(priv, + ST90TDS_CONFIG_REG_ID, + &val16); + if (ret < 0) + return ret; + *val = val16; + return 0; + } + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + default: + cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val); + break; + } + return 0; +} + +static int config_reg_write(struct cw1200_common *priv, u32 val) +{ + switch (priv->hw_type) { + case HIF_9000_SILICON_VERSATILE: + return cw1200_reg_write_16(priv, + ST90TDS_CONFIG_REG_ID, + (u16)val); + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + default: + return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val); + break; + } + return 0; +} + +int cw1200_load_firmware(struct cw1200_common *priv) +{ + int ret; + int i; + u32 val32; + u16 val16; + int major_revision = -1; + + /* Read CONFIG Register */ + ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + if (val32 == 0 || val32 == 0xffffffff) { + pr_err("Bad config register value (0x%08x)\n", val32); + ret = -EIO; + goto out; + } + + priv->hw_type = cw1200_get_hw_type(val32, &major_revision); + if (priv->hw_type < 0) { + pr_err("Can't deduce hardware type.\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Set DPLL Reg value, and read back to confirm writes work */ + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, + cw1200_dpll_from_clk(priv->hw_refclk)); + if (ret < 0) { + pr_err("Can't write DPLL register.\n"); + goto out; + } + + msleep(20); + + ret = cw1200_reg_read_32(priv, + ST90TDS_TSET_GEN_R_W_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read DPLL register.\n"); + goto out; + } + + if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) { + pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n", + cw1200_dpll_from_clk(priv->hw_refclk), val32); + ret = -EIO; + goto out; + } + + /* Set wakeup bit in device */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + pr_err("set_wakeup: can't read control register.\n"); + goto out; + } + + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + val16 | ST90TDS_CONT_WUP_BIT); + if (ret < 0) { + pr_err("set_wakeup: can't write control register.\n"); + goto out; + } + + /* Wait for wakeup */ + for (i = 0; i < 300; i += (1 + i / 2)) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + pr_err("wait_for_wakeup: can't read control register.\n"); + goto out; + } + + if (val16 & ST90TDS_CONT_RDY_BIT) + break; + + msleep(i); + } + + if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { + pr_err("wait_for_wakeup: device is not responding.\n"); + ret = -ETIMEDOUT; + goto out; + } + + switch (major_revision) { + case 1: + /* CW1200 Hardware detection logic : Check for CUT1.1 */ + ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); + if (ret) { + pr_err("HW detection: can't read CUT ID.\n"); + goto out; + } + + switch (val32) { + case CW1200_CUT_11_ID_STR: + pr_info("CW1x00 Cut 1.1 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT11; + break; + default: + pr_info("CW1x00 Cut 1.0 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT10; + break; + } + + /* According to ST-E, CUT<2.0 has busted BA TID0-3. + Just disable it entirely... + */ + priv->ba_rx_tid_mask = 0; + priv->ba_tx_tid_mask = 0; + break; + case 2: { + u32 ar1, ar2, ar3; + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); + if (ret) { + pr_err("(1) HW detection: can't read CUT ID\n"); + goto out; + } + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); + if (ret) { + pr_err("(2) HW detection: can't read CUT ID.\n"); + goto out; + } + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); + if (ret) { + pr_err("(3) HW detection: can't read CUT ID.\n"); + goto out; + } + + if (ar1 == CW1200_CUT_22_ID_STR1 && + ar2 == CW1200_CUT_22_ID_STR2 && + ar3 == CW1200_CUT_22_ID_STR3) { + pr_info("CW1x00 Cut 2.2 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT22; + } else { + pr_info("CW1x00 Cut 2.0 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT20; + } + break; + } + case 4: + pr_info("CW1x60 silicon detected.\n"); + priv->hw_revision = CW1X60_HW_REV; + break; + default: + pr_err("Unsupported silicon major revision %d.\n", + major_revision); + ret = -ENOTSUPP; + goto out; + } + + /* Checking for access mode */ + ret = config_reg_read(priv, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) { + pr_err("Device is already in QUEUE mode!\n"); + ret = -EINVAL; + goto out; + } + + switch (priv->hw_type) { + case HIF_8601_SILICON: + if (priv->hw_revision == CW1X60_HW_REV) { + pr_err("Can't handle CW1160/1260 firmware load yet.\n"); + ret = -ENOTSUPP; + goto out; + } + ret = cw1200_load_firmware_cw1200(priv); + break; + default: + pr_err("Can't perform firmware load for hw type %d.\n", + priv->hw_type); + ret = -ENOTSUPP; + goto out; + } + if (ret < 0) { + pr_err("Firmware load error.\n"); + goto out; + } + + /* Enable interrupt signalling */ + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_irq_enable(priv, 1); + priv->hwbus_ops->unlock(priv->hwbus_priv); + if (ret < 0) + goto unsubscribe; + + /* Configure device for MESSSAGE MODE */ + ret = config_reg_read(priv, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto unsubscribe; + } + ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); + if (ret < 0) { + pr_err("Can't write config register.\n"); + goto unsubscribe; + } + + /* Unless we read the CONFIG Register we are + * not able to get an interrupt + */ + mdelay(10); + config_reg_read(priv, &val32); + +out: + return ret; + +unsubscribe: + /* Disable interrupt signalling */ + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_irq_enable(priv, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h new file mode 100644 index 00000000000..ea3099362cd --- /dev/null +++ b/drivers/net/wireless/cw1200/fwio.h @@ -0,0 +1,39 @@ +/* + * Firmware API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define BOOTLOADER_CW1X60 "boot_cw1x60.bin" +#define FIRMWARE_CW1X60 "wsm_cw1x60.bin" +#define FIRMWARE_CUT22 "wsm_22.bin" +#define FIRMWARE_CUT20 "wsm_20.bin" +#define FIRMWARE_CUT11 "wsm_11.bin" +#define FIRMWARE_CUT10 "wsm_10.bin" +#define SDD_FILE_CW1X60 "sdd_cw1x60.bin" +#define SDD_FILE_22 "sdd_22.bin" +#define SDD_FILE_20 "sdd_20.bin" +#define SDD_FILE_11 "sdd_11.bin" +#define SDD_FILE_10 "sdd_10.bin" + +int cw1200_load_firmware(struct cw1200_common *priv); + +/* SDD definitions */ +#define SDD_PTA_CFG_ELT_ID 0xEB +#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5 +u32 cw1200_dpll_from_clk(u16 clk); + +#endif diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/cw1200/hwbus.h new file mode 100644 index 00000000000..8b2fc831c3d --- /dev/null +++ b/drivers/net/wireless/cw1200/hwbus.h @@ -0,0 +1,33 @@ +/* + * Common hwbus abstraction layer interface for cw1200 wireless driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HWBUS_H +#define CW1200_HWBUS_H + +struct hwbus_priv; + +void cw1200_irq_handler(struct cw1200_common *priv); + +/* This MUST be wrapped with hwbus_ops->lock/unlock! */ +int __cw1200_irq_enable(struct cw1200_common *priv, int enable); + +struct hwbus_ops { + int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr, + void *dst, int count); + int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct hwbus_priv *self); + void (*unlock)(struct hwbus_priv *self); + size_t (*align_size)(struct hwbus_priv *self, size_t size); + int (*power_mgmt)(struct hwbus_priv *self, bool suspend); +}; + +#endif /* CW1200_HWBUS_H */ diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c new file mode 100644 index 00000000000..ff230b7aeed --- /dev/null +++ b/drivers/net/wireless/cw1200/hwio.c @@ -0,0 +1,312 @@ +/* + * Low-level device IO routines for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/types.h> + +#include "cw1200.h" +#include "hwio.h" +#include "hwbus.h" + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) +#define MAX_RETRY 3 + + +static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit; + + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + pr_err("buffer is not aligned.\n"); + return -EINVAL; + } + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit; + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + __le32 tmp; + int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); + *val = le32_to_cpu(tmp); + return i; +} + +static inline int __cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + __le32 tmp = cpu_to_le32(val); + return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); +} + +static inline int __cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + __le16 tmp; + int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); + *val = le16_to_cpu(tmp); + return i; +} + +static inline int __cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + __le16 tmp = cpu_to_le16(val); + return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); +} + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, + size_t buf_len) +{ + int ret; + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, + size_t buf_len) +{ + int ret; + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) +{ + int ret, retry = 1; + int buf_id_rx = priv->buf_id_rx; + + priv->hwbus_ops->lock(priv->hwbus_priv); + + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_read(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + priv->buf_id_rx = buf_id_rx; + break; + } else { + retry++; + mdelay(1); + pr_err("error :[%d]\n", ret); + } + } + + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_data_write(struct cw1200_common *priv, const void *buf, + size_t buf_len) +{ + int ret, retry = 1; + int buf_id_tx = priv->buf_id_tx; + + priv->hwbus_ops->lock(priv->hwbus_priv); + + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_write(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + priv->buf_id_tx = buf_id_tx; + break; + } else { + retry++; + mdelay(1); + pr_err("error :[%d]\n", ret); + } + } + + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + pr_err("Can't read more than 0xfff words.\n"); + return -EINVAL; + } + + priv->hwbus_ops->lock(priv->hwbus_priv); + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + pr_err("Can't write address register.\n"); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + /* Set PREFETCH bit */ + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, + val32 | prefetch); + if (ret < 0) { + pr_err("Can't write prefetch bit.\n"); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't check prefetch bit.\n"); + goto out; + } + if (!(val32 & prefetch)) + break; + + mdelay(i); + } + + if (val32 & prefetch) { + pr_err("Prefetch bit is not cleared.\n"); + goto out; + } + + /* Read data port */ + ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); + if (ret < 0) { + pr_err("Can't read data port.\n"); + goto out; + } + +out: + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + pr_err("Can't write more than 0xfff words.\n"); + return -EINVAL; + } + + priv->hwbus_ops->lock(priv->hwbus_priv); + + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + pr_err("Can't write address register.\n"); + goto out; + } + + /* Write data port */ + ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, + buf, buf_len, 0); + if (ret < 0) { + pr_err("Can't write data port.\n"); + goto out; + } + +out: + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int __cw1200_irq_enable(struct cw1200_common *priv, int enable) +{ + u32 val32; + u16 val16; + int ret; + + if (HIF_8601_SILICON == priv->hw_type) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + return ret; + } + + if (enable) + val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE; + else + val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE; + + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32); + if (ret < 0) { + pr_err("Can't write config register.\n"); + return ret; + } + } else { + ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); + if (ret < 0) { + pr_err("Can't read control register.\n"); + return ret; + } + + if (enable) + val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE; + else + val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE; + + ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16); + if (ret < 0) { + pr_err("Can't write control register.\n"); + return ret; + } + } + return 0; +} diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h new file mode 100644 index 00000000000..ddf52669dc5 --- /dev/null +++ b/drivers/net/wireless/cw1200/hwio.h @@ -0,0 +1,247 @@ +/* + * Low-level API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HWIO_H_INCLUDED +#define CW1200_HWIO_H_INCLUDED + +/* extern */ struct cw1200_common; + +#define CW1200_CUT_11_ID_STR (0x302E3830) +#define CW1200_CUT_22_ID_STR1 (0x302e3132) +#define CW1200_CUT_22_ID_STR2 (0x32302e30) +#define CW1200_CUT_22_ID_STR3 (0x3335) +#define CW1200_CUT_ID_ADDR (0xFFF17F90) +#define CW1200_CUT2_ID_ADDR (0xFFF1FF90) + +/* Download control area */ +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 image_size; + /* downloading flags */ + u32 flags; + /* No. of bytes put into the download, init & updated by host */ + u32 put; + /* last traced program counter, last ARM reg_pc */ + u32 trace_pc; + /* No. of bytes read from the download, host init, device updates */ + u32 get; + /* r0, boot losader status, host init to pending, device updates */ + u32 status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data)) +#define DOWNLOAD_DEBUG_DATA_LEN (108) + +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + + +#define SYS_BASE_ADDR_SILICON (0) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) + +#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* Device register definitions */ + +/* WBF - SPI Register Addresses */ +#define ST90TDS_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define ST90TDS_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define ST90TDS_FRAME_OUT_REG_ID (0x0007) +#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) +#define ST90TDS_CONT_WUP_BIT (BIT(12)) +#define ST90TDS_CONT_RDY_BIT (BIT(13)) +#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) +#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) +#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) +#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) +#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) +#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) +#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) +#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) +#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11)) +#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13)) +/* cpu reset */ +#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) +#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ +#define ST90TDS_CONF_IRQ_ENABLE (BIT(16)) +#define ST90TDS_CONF_RDY_ENABLE (BIT(17)) +#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +int cw1200_data_read(struct cw1200_common *priv, + void *buf, size_t buf_len); +int cw1200_data_write(struct cw1200_common *priv, + const void *buf, size_t buf_len); + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len); +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len); + +static inline int cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + __le32 tmp; + int i; + i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp) & 0xfffff; + return i; +} + +static inline int cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + __le32 tmp = cpu_to_le32((u32)val); + return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp)); +} + +static inline int cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + __le32 tmp; + int i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp); + return i; +} + +static inline int cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + __le32 tmp = cpu_to_le32(val); + return cw1200_reg_write(priv, addr, &tmp, sizeof(val)); +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr); +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len); + +static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_PRFETCH_BIT, + ST90TDS_SRAM_DPORT_REG_ID); +} + +static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_AHB_PRFETCH_BIT, + ST90TDS_AHB_DPORT_REG_ID); +} + +static inline int cw1200_apb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + __le32 tmp; + int i = cw1200_apb_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp); + return i; +} + +static inline int cw1200_apb_write_32(struct cw1200_common *priv, + u32 addr, u32 val) +{ + __le32 tmp = cpu_to_le32(val); + return cw1200_apb_write(priv, addr, &tmp, sizeof(val)); +} +static inline int cw1200_ahb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + __le32 tmp; + int i = cw1200_ahb_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp); + return i; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c new file mode 100644 index 00000000000..3724e739cbf --- /dev/null +++ b/drivers/net/wireless/cw1200/main.c @@ -0,0 +1,605 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on: + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/vmalloc.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <net/mac80211.h> + +#include "cw1200.h" +#include "txrx.h" +#include "hwbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "scan.h" +#include "debug.h" +#include "pm.h" + +MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>"); +MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_core"); + +/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ +static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00}; +module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(macaddr, "Override platform_data MAC address"); + +static char *cw1200_sdd_path; +module_param(cw1200_sdd_path, charp, 0644); +MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file"); +static int cw1200_refclk; +module_param(cw1200_refclk, int, 0644); +MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock"); + +int cw1200_power_mode = wsm_power_mode_quiescent; +module_param(cw1200_power_mode, int, 0644); +MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); + +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate cw1200_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate cw1200_mcs_rates[] = { + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), +}; + +#define cw1200_a_rates (cw1200_rates + 4) +#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) +#define cw1200_g_rates (cw1200_rates + 0) +#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) +#define cw1200_n_rates (cw1200_mcs_rates) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel cw1200_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static struct ieee80211_channel cw1200_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; + +static struct ieee80211_supported_band cw1200_band_2ghz = { + .channels = cw1200_2ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), + .bitrates = cw1200_g_rates, + .n_bitrates = cw1200_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static struct ieee80211_supported_band cw1200_band_5ghz = { + .channels = cw1200_5ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), + .bitrates = cw1200_a_rates, + .n_bitrates = cw1200_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const unsigned long cw1200_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + +static const struct ieee80211_ops cw1200_ops = { + .start = cw1200_start, + .stop = cw1200_stop, + .add_interface = cw1200_add_interface, + .remove_interface = cw1200_remove_interface, + .change_interface = cw1200_change_interface, + .tx = cw1200_tx, + .hw_scan = cw1200_hw_scan, + .set_tim = cw1200_set_tim, + .sta_notify = cw1200_sta_notify, + .sta_add = cw1200_sta_add, + .sta_remove = cw1200_sta_remove, + .set_key = cw1200_set_key, + .set_rts_threshold = cw1200_set_rts_threshold, + .config = cw1200_config, + .bss_info_changed = cw1200_bss_info_changed, + .prepare_multicast = cw1200_prepare_multicast, + .configure_filter = cw1200_configure_filter, + .conf_tx = cw1200_conf_tx, + .get_stats = cw1200_get_stats, + .ampdu_action = cw1200_ampdu_action, + .flush = cw1200_flush, +#ifdef CONFIG_PM + .suspend = cw1200_wow_suspend, + .resume = cw1200_wow_resume, +#endif + /* Intentionally not offloaded: */ + /*.channel_switch = cw1200_channel_switch, */ + /*.remain_on_channel = cw1200_remain_on_channel, */ + /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ +}; + +static int cw1200_ba_rx_tids = -1; +static int cw1200_ba_tx_tids = -1; +module_param(cw1200_ba_rx_tids, int, 0644); +module_param(cw1200_ba_tx_tids, int, 0644); +MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs"); +MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs"); + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support cw1200_wowlan_support = { + /* Support only for limited wowlan functionalities */ + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + + +static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, + const bool have_5ghz) +{ + int i, band; + struct ieee80211_hw *hw; + struct cw1200_common *priv; + + hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops); + if (!hw) + return NULL; + + priv = hw->priv; + priv->hw = hw; + priv->hw_type = -1; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->rates = cw1200_rates; /* TODO: fetch from FW */ + priv->mcs_rates = cw1200_n_rates; + if (cw1200_ba_rx_tids != -1) + priv->ba_rx_tid_mask = cw1200_ba_rx_tids; + else + priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */ + if (cw1200_ba_tx_tids != -1) + priv->ba_tx_tid_mask = cw1200_ba_tx_tids; + else + priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */ + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_UAPSD | + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | + IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC; + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + +#ifdef CONFIG_PM + hw->wiphy->wowlan = &cw1200_wowlan_support; +#endif + + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + + hw->channel_change_time = 1000; /* TODO: find actual value */ + hw->queues = 4; + + priv->rts_threshold = -1; + + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8; /* TKIP IV */ + + hw->sta_data_size = sizeof(struct cw1200_sta_priv); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; + if (have_5ghz) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; + + /* Channel params have to be cleared before registering wiphy again */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].flags = 0; + sband->channels[i].max_antenna_gain = 0; + sband->channels[i].max_power = 30; + } + } + + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + + if (macaddr) + SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr); + else + SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); + + /* Fix up mac address if necessary */ + if (hw->wiphy->perm_addr[3] == 0 && + hw->wiphy->perm_addr[4] == 0 && + hw->wiphy->perm_addr[5] == 0) { + get_random_bytes(&hw->wiphy->perm_addr[3], 3); + } + + mutex_init(&priv->wsm_cmd_mux); + mutex_init(&priv->conf_mutex); + priv->workqueue = create_singlethread_workqueue("cw1200_wq"); + sema_init(&priv->scan.lock, 1); + INIT_WORK(&priv->scan.work, cw1200_scan_work); + INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); + INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); + INIT_DELAYED_WORK(&priv->clear_recent_scan_work, + cw1200_clear_recent_scan_work); + INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); + INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); + INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work); + INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); + INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); + spin_lock_init(&priv->event_queue_lock); + INIT_LIST_HEAD(&priv->event_queue); + INIT_WORK(&priv->event_handler, cw1200_event_handler); + INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); + INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work); + spin_lock_init(&priv->bss_loss_lock); + spin_lock_init(&priv->ps_state_lock); + INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work); + INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); + INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + INIT_WORK(&priv->link_id_work, cw1200_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); + INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); + INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); + INIT_WORK(&priv->set_beacon_wakeup_period_work, + cw1200_set_beacon_wakeup_period_work); + init_timer(&priv->mcast_timeout); + priv->mcast_timeout.data = (unsigned long)priv; + priv->mcast_timeout.function = cw1200_mcast_timeout; + + if (cw1200_queue_stats_init(&priv->tx_queue_stats, + CW1200_LINK_ID_MAX, + cw1200_skb_dtor, + priv)) { + ieee80211_free_hw(hw); + return NULL; + } + + for (i = 0; i < 4; ++i) { + if (cw1200_queue_init(&priv->tx_queue[i], + &priv->tx_queue_stats, i, 16, + cw1200_ttl[i])) { + for (; i > 0; i--) + cw1200_queue_deinit(&priv->tx_queue[i - 1]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&priv->channel_switch_done); + init_waitqueue_head(&priv->wsm_cmd_wq); + init_waitqueue_head(&priv->wsm_startup_done); + init_waitqueue_head(&priv->ps_mode_switch_done); + wsm_buf_init(&priv->wsm_cmd_buf); + spin_lock_init(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + tx_policy_init(priv); + + return hw; +} + +static int cw1200_register_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int err; + +#ifdef CONFIG_PM + err = cw1200_pm_init(&priv->pm_state, priv); + if (err) { + pr_err("Cannot init PM. (%d).\n", + err); + return err; + } +#endif + + err = ieee80211_register_hw(dev); + if (err) { + pr_err("Cannot register device (%d).\n", + err); +#ifdef CONFIG_PM + cw1200_pm_deinit(&priv->pm_state); +#endif + return err; + } + + cw1200_debug_init(priv); + + pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); + return 0; +} + +static void cw1200_free_common(struct ieee80211_hw *dev) +{ + ieee80211_free_hw(dev); +} + +static void cw1200_unregister_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int i; + + ieee80211_unregister_hw(dev); + + del_timer_sync(&priv->mcast_timeout); + cw1200_unregister_bh(priv); + + cw1200_debug_release(priv); + + mutex_destroy(&priv->conf_mutex); + + wsm_buf_deinit(&priv->wsm_cmd_buf); + + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + if (priv->sdd) { + release_firmware(priv->sdd); + priv->sdd = NULL; + } + + for (i = 0; i < 4; ++i) + cw1200_queue_deinit(&priv->tx_queue[i]); + + cw1200_queue_stats_deinit(&priv->tx_queue_stats); +#ifdef CONFIG_PM + cw1200_pm_deinit(&priv->pm_state); +#endif +} + +/* Clock is in KHz */ +u32 cw1200_dpll_from_clk(u16 clk_khz) +{ + switch (clk_khz) { + case 0x32C8: /* 13000 KHz */ + return 0x1D89D241; + case 0x3E80: /* 16000 KHz */ + return 0x000001E1; + case 0x41A0: /* 16800 KHz */ + return 0x124931C1; + case 0x4B00: /* 19200 KHz */ + return 0x00000191; + case 0x5DC0: /* 24000 KHz */ + return 0x00000141; + case 0x6590: /* 26000 KHz */ + return 0x0EC4F121; + case 0x8340: /* 33600 KHz */ + return 0x092490E1; + case 0x9600: /* 38400 KHz */ + return 0x100010C1; + case 0x9C40: /* 40000 KHz */ + return 0x000000C1; + case 0xBB80: /* 48000 KHz */ + return 0x000000A1; + case 0xCB20: /* 52000 KHz */ + return 0x07627091; + default: + pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n", + clk_khz); + return 0x0EC4F121; + } +} + +int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, + struct hwbus_priv *hwbus, + struct device *pdev, + struct cw1200_common **core, + int ref_clk, const u8 *macaddr, + const char *sdd_path, bool have_5ghz) +{ + int err = -EINVAL; + struct ieee80211_hw *dev; + struct cw1200_common *priv; + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + dev = cw1200_init_common(macaddr, have_5ghz); + if (!dev) + goto err; + + priv = dev->priv; + priv->hw_refclk = ref_clk; + if (cw1200_refclk) + priv->hw_refclk = cw1200_refclk; + + priv->sdd_path = (char *)sdd_path; + if (cw1200_sdd_path) + priv->sdd_path = cw1200_sdd_path; + + priv->hwbus_ops = hwbus_ops; + priv->hwbus_priv = hwbus; + priv->pdev = pdev; + SET_IEEE80211_DEV(priv->hw, pdev); + + /* Pass struct cw1200_common back up */ + *core = priv; + + err = cw1200_register_bh(priv); + if (err) + goto err1; + + err = cw1200_load_firmware(priv); + if (err) + goto err2; + + if (wait_event_interruptible_timeout(priv->wsm_startup_done, + priv->firmware_ready, + 3*HZ) <= 0) { + /* TODO: Need to find how to reset device + in QUEUE mode properly. + */ + pr_err("Timeout waiting on device startup\n"); + err = -ETIMEDOUT; + goto err2; + } + + /* Set low-power mode. */ + wsm_set_operational_mode(priv, &mode); + + /* Enable multi-TX confirmation */ + wsm_use_multi_tx_conf(priv, true); + + err = cw1200_register_common(dev); + if (err) + goto err2; + + return err; + +err2: + cw1200_unregister_bh(priv); +err1: + cw1200_free_common(dev); +err: + *core = NULL; + return err; +} +EXPORT_SYMBOL_GPL(cw1200_core_probe); + +void cw1200_core_release(struct cw1200_common *self) +{ + /* Disable device interrupts */ + self->hwbus_ops->lock(self->hwbus_priv); + __cw1200_irq_enable(self, 0); + self->hwbus_ops->unlock(self->hwbus_priv); + + /* And then clean up */ + cw1200_unregister_common(self->hw); + cw1200_free_common(self->hw); + return; +} +EXPORT_SYMBOL_GPL(cw1200_core_release); diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c new file mode 100644 index 00000000000..b37abb9f045 --- /dev/null +++ b/drivers/net/wireless/cw1200/pm.c @@ -0,0 +1,367 @@ +/* + * Mac80211 power management API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/if_ether.h> +#include "cw1200.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "hwbus.h" + +#define CW1200_BEACON_SKIPPING_MULTIPLIER 3 + +struct cw1200_udp_port_filter { + struct wsm_udp_port_filter_hdr hdr; + /* Up to 4 filters are allowed. */ + struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS]; +} __packed; + +struct cw1200_ether_type_filter { + struct wsm_ether_type_filter_hdr hdr; + /* Up to 4 filters are allowed. */ + struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS]; +} __packed; + +static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { + .hdr.num = 2, + .filters = { + [0] = { + .action = WSM_FILTER_ACTION_FILTER_OUT, + .type = WSM_FILTER_PORT_TYPE_DST, + .port = __cpu_to_le16(67), /* DHCP Bootps */ + }, + [1] = { + .action = WSM_FILTER_ACTION_FILTER_OUT, + .type = WSM_FILTER_PORT_TYPE_DST, + .port = __cpu_to_le16(68), /* DHCP Bootpc */ + }, + } +}; + +static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { + .num = 0, +}; + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + +static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { + .hdr.num = 4, + .filters = { + [0] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_IP), + }, + [1] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_PAE), + }, + [2] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_WAPI), + }, + [3] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_ARP), + }, + }, +}; + +static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { + .num = 0, +}; + +/* private */ +struct cw1200_suspend_state { + unsigned long bss_loss_tmo; + unsigned long join_tmo; + unsigned long direct_probe; + unsigned long link_id_gc; + bool beacon_skipping; + u8 prev_ps_mode; +}; + +static void cw1200_pm_stay_awake_tmo(unsigned long arg) +{ + /* XXX what's the point of this ? */ +} + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + spin_lock_init(&pm->lock); + + init_timer(&pm->stay_awake); + pm->stay_awake.data = (unsigned long)pm; + pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + + return 0; +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + del_timer_sync(&pm->stay_awake); +} + +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->stay_awake.expires - jiffies; + if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) + mod_timer(&pm->stay_awake, jiffies + tmo); + spin_unlock_bh(&pm->lock); +} + +static long cw1200_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int cw1200_resume_work(struct cw1200_common *priv, + struct delayed_work *work, + unsigned long tmo) +{ + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(priv->workqueue, work, tmo); +} + +int cw1200_can_suspend(struct cw1200_common *priv) +{ + if (atomic_read(&priv->bh_rx)) { + wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); + return 0; + } + return 1; +} +EXPORT_SYMBOL_GPL(cw1200_can_suspend); + +int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + int ret; + + spin_lock_bh(&pm_state->lock); + ret = timer_pending(&pm_state->stay_awake); + spin_unlock_bh(&pm_state->lock); + if (ret) + return -EAGAIN; + + /* Do not suspend when datapath is not idle */ + if (priv->tx_queue_stats.num_queued) + return -EBUSY; + + /* Make sure there is no configuration requests in progress. */ + if (!mutex_trylock(&priv->conf_mutex)) + return -EBUSY; + + /* Ensure pending operations are done. + * Note also that wow_suspend must return in ~2.5sec, before + * watchdog is triggered. + */ + if (priv->channel_switch_in_progress) + goto revert1; + + /* Do not suspend when join is pending */ + if (priv->join_pending) + goto revert1; + + /* Do not suspend when scanning */ + if (down_trylock(&priv->scan.lock)) + goto revert1; + + /* Lock TX. */ + wsm_lock_tx_async(priv); + + /* Wait to avoid possible race with bh code. + * But do not wait too long... + */ + if (wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, HZ / 10) <= 0) + goto revert2; + + /* Set UDP filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); + + /* Set ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); + + /* Allocate state */ + state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); + if (!state) + goto revert3; + + /* Change to legacy PS while going to suspend */ + if (!priv->vif->p2p && + priv->join_status == CW1200_JOIN_STATUS_STA && + priv->powersave_mode.mode != WSM_PSM_PS) { + state->prev_ps_mode = priv->powersave_mode.mode; + priv->powersave_mode.mode = WSM_PSM_PS; + cw1200_set_pm(priv, &priv->powersave_mode); + if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, + !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { + goto revert3; + } + } + + /* Store delayed work states. */ + state->bss_loss_tmo = + cw1200_suspend_work(&priv->bss_loss_work); + state->join_tmo = + cw1200_suspend_work(&priv->join_timeout); + state->direct_probe = + cw1200_suspend_work(&priv->scan.probe_work); + state->link_id_gc = + cw1200_suspend_work(&priv->link_id_gc_work); + + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + atomic_set(&priv->recent_scan, 0); + + /* Enable beacon skipping */ + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->join_dtim_period && + !priv->has_multicast_subscription) { + state->beacon_skipping = true; + wsm_set_beacon_wakeup_period(priv, + priv->join_dtim_period, + CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period); + } + + /* Stop serving thread */ + if (cw1200_bh_suspend(priv)) + goto revert4; + + ret = timer_pending(&priv->mcast_timeout); + if (ret) + goto revert5; + + /* Store suspend state */ + pm_state->suspend_state = state; + + /* Enable IRQ wake */ + ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true); + if (ret) { + wiphy_err(priv->hw->wiphy, + "PM request failed: %d. WoW is disabled.\n", ret); + cw1200_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&priv->bh_rx)) { + cw1200_wow_resume(hw); + return -EAGAIN; + } + + return 0; + +revert5: + WARN_ON(cw1200_bh_resume(priv)); +revert4: + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + kfree(state); +revert3: + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); +revert2: + wsm_unlock_tx(priv); + up(&priv->scan.lock); +revert1: + mutex_unlock(&priv->conf_mutex); + return -EBUSY; +} + +int cw1200_wow_resume(struct ieee80211_hw *hw) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + + state = pm_state->suspend_state; + pm_state->suspend_state = NULL; + + /* Disable IRQ wake */ + priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false); + + /* Scan.lock must be released before BH is resumed other way + * in case when BSS_LOST command arrived the processing of the + * command will be delayed. + */ + up(&priv->scan.lock); + + /* Resume BH thread */ + WARN_ON(cw1200_bh_resume(priv)); + + /* Restores previous PS mode */ + if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) { + priv->powersave_mode.mode = state->prev_ps_mode; + cw1200_set_pm(priv, &priv->powersave_mode); + } + + if (state->beacon_skipping) { + wsm_set_beacon_wakeup_period(priv, priv->beacon_int * + priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0); + state->beacon_skipping = false; + } + + /* Resume delayed work */ + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + + /* Remove UDP port filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + + /* Remove ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); + + /* Unlock datapath */ + wsm_unlock_tx(priv); + + /* Unlock configuration mutex */ + mutex_unlock(&priv->conf_mutex); + + /* Free memory */ + kfree(state); + + return 0; +} diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h new file mode 100644 index 00000000000..3ed90ff22bb --- /dev/null +++ b/drivers/net/wireless/cw1200/pm.h @@ -0,0 +1,43 @@ +/* + * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +/* extern */ struct cw1200_common; +/* private */ struct cw1200_suspend_state; + +struct cw1200_pm_state { + struct cw1200_suspend_state *suspend_state; + struct timer_list stay_awake; + struct platform_device *pm_dev; + spinlock_t lock; /* Protect access */ +}; + +#ifdef CONFIG_PM +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv); +void cw1200_pm_deinit(struct cw1200_pm_state *pm); +int cw1200_wow_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int cw1200_wow_resume(struct ieee80211_hw *hw); +int cw1200_can_suspend(struct cw1200_common *priv); +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo); +#else +static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) { +} +#endif +#endif diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c new file mode 100644 index 00000000000..9c3925f58d7 --- /dev/null +++ b/drivers/net/wireless/cw1200/queue.c @@ -0,0 +1,583 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <net/mac80211.h> +#include <linux/sched.h> +#include "queue.h" +#include "cw1200.h" +#include "debug.h" + +/* private */ struct cw1200_queue_item +{ + struct list_head head; + struct sk_buff *skb; + u32 packet_id; + unsigned long queue_timestamp; + unsigned long xmit_timestamp; + struct cw1200_txpriv txpriv; + u8 generation; +}; + +static inline void __cw1200_queue_lock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + if (queue->tx_locked_cnt++ == 0) { + pr_debug("[TX] Queue %d is locked.\n", + queue->queue_id); + ieee80211_stop_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) { + pr_debug("[TX] Queue %d is unlocked.\n", + queue->queue_id); + ieee80211_wake_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation, + u8 *queue_id, u8 *item_generation, + u8 *item_id) +{ + *item_id = (packet_id >> 0) & 0xFF; + *item_generation = (packet_id >> 8) & 0xFF; + *queue_id = (packet_id >> 16) & 0xFF; + *queue_generation = (packet_id >> 24) & 0xFF; +} + +static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id, + u8 item_generation, u8 item_id) +{ + return ((u32)item_id << 0) | + ((u32)item_generation << 8) | + ((u32)queue_id << 16) | + ((u32)queue_generation << 24); +} + +static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, + struct list_head *gc_list) +{ + struct cw1200_queue_item *item, *tmp; + + list_for_each_entry_safe(item, tmp, gc_list, head) { + list_del(&item->head); + stats->skb_dtor(stats->priv, item->skb, &item->txpriv); + kfree(item); + } +} + +static void cw1200_queue_register_post_gc(struct list_head *gc_list, + struct cw1200_queue_item *item) +{ + struct cw1200_queue_item *gc_item; + gc_item = kmalloc(sizeof(struct cw1200_queue_item), + GFP_ATOMIC); + BUG_ON(!gc_item); + memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); + list_add_tail(&gc_item->head, gc_list); +} + +static void __cw1200_queue_gc(struct cw1200_queue *queue, + struct list_head *head, + bool unlock) +{ + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item = NULL, *tmp; + bool wakeup_stats = false; + + list_for_each_entry_safe(item, tmp, &queue->queue, head) { + if (jiffies - item->queue_timestamp < queue->ttl) + break; + --queue->num_queued; + --queue->link_map_cache[item->txpriv.link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + cw1200_debug_tx_ttl(stats->priv); + cw1200_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + if (queue->overfull) { + if (queue->num_queued <= (queue->capacity >> 1)) { + queue->overfull = false; + if (unlock) + __cw1200_queue_unlock(queue); + } else if (item) { + unsigned long tmo = item->queue_timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + cw1200_pm_stay_awake(&stats->priv->pm_state, + tmo - jiffies); + } + } +} + +static void cw1200_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct cw1200_queue *queue = + (struct cw1200_queue *)arg; + + spin_lock_bh(&queue->lock); + __cw1200_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + cw1200_queue_post_gc(queue->stats, &list); +} + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv) +{ + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->priv = priv; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + + stats->link_map_cache = kzalloc(sizeof(int) * map_capacity, + GFP_KERNEL); + if (!stats->link_map_cache) + return -ENOMEM; + + return 0; +} + +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl) +{ + size_t i; + + memset(queue, 0, sizeof(*queue)); + queue->stats = stats; + queue->capacity = capacity; + queue->queue_id = queue_id; + queue->ttl = ttl; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); + init_timer(&queue->gc); + queue->gc.data = (unsigned long)queue; + queue->gc.function = cw1200_queue_gc; + + queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity, + GFP_KERNEL); + if (!queue->link_map_cache) { + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +int cw1200_queue_clear(struct cw1200_queue *queue) +{ + int i; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item, *tmp; + + spin_lock_bh(&queue->lock); + queue->generation++; + list_splice_tail_init(&queue->queue, &queue->pending); + list_for_each_entry_safe(item, tmp, &queue->pending, head) { + WARN_ON(!item->skb); + cw1200_queue_register_post_gc(&gc_list, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + queue->num_queued = 0; + queue->num_pending = 0; + + spin_lock_bh(&stats->lock); + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued -= queue->link_map_cache[i]; + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->lock); + if (queue->overfull) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + spin_unlock_bh(&queue->lock); + wake_up(&stats->wait_link_id_empty); + cw1200_queue_post_gc(stats, &gc_list); + return 0; +} + +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) +{ + kfree(stats->link_map_cache); + stats->link_map_cache = NULL; +} + +void cw1200_queue_deinit(struct cw1200_queue *queue) +{ + cw1200_queue_clear(queue); + del_timer_sync(&queue->gc); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + kfree(queue->link_map_cache); + queue->pool = NULL; + queue->link_map_cache = NULL; + queue->capacity = 0; +} + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + size_t map_capacity = queue->stats->map_capacity; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->lock); + if (link_id_map == (u32)-1) { + ret = queue->num_queued - queue->num_pending; + } else { + ret = 0; + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv) +{ + int ret = 0; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + + if (txpriv->link_id >= queue->stats->map_capacity) + return -EINVAL; + + spin_lock_bh(&queue->lock); + if (!WARN_ON(list_empty(&queue->free_pool))) { + struct cw1200_queue_item *item = list_first_entry( + &queue->free_pool, struct cw1200_queue_item, head); + BUG_ON(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->txpriv = *txpriv; + item->generation = 0; + item->packet_id = cw1200_queue_mk_packet_id(queue->generation, + queue->queue_id, + item->generation, + item - queue->pool); + item->queue_timestamp = jiffies; + + ++queue->num_queued; + ++queue->link_map_cache[txpriv->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[txpriv->link_id]; + spin_unlock_bh(&stats->lock); + + /* TX may happen in parallel sometimes. + * Leave extra queue slots so we don't overflow. + */ + if (queue->overfull == false && + queue->num_queued >= + (queue->capacity - (num_present_cpus() - 1))) { + queue->overfull = true; + __cw1200_queue_lock(queue); + mod_timer(&queue->gc, jiffies); + } + } else { + ret = -ENOENT; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv) +{ + int ret = -ENOENT; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + bool wakeup_stats = false; + + spin_lock_bh(&queue->lock); + list_for_each_entry(item, &queue->queue, head) { + if (link_id_map & BIT(item->txpriv.link_id)) { + ret = 0; + break; + } + } + + if (!WARN_ON(ret)) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + *txpriv = &item->txpriv; + (*tx)->packet_id = item->packet_id; + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + --queue->link_map_cache[item->txpriv.link_id]; + item->xmit_timestamp = jiffies; + + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + } + spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return ret; +} + +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + item->generation = ++item_generation; + item->packet_id = cw1200_queue_mk_packet_id(queue_generation, + queue_id, + item_generation, + item_id); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_requeue_all(struct cw1200_queue *queue) +{ + struct cw1200_queue_item *item, *tmp; + struct cw1200_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); + + list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + ++item->generation; + item->packet_id = cw1200_queue_mk_packet_id(queue->generation, + queue->queue_id, + item->generation, + item - queue->pool); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + + return 0; +} + +int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct cw1200_txpriv gc_txpriv; + + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; + --queue->num_pending; + --queue->num_queued; + ++queue->num_sent; + ++item->generation; + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (queue->overfull && + (queue->num_queued <= (queue->capacity >> 1))) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + } + spin_unlock_bh(&queue->lock); + + if (gc_skb) + stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); + + return ret; +} + +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +void cw1200_queue_lock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_lock(queue); + spin_unlock_bh(&queue->lock); +} + +void cw1200_queue_unlock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_unlock(queue); + spin_unlock_bh(&queue->lock); +} + +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp, + u32 pending_frame_id) +{ + struct cw1200_queue_item *item; + bool ret; + + spin_lock_bh(&queue->lock); + ret = !list_empty(&queue->pending); + if (ret) { + list_for_each_entry(item, &queue->pending, head) { + if (item->packet_id != pending_frame_id) + if (time_before(item->xmit_timestamp, + *timestamp)) + *timestamp = item->xmit_timestamp; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) { + empty = stats->num_queued == 0; + } else { + int i; + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[i]) { + empty = false; + break; + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h new file mode 100644 index 00000000000..119f9c79c14 --- /dev/null +++ b/drivers/net/wireless/cw1200/queue.h @@ -0,0 +1,116 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_QUEUE_H_INCLUDED +#define CW1200_QUEUE_H_INCLUDED + +/* private */ struct cw1200_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct cw1200_common; +/* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct cw1200_txpriv; + +/* forward */ struct cw1200_queue_stats; + +typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +struct cw1200_queue { + struct cw1200_queue_stats *stats; + size_t capacity; + size_t num_queued; + size_t num_pending; + size_t num_sent; + struct cw1200_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache; + bool overfull; + spinlock_t lock; /* Protect queue entry */ + u8 queue_id; + u8 generation; + struct timer_list gc; + unsigned long ttl; +}; + +struct cw1200_queue_stats { + spinlock_t lock; /* Protect stats entry */ + int *link_map_cache; + int num_queued; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; + cw1200_queue_skb_dtor_t skb_dtor; + struct cw1200_common *priv; +}; + +struct cw1200_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; + u8 rate_id; + u8 offset; +}; + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv); +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl); +int cw1200_queue_clear(struct cw1200_queue *queue); +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); +void cw1200_queue_deinit(struct cw1200_queue *queue); + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map); +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv); +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv); +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id); +int cw1200_queue_requeue_all(struct cw1200_queue *queue); +int cw1200_queue_remove(struct cw1200_queue *queue, + u32 packet_id); +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv); +void cw1200_queue_lock(struct cw1200_queue *queue); +void cw1200_queue_unlock(struct cw1200_queue *queue); +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp, + u32 pending_frame_id); + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map); + +static inline u8 cw1200_queue_get_queue_id(u32 packet_id) +{ + return (packet_id >> 16) & 0xFF; +} + +static inline u8 cw1200_queue_get_generation(u32 packet_id) +{ + return (packet_id >> 8) & 0xFF; +} + +#endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c new file mode 100644 index 00000000000..ee3c19037aa --- /dev/null +++ b/drivers/net/wireless/cw1200/scan.c @@ -0,0 +1,461 @@ +/* + * Scan implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/sched.h> +#include "cw1200.h" +#include "scan.h" +#include "sta.h" +#include "pm.h" + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv); + +static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) +{ + int ret, i; + int tmo = 2000; + + switch (priv->join_status) { + case CW1200_JOIN_STATUS_PRE_STA: + case CW1200_JOIN_STATUS_JOINING: + return -EBUSY; + default: + break; + } + + wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", + scan->type, scan->num_channels, scan->flags); + + for (i = 0; i < scan->num_channels; ++i) + tmo += scan->ch[i].max_chan_time + 10; + + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + atomic_set(&priv->scan.in_progress, 1); + atomic_set(&priv->recent_scan, 1); + cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000); + queue_delayed_work(priv->workqueue, &priv->scan.timeout, + tmo * HZ / 1000); + ret = wsm_scan(priv, scan); + if (ret) { + atomic_set(&priv->scan.in_progress, 0); + cancel_delayed_work_sync(&priv->scan.timeout); + cw1200_scan_restart_delayed(priv); + } + return ret; +} + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct cw1200_common *priv = hw->priv; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i, ret; + + if (!priv->vif) + return -EINVAL; + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == CW1200_JOIN_STATUS_AP) + return -EOPNOTSUPP; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) + return -EINVAL; + + frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + req->ie_len); + if (!frame.skb) + return -ENOMEM; + + if (req->ie_len) + memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); + + /* will be unlocked in cw1200_scan_work() */ + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + ret = wsm_set_template_frame(priv, &frame); + if (!ret) { + /* Host want to be the probe responder. */ + ret = wsm_set_probe_responder(priv, true); + } + if (ret) { + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + + wsm_lock_tx(priv); + + BUG_ON(priv->scan.req); + priv->scan.req = req; + priv->scan.n_ssids = 0; + priv->scan.status = 0; + priv->scan.begin = &req->channels[0]; + priv->scan.curr = priv->scan.begin; + priv->scan.end = &req->channels[req->n_channels]; + priv->scan.output_power = priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++priv->scan.n_ssids; + } + + mutex_unlock(&priv->conf_mutex); + + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(priv->workqueue, &priv->scan.work); + return 0; +} + +void cw1200_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = container_of(work, struct cw1200_common, + scan.work); + struct ieee80211_channel **it; + struct wsm_scan scan = { + .type = WSM_SCAN_TYPE_FOREGROUND, + .flags = WSM_SCAN_FLAG_SPLIT_METHOD, + }; + bool first_run = (priv->scan.begin == priv->scan.curr && + priv->scan.begin != priv->scan.end); + int i; + + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. + */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) + cw1200_join_timeout(&priv->join_timeout.work); + } + + mutex_lock(&priv->conf_mutex); + + if (first_run) { + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.mode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.mode = WSM_PSM_PS; + cw1200_set_pm(priv, &pm); + } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan + */ + cw1200_disable_listening(priv); + } + } + + if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { + if (priv->scan.output_power != priv->output_power) + wsm_set_output_power(priv, priv->output_power * 10); + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.mode & WSM_PSM_PS)) + cw1200_set_pm(priv, &priv->powersave_mode); + + if (priv->scan.status < 0) + wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n", + priv->scan.status); + else if (priv->scan.req) + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); + else + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); + + priv->scan.req = NULL; + cw1200_scan_restart_delayed(priv); + wsm_unlock_tx(priv); + mutex_unlock(&priv->conf_mutex); + ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); + up(&priv->scan.lock); + return; + } else { + struct ieee80211_channel *first = *priv->scan.curr; + for (it = priv->scan.curr + 1, i = 1; + it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_PASSIVE_SCAN) + break; + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + (*it)->max_power != first->max_power) + break; + } + scan.band = first->band; + + if (priv->scan.req->no_cck) + scan.max_tx_rate = WSM_TRANSMIT_RATE_6; + else + scan.max_tx_rate = WSM_TRANSMIT_RATE_1; + scan.num_probes = + (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; + scan.num_ssids = priv->scan.n_ssids; + scan.ssids = &priv->scan.ssids[0]; + scan.num_channels = it - priv->scan.curr; + /* TODO: Is it optimal? */ + scan.probe_delay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. + */ + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + scan.type = WSM_SCAN_TYPE_BACKGROUND; + scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + scan.ch = kzalloc( + sizeof(struct wsm_scan_ch) * (it - priv->scan.curr), + GFP_KERNEL); + if (!scan.ch) { + priv->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.num_channels; ++i) { + scan.ch[i].number = priv->scan.curr[i]->hw_value; + if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + scan.ch[i].min_chan_time = 50; + scan.ch[i].max_chan_time = 100; + } else { + scan.ch[i].min_chan_time = 10; + scan.ch[i].max_chan_time = 25; + } + } + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + priv->scan.output_power != first->max_power) { + priv->scan.output_power = first->max_power; + wsm_set_output_power(priv, + priv->scan.output_power * 10); + } + priv->scan.status = cw1200_scan_start(priv, &scan); + kfree(scan.ch); + if (priv->scan.status) + goto fail; + priv->scan.curr = it; + } + mutex_unlock(&priv->conf_mutex); + return; + +fail: + priv->scan.curr = priv->scan.end; + mutex_unlock(&priv->conf_mutex); + queue_work(priv->workqueue, &priv->scan.work); + return; +} + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv) +{ + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + cw1200_enable_listening(priv); + cw1200_update_filtering(priv); + } + + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else if (priv->delayed_link_loss) { + wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); + priv->delayed_link_loss = 0; + cw1200_cqm_bssloss_sm(priv, 1, 0, 0); + } +} + +static void cw1200_scan_complete(struct cw1200_common *priv) +{ + queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); + if (priv->scan.direct_probe) { + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); + cw1200_scan_restart_delayed(priv); + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } else { + cw1200_scan_work(&priv->scan.work); + } +} + +void cw1200_scan_failed_cb(struct cw1200_common *priv) +{ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = -EIO; + queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); + } +} + + +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg) +{ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = 1; + queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); + } +} + +void cw1200_clear_recent_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + clear_recent_scan_work.work); + atomic_xchg(&priv->recent_scan, 0); +} + +void cw1200_scan_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.timeout.work); + if (atomic_xchg(&priv->scan.in_progress, 0)) { + if (priv->scan.status > 0) { + priv->scan.status = 0; + } else if (!priv->scan.status) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for scan complete notification.\n"); + priv->scan.status = -ETIMEDOUT; + priv->scan.curr = priv->scan.end; + wsm_stop_scan(priv); + } + cw1200_scan_complete(priv); + } +} + +void cw1200_probe_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.probe_work.work); + u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + const struct cw1200_txpriv *txpriv; + struct wsm_tx *wsm; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .min_chan_time = 0, + .max_chan_time = 10, + } }; + struct wsm_scan scan = { + .type = WSM_SCAN_TYPE_FOREGROUND, + .num_probes = 1, + .probe_delay = 0, + .num_channels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret; + + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); + + mutex_lock(&priv->conf_mutex); + if (down_trylock(&priv->scan.lock)) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, HZ / 10); + mutex_unlock(&priv->conf_mutex); + return; + } + + /* Make sure we still have a pending probe req */ + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &frame.skb, &txpriv)) { + up(&priv->scan.lock); + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.max_tx_rate = wsm->max_tx_rate; + scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == CW1200_JOIN_STATUS_STA || + priv->join_status == CW1200_JOIN_STATUS_IBSS) { + scan.type = WSM_SCAN_TYPE_BACKGROUND; + scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + ch[0].number = priv->channel->hw_value; + + skb_pull(frame.skb, txpriv->offset); + + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = + (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in cw1200_scan_start call + */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.num_ssids = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + cw1200_disable_listening(priv); + ret = wsm_set_template_frame(priv, &frame); + priv->scan.direct_probe = 1; + if (!ret) { + wsm_flush_tx(priv); + ret = cw1200_scan_start(priv, &scan); + } + mutex_unlock(&priv->conf_mutex); + + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; + BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); + + if (ret) { + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } + + return; +} diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h new file mode 100644 index 00000000000..5a8296ccfa8 --- /dev/null +++ b/drivers/net/wireless/cw1200/scan.h @@ -0,0 +1,56 @@ +/* + * Scan interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include <linux/semaphore.h> +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +struct cw1200_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + int direct_probe; +}; + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); +void cw1200_scan_work(struct work_struct *work); +void cw1200_scan_timeout(struct work_struct *work); +void cw1200_clear_recent_scan_work(struct work_struct *work); +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg); +void cw1200_scan_failed_cb(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void cw1200_probe_work(struct work_struct *work); + +#endif diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c new file mode 100644 index 00000000000..7365674366f --- /dev/null +++ b/drivers/net/wireless/cw1200/sta.c @@ -0,0 +1,2403 @@ +/* + * Mac80211 STA API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/firmware.h> +#include <linux/module.h> + +#include "cw1200.h" +#include "sta.h" +#include "fwio.h" +#include "bh.h" +#include "debug.h" + +#ifndef ERP_INFO_BYTE_OFFSET +#define ERP_INFO_BYTE_OFFSET 2 +#endif + +static void cw1200_do_join(struct cw1200_common *priv); +static void cw1200_do_unjoin(struct cw1200_common *priv); + +static int cw1200_upload_beacon(struct cw1200_common *priv); +static int cw1200_upload_pspoll(struct cw1200_common *priv); +static int cw1200_upload_null(struct cw1200_common *priv); +static int cw1200_upload_qosnull(struct cw1200_common *priv); +static int cw1200_start_ap(struct cw1200_common *priv); +static int cw1200_update_beaconing(struct cw1200_common *priv); +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable); +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + int link_id); +static int __cw1200_flush(struct cw1200_common *priv, bool drop); + +static inline void __cw1200_free_event_queue(struct list_head *list) +{ + struct cw1200_wsm_event *event, *tmp; + list_for_each_entry_safe(event, tmp, list, link) { + list_del(&event->link); + kfree(event); + } +} + +/* ******************************************************************** */ +/* STA API */ + +int cw1200_start(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + + cw1200_pm_stay_awake(&priv->pm_state, HZ); + + mutex_lock(&priv->conf_mutex); + + /* default EDCA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); + ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) + goto out; + + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (ret) + goto out; + + priv->setbssparams_done = false; + + memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + priv->mode = NL80211_IFTYPE_MONITOR; + priv->wep_default_key_id = -1; + + priv->cqm_beacon_loss_count = 10; + + ret = cw1200_setup_mac(priv); + if (ret) + goto out; + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_stop(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + LIST_HEAD(list); + int i; + + wsm_lock_tx(priv); + + while (down_trylock(&priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + priv->scan.req = NULL; + schedule(); + } + up(&priv->scan.lock); + + cancel_delayed_work_sync(&priv->scan.probe_work); + cancel_delayed_work_sync(&priv->scan.timeout); + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + flush_workqueue(priv->workqueue); + del_timer_sync(&priv->mcast_timeout); + mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + __cw1200_free_event_queue(&list); + + + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + priv->join_pending = false; + + for (i = 0; i < 4; i++) + cw1200_queue_clear(&priv->tx_queue[i]); + mutex_unlock(&priv->conf_mutex); + tx_policy_clean(priv); + + /* HACK! */ + if (atomic_xchg(&priv->tx_lock, 1) != 1) + pr_debug("[STA] TX is force-unlocked due to stop request.\n"); + + wsm_unlock_tx(priv); + atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ +} + +static int cw1200_bssloss_mitigation = 1; +module_param(cw1200_bssloss_mitigation, int, 0644); +MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); + + +void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, + int init, int good, int bad) +{ + int tx = 0; + + priv->delayed_link_loss = 0; + cancel_work_sync(&priv->bss_params_work); + + pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", + priv->bss_loss_state, + init, good, bad, + atomic_read(&priv->tx_lock), + priv->delayed_unjoin); + + /* If we have a pending unjoin */ + if (priv->delayed_unjoin) + return; + + if (init) { + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, + HZ); + priv->bss_loss_state = 0; + + /* Skip the confimration procedure in P2P case */ + if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) + tx = 1; + } else if (good) { + cancel_delayed_work_sync(&priv->bss_loss_work); + priv->bss_loss_state = 0; + queue_work(priv->workqueue, &priv->bss_params_work); + } else if (bad) { + /* XXX Should we just keep going until we time out? */ + if (priv->bss_loss_state < 3) + tx = 1; + } else { + cancel_delayed_work_sync(&priv->bss_loss_work); + priv->bss_loss_state = 0; + } + + /* Bypass mitigation if it's disabled */ + if (!cw1200_bssloss_mitigation) + tx = 0; + + /* Spit out a NULL packet to our AP if necessary */ + if (tx) { + struct sk_buff *skb; + + priv->bss_loss_state++; + + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + WARN_ON(!skb); + if (skb) + cw1200_tx(priv->hw, NULL, skb); + } +} + +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct cw1200_common *priv = dev->priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + + mutex_lock(&priv->conf_mutex); + + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + priv->mode = vif->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = vif; + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); + ret = cw1200_setup_mac(priv); + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + * &auto_calibration_mode, sizeof(auto_calibration_mode)); + */ + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct cw1200_common *priv = dev->priv; + struct wsm_reset reset = { + .reset_statistics = true, + }; + int i; + + mutex_lock(&priv->conf_mutex); + switch (priv->join_status) { + case CW1200_JOIN_STATUS_JOINING: + case CW1200_JOIN_STATUS_PRE_STA: + case CW1200_JOIN_STATUS_STA: + case CW1200_JOIN_STATUS_IBSS: + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + break; + case CW1200_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + reset.link_id = i; + wsm_reset(priv, &reset); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(priv, &reset); + break; + case CW1200_JOIN_STATUS_MONITOR: + cw1200_update_listening(priv, false); + break; + default: + break; + } + priv->vif = NULL; + priv->mode = NL80211_IFTYPE_MONITOR; + memset(priv->mac_addr, 0, ETH_ALEN); + memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); + cw1200_free_keys(priv); + cw1200_setup_mac(priv); + priv->listening = false; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p) +{ + int ret = 0; + pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, + p2p, vif->type, vif->p2p); + + if (new_type != vif->type || vif->p2p != p2p) { + cw1200_remove_interface(dev, vif); + vif->type = new_type; + vif->p2p = p2p; + ret = cw1200_add_interface(dev, vif); + } + + return ret; +} + +int cw1200_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + pr_debug("CONFIG CHANGED: %08x\n", changed); + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + priv->output_power = conf->power_level; + pr_debug("[STA] TX power: %d\n", priv->output_power); + wsm_set_output_power(priv, priv->output_power * 10); + } + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && + (priv->channel != conf->chandef.chan)) { + struct ieee80211_channel *ch = conf->chandef.chan; + struct wsm_switch_channel channel = { + .channel_number = ch->hw_value, + }; + pr_debug("[STA] Freq %d (wsm ch: %d).\n", + ch->center_freq, ch->hw_value); + + /* __cw1200_flush() implicitly locks tx, if successful */ + if (!__cw1200_flush(priv, false)) { + if (!wsm_switch_channel(priv, &channel)) { + ret = wait_event_timeout(priv->channel_switch_done, + !priv->channel_switch_in_progress, + 3 * HZ); + if (ret) { + /* Already unlocks if successful */ + priv->channel = ch; + ret = 0; + } else { + ret = -ETIMEDOUT; + } + } else { + /* Unlock if switch channel fails */ + wsm_unlock_tx(priv); + } + } + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (!(conf->flags & IEEE80211_CONF_PS)) + priv->powersave_mode.mode = WSM_PSM_ACTIVE; + else if (conf->dynamic_ps_timeout <= 0) + priv->powersave_mode.mode = WSM_PSM_PS; + else + priv->powersave_mode.mode = WSM_PSM_FAST_PS; + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. + */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fast_psm_idle_period = 0xFF; + else + priv->powersave_mode.fast_psm_idle_period = + conf->dynamic_ps_timeout << 1; + + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->bss_params.aid) + cw1200_set_pm(priv, &priv->powersave_mode); + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! + */ + } + + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + wsm_lock_tx(priv); + /* Disable p2p-dev mode forced by TX request */ + if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && + (conf->flags & IEEE80211_CONF_IDLE) && + !priv->listening) { + cw1200_disable_listening(priv); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + } + wsm_set_operational_mode(priv, &mode); + wsm_unlock_tx(priv); + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + pr_debug("[STA] Retry limits: %d (long), %d (short).\n", + conf->long_frame_max_tx_count, + conf->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; + priv->short_frame_max_tx_count = + (conf->short_frame_max_tx_count < 0x0F) ? + conf->short_frame_max_tx_count : 0x0F; + priv->hw->max_rate_tries = priv->short_frame_max_tx_count; + spin_unlock_bh(&priv->tx_policy_cache.lock); + } + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + return ret; +} + +void cw1200_update_filtering(struct cw1200_common *priv) +{ + int ret; + bool bssid_filtering = !priv->rx_filter.bssid; + bool is_p2p = priv->vif && priv->vif->p2p; + bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; + + static struct wsm_beacon_filter_control bf_ctrl; + static struct wsm_mib_beacon_filter_table bf_tbl = { + .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, + .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[0].oui[0] = 0x50, + .entry[0].oui[1] = 0x6F, + .entry[0].oui[2] = 0x9A, + .entry[1].ie_id = WLAN_EID_HT_OPERATION, + .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[2].ie_id = WLAN_EID_ERP_INFO, + .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + }; + + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) + return; + else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + bssid_filtering = false; + + if (priv->disable_beacon_filter) { + bf_ctrl.enabled = 0; + bf_ctrl.bcn_count = 1; + bf_tbl.num = __cpu_to_le32(0); + } else if (is_p2p || !is_sta) { + bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | + WSM_BEACON_FILTER_AUTO_ERP; + bf_ctrl.bcn_count = 0; + bf_tbl.num = __cpu_to_le32(2); + } else { + bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; + bf_ctrl.bcn_count = 0; + bf_tbl.num = __cpu_to_le32(3); + } + + /* When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ + if (is_p2p) + bssid_filtering = false; + + ret = wsm_set_rx_filter(priv, &priv->rx_filter); + if (!ret) + ret = wsm_set_beacon_filter_table(priv, &bf_tbl); + if (!ret) + ret = wsm_beacon_filter_control(priv, &bf_ctrl); + if (!ret) + ret = wsm_set_bssid_filtering(priv, bssid_filtering); + if (!ret) + ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); + if (ret) + wiphy_err(priv->hw->wiphy, + "Update filtering failed: %d.\n", ret); + return; +} + +void cw1200_update_filtering_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + update_filtering_work); + + cw1200_update_filtering(priv); +} + +void cw1200_set_beacon_wakeup_period_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + set_beacon_wakeup_period_work); + + wsm_set_beacon_wakeup_period(priv, + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0); +} + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + static u8 broadcast_ipv6[ETH_ALEN] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 + }; + static u8 broadcast_ipv4[ETH_ALEN] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 + }; + struct cw1200_common *priv = hw->priv; + struct netdev_hw_addr *ha; + int count = 0; + + /* Disable multicast filtering */ + priv->has_multicast_subscription = false; + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + pr_debug("[STA] multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macaddrs[count], + ha->addr, ETH_ALEN); + if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && + memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) + priv->has_multicast_subscription = true; + count++; + } + + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.num_addrs = __cpu_to_le32(count); + } + + return netdev_hw_addr_list_count(mc_list); +} + +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct cw1200_common *priv = dev->priv; + bool listening = !!(*total_flags & + (FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); + + *total_flags &= FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) + ? 1 : 0; + priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->disable_beacon_filter = !(*total_flags & + (FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS | + FIF_PROBE_REQ)); + if (priv->listening != listening) { + priv->listening = listening; + wsm_lock_tx(priv); + cw1200_update_listening(priv, listening); + wsm_unlock_tx(priv); + } + cw1200_update_filtering(priv); + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); +} + +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsd_flags; + + mutex_lock(&priv->conf_mutex); + + if (queue < dev->queues) { + old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags); + + WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); + ret = wsm_set_tx_queue_params(priv, + &priv->tx_queue_params.params[queue], queue); + if (ret) { + ret = -EINVAL; + goto out; + } + + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, + params->txop, 0xc8, + params->uapsd); + ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) { + ret = -EINVAL; + goto out; + } + + if (priv->mode == NL80211_IFTYPE_STATION) { + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (!ret && priv->setbssparams_done && + (priv->join_status == CW1200_JOIN_STATUS_STA) && + (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags))) + ret = cw1200_set_pm(priv, &priv->powersave_mode); + } + } else { + ret = -EINVAL; + } + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct cw1200_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + + if (priv->uapsd_info.uapsd_flags != 0) + pm.mode &= ~WSM_PSM_FAST_PS_FLAG; + + if (memcmp(&pm, &priv->firmware_ps_mode, + sizeof(struct wsm_set_pm))) { + priv->firmware_ps_mode = pm; + return wsm_set_pm(priv, &pm); + } else { + return 0; + } +} + +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct cw1200_common *priv = dev->priv; + struct ieee80211_key_seq seq; + + mutex_lock(&priv->conf_mutex); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? + 1 : 0; + int idx = cw1200_alloc_key(priv); + struct wsm_add_key *wsm_key = &priv->keys[idx]; + + if (idx < 0) { + ret = -EINVAL; + goto finally; + } + + if (sta) + peer_addr = sta->addr; + + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + cw1200_free_key(priv, idx); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wep_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wep_pairwise.keydata, + &key->key[0], key->keylen); + wsm_key->wep_pairwise.keylen = key->keylen; + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wep_group.keydata, + &key->key[0], key->keylen); + wsm_key->wep_group.keylen = key->keylen; + wsm_key->wep_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_TKIP: + ieee80211_get_key_rx_seq(key, 0, &seq); + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkip_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->tkip_pairwise.keydata, + &key->key[0], 16); + memcpy(wsm_key->tkip_pairwise.tx_mic_key, + &key->key[16], 8); + memcpy(wsm_key->tkip_pairwise.rx_mic_key, + &key->key[24], 8); + } else { + size_t mic_offset = + (priv->mode == NL80211_IFTYPE_AP) ? + 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkip_group.keydata, + &key->key[0], 16); + memcpy(wsm_key->tkip_group.rx_mic_key, + &key->key[mic_offset], 8); + + wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; + wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; + wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; + wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; + wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; + wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; + wsm_key->tkip_group.rx_seqnum[6] = 0; + wsm_key->tkip_group.rx_seqnum[7] = 0; + + wsm_key->tkip_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_CCMP: + ieee80211_get_key_rx_seq(key, 0, &seq); + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aes_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->aes_pairwise.keydata, + &key->key[0], 16); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aes_group.keydata, + &key->key[0], 16); + + wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; + wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; + wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; + wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; + wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; + wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; + wsm_key->aes_group.rx_seqnum[6] = 0; + wsm_key->aes_group.rx_seqnum[7] = 0; + wsm_key->aes_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_SMS4: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapi_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wapi_pairwise.keydata, + &key->key[0], 16); + memcpy(wsm_key->wapi_pairwise.mic_key, + &key->key[16], 16); + wsm_key->wapi_pairwise.keyid = key->keyidx; + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapi_group.keydata, + &key->key[0], 16); + memcpy(wsm_key->wapi_group.mic_key, + &key->key[16], 16); + wsm_key->wapi_group.keyid = key->keyidx; + } + break; + default: + pr_warn("Unhandled key type %d\n", key->cipher); + cw1200_free_key(priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + ret = wsm_add_key(priv, wsm_key); + if (!ret) + key->hw_key_idx = idx; + else + cw1200_free_key(priv, idx); + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .index = key->hw_key_idx, + }; + + if (wsm_key.index > WSM_KEY_MAX_INDEX) { + ret = -EINVAL; + goto finally; + } + + cw1200_free_key(priv, wsm_key.index); + ret = wsm_remove_key(priv, &wsm_key); + } else { + pr_warn("Unhandled key command %d\n", cmd); + } + +finally: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_wep_key_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, wep_key_work); + u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + __le32 wep_default_key_id = __cpu_to_le32( + priv->wep_default_key_id); + + pr_debug("[STA] Setting default WEP key: %d\n", + priv->wep_default_key_id); + wsm_flush_tx(priv); + wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id)); + cw1200_queue_requeue(queue, priv->pending_frame_id); + wsm_unlock_tx(priv); +} + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + int ret = 0; + __le32 val32; + struct cw1200_common *priv = hw->priv; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return 0; + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* device is down, can _not_ set threshold */ + ret = -ENODEV; + goto out; + } + + if (priv->rts_threshold == value) + goto out; + + pr_debug("[STA] Setting RTS threshold: %d\n", + priv->rts_threshold); + + /* mutex_lock(&priv->conf_mutex); */ + ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32)); + if (!ret) + priv->rts_threshold = value; + /* mutex_unlock(&priv->conf_mutex); */ + +out: + return ret; +} + +/* If successful, LOCKS the TX queue! */ +static int __cw1200_flush(struct cw1200_common *priv, bool drop) +{ + int i, ret; + + for (;;) { + /* TODO: correct flush handling is required when dev_stop. + * Temporary workaround: 2s + */ + if (drop) { + for (i = 0; i < 4; ++i) + cw1200_queue_clear(&priv->tx_queue[i]); + } else { + ret = wait_event_timeout( + priv->tx_queue_stats.wait_link_id_empty, + cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1), + 2 * HZ); + } + + if (!drop && ret <= 0) { + ret = -ETIMEDOUT; + break; + } else { + ret = 0; + } + + wsm_lock_tx(priv); + if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { + /* Highly unlikely: WSM requeued frames. */ + wsm_unlock_tx(priv); + continue; + } + break; + } + return ret; +} + +void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +{ + struct cw1200_common *priv = hw->priv; + + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + drop = true; + break; + case NL80211_IFTYPE_AP: + if (!priv->enable_beacon) + drop = true; + break; + } + + if (!__cw1200_flush(priv, drop)) + wsm_unlock_tx(priv); + + return; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_free_event_queue(struct cw1200_common *priv) +{ + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + __cw1200_free_event_queue(&list); +} + +void cw1200_event_handler(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, event_handler); + struct cw1200_wsm_event *event; + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + list_for_each_entry(event, &list, link) { + switch (event->evt.id) { + case WSM_EVENT_ERROR: + pr_err("Unhandled WSM Error from LMAC\n"); + break; + case WSM_EVENT_BSS_LOST: + pr_debug("[CQM] BSS lost.\n"); + cancel_work_sync(&priv->unjoin_work); + if (!down_trylock(&priv->scan.lock)) { + cw1200_cqm_bssloss_sm(priv, 1, 0, 0); + up(&priv->scan.lock); + } else { + /* Scan is in progress. Delay reporting. + * Scan complete will trigger bss_loss_work + */ + priv->delayed_link_loss = 1; + /* Also start a watchdog. */ + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 5*HZ); + } + break; + case WSM_EVENT_BSS_REGAINED: + pr_debug("[CQM] BSS regained.\n"); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + break; + case WSM_EVENT_RADAR_DETECTED: + wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); + break; + case WSM_EVENT_RCPI_RSSI: + { + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + int rcpi_rssi = (int)(event->evt.data & 0xFF); + int cqm_evt; + if (priv->cqm_use_rssi) + rcpi_rssi = (s8)rcpi_rssi; + else + rcpi_rssi = rcpi_rssi / 2 - 110; + + cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi); + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, + GFP_KERNEL); + break; + } + case WSM_EVENT_BT_INACTIVE: + pr_warn("Unhandled BT INACTIVE from LMAC\n"); + break; + case WSM_EVENT_BT_ACTIVE: + pr_warn("Unhandled BT ACTIVE from LMAC\n"); + break; + } + } + __cw1200_free_event_queue(&list); +} + +void cw1200_bss_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_loss_work.work); + + pr_debug("[CQM] Reporting connection loss.\n"); + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); +} + +void cw1200_bss_params_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_params_work); + mutex_lock(&priv->conf_mutex); + + priv->bss_params.reset_beacon_loss = 1; + wsm_set_bss_params(priv, &priv->bss_params); + priv->bss_params.reset_beacon_loss = 0; + + mutex_unlock(&priv->conf_mutex); +} + +/* ******************************************************************** */ +/* Internal API */ + +/* This function is called to Parse the SDD file + * to extract listen_interval and PTA related information + * sdd is a TLV: u8 id, u8 len, u8 data[] + */ +static int cw1200_parse_sdd_file(struct cw1200_common *priv) +{ + const u8 *p = priv->sdd->data; + int ret = 0; + + while (p + 2 <= priv->sdd->data + priv->sdd->size) { + if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { + pr_warn("Malformed sdd structure\n"); + return -1; + } + switch (p[0]) { + case SDD_PTA_CFG_ELT_ID: { + u16 v; + if (p[1] < 4) { + pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); + ret = -1; + break; + } + v = le16_to_cpu(*((__le16 *)(p + 2))); + if (!v) /* non-zero means this is enabled */ + break; + + v = le16_to_cpu(*((__le16 *)(p + 4))); + priv->conf_listen_interval = (v >> 7) & 0x1F; + pr_debug("PTA found; Listen Interval %d\n", + priv->conf_listen_interval); + break; + } + case SDD_REFERENCE_FREQUENCY_ELT_ID: { + u16 clk = le16_to_cpu(*((__le16 *)(p + 2))); + if (clk != priv->hw_refclk) + pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", + clk, priv->hw_refclk); + break; + } + default: + break; + } + p += p[1] + 2; + } + + if (!priv->bt_present) { + pr_debug("PTA element NOT found.\n"); + priv->conf_listen_interval = 0; + } + return ret; +} + +int cw1200_setup_mac(struct cw1200_common *priv) +{ + int ret = 0; + + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. + * + * NOTE2: RSSI based reports have been switched to RCPI, since + * FW has a bug and RSSI reported values are not stable, + * what can leads to signal level oscilations in user-end applications + */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + + struct wsm_configuration cfg = { + .dot11StationId = &priv->mac_addr[0], + }; + + /* Remember the decission here to make sure, we will handle + * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS + */ + if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) + priv->cqm_use_rssi = true; + + if (!priv->sdd) { + ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); + if (ret) { + pr_err("Can't load sdd file %s.\n", priv->sdd_path); + return ret; + } + cw1200_parse_sdd_file(priv); + } + + cfg.dpdData = priv->sdd->data; + cfg.dpdData_size = priv->sdd->size; + ret = wsm_configuration(priv, &cfg); + if (ret) + return ret; + + /* Configure RSSI/SCPI reporting as RSSI. */ + wsm_set_rcpi_rssi_threshold(priv, &threshold); + + return 0; +} + +static void cw1200_join_complete(struct cw1200_common *priv) +{ + pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); + + priv->join_pending = false; + if (priv->join_complete_status) { + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + cw1200_update_listening(priv, priv->listening); + cw1200_do_unjoin(priv); + ieee80211_connection_loss(priv->vif); + } else { + if (priv->mode == NL80211_IFTYPE_ADHOC) + priv->join_status = CW1200_JOIN_STATUS_IBSS; + else + priv->join_status = CW1200_JOIN_STATUS_PRE_STA; + } + wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ +} + +void cw1200_join_complete_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_complete_work); + mutex_lock(&priv->conf_mutex); + cw1200_join_complete(priv); + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_join_complete_cb(struct cw1200_common *priv, + struct wsm_join_complete *arg) +{ + pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", + arg->status); + + if (cancel_delayed_work(&priv->join_timeout)) { + priv->join_complete_status = arg->status; + queue_work(priv->workqueue, &priv->join_complete_work); + } +} + +/* MUST be called with tx_lock held! It will be unlocked for us. */ +static void cw1200_do_join(struct cw1200_common *priv) +{ + const u8 *bssid; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct cfg80211_bss *bss = NULL; + struct wsm_protected_mgmt_policy mgmt_policy; + struct wsm_join join = { + .mode = conf->ibss_joined ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .preamble_type = WSM_JOIN_PREAMBLE_LONG, + .probe_for_join = 1, + .atim_window = 0, + .basic_rate_set = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + if (delayed_work_pending(&priv->join_timeout)) { + pr_warn("[STA] - Join request already pending, skipping..\n"); + wsm_unlock_tx(priv); + return; + } + + if (priv->join_status) + cw1200_do_unjoin(priv); + + bssid = priv->vif->bss_conf.bssid; + + bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, + bssid, NULL, 0, 0, 0); + + if (!bss && !conf->ibss_joined) { + wsm_unlock_tx(priv); + return; + } + + mutex_lock(&priv->conf_mutex); + + /* Under the conf lock: check scan status and + * bail out if it is in progress. + */ + if (atomic_read(&priv->scan.in_progress)) { + wsm_unlock_tx(priv); + goto done_put; + } + + priv->join_pending = true; + + /* Sanity check basic rates */ + if (!join.basic_rate_set) + join.basic_rate_set = 7; + + /* Sanity check beacon interval */ + if (!priv->beacon_int) + priv->beacon_int = 1; + + join.beacon_interval = priv->beacon_int; + + /* BT Coex related changes */ + if (priv->bt_present) { + if (((priv->conf_listen_interval * 100) % + priv->beacon_int) == 0) + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + priv->beacon_int); + else + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + priv->beacon_int + 1); + } + + if (priv->hw->conf.ps_dtim_period) + priv->join_dtim_period = priv->hw->conf.ps_dtim_period; + join.dtim_period = priv->join_dtim_period; + + join.channel_number = priv->channel->hw_value; + join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + + memcpy(join.bssid, bssid, sizeof(join.bssid)); + + pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", + join.bssid, + join.dtim_period, priv->beacon_int); + + if (!conf->ibss_joined) { + const u8 *ssidie; + rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssidie) { + join.ssid_len = ssidie[1]; + memcpy(join.ssid, &ssidie[2], join.ssid_len); + } + rcu_read_unlock(); + } + + if (priv->vif->p2p) { + join.flags |= WSM_JOIN_FLAGS_P2P_GO; + join.basic_rate_set = + cw1200_rate_mask_to_wsm(priv, 0xFF0); + } + + /* Enable asynchronous join calls */ + if (!conf->ibss_joined) { + join.flags |= WSM_JOIN_FLAGS_FORCE; + join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; + } + + wsm_flush_tx(priv); + + /* Stay Awake for Join and Auth Timeouts and a bit more */ + cw1200_pm_stay_awake(&priv->pm_state, + CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); + + cw1200_update_listening(priv, false); + + /* Turn on Block ACKs */ + wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, + priv->ba_rx_tid_mask); + + /* Set up timeout */ + if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { + priv->join_status = CW1200_JOIN_STATUS_JOINING; + queue_delayed_work(priv->workqueue, + &priv->join_timeout, + CW1200_JOIN_TIMEOUT); + } + + /* 802.11w protected mgmt frames */ + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; + mgmt_policy.encryptionForAuthFrame = 1; + wsm_set_protected_mgmt_policy(priv, &mgmt_policy); + + /* Perform actual join */ + if (wsm_join(priv, &join)) { + pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_update_listening(priv, priv->listening); + /* Tx lock still held, unjoin will clear it. */ + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else { + if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) + cw1200_join_complete(priv); /* Will clear tx_lock */ + + /* Upload keys */ + cw1200_upload_keys(priv); + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one + */ + priv->disable_beacon_filter = true; + } + cw1200_update_filtering(priv); + +done_put: + mutex_unlock(&priv->conf_mutex); + if (bss) + cfg80211_put_bss(priv->hw->wiphy, bss); +} + +void cw1200_join_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_timeout.work); + pr_debug("[WSM] Join timed out.\n"); + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); +} + +static void cw1200_do_unjoin(struct cw1200_common *priv) +{ + struct wsm_reset reset = { + .reset_statistics = true, + }; + + cancel_delayed_work_sync(&priv->join_timeout); + + mutex_lock(&priv->conf_mutex); + priv->join_pending = false; + + if (atomic_read(&priv->scan.in_progress)) { + if (priv->delayed_unjoin) + wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); + else + priv->delayed_unjoin = true; + goto done; + } + + priv->delayed_link_loss = false; + + if (!priv->join_status) + goto done; + + if (priv->join_status > CW1200_JOIN_STATUS_IBSS) { + wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n", + priv->join_status); + BUG_ON(1); + } + + cancel_work_sync(&priv->update_filtering_work); + cancel_work_sync(&priv->set_beacon_wakeup_period_work); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + + /* Unjoin is a reset. */ + wsm_flush_tx(priv); + wsm_keep_alive_period(priv, 0); + wsm_reset(priv, &reset); + wsm_set_output_power(priv, priv->output_power * 10); + priv->join_dtim_period = 0; + cw1200_setup_mac(priv); + cw1200_free_event_queue(priv); + cancel_work_sync(&priv->event_handler); + cw1200_update_listening(priv, priv->listening); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + + /* Disable Block ACKs */ + wsm_set_block_ack_policy(priv, 0, 0); + + priv->disable_beacon_filter = false; + cw1200_update_filtering(priv); + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + priv->setbssparams_done = false; + memset(&priv->firmware_ps_mode, 0, + sizeof(priv->firmware_ps_mode)); + + pr_debug("[STA] Unjoin completed.\n"); + +done: + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_unjoin_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, unjoin_work); + + cw1200_do_unjoin(priv); + + /* Tell the stack we're dead */ + ieee80211_connection_loss(priv->vif); + + wsm_unlock_tx(priv); +} + +int cw1200_enable_listening(struct cw1200_common *priv) +{ + struct wsm_start start = { + .mode = WSM_START_MODE_P2P_DEV, + .band = WSM_PHY_BAND_2_4G, + .beacon_interval = 100, + .dtim_period = 1, + .probe_delay = 0, + .basic_rate_set = 0x0F, + }; + + if (priv->channel) { + start.band = priv->channel->band == IEEE80211_BAND_5GHZ ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + start.channel_number = priv->channel->hw_value; + } else { + start.band = WSM_PHY_BAND_2_4G; + start.channel_number = 1; + } + + return wsm_start(priv, &start); +} + +int cw1200_disable_listening(struct cw1200_common *priv) +{ + int ret; + struct wsm_reset reset = { + .reset_statistics = true, + }; + ret = wsm_reset(priv, &reset); + return ret; +} + +void cw1200_update_listening(struct cw1200_common *priv, bool enabled) +{ + if (enabled) { + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { + if (!cw1200_enable_listening(priv)) + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + wsm_set_probe_responder(priv, true); + } + } else { + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + if (!cw1200_disable_listening(priv)) + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + wsm_set_probe_responder(priv, false); + } + } +} + +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + u16 uapsd_flags = 0; + + /* Here's the mapping AC [queue, bit] + * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] + */ + + if (arg->uapsd_enable[0]) + uapsd_flags |= 1 << 3; + + if (arg->uapsd_enable[1]) + uapsd_flags |= 1 << 2; + + if (arg->uapsd_enable[2]) + uapsd_flags |= 1 << 1; + + if (arg->uapsd_enable[3]) + uapsd_flags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 + */ + + priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); + priv->uapsd_info.min_auto_trigger_interval = 0; + priv->uapsd_info.max_auto_trigger_interval = 0; + priv->uapsd_info.auto_trigger_step = 0; + + ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); + return ret; +} + +/* ******************************************************************** */ +/* AP API */ + +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + struct sk_buff *skb; + + if (priv->mode != NL80211_IFTYPE_AP) + return 0; + + sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); + if (WARN_ON(!sta_priv->link_id)) { + wiphy_info(priv->hw->wiphy, + "[AP] No more link IDs available.\n"); + return -ENOENT; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + priv->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = CW1200_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + return 0; +} + +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_RESERVE; + entry->timestamp = jiffies; + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + spin_unlock_bh(&priv->ps_state_lock); + flush_workqueue(priv->workqueue); + return 0; +} + +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + int link_id) +{ + struct cw1200_common *priv = dev->priv; + u32 bit, prev; + + /* Zero link id means "for all link IDs" */ + if (link_id) + bit = BIT(link_id); + else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) + bit = 0; + else + bit = priv->link_id_map; + prev = priv->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && link_id && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } + break; + } +} + +void cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + + spin_lock_bh(&priv->ps_state_lock); + __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); + spin_unlock_bh(&priv->ps_state_lock); +} + +static void cw1200_ps_notify(struct cw1200_common *priv, + int link_id, bool ps) +{ + if (link_id > CW1200_MAX_STA_IN_AP_MODE) + return; + + pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); + + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); +} + +static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + u16 tim_offset, tim_length; + + pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); + + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_length); + if (!skb) { + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + return -ENOENT; + } + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. + */ + skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + skb->data[tim_offset + 4] |= 1; + else + skb->data[tim_offset + 4] &= ~1; + } + + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + wsm_update_ie(priv, &update_ie); + + dev_kfree_skb(skb); + + return 0; +} + +void cw1200_set_tim_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_tim_work); + (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); +} + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct cw1200_common *priv = dev->priv; + queue_work(priv->workqueue, &priv->set_tim_work); + return 0; +} + +void cw1200_set_cts_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_cts_work); + + u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + .ies = erp_ie, + .length = 3, + }; + u32 erp_info; + __le32 use_cts_prot; + mutex_lock(&priv->conf_mutex); + erp_info = priv->erp_info; + mutex_unlock(&priv->conf_mutex); + use_cts_prot = + erp_info & WLAN_ERP_USE_PROTECTION ? + __cpu_to_le32(1) : 0; + + erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; + + pr_debug("[STA] ERP information 0x%x\n", erp_info); + + wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot)); + wsm_update_ie(priv, &update_ie); + + return; +} + +static int cw1200_set_btcoexinfo(struct cw1200_common *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + cw1200_upload_pspoll(priv); + cw1200_upload_null(priv); + cw1200_upload_qosnull(priv); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operational_rate_set & ~0xF) { + pr_debug("[STA] STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operational_rate_set & ~0xF)); + } else { + pr_debug("[STA] STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); + } + arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); + arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); + } + + pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", + priv->mode, + arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg)); + + return ret; +} + +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct cw1200_common *priv = dev->priv; + bool do_join = false; + + mutex_lock(&priv->conf_mutex); + + pr_debug("BSS CHANGED: %08x\n", changed); + + /* TODO: BSS_CHANGED_QOS */ + /* TODO: BSS_CHANGED_TXPOWER */ + + if (changed & BSS_CHANGED_ARP_FILTER) { + struct wsm_mib_arp_ipv4_filter filter = {0}; + int i; + + pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", + info->arp_addr_cnt); + + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. + */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->arp_addr_cnt; i++) { + filter.ipv4addrs[i] = info->arp_addr_list[i]; + pr_debug("[STA] addr[%d]: 0x%X\n", + i, filter.ipv4addrs[i]); + } + filter.enable = __cpu_to_le32(1); + } + + pr_debug("[STA] arp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + wsm_set_arp_ipv4_filter(priv, &filter); + } + + if (changed & + (BSS_CHANGED_BEACON | + BSS_CHANGED_AP_PROBE_RESP | + BSS_CHANGED_BSSID | + BSS_CHANGED_SSID | + BSS_CHANGED_IBSS)) { + pr_debug("BSS_CHANGED_BEACON\n"); + priv->beacon_int = info->beacon_int; + cw1200_update_beaconing(priv); + cw1200_upload_beacon(priv); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); + + if (priv->enable_beacon != info->enable_beacon) { + cw1200_enable_beaconing(priv, info->enable_beacon); + priv->enable_beacon = info->enable_beacon; + } + } + + if (changed & BSS_CHANGED_BEACON_INT) { + pr_debug("CHANGED_BEACON_INT\n"); + if (info->ibss_joined) + do_join = true; + else if (priv->join_status == CW1200_JOIN_STATUS_AP) + cw1200_update_beaconing(priv); + } + + /* assoc/disassoc, or maybe AID changed */ + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(priv); + } + + if (changed & BSS_CHANGED_BSSID) { + pr_debug("BSS_CHANGED_BSSID\n"); + do_join = true; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BSSID | + BSS_CHANGED_IBSS | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_HT)) { + pr_debug("BSS_CHANGED_ASSOC\n"); + if (info->assoc) { + if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { + ieee80211_connection_loss(vif); + mutex_unlock(&priv->conf_mutex); + return; + } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { + priv->join_status = CW1200_JOIN_STATUS_STA; + } + } else { + do_join = true; + } + + if (info->assoc || info->ibss_joined) { + struct ieee80211_sta *sta = NULL; + __le32 htprot = 0; + + if (info->dtim_period) + priv->join_dtim_period = info->dtim_period; + priv->beacon_int = info->beacon_int; + + rcu_read_lock(); + + if (info->bssid && !info->ibss_joined) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operational_rate_set = + cw1200_rate_mask_to_wsm(priv, + sta->supp_rates[priv->channel->band]); + priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); + priv->ht_info.operation_mode = info->ht_operation_mode; + } else { + memset(&priv->ht_info, 0, + sizeof(priv->ht_info)); + priv->bss_params.operational_rate_set = -1; + } + rcu_read_unlock(); + + /* Non Greenfield stations present */ + if (priv->ht_info.operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT); + + /* Set HT protection method */ + htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2); + + /* TODO: + * STBC_param.dual_cts + * STBC_param.LSIG_TXOP_FILL + */ + + wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, + &htprot, sizeof(htprot)); + + priv->association_mode.greenfield = + cw1200_ht_greenfield(&priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + priv->association_mode.preamble = + info->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG; + priv->association_mode.basic_rate_set = __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + info->basic_rates)); + priv->association_mode.mpdu_start_spacing = + cw1200_ht_ampdu_density(&priv->ht_info); + + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + + priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; + priv->bss_params.aid = info->aid; + + if (priv->join_dtim_period < 1) + priv->join_dtim_period = 1; + + pr_debug("[STA] DTIM %d, interval: %d\n", + priv->join_dtim_period, priv->beacon_int); + pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preamble, + priv->association_mode.greenfield, + priv->bss_params.aid, + priv->bss_params.operational_rate_set, + priv->association_mode.basic_rate_set); + wsm_set_association_mode(priv, &priv->association_mode); + + if (!info->ibss_joined) { + wsm_keep_alive_period(priv, 30 /* sec */); + wsm_set_bss_params(priv, &priv->bss_params); + priv->setbssparams_done = true; + cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); + cw1200_set_pm(priv, &priv->powersave_mode); + } + if (priv->vif->p2p) { + pr_debug("[STA] Setting p2p powersave configuration.\n"); + wsm_set_p2p_ps_modeinfo(priv, + &priv->p2p_ps_modeinfo); + } + if (priv->bt_present) + cw1200_set_btcoexinfo(priv); + } else { + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + + /* ERP Protection */ + if (changed & (BSS_CHANGED_ASSOC | + BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_ERP_PREAMBLE)) { + u32 prev_erp_info = priv->erp_info; + if (info->use_cts_prot) + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (info->use_short_preamble) + priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); + + if (prev_erp_info != priv->erp_info) + queue_work(priv->workqueue, &priv->set_cts_work); + } + + /* ERP Slottime */ + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? + __cpu_to_le32(9) : __cpu_to_le32(20); + pr_debug("[STA] Slot time: %d us.\n", + __le32_to_cpu(slot_time)); + wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, + &slot_time, sizeof(slot_time)); + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rollingAverageCount = 8, + }; + pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; + + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. + */ + + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + if (priv->cqm_use_rssi) { + threshold.upperThreshold = + info->cqm_rssi_thold + info->cqm_rssi_hyst; + threshold.lowerThreshold = + info->cqm_rssi_thold; + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; + } else { + threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; + threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; + } + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. + */ + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + if (priv->cqm_use_rssi) + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; + } + wsm_set_rcpi_rssi_threshold(priv, &threshold); + } + mutex_unlock(&priv->conf_mutex); + + if (do_join) { + wsm_lock_tx(priv); + cw1200_do_join(priv); /* Will unlock it for us */ + } +} + +void cw1200_multicast_start_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_start_work); + long tmo = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; + + cancel_work_sync(&priv->multicast_stop_work); + + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv); + cw1200_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, jiffies + tmo); + wsm_unlock_tx(priv); + } +} + +void cw1200_multicast_stop_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_stop_work); + + if (priv->aid0_bit_set) { + del_timer_sync(&priv->mcast_timeout); + wsm_lock_tx(priv); + priv->aid0_bit_set = false; + cw1200_set_tim_impl(priv, false); + wsm_unlock_tx(priv); + } +} + +void cw1200_mcast_timeout(unsigned long arg) +{ + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + wiphy_warn(priv->hw->wiphy, + "Multicast delivery timeout.\n"); + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + cw1200_bh_wakeup(priv); + spin_unlock_bh(&priv->ps_state_lock); +} + +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. + */ + + /* Note that we still need this function stubbed. */ + return -ENOTSUPP; +} + +/* ******************************************************************** */ +/* WSM callback */ +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg) +{ + pr_debug("[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); + + if (arg->multicast) { + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); + if (arg->stop) { + priv->tx_multicast = false; + } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than it could be saved. + */ + cw1200_pm_stay_awake(&priv->pm_state, + priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024); + priv->tx_multicast = (priv->aid0_bit_set && + priv->buffered_multicasts); + if (priv->tx_multicast) { + cancel_tmo = true; + cw1200_bh_wakeup(priv); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); + } else { + spin_lock_bh(&priv->ps_state_lock); + cw1200_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + cw1200_bh_wakeup(priv); + } + return; +} + +/* ******************************************************************** */ +/* AP privates */ + +static int cw1200_upload_beacon(struct cw1200_common *priv) +{ + int ret = 0; + struct ieee80211_mgmt *mgmt; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + + u16 tim_offset; + u16 tim_len; + + if (priv->mode == NL80211_IFTYPE_STATION || + priv->mode == NL80211_IFTYPE_MONITOR || + priv->mode == NL80211_IFTYPE_UNSPECIFIED) + goto done; + + if (priv->vif->p2p) + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_len); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + if (ret) + goto done; + + /* TODO: Distill probe resp; remove TIM + * and any other beacon-specific IEs + */ + mgmt = (void *)frame.skb->data; + mgmt->frame_control = + __cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + if (priv->vif->p2p) { + ret = wsm_set_probe_responder(priv, true); + } else { + ret = wsm_set_template_frame(priv, &frame); + wsm_set_probe_responder(priv, false); + } + +done: + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_pspoll(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_null(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_qosnull(struct cw1200_common *priv) +{ + int ret = 0; + /* TODO: This needs to be implemented + + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_QOS_NULL, + .rate = 0xFF, + }; + + frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + */ + return ret; +} + +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable) +{ + struct wsm_beacon_transmit transmit = { + .enable_beaconing = enable, + }; + + return wsm_beacon_transmit(priv, &transmit); +} + +static int cw1200_start_ap(struct cw1200_common *priv) +{ + int ret; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_start start = { + .mode = priv->vif->p2p ? + WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channel_number = priv->channel->hw_value, + .beacon_interval = conf->beacon_int, + .dtim_period = conf->dtim_period, + .preamble = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG, + .probe_delay = 100, + .basic_rate_set = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + memset(start.ssid, 0, sizeof(start.ssid)); + if (!conf->hidden_ssid) { + start.ssid_len = conf->ssid_len; + memcpy(start.ssid, conf->ssid, start.ssid_len); + } + + priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; + + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + + pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", + start.channel_number, start.band, + start.beacon_interval, start.dtim_period, + start.basic_rate_set, + start.ssid_len, start.ssid); + ret = wsm_start(priv, &start); + if (!ret) + ret = cw1200_upload_keys(priv); + if (!ret && priv->vif->p2p) { + pr_debug("[AP] Setting p2p powersave configuration.\n"); + wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); + } + if (!ret) { + wsm_set_block_ack_policy(priv, 0, 0); + priv->join_status = CW1200_JOIN_STATUS_AP; + cw1200_update_filtering(priv); + } + wsm_set_operational_mode(priv, &mode); + return ret; +} + +static int cw1200_update_beaconing(struct cw1200_common *priv) +{ + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + + if (priv->mode == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (priv->join_status != CW1200_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + pr_debug("ap restarting\n"); + wsm_lock_tx(priv); + if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) + wsm_reset(priv, &reset); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + cw1200_start_ap(priv); + wsm_unlock_tx(priv); + } else + pr_debug("ap started join_status: %d\n", + priv->join_status); + } + return 0; +} diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h new file mode 100644 index 00000000000..35babb62cc6 --- /dev/null +++ b/drivers/net/wireless/cw1200/sta.h @@ -0,0 +1,123 @@ +/* + * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +int cw1200_start(struct ieee80211_hw *dev); +void cw1200_stop(struct ieee80211_hw *dev); +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +int cw1200_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); +int cw1200_config(struct ieee80211_hw *dev, u32 changed); +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop); + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_join_complete_cb(struct cw1200_common *priv, + struct wsm_join_complete *arg); + +/* ******************************************************************** */ +/* WSM events */ + +void cw1200_free_event_queue(struct cw1200_common *priv); +void cw1200_event_handler(struct work_struct *work); +void cw1200_bss_loss_work(struct work_struct *work); +void cw1200_bss_params_work(struct work_struct *work); +void cw1200_keep_alive_work(struct work_struct *work); +void cw1200_tx_failure_work(struct work_struct *work); + +void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good, + int bad); +static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv, + int init, int good, int bad) +{ + spin_lock(&priv->bss_loss_lock); + __cw1200_cqm_bssloss_sm(priv, init, good, bad); + spin_unlock(&priv->bss_loss_lock); +} + +/* ******************************************************************** */ +/* Internal API */ + +int cw1200_setup_mac(struct cw1200_common *priv); +void cw1200_join_timeout(struct work_struct *work); +void cw1200_unjoin_work(struct work_struct *work); +void cw1200_join_complete_work(struct work_struct *work); +void cw1200_wep_key_work(struct work_struct *work); +void cw1200_update_listening(struct cw1200_common *priv, bool enabled); +void cw1200_update_filtering(struct cw1200_common *priv); +void cw1200_update_filtering_work(struct work_struct *work); +void cw1200_set_beacon_wakeup_period_work(struct work_struct *work); +int cw1200_enable_listening(struct cw1200_common *priv); +int cw1200_disable_listening(struct cw1200_common *priv); +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); +void cw1200_ba_work(struct work_struct *work); +void cw1200_ba_timer(unsigned long arg); + +/* AP stuffs */ +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); + +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg); +void cw1200_set_tim_work(struct work_struct *work); +void cw1200_set_cts_work(struct work_struct *work); +void cw1200_multicast_start_work(struct work_struct *work); +void cw1200_multicast_stop_work(struct work_struct *work); +void cw1200_mcast_timeout(unsigned long arg); + +#endif diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c new file mode 100644 index 00000000000..5862c373d71 --- /dev/null +++ b/drivers/net/wireless/cw1200/txrx.c @@ -0,0 +1,1473 @@ +/* + * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <net/mac80211.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "sta.h" +#include "debug.h" + +#define CW1200_INVALID_RATE_ID (0xFF) + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb); +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate); + +/* ******************************************************************** */ +/* TX queue lock / unlock */ + +static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_lock(&priv->tx_queue[i]); +} + +static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_unlock(&priv->tx_queue[i]); +} + +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void tx_policy_build(const struct cw1200_common *priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i, j; + unsigned limit = priv->short_frame_max_tx_count; + unsigned total = 0; + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + + /* Sort rates in descending order. */ + for (i = 1; i < count; ++i) { + if (rates[i].idx < 0) { + count = i; + break; + } + if (rates[i].idx > rates[i - 1].idx) { + struct ieee80211_tx_rate tmp = rates[i - 1]; + rates[i - 1] = rates[i]; + rates[i] = tmp; + } + } + + /* Eliminate duplicates. */ + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + count = i + 1; + + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. + */ + if (limit < count) + limit = count; + if (total > limit) { + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; + limit -= rates[i].count; + } + } + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more + */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (retries > 0x0F) { + rates[i].count = 0x0f; + retries = 0x0F; + } + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } + + pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n", + count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count); +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. + */ + struct tx_policy_cache_entry *it; + /* First search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +void tx_policy_clean(struct cw1200_common *priv) +{ + int idx, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy_cache_entry *entry; + + cw1200_tx_queues_lock(priv); + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + + for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) { + entry = &cache->cache[idx]; + /* Policy usage count should be 0 at this time as all queues + should be empty + */ + if (WARN_ON(entry->policy.usage_count)) { + entry->policy.usage_count = 0; + list_move(&entry->link, &cache->free); + } + memset(&entry->policy, 0, sizeof(entry->policy)); + } + if (locked) + cw1200_tx_queues_unlock(priv); + + cw1200_tx_queues_unlock(priv); + spin_unlock_bh(&cache->lock); +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +static int tx_policy_get(struct cw1200_common *priv, + struct ieee80211_tx_rate *rates, + size_t count, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy wanted; + + tx_policy_build(priv, &wanted, rates, count); + + spin_lock_bh(&cache->lock); + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return CW1200_INVALID_RATE_ID; + } + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + pr_debug("[TX policy] Used TX policy: %d\n", idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list + */ + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + pr_debug("[TX policy] New TX policy: %d\n", idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (list_empty(&cache->free)) { + /* Lock TX queues. */ + cw1200_tx_queues_lock(priv); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void tx_policy_put(struct cw1200_common *priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (locked && !usage) { + /* Unlock TX queues. */ + cw1200_tx_queues_unlock(priv); + } + spin_unlock_bh(&cache->lock); +} + +static int tx_policy_upload(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .num = 0, + }; + spin_lock_bh(&cache->lock); + + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_tx_rate_retry_policy *dst = + &arg.tbl[arg.num]; + dst->index = i; + dst->short_retries = priv->short_frame_max_tx_count; + dst->long_retries = priv->long_frame_max_tx_count; + + dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED | + WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT; + memcpy(dst->rate_count_indices, src->tbl, + sizeof(dst->rate_count_indices)); + src->uploaded = 1; + ++arg.num; + } + } + spin_unlock_bh(&cache->lock); + cw1200_debug_tx_cache_miss(priv); + pr_debug("[TX policy] Upload %d policies\n", arg.num); + return wsm_set_tx_rate_retry_policy(priv, &arg); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_policy_upload_work); + + pr_debug("[TX] TX policy upload.\n"); + tx_policy_upload(priv); + + wsm_unlock_tx(priv); + cw1200_tx_queues_unlock(priv); +} + +/* ******************************************************************** */ +/* cw1200 TX implementation */ + +struct cw1200_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct cw1200_sta_priv *sta_priv; + struct ieee80211_sta *sta; + struct cw1200_txpriv txpriv; +}; + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) +{ + u32 ret = 0; + int i; + for (i = 0; i < 32; ++i) { + if (rates & BIT(i)) + ret |= BIT(priv->rates[i].hw_value); + } + return ret; +} + +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &priv->mcs_rates[rate->idx]; + return &priv->hw->wiphy->bands[priv->channel->band]-> + bitrates[rate->idx]; +} + +static int +cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (t->sta && t->sta_priv->link_id) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; + } + } else { + t->txpriv.link_id = cw1200_find_link_id(priv, t->da); + if (!t->txpriv.link_id) + t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + wiphy_err(priv->hw->wiphy, + "No more link IDs available.\n"); + return -ENOENT; + } + t->txpriv.raw_link_id = t->txpriv.link_id; + } + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; + if (t->sta && (t->sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = CW1200_LINK_ID_UAPSD; + return 0; +} + +static void +cw1200_tx_h_pm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_auth(t->hdr->frame_control)) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); + } +} + +static void +cw1200_tx_h_calc_tid(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} + +static int +cw1200_tx_h_crypt(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (!t->tx_info->control.hw_key || + !ieee80211_has_protected(t->hdr->frame_control)) + return 0; + + t->hdrlen += t->tx_info->control.hw_key->iv_len; + skb_put(t->skb, t->tx_info->control.hw_key->icv_len); + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_put(t->skb, 8); /* MIC space */ + + return 0; +} + +static int +cw1200_tx_h_align(struct cw1200_common *priv, + struct cw1200_txinfo *t, + u8 *flags) +{ + size_t offset = (size_t)t->skb->data & 3; + + if (!offset) + return 0; + + if (offset & 1) { + wiphy_err(priv->hw->wiphy, + "Bug: attempt to transmit a frame with wrong alignment: %zu\n", + offset); + return -EINVAL; + } + + if (skb_headroom(t->skb) < offset) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for DMA alignment. headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + cw1200_debug_tx_align(priv); + return 0; +} + +static int +cw1200_tx_h_action(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)t->hdr; + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} + +/* Add WSM header */ +static struct wsm_tx * +cw1200_tx_h_wsm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct wsm_tx *wsm; + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for WSM header. headroom: %d\n", + skb_headroom(t->skb)); + return NULL; + } + + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queue_id = wsm_queue_id_to_wsm(t->queue); + return wsm; +} + +/* BT Coex specific handling */ +static void +cw1200_tx_h_bt(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + u8 priority = 0; + + if (!priv->bt_present) + return; + + if (ieee80211_is_nullfunc(t->hdr->frame_control)) { + priority = WSM_EPTA_PRIORITY_MGT; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + __be16 *ethertype = (__be16 *)&payload[6]; + if (be16_to_cpu(*ethertype) == ETH_P_PAE) + priority = WSM_EPTA_PRIORITY_EAPOL; + } else if (ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control)) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (le16_to_cpu(mgt_frame->u.assoc_req.listen_interval) < + priv->listen_interval) { + pr_debug("Modified Listen Interval to %d from %d\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + * the one read from SDD + */ + mgt_frame->u.assoc_req.listen_interval = cpu_to_le16(priv->listen_interval); + } + } + + if (!priority) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queue_id == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queue_id == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } + + pr_debug("[TX] EPTA priority %d.\n", priority); + + wsm->flags |= priority << 1; +} + +static int +cw1200_tx_h_rate_policy(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + + t->txpriv.rate_id = tx_policy_get(priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) + return -EFAULT; + + wsm->flags |= t->txpriv.rate_id << 4; + + t->rate = cw1200_get_tx_rate(priv, + &t->tx_info->control.rates[0]), + wsm->max_tx_rate = t->rate->hw_value; + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (cw1200_ht_greenfield(&priv->ht_info)) + wsm->ht_tx_parameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->ht_tx_parameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + + if (tx_policy_renew) { + pr_debug("[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Better to reimplement task scheduling with + * a counter. TODO. + */ + wsm_lock_tx_async(priv); + cw1200_tx_queues_lock(priv); + if (queue_work(priv->workqueue, + &priv->tx_policy_upload_work) <= 0) { + cw1200_tx_queues_unlock(priv); + wsm_unlock_tx(priv); + } + } + return 0; +} + +static bool +cw1200_tx_h_pm_state(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + int was_buffered = 1; + + if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + + if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++; + + return !was_buffered; +} + +/* ******************************************************************** */ + +void cw1200_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = CW1200_MAX_TID, + .txpriv.rate_id = CW1200_INVALID_RATE_ID, + }; + struct ieee80211_sta *sta; + struct wsm_tx *wsm; + bool tid_update = 0; + u8 flags = 0; + int ret; + + if (priv->bh_error) + goto drop; + + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + if (control) { + t.sta = control->sta; + t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv; + } + + if (WARN_ON(t.queue >= 4)) + goto drop; + + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; + + pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", + skb->len, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + cw1200_tx_h_pm(priv, &t); + cw1200_tx_h_calc_tid(priv, &t); + ret = cw1200_tx_h_crypt(priv, &t); + if (ret) + goto drop; + ret = cw1200_tx_h_align(priv, &t, &flags); + if (ret) + goto drop; + ret = cw1200_tx_h_action(priv, &t); + if (ret) + goto drop; + wsm = cw1200_tx_h_wsm(priv, &t); + if (!wsm) { + ret = -ENOMEM; + goto drop; + } + wsm->flags |= flags; + cw1200_tx_h_bt(priv, &t, wsm); + ret = cw1200_tx_h_rate_policy(priv, &t, wsm); + if (ret) + goto drop; + + rcu_read_lock(); + sta = rcu_dereference(t.sta); + + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = cw1200_tx_h_pm_state(priv, &t); + BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], + t.skb, &t.txpriv)); + } + spin_unlock_bh(&priv->ps_state_lock); + + if (tid_update && sta) + ieee80211_sta_set_buffered(sta, t.txpriv.tid, true); + + rcu_read_unlock(); + + cw1200_bh_wakeup(priv); + + return; + +drop: + cw1200_skb_dtor(priv, skb, &t.txpriv); + return; +} + +/* ******************************************************************** */ + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; +} + +static int cw1200_handle_pspoll(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int drop = 1; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (sta) { + struct cw1200_sta_priv *sta_priv; + sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); + } + rcu_read_unlock(); + if (!link_id) + goto done; + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is queued already. */ + for (i = 0; i < 4; ++i) { + if (cw1200_queue_get_num_queued(&priv->tx_queue[i], + pspoll_mask)) { + cw1200_bh_wakeup(priv); + drop = 1; + break; + } + } + pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); +done: + return drop; +} + +/* ******************************************************************** */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + int link_id, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + struct sk_buff *skb; + const struct cw1200_txpriv *txpriv; + + pr_debug("[TX] TX confirm: %d, %d.\n", + arg->status, arg->ack_failures); + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return; + } + + if (WARN_ON(queue_id >= 4)) + return; + + if (arg->status) + pr_debug("TX failed: %d.\n", arg->status); + + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = link_id, + .stop = 1, + .multicast = !link_id, + }; + cw1200_suspend_resume(priv, &suspend); + wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n", + link_id, + cw1200_queue_get_generation(arg->packet_id) + 1, + priv->sta_asleep_mask); + cw1200_queue_requeue(queue, arg->packet_id); + spin_lock_bh(&priv->ps_state_lock); + if (!link_id) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) { + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + } + spin_unlock_bh(&priv->ps_state_lock); + } else if (!cw1200_queue_get_skb(queue, arg->packet_id, + &skb, &txpriv)) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + int tx_count = arg->ack_failures; + u8 ht_flags = 0; + int i; + + if (cw1200_ht_greenfield(&priv->ht_info)) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_state && + arg->packet_id == priv->bss_loss_confirm_id) { + if (arg->status) { + /* Recovery failed */ + __cw1200_cqm_bssloss_sm(priv, 0, 0, 1); + } else { + /* Recovery succeeded */ + __cw1200_cqm_bssloss_sm(priv, 0, 1, 0); + } + } + spin_unlock(&priv->bss_loss_lock); + + if (!arg->status) { + tx->flags |= IEEE80211_TX_STAT_ACK; + ++tx_count; + cw1200_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. + */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ + cw1200_debug_txed_agg(priv); + } + } else { + if (tx_count) + ++tx_count; + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + + /* Pull off any crypto trailers that we added on */ + if (tx->control.hw_key) { + skb_trim(skb, skb->len - tx->control.hw_key->icv_len); + if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_trim(skb, skb->len - 8); /* MIC space */ + } + cw1200_queue_remove(queue, arg->packet_id); + } + /* XXX TODO: Only wake if there are pending transmits.. */ + cw1200_bh_wakeup(priv); +} + +static void cw1200_notify_buffered_tx(struct cw1200_common *priv, + struct sk_buff *skb, int link_id, int tid) +{ + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + + if (link_id && tid < CW1200_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->ps_state_lock); + if (!WARN_ON(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->ps_state_lock); + + if (!still_buffered && tid < CW1200_MAX_TID) { + hdr = (struct ieee80211_hdr *)skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +} + +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv) +{ + skb_pull(skb, txpriv->offset); + if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { + cw1200_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(priv, txpriv->rate_id); + } + ieee80211_tx_status(priv->hw, skb); +} + +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + int link_id, + struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct cw1200_link_entry *entry = NULL; + unsigned long grace_period; + + bool early_data = false; + bool p2p = priv->vif && priv->vif->p2p; + size_t hdrlen; + hdr->flag = 0; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + goto drop; + } + + if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) { + entry = &priv->link_id_db[link_id - 1]; + if (entry->status == CW1200_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } else if (p2p && + ieee80211_is_action(frame->frame_control) && + (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + pr_debug("[RX] Going to MAP&RESET link ID\n"); + WARN_ON(work_pending(&priv->linkid_reset_work)); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = 0; + schedule_work(&priv->linkid_reset_work); + } + + if (link_id && p2p && + ieee80211_is_action(frame->frame_control) && + (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + /* Link ID already exists for the ACTION frame. + * Reset and Remap + */ + WARN_ON(work_pending(&priv->linkid_reset_work)); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = link_id; + schedule_work(&priv->linkid_reset_work); + } + if (arg->status) { + if (arg->status == WSM_STATUS_MICFAILURE) { + pr_debug("[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + pr_debug("[RX] No key found.\n"); + goto drop; + } else { + pr_debug("[RX] Receive failure: %d.\n", + arg->status); + goto drop; + } + } + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n"); + goto drop; + } + + if (ieee80211_is_pspoll(frame->frame_control)) + if (cw1200_handle_pspoll(priv, skb)) + goto drop; + + hdr->band = ((arg->channel_number & 0xff00) || + (arg->channel_number > 14)) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channel_number, + hdr->band); + + if (arg->rx_rate >= 14) { + hdr->flag |= RX_FLAG_HT; + hdr->rate_idx = arg->rx_rate - 14; + } else if (arg->rx_rate >= 4) { + hdr->rate_idx = arg->rx_rate - 2; + } else { + hdr->rate_idx = arg->rx_rate; + } + + hdr->signal = (s8)arg->rcpi_rssi; + hdr->antenna = 0; + + hdrlen = ieee80211_hdrlen(frame->frame_control); + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + size_t iv_len = 0, icv_len = 0; + + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed. + */ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + pr_warn("Unknown encryption type %d\n", + WSM_RX_STATUS_ENCRYPTION(arg->flags)); + goto drop; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == WSM_STATUS_MICFAILURE) + icv_len = 0; + + if (skb->len < hdrlen + iv_len + icv_len) { + wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n"); + goto drop; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + + /* Remove TSF from the end of frame */ + if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) { + memcpy(&hdr->mactime, skb->data + skb->len - 8, 8); + hdr->mactime = le64_to_cpu(hdr->mactime); + if (skb->len >= 8) + skb_trim(skb, skb->len - 8); + } else { + hdr->mactime = 0; + } + + cw1200_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + cw1200_debug_rxed_agg(priv); + + if (ieee80211_is_action(frame->frame_control) && + (arg->flags & WSM_RX_STATUS_ADDRESS1)) { + if (cw1200_handle_action_rx(priv, skb)) + return; + } else if (ieee80211_is_beacon(frame->frame_control) && + !arg->status && + !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid, + ETH_ALEN)) { + const u8 *tim_ie; + u8 *ies = ((struct ieee80211_mgmt *) + (skb->data))->u.beacon.variable; + size_t ies_len = skb->len - (ies - (u8 *)(skb->data)); + + tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); + if (tim_ie) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *)&tim_ie[2]; + + if (priv->join_dtim_period != tim->dtim_period) { + priv->join_dtim_period = tim->dtim_period; + queue_work(priv->workqueue, + &priv->set_beacon_wakeup_period_work); + } + } + + /* Disable beacon filter once we're associated... */ + if (priv->disable_beacon_filter && + (priv->vif->bss_conf.assoc || + priv->vif->bss_conf.ibss_joined)) { + priv->disable_beacon_filter = false; + queue_work(priv->workqueue, + &priv->update_filtering_work); + } + } + + /* Stay awake after frame is received to give + * userspace chance to react and acquire appropriate + * wakelock. + */ + if (ieee80211_is_auth(frame->frame_control)) + grace_period = 5 * HZ; + else if (ieee80211_is_deauth(frame->frame_control)) + grace_period = 5 * HZ; + else + grace_period = 1 * HZ; + cw1200_pm_stay_awake(&priv->pm_state, grace_period); + + if (early_data) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == CW1200_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } + *skb_p = NULL; + + return; + +drop: + /* TODO: update failure counters */ + return; +} + +/* ******************************************************************** */ +/* Security */ + +int cw1200_alloc_key(struct cw1200_common *priv) +{ + int idx; + + idx = ffs(~priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + priv->key_map |= BIT(idx); + priv->keys[idx].index = idx; + return idx; +} + +void cw1200_free_key(struct cw1200_common *priv, int idx) +{ + BUG_ON(!(priv->key_map & BIT(idx))); + memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); + priv->key_map &= ~BIT(idx); +} + +void cw1200_free_keys(struct cw1200_common *priv) +{ + memset(&priv->keys, 0, sizeof(priv->keys)); + priv->key_map = 0; +} + +int cw1200_upload_keys(struct cw1200_common *priv) +{ + int idx, ret = 0; + for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) + if (priv->key_map & BIT(idx)) { + ret = wsm_add_key(priv, &priv->keys[idx]); + if (ret < 0) + break; + } + return ret; +} + +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, linkid_reset_work); + int temp_linkid; + + if (!priv->action_linkid) { + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = cw1200_alloc_link_id(priv, + &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(priv->workqueue); + /* Release the link ID */ + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].prev_status = + priv->link_id_db[temp_linkid - 1].status; + priv->link_id_db[temp_linkid - 1].status = + CW1200_LINK_RESET; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } + } else { + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[priv->action_linkid - 1].prev_status = + priv->link_id_db[priv->action_linkid - 1].status; + priv->link_id_db[priv->action_linkid - 1].status = + CW1200_LINK_RESET_REMAP; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + flush_workqueue(priv->workqueue); + } +} + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && + !priv->tx_queue_stats.link_map_cache[i + 1]) { + unsigned long inactivity = + now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; + pr_debug("[AP] STA added, link_id: %d\n", ret); + entry->status = CW1200_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, CW1200_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } else { + wiphy_info(priv->hw->wiphy, + "[AP] Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +void cw1200_link_id_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_work); + wsm_flush_tx(priv); + cw1200_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(priv); +} + +void cw1200_link_id_gc_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_gc_work.work); + struct wsm_reset reset = { + .reset_statistics = false, + }; + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + return; + + wsm_lock_tx(priv); + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || + (priv->link_id_db[i].status == CW1200_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != CW1200_LINK_HARD) + priv->link_id_db[i].status = CW1200_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, + ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + if (need_reset) { + reset.link_id = i + 1; + wsm_reset(priv, &reset); + } + map_link.link_id = i + 1; + wsm_map_link(priv, &map_link); + next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->ps_state_lock); + } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + + CW1200_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = CW1200_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + memset(map_link.mac_addr, 0, ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + reset.link_id = i + 1; + wsm_reset(priv, &reset); + spin_lock_bh(&priv->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } + } else if (priv->link_id_db[i].status == CW1200_LINK_RESET || + priv->link_id_db[i].status == + CW1200_LINK_RESET_REMAP) { + int status = priv->link_id_db[i].status; + priv->link_id_db[i].status = + priv->link_id_db[i].prev_status; + priv->link_id_db[i].timestamp = now; + reset.link_id = i + 1; + spin_unlock_bh(&priv->ps_state_lock); + wsm_reset(priv, &reset); + if (status == CW1200_LINK_RESET_REMAP) { + memcpy(map_link.mac_addr, + priv->link_id_db[i].mac, + ETH_ALEN); + map_link.link_id = i + 1; + wsm_map_link(priv, &map_link); + next_gc = min(next_gc, + CW1200_LINK_ID_GC_TIMEOUT); + } + spin_lock_bh(&priv->ps_state_lock); + } + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); + pr_debug("[AP] STA removed, link_id: %d\n", + reset.link_id); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (next_gc != -1) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(priv); +} diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h new file mode 100644 index 00000000000..492a4e14213 --- /dev/null +++ b/drivers/net/wireless/cw1200/txrx.h @@ -0,0 +1,106 @@ +/* + * Datapath interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_TXRX_H +#define CW1200_TXRX_H + +#include <linux/list.h> + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; +/* extern */ struct wsm_tx_confirm; +/* extern */ struct cw1200_txpriv; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; + u8 usage_count; + u8 retry_count; + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; /* Protect policy cache */ +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct cw1200_common *priv); +void tx_policy_upload_work(struct work_struct *work); +void tx_policy_clean(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, + u32 rates); +void cw1200_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + int link_id, + struct wsm_tx_confirm *arg); +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + int link_id, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* Timeout */ + +void cw1200_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int cw1200_alloc_key(struct cw1200_common *priv); +void cw1200_free_key(struct cw1200_common *priv, int idx); +void cw1200_free_keys(struct cw1200_common *priv); +int cw1200_upload_keys(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work); + +#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); +void cw1200_link_id_work(struct work_struct *work); +void cw1200_link_id_gc_work(struct work_struct *work); + + +#endif /* CW1200_TXRX_H */ diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c new file mode 100644 index 00000000000..cbb74d7a9be --- /dev/null +++ b/drivers/net/wireless/cw1200/wsm.c @@ -0,0 +1,1822 @@ +/* + * WSM host interface (HI) implementation for + * ST-Ericsson CW1200 mac80211 drivers. + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/skbuff.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/random.h> + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "sta.h" +#include "debug.h" + +#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ +#define WSM_CMD_MAX_TIMEOUT (3 * HZ) + +#define WSM_SKIP(buf, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + goto underflow; \ + (buf)->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + goto underflow; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, type2, cvt) \ + ({ \ + type val; \ + if ((buf)->data + sizeof(type) > (buf)->end) \ + goto underflow; \ + val = cvt(*(type2 *)(buf)->data); \ + (buf)->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + if (wsm_buf_reserve((buf), size)) \ + goto nomem; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, type2, cvt) \ + do { \ + if ((buf)->data + sizeof(type) > (buf)->end) \ + if (wsm_buf_reserve((buf), sizeof(type))) \ + goto nomem; \ + *(type2 *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo); + +#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux)) +#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux)) + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct cw1200_common *priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) + return -EINVAL; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(priv, buf, arg, + WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct cw1200_common *priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (WARN_ON(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +struct wsm_mib { + u16 mib_id; + void *buf; + size_t buf_size; +}; + +int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mib_id = mib_id, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mib_id); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, &mib_buf, + WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (WARN_ON(WSM_GET16(buf) != arg->mib_id)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mib_id = mib_id, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mib_id); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(priv, buf, &mib_buf, + WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_write_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + int ret; + + ret = wsm_generic_confirm(priv, arg, buf); + if (ret) + return ret; + + if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) { + /* OperationalMode: update PM status. */ + const char *p = arg->buf; + cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false); + } + return 0; +} + +/* ******************************************************************** */ + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) +{ + int i; + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + if (arg->num_channels > 48) + return -EINVAL; + + if (arg->num_ssids > 2) + return -EINVAL; + + if (arg->band > 1) + return -EINVAL; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->type); + WSM_PUT8(buf, arg->flags); + WSM_PUT8(buf, arg->max_tx_rate); + WSM_PUT32(buf, arg->auto_scan_interval); + WSM_PUT8(buf, arg->num_probes); + WSM_PUT8(buf, arg->num_channels); + WSM_PUT8(buf, arg->num_ssids); + WSM_PUT8(buf, arg->probe_delay); + + for (i = 0; i < arg->num_channels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].min_chan_time); + WSM_PUT32(buf, arg->ch[i].max_chan_time); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->num_ssids; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, + WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + + +static int wsm_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, + int link_id) +{ + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packet_id = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.tx_rate = WSM_GET8(buf); + tx_confirm.ack_failures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.media_delay = WSM_GET32(buf); + tx_confirm.tx_queue_delay = WSM_GET32(buf); + + cw1200_tx_confirm_cb(priv, link_id, &tx_confirm); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_multi_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, int link_id) +{ + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (WARN_ON(count <= 0)) + return -EINVAL; + + if (count > 1) { + /* We already released one buffer, now for the rest */ + ret = wsm_release_tx_buffer(priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + cw1200_bh_wakeup(priv); + } + + cw1200_debug_txed_multi(priv, count); + for (i = 0; i < count; ++i) { + ret = wsm_tx_confirm(priv, buf, link_id); + if (ret) + return ret; + } + return ret; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct cw1200_common *priv, + struct wsm_join_cnf *arg, + struct wsm_buf *buf) +{ + arg->status = WSM_GET32(buf); + if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS) + return -EINVAL; + + arg->min_power_level = WSM_GET32(buf); + arg->max_power_level = WSM_GET32(buf); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_join_cnf resp; + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channel_number); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atim_window); + WSM_PUT8(buf, arg->preamble_type); + WSM_PUT8(buf, arg->probe_for_join); + WSM_PUT8(buf, arg->dtim_period); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssid_len); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beacon_interval); + WSM_PUT32(buf, arg->basic_rate_set); + + priv->tx_burst_idx = -1; + ret = wsm_cmd_send(priv, buf, &resp, + WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT); + /* TODO: Update state based on resp.min|max_power_level */ + + priv->join_complete_status = resp.status; + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0)); + WSM_PUT8(buf, arg->beacon_lost_count); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operational_rate_set); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->index); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg, u8 id) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, queue_id_to_wmm_aci[id]); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* Implemented according to specification. */ + + WSM_PUT16(buf, arg->params[3].cwmin); + WSM_PUT16(buf, arg->params[2].cwmin); + WSM_PUT16(buf, arg->params[1].cwmin); + WSM_PUT16(buf, arg->params[0].cwmin); + + WSM_PUT16(buf, arg->params[3].cwmax); + WSM_PUT16(buf, arg->params[2].cwmax); + WSM_PUT16(buf, arg->params[1].cwmax); + WSM_PUT16(buf, arg->params[0].cwmax); + + WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + + WSM_PUT16(buf, arg->params[3].txop_limit); + WSM_PUT16(buf, arg->params[2].txop_limit); + WSM_PUT16(buf, arg->params[1].txop_limit); + WSM_PUT16(buf, arg->params[0].txop_limit); + + WSM_PUT32(buf, arg->params[3].max_rx_lifetime); + WSM_PUT32(buf, arg->params[2].max_rx_lifetime); + WSM_PUT32(buf, arg->params[1].max_rx_lifetime); + WSM_PUT32(buf, arg->params[0].max_rx_lifetime); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->switch_count); + WSM_PUT16(buf, arg->channel_number); + + priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT); + if (ret) + priv->channel_switch_in_progress = 0; + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + priv->ps_mode_switch_in_progress = 1; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->fast_psm_idle_period); + WSM_PUT8(buf, arg->ap_psm_change_period); + WSM_PUT8(buf, arg->min_auto_pspoll_period); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channel_number); + WSM_PUT32(buf, arg->ct_window); + WSM_PUT32(buf, arg->beacon_interval); + WSM_PUT8(buf, arg->dtim_period); + WSM_PUT8(buf, arg->preamble); + WSM_PUT8(buf, arg->probe_delay); + WSM_PUT8(buf, arg->ssid_len); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basic_rate_set); + + priv->tx_burst_idx = -1; + ret = wsm_cmd_send(priv, buf, NULL, + WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ +int wsm_set_probe_responder(struct cw1200_common *priv, bool enable) +{ + priv->rx_filter.probeResponder = enable; + return wsm_set_rx_filter(priv, &priv->rx_filter); +} + +/* ******************************************************************** */ +/* WSM indication events implementation */ +const char * const cw1200_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" +}; + +static int wsm_startup_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + priv->wsm_caps.input_buffers = WSM_GET16(buf); + priv->wsm_caps.input_buffer_size = WSM_GET16(buf); + priv->wsm_caps.hw_id = WSM_GET16(buf); + priv->wsm_caps.hw_subid = WSM_GET16(buf); + priv->wsm_caps.status = WSM_GET16(buf); + priv->wsm_caps.fw_cap = WSM_GET16(buf); + priv->wsm_caps.fw_type = WSM_GET16(buf); + priv->wsm_caps.fw_api = WSM_GET16(buf); + priv->wsm_caps.fw_build = WSM_GET16(buf); + priv->wsm_caps.fw_ver = WSM_GET16(buf); + WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label)); + priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */ + + if (WARN_ON(priv->wsm_caps.status)) + return -EINVAL; + + if (WARN_ON(priv->wsm_caps.fw_type > 4)) + return -EINVAL; + + pr_info("CW1200 WSM init done.\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware [%s], ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + priv->wsm_caps.input_buffers, + priv->wsm_caps.input_buffer_size, + priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid, + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build, + priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap); + + /* Disable unsupported frequency bands */ + if (!(priv->wsm_caps.fw_cap & 0x1)) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + if (!(priv->wsm_caps.fw_cap & 0x2)) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + priv->firmware_ready = 1; + wake_up(&priv->wsm_startup_done); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_receive_indication(struct cw1200_common *priv, + int link_id, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + struct wsm_rx rx; + struct ieee80211_hdr *hdr; + size_t hdr_len; + __le16 fctl; + + rx.status = WSM_GET32(buf); + rx.channel_number = WSM_GET16(buf); + rx.rx_rate = WSM_GET8(buf); + rx.rcpi_rssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + + /* FW Workaround: Drop probe resp or + beacon when RSSI is 0 + */ + hdr = (struct ieee80211_hdr *)(*skb_p)->data; + + if (!rx.rcpi_rssi && + (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control))) + return 0; + + /* If no RSSI subscription has been made, + * convert RCPI to RSSI here + */ + if (!priv->cqm_use_rssi) + rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110; + + fctl = *(__le16 *)buf->data; + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + if (!rx.status && ieee80211_is_deauth(fctl)) { + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + /* Shedule unjoin work */ + pr_debug("[WSM] Issue unjoin command (RX).\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + cw1200_rx_cb(priv, &rx, link_id, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) +{ + int first; + struct cw1200_wsm_event *event; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return 0; + } + + event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + event->evt.id = WSM_GET32(buf); + event->evt.data = WSM_GET32(buf); + + pr_debug("[WSM] Event: %d(%d)\n", + event->evt.id, event->evt.data); + + spin_lock(&priv->event_queue_lock); + first = list_empty(&priv->event_queue); + list_add_tail(&event->link, &priv->event_queue); + spin_unlock(&priv->event_queue_lock); + + if (first) + queue_work(priv->workqueue, &priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +static int wsm_channel_switch_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + WARN_ON(WSM_GET32(buf)); + + priv->channel_switch_in_progress = 0; + wake_up(&priv->channel_switch_done); + + wsm_unlock_tx(priv); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + /* TODO: Check buf (struct wsm_set_pm_complete) for validity */ + if (priv->ps_mode_switch_in_progress) { + priv->ps_mode_switch_in_progress = 0; + wake_up(&priv->ps_mode_switch_done); + } + return 0; +} + +static int wsm_scan_started(struct cw1200_common *priv, void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) { + cw1200_scan_failed_cb(priv); + return -EINVAL; + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_scan_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.num_channels = WSM_GET8(buf); + cw1200_scan_complete_cb(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_join_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + struct wsm_join_complete arg; + arg.status = WSM_GET32(buf); + pr_debug("[WSM] Join complete indication, status: %d\n", arg.status); + cw1200_join_complete_cb(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + pr_warn("Implement find_complete_indication\n"); + return 0; +} + +static int wsm_ba_timeout_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + u32 dummy; + u8 tid; + u8 dummy2; + u8 addr[ETH_ALEN]; + + dummy = WSM_GET32(buf); + tid = WSM_GET8(buf); + dummy2 = WSM_GET8(buf); + WSM_GET(buf, addr, ETH_ALEN); + + pr_info("BlockACK timeout, tid %d, addr %pM\n", + tid, addr); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_suspend_resume_indication(struct cw1200_common *priv, + int link_id, struct wsm_buf *buf) +{ + u32 flags; + struct wsm_suspend_resume arg; + + flags = WSM_GET32(buf); + arg.link_id = link_id; + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + cw1200_suspend_resume(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + + +/* ******************************************************************** */ +/* WSM TX */ + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo) +{ + size_t buf_len = buf->data - buf->begin; + int ret; + + /* Don't bother if we're dead. */ + if (priv->bh_error) { + ret = 0; + goto done; + } + + /* Block until the cmd buffer is completed. Tortuous. */ + spin_lock(&priv->wsm_cmd.lock); + while (!priv->wsm_cmd.done) { + spin_unlock(&priv->wsm_cmd.lock); + spin_lock(&priv->wsm_cmd.lock); + } + priv->wsm_cmd.done = 0; + spin_unlock(&priv->wsm_cmd.lock); + + if (cmd == WSM_WRITE_MIB_REQ_ID || + cmd == WSM_READ_MIB_REQ_ID) + pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + buf_len); + else + pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); + + /* Due to buggy SPI on CW1200, we need to + * pad the message by a few bytes to ensure + * that it's completely received. + */ + buf_len += 4; + + /* Fill HI message header */ + /* BH will add sequence number */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(priv->wsm_cmd.ptr); + priv->wsm_cmd.ptr = buf->begin; + priv->wsm_cmd.len = buf_len; + priv->wsm_cmd.arg = arg; + priv->wsm_cmd.cmd = cmd; + spin_unlock(&priv->wsm_cmd.lock); + + cw1200_bh_wakeup(priv); + + /* Wait for command completion */ + ret = wait_event_timeout(priv->wsm_cmd_wq, + priv->wsm_cmd.done, tmo); + + if (!ret && !priv->wsm_cmd.done) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + if (priv->bh_error) { + /* Return ok to help system cleanup */ + ret = 0; + } else { + pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd); + print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE, + buf->begin, buf_len); + pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used); + + /* Kill BH thread to report the error to the top layer. */ + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + ret = -ETIMEDOUT; + } + } else { + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.done); + ret = priv->wsm_cmd.ret; + spin_unlock(&priv->wsm_cmd.lock); + } +done: + wsm_buf_reset(buf); + return ret; +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv) +{ + wsm_cmd_lock(priv); + if (atomic_add_return(1, &priv->tx_lock) == 1) { + if (wsm_flush_tx(priv)) + pr_debug("[WSM] TX is locked.\n"); + } + wsm_cmd_unlock(priv); +} + +void wsm_lock_tx_async(struct cw1200_common *priv) +{ + if (atomic_add_return(1, &priv->tx_lock) == 1) + pr_debug("[WSM] TX is locked (async).\n"); +} + +bool wsm_flush_tx(struct cw1200_common *priv) +{ + unsigned long timestamp = jiffies; + bool pending = false; + long timeout; + int i; + + /* Flush must be called with TX lock held. */ + BUG_ON(!atomic_read(&priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. + */ + if (!priv->hw_bufs_used) + return true; + + if (priv->bh_error) { + /* In case of failure do not wait for magic. */ + pr_err("[WSM] Fatal error occured, will not flush TX.\n"); + return false; + } else { + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp, 0xffffffff); + /* If there's nothing pending, we're good */ + if (!pending) + return true; + + timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + timeout) <= 0) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + priv->bh_error = 1; + wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used); + wake_up(&priv->bh_wq); + return false; + } + + /* Ok, everything is flushed. */ + return true; + } +} + +void wsm_unlock_tx(struct cw1200_common *priv) +{ + int tx_lock; + tx_lock = atomic_sub_return(1, &priv->tx_lock); + BUG_ON(tx_lock < 0); + + if (tx_lock == 0) { + if (!priv->bh_error) + cw1200_bh_wakeup(priv); + pr_debug("[WSM] TX is unlocked.\n"); + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) +{ + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + unsigned int i; + + static const char * const reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) + wiphy_err(priv->hw->wiphy, + "Firmware exception: %s.\n", + reason_str[reason]); + else + wiphy_err(priv->hw->wiphy, + "Firmware assert at %.*s, line %d\n", + (int) sizeof(fname), fname, reg[1]); + + for (i = 0; i < 12; i += 4) + wiphy_err(priv->hw->wiphy, + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + i + 0, reg[i + 0], i + 1, reg[i + 1], + i + 2, reg[i + 2], i + 3, reg[i + 3]); + wiphy_err(priv->hw->wiphy, + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wiphy_err(priv->hw->wiphy, + "CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, + fname, sizeof(fname)); + return 0; + +underflow: + wiphy_err(priv->hw->wiphy, "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, + data, len); + return -EINVAL; +} + +int wsm_handle_rx(struct cw1200_common *priv, u16 id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + struct wsm_buf wsm_buf; + int link_id = (id >> 6) & 0x0F; + + /* Strip link id. */ + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)]; + + pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, + wsm_buf.end - wsm_buf.begin); + + if (id == WSM_TX_CONFIRM_IND_ID) { + ret = wsm_tx_confirm(priv, &wsm_buf, link_id); + } else if (id == WSM_MULTI_TX_CONFIRM_ID) { + ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); + } else if (id & 0x0400) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). + */ + spin_lock(&priv->wsm_cmd.lock); + wsm_arg = priv->wsm_cmd.arg; + wsm_cmd = priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock(&priv->wsm_cmd.lock); + + if (WARN_ON((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). + */ + + switch (id) { + case WSM_READ_MIB_RESP_ID: + if (wsm_arg) + ret = wsm_read_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_WRITE_MIB_RESP_ID: + if (wsm_arg) + ret = wsm_write_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_START_SCAN_RESP_ID: + if (wsm_arg) + ret = wsm_scan_started(priv, wsm_arg, &wsm_buf); + break; + case WSM_CONFIGURATION_RESP_ID: + if (wsm_arg) + ret = wsm_configuration_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_JOIN_RESP_ID: + if (wsm_arg) + ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); + break; + case WSM_STOP_SCAN_RESP_ID: + case WSM_RESET_RESP_ID: + case WSM_ADD_KEY_RESP_ID: + case WSM_REMOVE_KEY_RESP_ID: + case WSM_SET_PM_RESP_ID: + case WSM_SET_BSS_PARAMS_RESP_ID: + case 0x0412: /* set_tx_queue_params */ + case WSM_EDCA_PARAMS_RESP_ID: + case WSM_SWITCH_CHANNEL_RESP_ID: + case WSM_START_RESP_ID: + case WSM_BEACON_TRANSMIT_RESP_ID: + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ + case 0x041C: /* map_link */ + WARN_ON(wsm_arg != NULL); + ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); + if (ret) { + wiphy_warn(priv->hw->wiphy, + "wsm_generic_confirm failed for request 0x%04x.\n", + id & ~0x0400); + + /* often 0x407 and 0x410 occur, this means we're dead.. */ + if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) { + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + break; + default: + wiphy_warn(priv->hw->wiphy, + "Unrecognized confirmation 0x%04x\n", + id & ~0x0400); + } + + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ret = ret; + priv->wsm_cmd.done = 1; + spin_unlock(&priv->wsm_cmd.lock); + + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up(&priv->wsm_cmd_wq); + } else if (id & 0x0800) { + switch (id) { + case WSM_STARTUP_IND_ID: + ret = wsm_startup_indication(priv, &wsm_buf); + break; + case WSM_RECEIVE_IND_ID: + ret = wsm_receive_indication(priv, link_id, + &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(priv, &wsm_buf); + break; + case WSM_SCAN_COMPLETE_IND_ID: + ret = wsm_scan_complete_indication(priv, &wsm_buf); + break; + case 0x0808: + ret = wsm_ba_timeout_indication(priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(priv, &wsm_buf); + break; + case 0x080A: + ret = wsm_channel_switch_indication(priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(priv, + link_id, &wsm_buf); + break; + case 0x080F: + ret = wsm_join_complete_indication(priv, &wsm_buf); + break; + default: + pr_warn("Unrecognised WSM ID %04x\n", id); + } + } else { + WARN_ON(1); + ret = -EINVAL; + } +out: + return ret; +} + +static bool wsm_handle_tx_data(struct cw1200_common *priv, + struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info, + const struct cw1200_txpriv *txpriv, + struct cw1200_queue *queue) +{ + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset]; + __le16 fctl = frame->frame_control; + enum { + do_probe, + do_drop, + do_wep, + do_tx, + } action = do_tx; + + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + action = do_tx; + else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) + action = do_drop; + break; + case NL80211_IFTYPE_AP: + if (!priv->join_status) { + action = do_drop; + } else if (!(BIT(txpriv->raw_link_id) & + (BIT(0) | priv->link_id_map))) { + wiphy_warn(priv->hw->wiphy, + "A frame with expired link id is dropped.\n"); + action = do_drop; + } + if (cw1200_queue_get_generation(wsm->packet_id) > + CW1200_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. + */ + wiphy_warn(priv->hw->wiphy, + "Too many attempts to requeue a frame; dropped.\n"); + action = do_drop; + } + break; + case NL80211_IFTYPE_ADHOC: + if (priv->join_status != CW1200_JOIN_STATUS_IBSS) + action = do_drop; + break; + case NL80211_IFTYPE_MESH_POINT: + action = do_tx; /* TODO: Test me! */ + break; + case NL80211_IFTYPE_MONITOR: + default: + action = do_drop; + break; + } + + if (action == do_tx) { + if (ieee80211_is_nullfunc(fctl)) { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_state) { + priv->bss_loss_confirm_id = wsm->packet_id; + wsm->queue_id = WSM_QUEUE_VOICE; + } + spin_unlock(&priv->bss_loss_lock); + } else if (ieee80211_is_probe_req(fctl)) { + action = do_probe; + } else if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + pr_debug("[WSM] Issue unjoin command due to tx deauth.\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else if (ieee80211_has_protected(fctl) && + tx_info->control.hw_key && + tx_info->control.hw_key->keyidx != priv->wep_default_key_id && + (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { + action = do_wep; + } + } + + switch (action) { + case do_probe: + /* An interesting FW "feature". Device filters probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. + */ + pr_debug("[WSM] Convert probe request to scan.\n"); + wsm_lock_tx_async(priv); + priv->pending_frame_id = wsm->packet_id; + if (queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, 0) <= 0) + wsm_unlock_tx(priv); + handled = true; + break; + case do_drop: + pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl); + BUG_ON(cw1200_queue_remove(queue, wsm->packet_id)); + handled = true; + break; + case do_wep: + pr_debug("[WSM] Issue set_default_wep_key.\n"); + wsm_lock_tx_async(priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + priv->pending_frame_id = wsm->packet_id; + if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + break; + case do_tx: + pr_debug("[WSM] Transmit frame.\n"); + break; + default: + /* Do nothing */ + break; + } + return handled; +} + +static int cw1200_get_prio_queue(struct cw1200_common *priv, + u32 link_id_map, int *total) +{ + static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | + BIT(CW1200_LINK_ID_UAPSD); + struct wsm_edca_queue_params *edca; + unsigned score, best = -1; + int winner = -1; + int queued; + int i; + + /* search for a winner using edca params */ + for (i = 0; i < 4; ++i) { + queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], + link_id_map); + if (!queued) + continue; + *total += queued; + edca = &priv->edca.params[i]; + score = ((edca->aifns + edca->cwmin) << 16) + + ((edca->cwmax - edca->cwmin) * + (get_random_int() & 0xFFFF)); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && priv->tx_burst_idx >= 0 && + winner != priv->tx_burst_idx && + !cw1200_queue_get_num_queued( + &priv->tx_queue[winner], + link_id_map & urgent) && + cw1200_queue_get_num_queued( + &priv->tx_queue[priv->tx_burst_idx], + link_id_map)) + winner = priv->tx_burst_idx; + + return winner; +} + +static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, + struct cw1200_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->tx_multicast) { + tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); + } + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &priv->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct cw1200_queue *queue = NULL; + int queue_num; + u32 tx_allowed_mask = 0; + const struct cw1200_txpriv *txpriv = NULL; + int count = 0; + + /* More is used only for broadcasts. */ + bool more = false; + + if (priv->wsm_cmd.ptr) { /* CMD request */ + ++count; + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.ptr); + *data = priv->wsm_cmd.ptr; + *tx_len = priv->wsm_cmd.len; + *burst = 1; + spin_unlock(&priv->wsm_cmd.lock); + } else { + for (;;) { + int ret; + + if (atomic_add_return(0, &priv->tx_lock)) + break; + + spin_lock_bh(&priv->ps_state_lock); + + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + queue_num = queue - priv->tx_queue; + + if (priv->buffered_multicasts && + (ret || !more) && + (priv->tx_multicast || !priv->sta_asleep_mask)) { + priv->buffered_multicasts = false; + if (priv->tx_multicast) { + priv->tx_multicast = false; + queue_work(priv->workqueue, + &priv->multicast_stop_work); + } + } + + spin_unlock_bh(&priv->ps_state_lock); + + if (ret) + break; + + if (cw1200_queue_get(queue, + tx_allowed_mask, + &wsm, &tx_info, &txpriv)) + continue; + + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv, queue)) + continue; /* Handled by WSM */ + + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); + wsm->hdr.id |= cpu_to_le16( + WSM_TX_LINK_ID(txpriv->raw_link_id)); + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + + /* allow bursting if txop is set */ + if (priv->edca.params[queue_num].txop_limit) + *burst = min(*burst, + (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1); + else + *burst = 1; + + /* store index of bursting queue */ + if (*burst > 1) + priv->tx_burst_idx = queue_num; + else + priv->tx_burst_idx = -1; + + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n", + 0x0004, *tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + break; + } + } + + return count; +} + +void wsm_txed(struct cw1200_common *priv, u8 *data) +{ + if (data == priv->wsm_cmd.ptr) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + } +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + BUG_ON(buf->begin); + buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else { + buf->data = buf->begin; + } +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + size = round_up(size, FWLOAD_BLOCK_SIZE); + + buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h new file mode 100644 index 00000000000..7afc613c370 --- /dev/null +++ b/drivers/net/wireless/cw1200/wsm.h @@ -0,0 +1,1870 @@ +/* + * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> + * + * Based on CW1200 UMAC WSM API, which is + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stewart Mathers <stewart.mathers@stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_WSM_H_INCLUDED +#define CW1200_WSM_H_INCLUDED + +#include <linux/spinlock.h> + +struct cw1200_common; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK */ +#define WSM_TRANSMIT_RATE_5 (2) + +/* 11 Mbps ERP-CCK */ +#define WSM_TRANSMIT_RATE_11 (3) + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS BIT(0) + +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN BIT(1) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Indicates TSF inclusion after 802.11 frame body */ +#define WSM_RX_STATUS_TSF_INCLUDED (BIT(24)) + +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. + */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic + */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) +/* Issue immediate join confirmation and use + * join complete to notify about completion + */ +#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) + +/* Key indexes */ +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 4.38 ProtectedMgmtPolicy */ +#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 + +/* 4.39 SetHtProtection */ +#define WSM_MIB_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + +/* Advanced filtering options */ +#define WSM_MAX_FILTER_ELEMENTS (4) + +#define WSM_FILTER_ACTION_IGNORE (0) +#define WSM_FILTER_ACTION_FILTER_IN (1) +#define WSM_FILTER_ACTION_FILTER_OUT (2) + +#define WSM_FILTER_PORT_TYPE_DST (0) +#define WSM_FILTER_PORT_TYPE_SRC (1) + +/* Actual header of WSM messages */ +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + +#define MAX_BEACON_SKIP_TIME_MS 1000 + +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) + +/* ******************************************************************** */ +/* WSM capability */ + +#define WSM_STARTUP_IND_ID 0x0801 + +struct wsm_startup_ind { + u16 input_buffers; + u16 input_buffer_size; + u16 status; + u16 hw_id; + u16 hw_subid; + u16 fw_cap; + u16 fw_type; + u16 fw_api; + u16 fw_build; + u16 fw_ver; + char fw_label[128]; + u32 config[4]; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +/* 3.1 */ +#define WSM_CONFIGURATION_REQ_ID 0x0009 +#define WSM_CONFIGURATION_RESP_ID 0x0409 + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct cw1200_common *priv, + struct wsm_configuration *arg); + +/* 3.3 */ +#define WSM_RESET_REQ_ID 0x000A +#define WSM_RESET_RESP_ID 0x040A +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); + +/* 3.5 */ +#define WSM_READ_MIB_REQ_ID 0x0005 +#define WSM_READ_MIB_RESP_ID 0x0405 +int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf, + size_t buf_size); + +/* 3.7 */ +#define WSM_WRITE_MIB_REQ_ID 0x0006 +#define WSM_WRITE_MIB_RESP_ID 0x0406 +int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf, + size_t buf_size); + +/* 3.9 */ +#define WSM_START_SCAN_REQ_ID 0x0007 +#define WSM_START_SCAN_RESP_ID 0x0407 + +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 min_chan_time; + u32 max_chan_time; + u32 tx_power_level; +}; + +struct wsm_scan { + /* WSM_PHY_BAND_... */ + u8 band; + + /* WSM_SCAN_TYPE_... */ + u8 type; + + /* WSM_SCAN_FLAG_... */ + u8 flags; + + /* WSM_TRANSMIT_RATE_... */ + u8 max_tx_rate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + u32 auto_scan_interval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + u32 num_probes; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + u8 num_channels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + u8 num_ssids; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + u8 probe_delay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); + +/* 3.11 */ +#define WSM_STOP_SCAN_REQ_ID 0x0008 +#define WSM_STOP_SCAN_RESP_ID 0x0408 +int wsm_stop_scan(struct cw1200_common *priv); + +/* 3.13 */ +#define WSM_SCAN_COMPLETE_IND_ID 0x0806 +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 num_channels; +}; + +/* 3.14 */ +#define WSM_TX_CONFIRM_IND_ID 0x0404 +#define WSM_MULTI_TX_CONFIRM_ID 0x041E + +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + u32 packet_id; + + /* WSM_STATUS_... */ + u32 status; + + /* WSM_TRANSMIT_RATE_... */ + u8 tx_rate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + u8 ack_failures; + + /* WSM_TX_STATUS_... */ + u16 flags; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + u32 media_delay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + u32 tx_queue_delay; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in cw1200_tx, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: cw1200_tx must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + u32 packet_id; /* Note this is actually a cookie */ + + /* WSM_TRANSMIT_RATE_... */ + u8 max_tx_rate; + + /* WSM_QUEUE_... */ + u8 queue_id; + + /* True: another packet is pending on the host for transmission. */ + u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + u8 flags; + + /* Should be 0. */ + u32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + u32 expire_time; + + /* WSM_HT_TX_... */ + __le32 ht_tx_parameters; +} __packed; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +#define WSM_RECEIVE_IND_ID 0x0804 + +struct wsm_rx { + /* WSM_STATUS_... */ + u32 status; + + /* Specifies the channel of the received packet. */ + u16 channel_number; + + /* WSM_TRANSMIT_RATE_... */ + u8 rx_rate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + u8 rcpi_rssi; + + /* WSM_RX_STATUS_... */ + u32 flags; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 id; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 data; +}; + +struct cw1200_wsm_event { + struct list_head link; + struct wsm_event evt; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct cw1200_common *priv, + struct wsm_event *arg); + +/* 3.23 */ +#define WSM_JOIN_REQ_ID 0x000B +#define WSM_JOIN_RESP_ID 0x040B + +struct wsm_join { + /* WSM_JOIN_MODE_... */ + u8 mode; + + /* WSM_PHY_BAND_... */ + u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + u16 channel_number; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + u16 atim_window; + + /* WSM_JOIN_PREAMBLE_... */ + u8 preamble_type; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + u8 probe_for_join; + + /* DTIM Period (In multiples of beacon interval) */ + u8 dtim_period; + + /* WSM_JOIN_FLAGS_... */ + u8 flags; + + /* Length of the SSID */ + u32 ssid_len; + + /* Specifies the SSID of the IBSS to join or start */ + u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + u32 beacon_interval; + + /* A bit mask that defines the BSS basic rate set. */ + u32 basic_rate_set; +}; + +struct wsm_join_cnf { + u32 status; + + /* Minimum transmission power level in units of 0.1dBm */ + u32 min_power_level; + + /* Maximum transmission power level in units of 0.1dBm */ + u32 max_power_level; +}; + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); + +/* 3.24 */ +struct wsm_join_complete { + /* WSM_STATUS_... */ + u32 status; +}; + +/* 3.25 */ +#define WSM_SET_PM_REQ_ID 0x0010 +#define WSM_SET_PM_RESP_ID 0x0410 +struct wsm_set_pm { + /* WSM_PSM_... */ + u8 mode; + + /* in unit of 500us; 0 to use default */ + u8 fast_psm_idle_period; + + /* in unit of 500us; 0 to use default */ + u8 ap_psm_change_period; + + /* in unit of 500us; 0 to disable auto-pspoll */ + u8 min_auto_pspoll_period; +}; + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* 3.27 */ +struct wsm_set_pm_complete { + u8 psm; /* WSM_PSM_... */ +}; + +/* 3.28 */ +#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011 +#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411 +struct wsm_set_bss_params { + /* This resets the beacon loss counters only */ + u8 reset_beacon_loss; + + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beacon_lost_count; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operational_rate_set; +}; + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg); + +/* 3.30 */ +#define WSM_ADD_KEY_REQ_ID 0x000C +#define WSM_ADD_KEY_RESP_ID 0x040C +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peer[6]; /* MAC address of the peer station */ + u8 reserved; + u8 keylen; /* Key length in bytes */ + u8 keydata[16]; /* Key data */ + } __packed wep_pairwise; + struct { + u8 keyid; /* Unique per key identifier (0..3) */ + u8 keylen; /* Key length in bytes */ + u16 reserved; + u8 keydata[16]; /* Key data */ + } __packed wep_group; + struct { + u8 peer[6]; /* MAC address of the peer station */ + u16 reserved; + u8 keydata[16]; /* TKIP key data */ + u8 rx_mic_key[8]; /* Rx MIC key */ + u8 tx_mic_key[8]; /* Tx MIC key */ + } __packed tkip_pairwise; + struct { + u8 keydata[16]; /* TKIP key data */ + u8 rx_mic_key[8]; /* Rx MIC key */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + u8 rx_seqnum[8]; /* Receive Sequence Counter */ + } __packed tkip_group; + struct { + u8 peer[6]; /* MAC address of the peer station */ + u16 reserved; + u8 keydata[16]; /* AES key data */ + } __packed aes_pairwise; + struct { + u8 keydata[16]; /* AES key data */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + u8 rx_seqnum[8]; /* Receive Sequence Counter */ + } __packed aes_group; + struct { + u8 peer[6]; /* MAC address of the peer station */ + u8 keyid; /* Key ID */ + u8 reserved; + u8 keydata[16]; /* WAPI key data */ + u8 mic_key[16]; /* MIC key data */ + } __packed wapi_pairwise; + struct { + u8 keydata[16]; /* WAPI key data */ + u8 mic_key[16]; /* MIC key data */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + } __packed wapi_group; + } __packed; +} __packed; + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); + +/* 3.32 */ +#define WSM_REMOVE_KEY_REQ_ID 0x000D +#define WSM_REMOVE_KEY_RESP_ID 0x040D +struct wsm_remove_key { + u8 index; /* Key entry index : 0-10 */ +}; + +int wsm_remove_key(struct cw1200_common *priv, + const struct wsm_remove_key *arg); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +struct wsm_tx_queue_params { + /* NOTE: index is a linux queue id. */ + struct wsm_set_tx_queue_params params[4]; +}; + + +#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\ + max_life_time) \ +do { \ + struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ + p->ackPolicy = (ack_policy); \ + p->allowedMediumTime = (allowed_time); \ + p->maxTransmitLifetime = (max_life_time); \ +} while (0) + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg, u8 id); + +/* 3.36 */ +#define WSM_EDCA_PARAMS_REQ_ID 0x0013 +#define WSM_EDCA_PARAMS_RESP_ID 0x0413 +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + u16 cwmin; + + /* CWmax (in slots) for the access class. */ + u16 cwmax; + + /* AIFS (in slots) for the access class. */ + u16 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + u16 txop_limit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + u32 max_rx_lifetime; +}; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; + bool uapsd_enable[4]; +}; + +#define TXOP_UNIT 32 +#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\ + __uapsd) \ + do { \ + struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \ + p->cwmin = __cw_min; \ + p->cwmax = __cw_max; \ + p->aifns = __aifs; \ + p->txop_limit = ((__txop) * TXOP_UNIT); \ + p->max_rx_lifetime = __lifetime; \ + (__edca)->uapsd_enable[__queue] = (__uapsd); \ + } while (0) + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +int wsm_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016 +#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416 + +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + u8 mode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + u8 switch_count; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + u16 channel_number; +}; + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg); + +typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); + +#define WSM_START_REQ_ID 0x0017 +#define WSM_START_RESP_ID 0x0417 + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channel_number; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 ct_window; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beacon_interval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 dtim_period; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preamble; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probe_delay; + + /* Length of the SSID */ + /* [in] */ u8 ssid_len; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basic_rate_set; +}; + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); + +#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018 +#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418 + +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enable_beaconing; +}; + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg); + +int wsm_start_find(struct cw1200_common *priv); + +int wsm_stop_find(struct cw1200_common *priv); + +typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; +}; + +typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, + struct wsm_suspend_resume *arg); + +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg); + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); + +/* ******************************************************************** */ +/* MIB shortcats */ + +static inline int wsm_set_output_power(struct cw1200_common *priv, + int power_level) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, + unsigned dtim_interval, + unsigned listen_interval) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval) + }; + + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, + struct wsm_rcpi_rssi_threshold *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg)); +} + +struct wsm_mib_counters_table { + __le32 plcp_errors; + __le32 fcs_errors; + __le32 tx_packets; + __le32 rx_packets; + __le32 rx_packet_errors; + __le32 rx_decryption_failures; + __le32 rx_mic_failures; + __le32 rx_no_key_failures; + __le32 tx_multicast_frames; + __le32 tx_frames_success; + __le32 tx_frame_failures; + __le32 tx_frames_retried; + __le32 tx_frames_multi_retried; + __le32 rx_frame_duplicates; + __le32 rts_success; + __le32 rts_failures; + __le32 ack_failures; + __le32 rx_multicast_frames; + __le32 rx_frames_success; + __le32 rx_cmac_icv_errors; + __le32 rx_cmac_replays; + __le32 rx_mgmt_ccmp_replays; +} __packed; + +static inline int wsm_get_counters_table(struct cw1200_common *priv, + struct wsm_mib_counters_table *arg) +{ + return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) +{ + return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; + bool probeResponder; +}; + +static inline int wsm_set_rx_filter(struct cw1200_common *priv, + const struct wsm_rx_filter *arg) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + if (arg->probeResponder) + val |= __cpu_to_le32(BIT(3)); + return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); +} + +int wsm_set_probe_responder(struct cw1200_common *priv, bool enable); + +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ie_id; + u8 flags; + u8 oui[3]; + u8 match_data[3]; +} __packed; + +struct wsm_mib_beacon_filter_table { + __le32 num; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, + struct wsm_mib_beacon_filter_table *ft) +{ + size_t size = __le32_to_cpu(ft->num) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); +} + +#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ +#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct cw1200_common *priv, + struct wsm_beacon_filter_control *arg) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val)); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disable_more_flag_usage; + int perform_ant_diversity; +}; + +static inline int wsm_set_operational_mode(struct cw1200_common *priv, + const struct wsm_operational_mode *arg) +{ + u8 val = arg->power_mode; + if (arg->disable_more_flag_usage) + val |= BIT(4); + if (arg->perform_ant_diversity) + val |= BIT(5); + return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val)); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct cw1200_common *priv, + struct wsm_template_frame *arg) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + ((__le16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4); + ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); + skb_pull(arg->skb, 4); + return ret; +} + + +struct wsm_protected_mgmt_policy { + bool protectedMgmtEnable; + bool unprotectedMgmtFramesAllowed; + bool encryptionForAuthFrame; +}; + +static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv, + struct wsm_protected_mgmt_policy *arg) +{ + __le32 val = 0; + int ret; + if (arg->protectedMgmtEnable) + val |= __cpu_to_le32(BIT(0)); + if (arg->unprotectedMgmtFramesAllowed) + val |= __cpu_to_le32(BIT(1)); + if (arg->encryptionForAuthFrame) + val |= __cpu_to_le32(BIT(2)); + ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, + &val, sizeof(val)); + return ret; +} + +struct wsm_mib_block_ack_policy { + u8 tx_tid; + u8 reserved1; + u8 rx_tid; + u8 reserved2; +} __packed; + +static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, + u8 tx_tid_policy, + u8 rx_tid_policy) +{ + struct wsm_mib_block_ack_policy val = { + .tx_tid = tx_tid_policy, + .rx_tid = rx_tid_policy, + }; + return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val)); +} + +struct wsm_mib_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preamble; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfield; /* 1 for greenfield */ + u8 mpdu_start_spacing; + __le32 basic_rate_set; +} __packed; + +static inline int wsm_set_association_mode(struct cw1200_common *priv, + struct wsm_mib_association_mode *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg)); +} + +#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2) +#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3) +struct wsm_tx_rate_retry_policy { + u8 index; + u8 short_retries; + u8 long_retries; + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt + */ + u8 flags; + u8 rate_recoveries; + u8 reserved[3]; + __le32 rate_count_indices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + u8 num; + u8 reserved[3]; + struct wsm_tx_rate_retry_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, + struct wsm_set_tx_rate_retry_policy *arg) +{ + size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy); + return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size); +} + +/* 4.32 SetEtherTypeDataFrameFilter */ +struct wsm_ether_type_filter_hdr { + u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_ether_type_filter { + u8 action; /* WSM_FILTER_ACTION_XXX */ + u8 reserved; + __le16 type; /* Type of ethernet frame */ +} __packed; + +static inline int wsm_set_ether_type_filter(struct cw1200_common *priv, + struct wsm_ether_type_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_ether_type_filter_hdr) + + arg->num * sizeof(struct wsm_ether_type_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, + arg, size); +} + +/* 4.33 SetUDPPortDataFrameFilter */ +struct wsm_udp_port_filter_hdr { + u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_udp_port_filter { + u8 action; /* WSM_FILTER_ACTION_XXX */ + u8 type; /* WSM_FILTER_PORT_TYPE_XXX */ + __le16 port; /* Port number */ +} __packed; + +static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, + struct wsm_udp_port_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_udp_port_filter_hdr) + + arg->num * sizeof(struct wsm_udp_port_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, + arg, size); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 category_id; + u8 oui[4]; + __le16 subcategory_id; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devname_size; + u8 local_devname[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 num_secdev_supported; + struct wsm_p2p_device_type secdevs[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 wcdma_band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 tsf_counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 period; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct cw1200_common *priv, + int period) +{ + struct wsm_keep_alive_period arg = { + .period = __cpu_to_le16(period), + }; + return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, + bool enabled) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg)); +} + +/* Multicast filtering - 4.5 */ +struct wsm_mib_multicast_filter { + __le32 enable; + __le32 num_addrs; + u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +static inline int wsm_set_multicast_filter(struct cw1200_common *priv, + struct wsm_mib_multicast_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* ARP IPv4 filtering - 4.10 */ +struct wsm_mib_arp_ipv4_filter { + __le32 enable; + __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; +} __packed; + +static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, + struct wsm_mib_arp_ipv4_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 opp_ps_ct_window; + u8 count; + u8 reserved; + u8 dtim_count; + __le32 duration; + __le32 interval; + __le32 start_time; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, + bool enabled) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg)); +} + + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsd_flags; + __le16 min_auto_trigger_interval; + __le16 max_auto_trigger_interval; + __le16 auto_trigger_step; +}; + +static inline int wsm_set_uapsd_info(struct cw1200_common *priv, + struct wsm_uapsd_info *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, + struct wsm_override_internal_txrate *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg)); +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv); +void wsm_lock_tx_async(struct cw1200_common *priv); +bool wsm_flush_tx(struct cw1200_common *priv); +void wsm_unlock_tx(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len); +int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; /* Protect structure from multiple access */ + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +void wsm_txed(struct cw1200_common *priv, u8 *data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queue_id) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queue_id]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queue_id) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queue_id]; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 15920aaa5dd..f8ab193009c 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -6242,8 +6242,6 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, if ((val & 0x0000ff00) != 0) pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); - pci_set_power_state(pci_dev, PCI_D0); - if (!ipw2100_hw_is_adapter_in_system(dev)) { printk(KERN_WARNING DRV_NAME "Device not found via register read.\n"); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index d96257b79a8..6b823a1ab78 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -3548,6 +3548,7 @@ static int ipw_load(struct ipw_priv *priv) ipw_rx_queue_reset(priv, priv->rxq); if (!priv->rxq) { IPW_ERROR("Unable to initialize Rx queue\n"); + rc = -ENOMEM; goto error; } @@ -8256,7 +8257,7 @@ static int is_duplicate_packet(struct ipw_priv *priv, u8 *mac = header->addr2; int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE; - __list_for_each(p, &priv->ibss_mac_hash[index]) { + list_for_each(p, &priv->ibss_mac_hash[index]) { entry = list_entry(p, struct ipw_ibss_seq, list); if (!memcmp(entry->mac, mac, ETH_ALEN)) diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index 95a1ca1e895..9ffe65931b2 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -1195,7 +1195,7 @@ static int libipw_parse_info_param(struct libipw_info_element #ifdef CONFIG_LIBIPW_DEBUG p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", - network->rates[i]); + network->rates_ex[i]); #endif if (libipw_is_ofdm_rate (info_element->data[i])) { diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index b37a582ccbe..9581d07a424 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3119,7 +3119,7 @@ il3945_store_debug_level(struct device *d, struct device_attribute *attr, unsigned long val; int ret; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret) IL_INFO("%s is not in hex or decimal form.\n", buf); else @@ -3727,7 +3727,8 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * 5. Setup HW Constants * ********************/ /* Device-specific setup */ - if (il3945_hw_set_hw_params(il)) { + err = il3945_hw_set_hw_params(il); + if (err) { IL_ERR("failed to set hw settings\n"); goto out_eeprom_free; } diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index dc1e6da9976..c092033945c 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -331,6 +331,19 @@ il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) return; } + /* + * Firmware will not transmit frame on passive channel, if it not yet + * received some valid frame on that channel. When this error happen + * we have to wait until firmware will unblock itself i.e. when we + * note received beacon or other frame. We unblock queues in + * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed. + */ + if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && + il->iw_mode == NL80211_IFTYPE_STATION) { + il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Stopped queues - RX waiting on passive channel\n"); + } + txq->time_stamp = jiffies; info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]); ieee80211_tx_info_clear_status(info); @@ -488,6 +501,11 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, return; } + if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Woke queues - frame received on passive channel\n"); + } + skb = dev_alloc_skb(128); if (!skb) { IL_ERR("dev_alloc_skb failed\n"); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 9a95045c97b..b9b2bb51e60 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -588,6 +588,11 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, return; } + if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Woke queues - frame received on passive channel\n"); + } + /* In case of HW accelerated crypto and bad decryption, drop */ if (!il->cfg->mod_params->sw_crypto && il_set_decrypted_flag(il, hdr, ampdu_status, stats)) @@ -2806,6 +2811,19 @@ il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) return; } + /* + * Firmware will not transmit frame on passive channel, if it not yet + * received some valid frame on that channel. When this error happen + * we have to wait until firmware will unblock itself i.e. when we + * note received beacon or other frame. We unblock queues in + * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed. + */ + if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && + il->iw_mode == NL80211_IFTYPE_STATION) { + il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Stopped queues - RX waiting on passive channel\n"); + } + spin_lock_irqsave(&il->sta_lock, flags); if (txq->sched_retry) { const u32 scd_ssn = il4965_get_scd_ssn(tx_resp); @@ -4567,7 +4585,7 @@ il4965_store_debug_level(struct device *d, struct device_attribute *attr, unsigned long val; int ret; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret) IL_ERR("%s is not in hex or decimal form.\n", buf); else @@ -4614,7 +4632,7 @@ il4965_store_tx_power(struct device *d, struct device_attribute *attr, unsigned long val; int ret; - ret = strict_strtoul(buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (ret) IL_INFO("%s is not in decimal form.\n", buf); else { @@ -5741,7 +5759,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; if (il->cfg->sku & IL_SKU_N) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h index 3b6c9940089..04842151198 100644 --- a/drivers/net/wireless/iwlegacy/commands.h +++ b/drivers/net/wireless/iwlegacy/commands.h @@ -1348,14 +1348,6 @@ struct il_rx_mpdu_res_start { #define TX_CMD_SEC_KEY128 0x08 /* - * security overhead sizes - */ -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 -#define CCMP_MIC_LEN 8 -#define TKIP_ICV_LEN 4 - -/* * C_TX = 0x1c (command) */ diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index e9a3cbc409a..3195aad440d 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -5307,6 +5307,17 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, D_MAC80211("BSSID %pM\n", bss_conf->bssid); /* + * On passive channel we wait with blocked queues to see if + * there is traffic on that channel. If no frame will be + * received (what is very unlikely since scan detects AP on + * that channel, but theoretically possible), mac80211 associate + * procedure will time out and mac80211 will call us with NULL + * bssid. We have to unblock queues on such condition. + */ + if (is_zero_ether_addr(bss_conf->bssid)) + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + + /* * If there is currently a HW scan going on in the background, * then we need to cancel it, otherwise sometimes we are not * able to authenticate (FIXME: why ?) diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 4caaf52986a..83f8ed8a552 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1299,6 +1299,8 @@ struct il_priv { /* queue refcounts */ #define IL_MAX_HW_QUEUES 32 unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)]; +#define IL_STOP_REASON_PASSIVE 0 + unsigned long stop_reason; /* for each AC */ atomic_t queue_stop_count[4]; @@ -2257,6 +2259,19 @@ il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq) } static inline void +_il_wake_queue(struct il_priv *il, u8 ac) +{ + if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) + ieee80211_wake_queue(il->hw, ac); +} + +static inline void +_il_stop_queue(struct il_priv *il, u8 ac) +{ + if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) + ieee80211_stop_queue(il->hw, ac); +} +static inline void il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) { u8 queue = txq->swq_id; @@ -2264,8 +2279,7 @@ il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) u8 hwq = (queue >> 2) & 0x1f; if (test_and_clear_bit(hwq, il->queue_stopped)) - if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) - ieee80211_wake_queue(il->hw, ac); + _il_wake_queue(il, ac); } static inline void @@ -2276,8 +2290,27 @@ il_stop_queue(struct il_priv *il, struct il_tx_queue *txq) u8 hwq = (queue >> 2) & 0x1f; if (!test_and_set_bit(hwq, il->queue_stopped)) - if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) - ieee80211_stop_queue(il->hw, ac); + _il_stop_queue(il, ac); +} + +static inline void +il_wake_queues_by_reason(struct il_priv *il, int reason) +{ + u8 ac; + + if (test_and_clear_bit(reason, &il->stop_reason)) + for (ac = 0; ac < 4; ac++) + _il_wake_queue(il, ac); +} + +static inline void +il_stop_queues_by_reason(struct il_priv *il, int reason) +{ + u8 ac; + + if (!test_and_set_bit(reason, &il->stop_reason)) + for (ac = 0; ac < 4; ac++) + _il_stop_queue(il, ac); } #ifdef ieee80211_stop_queue diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 56c2040a955..cbaa5c2c410 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -128,16 +128,6 @@ config IWLWIFI_DEVICE_TRACING occur. endmenu -config IWLWIFI_DEVICE_TESTMODE - def_bool y - depends on IWLWIFI - depends on NL80211_TESTMODE - help - This option enables the testmode support for iwlwifi device through - NL80211_TESTMODE. This provide the capabilities of enable user space - validation applications to interacts with the device through the - generic netlink message via NL80211_TESTMODE channel. - config IWLWIFI_P2P def_bool y bool "iwlwifi experimental P2P support" diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 3b5613ea458..1fa64429bcc 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -7,14 +7,15 @@ iwlwifi-objs += iwl-notif-wait.o iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o -iwlwifi-objs += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwl-7000.o +iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o +iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o + +iwlwifi-objs += $(iwlwifi-m) iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o -iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src) - obj-$(CONFIG_IWLDVM) += dvm/ obj-$(CONFIG_IWLMVM) += mvm/ diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/iwlwifi/dvm/Makefile index 5ff76b20414..dce7ab2e0c4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/Makefile +++ b/drivers/net/wireless/iwlwifi/dvm/Makefile @@ -8,6 +8,5 @@ iwldvm-objs += scan.o led.o iwldvm-objs += rxon.o devices.o iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o -iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += testmode.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 48545ab0031..18355110def 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -76,13 +76,16 @@ #define IWL_INVALID_STATION 255 /* device operations */ -extern struct iwl_lib_ops iwl1000_lib; -extern struct iwl_lib_ops iwl2000_lib; -extern struct iwl_lib_ops iwl2030_lib; -extern struct iwl_lib_ops iwl5000_lib; -extern struct iwl_lib_ops iwl5150_lib; -extern struct iwl_lib_ops iwl6000_lib; -extern struct iwl_lib_ops iwl6030_lib; +extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_105_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; #define TIME_UNIT 1024 @@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) { - return priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist; + return priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -402,43 +405,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) extern int iwl_alive_start(struct iwl_priv *priv); -/* testmode support */ -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - -extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, - int len); -extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct netlink_callback *cb, - void *data, int len); -extern void iwl_testmode_init(struct iwl_priv *priv); -extern void iwl_testmode_free(struct iwl_priv *priv); - -#else - -static inline -int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) -{ - return -ENOSYS; -} - -static inline -int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, - struct netlink_callback *cb, - void *data, int len) -{ - return -ENOSYS; -} - -static inline void iwl_testmode_init(struct iwl_priv *priv) -{ -} - -static inline void iwl_testmode_free(struct iwl_priv *priv) -{ -} -#endif - #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, enum iwl_rxon_context_id ctxid); diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index d6c4cf2ad7c..1b0f0d50256 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); - if (priv->cfg->base_params->hd_v2) { + if (priv->lib->hd_v2) { cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = @@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv, continue; } - delta_g = (priv->cfg->base_params->chain_noise_scale * + delta_g = (priv->lib->chain_noise_scale * ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; @@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) return; /* Analyze signal for disconnected antenna */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* Disable disconnected antenna algorithm for advanced bt coex, assuming valid antennas are connected */ data->active_chains = priv->nvm_data->valid_rx_ant; diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 95ca026ecc9..ebdac909f0c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -838,10 +838,6 @@ struct iwl_qosparam_cmd { #define STA_MODIFY_DELBA_TID_MSK 0x10 #define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 -/* Receiver address (actually, Rx station's index into station table), - * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ -#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) - /* agn */ struct iwl_keyinfo { __le16 key_flags; @@ -1225,14 +1221,6 @@ struct iwl_rx_mpdu_res_start { #define TX_CMD_SEC_KEY128 0x08 /* - * security overhead sizes - */ -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 -#define CCMP_MIC_LEN 8 -#define TKIP_ICV_LEN 4 - -/* * REPLY_TX = 0x1c (command) */ diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 71ea77576d2..60a4e0d1571 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -52,8 +52,6 @@ #include "rs.h" #include "tt.h" -#include "iwl-test.h" - /* CT-KILL constants */ #define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ #define CT_KILL_THRESHOLD 114 /* in Celsius */ @@ -568,16 +566,61 @@ struct iwl_hw_params { const struct iwl_sensitivity_ranges *sens; }; -struct iwl_lib_ops { - /* set hw dependent parameters */ +/** + * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters + * @advanced_bt_coexist: support advanced bt coexist + * @bt_init_traffic_load: specify initial bt traffic load + * @bt_prio_boost: default bt priority boost value + * @agg_time_limit: maximum number of uSec in aggregation + * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode + */ +struct iwl_dvm_bt_params { + bool advanced_bt_coexist; + u8 bt_init_traffic_load; + u32 bt_prio_boost; + u16 agg_time_limit; + bool bt_sco_disable; + bool bt_session_2; +}; + +/** + * struct iwl_dvm_cfg - DVM firmware specific device configuration + * @set_hw_params: set hardware parameters + * @set_channel_switch: send channel switch command + * @nic_config: apply device specific configuration + * @temperature: read temperature + * @adv_thermal_throttle: support advance thermal throttle + * @support_ct_kill_exit: support ct kill exit condition + * @plcp_delta_threshold: plcp error rate threshold used to trigger + * radio tuning when there is a high receiving plcp error rate + * @chain_noise_scale: default chain noise scale used for gain computation + * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up + * @no_idle_support: do not support idle mode + * @bt_params: pointer to BT parameters + * @need_temp_offset_calib: need to perform temperature offset calibration + * @no_xtal_calib: some devices do not need crystal calibration data, + * don't send it to those + * @temp_offset_v2: support v2 of temperature offset calibration + * @adv_pm: advanced power management + */ +struct iwl_dvm_cfg { void (*set_hw_params)(struct iwl_priv *priv); int (*set_channel_switch)(struct iwl_priv *priv, struct ieee80211_channel_switch *ch_switch); - /* device specific configuration */ void (*nic_config)(struct iwl_priv *priv); - - /* temperature */ void (*temperature)(struct iwl_priv *priv); + + const struct iwl_dvm_bt_params *bt_params; + s32 chain_noise_scale; + u8 plcp_delta_threshold; + bool adv_thermal_throttle; + bool support_ct_kill_exit; + bool hd_v2; + bool no_idle_support; + bool need_temp_offset_calib; + bool no_xtal_calib; + bool temp_offset_v2; + bool adv_pm; }; struct iwl_wipan_noa_data { @@ -610,7 +653,7 @@ struct iwl_priv { struct device *dev; /* for debug prints only */ const struct iwl_cfg *cfg; const struct iwl_fw *fw; - const struct iwl_lib_ops *lib; + const struct iwl_dvm_cfg *lib; unsigned long status; spinlock_t sta_lock; @@ -646,10 +689,6 @@ struct iwl_priv { struct iwl_spectrum_notification measure_report; u8 measurement_status; -#define IWL_OWNERSHIP_DRIVER 0 -#define IWL_OWNERSHIP_TM 1 - u8 ucode_owner; - /* ucode beacon time */ u32 ucode_beacon_time; int missed_beacon_threshold; @@ -844,7 +883,7 @@ struct iwl_priv { #endif /* CONFIG_IWLWIFI_DEBUGFS */ struct iwl_nvm_data *nvm_data; - /* eeprom blob for debugfs/testmode */ + /* eeprom blob for debugfs */ u8 *eeprom_blob; size_t eeprom_blob_size; @@ -860,16 +899,14 @@ struct iwl_priv { unsigned long blink_on, blink_off; bool led_registered; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - struct iwl_test tst; - u32 tm_fixed_rate; -#endif - /* WoWLAN GTK rekey data */ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; __le64 replay_ctr; __le16 last_seq_ctl; bool have_rekey_data; +#ifdef CONFIG_PM_SLEEP + struct wiphy_wowlan_support wowlan_support; +#endif /* device_pointers: pointers to ucode event tables */ struct { diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index c48907c8ab4..352c6cb7b4f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -174,10 +174,13 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl1000_sensitivity; } -struct iwl_lib_ops iwl1000_lib = { +const struct iwl_dvm_cfg iwl_dvm_1000_cfg = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, .temperature = iwlagn_temperature, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; @@ -232,16 +235,56 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl2000_sensitivity; } -struct iwl_lib_ops iwl2000_lib = { +const struct iwl_dvm_cfg iwl_dvm_2000_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, }; -struct iwl_lib_ops iwl2030_lib = { +const struct iwl_dvm_cfg iwl_dvm_105_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, +}; + +static const struct iwl_dvm_bt_params iwl2030_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, + .bt_sco_disable = true, + .bt_session_2 = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_2030_cfg = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .bt_params = &iwl2030_bt_params, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, }; /* @@ -420,16 +463,23 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, return iwl_dvm_send_cmd(priv, &hcmd); } -struct iwl_lib_ops iwl5000_lib = { +const struct iwl_dvm_cfg iwl_dvm_5000_cfg = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwlagn_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, }; -struct iwl_lib_ops iwl5150_lib = { +const struct iwl_dvm_cfg iwl_dvm_5150_cfg = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwl5150_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, + .no_xtal_calib = true, }; @@ -584,16 +634,59 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, return err; } -struct iwl_lib_ops iwl6000_lib = { +const struct iwl_dvm_cfg iwl_dvm_6000_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, +}; + +const struct iwl_dvm_cfg iwl_dvm_6005_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .need_temp_offset_calib = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_6050_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, +}; + +static const struct iwl_dvm_bt_params iwl6000_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, + .bt_sco_disable = true, }; -struct iwl_lib_ops iwl6030_lib = { +const struct iwl_dvm_cfg iwl_dvm_6030_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .bt_params = &iwl6000_bt_params, + .need_temp_offset_calib = true, + .adv_pm = true, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 54f553380aa..3d5bdc4217a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != sizeof(basic.bt3_lookup_table)); - if (priv->cfg->bt_params) { + if (priv->lib->bt_params) { /* * newer generation of devices (2000 series and newer) * use the version 2 of the bt command * we need to make sure sending the host command * with correct data structure to avoid uCode assert */ - if (priv->cfg->bt_params->bt_session_2) { + if (priv->lib->bt_params->bt_session_2) { bt_cmd_v2.prio_boost = cpu_to_le32( - priv->cfg->bt_params->bt_prio_boost); + priv->lib->bt_params->bt_prio_boost); bt_cmd_v2.tx_prio_boost = 0; bt_cmd_v2.rx_prio_boost = 0; } else { /* older version only has 8 bits */ - WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF); + WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); bt_cmd_v1.prio_boost = - priv->cfg->bt_params->bt_prio_boost; + priv->lib->bt_params->bt_prio_boost; bt_cmd_v1.tx_prio_boost = 0; bt_cmd_v1.rx_prio_boost = 0; } @@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - if (priv->cfg->bt_params->bt_session_2) { + if (priv->lib->bt_params->bt_session_2) { memcpy(&bt_cmd_v2.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, @@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv) */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* @@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) else active_chains = priv->nvm_data->valid_rx_ant; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* @@ -1288,12 +1288,6 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) if (!(cmd->flags & CMD_ASYNC)) lockdep_assert_held(&priv->mutex); - if (priv->ucode_owner == IWL_OWNERSHIP_TM && - !(cmd->flags & CMD_ON_DEMAND)) { - IWL_DEBUG_HC(priv, "tm own the uCode, no regular hcmd send\n"); - return -EIO; - } - return iwl_trans_send_cmd(priv->trans, cmd); } diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index cab23af0be9..822f1a00efb 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -208,20 +208,21 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, priv->trans->ops->d3_suspend && priv->trans->ops->d3_resume && device_can_wakeup(priv->trans->dev)) { - hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE; + priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE; if (!iwlwifi_mod_params.sw_crypto) - hw->wiphy->wowlan.flags |= + priv->wowlan_support.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_GTK_REKEY_FAILURE; - hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; - hw->wiphy->wowlan.pattern_min_len = + priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; + priv->wowlan_support.pattern_min_len = IWLAGN_WOWLAN_MIN_PATTERN_LEN; - hw->wiphy->wowlan.pattern_max_len = + priv->wowlan_support.pattern_max_len = IWLAGN_WOWLAN_MAX_PATTERN_LEN; + hw->wiphy->wowlan = &priv->wowlan_support; } #endif @@ -426,7 +427,11 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, if (ret) goto error; - iwl_trans_d3_suspend(priv->trans); + /* let the ucode operate on its own */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + iwl_trans_d3_suspend(priv->trans, false); goto out; @@ -500,7 +505,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) /* we'll clear ctx->vif during iwlagn_prepare_restart() */ vif = ctx->vif; - ret = iwl_trans_d3_resume(priv->trans, &d3_status); + ret = iwl_trans_d3_resume(priv->trans, &d3_status, false); if (ret) goto out_unlock; @@ -509,6 +514,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) goto out_unlock; } + /* uCode is no longer operating by itself */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + base = priv->device_pointers.error_event_table; if (!iwlagn_hw_valid_rtc_data_addr(base)) { IWL_WARN(priv, "Invalid error table during resume!\n"); @@ -1276,8 +1285,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { if (rssi_event == RSSI_EVENT_LOW) priv->bt_enable_pspoll = true; else if (rssi_event == RSSI_EVENT_HIGH) @@ -1387,7 +1396,7 @@ static int iwl_setup_interface(struct iwl_priv *priv, return err; } - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist && vif->type == NL80211_IFTYPE_ADHOC) { /* * pretend to have high BT traffic as long as we @@ -1757,8 +1766,6 @@ struct ieee80211_ops iwlagn_hw_ops = { .remain_on_channel = iwlagn_mac_remain_on_channel, .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, .rssi_callback = iwlagn_mac_rssi_callback, - CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd) - CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump) .set_tim = iwlagn_mac_set_tim, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 74d7572e709..3952ddf2ddb 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) priv->thermal_throttle.ct_kill_toggle = false; - if (priv->cfg->base_params->support_ct_kill_exit) { + if (priv->lib->support_ct_kill_exit) { adv_cmd.critical_temperature_enter = cpu_to_le32(priv->hw_params.ct_kill_threshold); adv_cmd.critical_temperature_exit = @@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv) } /* download priority table before any calibration request */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* Configure Bluetooth device coexistence support */ - if (priv->cfg->bt_params->bt_sco_disable) + if (priv->lib->bt_params->bt_sco_disable) priv->bt_enable_pspoll = false; else priv->bt_enable_pspoll = true; @@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv) priv->bt_status = 0; priv->cur_rssi_ctx = NULL; priv->bt_is_sco = 0; - if (priv->cfg->bt_params) + if (priv->lib->bt_params) priv->bt_traffic_load = - priv->cfg->bt_params->bt_init_traffic_load; + priv->lib->bt_params->bt_init_traffic_load; else priv->bt_traffic_load = 0; priv->bt_full_concurrent = false; @@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) iwl_setup_scan_deferred_work(priv); - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_setup_deferred_work(priv); init_timer(&priv->statistics_periodic); @@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) void iwl_cancel_deferred_work(struct iwl_priv *priv) { - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_cancel_deferred_work(priv); cancel_work_sync(&priv->run_time_calib_work); @@ -1098,16 +1098,13 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->band = IEEE80211_BAND_2GHZ; - priv->plcp_delta_threshold = - priv->cfg->base_params->plcp_delta_threshold; + priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold; priv->iw_mode = NL80211_IFTYPE_STATION; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; priv->agg_tids_count = 0; - priv->ucode_owner = IWL_OWNERSHIP_DRIVER; - priv->rx_statistics_jiffies = jiffies; /* Choose which receivers/antennas to use */ @@ -1116,8 +1113,8 @@ static int iwl_init_drv(struct iwl_priv *priv) iwl_init_scan_params(priv); /* init bt coex */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; @@ -1173,12 +1170,6 @@ static void iwl_option_config(struct iwl_priv *priv) IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); #endif -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE disabled\n"); -#endif - #ifdef CONFIG_IWLWIFI_P2P IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n"); #else @@ -1264,31 +1255,37 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, switch (priv->cfg->device_family) { case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_100: - priv->lib = &iwl1000_lib; + priv->lib = &iwl_dvm_1000_cfg; break; case IWL_DEVICE_FAMILY_2000: + priv->lib = &iwl_dvm_2000_cfg; + break; case IWL_DEVICE_FAMILY_105: - priv->lib = &iwl2000_lib; + priv->lib = &iwl_dvm_105_cfg; break; case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_135: - priv->lib = &iwl2030_lib; + priv->lib = &iwl_dvm_2030_cfg; break; case IWL_DEVICE_FAMILY_5000: - priv->lib = &iwl5000_lib; + priv->lib = &iwl_dvm_5000_cfg; break; case IWL_DEVICE_FAMILY_5150: - priv->lib = &iwl5150_lib; + priv->lib = &iwl_dvm_5150_cfg; break; case IWL_DEVICE_FAMILY_6000: - case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6000i: + priv->lib = &iwl_dvm_6000_cfg; + break; + case IWL_DEVICE_FAMILY_6005: + priv->lib = &iwl_dvm_6005_cfg; + break; case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6150: - priv->lib = &iwl6000_lib; + priv->lib = &iwl_dvm_6050_cfg; break; case IWL_DEVICE_FAMILY_6030: - priv->lib = &iwl6030_lib; + priv->lib = &iwl_dvm_6030_cfg; break; default: break; @@ -1350,8 +1347,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, IWL_BT_ANTENNA_COUPLING_THRESHOLD) ? true : false; - /* enable/disable bt channel inhibition */ - priv->bt_ch_announce = iwlwifi_mod_params.bt_ch_announce; + /* bt channel inhibition enabled*/ + priv->bt_ch_announce = true; IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n", (priv->bt_ch_announce) ? "On" : "Off"); @@ -1446,7 +1443,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, ********************/ iwl_setup_deferred_work(priv); iwl_setup_rx_handlers(priv); - iwl_testmode_init(priv); iwl_power_initialize(priv); iwl_tt_initialize(priv); @@ -1483,7 +1479,6 @@ out_mac80211_unregister: iwlagn_mac_unregister(priv); out_destroy_workqueue: iwl_tt_exit(priv); - iwl_testmode_free(priv); iwl_cancel_deferred_work(priv); destroy_workqueue(priv->workqueue); priv->workqueue = NULL; @@ -1505,7 +1500,6 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); - iwl_testmode_free(priv); iwlagn_mac_unregister(priv); iwl_tt_exit(priv); @@ -1854,14 +1848,9 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, return pos; } -#ifdef CONFIG_IWLWIFI_DEBUG if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log) size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; -#else - size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) - ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; -#endif IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", size); @@ -1905,10 +1894,8 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) unsigned int reload_msec; unsigned long reload_jiffies; -#ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(IWL_DL_FW_ERRORS)) iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS); -#endif /* uCode is no longer loaded. */ priv->ucode_loaded = false; diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index bd69018d07a..77cb5971223 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, u8 skip; u32 slp_itrvl; - if (priv->cfg->adv_pm) { + if (priv->lib->adv_pm) { table = apm_range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = apm_range_1; @@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { - if (!priv->cfg->bt_params->bt_sco_disable) + if (!priv->lib->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; @@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, if (priv->wowlan) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); - else if (!priv->cfg->base_params->no_idle_support && + else if (!priv->lib->no_idle_support && priv->hw->conf.flags & IEEE80211_CONF_IDLE) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); else if (iwl_tt_is_low_power_state(priv)) { diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 10fbb176cc8..1b693944123 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -351,12 +351,6 @@ static void rs_program_fix_rate(struct iwl_priv *priv, lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - /* testmode has higher priority to overwirte the fixed rate */ - if (priv->tm_fixed_rate) - lq_sta->dbg_fixed_rate = priv->tm_fixed_rate; -#endif - IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); @@ -419,23 +413,18 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, load = rs_tl_get_load(lq_data, tid); - if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) { - IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", - sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } else { - IWL_DEBUG_HT(priv, "Aggregation not enabled for tid %d " - "because load = %u\n", tid, load); + IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", + sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); } return ret; } @@ -1083,12 +1072,7 @@ done: if (sta && sta->supp_rates[sband->band]) rs_rate_scale_perform(priv, skb, sta, lq_sta); -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_TESTMODE) - if ((priv->tm_fixed_rate) && - (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) - rs_program_fix_rate(priv, lq_sta); -#endif - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) rs_bt_update_lq(priv, ctx, lq_sta); } @@ -2913,9 +2897,6 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i if (sband->band == IEEE80211_BAND_5GHZ) lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; lq_sta->is_agg = 0; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - priv->tm_fixed_rate = 0; -#endif #ifdef CONFIG_MAC80211_DEBUGFS lq_sta->dbg_fixed_rate = 0; #endif @@ -3064,11 +3045,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, * overwrite if needed, pass aggregation time limit * to uCode in uSec */ - if (priv && priv->cfg->bt_params && - priv->cfg->bt_params->agg_time_limit && + if (priv && priv->lib->bt_params && + priv->lib->bt_params->agg_time_limit && priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(priv->cfg->bt_params->agg_time_limit); + cpu_to_le16(priv->lib->bt_params->agg_time_limit); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index a4eed2055fd..d71776dd1e6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -335,8 +335,7 @@ static void iwlagn_recover_from_statistics(struct iwl_priv *priv, if (msecs < 99) return; - if (iwlwifi_mod_params.plcp_check && - !iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) + if (!iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) iwl_force_rf_reset(priv, false); } @@ -1102,7 +1101,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) iwl_notification_wait_init(&priv->notif_wait); /* Set up BT Rx handlers */ - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_rx_handler_setup(priv); } @@ -1120,32 +1119,17 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, */ iwl_notification_wait_notify(&priv->notif_wait, pkt); -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - /* - * RX data may be forwarded to userspace in one - * of two cases: the user owns the fw through testmode or when - * the user requested to monitor the rx w/o affecting the regular flow. - * In these cases the iwl_test object will handle forwarding the rx - * data to user space. - * Note that if the ownership flag != IWL_OWNERSHIP_TM the flow - * continues. - */ - iwl_test_rx(&priv->tst, rxb); -#endif - - if (priv->ucode_owner != IWL_OWNERSHIP_TM) { - /* Based on type of command response or notification, - * handle those that need handling via function in - * rx_handlers table. See iwl_setup_rx_handlers() */ - if (priv->rx_handlers[pkt->hdr.cmd]) { - priv->rx_handlers_stats[pkt->hdr.cmd]++; - err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd); - } else { - /* No handling needed */ - IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", - iwl_dvm_get_cmd_string(pkt->hdr.cmd), - pkt->hdr.cmd); - } + /* Based on type of command response or notification, + * handle those that need handling via function in + * rx_handlers table. See iwl_setup_rx_handlers() */ + if (priv->rx_handlers[pkt->hdr.cmd]) { + priv->rx_handlers_stats[pkt->hdr.cmd]++; + err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd); + } else { + /* No handling needed */ + IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", + iwl_dvm_get_cmd_string(pkt->hdr.cmd), + pkt->hdr.cmd); } return err; } diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index d69b5586671..8c686a5b90a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) * Internal scans are passive, so we can indiscriminately set * the BT ignore flag on 2.4 GHz since it applies to TX only. */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; break; case IEEE80211_BAND_5GHZ: @@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) band = priv->scan_band; if (band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* transmit 2.4 GHz probes only on first antenna */ scan_tx_antennas = first_antenna(scan_tx_antennas); } @@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) rx_ant = first_antenna(active_chains); } - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ rx_ant = first_antenna(rx_ant); diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c deleted file mode 100644 index b89b9d9b996..00000000000 --- a/drivers/net/wireless/iwlwifi/dvm/testmode.c +++ /dev/null @@ -1,471 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <net/net_namespace.h> -#include <linux/netdevice.h> -#include <net/cfg80211.h> -#include <net/mac80211.h> -#include <net/netlink.h> - -#include "iwl-debug.h" -#include "iwl-trans.h" -#include "dev.h" -#include "agn.h" -#include "iwl-test.h" -#include "iwl-testmode.h" - -static int iwl_testmode_send_cmd(struct iwl_op_mode *op_mode, - struct iwl_host_cmd *cmd) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - return iwl_dvm_send_cmd(priv, cmd); -} - -static bool iwl_testmode_valid_hw_addr(u32 addr) -{ - if (iwlagn_hw_valid_rtc_data_addr(addr)) - return true; - - if (IWLAGN_RTC_INST_LOWER_BOUND <= addr && - addr < IWLAGN_RTC_INST_UPPER_BOUND) - return true; - - return false; -} - -static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - return priv->fw->ucode_ver; -} - -static struct sk_buff* -iwl_testmode_alloc_reply(struct iwl_op_mode *op_mode, int len) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - return cfg80211_testmode_alloc_reply_skb(priv->hw->wiphy, len); -} - -static int iwl_testmode_reply(struct iwl_op_mode *op_mode, struct sk_buff *skb) -{ - return cfg80211_testmode_reply(skb); -} - -static struct sk_buff *iwl_testmode_alloc_event(struct iwl_op_mode *op_mode, - int len) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - return cfg80211_testmode_alloc_event_skb(priv->hw->wiphy, len, - GFP_ATOMIC); -} - -static void iwl_testmode_event(struct iwl_op_mode *op_mode, struct sk_buff *skb) -{ - return cfg80211_testmode_event(skb, GFP_ATOMIC); -} - -static struct iwl_test_ops tst_ops = { - .send_cmd = iwl_testmode_send_cmd, - .valid_hw_addr = iwl_testmode_valid_hw_addr, - .get_fw_ver = iwl_testmode_get_fw_ver, - .alloc_reply = iwl_testmode_alloc_reply, - .reply = iwl_testmode_reply, - .alloc_event = iwl_testmode_alloc_event, - .event = iwl_testmode_event, -}; - -void iwl_testmode_init(struct iwl_priv *priv) -{ - iwl_test_init(&priv->tst, priv->trans, &tst_ops); -} - -void iwl_testmode_free(struct iwl_priv *priv) -{ - iwl_test_free(&priv->tst); -} - -static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv) -{ - struct iwl_notification_wait calib_wait; - static const u8 calib_complete[] = { - CALIBRATION_COMPLETE_NOTIFICATION - }; - int ret; - - iwl_init_notification_wait(&priv->notif_wait, &calib_wait, - calib_complete, ARRAY_SIZE(calib_complete), - NULL, NULL); - ret = iwl_init_alive_start(priv); - if (ret) { - IWL_ERR(priv, "Fail init calibration: %d\n", ret); - goto cfg_init_calib_error; - } - - ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, 2 * HZ); - if (ret) - IWL_ERR(priv, "Error detecting" - " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret); - return ret; - -cfg_init_calib_error: - iwl_remove_notification(&priv->notif_wait, &calib_wait); - return ret; -} - -/* - * This function handles the user application commands for driver. - * - * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the - * handlers respectively. - * - * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned - * value of the actual command execution is replied to the user application. - * - * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP - * is used for carry the message while IWL_TM_ATTR_COMMAND must set to - * IWL_TM_CMD_DEV2APP_SYNC_RSP. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_trans *trans = priv->trans; - struct sk_buff *skb; - unsigned char *rsp_data_ptr = NULL; - int status = 0, rsp_data_len = 0; - u32 inst_size = 0, data_size = 0; - const struct fw_img *img; - - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: - rsp_data_ptr = (unsigned char *)priv->cfg->name; - rsp_data_len = strlen(priv->cfg->name); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - rsp_data_len + 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, - IWL_TM_CMD_DEV2APP_SYNC_RSP) || - nla_put(skb, IWL_TM_ATTR_SYNC_RSP, - rsp_data_len, rsp_data_ptr)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: - status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); - if (status) - IWL_ERR(priv, "Error loading init ucode: %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: - iwl_testmode_cfg_init_calib(priv); - priv->ucode_loaded = false; - iwl_trans_stop_device(trans); - break; - - case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: - status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); - if (status) { - IWL_ERR(priv, - "Error loading runtime ucode: %d\n", status); - break; - } - status = iwl_alive_start(priv); - if (status) - IWL_ERR(priv, - "Error starting the device: %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: - iwl_scan_cancel_timeout(priv, 200); - priv->ucode_loaded = false; - iwl_trans_stop_device(trans); - status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); - if (status) { - IWL_ERR(priv, - "Error loading WOWLAN ucode: %d\n", status); - break; - } - status = iwl_alive_start(priv); - if (status) - IWL_ERR(priv, - "Error starting the device: %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_GET_EEPROM: - if (priv->eeprom_blob) { - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - priv->eeprom_blob_size + 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, - IWL_TM_CMD_DEV2APP_EEPROM_RSP) || - nla_put(skb, IWL_TM_ATTR_EEPROM, - priv->eeprom_blob_size, - priv->eeprom_blob)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", - status); - } else - return -ENODATA; - break; - - case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: - if (!tb[IWL_TM_ATTR_FIXRATE]) { - IWL_ERR(priv, "Missing fixrate setting\n"); - return -ENOMSG; - } - priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]); - break; - - case IWL_TM_CMD_APP2DEV_GET_FW_INFO: - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (!priv->ucode_loaded) { - IWL_ERR(priv, "No uCode has not been loaded\n"); - return -EINVAL; - } else { - img = &priv->fw->img[priv->cur_ucode]; - inst_size = img->sec[IWL_UCODE_SECTION_INST].len; - data_size = img->sec[IWL_UCODE_SECTION_DATA].len; - } - if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) || - nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) || - nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - default: - IWL_ERR(priv, "Unknown testmode driver command ID\n"); - return -ENOSYS; - } - return status; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; -} - -/* - * This function handles the user application switch ucode ownership. - * - * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and - * decide who the current owner of the uCode - * - * If the current owner is OWNERSHIP_TM, then the only host command - * can deliver to uCode is from testmode, all the other host commands - * will dropped. - * - * default driver is the owner of uCode in normal operational mode - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u8 owner; - - if (!tb[IWL_TM_ATTR_UCODE_OWNER]) { - IWL_ERR(priv, "Missing ucode owner\n"); - return -ENOMSG; - } - - owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]); - if (owner == IWL_OWNERSHIP_DRIVER) { - priv->ucode_owner = owner; - iwl_test_enable_notifications(&priv->tst, false); - } else if (owner == IWL_OWNERSHIP_TM) { - priv->ucode_owner = owner; - iwl_test_enable_notifications(&priv->tst, true); - } else { - IWL_ERR(priv, "Invalid owner\n"); - return -EINVAL; - } - return 0; -} - -/* The testmode gnl message handler that takes the gnl message from the - * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then - * invoke the corresponding handlers. - * - * This function is invoked when there is user space application sending - * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated - * by nl80211. - * - * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before - * dispatching it to the corresponding handler. - * - * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application; - * -ENOSYS is replied to the user application if the command is unknown; - * Otherwise, the command is dispatched to the respective handler. - * - * @hw: ieee80211_hw object that represents the device - * @data: pointer to user space message - * @len: length in byte of @data - */ -int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) -{ - struct nlattr *tb[IWL_TM_ATTR_MAX]; - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int result; - - result = iwl_test_parse(&priv->tst, tb, data, len); - if (result) - return result; - - /* in case multiple accesses to the device happens */ - mutex_lock(&priv->mutex); - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_UCODE: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - case IWL_TM_CMD_APP2DEV_END_TRACE: - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: - case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - result = iwl_test_handle_cmd(&priv->tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: - case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: - case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: - case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: - case IWL_TM_CMD_APP2DEV_GET_EEPROM: - case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: - case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: - case IWL_TM_CMD_APP2DEV_GET_FW_INFO: - IWL_DEBUG_INFO(priv, "testmode cmd to driver\n"); - result = iwl_testmode_driver(hw, tb); - break; - - case IWL_TM_CMD_APP2DEV_OWNERSHIP: - IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n"); - result = iwl_testmode_ownership(hw, tb); - break; - - default: - IWL_ERR(priv, "Unknown testmode command\n"); - result = -ENOSYS; - break; - } - mutex_unlock(&priv->mutex); - - if (result) - IWL_ERR(priv, "Test cmd failed result=%d\n", result); - return result; -} - -int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, - struct netlink_callback *cb, - void *data, int len) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int result; - u32 cmd; - - if (cb->args[3]) { - /* offset by 1 since commands start at 0 */ - cmd = cb->args[3] - 1; - } else { - struct nlattr *tb[IWL_TM_ATTR_MAX]; - - result = iwl_test_parse(&priv->tst, tb, data, len); - if (result) - return result; - - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - cb->args[3] = cmd + 1; - } - - /* in case multiple accesses to the device happens */ - mutex_lock(&priv->mutex); - result = iwl_test_dump(&priv->tst, cmd, skb, cb); - mutex_unlock(&priv->mutex); - return result; -} diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index 03f9bc01c0c..fbeee081ee2 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv) INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - if (priv->cfg->base_params->adv_thermal_throttle) { + if (priv->lib->adv_thermal_throttle) { IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); tt->restriction = kcalloc(IWL_TI_STATE_MAX, sizeof(struct iwl_tt_restriction), diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index a900aaf4779..5ee983faa67 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; else if (info->band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || skb->protocol == cpu_to_be16(ETH_P_PAE))) @@ -162,18 +162,6 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, if (ieee80211_is_data(fc)) { tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - if (priv->tm_fixed_rate) { - /* - * rate overwrite by testmode - * we not only send lq command to change rate - * we also re-enforce per data pkt base. - */ - tx_cmd->tx_flags &= ~TX_CMD_FLG_STA_RATE_MSK; - memcpy(&tx_cmd->rate_n_flags, &priv->tm_fixed_rate, - sizeof(tx_cmd->rate_n_flags)); - } -#endif return; } else if (ieee80211_is_back_req(fc)) tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; @@ -202,8 +190,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, @@ -986,8 +974,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, * notification again. */ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); } diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 0a1cdc5e856..86270b69cd0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv) { int ret; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* * Tell uCode we are ready to perform calibration * need to perform this before any calibration @@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv) * temperature offset calibration is only needed for runtime ucode, * so prepare the value now. */ - if (priv->cfg->need_temp_offset_calib) { - if (priv->cfg->temp_offset_v2) + if (priv->lib->need_temp_offset_calib) { + if (priv->lib->temp_offset_v2) return iwl_set_temperature_offset_calib_v2(priv); else return iwl_set_temperature_offset_calib(priv); @@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) if (ret) return ret; - if (!priv->cfg->no_xtal_calib) { + if (!priv->lib->no_xtal_calib) { ret = iwl_set_Xtal_calib(priv); if (ret) return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index c080ae3070b..0d2afe098af 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .led_compensation = 51, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 128, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index a6ddd2f9fba..c727ec7c90a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, }; @@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, }; static const struct iwl_ht_params iwl2000_ht_params = { @@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; -static const struct iwl_bt_params iwl2030_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, - .bt_sco_disable = true, - .bt_session_2 = true, -}; - static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .device_family = IWL_DEVICE_FAMILY_2000, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2000_2bgn_cfg = { @@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", @@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl105_bgn_cfg = { @@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl135_bgn_cfg = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 403f3f224bf..ecc01e1a61a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = { .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .led_compensation = 51, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 512, - .no_idle_support = true, }; static const struct iwl_ht_params iwl5000_ht_params = { @@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = { .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ - .no_xtal_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index b5ab8d1bcac..30d45e2fc19 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1500, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 1024, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; -static const struct iwl_bt_params iwl6000_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, - .bt_sco_disable = true, -}; - static const struct iwl_eeprom_params iwl6000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6005_2agn_cfg = { @@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", @@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 50263e87fe1..22b7fa5b971 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -67,16 +67,16 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 6 -#define IWL3160_UCODE_API_MAX 6 +#define IWL7260_UCODE_API_MAX 7 +#define IWL3160_UCODE_API_MAX 7 /* Oldest version we won't warn about */ -#define IWL7260_UCODE_API_OK 6 -#define IWL3160_UCODE_API_OK 6 +#define IWL7260_UCODE_API_OK 7 +#define IWL3160_UCODE_API_OK 7 /* Lowest firmware API version supported */ -#define IWL7260_UCODE_API_MIN 6 -#define IWL3160_UCODE_API_MIN 6 +#define IWL7260_UCODE_API_MIN 7 +#define IWL3160_UCODE_API_MIN 7 /* NVM versions */ #define IWL7260_NVM_VERSION 0x0a1d @@ -96,13 +96,9 @@ static const struct iwl_base_params iwl7000_base_params = { .pll_cfg_val = 0, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .shadow_reg_enable = true, }; static const struct iwl_ht_params iwl7000_ht_params = { @@ -118,14 +114,11 @@ static const struct iwl_ht_params iwl7000_ht_params = { .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl7000_base_params, \ - /* TODO: .bt_params? */ \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl7260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC7260", + .name = "Intel(R) Dual Band Wireless AC 7260", .fw_name_pre = IWL7260_FW_PRE, IWL_DEVICE_7000, .ht_params = &iwl7000_ht_params, @@ -133,8 +126,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, }; -const struct iwl_cfg iwl3160_ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC3160", +const struct iwl_cfg iwl7260_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl7260_n_cfg = { + .name = "Intel(R) Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_n_cfg = { + .name = "Intel(R) Wireless N 3160", .fw_name_pre = IWL3160_FW_PRE, IWL_DEVICE_7000, .ht_params = &iwl7000_ht_params, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index c38aa8f7755..83b9ff6ff3a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -136,17 +136,9 @@ enum iwl_led_mode { * @led_compensation: compensate on the led on/off time per HW according * to the deviation to achieve the desired led frequency. * The detail algorithm is described in iwl-led.c - * @chain_noise_num_beacons: number of beacons used to compute chain noise - * @adv_thermal_throttle: support advance thermal throttle - * @support_ct_kill_exit: support ct kill exit condition - * @plcp_delta_threshold: plcp error rate threshold used to trigger - * radio tuning when there is a high receiving plcp error rate - * @chain_noise_scale: default chain noise scale used for gain computation * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadow register support - * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up - * @no_idle_support: do not support idle mode */ struct iwl_base_params { int eeprom_size; @@ -157,31 +149,9 @@ struct iwl_base_params { const u16 max_ll_items; const bool shadow_ram_support; u16 led_compensation; - bool adv_thermal_throttle; - bool support_ct_kill_exit; - u8 plcp_delta_threshold; - s32 chain_noise_scale; unsigned int wd_timeout; u32 max_event_log_size; const bool shadow_reg_enable; - const bool hd_v2; - const bool no_idle_support; -}; - -/* - * @advanced_bt_coexist: support advanced bt coexist - * @bt_init_traffic_load: specify initial bt traffic load - * @bt_prio_boost: default bt priority boost value - * @agg_time_limit: maximum number of uSec in aggregation - * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode - */ -struct iwl_bt_params { - bool advanced_bt_coexist; - u8 bt_init_traffic_load; - u32 bt_prio_boost; - u16 agg_time_limit; - bool bt_sco_disable; - bool bt_session_2; }; /* @@ -231,16 +201,10 @@ struct iwl_eeprom_params { * @nvm_calib_ver: NVM calibration version * @lib: pointer to the lib ops * @base_params: pointer to basic parameters - * @ht_params: point to ht patameters - * @bt_params: pointer to bt parameters - * @need_temp_offset_calib: need to perform temperature offset calibration - * @no_xtal_calib: some devices do not need crystal calibration data, - * don't send it to those + * @ht_params: point to ht parameters * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) - * @adv_pm: advance power management * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device - * @temp_offset_v2: support v2 of temperature offset calibration * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -258,26 +222,23 @@ struct iwl_cfg { const u32 max_inst_size; u8 valid_tx_ant; u8 valid_rx_ant; + bool bt_shared_single_ant; u16 nvm_ver; u16 nvm_calib_ver; /* params not likely to change within a device family */ const struct iwl_base_params *base_params; /* params likely to change within a device family */ const struct iwl_ht_params *ht_params; - const struct iwl_bt_params *bt_params; const struct iwl_eeprom_params *eeprom_params; - const bool need_temp_offset_calib; /* if used set to true */ - const bool no_xtal_calib; enum iwl_led_mode led_mode; - const bool adv_pm; const bool rx_with_siso_diversity; const bool internal_wimax_coex; - const bool temp_offset_v2; }; /* * This list declares the config structures for all devices. */ +#if IS_ENABLED(CONFIG_IWLDVM) extern const struct iwl_cfg iwl5300_agn_cfg; extern const struct iwl_cfg iwl5100_agn_cfg; extern const struct iwl_cfg iwl5350_agn_cfg; @@ -319,7 +280,14 @@ extern const struct iwl_cfg iwl6035_2agn_cfg; extern const struct iwl_cfg iwl105_bgn_cfg; extern const struct iwl_cfg iwl105_bgn_d_cfg; extern const struct iwl_cfg iwl135_bgn_cfg; +#endif /* CONFIG_IWLDVM */ +#if IS_ENABLED(CONFIG_IWLMVM) extern const struct iwl_cfg iwl7260_2ac_cfg; -extern const struct iwl_cfg iwl3160_ac_cfg; +extern const struct iwl_cfg iwl7260_2n_cfg; +extern const struct iwl_cfg iwl7260_n_cfg; +extern const struct iwl_cfg iwl3160_2ac_cfg; +extern const struct iwl_cfg iwl3160_2n_cfg; +extern const struct iwl_cfg iwl3160_n_cfg; +#endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 20e845d4da0..a276af476e2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -472,4 +472,23 @@ #define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) #define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +/* Diode Results Register Structure: */ +enum dtd_diode_reg { + DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */ + DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */ + DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */ + DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */ + DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */ + DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */ +/* Those are the masks INSIDE the flags bit-field: */ + DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0, + DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */ + DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7, + DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ +}; + #endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 8cf5db7fb5c..7edb8519c8a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -34,7 +34,11 @@ static inline bool iwl_have_debug_level(u32 level) { +#ifdef CONFIG_IWLWIFI_DEBUG return iwlwifi_mod_params.debug_level & level; +#else + return false; +#endif } void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace, diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 40fed1f511e..d0162d426f8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1111,11 +1111,8 @@ void iwl_drv_stop(struct iwl_drv *drv) /* shared module parameters */ struct iwl_mod_params iwlwifi_mod_params = { .restart_fw = true, - .plcp_check = true, .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, - .bt_ch_announce = true, - .auto_agg = true, .wd_disable = true, /* the rest are 0 by default */ }; @@ -1223,19 +1220,14 @@ module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling, MODULE_PARM_DESC(antenna_coupling, "specify antenna coupling in dB (defualt: 0 dB)"); -module_param_named(bt_ch_inhibition, iwlwifi_mod_params.bt_ch_announce, - bool, S_IRUGO); -MODULE_PARM_DESC(bt_ch_inhibition, - "Enable BT channel inhibition (default: enable)"); - -module_param_named(plcp_check, iwlwifi_mod_params.plcp_check, bool, S_IRUGO); -MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])"); - module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO); MODULE_PARM_DESC(wd_disable, "Disable stuck queue watchdog timer 0=system default, " "1=disable, 2=enable (default: 0)"); +module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); +MODULE_PARM_DESC(nvm_file, "NVM file name"); + /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the @@ -1269,8 +1261,3 @@ module_param_named(power_level, iwlwifi_mod_params.power_level, int, S_IRUGO); MODULE_PARM_DESC(power_level, "default power save level (range from 1 - 5, default: 1)"); - -module_param_named(auto_agg, iwlwifi_mod_params.auto_agg, - bool, S_IRUGO); -MODULE_PARM_DESC(auto_agg, - "enable agg w/o check traffic load (default: enable)"); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 7d145091630..429337a2b9a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -62,8 +62,7 @@ #ifndef __iwl_drv_h__ #define __iwl_drv_h__ - -#include <linux/module.h> +#include <linux/export.h> /* for all modules */ #define DRV_NAME "iwlwifi" diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index 600c9fdd7f7..4c887f36590 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -732,17 +732,16 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data, void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band) + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains) { int max_bit_rate = 0; - u8 rx_chains; - u8 tx_chains; - tx_chains = hweight8(data->valid_tx_ant); + tx_chains = hweight8(tx_chains); if (cfg->rx_with_siso_diversity) rx_chains = 1; else - rx_chains = hweight8(data->valid_rx_ant); + rx_chains = hweight8(rx_chains); if (!(data->sku_cap_11n_enable) || !cfg->ht_params) { ht_info->ht_supported = false; @@ -806,7 +805,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + data->valid_tx_ant, data->valid_rx_ant); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; @@ -814,7 +814,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + data->valid_tx_ant, data->valid_rx_ant); if (n_channels != n_used) IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index 37f115390b1..d73304a23ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -133,6 +133,7 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data, void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band); + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains); #endif /* __iwl_eeprom_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index c4c446d41eb..f844d5c748c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -106,11 +106,14 @@ enum iwl_ucode_type { /* * enumeration of ucode section. - * This enumeration is used for legacy tlv style (before 16.0 uCode). + * This enumeration is used directly for older firmware (before 16.0). + * For new firmware, there can be up to 4 sections (see below) but the + * first one packaged into the firmware file is the DATA section and + * some debugging code accesses that. */ enum iwl_ucode_sec { - IWL_UCODE_SECTION_INST, IWL_UCODE_SECTION_DATA, + IWL_UCODE_SECTION_INST, }; /* * For 16.0 uCode and above, there is no differentiation between sections, diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d6f6c37c09f..a1f580c0c6c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -93,7 +93,6 @@ enum iwl_power_level { * use IWL_DISABLE_HT_* constants * @amsdu_size_8K: enable 8K amsdu size, default = 0 * @restart_fw: restart firmware, default = 1 - * @plcp_check: enable plcp health check, default = true * @wd_disable: enable stuck queue check, default = 0 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 @@ -101,24 +100,22 @@ enum iwl_power_level { * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 - * @bt_ch_announce: BT channel inhibition, default = enable - * @auto_agg: enable agg. without check, default = true */ struct iwl_mod_params { int sw_crypto; unsigned int disable_11n; int amsdu_size_8K; bool restart_fw; - bool plcp_check; int wd_disable; bool bt_coex_active; int led_mode; bool power_save; int power_level; +#ifdef CONFIG_IWLWIFI_DEBUG u32 debug_level; +#endif int ant_coupling; - bool bt_ch_announce; - bool auto_agg; + char *nvm_file; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 6199a0a597a..acd2665afb8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -89,6 +89,7 @@ enum nvm_sku_bits { NVM_SKU_CAP_BAND_24GHZ = BIT(0), NVM_SKU_CAP_BAND_52GHZ = BIT(1), NVM_SKU_CAP_11N_ENABLE = BIT(2), + NVM_SKU_CAP_11AC_ENABLE = BIT(3), }; /* radio config bits (actual values from NVM definition) */ @@ -258,8 +259,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_vht_cap *vht_cap) { - /* For now, assume new devices with NVM are VHT capable */ - vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | @@ -292,7 +291,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_sw) + struct iwl_nvm_data *data, const __le16 *nvm_sw, + bool enable_vht, u8 tx_chains, u8 rx_chains) { int n_channels = iwl_init_channel_map(dev, cfg, data, &nvm_sw[NVM_CHANNELS]); @@ -305,7 +305,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + tx_chains, rx_chains); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; @@ -313,8 +314,10 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); - iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + tx_chains, rx_chains); + if (enable_vht) + iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); if (n_channels != n_used) IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", @@ -324,7 +327,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib) + const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains) { struct iwl_nvm_data *data; u8 hw_addr[ETH_ALEN]; @@ -380,7 +383,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - iwl_init_sbands(dev, cfg, data, nvm_sw); + iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE, + tx_chains, rx_chains); data->calib_version = 255; /* TODO: this value will prevent some checks from diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index e57fb989661..3325059c52d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -75,6 +75,6 @@ struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib); + const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains); #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c index 25745daa0d5..1a405ae6a9c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c @@ -92,20 +92,16 @@ struct iwl_phy_db_entry { struct iwl_phy_db { struct iwl_phy_db_entry cfg; struct iwl_phy_db_entry calib_nch; - struct iwl_phy_db_entry calib_ch; struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS]; struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS]; - u32 channel_num; - u32 channel_size; - struct iwl_trans *trans; }; enum iwl_phy_db_section_type { IWL_PHY_DB_CFG = 1, IWL_PHY_DB_CALIB_NCH, - IWL_PHY_DB_CALIB_CH, + IWL_PHY_DB_UNUSED, IWL_PHY_DB_CALIB_CHG_PAPD, IWL_PHY_DB_CALIB_CHG_TXP, IWL_PHY_DB_MAX @@ -169,8 +165,6 @@ iwl_phy_db_get_section(struct iwl_phy_db *phy_db, return &phy_db->cfg; case IWL_PHY_DB_CALIB_NCH: return &phy_db->calib_nch; - case IWL_PHY_DB_CALIB_CH: - return &phy_db->calib_ch; case IWL_PHY_DB_CALIB_CHG_PAPD: if (chg_id >= IWL_NUM_PAPD_CH_GROUPS) return NULL; @@ -208,7 +202,6 @@ void iwl_phy_db_free(struct iwl_phy_db *phy_db) iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0); iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0); - iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0); for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++) iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i); for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) @@ -248,13 +241,6 @@ int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, entry->size = size; - if (type == IWL_PHY_DB_CALIB_CH) { - phy_db->channel_num = - le32_to_cpup((__le32 *)phy_db_notif->data); - phy_db->channel_size = - (size - CHANNEL_NUM_SIZE) / phy_db->channel_num; - } - IWL_DEBUG_INFO(phy_db->trans, "%s(%d): [PHYDB]SET: Type %d , Size: %d\n", __func__, __LINE__, type, size); @@ -328,10 +314,7 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db, u32 type, u8 **data, u16 *size, u16 ch_id) { struct iwl_phy_db_entry *entry; - u32 channel_num; - u32 channel_size; u16 ch_group_id = 0; - u16 index; if (!phy_db) return -EINVAL; @@ -346,21 +329,8 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db, if (!entry) return -EINVAL; - if (type == IWL_PHY_DB_CALIB_CH) { - index = ch_id_to_ch_index(ch_id); - channel_num = phy_db->channel_num; - channel_size = phy_db->channel_size; - if (index >= channel_num) { - IWL_ERR(phy_db->trans, "Wrong channel number %d\n", - ch_id); - return -EINVAL; - } - *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size; - *size = channel_size; - } else { - *data = entry->data; - *size = entry->size; - } + *data = entry->data; + *size = entry->size; IWL_DEBUG_INFO(phy_db->trans, "%s(%d): [PHYDB] GET: Type %d , Size: %d\n", @@ -413,6 +383,9 @@ static int iwl_phy_db_send_all_channel_groups( if (!entry) return -EINVAL; + if (WARN_ON_ONCE(!entry->size)) + continue; + /* Send the requested PHY DB section */ err = iwl_send_phy_db_cmd(phy_db, type, diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 386f2a7c87c..ff8cc75c189 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -100,6 +100,18 @@ /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +#define SHR_MISC_WFM_DTS_EN (0x00a10024) +#define DTSC_CFG_MODE (0x00a10604) +#define DTSC_VREF_AVG (0x00a10648) +#define DTSC_VREF5_AVG (0x00a1064c) +#define DTSC_CFG_MODE_PERIODIC (0x2) +#define DTSC_PTAT_AVG (0x00a10650) + + /** * Tx Scheduler * diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c deleted file mode 100644 index 5cfd55b86ed..00000000000 --- a/drivers/net/wireless/iwlwifi/iwl-test.c +++ /dev/null @@ -1,852 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include <linux/export.h> -#include <net/netlink.h> - -#include "iwl-drv.h" -#include "iwl-io.h" -#include "iwl-fh.h" -#include "iwl-prph.h" -#include "iwl-trans.h" -#include "iwl-test.h" -#include "iwl-csr.h" -#include "iwl-testmode.h" - -/* - * Periphery registers absolute lower bound. This is used in order to - * differentiate registery access through HBUS_TARG_PRPH_* and - * HBUS_TARG_MEM_* accesses. - */ -#define IWL_ABS_PRPH_START (0xA00000) - -/* - * The TLVs used in the gnl message policy between the kernel module and - * user space application. iwl_testmode_gnl_msg_policy is to be carried - * through the NL80211_CMD_TESTMODE channel regulated by nl80211. - * See iwl-testmode.h - */ -static -struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { - [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, }, - [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, }, - [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, }, - [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, - - [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, }, - [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, - [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, }, -}; - -static inline void iwl_test_trace_clear(struct iwl_test *tst) -{ - memset(&tst->trace, 0, sizeof(struct iwl_test_trace)); -} - -static void iwl_test_trace_stop(struct iwl_test *tst) -{ - if (!tst->trace.enabled) - return; - - if (tst->trace.cpu_addr && tst->trace.dma_addr) - dma_free_coherent(tst->trans->dev, - tst->trace.tsize, - tst->trace.cpu_addr, - tst->trace.dma_addr); - - iwl_test_trace_clear(tst); -} - -static inline void iwl_test_mem_clear(struct iwl_test *tst) -{ - memset(&tst->mem, 0, sizeof(struct iwl_test_mem)); -} - -static inline void iwl_test_mem_stop(struct iwl_test *tst) -{ - if (!tst->mem.in_read) - return; - - iwl_test_mem_clear(tst); -} - -/* - * Initializes the test object - * During the lifetime of the test object it is assumed that the transport is - * started. The test object should be stopped before the transport is stopped. - */ -void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans, - struct iwl_test_ops *ops) -{ - tst->trans = trans; - tst->ops = ops; - - iwl_test_trace_clear(tst); - iwl_test_mem_clear(tst); -} -EXPORT_SYMBOL_GPL(iwl_test_init); - -/* - * Stop the test object - */ -void iwl_test_free(struct iwl_test *tst) -{ - iwl_test_mem_stop(tst); - iwl_test_trace_stop(tst); -} -EXPORT_SYMBOL_GPL(iwl_test_free); - -static inline int iwl_test_send_cmd(struct iwl_test *tst, - struct iwl_host_cmd *cmd) -{ - return tst->ops->send_cmd(tst->trans->op_mode, cmd); -} - -static inline bool iwl_test_valid_hw_addr(struct iwl_test *tst, u32 addr) -{ - return tst->ops->valid_hw_addr(addr); -} - -static inline u32 iwl_test_fw_ver(struct iwl_test *tst) -{ - return tst->ops->get_fw_ver(tst->trans->op_mode); -} - -static inline struct sk_buff* -iwl_test_alloc_reply(struct iwl_test *tst, int len) -{ - return tst->ops->alloc_reply(tst->trans->op_mode, len); -} - -static inline int iwl_test_reply(struct iwl_test *tst, struct sk_buff *skb) -{ - return tst->ops->reply(tst->trans->op_mode, skb); -} - -static inline struct sk_buff* -iwl_test_alloc_event(struct iwl_test *tst, int len) -{ - return tst->ops->alloc_event(tst->trans->op_mode, len); -} - -static inline void -iwl_test_event(struct iwl_test *tst, struct sk_buff *skb) -{ - return tst->ops->event(tst->trans->op_mode, skb); -} - -/* - * This function handles the user application commands to the fw. The fw - * commands are sent in a synchronuous manner. In case that the user requested - * to get commands response, it is send to the user. - */ -static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb) -{ - struct iwl_host_cmd cmd; - struct iwl_rx_packet *pkt; - struct sk_buff *skb; - void *reply_buf; - u32 reply_len; - int ret; - bool cmd_want_skb; - - memset(&cmd, 0, sizeof(struct iwl_host_cmd)); - - if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] || - !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) { - IWL_ERR(tst->trans, "Missing fw command mandatory fields\n"); - return -ENOMSG; - } - - cmd.flags = CMD_ON_DEMAND | CMD_SYNC; - cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]); - if (cmd_want_skb) - cmd.flags |= CMD_WANT_SKB; - - cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]); - cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); - cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n", - cmd.id, cmd.flags, cmd.len[0]); - - ret = iwl_test_send_cmd(tst, &cmd); - if (ret) { - IWL_ERR(tst->trans, "Failed to send hcmd\n"); - return ret; - } - if (!cmd_want_skb) - return ret; - - /* Handling return of SKB to the user */ - pkt = cmd.resp_pkt; - if (!pkt) { - IWL_ERR(tst->trans, "HCMD received a null response packet\n"); - return ret; - } - - reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - skb = iwl_test_alloc_reply(tst, reply_len + 20); - reply_buf = kmemdup(&pkt->hdr, reply_len, GFP_KERNEL); - if (!skb || !reply_buf) { - kfree_skb(skb); - kfree(reply_buf); - return -ENOMEM; - } - - /* The reply is in a page, that we cannot send to user space. */ - iwl_free_resp(&cmd); - - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, - IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || - nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) - goto nla_put_failure; - return iwl_test_reply(tst, skb); - -nla_put_failure: - IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n"); - kfree(reply_buf); - kfree_skb(skb); - return -ENOMSG; -} - -/* - * Handles the user application commands for register access. - */ -static int iwl_test_reg(struct iwl_test *tst, struct nlattr **tb) -{ - u32 ofs, val32, cmd; - u8 val8; - struct sk_buff *skb; - int status = 0; - struct iwl_trans *trans = tst->trans; - - if (!tb[IWL_TM_ATTR_REG_OFFSET]) { - IWL_ERR(trans, "Missing reg offset\n"); - return -ENOMSG; - } - - ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]); - IWL_DEBUG_INFO(trans, "test reg access cmd offset=0x%x\n", ofs); - - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - - /* - * Allow access only to FH/CSR/HBUS in direct mode. - * Since we don't have the upper bounds for the CSR and HBUS segments, - * we will use only the upper bound of FH for sanity check. - */ - if (ofs >= FH_MEM_UPPER_BOUND) { - IWL_ERR(trans, "offset out of segment (0x0 - 0x%x)\n", - FH_MEM_UPPER_BOUND); - return -EINVAL; - } - - switch (cmd) { - case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - val32 = iwl_read_direct32(tst->trans, ofs); - IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32); - - skb = iwl_test_alloc_reply(tst, 20); - if (!skb) { - IWL_ERR(trans, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) - goto nla_put_failure; - status = iwl_test_reply(tst, skb); - if (status < 0) - IWL_ERR(trans, "Error sending msg : %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - if (!tb[IWL_TM_ATTR_REG_VALUE32]) { - IWL_ERR(trans, "Missing value to write\n"); - return -ENOMSG; - } else { - val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); - IWL_DEBUG_INFO(trans, "32b write val=0x%x\n", val32); - iwl_write_direct32(tst->trans, ofs, val32); - } - break; - - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - if (!tb[IWL_TM_ATTR_REG_VALUE8]) { - IWL_ERR(trans, "Missing value to write\n"); - return -ENOMSG; - } else { - val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]); - IWL_DEBUG_INFO(trans, "8b write val=0x%x\n", val8); - iwl_write8(tst->trans, ofs, val8); - } - break; - - default: - IWL_ERR(trans, "Unknown test register cmd ID\n"); - return -ENOMSG; - } - - return status; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; -} - -/* - * Handles the request to start FW tracing. Allocates of the trace buffer - * and sends a reply to user space with the address of the allocated buffer. - */ -static int iwl_test_trace_begin(struct iwl_test *tst, struct nlattr **tb) -{ - struct sk_buff *skb; - int status = 0; - - if (tst->trace.enabled) - return -EBUSY; - - if (!tb[IWL_TM_ATTR_TRACE_SIZE]) - tst->trace.size = TRACE_BUFF_SIZE_DEF; - else - tst->trace.size = - nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]); - - if (!tst->trace.size) - return -EINVAL; - - if (tst->trace.size < TRACE_BUFF_SIZE_MIN || - tst->trace.size > TRACE_BUFF_SIZE_MAX) - return -EINVAL; - - tst->trace.tsize = tst->trace.size + TRACE_BUFF_PADD; - tst->trace.cpu_addr = dma_alloc_coherent(tst->trans->dev, - tst->trace.tsize, - &tst->trace.dma_addr, - GFP_KERNEL); - if (!tst->trace.cpu_addr) - return -ENOMEM; - - tst->trace.enabled = true; - tst->trace.trace_addr = (u8 *)PTR_ALIGN(tst->trace.cpu_addr, 0x100); - - memset(tst->trace.trace_addr, 0x03B, tst->trace.size); - - skb = iwl_test_alloc_reply(tst, sizeof(tst->trace.dma_addr) + 20); - if (!skb) { - IWL_ERR(tst->trans, "Memory allocation fail\n"); - iwl_test_trace_stop(tst); - return -ENOMEM; - } - - if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR, - sizeof(tst->trace.dma_addr), - (u64 *)&tst->trace.dma_addr)) - goto nla_put_failure; - - status = iwl_test_reply(tst, skb); - if (status < 0) - IWL_ERR(tst->trans, "Error sending msg : %d\n", status); - - tst->trace.nchunks = DIV_ROUND_UP(tst->trace.size, - DUMP_CHUNK_SIZE); - - return status; - -nla_put_failure: - kfree_skb(skb); - if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) == - IWL_TM_CMD_APP2DEV_BEGIN_TRACE) - iwl_test_trace_stop(tst); - return -EMSGSIZE; -} - -/* - * Handles indirect read from the periphery or the SRAM. The read is performed - * to a temporary buffer. The user space application should later issue a dump - */ -static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size) -{ - struct iwl_trans *trans = tst->trans; - unsigned long flags; - int i; - - if (size & 0x3) - return -EINVAL; - - tst->mem.size = size; - tst->mem.addr = kmalloc(tst->mem.size, GFP_KERNEL); - if (tst->mem.addr == NULL) - return -ENOMEM; - - /* Hard-coded periphery absolute address */ - if (IWL_ABS_PRPH_START <= addr && - addr < IWL_ABS_PRPH_START + PRPH_END) { - if (!iwl_trans_grab_nic_access(trans, false, &flags)) { - return -EIO; - } - iwl_write32(trans, HBUS_TARG_PRPH_RADDR, - addr | (3 << 24)); - for (i = 0; i < size; i += 4) - *(u32 *)(tst->mem.addr + i) = - iwl_read32(trans, HBUS_TARG_PRPH_RDAT); - iwl_trans_release_nic_access(trans, &flags); - } else { /* target memory (SRAM) */ - iwl_trans_read_mem(trans, addr, tst->mem.addr, - tst->mem.size / 4); - } - - tst->mem.nchunks = - DIV_ROUND_UP(tst->mem.size, DUMP_CHUNK_SIZE); - tst->mem.in_read = true; - return 0; - -} - -/* - * Handles indirect write to the periphery or SRAM. The is performed to a - * temporary buffer. - */ -static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr, - u32 size, unsigned char *buf) -{ - struct iwl_trans *trans = tst->trans; - u32 val, i; - unsigned long flags; - - if (IWL_ABS_PRPH_START <= addr && - addr < IWL_ABS_PRPH_START + PRPH_END) { - /* Periphery writes can be 1-3 bytes long, or DWORDs */ - if (size < 4) { - memcpy(&val, buf, size); - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - return -EIO; - iwl_write32(trans, HBUS_TARG_PRPH_WADDR, - (addr & 0x0000FFFF) | - ((size - 1) << 24)); - iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); - iwl_trans_release_nic_access(trans, &flags); - } else { - if (size % 4) - return -EINVAL; - for (i = 0; i < size; i += 4) - iwl_write_prph(trans, addr+i, - *(u32 *)(buf+i)); - } - } else if (iwl_test_valid_hw_addr(tst, addr)) { - iwl_trans_write_mem(trans, addr, buf, size / 4); - } else { - return -EINVAL; - } - return 0; -} - -/* - * Handles the user application commands for indirect read/write - * to/from the periphery or the SRAM. - */ -static int iwl_test_indirect_mem(struct iwl_test *tst, struct nlattr **tb) -{ - u32 addr, size, cmd; - unsigned char *buf; - - /* Both read and write should be blocked, for atomicity */ - if (tst->mem.in_read) - return -EBUSY; - - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - if (!tb[IWL_TM_ATTR_MEM_ADDR]) { - IWL_ERR(tst->trans, "Error finding memory offset address\n"); - return -ENOMSG; - } - addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]); - if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) { - IWL_ERR(tst->trans, "Error finding size for memory reading\n"); - return -ENOMSG; - } - size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]); - - if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) { - return iwl_test_indirect_read(tst, addr, size); - } else { - if (!tb[IWL_TM_ATTR_BUFFER_DUMP]) - return -EINVAL; - buf = (unsigned char *)nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]); - return iwl_test_indirect_write(tst, addr, size, buf); - } -} - -/* - * Enable notifications to user space - */ -static int iwl_test_notifications(struct iwl_test *tst, - struct nlattr **tb) -{ - tst->notify = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]); - return 0; -} - -/* - * Handles the request to get the device id - */ -static int iwl_test_get_dev_id(struct iwl_test *tst, struct nlattr **tb) -{ - u32 devid = tst->trans->hw_id; - struct sk_buff *skb; - int status; - - IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid); - - skb = iwl_test_alloc_reply(tst, 20); - if (!skb) { - IWL_ERR(tst->trans, "Memory allocation fail\n"); - return -ENOMEM; - } - - if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) - goto nla_put_failure; - status = iwl_test_reply(tst, skb); - if (status < 0) - IWL_ERR(tst->trans, "Error sending msg : %d\n", status); - - return 0; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; -} - -/* - * Handles the request to get the FW version - */ -static int iwl_test_get_fw_ver(struct iwl_test *tst, struct nlattr **tb) -{ - struct sk_buff *skb; - int status; - u32 ver = iwl_test_fw_ver(tst); - - IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver); - - skb = iwl_test_alloc_reply(tst, 20); - if (!skb) { - IWL_ERR(tst->trans, "Memory allocation fail\n"); - return -ENOMEM; - } - - if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver)) - goto nla_put_failure; - - status = iwl_test_reply(tst, skb); - if (status < 0) - IWL_ERR(tst->trans, "Error sending msg : %d\n", status); - - return 0; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; -} - -/* - * Parse the netlink message and validate that the IWL_TM_ATTR_CMD exists - */ -int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, - void *data, int len) -{ - int result; - - result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, - iwl_testmode_gnl_msg_policy); - if (result) { - IWL_ERR(tst->trans, "Fail parse gnl msg: %d\n", result); - return result; - } - - /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ - if (!tb[IWL_TM_ATTR_COMMAND]) { - IWL_ERR(tst->trans, "Missing testmode command type\n"); - return -ENOMSG; - } - return 0; -} -IWL_EXPORT_SYMBOL(iwl_test_parse); - -/* - * Handle test commands. - * Returns 1 for unknown commands (not handled by the test object); negative - * value in case of error. - */ -int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb) -{ - int result; - - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_UCODE: - IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n"); - result = iwl_test_fw_cmd(tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - IWL_DEBUG_INFO(tst->trans, "test cmd to register\n"); - result = iwl_test_reg(tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n"); - result = iwl_test_trace_begin(tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_END_TRACE: - iwl_test_trace_stop(tst); - result = 0; - break; - - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - IWL_DEBUG_INFO(tst->trans, "test indirect memory cmd\n"); - result = iwl_test_indirect_mem(tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - IWL_DEBUG_INFO(tst->trans, "test notifications cmd\n"); - result = iwl_test_notifications(tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: - IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n"); - result = iwl_test_get_fw_ver(tst, tb); - break; - - case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: - IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n"); - result = iwl_test_get_dev_id(tst, tb); - break; - - default: - IWL_DEBUG_INFO(tst->trans, "Unknown test command\n"); - result = 1; - break; - } - return result; -} -IWL_EXPORT_SYMBOL(iwl_test_handle_cmd); - -static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb, - struct netlink_callback *cb) -{ - int idx, length; - - if (!tst->trace.enabled || !tst->trace.trace_addr) - return -EFAULT; - - idx = cb->args[4]; - if (idx >= tst->trace.nchunks) - return -ENOENT; - - length = DUMP_CHUNK_SIZE; - if (((idx + 1) == tst->trace.nchunks) && - (tst->trace.size % DUMP_CHUNK_SIZE)) - length = tst->trace.size % - DUMP_CHUNK_SIZE; - - if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length, - tst->trace.trace_addr + (DUMP_CHUNK_SIZE * idx))) - goto nla_put_failure; - - cb->args[4] = ++idx; - return 0; - - nla_put_failure: - return -ENOBUFS; -} - -static int iwl_test_buffer_dump(struct iwl_test *tst, struct sk_buff *skb, - struct netlink_callback *cb) -{ - int idx, length; - - if (!tst->mem.in_read) - return -EFAULT; - - idx = cb->args[4]; - if (idx >= tst->mem.nchunks) { - iwl_test_mem_stop(tst); - return -ENOENT; - } - - length = DUMP_CHUNK_SIZE; - if (((idx + 1) == tst->mem.nchunks) && - (tst->mem.size % DUMP_CHUNK_SIZE)) - length = tst->mem.size % DUMP_CHUNK_SIZE; - - if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length, - tst->mem.addr + (DUMP_CHUNK_SIZE * idx))) - goto nla_put_failure; - - cb->args[4] = ++idx; - return 0; - - nla_put_failure: - return -ENOBUFS; -} - -/* - * Handle dump commands. - * Returns 1 for unknown commands (not handled by the test object); negative - * value in case of error. - */ -int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, - struct netlink_callback *cb) -{ - int result; - - switch (cmd) { - case IWL_TM_CMD_APP2DEV_READ_TRACE: - IWL_DEBUG_INFO(tst->trans, "uCode trace cmd\n"); - result = iwl_test_trace_dump(tst, skb, cb); - break; - - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: - IWL_DEBUG_INFO(tst->trans, "testmode sram dump cmd\n"); - result = iwl_test_buffer_dump(tst, skb, cb); - break; - - default: - result = 1; - break; - } - return result; -} -IWL_EXPORT_SYMBOL(iwl_test_dump); - -/* - * Multicast a spontaneous messages from the device to the user space. - */ -static void iwl_test_send_rx(struct iwl_test *tst, - struct iwl_rx_cmd_buffer *rxb) -{ - struct sk_buff *skb; - struct iwl_rx_packet *data; - int length; - - data = rxb_addr(rxb); - length = le32_to_cpu(data->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - - /* the length doesn't include len_n_flags field, so add it manually */ - length += sizeof(__le32); - - skb = iwl_test_alloc_event(tst, length + 20); - if (skb == NULL) { - IWL_ERR(tst->trans, "Out of memory for message to user\n"); - return; - } - - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, - IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || - nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data)) - goto nla_put_failure; - - iwl_test_event(tst, skb); - return; - -nla_put_failure: - kfree_skb(skb); - IWL_ERR(tst->trans, "Ouch, overran buffer, check allocation!\n"); -} - -/* - * Called whenever a Rx frames is recevied from the device. If notifications to - * the user space are requested, sends the frames to the user. - */ -void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb) -{ - if (tst->notify) - iwl_test_send_rx(tst, rxb); -} -IWL_EXPORT_SYMBOL(iwl_test_rx); diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h deleted file mode 100644 index 8fbd2170484..00000000000 --- a/drivers/net/wireless/iwlwifi/iwl-test.h +++ /dev/null @@ -1,161 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __IWL_TEST_H__ -#define __IWL_TEST_H__ - -#include <linux/types.h> -#include "iwl-trans.h" - -struct iwl_test_trace { - u32 size; - u32 tsize; - u32 nchunks; - u8 *cpu_addr; - u8 *trace_addr; - dma_addr_t dma_addr; - bool enabled; -}; - -struct iwl_test_mem { - u32 size; - u32 nchunks; - u8 *addr; - bool in_read; -}; - -/* - * struct iwl_test_ops: callback to the op mode - * - * The structure defines the callbacks that the op_mode should handle, - * inorder to handle logic that is out of the scope of iwl_test. The - * op_mode must set all the callbacks. - - * @send_cmd: handler that is used by the test object to request the - * op_mode to send a command to the fw. - * - * @valid_hw_addr: handler that is used by the test object to request the - * op_mode to check if the given address is a valid address. - * - * @get_fw_ver: handler used to get the FW version. - * - * @alloc_reply: handler used by the test object to request the op_mode - * to allocate an skb for sending a reply to the user, and initialize - * the skb. It is assumed that the test object only fills the required - * attributes. - * - * @reply: handler used by the test object to request the op_mode to reply - * to a request. The skb is an skb previously allocated by the the - * alloc_reply callback. - I - * @alloc_event: handler used by the test object to request the op_mode - * to allocate an skb for sending an event, and initialize - * the skb. It is assumed that the test object only fills the required - * attributes. - * - * @reply: handler used by the test object to request the op_mode to send - * an event. The skb is an skb previously allocated by the the - * alloc_event callback. - */ -struct iwl_test_ops { - int (*send_cmd)(struct iwl_op_mode *op_modes, - struct iwl_host_cmd *cmd); - bool (*valid_hw_addr)(u32 addr); - u32 (*get_fw_ver)(struct iwl_op_mode *op_mode); - - struct sk_buff *(*alloc_reply)(struct iwl_op_mode *op_mode, int len); - int (*reply)(struct iwl_op_mode *op_mode, struct sk_buff *skb); - struct sk_buff* (*alloc_event)(struct iwl_op_mode *op_mode, int len); - void (*event)(struct iwl_op_mode *op_mode, struct sk_buff *skb); -}; - -struct iwl_test { - struct iwl_trans *trans; - struct iwl_test_ops *ops; - struct iwl_test_trace trace; - struct iwl_test_mem mem; - bool notify; -}; - -void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans, - struct iwl_test_ops *ops); - -void iwl_test_free(struct iwl_test *tst); - -int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, - void *data, int len); - -int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb); - -int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, - struct netlink_callback *cb); - -void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb); - -static inline void iwl_test_enable_notifications(struct iwl_test *tst, - bool enable) -{ - tst->notify = enable; -} - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h deleted file mode 100644 index 98f48a9afc9..00000000000 --- a/drivers/net/wireless/iwlwifi/iwl-testmode.h +++ /dev/null @@ -1,309 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __IWL_TESTMODE_H__ -#define __IWL_TESTMODE_H__ - -#include <linux/types.h> - - -/* - * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and - * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX). - * The command ID is carried with IWL_TM_ATTR_COMMAND. - * - * @IWL_TM_CMD_APP2DEV_UCODE: - * commands from user application to the uCode, - * the actual uCode host command ID is carried with - * IWL_TM_ATTR_UCODE_CMD_ID - * - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - * commands from user applicaiton to access register - * - * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name - * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image - * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration - * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image - * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data - * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS - * commands fom user space for pure driver level operations - * - * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - * @IWL_TM_CMD_APP2DEV_END_TRACE: - * @IWL_TM_CMD_APP2DEV_READ_TRACE: - * commands fom user space for uCode trace operations - * - * @IWL_TM_CMD_DEV2APP_SYNC_RSP: - * commands from kernel space to carry the synchronous response - * to user application - * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT: - * commands from kernel space to multicast the spontaneous messages - * to user application, or reply of host commands - * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: - * commands from kernel space to carry the eeprom response - * to user application - * - * @IWL_TM_CMD_APP2DEV_OWNERSHIP: - * commands from user application to own change the ownership of the uCode - * if application has the ownership, the only host command from - * testmode will deliver to uCode. Default owner is driver - * - * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image - * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version - * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device - * @IWL_TM_CMD_APP2DEV_GET_FW_INFO: - * retrieve information of existing loaded uCode image - * - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - * Commands to read/write data from periphery or SRAM memory ranges. - * Fore reading, a READ command is sent from the userspace and the data - * is returned when the user calls a DUMP command. - * For writing, only a WRITE command is used. - * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - * Command to enable/disable notifications (currently RX packets) from the - * driver to userspace. - */ -enum iwl_tm_cmd_t { - IWL_TM_CMD_APP2DEV_UCODE = 1, - IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, - IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, - IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, - IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, - IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, - IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, - IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8, - IWL_TM_CMD_APP2DEV_GET_EEPROM = 9, - IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10, - IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11, - IWL_TM_CMD_APP2DEV_END_TRACE = 12, - IWL_TM_CMD_APP2DEV_READ_TRACE = 13, - IWL_TM_CMD_DEV2APP_SYNC_RSP = 14, - IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, - IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, - IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, - RESERVED_18 = 18, - RESERVED_19 = 19, - RESERVED_20 = 20, - RESERVED_21 = 21, - IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, - IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, - IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, - IWL_TM_CMD_APP2DEV_GET_FW_INFO = 25, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28, - IWL_TM_CMD_APP2DEV_NOTIFICATIONS = 29, - IWL_TM_CMD_MAX = 30, -}; - -/* - * Atrribute filed in testmode command - * See enum iwl_tm_cmd_t. - * - * @IWL_TM_ATTR_NOT_APPLICABLE: - * The attribute is not applicable or invalid - * @IWL_TM_ATTR_COMMAND: - * From user space to kernel space: - * the command either destines to ucode, driver, or register; - * From kernel space to user space: - * the command either carries synchronous response, - * or the spontaneous message multicast from the device; - * - * @IWL_TM_ATTR_UCODE_CMD_ID: - * @IWL_TM_ATTR_UCODE_CMD_DATA: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE, - * The mandatory fields are : - * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID; - * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload - * to the ucode - * - * @IWL_TM_ATTR_REG_OFFSET: - * @IWL_TM_ATTR_REG_VALUE8: - * @IWL_TM_ATTR_REG_VALUE32: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX, - * The mandatory fields are: - * IWL_TM_ATTR_REG_OFFSET for the offset of the target register; - * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value - * - * @IWL_TM_ATTR_SYNC_RSP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP, - * The mandatory fields are: - * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user - * application command - * - * @IWL_TM_ATTR_UCODE_RX_PKT: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT, - * The mandatory fields are: - * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user - * application - * - * @IWL_TM_ATTR_EEPROM: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM, - * The mandatory fields are: - * IWL_TM_ATTR_EEPROM for the data content responging to the user - * application - * - * @IWL_TM_ATTR_TRACE_ADDR: - * @IWL_TM_ATTR_TRACE_SIZE: - * @IWL_TM_ATTR_TRACE_DUMP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE, - * The mandatory fields are: - * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address - * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size - * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump - * - * @IWL_TM_ATTR_FIXRATE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ, - * The mandatory fields are: - * IWL_TM_ATTR_FIXRATE for the fixed rate - * - * @IWL_TM_ATTR_UCODE_OWNER: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, - * The mandatory fields are: - * IWL_TM_ATTR_UCODE_OWNER for the new owner - * - * @IWL_TM_ATTR_MEM_ADDR: - * @IWL_TM_ATTR_BUFFER_SIZE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ - * or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE. - * The mandatory fields are: - * IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write - * IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write. - * - * @IWL_TM_ATTR_BUFFER_DUMP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP, - * IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read. - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE, - * this attribute contains the data to write. - * - * @IWL_TM_ATTR_FW_VERSION: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, - * IWL_TM_ATTR_FW_VERSION for the uCode version - * - * @IWL_TM_ATTR_DEVICE_ID: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, - * IWL_TM_ATTR_DEVICE_ID for the device ID information - * - * @IWL_TM_ATTR_FW_TYPE: - * @IWL_TM_ATTR_FW_INST_SIZE: - * @IWL_TM_ATTR_FW_DATA_SIZE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO, - * The mandatory fields are: - * IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...) - * IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section - * IWL_TM_ATTR_FW_DATA_SIZE for the size of data section - * - * @IWL_TM_ATTR_UCODE_CMD_SKB: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag - * indicates that the user wants to receive the response of the command - * in a reply SKB. If it's not present, the response is not returned. - * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this - * flag enables (if present) or disables (if not) the forwarding - * to userspace. - */ -enum iwl_tm_attr_t { - IWL_TM_ATTR_NOT_APPLICABLE = 0, - IWL_TM_ATTR_COMMAND = 1, - IWL_TM_ATTR_UCODE_CMD_ID = 2, - IWL_TM_ATTR_UCODE_CMD_DATA = 3, - IWL_TM_ATTR_REG_OFFSET = 4, - IWL_TM_ATTR_REG_VALUE8 = 5, - IWL_TM_ATTR_REG_VALUE32 = 6, - IWL_TM_ATTR_SYNC_RSP = 7, - IWL_TM_ATTR_UCODE_RX_PKT = 8, - IWL_TM_ATTR_EEPROM = 9, - IWL_TM_ATTR_TRACE_ADDR = 10, - IWL_TM_ATTR_TRACE_SIZE = 11, - IWL_TM_ATTR_TRACE_DUMP = 12, - IWL_TM_ATTR_FIXRATE = 13, - IWL_TM_ATTR_UCODE_OWNER = 14, - IWL_TM_ATTR_MEM_ADDR = 15, - IWL_TM_ATTR_BUFFER_SIZE = 16, - IWL_TM_ATTR_BUFFER_DUMP = 17, - IWL_TM_ATTR_FW_VERSION = 18, - IWL_TM_ATTR_DEVICE_ID = 19, - IWL_TM_ATTR_FW_TYPE = 20, - IWL_TM_ATTR_FW_INST_SIZE = 21, - IWL_TM_ATTR_FW_DATA_SIZE = 22, - IWL_TM_ATTR_UCODE_CMD_SKB = 23, - IWL_TM_ATTR_ENABLE_NOTIFICATION = 24, - IWL_TM_ATTR_MAX = 25, -}; - -/* uCode trace buffer */ -#define TRACE_BUFF_SIZE_MAX 0x200000 -#define TRACE_BUFF_SIZE_MIN 0x20000 -#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN -#define TRACE_BUFF_PADD 0x2000 - -/* Maximum data size of each dump it packet */ -#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) - -/* Address offset of data segment in SRAM */ -#define SRAM_DATA_SEG_OFFSET 0x800000 - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 7a13790b5bf..8d91422c598 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -183,13 +183,12 @@ struct iwl_rx_packet { * @CMD_ASYNC: Return right away and don't want for the response * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the * response. The caller needs to call iwl_free_resp when done. - * @CMD_ON_DEMAND: This command is sent by the test mode pipe. */ enum CMD_MODE { CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), - CMD_ON_DEMAND = BIT(2), + CMD_SEND_IN_RFKILL = BIT(2), }; #define DEF_CMD_PAYLOAD_SIZE 320 @@ -427,8 +426,9 @@ struct iwl_trans_ops { void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans); - void (*d3_suspend)(struct iwl_trans *trans); - int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status); + void (*d3_suspend)(struct iwl_trans *trans, bool test); + int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status, + bool test); int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); @@ -455,7 +455,7 @@ struct iwl_trans_ops { int (*read_mem)(struct iwl_trans *trans, u32 addr, void *buf, int dwords); int (*write_mem)(struct iwl_trans *trans, u32 addr, - void *buf, int dwords); + const void *buf, int dwords); void (*configure)(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg); void (*set_pmi)(struct iwl_trans *trans, bool state); @@ -587,17 +587,18 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans) trans->state = IWL_TRANS_NO_FW; } -static inline void iwl_trans_d3_suspend(struct iwl_trans *trans) +static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test) { might_sleep(); - trans->ops->d3_suspend(trans); + trans->ops->d3_suspend(trans, test); } static inline int iwl_trans_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status) + enum iwl_d3_status *status, + bool test) { might_sleep(); - return trans->ops->d3_resume(trans, status); + return trans->ops->d3_resume(trans, status, test); } static inline int iwl_trans_send_cmd(struct iwl_trans *trans, @@ -761,7 +762,7 @@ static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr) } static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) + const void *buf, int dwords) { return trans->ops->write_mem(trans, addr, buf, dwords); } diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 2acc44b4098..ff856e543ae 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o bt-coex.o -iwlmvm-y += led.o +iwlmvm-y += led.o tt.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index 810bfa5f6de..dbd622a3929 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -174,7 +174,7 @@ static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = { static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xcc00ff28), cpu_to_le32(0x0000aaaa), @@ -202,6 +202,22 @@ static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = { cpu_to_le32(0x00000000), }; +/* single shared antenna */ +static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xC0004000), + cpu_to_le32(0xF0005000), + cpu_to_le32(0xC0004000), + cpu_to_le32(0xF0005000), +}; + int iwl_send_bt_init_conf(struct iwl_mvm *mvm) { struct iwl_bt_coex_cmd cmd = { @@ -225,7 +241,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) BT_VALID_REDUCED_TX_POWER | BT_VALID_LUT); - if (is_loose_coex()) + if (mvm->cfg->bt_shared_single_ant) + memcpy(&cmd.decision_lut, iwl_single_shared_ant_lookup, + sizeof(iwl_single_shared_ant_lookup)); + else if (is_loose_coex()) memcpy(&cmd.decision_lut, iwl_loose_lookup, sizeof(iwl_tight_lookup)); else @@ -351,6 +370,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, enum ieee80211_band band; int ave_rssi; + lockdep_assert_held(&mvm->mutex); if (vif->type != NL80211_IFTYPE_STATION) return; @@ -365,7 +385,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = IEEE80211_SMPS_AUTOMATIC; if (band != IEEE80211_BAND_2GHZ) { - ieee80211_request_smps(vif, smps_mode); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); return; } @@ -380,7 +401,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, mvmvif->id, data->notif->bt_status, data->notif->bt_traffic_load, smps_mode); - ieee80211_request_smps(vif, smps_mode); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); /* don't reduce the Tx power if in loose scheme */ if (is_loose_coex()) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 16bbdcc8627..7e5e5c2f9f8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -63,6 +63,7 @@ #include <linux/etherdevice.h> #include <linux/ip.h> +#include <linux/fs.h> #include <net/cfg80211.h> #include <net/ipv6.h> #include <net/tcp.h> @@ -419,8 +420,7 @@ static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr) return cpu_to_le16(be16_to_cpu((__force __be16)check)); } -static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, +static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif, struct cfg80211_wowlan_tcp *tcp, void *_pkt, u8 *mask, __le16 *pseudo_hdr_csum, @@ -566,21 +566,21 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, /* SYN (TX) */ iwl_mvm_build_tcp_packet( - mvm, vif, tcp, cfg->syn_tx.data, NULL, + vif, tcp, cfg->syn_tx.data, NULL, &cfg->syn_tx.info.tcp_pseudo_header_checksum, MVM_TCP_TX_SYN); cfg->syn_tx.info.tcp_payload_length = 0; /* SYN/ACK (RX) */ iwl_mvm_build_tcp_packet( - mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask, + vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask, &cfg->synack_rx.info.tcp_pseudo_header_checksum, MVM_TCP_RX_SYNACK); cfg->synack_rx.info.tcp_payload_length = 0; /* KEEPALIVE/ACK (TX) */ iwl_mvm_build_tcp_packet( - mvm, vif, tcp, cfg->keepalive_tx.data, NULL, + vif, tcp, cfg->keepalive_tx.data, NULL, &cfg->keepalive_tx.info.tcp_pseudo_header_checksum, MVM_TCP_TX_DATA); cfg->keepalive_tx.info.tcp_payload_length = @@ -604,7 +604,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, /* ACK (RX) */ iwl_mvm_build_tcp_packet( - mvm, vif, tcp, cfg->keepalive_ack_rx.data, + vif, tcp, cfg->keepalive_ack_rx.data, cfg->keepalive_ack_rx.rx_mask, &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum, MVM_TCP_RX_ACK); @@ -612,7 +612,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, /* WAKEUP (RX) */ iwl_mvm_build_tcp_packet( - mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask, + vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask, &cfg->wake_rx.info.tcp_pseudo_header_checksum, MVM_TCP_RX_WAKE); cfg->wake_rx.info.tcp_payload_length = @@ -620,7 +620,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, /* FIN */ iwl_mvm_build_tcp_packet( - mvm, vif, tcp, cfg->fin_tx.data, NULL, + vif, tcp, cfg->fin_tx.data, NULL, &cfg->fin_tx.info.tcp_pseudo_header_checksum, MVM_TCP_TX_FIN); cfg->fin_tx.info.tcp_payload_length = 0; @@ -756,7 +756,9 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return 0; } -int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +static int __iwl_mvm_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan, + bool test) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_d3_iter_data suspend_iter_data = { @@ -769,7 +771,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; - struct iwl_d3_manager_config d3_cfg_cmd = { + struct iwl_d3_manager_config d3_cfg_cmd_data = { /* * Program the minimum sleep time to 10 seconds, as many * platforms have issues processing a wakeup signal while @@ -777,17 +779,30 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) */ .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), }; + struct iwl_host_cmd d3_cfg_cmd = { + .id = D3_CONFIG_CMD, + .flags = CMD_SYNC | CMD_WANT_SKB, + .data[0] = &d3_cfg_cmd_data, + .len[0] = sizeof(d3_cfg_cmd_data), + }; struct wowlan_key_data key_data = { .use_rsc_tsc = false, .tkip = &tkip_cmd, .use_tkip = false, }; int ret, i; + int len __maybe_unused; u16 seq; u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; - if (WARN_ON(!wowlan)) + if (!wowlan) { + /* + * mac80211 shouldn't get here, but for D3 test + * it doesn't warrant a warning + */ + WARN_ON(!test); return -EINVAL; + } key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); if (!key_data.rsc_tsc) @@ -1007,15 +1022,37 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto out; + ret = iwl_mvm_power_update_mode(mvm, vif); + if (ret) + goto out; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->d3_wake_sysassert) + d3_cfg_cmd_data.wakeup_flags |= + cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR); +#endif + /* must be last -- this switches firmware state */ - ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, - sizeof(d3_cfg_cmd), &d3_cfg_cmd); + ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); if (ret) goto out; +#ifdef CONFIG_IWLWIFI_DEBUGFS + len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) & + FH_RSCSR_FRAME_SIZE_MSK; + if (len >= sizeof(u32) * 2) { + mvm->d3_test_pme_ptr = + le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); + } else if (test) { + /* in test mode we require the pointer */ + ret = -EIO; + goto out; + } +#endif + iwl_free_resp(&d3_cfg_cmd); clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_trans_d3_suspend(mvm->trans); + iwl_trans_d3_suspend(mvm->trans, test); out: mvm->aux_sta.sta_id = old_aux_sta_id; mvm_ap_sta->sta_id = old_ap_sta_id; @@ -1030,6 +1067,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return ret; } +int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + return __iwl_mvm_suspend(hw, wowlan, false); +} + static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -1214,9 +1256,28 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, iwl_free_resp(&cmd); } -int iwl_mvm_resume(struct ieee80211_hw *hw) +static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) +{ +#ifdef CONFIG_IWLWIFI_DEBUGFS + const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + u32 len = img->sec[IWL_UCODE_SECTION_DATA].len; + u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset; + + if (!mvm->store_d3_resume_sram) + return; + + if (!mvm->d3_resume_sram) { + mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL); + if (!mvm->d3_resume_sram) + return; + } + + iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len); +#endif +} + +static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_d3_iter_data resume_iter_data = { .mvm = mvm, }; @@ -1236,7 +1297,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) vif = resume_iter_data.vif; - ret = iwl_trans_d3_resume(mvm->trans, &d3_status); + ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test); if (ret) goto out_unlock; @@ -1245,12 +1306,15 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) goto out_unlock; } + /* query SRAM first in case we want event logging */ + iwl_mvm_read_d3_sram(mvm); + iwl_mvm_query_wakeup_reasons(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); - if (vif) + if (!test && vif) ieee80211_resume_disconnect(vif); /* return 1 to reconfigure the device */ @@ -1258,9 +1322,106 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) return 1; } +int iwl_mvm_resume(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + return __iwl_mvm_resume(mvm, false); +} + void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); device_set_wakeup_enable(mvm->trans->dev, enabled); } + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int err; + + if (mvm->d3_test_active) + return -EBUSY; + + file->private_data = inode->i_private; + + ieee80211_stop_queues(mvm->hw); + synchronize_net(); + + /* start pseudo D3 */ + rtnl_lock(); + err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); + rtnl_unlock(); + if (err > 0) + err = -EINVAL; + if (err) { + ieee80211_wake_queues(mvm->hw); + return err; + } + mvm->d3_test_active = true; + return 0; +} + +static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + u32 pme_asserted; + + while (true) { + pme_asserted = iwl_trans_read_mem32(mvm->trans, + mvm->d3_test_pme_ptr); + if (pme_asserted) + break; + if (msleep_interruptible(100)) + break; + } + + return 0; +} + +static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_connection_loss(vif); +} + +static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int remaining_time = 10; + + mvm->d3_test_active = false; + __iwl_mvm_resume(mvm, true); + iwl_abort_notification_waits(&mvm->notif_wait); + ieee80211_restart_hw(mvm->hw); + + /* wait for restart and disconnect all interfaces */ + while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + remaining_time > 0) { + remaining_time--; + msleep(1000); + } + + if (remaining_time == 0) + IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n"); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d3_test_disconn_work_iter, NULL); + + ieee80211_wake_queues(mvm->hw); + + return 0; +} + +const struct file_operations iwl_dbgfs_d3_test_ops = { + .llseek = no_llseek, + .open = iwl_mvm_d3_test_open, + .read = iwl_mvm_d3_test_read, + .release = iwl_mvm_d3_test_release, +}; +#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 2053dccefcd..e56ed2a8488 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -145,15 +145,18 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, char *buf; u8 *ptr; + if (!mvm->ucode_loaded) + return -EINVAL; + /* default is to dump the entire data segment */ if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { - mvm->dbgfs_sram_offset = 0x800000; - if (!mvm->ucode_loaded) - return -EINVAL; img = &mvm->fw->img[mvm->cur_ucode]; - mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + ofs = mvm->dbgfs_sram_offset; + len = mvm->dbgfs_sram_len; } - len = mvm->dbgfs_sram_len; bufsz = len * 4 + 256; buf = kzalloc(bufsz, GFP_KERNEL); @@ -167,12 +170,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, } pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len); - pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - mvm->dbgfs_sram_offset); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs); - iwl_trans_read_mem_bytes(mvm->trans, - mvm->dbgfs_sram_offset, - ptr, len); + iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); for (ofs = 0; ofs < len; ofs += 16) { pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, @@ -300,6 +300,168 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file, return count; } +static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_dbgfs_pm_mask param, int val) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; + + dbgfs_pm->mask |= param; + + switch (param) { + case MVM_DEBUGFS_PM_KEEP_ALIVE: { + struct ieee80211_hw *hw = mvm->hw; + int dtimper = hw->conf.ps_dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); + if (val * MSEC_PER_SEC < 3 * dtimper_msec) { + IWL_WARN(mvm, + "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", + val * MSEC_PER_SEC, 3 * dtimper_msec); + } + dbgfs_pm->keep_alive_seconds = val; + break; + } + case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: + IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", + val ? "enabled" : "disabled"); + dbgfs_pm->skip_over_dtim = val; + break; + case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: + IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); + dbgfs_pm->skip_dtim_periods = val; + break; + case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); + dbgfs_pm->rx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); + dbgfs_pm->tx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: + IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); + dbgfs_pm->disable_power_off = val; + case MVM_DEBUGFS_PM_LPRX_ENA: + IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); + dbgfs_pm->lprx_ena = val; + break; + case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: + IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); + dbgfs_pm->lprx_rssi_threshold = val; + break; + } +} + +static ssize_t iwl_dbgfs_pm_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + enum iwl_dbgfs_pm_mask param; + char buf[32] = {}; + int val; + int ret; + + if (copy_from_user(buf, user_buf, sizeof(buf))) + return -EFAULT; + + if (!strncmp("keep_alive=", buf, 11)) { + if (sscanf(buf + 11, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_KEEP_ALIVE; + } else if (!strncmp("skip_over_dtim=", buf, 15)) { + if (sscanf(buf + 15, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; + } else if (!strncmp("skip_dtim_periods=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; + } else if (!strncmp("rx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; + } else if (!strncmp("tx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; + } else if (!strncmp("disable_power_off=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; + } else if (!strncmp("lprx=", buf, 5)) { + if (sscanf(buf + 5, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_ENA; + } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { + if (sscanf(buf + 20, "%d", &val) != 1) + return -EINVAL; + if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < + POWER_LPRX_RSSI_THRESHOLD_MIN) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_pm(mvm, vif, param, val); + ret = iwl_mvm_power_update_mode(mvm, vif); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_pm_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + struct iwl_powertable_cmd cmd = {}; + char buf[256]; + int bufsz = sizeof(buf); + int pos = 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? + 0 : 1); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + le32_to_cpu(cmd.skip_dtim_periods)); + pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", + iwlmvm_mod_params.power_scheme); + pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", + le16_to_cpu(cmd.flags)); + pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", + cmd.keep_alive_seconds); + + if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? + 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); + if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + pos += scnprintf(buf+pos, bufsz-pos, + "lprx_rssi_threshold = %d\n", + le32_to_cpu(cmd.lprx_rssi_threshold)); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + static ssize_t iwl_dbgfs_mac_params_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -481,6 +643,255 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, return count; } +static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, + enum iwl_dbgfs_bf_mask param, int value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + dbgfs_bf->mask |= param; + + switch (param) { + case MVM_DEBUGFS_BF_ENERGY_DELTA: + dbgfs_bf->bf_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: + dbgfs_bf->bf_roaming_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_STATE: + dbgfs_bf->bf_roaming_state = value; + break; + case MVM_DEBUGFS_BF_TEMPERATURE_DELTA: + dbgfs_bf->bf_temperature_delta = value; + break; + case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: + dbgfs_bf->bf_enable_beacon_filter = value; + break; + case MVM_DEBUGFS_BF_DEBUG_FLAG: + dbgfs_bf->bf_debug_flag = value; + break; + case MVM_DEBUGFS_BF_ESCAPE_TIMER: + dbgfs_bf->bf_escape_timer = value; + break; + case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: + dbgfs_bf->ba_enable_beacon_abort = value; + break; + case MVM_DEBUGFS_BA_ESCAPE_TIMER: + dbgfs_bf->ba_escape_timer = value; + break; + } +} + +static ssize_t iwl_dbgfs_bf_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + enum iwl_dbgfs_bf_mask param; + char buf[256]; + int buf_size; + int value; + int ret = 0; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (!strncmp("bf_energy_delta=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ENERGY_DELTA_MIN || + value > IWL_BF_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || + value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_state=", buf, 17)) { + if (sscanf(buf+17, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_STATE_MIN || + value > IWL_BF_ROAMING_STATE_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_STATE; + } else if (!strncmp("bf_temperature_delta=", buf, 21)) { + if (sscanf(buf+21, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMPERATURE_DELTA_MIN || + value > IWL_BF_TEMPERATURE_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA; + } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; + } else if (!strncmp("bf_debug_flag=", buf, 14)) { + if (sscanf(buf+14, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_DEBUG_FLAG; + } else if (!strncmp("bf_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ESCAPE_TIMER_MIN || + value > IWL_BF_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ESCAPE_TIMER; + } else if (!strncmp("ba_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BA_ESCAPE_TIMER_MIN || + value > IWL_BA_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BA_ESCAPE_TIMER; + } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { + if (sscanf(buf+23, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_bf(vif, param, value); + if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) { + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + } else { + if (mvmvif->bf_enabled) + ret = iwl_mvm_enable_beacon_filter(mvm, vif); + else + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + } + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_bf_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_beacon_filter_cmd cmd = { + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, + .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT, + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT), + .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT, + }; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + if (mvmvif->bf_enabled) + cmd.bf_enable_beacon_filter = 1; + else + cmd.bf_enable_beacon_filter = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", + cmd.bf_energy_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", + cmd.bf_roaming_energy_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", + cmd.bf_roaming_state); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n", + cmd.bf_temperature_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", + cmd.bf_enable_beacon_filter); + pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", + cmd.bf_debug_flag); + pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", + cmd.bf_escape_timer); + pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", + cmd.ba_escape_timer); + pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", + cmd.ba_enable_beacon_abort); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +#ifdef CONFIG_PM_SLEEP +static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[8] = {}; + int store; + + if (copy_from_user(buf, user_buf, sizeof(buf))) + return -EFAULT; + + if (sscanf(buf, "%d", &store) != 1) + return -EINVAL; + + mvm->store_d3_resume_sram = store; + + return count; +} + +static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + const struct fw_img *img; + int ofs, len, pos = 0; + size_t bufsz, ret; + char *buf; + u8 *ptr = mvm->d3_resume_sram; + + img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + bufsz = len * 4 + 256; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n", + mvm->store_d3_resume_sram ? "en" : "dis"); + + if (ptr) { + for (ofs = 0; ofs < len; ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, + bufsz - pos, false); + pos += strlen(buf + pos); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "(no data captured)\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + + kfree(buf); + + return ret; +} +#endif + #define MVM_DEBUGFS_READ_FILE_OPS(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ @@ -524,9 +935,14 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); +#ifdef CONFIG_PM_SLEEP +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); +#endif /* Interface specific debugfs entries */ MVM_DEBUGFS_READ_FILE_OPS(mac_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params); int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) { @@ -542,6 +958,13 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); +#ifdef CONFIG_PM_SLEEP + MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); + if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR, + mvm->debugfs_dir, &mvm->d3_wake_sysassert)) + goto err; +#endif /* * Create a symlink with mac80211. It will be removed when mac80211 @@ -577,9 +1000,19 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return; } + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p) + MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | + S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvmvif == mvm->bf_allowed_vif) + MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + /* * Create symlink for convenience pointing to interface specific * debugfs entries for the driver. For example, under diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 51e015d1dfb..6f8b2c16ae1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -75,13 +75,15 @@ enum iwl_d3_wakeup_flags { * struct iwl_d3_manager_config - D3 manager configuration command * @min_sleep_time: minimum sleep time (in usec) * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags + * @wakeup_host_timer: force wakeup after this many seconds * * The structure is used for the D3_CONFIG_CMD command. */ struct iwl_d3_manager_config { __le32 min_sleep_time; __le32 wakeup_flags; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */ + __le32 wakeup_host_timer; +} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ /* TODO: OFFLOADS_QUERY_API_S_VER_1 */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h index d68640ea41d..98b1feb43d3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h @@ -71,7 +71,13 @@ #define MAC_INDEX_MIN_DRIVER 0 #define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX -#define AC_NUM 4 /* Number of access categories */ +enum iwl_ac { + AC_BK, + AC_BE, + AC_VI, + AC_VO, + AC_NUM, +}; /** * enum iwl_mac_protection_flags - MAC context flags diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 81fe45f46be..a6da359a80c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -66,6 +66,11 @@ /* Power Management Commands, Responses, Notifications */ +/* Radio LP RX Energy Threshold measured in dBm */ +#define POWER_LPRX_RSSI_THRESHOLD 75 +#define POWER_LPRX_RSSI_THRESHOLD_MAX 94 +#define POWER_LPRX_RSSI_THRESHOLD_MIN 30 + /** * enum iwl_scan_flags - masks for power table command flags * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off @@ -101,20 +106,107 @@ enum iwl_power_flags { * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to * PSM transition - legacy PM * @sleep_interval: not in use - * @keep_alive_beacons: not in use + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. * Default: 80dbm */ struct iwl_powertable_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_5 */ + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ __le16 flags; u8 keep_alive_seconds; u8 debug_flags; __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 keep_alive_beacons; + __le32 skip_dtim_periods; __le32 lprx_rssi_threshold; } __packed; +/** + * struct iwl_beacon_filter_cmd + * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) + * @id_and_color: MAC contex identifier + * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon + * to driver if delta in Energy values calculated for this and last + * passed beacon is greater than this threshold. Zero value means that + * the Energy change is ignored for beacon filtering, and beacon will + * not be forced to be sent to driver regardless of this delta. Typical + * energy delta 5dB. + * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. + * Send beacon to driver if delta in Energy values calculated for this + * and last passed beacon is greater than this threshold. Zero value + * means that the Energy change is ignored for beacon filtering while in + * Roaming state, typical energy delta 1dB. + * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values + * calculated for current beacon is less than the threshold, use + * Roaming Energy Delta Threshold, otherwise use normal Energy Delta + * Threshold. Typical energy threshold is -72dBm. + * @bf_temperature_delta: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature changeis ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. + * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed + * for a specific period of time. Units: Beacons. + * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed + * for a longer period of time then this escape-timeout. Units: Beacons. + * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. + */ +struct iwl_beacon_filter_cmd { + u8 bf_energy_delta; + u8 bf_roaming_energy_delta; + u8 bf_roaming_state; + u8 bf_temperature_delta; + u8 bf_enable_beacon_filter; + u8 bf_debug_flag; + __le16 reserved1; + __le32 bf_escape_timer; + __le32 ba_escape_timer; + u8 ba_enable_beacon_abort; + u8 reserved2[3]; +} __packed; + +/* Beacon filtering and beacon abort */ +#define IWL_BF_ENERGY_DELTA_DEFAULT 5 +#define IWL_BF_ENERGY_DELTA_MAX 255 +#define IWL_BF_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 +#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 +#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_STATE_DEFAULT 72 +#define IWL_BF_ROAMING_STATE_MAX 255 +#define IWL_BF_ROAMING_STATE_MIN 0 + +#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5 +#define IWL_BF_TEMPERATURE_DELTA_MAX 255 +#define IWL_BF_TEMPERATURE_DELTA_MIN 0 + +#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 + +#define IWL_BF_DEBUG_FLAG_DEFAULT 0 + +#define IWL_BF_ESCAPE_TIMER_DEFAULT 50 +#define IWL_BF_ESCAPE_TIMER_MAX 1024 +#define IWL_BF_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ESCAPE_TIMER_DEFAULT 3 +#define IWL_BA_ESCAPE_TIMER_MAX 1024 +#define IWL_BA_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 + +#define IWL_BF_CMD_CONFIG_DEFAULTS \ + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, \ + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \ + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, \ + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, \ + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \ + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT) + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 007a93b25bd..700cce73177 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -134,6 +134,7 @@ enum iwl_tx_flags { #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x07 #define TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 #define TX_CMD_SEC_KEY128 0x08 @@ -227,10 +228,11 @@ struct iwl_tx_cmd { __le16 len; __le16 next_frame_len; __le32 tx_flags; - /* DRAM_SCRATCH_API_U_VER_1 */ - u8 try_cnt; - u8 btkill_cnt; - __le16 reserved; + struct { + u8 try_cnt; + u8 btkill_cnt; + __le16 reserved; + } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ __le32 rate_n_flags; u8 sta_id; u8 sec_ctl; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index c6384555aab..cbfb3beae78 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -139,6 +139,9 @@ enum { /* Power */ POWER_TABLE_CMD = 0x77, + /* Thermal Throttling*/ + REPLY_THERMAL_MNG_BACKOFF = 0x7e, + /* Scanning */ SCAN_REQUEST_CMD = 0x80, SCAN_ABORT_CMD = 0x81, @@ -161,6 +164,8 @@ enum { CARD_STATE_CMD = 0xa0, CARD_STATE_NOTIFICATION = 0xa1, + MISSED_BEACONS_NOTIFICATION = 0xa2, + REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, @@ -170,6 +175,8 @@ enum { BT_COEX_PROT_ENV = 0xcd, BT_PROFILE_NOTIFICATION = 0xce, + REPLY_BEACON_FILTERING_CMD = 0xd2, + REPLY_DEBUG_CMD = 0xf0, DEBUG_LOG_MSG = 0xf7, @@ -938,6 +945,24 @@ struct iwl_card_state_notif { } __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ /** + * struct iwl_missed_beacons_notif - information on missed beacons + * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) + * @mac_id: interface ID + * @consec_missed_beacons_since_last_rx: number of consecutive missed + * beacons since last RX. + * @consec_missed_beacons: number of consecutive missed beacons + * @num_expected_beacons: + * @num_recvd_beacons: + */ +struct iwl_missed_beacons_notif { + __le32 mac_id; + __le32 consec_missed_beacons_since_last_rx; + __le32 consec_missed_beacons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ + +/** * struct iwl_set_calib_default_cmd - set default value for calibration. * ( SET_CALIB_DEFAULT_CMD = 0x8e ) * @calib_index: the calibration to set value for @@ -975,4 +1000,212 @@ struct iwl_mcast_filter_cmd { u8 addr_list[0]; } __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ +struct mvm_statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ + +struct mvm_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 rssi_ant; + __le32 reserved2; +} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ + +struct mvm_statistics_general_common { + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* radio voltage */ + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; + __le32 directed_data_mpdu; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ + +#define MAX_CHAINS 3 + +struct mvm_statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; + __s8 txpower[MAX_CHAINS]; + __s8 reserved; + __le32 reserved2; +} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ + +struct mvm_statistics_tx_channel_width { + __le32 ext_cca_narrow_ch20[1]; + __le32 ext_cca_narrow_ch40[2]; + __le32 ext_cca_narrow_ch80[3]; + __le32 ext_cca_narrow_ch160[4]; + __le32 last_tx_ch_width_indx; + __le32 rx_detected_per_ch_width[4]; + __le32 success_per_ch_width[4]; + __le32 fail_per_ch_width[4]; +}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ + +struct mvm_statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_4 */ + + +struct mvm_statistics_bt_activity { + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ + +struct mvm_statistics_general { + struct mvm_statistics_general_common common; + __le32 beacon_filtered; + __le32 missed_beacons; + __s8 beacon_filter_everage_energy; + __s8 beacon_filter_reason; + __s8 beacon_filter_current_energy; + __s8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx { + struct mvm_statistics_rx_phy ofdm; + struct mvm_statistics_rx_phy cck; + struct mvm_statistics_rx_non_phy general; + struct mvm_statistics_rx_ht_phy ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_3 */ + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ + +struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */ + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general general; +} __packed; + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index e18c92dd60e..cd7c0032cc5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -326,6 +326,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); WARN_ON(ret); + /* + * abort after reading the nvm in case RF Kill is on, we will complete + * the init seq later when RF kill will switch to off + */ + if (iwl_mvm_is_radio_killed(mvm)) { + IWL_DEBUG_RF_KILL(mvm, + "jump over all phy activities due to RF kill\n"); + iwl_remove_notification(&mvm->notif_wait, &calib_wait); + return 1; + } + /* Send TX valid antennas before triggering calibrations */ ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); if (ret) @@ -388,6 +399,8 @@ out: int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; + struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); @@ -400,8 +413,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); + /* this can't happen */ + if (WARN_ON(ret > 0)) + ret = -ERFKILL; goto error; } + /* should stop & start HW since that INIT image just loaded */ + iwl_trans_stop_hw(mvm->trans, false); + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; } if (iwlmvm_mod_params.init_dbg) @@ -443,8 +464,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; - IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); + /* Add all the PHY contexts */ + chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + for (i = 0; i < NUM_PHY_CTX; i++) { + /* + * The channel used here isn't relevant as it's + * going to be overwritten in the other flows. + * For now use the first channel we have. + */ + ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], + &chandef, 1, 1); + if (ret) + goto error; + } + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: iwl_trans_stop_device(mvm->trans); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index b2cc3d98e0f..94aae9c8562 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -193,14 +193,11 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - u32 qmask, ac; + u32 qmask = 0, ac; if (vif->type == NL80211_IFTYPE_P2P_DEVICE) return BIT(IWL_MVM_OFFCHANNEL_QUEUE); - qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ? - BIT(vif->cab_queue) : 0; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) qmask |= BIT(vif->hw_queue[ac]); @@ -227,7 +224,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, .found_vif = false, }; u32 ac; - int ret; + int ret, i; /* * Allocate a MAC ID and a TSF for this MAC, along with the queues @@ -335,6 +332,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) + mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; + return 0; exit_fail: @@ -362,7 +362,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) break; case NL80211_IFTYPE_AP: iwl_trans_ac_txq_enable(mvm->trans, vif->cab_queue, - IWL_MVM_TX_FIFO_VO); + IWL_MVM_TX_FIFO_MCAST); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) @@ -550,6 +550,10 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]); } + /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ + if (vif->type == NL80211_IFTYPE_AP) + cmd->ac[AC_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); + if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); @@ -861,6 +865,30 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, return ret; } +struct iwl_mvm_mac_ap_iterator_data { + struct iwl_mvm *mvm; + struct ieee80211_vif *vif; + u32 beacon_device_ts; + u16 beacon_int; +}; + +/* Find the beacon_device_ts and beacon_int for a managed interface */ +static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mac_ap_iterator_data *data = _data; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) + return; + + /* Station client has higher priority over P2P client*/ + if (vif->p2p && data->beacon_device_ts) + return; + + data->beacon_device_ts = vif->bss_conf.sync_device_ts; + data->beacon_int = vif->bss_conf.beacon_int; +} + /* * Fill the specific data for mac context of type AP of P2P GO */ @@ -870,6 +898,11 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, bool add) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_mac_ap_iterator_data data = { + .mvm = mvm, + .vif = vif, + .beacon_device_ts = 0 + }; ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_ap->bi_reciprocal = @@ -883,16 +916,33 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); /* - * Only read the system time when the MAC is being added, when we + * Only set the beacon time when the MAC is being added, when we * just modify the MAC then we should keep the time -- the firmware * can otherwise have a "jumping" TBTT. */ - if (add) - mvmvif->ap_beacon_time = - iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); + if (add) { + /* + * If there is a station/P2P client interface which is + * associated, set the AP's TBTT far enough from the station's + * TBTT. Otherwise, set it to the current system time + */ + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_mac_ap_iterator, &data); + + if (data.beacon_device_ts) { + u32 rand = (prandom_u32() % (80 - 20)) + 20; + mvmvif->ap_beacon_time = data.beacon_device_ts + + ieee80211_tu_to_usec(data.beacon_int * rand / + 100); + } else { + mvmvif->ap_beacon_time = + iwl_read_prph(mvm->trans, + DEVICE_SYSTEM_TIME_REG); + } + } ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time); - ctxt_ap->beacon_tsf = 0; /* unused */ /* TODO: Assume that the beacon id == mac context id */ @@ -1047,3 +1097,28 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, rate); return 0; } + +static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u16 *id = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (mvmvif->id == *id) + ieee80211_beacon_loss(vif); +} + +int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data; + u16 id = (u16)le32_to_cpu(missed_beacons->mac_id); + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_beacon_loss_iterator, + &id); + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index a5eb8c82f16..e08683b2053 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -81,12 +81,12 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { .max = 1, - .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP), + .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), }, { @@ -127,6 +127,17 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { }; #endif +static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) +{ + int i; + + memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts)); + for (i = 0; i < NUM_PHY_CTX; i++) { + mvm->phy_ctxts[i].id = i; + mvm->phy_ctxts[i].ref = 0; + } +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -141,7 +152,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_TIMING_BEACON_ONLY; + IEEE80211_HW_TIMING_BEACON_ONLY | + IEEE80211_HW_CONNECTION_MONITOR; hw->queues = IWL_MVM_FIRST_AGG_QUEUE; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; @@ -158,7 +170,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); - hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt); + hw->chanctx_data_size = sizeof(u16); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) | @@ -193,6 +205,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->n_addresses++; } + iwl_mvm_reset_phy_ctxts(mvm); + /* we create the 802.11 header and a max-length SSID element */ hw->wiphy->max_scan_ie_len = mvm->fw->ucode_capa.max_probe_length - 24 - 34; @@ -222,20 +236,20 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) mvm->trans->ops->d3_suspend && mvm->trans->ops->d3_resume && device_can_wakeup(mvm->trans->dev)) { - hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE; + mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE; if (!iwlwifi_mod_params.sw_crypto) - hw->wiphy->wowlan.flags |= - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_GTK_REKEY_FAILURE | - WIPHY_WOWLAN_4WAY_HANDSHAKE; - - hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; - hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; - hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; - hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support; + mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_4WAY_HANDSHAKE; + + mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; + mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; + mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; + mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support; + hw->wiphy->wowlan = &mvm->wowlan; } #endif @@ -252,8 +266,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { - IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n"); + if (iwl_mvm_is_radio_killed(mvm)) { + IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); goto drop; } @@ -345,8 +359,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); spin_unlock_bh(&mvm->time_event_lock); - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvmvif->phy_ctxt = NULL; + mvmvif->phy_ctxt = NULL; } static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) @@ -363,6 +376,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_cleanup_iterator, mvm); + mvm->p2p_device_vif = NULL; + + iwl_mvm_reset_phy_ctxts(mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); @@ -456,6 +472,20 @@ static void iwl_mvm_power_update_iterator(void *data, u8 *mac, iwl_mvm_power_update_mode(mvm, vif); } +static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) +{ + u16 i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < NUM_PHY_CTX; i++) + if (!mvm->phy_ctxts[i].ref) + return &mvm->phy_ctxts[i]; + + IWL_ERR(mvm, "No available PHY context\n"); + return NULL; +} + static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -530,32 +560,34 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ iwl_mvm_power_update_mode(mvm, vif); + /* beacon filtering */ + if (!mvm->bf_allowed_vif && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p){ + mvm->bf_allowed_vif = mvmvif; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + } + + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + if (ret) + goto out_release; + /* * P2P_DEVICE interface does not have a channel context assigned to it, * so a dedicated PHY context is allocated to it and the corresponding * MAC context is bound to it at this stage. */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - struct ieee80211_channel *chan; - struct cfg80211_chan_def chandef; - mvmvif->phy_ctxt = &mvm->phy_ctxt_roc; - - /* - * The channel used here isn't relevant as it's - * going to be overwritten as part of the ROC flow. - * For now use the first channel we have. - */ - chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, - &chandef, 1, 1); - if (ret) + mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!mvmvif->phy_ctxt) { + ret = -ENOSPC; goto out_remove_mac; + } + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) - goto out_remove_phy; + goto out_unref_phy; ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) @@ -571,27 +603,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); - out_remove_phy: - iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); + out_unref_phy: + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); out_remove_mac: mvmvif->phy_ctxt = NULL; iwl_mvm_mac_ctxt_remove(mvm, vif); out_release: - /* - * TODO: remove this temporary code. - * Currently MVM FW supports power management only on single MAC. - * Check if only one additional interface remains after releasing - * current one. Update power mode on the remaining interface. - */ if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; - IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count == 1) { - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_update_iterator, mvm); - } + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_update_iterator, mvm); iwl_mvm_mac_ctxt_release(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); @@ -629,8 +651,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, * By now, all the AC queues are empty. The AGG queues are * empty too. We already got all the Tx responses for all the * packets in the queues. The drain work can have been - * triggered. Flush it. This work item takes the mutex, so kill - * it before we take it. + * triggered. Flush it. */ flush_work(&mvm->sta_drained_wk); } @@ -646,6 +667,11 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + } + iwl_mvm_vif_dbgfs_clean(mvm, vif); /* @@ -661,7 +687,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->p2p_device_vif = NULL; iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); mvmvif->phy_ctxt = NULL; } @@ -748,7 +774,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (ret) IWL_ERR(mvm, "failed to update quotas\n"); } - } else if (changes & BSS_CHANGED_DTIM_PERIOD) { + ret = iwl_mvm_power_update_mode(mvm, vif); + if (ret) + IWL_ERR(mvm, "failed to update power mode\n"); + } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so * remove the session protection. @@ -756,19 +785,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); } else if (changes & BSS_CHANGED_PS) { - /* - * TODO: remove this temporary code. - * Currently MVM FW supports power management only on single - * MAC. Avoid power mode update if more than one interface - * is active. - */ - IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count == 1) { - ret = iwl_mvm_power_update_mode(mvm, vif); - if (ret) - IWL_ERR(mvm, "failed to update power mode\n"); - } + ret = iwl_mvm_power_update_mode(mvm, vif); + if (ret) + IWL_ERR(mvm, "failed to update power mode\n"); } } @@ -999,9 +1018,13 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, mvmvif->phy_ctxt->channel->band); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { + /* enable beacon filtering */ + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { + /* disable beacon filtering */ + WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif)); ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { @@ -1167,29 +1190,107 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, enum ieee80211_roc_type type) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct cfg80211_chan_def chandef; - int ret; + struct iwl_mvm_phy_ctxt *phy_ctxt; + int ret, i; + + IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, + duration, type); if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type); return -EINVAL; } - IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, - duration, type); - mutex_lock(&mvm->mutex); + for (i = 0; i < NUM_PHY_CTX; i++) { + phy_ctxt = &mvm->phy_ctxts[i]; + if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) + continue; + + if (phy_ctxt->ref && channel == phy_ctxt->channel) { + /* + * Unbind the P2P_DEVICE from the current PHY context, + * and if the PHY context is not used remove it. + */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the current PHY Context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + goto schedule_time_event; + } + } + + /* Need to update the PHY context only if the ROC channel changed */ + if (channel == mvmvif->phy_ctxt->channel) + goto schedule_time_event; + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc, - &chandef, 1, 1); + /* + * Change the PHY context configuration as it is currently referenced + * only by the P2P Device MAC + */ + if (mvmvif->phy_ctxt->ref == 1) { + ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, + &chandef, 1, 1); + if (ret) + goto out_unlock; + } else { + /* + * The PHY context is shared with other MACs. Need to remove the + * P2P Device from the binding, allocate an new PHY context and + * create a new binding + */ + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out_unlock; + } + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, + 1, 1); + if (ret) { + IWL_ERR(mvm, "Failed to change PHY context\n"); + goto out_unlock; + } + + /* Unbind the P2P_DEVICE from the current PHY context */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the new allocated PHY context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + } + +schedule_time_event: /* Schedule the time events */ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); +out_unlock: mutex_unlock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "leave\n"); - return ret; } @@ -1211,15 +1312,30 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt; int ret; + IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); + mutex_lock(&mvm->mutex); + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out; + } - IWL_DEBUG_MAC80211(mvm, "Add PHY context\n"); - ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + ctx->rx_chains_static, + ctx->rx_chains_dynamic); + if (ret) { + IWL_ERR(mvm, "Failed to add PHY context\n"); + goto out; + } + + iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); + *phy_ctxt_id = phy_ctxt->id; +out: mutex_unlock(&mvm->mutex); return ret; } @@ -1228,10 +1344,11 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); mutex_unlock(&mvm->mutex); } @@ -1240,7 +1357,16 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, u32 changed) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + + if (WARN_ONCE((phy_ctxt->ref > 1) && + (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | + IEEE80211_CHANCTX_CHANGE_RX_CHAINS | + IEEE80211_CHANCTX_CHANGE_RADAR)), + "Cannot change PHY. Ref=%d, changed=0x%X\n", + phy_ctxt->ref, changed)) + return; mutex_lock(&mvm->mutex); iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, @@ -1254,13 +1380,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; mutex_lock(&mvm->mutex); - mvmvif->phy_ctxt = phyctx; + mvmvif->phy_ctxt = phy_ctxt; switch (vif->type) { case NL80211_IFTYPE_AP: diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 9f46b23801b..d40d7db185d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -73,7 +73,6 @@ #include "iwl-trans.h" #include "iwl-notif-wait.h" #include "iwl-eeprom-parse.h" -#include "iwl-test.h" #include "iwl-trans.h" #include "sta.h" #include "fw-api.h" @@ -88,6 +87,7 @@ enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_BE, IWL_MVM_TX_FIFO_VI, IWL_MVM_TX_FIFO_VO, + IWL_MVM_TX_FIFO_MCAST = 5, }; extern struct ieee80211_ops iwl_mvm_hw_ops; @@ -109,6 +109,7 @@ extern struct iwl_mvm_mod_params iwlmvm_mod_params; struct iwl_mvm_phy_ctxt { u16 id; u16 color; + u32 ref; /* * TODO: This should probably be removed. Currently here only for rate @@ -149,6 +150,64 @@ enum iwl_power_scheme { #define IWL_CONN_MAX_LISTEN_INTERVAL 70 +#ifdef CONFIG_IWLWIFI_DEBUGFS +enum iwl_dbgfs_pm_mask { + MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), + MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1), + MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), + MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), + MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), + MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5), + MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), + MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), +}; + +struct iwl_dbgfs_pm { + u8 keep_alive_seconds; + u32 rx_data_timeout; + u32 tx_data_timeout; + bool skip_over_dtim; + u8 skip_dtim_periods; + bool disable_power_off; + bool lprx_ena; + u32 lprx_rssi_threshold; + int mask; +}; + +/* beacon filtering */ + +enum iwl_dbgfs_bf_mask { + MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), + MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), + MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), + MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3), + MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4), + MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5), + MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6), + MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7), + MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8), +}; + +struct iwl_dbgfs_bf { + u8 bf_energy_delta; + u8 bf_roaming_energy_delta; + u8 bf_roaming_state; + u8 bf_temperature_delta; + u8 bf_enable_beacon_filter; + u8 bf_debug_flag; + u32 bf_escape_timer; + u32 ba_escape_timer; + u8 ba_enable_beacon_abort; + int mask; +}; +#endif + +enum iwl_mvm_smps_type_request { + IWL_MVM_SMPS_REQ_BT_COEX, + IWL_MVM_SMPS_REQ_TT, + NUM_IWL_MVM_SMPS_REQ, +}; + /** * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context * @id: between 0 and 3 @@ -163,6 +222,8 @@ enum iwl_power_scheme { * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. * @beacon_skb: the skb used to hold the AP/GO beacon template + * @smps_requests: the requests of of differents parts of the driver, regard + the desired smps mode. */ struct iwl_mvm_vif { u16 id; @@ -172,6 +233,8 @@ struct iwl_mvm_vif { bool uploaded; bool ap_active; bool monitor_active; + /* indicate whether beacon filtering is enabled */ + bool bf_enabled; u32 ap_beacon_time; @@ -214,7 +277,11 @@ struct iwl_mvm_vif { struct dentry *dbgfs_dir; struct dentry *dbgfs_slink; void *dbgfs_data; + struct iwl_dbgfs_pm dbgfs_pm; + struct iwl_dbgfs_bf dbgfs_bf; #endif + + enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; }; static inline struct iwl_mvm_vif * @@ -223,12 +290,6 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) return (void *)vif->drv_priv; } -enum iwl_mvm_status { - IWL_MVM_STATUS_HW_RFKILL, - IWL_MVM_STATUS_ROC_RUNNING, - IWL_MVM_STATUS_IN_HW_RESTART, -}; - enum iwl_scan_status { IWL_MVM_SCAN_NONE, IWL_MVM_SCAN_OS, @@ -246,6 +307,65 @@ struct iwl_nvm_section { const u8 *data; }; +/* + * Tx-backoff threshold + * @temperature: The threshold in Celsius + * @backoff: The tx-backoff in uSec + */ +struct iwl_tt_tx_backoff { + s32 temperature; + u32 backoff; +}; + +#define TT_TX_BACKOFF_SIZE 6 + +/** + * struct iwl_tt_params - thermal throttling parameters + * @ct_kill_entry: CT Kill entry threshold + * @ct_kill_exit: CT Kill exit threshold + * @ct_kill_duration: The time intervals (in uSec) in which the driver needs + * to checks whether to exit CT Kill. + * @dynamic_smps_entry: Dynamic SMPS entry threshold + * @dynamic_smps_exit: Dynamic SMPS exit threshold + * @tx_protection_entry: TX protection entry threshold + * @tx_protection_exit: TX protection exit threshold + * @tx_backoff: Array of thresholds for tx-backoff , in ascending order. + * @support_ct_kill: Support CT Kill? + * @support_dynamic_smps: Support dynamic SMPS? + * @support_tx_protection: Support tx protection? + * @support_tx_backoff: Support tx-backoff? + */ +struct iwl_tt_params { + s32 ct_kill_entry; + s32 ct_kill_exit; + u32 ct_kill_duration; + s32 dynamic_smps_entry; + s32 dynamic_smps_exit; + s32 tx_protection_entry; + s32 tx_protection_exit; + struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE]; + bool support_ct_kill; + bool support_dynamic_smps; + bool support_tx_protection; + bool support_tx_backoff; +}; + +/** + * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure + * @ct_kill_exit: worker to exit thermal kill + * @dynamic_smps: Is thermal throttling enabled dynamic_smps? + * @tx_backoff: The current thremal throttling tx backoff in uSec. + * @params: Parameters to configure the thermal throttling algorithm. + * @throttle: Is thermal throttling is active? + */ +struct iwl_mvm_tt_mgmt { + struct delayed_work ct_kill_exit; + bool dynamic_smps; + u32 tx_backoff; + const struct iwl_tt_params *params; + bool throttle; +}; + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -266,6 +386,12 @@ struct iwl_mvm { unsigned long status; + /* + * for beacon filtering - + * currently only one interface can be supported + */ + struct iwl_mvm_vif *bf_allowed_vif; + enum iwl_ucode_type cur_ucode; bool ucode_loaded; bool init_ucode_run; @@ -313,7 +439,7 @@ struct iwl_mvm { bool prevent_power_down_d3; #endif - struct iwl_mvm_phy_ctxt phy_ctxt_roc; + struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; struct list_head time_event_list; spinlock_t time_event_lock; @@ -337,12 +463,24 @@ struct iwl_mvm { struct ieee80211_vif *p2p_device_vif; #ifdef CONFIG_PM_SLEEP + struct wiphy_wowlan_support wowlan; int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; +#ifdef CONFIG_IWLWIFI_DEBUGFS + u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */ + bool d3_test_active; + bool store_d3_resume_sram; + void *d3_resume_sram; + u32 d3_test_pme_ptr; +#endif #endif /* BT-Coex */ u8 bt_kill_msk; struct iwl_bt_coex_profile_notif last_bt_notif; + + /* Thermal Throttling and CTkill */ + struct iwl_mvm_tt_mgmt thermal_throttle; + s32 temperature; /* Celsius */ }; /* Extract MVM priv from op_mode and _hw */ @@ -352,6 +490,19 @@ struct iwl_mvm { #define IWL_MAC80211_GET_MVM(_hw) \ IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) +enum iwl_mvm_status { + IWL_MVM_STATUS_HW_RFKILL, + IWL_MVM_STATUS_HW_CTKILL, + IWL_MVM_STATUS_ROC_RUNNING, + IWL_MVM_STATUS_IN_HW_RESTART, +}; + +static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) +{ + return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) || + test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); +} + extern const u8 iwl_mvm_ac_to_tx_fifo[]; struct iwl_rate_info { @@ -443,8 +594,10 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic); -void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); /* MAC (virtual interface) programming */ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -459,6 +612,9 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -523,6 +679,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, struct inet6_dev *idev); void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); +extern const struct file_operations iwl_dbgfs_d3_test_ops; /* BT Coex */ int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); @@ -534,4 +691,31 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +/* beacon filtering */ +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd); +#else +static inline void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{} +#endif +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); + +/* SMPS */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request); + +/* Thermal management and CT-kill */ +void iwl_mvm_tt_handler(struct iwl_mvm *mvm); +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm); +void iwl_mvm_tt_exit(struct iwl_mvm *mvm); +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index b8ec02f89ac..edb94ea3165 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -60,6 +60,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +#include <linux/firmware.h> #include "iwl-trans.h" #include "mvm.h" #include "iwl-eeprom-parse.h" @@ -75,31 +76,56 @@ static const int nvm_to_read[] = { }; /* Default NVM size to read */ -#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024); +#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWL_MAX_NVM_SECTION_SIZE 6000 -static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd, - u16 offset, u16 length, u16 section) +#define NVM_WRITE_OPCODE 1 +#define NVM_READ_OPCODE 0 + +/* + * prepare the NVM host command w/ the pointers to the nvm buffer + * and send it to fw + */ +static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, + u16 offset, u16 length, const u8 *data) { - cmd->offset = cpu_to_le16(offset); - cmd->length = cpu_to_le16(length); - cmd->type = cpu_to_le16(section); + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_WRITE_OPCODE, + }; + struct iwl_host_cmd cmd = { + .id = NVM_ACCESS_CMD, + .len = { sizeof(struct iwl_nvm_access_cmd), length }, + .flags = CMD_SYNC | CMD_SEND_IN_RFKILL, + .data = { &nvm_access_cmd, data }, + /* data may come from vmalloc, so use _DUP */ + .dataflags = { 0, IWL_HCMD_DFL_DUP }, + }; + + return iwl_mvm_send_cmd(mvm, &cmd); } static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, u16 offset, u16 length, u8 *data) { - struct iwl_nvm_access_cmd nvm_access_cmd = {}; + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_READ_OPCODE, + }; struct iwl_nvm_access_resp *nvm_resp; struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; int ret, bytes_read, offset_read; u8 *resp_data; - iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section); cmd.len[0] = sizeof(struct iwl_nvm_access_cmd); ret = iwl_mvm_send_cmd(mvm, &cmd); @@ -144,6 +170,30 @@ exit: return ret; } +static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, + const u8 *data, u16 length) +{ + int offset = 0; + + /* copy data in chunks of 2k (and remainder if any) */ + + while (offset < length) { + int chunk_size, ret; + + chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE, + length - offset); + + ret = iwl_nvm_write_chunk(mvm, section, offset, + chunk_size, data + offset); + if (ret < 0) + return ret; + + offset += chunk_size; + } + + return 0; +} + /* * Reads an NVM section completely. * NICs prior to 7000 family doesn't have a real NVM, but just read @@ -177,7 +227,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, offset += ret; } - IWL_INFO(mvm, "NVM section %d read completed\n", section); + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM section %d read completed\n", section); return offset; } @@ -200,7 +251,130 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; - return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib); + return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, + iwl_fw_valid_tx_ant(mvm->fw), + iwl_fw_valid_rx_ant(mvm->fw)); +} + +#define MAX_NVM_FILE_LEN 16384 + +/* + * HOW TO CREATE THE NVM FILE FORMAT: + * ------------------------------ + * 1. create hex file, format: + * 3800 -> header + * 0000 -> header + * 5a40 -> data + * + * rev - 6 bit (word1) + * len - 10 bit (word1) + * id - 4 bit (word2) + * rsv - 12 bit (word2) + * + * 2. flip 8bits with 8 bits per line to get the right NVM file format + * + * 3. create binary file from the hex file + * + * 4. save as "iNVM_xxx.bin" under /lib/firmware + */ +static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) +{ + int ret, section_id, section_size; + const struct firmware *fw_entry; + const struct { + __le16 word1; + __le16 word2; + u8 data[]; + } *file_sec; + const u8 *eof; + +#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) +#define NVM_WORD2_ID(x) (x >> 12) + + /* + * Obtain NVM image via request_firmware. Since we already used + * request_firmware_nowait() for the firmware binary load and only + * get here after that we assume the NVM request can be satisfied + * synchronously. + */ + ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, + mvm->trans->dev); + if (ret) { + IWL_ERR(mvm, "ERROR: %s isn't available %d\n", + iwlwifi_mod_params.nvm_file, ret); + return ret; + } + + IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", + iwlwifi_mod_params.nvm_file, fw_entry->size); + + if (fw_entry->size < sizeof(*file_sec)) { + IWL_ERR(mvm, "NVM file too small\n"); + ret = -EINVAL; + goto out; + } + + if (fw_entry->size > MAX_NVM_FILE_LEN) { + IWL_ERR(mvm, "NVM file too large\n"); + ret = -EINVAL; + goto out; + } + + eof = fw_entry->data + fw_entry->size; + + file_sec = (void *)fw_entry->data; + + while (true) { + if (file_sec->data > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section header\n"); + ret = -EINVAL; + break; + } + + /* check for EOF marker */ + if (!file_sec->word1 && !file_sec->word2) { + ret = 0; + break; + } + + section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); + section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + + if (section_size > IWL_MAX_NVM_SECTION_SIZE) { + IWL_ERR(mvm, "ERROR - section too large (%d)\n", + section_size); + ret = -EINVAL; + break; + } + + if (!section_size) { + IWL_ERR(mvm, "ERROR - section empty\n"); + ret = -EINVAL; + break; + } + + if (file_sec->data + section_size > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section (%d bytes)\n", + section_size); + ret = -EINVAL; + break; + } + + ret = iwl_nvm_write_section(mvm, section_id, file_sec->data, + section_size); + if (ret < 0) { + IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + break; + } + + /* advance to the next section */ + file_sec = (void *)(file_sec->data + section_size); + } +out: + release_firmware(fw_entry); + return ret; } int iwl_nvm_init(struct iwl_mvm *mvm) @@ -208,6 +382,17 @@ int iwl_nvm_init(struct iwl_mvm *mvm) int ret, i, section; u8 *nvm_buffer, *temp; + /* load external NVM if configured */ + if (iwlwifi_mod_params.nvm_file) { + /* move to External NVM flow */ + ret = iwl_mvm_load_external_nvm(mvm); + if (ret) + return ret; + } + + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + /* TODO: find correct NVM max size for a section */ nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, GFP_KERNEL); @@ -231,8 +416,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm) if (ret < 0) return ret; - ret = 0; mvm->nvm_data = iwl_parse_nvm_sections(mvm); + if (!mvm->nvm_data) + return -ENODATA; - return ret; + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index b29c31a4159..af79a14063a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -215,17 +215,22 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false), RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false), RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), + + RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), + RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), + RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), + RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), - RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), - RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), - RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), + RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, + false), + RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), }; #undef RX_HANDLER @@ -288,11 +293,14 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(NET_DETECT_HOTSPOTS_CMD), CMD(NET_DETECT_HOTSPOTS_QUERY_CMD), CMD(CARD_STATE_NOTIFICATION), + CMD(MISSED_BEACONS_NOTIFICATION), CMD(BT_COEX_PRIO_TABLE), CMD(BT_COEX_PROT_ENV), CMD(BT_PROFILE_NOTIFICATION), CMD(BT_CONFIG), CMD(MCAST_FILTER_CMD), + CMD(REPLY_BEACON_FILTERING_CMD), + CMD(REPLY_THERMAL_MNG_BACKOFF), }; #undef CMD @@ -393,10 +401,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_free; + iwl_mvm_tt_initialize(mvm); + mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); mutex_unlock(&mvm->mutex); - if (err && !iwlmvm_mod_params.init_dbg) { + /* returns 0 if successful, 1 if success but in rfkill */ + if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } @@ -439,10 +450,16 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) iwl_mvm_leds_exit(mvm); + iwl_mvm_tt_exit(mvm); + ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) + kfree(mvm->d3_resume_sram); +#endif + iwl_trans_stop_hw(mvm->trans, true); iwl_phy_db_free(mvm->phy_db); @@ -589,6 +606,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) ieee80211_wake_queue(mvm->hw, mq); } +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) +{ + if (state) + set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + else + clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); +} + static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -598,7 +625,7 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) else clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state); + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); } static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index a28a1d1f23e..a8652ddd6be 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -195,21 +195,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, return ret; } - -struct phy_ctx_used_data { - unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)]; -}; - -static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx, - void *_data) -{ - struct phy_ctx_used_data *data = _data; - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; - - __set_bit(phy_ctxt->id, data->used); -} - /* * Send a command to add a PHY context based on the current HW configuration. */ @@ -217,34 +202,28 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { - struct phy_ctx_used_data data = { - .used = { }, - }; - - /* - * If this is a regular PHY context (not the ROC one) - * skip the ROC PHY context's ID. - */ - if (ctxt != &mvm->phy_ctxt_roc) - __set_bit(mvm->phy_ctxt_roc.id, data.used); + int ret; + WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + ctxt->ref); lockdep_assert_held(&mvm->mutex); - ctxt->color++; - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - ieee80211_iter_chan_contexts_atomic( - mvm->hw, iwl_mvm_phy_ctx_used_iter, &data); + ctxt->channel = chandef->chan; + ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD, 0); - ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX); - if (WARN_ONCE(ctxt->id == NUM_PHY_CTX, - "Failed to init PHY context - no free ID!\n")) - return -EIO; - } + return ret; +} - ctxt->channel = chandef->chan; - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); +/* + * Update the number of references to the given PHY context. This is valid only + * in case the PHY context was already created, i.e., its reference count > 0. + */ +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +{ + lockdep_assert_held(&mvm->mutex); + ctxt->ref++; } /* @@ -264,23 +243,12 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, FW_CTXT_ACTION_MODIFY, 0); } -/* - * Send a command to the FW to remove the given phy context. - * Once the command is sent, regardless of success or failure, the context is - * marked as invalid - */ -void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { - struct iwl_phy_context_cmd cmd; - int ret; - lockdep_assert_held(&mvm->mutex); - iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0); - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC, - sizeof(struct iwl_phy_context_cmd), - &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n", - ctxt->id); + if (WARN_ON_ONCE(!ctxt)) + return; + + ctxt->ref--; } diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index ed77e437aac..e7ca965a89b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -75,6 +75,54 @@ #define POWER_KEEP_ALIVE_PERIOD_SEC 25 +static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd) +{ + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC, + sizeof(struct iwl_beacon_filter_cmd), cmd); + + if (!ret) { + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", + cmd->ba_enable_beacon_abort); + IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", + cmd->ba_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", + cmd->bf_debug_flag); + IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", + cmd->bf_enable_beacon_filter); + IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", + cmd->bf_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", + cmd->bf_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", + cmd->bf_roaming_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", + cmd->bf_roaming_state); + IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n", + cmd->bf_temperature_delta); + } + return ret; +} + +static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = 1, + .ba_enable_beacon_abort = enable, + }; + + if (!mvmvif->bf_enabled) + return 0; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); +} + static void iwl_mvm_power_log(struct iwl_mvm *mvm, struct iwl_powertable_cmd *cmd) { @@ -89,8 +137,12 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, le32_to_cpu(cmd->rx_data_timeout)); IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", le32_to_cpu(cmd->tx_data_timeout)); - IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", - cmd->lprx_rssi_threshold); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) + IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", + le32_to_cpu(cmd->skip_dtim_periods)); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", + le32_to_cpu(cmd->lprx_rssi_threshold)); } } @@ -103,6 +155,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int dtimper, dtimper_msec; int keep_alive; bool radar_detect = false; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); /* * Regardless of power management state the driver must set @@ -115,12 +169,27 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + if (!vif->bss_conf.assoc) + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif if (!vif->bss_conf.ps) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + if (vif->bss_conf.beacon_rate && + (vif->bss_conf.beacon_rate->bitrate == 10 || + vif->bss_conf.beacon_rate->bitrate == 60)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + cmd->lprx_rssi_threshold = + cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD); + } + dtimper = hw->conf.ps_dtim_period ?: 1; /* Check if radar detection is required on current channel */ @@ -135,8 +204,11 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* Check skip over DTIM conditions */ if (!radar_detect && (dtimper <= 10) && - (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) + (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || + mvm->cur_ucode == IWL_UCODE_WOWLAN)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + cmd->skip_dtim_periods = cpu_to_le32(3); + } /* Check that keep alive period is at least 3 * DTIM */ dtimper_msec = dtimper * vif->bss_conf.beacon_int; @@ -145,27 +217,85 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); cmd->keep_alive_seconds = keep_alive; - cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { + cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + } else { + cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + } + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) + cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { + if (mvmvif->dbgfs_pm.skip_over_dtim) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) + cmd->rx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) + cmd->tx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) + cmd->skip_dtim_periods = + cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { + if (mvmvif->dbgfs_pm.lprx_ena) + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + else + cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) + cmd->lprx_rssi_threshold = + cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold); +#endif /* CONFIG_IWLWIFI_DEBUGFS */ } int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + int ret; + bool ba_enable; struct iwl_powertable_cmd cmd = {}; if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; + /* + * TODO: The following vif_count verification is temporary condition. + * Avoid power mode update if more than one interface is currently + * active. Remove this condition when FW will support power management + * on multiple MACs. + */ + IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n", + mvm->vif_count); + if (mvm->vif_count > 1) + return 0; + iwl_mvm_power_build_cmd(mvm, vif, &cmd); iwl_mvm_power_log(mvm, &cmd); - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, + sizeof(cmd), &cmd); + if (ret) + return ret; + + ba_enable = !!(cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); + + return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); } int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_powertable_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; @@ -173,8 +303,82 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif iwl_mvm_power_log(mvm, &cmd); return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, sizeof(cmd), &cmd); } + +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) + cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) + cmd->bf_roaming_energy_delta = + dbgfs_bf->bf_roaming_energy_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) + cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA) + cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) + cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) + cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) + cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) + cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort; +} +#endif + +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = 1, + }; + int ret; + + if (mvmvif != mvm->bf_allowed_vif || + vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + + if (!ret) + mvmvif->bf_enabled = true; + + return ret; +} + +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_beacon_filter_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + + if (!ret) + mvmvif->bf_enabled = false; + + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index a1e3e923ea3..29d49cf0fdb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -169,27 +169,34 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) num_active_bindings++; } - if (!num_active_bindings) - goto send_cmd; - - quota = IWL_MVM_MAX_QUOTA / num_active_bindings; - quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings; + quota = 0; + quota_rem = 0; + if (num_active_bindings) { + quota = IWL_MVM_MAX_QUOTA / num_active_bindings; + quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings; + } for (idx = 0, i = 0; i < MAX_BINDINGS; i++) { - if (data.n_interfaces[i] <= 0) + if (data.colors[i] < 0) continue; cmd.quotas[idx].id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i])); - cmd.quotas[idx].quota = cpu_to_le32(quota); - cmd.quotas[idx].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); + + if (data.n_interfaces[i] <= 0) { + cmd.quotas[idx].quota = cpu_to_le32(0); + cmd.quotas[idx].max_duration = cpu_to_le32(0); + } else { + cmd.quotas[idx].quota = cpu_to_le32(quota); + cmd.quotas[idx].max_duration = + cpu_to_le32(IWL_MVM_MAX_QUOTA); + } idx++; } /* Give the remainder of the session to the first binding */ le32_add_cpu(&cmd.quotas[0].quota, quota_rem); -send_cmd: ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC, sizeof(cmd), &cmd); if (ret) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index b99fe316386..b328a988c13 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -401,24 +401,29 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, load = rs_tl_get_load(lq_data, tid); - if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) { - IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", - sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } else { - IWL_DEBUG_HT(mvm, - "Aggregation not enabled for tid %d because load = %u\n", - tid, load); + /* + * Don't create TX aggregation sessions when in high + * BT traffic, as they would just be disrupted by BT. + */ + if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) { + IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n", + BT_MBOX_MSG(&mvm->last_bt_notif, + 3, TRAFFIC_LOAD)); + return ret; + } + + IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", + sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); } return ret; } @@ -1519,6 +1524,29 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, u8 update_search_tbl_counter = 0; int ret; + switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) + tbl->action = IWL_SISO_SWITCH_MIMO2_AB; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + valid_tx_ant = + first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); + if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + break; + default: + IWL_ERR(mvm, "Invalid BT load %d", + BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); + break; + } + start_action = tbl->action; while (1) { lq_sta->action_counter++; @@ -1532,7 +1560,9 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, tx_chains_num <= 2)) break; - if (window->success_ratio >= IWL_RS_GOOD_RATIO) + if (window->success_ratio >= IWL_RS_GOOD_RATIO && + BT_MBOX_MSG(&mvm->last_bt_notif, 3, + TRAFFIC_LOAD) == 0) break; memcpy(search_tbl, tbl, sz); @@ -1654,6 +1684,28 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, u8 update_search_tbl_counter = 0; int ret; + switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || + tbl->action == IWL_MIMO2_SWITCH_SISO_C) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + break; + default: + IWL_ERR(mvm, "Invalid BT load %d", + BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); + break; + } + start_action = tbl->action; while (1) { lq_sta->action_counter++; @@ -1791,6 +1843,28 @@ static int rs_move_mimo3_to_other(struct iwl_mvm *mvm, int ret; u8 update_search_tbl_counter = 0; + switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || + tbl->action == IWL_MIMO3_SWITCH_SISO_C) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + break; + default: + IWL_ERR(mvm, "Invalid BT load %d", + BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); + break; + } + start_action = tbl->action; while (1) { lq_sta->action_counter++; @@ -2302,6 +2376,32 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, (current_tpt > (100 * tbl->expected_tpt[low])))) scale_action = 0; + if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= + IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + if (lq_sta->last_bt_traffic > + BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + /* + * don't set scale_action, don't want to scale up if + * the rate scale doesn't otherwise think that is a + * good idea. + */ + } else if (lq_sta->last_bt_traffic <= + BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + scale_action = -1; + } + } + lq_sta->last_bt_traffic = + BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD); + + if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= + IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + /* search for a new modulation */ + rs_stay_in_table(lq_sta, true); + goto lq_update; + } + switch (scale_action) { case -1: /* Decrease starting rate, update uCode's rate table */ @@ -2783,6 +2883,13 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, lq_cmd->agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + /* + * overwrite if needed, pass aggregation time limit + * to uCode in uSec - This is racy - but heh, at least it helps... + */ + if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) + lq_cmd->agg_time_limit = cpu_to_le16(1200); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) @@ -3081,3 +3188,29 @@ void iwl_mvm_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_mvm_ops); } + +/** + * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable + * Tx protection, according to this rquest and previous requests, + * and send the LQ command. + * @lq: The LQ command + * @mvmsta: The station + * @enable: Enable Tx protection? + */ +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, + struct iwl_mvm_sta *mvmsta, bool enable) +{ + lockdep_assert_held(&mvm->mutex); + + if (enable) { + if (mvmsta->tx_protection == 0) + lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + mvmsta->tx_protection++; + } else { + mvmsta->tx_protection--; + if (mvmsta->tx_protection == 0) + lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK; + } + + return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 219c6857cc0..cff4f6da773 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -358,6 +358,18 @@ struct iwl_lq_sta { u8 last_bt_traffic; }; +enum iwl_bt_coex_profile_traffic_load { + IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, + IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, + IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, + IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, +/* + * There are no more even though below is a u8, the + * indication from the BT device only has two bits. + */ +}; + + static inline u8 num_of_ant(u8 mask) { return !!((mask) & ANT_A) + @@ -390,4 +402,9 @@ extern int iwl_mvm_rate_control_register(void); */ extern void iwl_mvm_rate_control_unregister(void); +struct iwl_mvm_sta; + +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, + struct iwl_mvm_sta *mvmsta, bool enable); + #endif /* __rs__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 4dfc21a3e83..e4930d5027d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -363,3 +363,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, rxb, &rx_status); return 0; } + +/* + * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler + * + * TODO: This handler is implemented partially. + * It only gets the NIC's temperature. + */ +int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_notif_statistics *stats = (void *)&pkt->data; + struct mvm_statistics_general_common *common = &stats->general.common; + + if (mvm->temperature != le32_to_cpu(common->temperature)) { + mvm->temperature = le32_to_cpu(common->temperature); + iwl_mvm_tt_handler(mvm); + } + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 2476e43799d..2157b0f8ced 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -298,12 +298,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, else cmd->type = cpu_to_le32(SCAN_TYPE_FORCED); - /* - * TODO: This is a WA due to a bug in the FW AUX framework that does not - * properly handle time events that fail to be scheduled - */ - cmd->type = cpu_to_le32(SCAN_TYPE_FORCED); - cmd->repeats = cpu_to_le32(1); /* diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 5c664ed5440..62fe5209093 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -64,6 +64,7 @@ #include "mvm.h" #include "sta.h" +#include "rs.h" static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) { @@ -217,6 +218,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvmvif->color); mvm_sta->vif = vif; mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + mvm_sta->tx_protection = 0; + mvm_sta->tt_tx_protection = false; /* HW restart, don't assume the memory has been zeroed */ atomic_set(&mvm->pending_frames[sta_id], 0); @@ -226,9 +229,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) - mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue); - /* for HW restart - need to reset the seq_number etc... */ memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); @@ -798,21 +798,23 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, min(mvmsta->max_agg_bufsize, buf_size); mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; + IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); + if (mvm->cfg->ht_params->use_rts_for_aggregation) { /* * switch to RTS/CTS if it is the prefer protection * method for HT traffic + * this function also sends the LQ command */ - mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, + mvmsta, true); /* * TODO: remove the TLC_RTS flag when we tear down the last * AGG session (agg_tids_count in DVM) */ } - IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); } @@ -1287,17 +1289,11 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, - .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, - .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE), + .station_flags_msk = cpu_to_le32(STA_FLG_PS), .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), }; int ret; - /* - * Same modify mask for sleep_tx_count and sleep_state_flags but this - * should be fine since if we set the STA as "awake", then - * sleep_tx_count is not relevant. - */ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index a4ddce77aaa..94b265eb32b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -250,7 +250,6 @@ enum iwl_mvm_agg_state { * the first packet to be sent in legacy HW queue in Tx AGG stop flow. * Basically when next_reclaimed reaches ssn, we can tell mac80211 that * we are ready to finish the Tx AGG stop / start flow. - * @wait_for_ba: Expect block-ack before next Tx reply */ struct iwl_mvm_tid_data { u16 seq_number; @@ -260,7 +259,6 @@ struct iwl_mvm_tid_data { enum iwl_mvm_agg_state state; u16 txq_id; u16 ssn; - bool wait_for_ba; }; /** @@ -275,6 +273,8 @@ struct iwl_mvm_tid_data { * @lock: lock to protect the whole struct. Since %tid_data is access from Tx * and from Tx response flow, it needs a spinlock. * @tid_data: per tid data. Look at %iwl_mvm_tid_data. + * @tx_protection: reference counter for controlling the Tx protection. + * @tt_tx_protection: is thermal throttling enable Tx protection? * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -296,6 +296,10 @@ struct iwl_mvm_sta { #ifdef CONFIG_PM_SLEEP u16 last_seq_ctl; #endif + + /* Temporary, until the new TLC will control the Tx protection */ + s8 tx_protection; + bool tt_tx_protection; }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c new file mode 100644 index 00000000000..d6ae7f16ac1 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -0,0 +1,530 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include "mvm.h" +#include "iwl-config.h" +#include "iwl-io.h" +#include "iwl-csr.h" +#include "iwl-prph.h" + +#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/ +/* VBG - Voltage Band Gap error data (temperature offset) */ +#define OTP_WP_DTS_VBG (OTP_DTS_DIODE_DEVIATION + 2) +#define MEAS_VBG_MIN_VAL 2300 +#define MEAS_VBG_MAX_VAL 3000 +#define MEAS_VBG_DEFAULT_VAL 2700 +#define DTS_DIODE_VALID(flags) (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE) +#define MIN_TEMPERATURE 0 +#define MAX_TEMPERATURE 125 +#define TEMPERATURE_ERROR (MAX_TEMPERATURE + 1) +#define PTAT_DIGITAL_VALUE_MIN_VALUE 0 +#define PTAT_DIGITAL_VALUE_MAX_VALUE 0xFF +#define DTS_VREFS_NUM 5 +static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags) +{ + return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >> + DTS_DIODE_REG_FLAGS_VREFS_ID_POS; +} + +#define CALC_VREFS_MIN_DIFF 43 +#define CALC_VREFS_MAX_DIFF 51 +#define CALC_LUT_SIZE (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF) +#define CALC_LUT_INDEX_OFFSET CALC_VREFS_MIN_DIFF +#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET 23 + +/* + * @digital_value: The diode's digital-value sampled (temperature/voltage) + * @vref_low: The lower voltage-reference (the vref just below the diode's + * sampled digital-value) + * @vref_high: The higher voltage-reference (the vref just above the diode's + * sampled digital-value) + * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref) + * bits[6:2]: Reserved. + * bits[7:7]: Indicates completion of at least 1 successful sample + * since last DTS reset. + */ +struct iwl_mvm_dts_diode_bits { + u8 digital_value; + u8 vref_low; + u8 vref_high; + u8 flags; +} __packed; + +union dts_diode_results { + u32 reg_value; + struct iwl_mvm_dts_diode_bits bits; +} __packed; + +static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm) +{ + struct iwl_nvm_section calib_sec; + const __le16 *calib; + u16 vbg; + + /* TODO: move parsing to NVM code */ + calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION]; + calib = (__le16 *)calib_sec.data; + + vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]); + + if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL) + vbg = MEAS_VBG_DEFAULT_VAL; + + return vbg; +} + +static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm) +{ + const u8 *calib; + u8 ptat, pa1, pa2, median; + + /* TODO: move parsing to NVM code */ + calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data; + ptat = calib[OTP_DTS_DIODE_DEVIATION]; + pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1]; + pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2]; + + /* get the median: */ + if (ptat > pa1) { + if (ptat > pa2) + median = (pa1 > pa2) ? pa1 : pa2; + else + median = ptat; + } else { + if (pa1 > pa2) + median = (ptat > pa2) ? ptat : pa2; + else + median = pa1; + } + + return ptat - median; +} + +static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value) +{ + /* Calibrate the PTAT digital value, based on PTAT deviation data: */ + s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm); + + if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE) + new_val = PTAT_DIGITAL_VALUE_MAX_VALUE; + else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE) + new_val = PTAT_DIGITAL_VALUE_MIN_VALUE; + + return new_val; +} + +static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm, + union dts_diode_results *avg_ptat) +{ + u8 vrefs_results[DTS_VREFS_NUM]; + u8 low_vref_index = 0, flags; + u32 reg; + + reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG); + memcpy(vrefs_results, ®, sizeof(reg)); + reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG); + vrefs_results[4] = reg & 0xff; + + if (avg_ptat->bits.digital_value < vrefs_results[0] || + avg_ptat->bits.digital_value > vrefs_results[4]) + return false; + + if (avg_ptat->bits.digital_value > vrefs_results[3]) + low_vref_index = 3; + else if (avg_ptat->bits.digital_value > vrefs_results[2]) + low_vref_index = 2; + else if (avg_ptat->bits.digital_value > vrefs_results[1]) + low_vref_index = 1; + + avg_ptat->bits.vref_low = vrefs_results[low_vref_index]; + avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1]; + flags = avg_ptat->bits.flags; + avg_ptat->bits.flags = + (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) | + (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID); + return true; +} + +/* + * return true it the results are valid, and false otherwise. + */ +static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm, + union dts_diode_results *avg_ptat) +{ + u32 reg; + u8 tmp; + + /* fill the diode value and pass_once with avg-reg results */ + reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG); + reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE; + avg_ptat->reg_value = reg; + + /* calibrate the PTAT digital value */ + tmp = avg_ptat->bits.digital_value; + tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp); + avg_ptat->bits.digital_value = tmp; + + /* + * fill vrefs fields, based on the avgVrefs results + * and the diode value + */ + return dts_get_adjacent_vrefs(mvm, avg_ptat) && + DTS_DIODE_VALID(avg_ptat->bits.flags); +} + +static s32 calculate_nic_temperature(union dts_diode_results avg_ptat, + u16 volt_band_gap) +{ + u32 tmp_result; + u8 vrefs_diff; + /* + * For temperature calculation (at the end, shift right by 23) + * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) } + * (D2-D1) == 43 44 45 46 47 48 49 50 51 + */ + static const u16 calc_lut[CALC_LUT_SIZE] = { + 2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828, + }; + + /* + * The diff between the high and low voltage-references is assumed + * to be strictly be in range of [60,68] + */ + vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low; + + if (vrefs_diff < CALC_VREFS_MIN_DIFF || + vrefs_diff > CALC_VREFS_MAX_DIFF) + return TEMPERATURE_ERROR; + + /* calculate the result: */ + tmp_result = + vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9); + tmp_result += avg_ptat.bits.digital_value; + tmp_result -= avg_ptat.bits.vref_high; + + /* multiply by the LUT value (based on the diff) */ + tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET]; + + /* + * Get the BandGap (the voltage refereces source) error data + * (temperature offset) + */ + tmp_result *= volt_band_gap; + + /* + * here, tmp_result value can be up to 32-bits. We want to right-shift + * it *without* sign-extend. + */ + tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET; + + /* + * at this point, tmp_result should be in the range: + * 200 <= tmp_result <= 365 + */ + return (s16)tmp_result - 240; +} + +static s32 check_nic_temperature(struct iwl_mvm *mvm) +{ + u16 volt_band_gap; + union dts_diode_results avg_ptat; + + volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm); + + /* disable DTS */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0); + + /* SV initialization */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1); + iwl_write_prph(mvm->trans, DTSC_CFG_MODE, + DTSC_CFG_MODE_PERIODIC); + + /* wait for results */ + msleep(100); + if (!dts_read_ptat_avg_results(mvm, &avg_ptat)) + return TEMPERATURE_ERROR; + + /* disable DTS */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0); + + return calculate_nic_temperature(avg_ptat, volt_band_gap); +} + +static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) +{ + u32 duration = mvm->thermal_throttle.params->ct_kill_duration; + + IWL_ERR(mvm, "Enter CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, true); + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies_relative(duration * HZ)); +} + +static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) +{ + IWL_ERR(mvm, "Exit CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, false); +} + +static void check_exit_ctkill(struct work_struct *work) +{ + struct iwl_mvm_tt_mgmt *tt; + struct iwl_mvm *mvm; + u32 duration; + s32 temp; + + tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); + mvm = container_of(tt, struct iwl_mvm, thermal_throttle); + + duration = tt->params->ct_kill_duration; + + iwl_trans_start_hw(mvm->trans); + temp = check_nic_temperature(mvm); + iwl_trans_stop_hw(mvm->trans, false); + + if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) { + IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n"); + goto reschedule; + } + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); + + if (temp <= tt->params->ct_kill_exit) { + iwl_mvm_exit_ctkill(mvm); + return; + } + +reschedule: + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies(duration * HZ)); +} + +static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + enum ieee80211_smps_mode smps_mode; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->thermal_throttle.dynamic_smps) + smps_mode = IEEE80211_SMPS_DYNAMIC; + else + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); +} + +static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int i, err; + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + mvmsta = (void *)sta->drv_priv; + if (enable == mvmsta->tt_tx_protection) + continue; + err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, + mvmsta, enable); + if (err) { + IWL_ERR(mvm, "Failed to %s Tx protection\n", + enable ? "enable" : "disable"); + } else { + IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", + enable ? "Enable" : "Disable"); + mvmsta->tt_tx_protection = enable; + } + } +} + +static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_THERMAL_MNG_BACKOFF, + .len = { sizeof(u32), }, + .data = { &backoff, }, + .flags = CMD_SYNC, + }; + + if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { + IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", + backoff); + mvm->thermal_throttle.tx_backoff = backoff; + } else { + IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); + } +} + +void iwl_mvm_tt_handler(struct iwl_mvm *mvm) +{ + const struct iwl_tt_params *params = mvm->thermal_throttle.params; + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + s32 temperature = mvm->temperature; + bool throttle_enable = false; + int i; + u32 tx_backoff; + + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); + + if (params->support_ct_kill && temperature >= params->ct_kill_entry) { + iwl_mvm_enter_ctkill(mvm); + return; + } + + if (params->support_dynamic_smps) { + if (!tt->dynamic_smps && + temperature >= params->dynamic_smps_entry) { + IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); + tt->dynamic_smps = true; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + throttle_enable = true; + } else if (tt->dynamic_smps && + temperature <= params->dynamic_smps_exit) { + IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); + tt->dynamic_smps = false; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + } + } + + if (params->support_tx_protection) { + if (temperature >= params->tx_protection_entry) { + iwl_mvm_tt_tx_protection(mvm, true); + throttle_enable = true; + } else if (temperature <= params->tx_protection_exit) { + iwl_mvm_tt_tx_protection(mvm, false); + } + } + + if (params->support_tx_backoff) { + tx_backoff = 0; + for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { + if (temperature < params->tx_backoff[i].temperature) + break; + tx_backoff = params->tx_backoff[i].backoff; + } + if (tx_backoff != 0) + throttle_enable = true; + if (tt->tx_backoff != tx_backoff) + iwl_mvm_tt_tx_backoff(mvm, tx_backoff); + } + + if (!tt->throttle && throttle_enable) { + IWL_WARN(mvm, + "Due to high temperature thermal throttling initiated\n"); + tt->throttle = true; + } else if (tt->throttle && !tt->dynamic_smps && tt->tx_backoff == 0 && + temperature <= params->tx_protection_exit) { + IWL_WARN(mvm, + "Temperature is back to normal thermal throttling stopped\n"); + tt->throttle = false; + } +} + +static const struct iwl_tt_params iwl7000_tt_params = { + .ct_kill_entry = 118, + .ct_kill_exit = 96, + .ct_kill_duration = 5, + .dynamic_smps_entry = 114, + .dynamic_smps_exit = 110, + .tx_protection_entry = 114, + .tx_protection_exit = 108, + .tx_backoff = { + {.temperature = 112, .backoff = 200}, + {.temperature = 113, .backoff = 600}, + {.temperature = 114, .backoff = 1200}, + {.temperature = 115, .backoff = 2000}, + {.temperature = 116, .backoff = 4000}, + {.temperature = 117, .backoff = 10000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + + IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); + tt->params = &iwl7000_tt_params; + tt->throttle = false; + INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); +} + +void iwl_mvm_tt_exit(struct iwl_mvm *mvm) +{ + cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); + IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 48c1891e3df..f0e96a92740 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -175,7 +175,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, * table is controlled by LINK_QUALITY commands */ - if (ieee80211_is_data(fc)) { + if (ieee80211_is_data(fc) && sta) { tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE); return; @@ -408,7 +408,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, tid, txq_id, seq_number); - /* NOTE: aggregation will need changes here (for txq id) */ if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; @@ -610,8 +609,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, !(info->flags & IEEE80211_TX_STAT_ACK)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - /* W/A FW bug: seq_ctl is wrong when the queue is flushed */ - if (status == TX_STATUS_FAIL_FIFO_FLUSHED) { + /* W/A FW bug: seq_ctl is wrong when the status isn't success */ + if (status != TX_STATUS_SUCCESS) { struct ieee80211_hdr *hdr = (void *)skb->data; seq_ctl = le16_to_cpu(hdr->seq_ctrl); } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 687b34e387a..1e1332839e4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -76,6 +76,11 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) { int ret; +#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) + if (WARN_ON(mvm->d3_test_active)) + return -EIO; +#endif + /* * Synchronous commands from this op-mode must hold * the mutex, this ensures we don't try to send two @@ -125,6 +130,11 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, lockdep_assert_held(&mvm->mutex); +#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) + if (WARN_ON(mvm->d3_test_active)) + return -EIO; +#endif + /* * Only synchronous commands can wait for status, * we use WANT_SKB so the caller can't. @@ -471,3 +481,34 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, return iwl_mvm_send_cmd(mvm, &cmd); } + +/** + * iwl_mvm_update_smps - Get a requst to change the SMPS mode + * @req_type: The part of the driver who call for a change. + * @smps_requests: The request to change the SMPS mode. + * + * Get a requst to change the SMPS mode, + * and change it according to all other requests in the driver. + */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request) +{ + struct iwl_mvm_vif *mvmvif; + enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; + int i; + + lockdep_assert_held(&mvm->mutex); + mvmvif = iwl_mvm_vif_from_mac80211(vif); + mvmvif->smps_requests[req_type] = smps_request; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { + smps_mode = IEEE80211_SMPS_STATIC; + break; + } + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + smps_mode = IEEE80211_SMPS_DYNAMIC; + } + + ieee80211_request_smps(vif, smps_mode); +} diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 8cb53ec2b77..81f3ea5b09a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -78,6 +78,7 @@ /* Hardware specific file defines the PCI IDs table for that hardware module */ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { +#if IS_ENABLED(CONFIG_IWLDVM) {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ @@ -253,13 +254,60 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, +#endif /* CONFIG_IWLDVM */ +#if IS_ENABLED(CONFIG_IWLMVM) /* 7000 Series */ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)}, + +/* 3160 Series */ + {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, +#endif /* CONFIG_IWLMVM */ {0} }; diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 148843e7f34..b654dcdd048 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -217,6 +217,7 @@ struct iwl_pcie_txq_scratch_buf { * @trans_pcie: pointer back to transport (for timer) * @need_update: indicates need to update read/write index * @active: stores if queue is active + * @ampdu: true if this queue is an ampdu queue for an specific RA/TID * * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. @@ -232,6 +233,7 @@ struct iwl_txq { struct iwl_trans_pcie *trans_pcie; u8 need_update; u8 active; + bool ampdu; }; static inline dma_addr_t diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 567e67ad1f6..fd848cd1583 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -110,9 +110,10 @@ /* * iwl_rxq_space - Return number of free slots available in queue. */ -static int iwl_rxq_space(const struct iwl_rxq *q) +static int iwl_rxq_space(const struct iwl_rxq *rxq) { - int s = q->read - q->write; + int s = rxq->read - rxq->write; + if (s <= 0) s += RX_QUEUE_SIZE; /* keep some buffer to not confuse full and empty queue */ @@ -143,21 +144,22 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans) /* * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue */ -static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_rxq *q) +static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_rxq *rxq) { unsigned long flags; u32 reg; - spin_lock_irqsave(&q->lock, flags); + spin_lock_irqsave(&rxq->lock, flags); - if (q->need_update == 0) + if (rxq->need_update == 0) goto exit_unlock; if (trans->cfg->base_params->shadow_reg_enable) { /* shadow register enabled */ /* Device expects a multiple of 8 */ - q->write_actual = (q->write & ~0x7); - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual); + rxq->write_actual = (rxq->write & ~0x7); + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); } else { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -175,22 +177,22 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_rxq *q) goto exit_unlock; } - q->write_actual = (q->write & ~0x7); + rxq->write_actual = (rxq->write & ~0x7); iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, - q->write_actual); + rxq->write_actual); /* Else device is assumed to be awake */ } else { /* Device expects a multiple of 8 */ - q->write_actual = (q->write & ~0x7); + rxq->write_actual = (rxq->write & ~0x7); iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, - q->write_actual); + rxq->write_actual); } } - q->need_update = 0; + rxq->need_update = 0; exit_unlock: - spin_unlock_irqrestore(&q->lock, flags); + spin_unlock_irqrestore(&rxq->lock, flags); } /* @@ -355,19 +357,16 @@ static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans) struct iwl_rxq *rxq = &trans_pcie->rxq; int i; - /* Fill the rx_used queue with _all_ of the Rx buffers */ + lockdep_assert_held(&rxq->lock); + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { - /* In the reset function, these buffers may have been allocated - * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].page != NULL) { - dma_unmap_page(trans->dev, rxq->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rxq->pool[i].page, - trans_pcie->rx_page_order); - rxq->pool[i].page = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + if (!rxq->pool[i].page) + continue; + dma_unmap_page(trans->dev, rxq->pool[i].page_dma, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order); + rxq->pool[i].page = NULL; } } @@ -491,6 +490,20 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); } +static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) +{ + int i; + + lockdep_assert_held(&rxq->lock); + + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + rxq->free_count = 0; + + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) + list_add(&rxq->pool[i].list, &rxq->rx_used); +} + int iwl_pcie_rx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -505,13 +518,12 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) } spin_lock_irqsave(&rxq->lock, flags); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - INIT_WORK(&trans_pcie->rx_replenish, - iwl_pcie_rx_replenish_work); + INIT_WORK(&trans_pcie->rx_replenish, iwl_pcie_rx_replenish_work); + /* free all first - we might be reconfigured for a different size */ iwl_pcie_rxq_free_rbs(trans); + iwl_pcie_rx_init_rxb_lists(rxq); for (i = 0; i < RX_QUEUE_SIZE; i++) rxq->queue[i] = NULL; @@ -520,7 +532,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) * not restocked the Rx queue with fresh buffers */ rxq->read = rxq->write = 0; rxq->write_actual = 0; - rxq->free_count = 0; memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); spin_unlock_irqrestore(&rxq->lock, flags); @@ -802,9 +813,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) u32 handled = 0; unsigned long flags; u32 i; -#ifdef CONFIG_IWLWIFI_DEBUG - u32 inta_mask; -#endif lock_map_acquire(&trans->sync_cmd_lockdep_map); @@ -826,14 +834,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) inta = trans_pcie->inta; -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(IWL_DL_ISR)) { - /* just for debug */ - inta_mask = iwl_read32(trans, CSR_INT_MASK); + if (iwl_have_debug_level(IWL_DL_ISR)) IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", - inta, inta_mask); - } -#endif + inta, iwl_read32(trans, CSR_INT_MASK)); /* saved interrupt in inta variable now we can reset trans_pcie->inta */ trans_pcie->inta = 0; @@ -855,12 +858,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) goto out; } -#ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { - IWL_DEBUG_ISR(trans, "Scheduler finished to transmit " - "the frame/frames.\n"); + IWL_DEBUG_ISR(trans, + "Scheduler finished to transmit the frame/frames.\n"); isr_stats->sch++; } @@ -870,7 +872,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) isr_stats->alive++; } } -#endif + /* Safely ignore these bits for debug checks below */ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); @@ -1118,9 +1120,6 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 inta, inta_mask; -#ifdef CONFIG_IWLWIFI_DEBUG - u32 inta_fh; -#endif lockdep_assert_held(&trans_pcie->irq_lock); @@ -1159,13 +1158,11 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) return IRQ_HANDLED; } -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(IWL_DL_ISR)) { - inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS); - IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, " - "fh 0x%08x\n", inta, inta_mask, inta_fh); - } -#endif + if (iwl_have_debug_level(IWL_DL_ISR)) + IWL_DEBUG_ISR(trans, + "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, + iwl_read32(trans, CSR_FH_INT_STATUS)); trans_pcie->inta |= inta; /* the thread will service interrupts and re-enable them */ @@ -1198,7 +1195,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) { struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie; - u32 inta, inta_mask; + u32 inta; u32 val = 0; u32 read; unsigned long flags; @@ -1226,7 +1223,6 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) * If we have something to service, the tasklet will re-enable ints. * If we *don't* have something, we'll re-enable before leaving here. */ - inta_mask = iwl_read32(trans, CSR_INT_MASK); iwl_write32(trans, CSR_INT_MASK, 0x00000000); /* Ignore interrupt if there's nothing in NIC to service. @@ -1271,8 +1267,11 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) val |= 0x8000; inta = (0xff & val) | ((0xff00 & val) << 16); - IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", - inta, inta_mask, val); + IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled(sw) 0x%08x ict 0x%08x\n", + inta, trans_pcie->inta_mask, val); + if (iwl_have_debug_level(IWL_DL_ISR)) + IWL_DEBUG_ISR(trans, "enabled(hw) 0x%08x\n", + iwl_read32(trans, CSR_INT_MASK)); inta &= trans_pcie->inta_mask; trans_pcie->inta |= inta; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 50ba0a468f9..826c15602c4 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -405,20 +405,27 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, { u8 *v_addr; dma_addr_t p_addr; - u32 offset; + u32 offset, chunk_sz = section->len; int ret = 0; IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", section_num); - v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL); - if (!v_addr) - return -ENOMEM; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, + GFP_KERNEL | __GFP_NOWARN); + if (!v_addr) { + IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); + chunk_sz = PAGE_SIZE; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, + &p_addr, GFP_KERNEL); + if (!v_addr) + return -ENOMEM; + } - for (offset = 0; offset < section->len; offset += PAGE_SIZE) { + for (offset = 0; offset < section->len; offset += chunk_sz) { u32 copy_size; - copy_size = min_t(u32, PAGE_SIZE, section->len - offset); + copy_size = min_t(u32, chunk_sz, section->len - offset); memcpy(v_addr, (u8 *)section->data + offset, copy_size); ret = iwl_pcie_load_firmware_chunk(trans, @@ -432,7 +439,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, } } - dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr); + dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); return ret; } @@ -571,13 +578,17 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) clear_bit(STATUS_RFKILL, &trans_pcie->status); } -static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) +static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) { - /* let the ucode operate on its own */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - iwl_disable_interrupts(trans); + + /* + * in testing mode, the host stays awake and the + * hardware won't be reset (not even partially) + */ + if (test) + return; + iwl_pcie_disable_ict(trans); iwl_clear_bit(trans, CSR_GP_CNTRL, @@ -596,11 +607,18 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) } static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status) + enum iwl_d3_status *status, + bool test) { u32 val; int ret; + if (test) { + iwl_enable_interrupts(trans); + *status = IWL_D3_STATUS_ALIVE; + return 0; + } + iwl_pcie_set_pwr(trans, false); val = iwl_read32(trans, CSR_RESET); @@ -636,9 +654,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, return ret; } - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - *status = IWL_D3_STATUS_ALIVE; return 0; } @@ -823,8 +838,9 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, unsigned long *flags) { int ret; - struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans); - spin_lock_irqsave(&pcie_trans->reg_lock, *flags); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock_irqsave(&trans_pcie->reg_lock, *flags); /* this bit wakes up the NIC */ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, @@ -860,7 +876,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, WARN_ONCE(1, "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", val); - spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags); + spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); return false; } } @@ -869,22 +885,22 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, * Fool sparse by faking we release the lock - sparse will * track nic_access anyway. */ - __release(&pcie_trans->reg_lock); + __release(&trans_pcie->reg_lock); return true; } static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, unsigned long *flags) { - struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - lockdep_assert_held(&pcie_trans->reg_lock); + lockdep_assert_held(&trans_pcie->reg_lock); /* * Fool sparse by faking we acquiring the lock - sparse will * track nic_access anyway. */ - __acquire(&pcie_trans->reg_lock); + __acquire(&trans_pcie->reg_lock); __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); @@ -895,7 +911,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, * scheduled on different CPUs (after we drop reg_lock). */ mmiowb(); - spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags); + spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); } static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, @@ -917,11 +933,11 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, } static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) + const void *buf, int dwords) { unsigned long flags; int offs, ret = 0; - u32 *vals = buf; + const u32 *vals = buf; if (iwl_trans_grab_nic_access(trans, false, &flags)) { iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c5e30294c5a..c47c92165ab 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -224,13 +224,13 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, switch (sec_ctl & TX_CMD_SEC_MSK) { case TX_CMD_SEC_CCM: - len += CCMP_MIC_LEN; + len += IEEE80211_CCMP_MIC_LEN; break; case TX_CMD_SEC_TKIP: - len += TKIP_ICV_LEN; + len += IEEE80211_TKIP_ICV_LEN; break; case TX_CMD_SEC_WEP: - len += WEP_IV_LEN + WEP_ICV_LEN; + len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN; break; } @@ -576,10 +576,16 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) spin_lock_bh(&txq->lock); while (q->write_ptr != q->read_ptr) { + IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", + txq_id, q->read_ptr); iwl_pcie_txq_free_tfd(trans, txq); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); } + txq->active = false; spin_unlock_bh(&txq->lock); + + /* just in case - this queue may have been stopped */ + iwl_wake_queue(trans, txq); } /* @@ -927,6 +933,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, spin_lock_bh(&txq->lock); + if (!txq->active) { + IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n", + txq_id, ssn); + goto out; + } + if (txq->q.read_ptr == tfd_num) goto out; @@ -1045,6 +1057,10 @@ static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans, (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn) { @@ -1069,6 +1085,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, /* enable aggregations for the queue */ iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + trans_pcie->txq[txq_id].ampdu = true; } else { /* * disable aggregations for the queue, this will also make the @@ -1103,6 +1120,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, (fifo << SCD_QUEUE_STTS_REG_POS_TXF) | (1 << SCD_QUEUE_STTS_REG_POS_WSL) | SCD_QUEUE_STTS_REG_MSK); + trans_pcie->txq[txq_id].active = true; IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n", txq_id, fifo, ssn & 0xff); } @@ -1125,6 +1143,7 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) ARRAY_SIZE(zero_val)); iwl_pcie_txq_unmap(trans, txq_id); + trans_pcie->txq[txq_id].ampdu = false; IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } @@ -1518,11 +1537,13 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { IWL_ERR(trans, "FW error in SYNC CMD %s\n", get_cmd_string(trans_pcie, cmd->id)); + dump_stack(); ret = -EIO; goto cancel; } - if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans_pcie->status)) { IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); ret = -ERFKILL; goto cancel; @@ -1564,7 +1585,8 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) return -EIO; - if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans_pcie->status)) { IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", cmd->id); return -ERFKILL; @@ -1592,7 +1614,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, u8 wait_write_ptr = 0; __le16 fc = hdr->frame_control; u8 hdr_len = ieee80211_hdrlen(fc); - u16 __maybe_unused wifi_seq; + u16 wifi_seq; txq = &trans_pcie->txq[txq_id]; q = &txq->q; @@ -1609,13 +1631,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, * the BA. * Check here that the packets are in the right place on the ring. */ -#ifdef CONFIG_IWLWIFI_DEBUG wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) && - ((wifi_seq & 0xff) != q->write_ptr), + WARN_ONCE(trans_pcie->txq[txq_id].ampdu && + (wifi_seq & 0xff) != q->write_ptr, "Q: %d WiFi Seq %d tfdNum %d", txq_id, wifi_seq, q->write_ptr); -#endif /* Set up driver data for this TFD */ txq->entries[q->write_ptr].skb = skb; diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 3e81264db81..efae07e05c8 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -240,7 +240,7 @@ static ssize_t lbs_prb_rsp_limit_set(struct device *dev, memset(&mesh_access, 0, sizeof(mesh_access)); mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); - if (!strict_strtoul(buf, 10, &retry_limit)) + if (!kstrtoul(buf, 10, &retry_limit)) return -ENOTSUPP; if (retry_limit > 15) return -ENOTSUPP; diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c new file mode 100644 index 00000000000..8d683070bdb --- /dev/null +++ b/drivers/net/wireless/mwifiex/11h.c @@ -0,0 +1,101 @@ +/* + * Marvell Wireless LAN device driver: 802.11h + * + * Copyright (C) 2013, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" +#include "fw.h" + + +/* This function appends 11h info to a buffer while joining an + * infrastructure BSS + */ +static void +mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct mwifiex_ie_types_header *ie_header; + struct mwifiex_ie_types_pwr_capability *cap; + struct mwifiex_ie_types_local_pwr_constraint *constraint; + struct ieee80211_supported_band *sband; + u8 radio_type; + int i; + + if (!buffer || !(*buffer)) + return; + + radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + sband = priv->wdev->wiphy->bands[radio_type]; + + cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; + cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); + cap->header.len = cpu_to_le16(2); + cap->min_pwr = 0; + cap->max_pwr = 0; + *buffer += sizeof(*cap); + + constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer; + constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); + constraint->header.len = cpu_to_le16(2); + constraint->chan = bss_desc->channel; + constraint->constraint = bss_desc->local_constraint; + *buffer += sizeof(*constraint); + + ie_header = (struct mwifiex_ie_types_header *)*buffer; + ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); + *buffer += sizeof(*ie_header); + *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; + *(*buffer)++ = 2 * sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + *(*buffer)++ = ieee80211_frequency_to_channel( + sband->channels[i].center_freq); + *(*buffer)++ = 1; /* one channel in the subband */ + } +} + +/* Enable or disable the 11h extensions in the firmware */ +static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) +{ + u32 enable = flag; + + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11H_I, &enable); +} + +/* This functions processes TLV buffer for a pending BSS Join command. + * + * Activate 11h functionality in the firmware if the spectrum management + * capability bit is found in the network we are joining. Also, necessary + * TLVs are set based on requested network's 11h capability. + */ +void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (bss_desc->sensed_11h) { + /* Activate 11h functions in firmware, turns on capability + * bit + */ + mwifiex_11h_activate(priv, true); + bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; + mwifiex_11h_process_infra_join(priv, buffer, bss_desc); + } else { + /* Deactivate 11h functions in the firmware */ + mwifiex_11h_activate(priv, false); + bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; + } +} diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index 4f614aad9de..f7ff4725506 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -3,13 +3,13 @@ config MWIFIEX depends on CFG80211 ---help--- This adds support for wireless adapters based on Marvell - 802.11n chipsets. + 802.11n/ac chipsets. If you choose to build it as a module, it will be called mwifiex. config MWIFIEX_SDIO - tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797" + tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897" depends on MWIFIEX && MMC select FW_LOADER ---help--- diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index ecf28464367..a42a506fd32 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -40,6 +40,7 @@ mwifiex-y += sta_rx.o mwifiex-y += uap_txrx.o mwifiex-y += cfg80211.o mwifiex-y += ethtool.o +mwifiex-y += 11h.o mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MWIFIEX) += mwifiex.o diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e42b266a023..ef5fa890a28 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -20,6 +20,9 @@ #include "cfg80211.h" #include "main.h" +static char *reg_alpha2; +module_param(reg_alpha2, charp, 0); + static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION), @@ -1231,6 +1234,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, return 0; } +/* cfg80211 operation handler for del_station. + * Function deauthenticates station which value is provided in mac parameter. + * If mac is NULL/broadcast, all stations in associated station list are + * deauthenticated. If bss is not started or there are no stations in + * associated stations list, no action is taken. + */ +static int +mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_sta_node *sta_node; + unsigned long flags; + + if (list_empty(&priv->sta_list) || !priv->bss_started) + return 0; + + if (!mac || is_broadcast_ether_addr(mac)) { + wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__); + list_for_each_entry(sta_node, &priv->sta_list, list) { + if (mwifiex_send_cmd_sync(priv, + HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr)) + return -1; + mwifiex_uap_del_sta_data(priv, sta_node); + } + } else { + wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac); + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_node = mwifiex_get_sta_entry(priv, mac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + if (sta_node) { + if (mwifiex_send_cmd_sync(priv, + HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr)) + return -1; + mwifiex_uap_del_sta_data(priv, sta_node); + } + } + + return 0; +} + static int mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) { @@ -1859,6 +1907,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, int i, offset, ret; struct ieee80211_channel *chan; struct ieee_types_header *ie; + struct mwifiex_user_scan_cfg *user_scan_cfg; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); @@ -1869,20 +1918,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, return -EBUSY; } - if (priv->user_scan_cfg) { + /* Block scan request if scan operation or scan cleanup when interface + * is disabled is in process + */ + if (priv->scan_request || priv->scan_aborting) { dev_err(priv->adapter->dev, "cmd: Scan already in process..\n"); return -EBUSY; } - priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), - GFP_KERNEL); - if (!priv->user_scan_cfg) + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) return -ENOMEM; priv->scan_request = request; - priv->user_scan_cfg->num_ssids = request->n_ssids; - priv->user_scan_cfg->ssid_list = request->ssids; + user_scan_cfg->num_ssids = request->n_ssids; + user_scan_cfg->ssid_list = request->ssids; if (request->ie && request->ie_len) { offset = 0; @@ -1902,25 +1953,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, for (i = 0; i < min_t(u32, request->n_channels, MWIFIEX_USER_SCAN_CHAN_MAX); i++) { chan = request->channels[i]; - priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value; - priv->user_scan_cfg->chan_list[i].radio_type = chan->band; + user_scan_cfg->chan_list[i].chan_number = chan->hw_value; + user_scan_cfg->chan_list[i].radio_type = chan->band; if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) - priv->user_scan_cfg->chan_list[i].scan_type = + user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_PASSIVE; else - priv->user_scan_cfg->chan_list[i].scan_type = + user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_ACTIVE; - priv->user_scan_cfg->chan_list[i].scan_time = 0; + user_scan_cfg->chan_list[i].scan_time = 0; } - ret = mwifiex_scan_networks(priv, priv->user_scan_cfg); + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); if (ret) { dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + priv->scan_aborting = false; priv->scan_request = NULL; - kfree(priv->user_scan_cfg); - priv->user_scan_cfg = NULL; return ret; } @@ -2419,6 +2470,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .change_beacon = mwifiex_cfg80211_change_beacon, .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, .set_antenna = mwifiex_cfg80211_set_antenna, + .del_station = mwifiex_cfg80211_del_station, #ifdef CONFIG_PM .suspend = mwifiex_cfg80211_suspend, .resume = mwifiex_cfg80211_resume, @@ -2426,6 +2478,27 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { #endif }; +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support mwifiex_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT, + .n_patterns = MWIFIEX_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; +#endif + +static bool mwifiex_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2 || strlen(alpha2) != 2) + return false; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + /* * This function registers the device with CFG802.11 subsystem. * @@ -2478,16 +2551,13 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_CUSTOM_REGULATORY | + WIPHY_FLAG_STRICT_REGULATORY | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); #ifdef CONFIG_PM - wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT; - wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS; - wiphy->wowlan.pattern_min_len = 1; - wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN; - wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN; + wiphy->wowlan = &mwifiex_wowlan_support; #endif wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | @@ -2519,10 +2589,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy_free(wiphy); return ret; } - country_code = mwifiex_11d_code_2_region(priv->adapter->region_code); - if (country_code) - dev_info(adapter->dev, - "ignoring F/W country code %2.2s\n", country_code); + + if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) { + wiphy_info(wiphy, "driver hint alpha2: %2.2s\n", reg_alpha2); + regulatory_hint(wiphy, reg_alpha2); + } else { + country_code = mwifiex_11d_code_2_region(adapter->region_code); + if (country_code) + wiphy_info(wiphy, "ignoring F/W country code %2.2s\n", + country_code); + } adapter->wiphy = wiphy; return ret; diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 26755d9acb5..2d761477d15 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_UAP_SYS_CONFIG: case HostCmd_CMD_UAP_BSS_START: case HostCmd_CMD_UAP_BSS_STOP: + case HostCmd_CMD_UAP_STA_DEAUTH: ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, cmd_oid, data_buf, cmd_ptr); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 1f7578d553e..1b45aa53330 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -245,6 +245,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HT_BW_20 0 #define HT_BW_40 1 +#define DFS_CHAN_MOVE_TIME 10000 + #define HostCmd_CMD_GET_HW_SPEC 0x0003 #define HostCmd_CMD_802_11_SCAN 0x0006 #define HostCmd_CMD_802_11_GET_LOG 0x000b @@ -271,6 +273,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 #define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 +#define HostCmd_CMD_CFG_DATA 0x008f #define HostCmd_CMD_VERSION_EXT 0x0097 #define HostCmd_CMD_MEF_CFG 0x009a #define HostCmd_CMD_RSSI_INFO 0x00a4 @@ -279,6 +282,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 #define HostCmd_CMD_UAP_BSS_START 0x00b1 #define HostCmd_CMD_UAP_BSS_STOP 0x00b2 +#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5 #define HostCmd_CMD_11N_CFG 0x00cd #define HostCmd_CMD_11N_ADDBA_REQ 0x00ce #define HostCmd_CMD_11N_ADDBA_RSP 0x00cf @@ -436,6 +440,7 @@ enum P2P_MODES { #define EVENT_BW_CHANGE 0x00000048 #define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c #define EVENT_HOSTWAKE_STAIE 0x0000004d +#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 #define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f #define EVENT_ID_MASK 0xffff @@ -464,6 +469,8 @@ enum P2P_MODES { #define MWIFIEX_CRITERIA_UNICAST BIT(1) #define MWIFIEX_CRITERIA_MULTICAST BIT(3) +#define CFG_DATA_TYPE_CAL 2 + struct mwifiex_ie_types_header { __le16 type; __le16 len; @@ -971,6 +978,7 @@ enum SNMP_MIB_INDEX { LONG_RETRY_LIM_I = 7, FRAG_THRESH_I = 8, DOT11D_I = 9, + DOT11H_I = 10, }; #define MAX_SNMP_BUF_SIZE 128 @@ -1197,6 +1205,23 @@ struct host_cmd_ds_amsdu_aggr_ctrl { __le16 curr_buf_size; } __packed; +struct host_cmd_ds_sta_deauth { + u8 mac[ETH_ALEN]; + __le16 reason; +} __packed; + +struct mwifiex_ie_types_pwr_capability { + struct mwifiex_ie_types_header header; + s8 min_pwr; + s8 max_pwr; +}; + +struct mwifiex_ie_types_local_pwr_constraint { + struct mwifiex_ie_types_header header; + u8 chan; + u8 constraint; +}; + struct mwifiex_ie_types_wmm_param_set { struct mwifiex_ie_types_header header; u8 wmm_ie[1]; @@ -1573,6 +1598,12 @@ struct mwifiex_ie_list { struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; } __packed; +struct host_cmd_ds_802_11_cfg_data { + __le16 action; + __le16 type; + __le16 data_len; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -1630,7 +1661,9 @@ struct host_cmd_ds_command { struct host_cmd_ds_802_11_eeprom_access eeprom; struct host_cmd_ds_802_11_subsc_evt subsc_evt; struct host_cmd_ds_sys_config uap_sys_config; + struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; + struct host_cmd_ds_802_11_cfg_data cfg_data; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 9f44fda19db..caaf4bd56b3 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -52,87 +52,6 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) return 0; } -static void scan_delay_timer_fn(unsigned long data) -{ - struct mwifiex_private *priv = (struct mwifiex_private *)data; - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node, *tmp_node; - unsigned long flags; - - if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { - /* - * Abort scan operation by cancelling all pending scan - * commands - */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - adapter->scan_delay_cnt = 0; - adapter->empty_tx_q_cnt = 0; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - if (priv->user_scan_cfg) { - if (priv->scan_request) { - dev_dbg(priv->adapter->dev, - "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } else { - dev_dbg(priv->adapter->dev, - "info: scan already aborted\n"); - } - - kfree(priv->user_scan_cfg); - priv->user_scan_cfg = NULL; - } - goto done; - } - - if (!atomic_read(&priv->adapter->is_tx_received)) { - adapter->empty_tx_q_cnt++; - if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) { - /* - * No Tx traffic for 200msec. Get scan command from - * scan pending queue and put to cmd pending queue to - * resume scan operation - */ - adapter->scan_delay_cnt = 0; - adapter->empty_tx_q_cnt = 0; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); - queue_work(adapter->workqueue, &adapter->main_work); - goto done; - } - } else { - adapter->empty_tx_q_cnt = 0; - } - - /* Delay scan operation further by 20msec */ - mod_timer(&priv->scan_delay_timer, jiffies + - msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); - adapter->scan_delay_cnt++; - -done: - if (atomic_read(&priv->adapter->is_tx_received)) - atomic_set(&priv->adapter->is_tx_received, false); - - return; -} - /* * This function initializes the private structure and sets default * values to the members. @@ -214,8 +133,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->scan_block = false; - setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn, - (unsigned long)priv); + priv->csa_chan = 0; + priv->csa_expire_time = 0; return mwifiex_add_bss_prio_tbl(priv); } @@ -447,23 +366,29 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) } /* - * This function frees the adapter structure. + * This function performs cleanup for adapter structure. * - * The freeing operation is done recursively, by canceling all - * pending commands, freeing the member buffers previously - * allocated (command buffers, scan table buffer, sleep confirm - * command buffer), stopping the timers and calling the cleanup - * routines for every interface, before the actual adapter - * structure is freed. + * The cleanup is done recursively, by canceling all pending + * commands, freeing the member buffers previously allocated + * (command buffers, scan table buffer, sleep confirm command + * buffer), stopping the timers and calling the cleanup routines + * for every interface. */ static void -mwifiex_free_adapter(struct mwifiex_adapter *adapter) +mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) { + int i; + if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); return; } + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) + del_timer_sync(&adapter->priv[i]->scan_delay_timer); + } + mwifiex_cancel_all_pending_cmd(adapter); /* Free lock variables */ @@ -684,7 +609,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) int ret = -EINPROGRESS; struct mwifiex_private *priv; s32 i; - unsigned long flags; struct sk_buff *skb; /* mwifiex already shutdown */ @@ -719,7 +643,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) } } - spin_lock_irqsave(&adapter->mwifiex_lock, flags); + spin_lock(&adapter->mwifiex_lock); if (adapter->if_ops.data_complete) { while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) { @@ -733,10 +657,9 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) } } - /* Free adapter structure */ - mwifiex_free_adapter(adapter); + mwifiex_adapter_cleanup(adapter); - spin_unlock_irqrestore(&adapter->mwifiex_lock, flags); + spin_unlock(&adapter->mwifiex_lock); /* Notify completion */ ret = mwifiex_shutdown_fw_complete(adapter); diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 6bcb66e6e97..1c8a771e8e8 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -534,6 +534,8 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); + mwifiex_11h_process_join(priv, &pos, bss_desc); + cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); /* Set the Capability info at last */ @@ -919,9 +921,8 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, memcpy(&priv->curr_bss_params.data_rates, &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); - dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n", - adhoc_start->data_rate[0], adhoc_start->data_rate[1], - adhoc_start->data_rate[2], adhoc_start->data_rate[3]); + dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%4ph\n", + adhoc_start->data_rate); dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 2eb88ea9acf..e15ab72fb03 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -25,6 +25,86 @@ #define VERSION "1.0" const char driver_version[] = "mwifiex " VERSION " (%s) "; +static char *cal_data_cfg; +module_param(cal_data_cfg, charp, 0); + +static void scan_delay_timer_fn(unsigned long data) +{ + struct mwifiex_private *priv = (struct mwifiex_private *)data; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + + if (adapter->surprise_removed) + return; + + if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { + /* + * Abort scan operation by cancelling all pending scan + * commands + */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + adapter->scan_delay_cnt = 0; + adapter->empty_tx_q_cnt = 0; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); + } + goto done; + } + + if (!atomic_read(&priv->adapter->is_tx_received)) { + adapter->empty_tx_q_cnt++; + if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) { + /* + * No Tx traffic for 200msec. Get scan command from + * scan pending queue and put to cmd pending queue to + * resume scan operation + */ + adapter->scan_delay_cnt = 0; + adapter->empty_tx_q_cnt = 0; + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + queue_work(adapter->workqueue, &adapter->main_work); + goto done; + } + } else { + adapter->empty_tx_q_cnt = 0; + } + + /* Delay scan operation further by 20msec */ + mod_timer(&priv->scan_delay_timer, jiffies + + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); + adapter->scan_delay_cnt++; + +done: + if (atomic_read(&priv->adapter->is_tx_received)) + atomic_set(&priv->adapter->is_tx_received, false); + + return; +} /* * This function registers the device and performs all the necessary @@ -73,6 +153,10 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, adapter->priv[i]->adapter = adapter; adapter->priv_num++; + + setup_timer(&adapter->priv[i]->scan_delay_timer, + scan_delay_timer_fn, + (unsigned long)adapter->priv[i]); } mwifiex_init_lock_list(adapter); @@ -336,6 +420,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) dev_notice(adapter->dev, "WLAN FW is active\n"); + if (cal_data_cfg) { + if ((request_firmware(&adapter->cal_data, cal_data_cfg, + adapter->dev)) < 0) + dev_err(adapter->dev, + "Cal data request_firmware() failed\n"); + } + adapter->init_wait_q_woken = false; ret = mwifiex_init_fw(adapter); if (ret == -1) { @@ -390,6 +481,10 @@ err_init_fw: pr_debug("info: %s: unregister device\n", __func__); adapter->if_ops.unregister_dev(adapter); done: + if (adapter->cal_data) { + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } release_firmware(adapter->firmware); complete(&adapter->fw_load); return; @@ -436,6 +531,7 @@ mwifiex_close(struct net_device *dev) dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n"); cfg80211_scan_done(priv->scan_request, 1); priv->scan_request = NULL; + priv->scan_aborting = true; } return 0; @@ -573,9 +669,8 @@ static void mwifiex_set_multicast_list(struct net_device *dev) mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; } else { mcast_list.mode = MWIFIEX_MULTICAST_MODE; - if (netdev_mc_count(dev)) - mcast_list.num_multicast_addr = - mwifiex_copy_mcast_addr(&mcast_list, dev); + mcast_list.num_multicast_addr = + mwifiex_copy_mcast_addr(&mcast_list, dev); } mwifiex_request_set_multicast_list(priv, &mcast_list); } diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 4ef67fca06d..3da73d36acd 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -309,6 +309,9 @@ struct mwifiex_bssdescriptor { u16 wapi_offset; u8 *beacon_buf; u32 beacon_buf_size; + u8 sensed_11h; + u8 local_constraint; + u8 chan_sw_ie_present; }; struct mwifiex_current_bss_params { @@ -492,7 +495,6 @@ struct mwifiex_private { struct semaphore async_sem; u8 report_scan_result; struct cfg80211_scan_request *scan_request; - struct mwifiex_user_scan_cfg *user_scan_cfg; u8 cfg_bssid[6]; struct wps wps; u8 scan_block; @@ -510,6 +512,9 @@ struct mwifiex_private { u8 ap_11ac_enabled; u32 mgmt_frame_mask; struct mwifiex_roc_cfg roc_cfg; + bool scan_aborting; + u8 csa_chan; + unsigned long csa_expire_time; }; enum mwifiex_ba_status { @@ -730,6 +735,7 @@ struct mwifiex_adapter { u16 max_mgmt_ie_index; u8 scan_delay_cnt; u8 empty_tx_q_cnt; + const struct firmware *cal_data; /* 11AC */ u32 is_hw_11ac_capable; @@ -1017,6 +1023,24 @@ static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) return (*(u32 *)skb->data == PKT_TYPE_MGMT); } +/* This function retrieves channel closed for operation by Channel + * Switch Announcement. + */ +static inline u8 +mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) +{ + if (!priv->csa_chan) + return 0; + + /* Clear csa channel, if DFS channel move time has passed */ + if (jiffies > priv->csa_expire_time) { + priv->csa_chan = 0; + priv->csa_expire_time = 0; + } + + return priv->csa_chan; +} + int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); @@ -1115,6 +1139,12 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *data); int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); u8 *mwifiex_11d_code_2_region(u8 code); +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node); + +void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); extern const struct ethtool_ops mwifiex_ethtool_ops; diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 9cf5d8f07df..c447d9bd1aa 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -391,6 +391,12 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv, return 0; } + if (bss_desc->chan_sw_ie_present) { + dev_err(adapter->dev, + "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); + return -1; + } + if (mwifiex_is_bss_wapi(priv, bss_desc)) { dev_dbg(adapter->dev, "info: return success for WAPI AP\n"); return 0; @@ -569,6 +575,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, return -1; } + /* Check csa channel expiry before preparing scan list */ + mwifiex_11h_get_csa_closed_channel(priv); + chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); /* Set the temp channel struct pointer to the start of the desired @@ -598,6 +607,11 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, while (tlv_idx < max_chan_per_scan && tmp_chan_list->chan_number && !done_early) { + if (tmp_chan_list->chan_number == priv->csa_chan) { + tmp_chan_list++; + continue; + } + dev_dbg(priv->adapter->dev, "info: Scan: Chan(%3d), Radio(%d)," " Mode(%d, %d), Dur(%d)\n", @@ -1169,6 +1183,19 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, bss_entry->erp_flags = *(current_ptr + 2); break; + case WLAN_EID_PWR_CONSTRAINT: + bss_entry->local_constraint = *(current_ptr + 2); + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_CHANNEL_SWITCH: + bss_entry->chan_sw_ie_present = true; + case WLAN_EID_PWR_CAPABILITY: + case WLAN_EID_TPC_REPORT: + case WLAN_EID_QUIET: + bss_entry->sensed_11h = true; + break; + case WLAN_EID_EXT_SUPP_RATES: /* * Only process extended supported rate @@ -1575,6 +1602,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, goto check_next_scan; } + /* Check csa channel expiry before parsing scan response */ + mwifiex_11h_get_csa_closed_channel(priv); + bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n", bytes_left); @@ -1727,6 +1757,13 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, struct ieee80211_channel *chan; u8 band; + /* Skip entry if on csa closed channel */ + if (channel == priv->csa_chan) { + dev_dbg(adapter->dev, + "Dropping entry on csa closed channel\n"); + continue; + } + band = BAND_G; if (chan_band_tlv) { chan_band = @@ -1784,22 +1821,17 @@ check_next_scan: if (priv->report_scan_result) priv->report_scan_result = false; - if (priv->user_scan_cfg) { - if (priv->scan_request) { - dev_dbg(priv->adapter->dev, - "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); - priv->scan_request = NULL; - } else { - dev_dbg(priv->adapter->dev, - "info: scan already aborted\n"); - } - - kfree(priv->user_scan_cfg); - priv->user_scan_cfg = NULL; + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, 0); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); } } else { - if (priv->user_scan_cfg && !priv->scan_request) { + if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 363ba31b58b..5ee5ed02ecc 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -77,6 +77,17 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; + if (id->driver_data) { + struct mwifiex_sdio_device *data = (void *)id->driver_data; + + card->firmware = data->firmware; + card->reg = data->reg; + card->max_ports = data->max_ports; + card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; + card->supports_sdio_new_mode = data->supports_sdio_new_mode; + card->has_control_mask = data->has_control_mask; + } + sdio_claim_host(func); ret = sdio_enable_func(func); sdio_release_host(func); @@ -251,12 +262,19 @@ static int mwifiex_sdio_resume(struct device *dev) #define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) /* Device ID for SD8797 */ #define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) +/* Device ID for SD8897 */ +#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) /* WLAN IDs */ static const struct sdio_device_id mwifiex_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786), + .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787), + .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), + .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897), + .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, {}, }; @@ -282,13 +300,13 @@ static struct sdio_driver mwifiex_sdio = { * This function writes data into SDIO card register. */ static int -mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) +mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) { struct sdio_mmc_card *card = adapter->card; int ret = -1; sdio_claim_host(card->func); - sdio_writeb(card->func, (u8) data, reg, &ret); + sdio_writeb(card->func, data, reg, &ret); sdio_release_host(card->func); return ret; @@ -298,7 +316,7 @@ mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) * This function reads data from SDIO card register. */ static int -mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data) +mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) { struct sdio_mmc_card *card = adapter->card; int ret = -1; @@ -400,7 +418,40 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) } /* - * This function initializes the IO ports. + * This function is used to initialize IO ports for the + * chipsets supporting SDIO new mode eg SD8897. + */ +static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) +{ + u8 reg; + + adapter->ioport = MEM_PORT; + + /* enable sdio new mode */ + if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, ®)) + return -1; + if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG, + reg | CMD53_NEW_MODE)) + return -1; + + /* Configure cmd port and enable reading rx length from the register */ + if (mwifiex_read_reg(adapter, CMD_CONFIG_0, ®)) + return -1; + if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN)) + return -1; + + /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is + * completed + */ + if (mwifiex_read_reg(adapter, CMD_CONFIG_1, ®)) + return -1; + if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN)) + return -1; + + return 0; +} + +/* This function initializes the IO ports. * * The following operations are performed - * - Read the IO ports (0, 1 and 2) @@ -409,10 +460,17 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) */ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) { - u32 reg; + u8 reg; + struct sdio_mmc_card *card = adapter->card; adapter->ioport = 0; + if (card->supports_sdio_new_mode) { + if (mwifiex_init_sdio_new_mode(adapter)) + return -1; + goto cont; + } + /* Read the IO port */ if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, ®)) adapter->ioport |= (reg & 0xff); @@ -428,19 +486,19 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) adapter->ioport |= ((reg & 0xff) << 16); else return -1; - +cont: pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); /* Set Host interrupt reset to read to clear */ if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, ®)) mwifiex_write_reg(adapter, HOST_INT_RSR_REG, - reg | SDIO_INT_MASK); + reg | card->reg->sdio_int_mask); else return -1; /* Dnld/Upld ready set to auto reset */ - if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, ®)) - mwifiex_write_reg(adapter, CARD_MISC_CFG_REG, + if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) + mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, reg | AUTO_RE_ENABLE_INT); else return -1; @@ -486,34 +544,42 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) { struct sdio_mmc_card *card = adapter->card; - u16 rd_bitmap = card->mp_rd_bitmap; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u32 rd_bitmap = card->mp_rd_bitmap; - dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap); + dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); - if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) - return -1; + if (card->supports_sdio_new_mode) { + if (!(rd_bitmap & reg->data_port_mask)) + return -1; + } else { + if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) + return -1; + } - if (card->mp_rd_bitmap & CTRL_PORT_MASK) { - card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK); + if ((card->has_control_mask) && + (card->mp_rd_bitmap & CTRL_PORT_MASK)) { + card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); *port = CTRL_PORT; - dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n", + dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n", *port, card->mp_rd_bitmap); - } else { - if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) { - card->mp_rd_bitmap &= (u16) - (~(1 << card->curr_rd_port)); - *port = card->curr_rd_port; + return 0; + } - if (++card->curr_rd_port == MAX_PORT) - card->curr_rd_port = 1; - } else { - return -1; - } + if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) + return -1; + + /* We are now handling the SDIO data ports */ + card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); + *port = card->curr_rd_port; + + if (++card->curr_rd_port == card->max_ports) + card->curr_rd_port = reg->start_rd_port; + + dev_dbg(adapter->dev, + "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", + *port, rd_bitmap, card->mp_rd_bitmap); - dev_dbg(adapter->dev, - "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n", - *port, rd_bitmap, card->mp_rd_bitmap); - } return 0; } @@ -524,35 +590,45 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) * increased (provided it does not reach the maximum limit, in which * case it is reset to 1) */ -static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) +static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) { struct sdio_mmc_card *card = adapter->card; - u16 wr_bitmap = card->mp_wr_bitmap; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u32 wr_bitmap = card->mp_wr_bitmap; - dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap); + dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); - if (!(wr_bitmap & card->mp_data_port_mask)) + if (card->supports_sdio_new_mode && + !(wr_bitmap & reg->data_port_mask)) { + adapter->data_sent = true; + return -EBUSY; + } else if (!card->supports_sdio_new_mode && + !(wr_bitmap & card->mp_data_port_mask)) { return -1; + } if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { - card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port)); + card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); *port = card->curr_wr_port; - if (++card->curr_wr_port == card->mp_end_port) - card->curr_wr_port = 1; + if (((card->supports_sdio_new_mode) && + (++card->curr_wr_port == card->max_ports)) || + ((!card->supports_sdio_new_mode) && + (++card->curr_wr_port == card->mp_end_port))) + card->curr_wr_port = reg->start_wr_port; } else { adapter->data_sent = true; return -EBUSY; } - if (*port == CTRL_PORT) { - dev_err(adapter->dev, "invalid data port=%d cur port=%d" - " mp_wr_bitmap=0x%04x -> 0x%04x\n", + if ((card->has_control_mask) && (*port == CTRL_PORT)) { + dev_err(adapter->dev, + "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *port, card->curr_wr_port, wr_bitmap, card->mp_wr_bitmap); return -1; } - dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n", + dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *port, wr_bitmap, card->mp_wr_bitmap); return 0; @@ -564,11 +640,12 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) static int mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) { + struct sdio_mmc_card *card = adapter->card; u32 tries; - u32 cs; + u8 cs; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs)) + if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) break; else if ((cs & bits) == bits) return 0; @@ -587,12 +664,14 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) static int mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) { - u32 fws0, fws1; + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u8 fws0, fws1; - if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0)) + if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) return -1; - if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1)) + if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) return -1; *dat = (u16) ((fws1 << 8) | fws0); @@ -608,14 +687,14 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) */ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) { - u32 host_int_mask; + u8 host_int_mask, host_int_disable = HOST_INT_DISABLE; /* Read back the host_int_mask register */ if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask)) return -1; /* Update with the mask and write back to the register */ - host_int_mask &= ~HOST_INT_DISABLE; + host_int_mask &= ~host_int_disable; if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) { dev_err(adapter->dev, "disable host interrupt failed\n"); @@ -633,8 +712,11 @@ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) */ static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) { + struct sdio_mmc_card *card = adapter->card; + /* Simply write the mask to the register */ - if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) { + if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, + card->reg->host_int_enable)) { dev_err(adapter->dev, "enable host interrupt failed\n"); return -1; } @@ -686,11 +768,13 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int ret; u8 *firmware = fw->fw_buf; u32 firmware_len = fw->fw_len; u32 offset = 0; - u32 base0, base1; + u8 base0, base1; u8 *fwbuf; u16 len = 0; u32 txlen, tx_blocks = 0, tries; @@ -727,7 +811,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0, + ret = mwifiex_read_reg(adapter, reg->base_0_reg, &base0); if (ret) { dev_err(adapter->dev, @@ -736,7 +820,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, base0, base0); goto done; } - ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1, + ret = mwifiex_read_reg(adapter, reg->base_1_reg, &base1); if (ret) { dev_err(adapter->dev, @@ -828,10 +912,11 @@ done: static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) { + struct sdio_mmc_card *card = adapter->card; int ret = 0; u16 firmware_stat; u32 tries; - u32 winner_status; + u8 winner_status; /* Wait for firmware initialization event */ for (tries = 0; tries < poll_num; tries++) { @@ -849,7 +934,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, if (ret) { if (mwifiex_read_reg - (adapter, CARD_FW_STATUS0_REG, &winner_status)) + (adapter, card->reg->status_reg_0, &winner_status)) winner_status = 0; if (winner_status) @@ -866,12 +951,12 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; - u32 sdio_ireg; + u8 sdio_ireg; unsigned long flags; - if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS, - REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, - 0)) { + if (mwifiex_read_data_sync(adapter, card->mp_regs, + card->reg->max_mp_regs, + REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { dev_err(adapter->dev, "read mp_regs failed\n"); return; } @@ -880,6 +965,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) if (sdio_ireg) { /* * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * For SDIO new mode CMD port interrupts + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS * Clear the interrupt status register */ dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); @@ -1003,11 +1091,11 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, s32 f_aggr_cur = 0; struct sk_buff *skb_deaggr; u32 pind; - u32 pkt_len, pkt_type = 0; + u32 pkt_len, pkt_type, mport; u8 *curr_ptr; u32 rx_len = skb->len; - if (port == CTRL_PORT) { + if ((card->has_control_mask) && (port == CTRL_PORT)) { /* Read the command Resp without aggr */ dev_dbg(adapter->dev, "info: %s: no aggregation for cmd " "response\n", __func__); @@ -1024,7 +1112,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, goto rx_curr_single; } - if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) { + if ((!card->has_control_mask && (card->mp_rd_bitmap & + card->reg->data_port_mask)) || + (card->has_control_mask && (card->mp_rd_bitmap & + (~((u32) CTRL_PORT_MASK))))) { /* Some more data RX pending */ dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); @@ -1060,10 +1151,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, if (f_aggr_cur) { dev_dbg(adapter->dev, "info: current packet aggregation\n"); /* Curr pkt can be aggregated */ - MP_RX_AGGR_SETUP(card, skb, port); + mp_rx_aggr_setup(card, skb, port); if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || - MP_RX_AGGR_PORT_LIMIT_REACHED(card)) { + mp_rx_aggr_port_limit_reached(card)) { dev_dbg(adapter->dev, "info: %s: aggregated packet " "limit reached\n", __func__); /* No more pkts allowed in Aggr buf, rx it */ @@ -1076,11 +1167,28 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", card->mpa_rx.pkt_cnt); + if (card->supports_sdio_new_mode) { + int i; + u32 port_count; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_rx.ports & BIT(i)) + port_count++; + + /* Reading data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_rx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_rx.ports << 4)) + + card->mpa_rx.start_port; + } + if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, - card->mpa_rx.buf_len, - (adapter->ioport | 0x1000 | - (card->mpa_rx.ports << 4)) + - card->mpa_rx.start_port, 1)) + card->mpa_rx.buf_len, mport, 1)) goto error; curr_ptr = card->mpa_rx.buf; @@ -1167,6 +1275,7 @@ error: static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int ret = 0; u8 sdio_ireg; struct sk_buff *skb; @@ -1175,6 +1284,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) u32 rx_blocks; u16 rx_len; unsigned long flags; + u32 bitmap; + u8 cr; spin_lock_irqsave(&adapter->int_lock, flags); sdio_ireg = adapter->int_status; @@ -1184,10 +1295,60 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) if (!sdio_ireg) return ret; + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) + adapter->cmd_sent = false; + + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + u32 pkt_type; + + /* read the len of control packet */ + rx_len = card->mp_regs[CMD_RD_LEN_1] << 8; + rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0]; + rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + MWIFIEX_RX_DATA_BUF_SIZE) + return -1; + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + + skb = dev_alloc_skb(rx_len); + if (!skb) + return -1; + + skb_put(skb, rx_len); + + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, + skb->len, adapter->ioport | + CMD_PORT_SLCT)) { + dev_err(adapter->dev, + "%s: failed to card_to_host", __func__); + dev_kfree_skb_any(skb); + goto term_cmd; + } + + if ((pkt_type != MWIFIEX_TYPE_CMD) && + (pkt_type != MWIFIEX_TYPE_EVENT)) + dev_err(adapter->dev, + "%s:Received wrong packet on cmd port", + __func__); + + mwifiex_decode_rx_packet(adapter, skb, pkt_type); + } + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { - card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8; - card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L]; - dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n", + bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; + } + card->mp_wr_bitmap = bitmap; + + dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n", card->mp_wr_bitmap); if (adapter->data_sent && (card->mp_wr_bitmap & card->mp_data_port_mask)) { @@ -1200,11 +1361,11 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) /* As firmware will not generate download ready interrupt if the port updated is command port only, cmd_sent should be done for any SDIO interrupt. */ - if (adapter->cmd_sent) { + if (card->has_control_mask && adapter->cmd_sent) { /* Check if firmware has attach buffer at command port and update just that in wr_bit_map. */ card->mp_wr_bitmap |= - (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; + (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; if (card->mp_wr_bitmap & CTRL_PORT_MASK) adapter->cmd_sent = false; } @@ -1212,9 +1373,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); if (sdio_ireg & UP_LD_HOST_INT_STATUS) { - card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8; - card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L]; - dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n", + bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; + } + card->mp_rd_bitmap = bitmap; + dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n", card->mp_rd_bitmap); while (true) { @@ -1224,8 +1392,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) "info: no more rd_port available\n"); break; } - len_reg_l = RD_LEN_P0_L + (port << 1); - len_reg_u = RD_LEN_P0_U + (port << 1); + len_reg_l = reg->rd_len_p0_l + (port << 1); + len_reg_u = reg->rd_len_p0_u + (port << 1); rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; rx_len |= (u16) card->mp_regs[len_reg_l]; dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n", @@ -1257,37 +1425,33 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, port)) { - u32 cr = 0; - dev_err(adapter->dev, "card_to_host_mpa failed:" " int status=%#x\n", sdio_ireg); - if (mwifiex_read_reg(adapter, - CONFIGURATION_REG, &cr)) - dev_err(adapter->dev, - "read CFG reg failed\n"); - - dev_dbg(adapter->dev, - "info: CFG reg val = %d\n", cr); - if (mwifiex_write_reg(adapter, - CONFIGURATION_REG, - (cr | 0x04))) - dev_err(adapter->dev, - "write CFG reg failed\n"); - - dev_dbg(adapter->dev, "info: write success\n"); - if (mwifiex_read_reg(adapter, - CONFIGURATION_REG, &cr)) - dev_err(adapter->dev, - "read CFG reg failed\n"); - - dev_dbg(adapter->dev, - "info: CFG reg val =%x\n", cr); - return -1; + goto term_cmd; } } } return 0; + +term_cmd: + /* terminate cmd */ + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + dev_err(adapter->dev, "read CFG reg failed\n"); + else + dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr); + + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) + dev_err(adapter->dev, "write CFG reg failed\n"); + else + dev_dbg(adapter->dev, "info: write success\n"); + + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + dev_err(adapter->dev, "read CFG reg failed\n"); + else + dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr); + + return -1; } /* @@ -1305,7 +1469,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) * and return. */ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, - u8 *payload, u32 pkt_len, u8 port, + u8 *payload, u32 pkt_len, u32 port, u32 next_pkt_len) { struct sdio_mmc_card *card = adapter->card; @@ -1314,8 +1478,11 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, s32 f_send_cur_buf = 0; s32 f_precopy_cur_buf = 0; s32 f_postcopy_cur_buf = 0; + u32 mport; - if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) { + if (!card->mpa_tx.enabled || + (card->has_control_mask && (port == CTRL_PORT)) || + (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n", __func__); @@ -1329,7 +1496,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, __func__); if (MP_TX_AGGR_IN_PROGRESS(card)) { - if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) && + if (!mp_tx_aggr_port_limit_reached(card) && MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { f_precopy_cur_buf = 1; @@ -1342,7 +1509,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, /* No room in Aggr buf, send it */ f_send_aggr_buf = 1; - if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) || + if (mp_tx_aggr_port_limit_reached(card) || !(card->mp_wr_bitmap & (1 << card->curr_wr_port))) f_send_cur_buf = 1; @@ -1381,7 +1548,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || - MP_TX_AGGR_PORT_LIMIT_REACHED(card)) + mp_tx_aggr_port_limit_reached(card)) /* No more pkts allowed in Aggr buf, send it */ f_send_aggr_buf = 1; } @@ -1390,11 +1557,28 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", __func__, card->mpa_tx.start_port, card->mpa_tx.ports); + if (card->supports_sdio_new_mode) { + u32 port_count; + int i; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_tx.ports & BIT(i)) + port_count++; + + /* Writing data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_tx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_tx.ports << 4)) + + card->mpa_tx.start_port; + } + ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, - card->mpa_tx.buf_len, - (adapter->ioport | 0x1000 | - (card->mpa_tx.ports << 4)) + - card->mpa_tx.start_port); + card->mpa_tx.buf_len, mport); MP_TX_AGGR_BUF_RESET(card); } @@ -1434,7 +1618,7 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, int ret; u32 buf_block_len; u32 blk_size; - u8 port = CTRL_PORT; + u32 port = CTRL_PORT; u8 *payload = (u8 *)skb->data; u32 pkt_len = skb->len; @@ -1465,6 +1649,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, pkt_len > MWIFIEX_UPLD_SIZE) dev_err(adapter->dev, "%s: payload=%p, nb=%d\n", __func__, payload, pkt_len); + + if (card->supports_sdio_new_mode) + port = CMD_PORT_SLCT; } /* Transfer data to card */ @@ -1586,18 +1773,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) adapter->dev = &func->dev; - switch (func->device) { - case SDIO_DEVICE_ID_MARVELL_8786: - strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME); - break; - case SDIO_DEVICE_ID_MARVELL_8797: - strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME); - break; - case SDIO_DEVICE_ID_MARVELL_8787: - default: - strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME); - break; - } + strcpy(adapter->fw_name, card->firmware); return 0; @@ -1626,8 +1802,9 @@ disable_func: static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int ret; - u32 sdio_ireg; + u8 sdio_ireg; /* * Read the HOST_INT_STATUS_REG for ACK the first interrupt got @@ -1645,30 +1822,35 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) /* Initialize SDIO variables in card */ card->mp_rd_bitmap = 0; card->mp_wr_bitmap = 0; - card->curr_rd_port = 1; - card->curr_wr_port = 1; + card->curr_rd_port = reg->start_rd_port; + card->curr_wr_port = reg->start_wr_port; - card->mp_data_port_mask = DATA_PORT_MASK; + card->mp_data_port_mask = reg->data_port_mask; card->mpa_tx.buf_len = 0; card->mpa_tx.pkt_cnt = 0; card->mpa_tx.start_port = 0; card->mpa_tx.enabled = 1; - card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; card->mpa_rx.buf_len = 0; card->mpa_rx.pkt_cnt = 0; card->mpa_rx.start_port = 0; card->mpa_rx.enabled = 1; - card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; /* Allocate buffers for SDIO MP-A */ - card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL); + card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); if (!card->mp_regs) return -ENOMEM; + /* Allocate skb pointer buffers */ + card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * + card->mp_agg_pkt_limit, GFP_KERNEL); + card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * + card->mp_agg_pkt_limit, GFP_KERNEL); ret = mwifiex_alloc_sdio_mpa_buffers(adapter, SDIO_MP_TX_AGGR_DEF_BUF_SIZE, SDIO_MP_RX_AGGR_DEF_BUF_SIZE); @@ -1705,6 +1887,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) struct sdio_mmc_card *card = adapter->card; kfree(card->mp_regs); + kfree(card->mpa_rx.skb_arr); + kfree(card->mpa_rx.len_arr); kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); } @@ -1716,16 +1900,20 @@ static void mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int i; card->mp_end_port = port; - card->mp_data_port_mask = DATA_PORT_MASK; + card->mp_data_port_mask = reg->data_port_mask; - for (i = 1; i <= MAX_PORT - card->mp_end_port; i++) - card->mp_data_port_mask &= ~(1 << (MAX_PORT - i)); + if (reg->start_wr_port) { + for (i = 1; i <= card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &= + ~(1 << (card->max_ports - i)); + } - card->curr_wr_port = 1; + card->curr_wr_port = reg->start_wr_port; dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n", port, card->mp_data_port_mask); @@ -1831,3 +2019,4 @@ MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 8cc5468654b..6d51dfdd825 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -32,30 +32,37 @@ #define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" #define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" +#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" #define BLOCK_MODE 1 #define BYTE_MODE 0 #define REG_PORT 0 -#define RD_BITMAP_L 0x04 -#define RD_BITMAP_U 0x05 -#define WR_BITMAP_L 0x06 -#define WR_BITMAP_U 0x07 -#define RD_LEN_P0_L 0x08 -#define RD_LEN_P0_U 0x09 #define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff #define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 +#define SDIO_MPA_ADDR_BASE 0x1000 #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 -#define DATA_PORT_MASK 0xfffe -#define MAX_MP_REGS 64 -#define MAX_PORT 16 - -#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8 +#define CMD_PORT_UPLD_INT_MASK (0x1U<<6) +#define CMD_PORT_DNLD_INT_MASK (0x1U<<7) +#define HOST_TERM_CMD53 (0x1U << 2) +#define REG_PORT 0 +#define MEM_PORT 0x10000 +#define CMD_RD_LEN_0 0xB4 +#define CMD_RD_LEN_1 0xB5 +#define CARD_CONFIG_2_1_REG 0xCD +#define CMD53_NEW_MODE (0x1U << 0) +#define CMD_CONFIG_0 0xB8 +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +#define CMD_CONFIG_1 0xB9 +#define CMD_PORT_AUTO_EN (0x1U << 0) +#define CMD_PORT_SLCT 0x8000 +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */ @@ -75,14 +82,8 @@ /* Host Control Registers : Configuration */ #define CONFIGURATION_REG 0x00 -/* Host Control Registers : Host without Command 53 finish host*/ -#define HOST_TO_CARD_EVENT (0x1U << 3) -/* Host Control Registers : Host without Command 53 finish host */ -#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) /* Host Control Registers : Host power up */ #define HOST_POWER_UP (0x1U << 1) -/* Host Control Registers : Host power down */ -#define HOST_POWER_DOWN (0x1U << 0) /* Host Control Registers : Host interrupt mask */ #define HOST_INT_MASK_REG 0x02 @@ -90,8 +91,7 @@ #define UP_LD_HOST_INT_MASK (0x1U) /* Host Control Registers : Download host interrupt mask */ #define DN_LD_HOST_INT_MASK (0x2U) -/* Enable Host interrupt mask */ -#define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) + /* Disable Host interrupt mask */ #define HOST_INT_DISABLE 0xff @@ -104,74 +104,15 @@ /* Host Control Registers : Host interrupt RSR */ #define HOST_INT_RSR_REG 0x01 -/* Host Control Registers : Upload host interrupt RSR */ -#define UP_LD_HOST_INT_RSR (0x1U) -#define SDIO_INT_MASK 0x3F /* Host Control Registers : Host interrupt status */ #define HOST_INT_STATUS_REG 0x28 -/* Host Control Registers : Upload CRC error */ -#define UP_LD_CRC_ERR (0x1U << 2) -/* Host Control Registers : Upload restart */ -#define UP_LD_RESTART (0x1U << 1) -/* Host Control Registers : Download restart */ -#define DN_LD_RESTART (0x1U << 0) - -/* Card Control Registers : Card status register */ -#define CARD_STATUS_REG 0x30 + /* Card Control Registers : Card I/O ready */ #define CARD_IO_READY (0x1U << 3) -/* Card Control Registers : CIS card ready */ -#define CIS_CARD_RDY (0x1U << 2) -/* Card Control Registers : Upload card ready */ -#define UP_LD_CARD_RDY (0x1U << 1) /* Card Control Registers : Download card ready */ #define DN_LD_CARD_RDY (0x1U << 0) -/* Card Control Registers : Host interrupt mask register */ -#define HOST_INTERRUPT_MASK_REG 0x34 -/* Card Control Registers : Host power interrupt mask */ -#define HOST_POWER_INT_MASK (0x1U << 3) -/* Card Control Registers : Abort card interrupt mask */ -#define ABORT_CARD_INT_MASK (0x1U << 2) -/* Card Control Registers : Upload card interrupt mask */ -#define UP_LD_CARD_INT_MASK (0x1U << 1) -/* Card Control Registers : Download card interrupt mask */ -#define DN_LD_CARD_INT_MASK (0x1U << 0) - -/* Card Control Registers : Card interrupt status register */ -#define CARD_INTERRUPT_STATUS_REG 0x38 -/* Card Control Registers : Power up interrupt */ -#define POWER_UP_INT (0x1U << 4) -/* Card Control Registers : Power down interrupt */ -#define POWER_DOWN_INT (0x1U << 3) - -/* Card Control Registers : Card interrupt RSR register */ -#define CARD_INTERRUPT_RSR_REG 0x3c -/* Card Control Registers : Power up RSR */ -#define POWER_UP_RSR (0x1U << 4) -/* Card Control Registers : Power down RSR */ -#define POWER_DOWN_RSR (0x1U << 3) - -/* Card Control Registers : Miscellaneous Configuration Register */ -#define CARD_MISC_CFG_REG 0x6C - -/* Host F1 read base 0 */ -#define HOST_F1_RD_BASE_0 0x0040 -/* Host F1 read base 1 */ -#define HOST_F1_RD_BASE_1 0x0041 -/* Host F1 card ready */ -#define HOST_F1_CARD_RDY 0x0020 - -/* Firmware status 0 register */ -#define CARD_FW_STATUS0_REG 0x60 -/* Firmware status 1 register */ -#define CARD_FW_STATUS1_REG 0x61 -/* Rx length register */ -#define CARD_RX_LEN_REG 0x62 -/* Rx unit register */ -#define CARD_RX_UNIT_REG 0x63 - /* Max retry number of CMD53 write */ #define MAX_WRITE_IOMEM_RETRY 2 @@ -192,7 +133,8 @@ if (a->mpa_tx.start_port <= port) \ a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ else \ - a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \ + (a->max_ports - \ a->mp_end_port))); \ a->mpa_tx.pkt_cnt++; \ } while (0) @@ -201,12 +143,6 @@ #define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) -/* SDIO Tx aggregation port limit ? */ -#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \ - a->mpa_tx.start_port) && (((MAX_PORT - \ - a->mpa_tx.start_port) + a->curr_wr_port) >= \ - SDIO_MP_AGGR_DEF_PKT_LIMIT)) - /* Reset SDIO Tx aggregation buffer parameters */ #define MP_TX_AGGR_BUF_RESET(a) do { \ a->mpa_tx.pkt_cnt = 0; \ @@ -219,12 +155,6 @@ #define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) -/* SDIO Tx aggregation port limit ? */ -#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \ - a->mpa_rx.start_port) && (((MAX_PORT - \ - a->mpa_rx.start_port) + a->curr_rd_port) >= \ - SDIO_MP_AGGR_DEF_PKT_LIMIT)) - /* SDIO Rx aggregation in progress ? */ #define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) @@ -232,20 +162,6 @@ #define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) -/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ -#define MP_RX_AGGR_SETUP(a, skb, port) do { \ - a->mpa_rx.buf_len += skb->len; \ - if (!a->mpa_rx.pkt_cnt) \ - a->mpa_rx.start_port = port; \ - if (a->mpa_rx.start_port <= port) \ - a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \ - else \ - a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \ - a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \ - a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \ - a->mpa_rx.pkt_cnt++; \ -} while (0) - /* Reset SDIO Rx aggregation buffer parameters */ #define MP_RX_AGGR_BUF_RESET(a) do { \ a->mpa_rx.pkt_cnt = 0; \ @@ -254,14 +170,13 @@ a->mpa_rx.start_port = 0; \ } while (0) - /* data structure for SDIO MPA TX */ struct mwifiex_sdio_mpa_tx { /* multiport tx aggregation buffer pointer */ u8 *buf; u32 buf_len; u32 pkt_cnt; - u16 ports; + u32 ports; u16 start_port; u8 enabled; u32 buf_size; @@ -272,11 +187,11 @@ struct mwifiex_sdio_mpa_rx { u8 *buf; u32 buf_len; u32 pkt_cnt; - u16 ports; + u32 ports; u16 start_port; - struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; - u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + struct sk_buff **skb_arr; + u32 *len_arr; u8 enabled; u32 buf_size; @@ -286,15 +201,47 @@ struct mwifiex_sdio_mpa_rx { int mwifiex_bus_register(void); void mwifiex_bus_unregister(void); +struct mwifiex_sdio_card_reg { + u8 start_rd_port; + u8 start_wr_port; + u8 base_0_reg; + u8 base_1_reg; + u8 poll_reg; + u8 host_int_enable; + u8 status_reg_0; + u8 status_reg_1; + u8 sdio_int_mask; + u32 data_port_mask; + u8 max_mp_regs; + u8 rd_bitmap_l; + u8 rd_bitmap_u; + u8 rd_bitmap_1l; + u8 rd_bitmap_1u; + u8 wr_bitmap_l; + u8 wr_bitmap_u; + u8 wr_bitmap_1l; + u8 wr_bitmap_1u; + u8 rd_len_p0_l; + u8 rd_len_p0_u; + u8 card_misc_cfg_reg; +}; + struct sdio_mmc_card { struct sdio_func *func; struct mwifiex_adapter *adapter; - u16 mp_rd_bitmap; - u16 mp_wr_bitmap; + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + bool supports_sdio_new_mode; + bool has_control_mask; + + u32 mp_rd_bitmap; + u32 mp_wr_bitmap; u16 mp_end_port; - u16 mp_data_port_mask; + u32 mp_data_port_mask; u8 curr_rd_port; u8 curr_wr_port; @@ -305,6 +252,98 @@ struct sdio_mmc_card { struct mwifiex_sdio_mpa_rx mpa_rx; }; +struct mwifiex_sdio_device { + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + bool supports_sdio_new_mode; + bool has_control_mask; +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { + .start_rd_port = 1, + .start_wr_port = 1, + .base_0_reg = 0x0040, + .base_1_reg = 0x0041, + .poll_reg = 0x30, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, + .status_reg_0 = 0x60, + .status_reg_1 = 0x61, + .sdio_int_mask = 0x3f, + .data_port_mask = 0x0000fffe, + .max_mp_regs = 64, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .wr_bitmap_l = 0x06, + .wr_bitmap_u = 0x07, + .rd_len_p0_l = 0x08, + .rd_len_p0_u = 0x09, + .card_misc_cfg_reg = 0x6c, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0x60, + .base_1_reg = 0x61, + .poll_reg = 0x50, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .status_reg_0 = 0xc0, + .status_reg_1 = 0xc1, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .max_mp_regs = 184, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .rd_bitmap_1l = 0x06, + .rd_bitmap_1u = 0x07, + .wr_bitmap_l = 0x08, + .wr_bitmap_u = 0x09, + .wr_bitmap_1l = 0x0a, + .wr_bitmap_1u = 0x0b, + .rd_len_p0_l = 0x0c, + .rd_len_p0_u = 0x0d, + .card_misc_cfg_reg = 0xcc, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { + .firmware = SD8786_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { + .firmware = SD8787_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { + .firmware = SD8797_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { + .firmware = SD8897_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8897, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .supports_sdio_new_mode = true, + .has_control_mask = false, +}; + /* * .cmdrsp_complete handler */ @@ -325,4 +364,77 @@ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, return 0; } +static inline bool +mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u8 tmp; + + if (card->curr_rd_port < card->mpa_rx.start_port) { + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_rx.start_port) + + card->curr_rd_port) >= tmp) + return true; + } + + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_rd_port - card->mpa_rx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +static inline bool +mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u16 tmp; + + if (card->curr_wr_port < card->mpa_tx.start_port) { + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_tx.start_port) + + card->curr_wr_port) >= tmp) + return true; + } + + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_wr_port - card->mpa_tx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ +static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, + struct sk_buff *skb, u8 port) +{ + card->mpa_rx.buf_len += skb->len; + + if (!card->mpa_rx.pkt_cnt) + card->mpa_rx.start_port = port; + + if (card->supports_sdio_new_mode) { + card->mpa_rx.ports |= (1 << port); + } else { + if (card->mpa_rx.start_port <= port) + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); + else + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); + } + card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb; + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len; + card->mpa_rx.pkt_cnt++; +} #endif /* _MWIFIEX_SDIO_H */ diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index b193e25977d..8ece4858064 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1134,6 +1134,55 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, return 0; } +/* This function parse cal data from ASCII to hex */ +static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) +{ + u8 *s = src, *d = dst; + + while (s - src < len) { + if (*s && (isspace(*s) || *s == '\t')) { + s++; + continue; + } + if (isxdigit(*s)) { + *d++ = simple_strtol(s, NULL, 16); + s += 2; + } else { + s++; + } + } + + return d - dst; +} + +/* This function prepares command of set_cfg_data. */ +static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action) +{ + struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data; + struct mwifiex_adapter *adapter = priv->adapter; + u32 len, cal_data_offset; + u8 *tmp_cmd = (u8 *)cmd; + + cal_data_offset = S_DS_GEN + sizeof(*cfg_data); + if ((adapter->cal_data->data) && (adapter->cal_data->size > 0)) + len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, + adapter->cal_data->size, + (u8 *)(tmp_cmd + cal_data_offset)); + else + return -1; + + cfg_data->action = cpu_to_le16(cmd_action); + cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL); + cfg_data->data_len = cpu_to_le16(len); + + cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len); + + return 0; +} + /* * This function prepares the commands before sending them to the firmware. * @@ -1152,6 +1201,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_GET_HW_SPEC: ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action); + break; case HostCmd_CMD_MAC_CONTROL: ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, data_buf); @@ -1384,6 +1436,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, */ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) { + struct mwifiex_adapter *adapter = priv->adapter; int ret; u16 enable = true; struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; @@ -1404,6 +1457,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return -1; + + /* Download calibration data to firmware */ + if (adapter->cal_data) { + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, NULL); + if (ret) + return -1; + } + /* Read MAC address from HW */ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC, HostCmd_ACT_GEN_GET, 0, NULL); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 9f990e14966..d85df158cc6 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -818,6 +818,18 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, return 0; } +/* This function handles the command response of set_cfg_data */ +static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (resp->result != HostCmd_RESULT_OK) { + dev_err(priv->adapter->dev, "Cal data cmd resp failed\n"); + return -1; + } + + return 0; +} + /* * This function handles the command responses. * @@ -841,6 +853,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_GET_HW_SPEC: ret = mwifiex_ret_get_hw_spec(priv, resp); break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_ret_cfg_data(priv, resp); + break; case HostCmd_CMD_MAC_CONTROL: break; case HostCmd_CMD_802_11_MAC_ADDRESS: @@ -978,6 +993,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_UAP_BSS_STOP: priv->bss_started = 0; break; + case HostCmd_CMD_UAP_STA_DEAUTH: + break; case HostCmd_CMD_MEF_CFG: break; default: diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 41aafc7454e..ea265ec0e52 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -427,6 +427,17 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) break; + case EVENT_CHANNEL_SWITCH_ANN: + dev_dbg(adapter->dev, "event: Channel Switch Announcement\n"); + priv->csa_expire_time = + jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); + priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel; + ret = mwifiex_send_cmd_async(priv, + HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, + priv->curr_bss_params.bss_descriptor.mac_address); + break; + default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 1a8a19dbd63..206c3e03807 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -104,16 +104,14 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, } else { priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - if (mcast_list->num_multicast_addr) { - dev_dbg(priv->adapter->dev, - "info: Set multicast list=%d\n", - mcast_list->num_multicast_addr); - /* Send multicast addresses to firmware */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list); - } + dev_dbg(priv->adapter->dev, + "info: Set multicast list=%d\n", + mcast_list->num_multicast_addr); + /* Send multicast addresses to firmware */ + ret = mwifiex_send_cmd_async(priv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + mcast_list); } } dev_dbg(priv->adapter->dev, @@ -180,6 +178,9 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, */ bss_desc->disable_11ac = true; + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) + bss_desc->sensed_11h = true; + return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); } @@ -257,30 +258,37 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, } if (priv->bss_mode == NL80211_IFTYPE_STATION) { + u8 config_bands; + /* Infra mode */ ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; - if (bss_desc) { - u8 config_bands = 0; + if (!bss_desc) + return -1; - if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band) - == HostCmd_SCAN_RADIO_TYPE_BG) - config_bands = BAND_B | BAND_G | BAND_GN | - BAND_GAC; - else - config_bands = BAND_A | BAND_AN | BAND_AAC; + if (mwifiex_band_to_radio_type(bss_desc->bss_band) == + HostCmd_SCAN_RADIO_TYPE_BG) + config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC; + else + config_bands = BAND_A | BAND_AN | BAND_AAC; - if (!((config_bands | adapter->fw_bands) & - ~adapter->fw_bands)) - adapter->config_bands = config_bands; - } + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) + adapter->config_bands = config_bands; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; + if (mwifiex_11h_get_csa_closed_channel(priv) == + (u8)bss_desc->channel) { + dev_err(adapter->dev, + "Attempt to reconnect on csa closed chan(%d)\n", + bss_desc->channel); + goto done; + } + dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index b04b1db2910..2de882dead0 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -689,6 +689,23 @@ mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, return 0; } +/* This function prepares AP specific deauth command with mac supplied in + * function parameter. + */ +static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u8 *mac) +{ + struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; + + cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH); + memcpy(sta_deauth->mac, mac, ETH_ALEN); + sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + + S_DS_GEN); + return 0; +} + /* This function prepares the AP specific commands before sending them * to the firmware. * This is a generic function which calls specific command preparation @@ -710,6 +727,10 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, cmd->command = cpu_to_le16(cmd_no); cmd->size = cpu_to_le16(S_DS_GEN); break; + case HostCmd_CMD_UAP_STA_DEAUTH: + if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) + return -1; + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd %#x\n", cmd_no); diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 21c640d3b57..718066577c6 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -107,18 +107,15 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, */ static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) { - struct mwifiex_sta_node *node, *tmp; + struct mwifiex_sta_node *node; unsigned long flags; spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, mac); if (node) { - list_for_each_entry_safe(node, tmp, &priv->sta_list, - list) { - list_del(&node->list); - kfree(node); - } + list_del(&node->list); + kfree(node); } spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); @@ -295,3 +292,19 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) return 0; } + +/* This function deletes station entry from associated station list. + * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream + * tables created for this station are deleted. + */ +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node) +{ + if (priv->ap_11n_enabled && node->is_11n_enabled) { + mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); + mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); + } + mwifiex_del_sta_entry(priv, node->mac_addr); + + return; +} diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 4be3d33ceae..944e8846f6f 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -37,6 +37,9 @@ /* Offset for TOS field in the IP header */ #define IPTOS_OFFSET 5 +static bool enable_tx_amsdu; +module_param(enable_tx_amsdu, bool, 0644); + /* WMM information IE */ static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, 0x00, 0x50, 0xf2, 0x02, @@ -1233,7 +1236,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_send_delba(priv, tid_del, ra, 1); } } - if (mwifiex_is_amsdu_allowed(priv, tid) && + if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 6820fce4016..a3707fd4ef6 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1548,7 +1548,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) if (!priv->pending_tx_pkts) return 0; - retry = 0; + retry = 1; rc = 0; spin_lock_bh(&priv->tx_lock); @@ -1572,13 +1572,19 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) spin_lock_bh(&priv->tx_lock); - if (timeout) { + if (timeout || !priv->pending_tx_pkts) { WARN_ON(priv->pending_tx_pkts); if (retry) wiphy_notice(hw->wiphy, "tx rings drained\n"); break; } + if (retry) { + mwl8k_tx_start(priv); + retry = 0; + continue; + } + if (priv->pending_tx_pkts < oldcount) { wiphy_notice(hw->wiphy, "waiting for tx rings to drain (%d -> %d pkts)\n", @@ -2055,6 +2061,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, mwl8k_remove_stream(hw, stream); spin_unlock(&priv->stream_lock); } + mwl8k_tx_start(priv); spin_unlock_bh(&priv->tx_lock); pci_unmap_single(priv->pdev, dma, skb->len, PCI_DMA_TODEVICE); diff --git a/drivers/net/wireless/orinoco/orinoco_pci.h b/drivers/net/wireless/orinoco/orinoco_pci.h index ea7231af40a..43f5b9f5a0b 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.h +++ b/drivers/net/wireless/orinoco/orinoco_pci.h @@ -38,7 +38,7 @@ static int orinoco_pci_resume(struct pci_dev *pdev) struct net_device *dev = priv->ndev; int err; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s: pci_enable_device failed on resume\n", diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c index 1f9cb55c336..bdfe637953f 100644 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -881,7 +881,8 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv, if (!upriv->udev) { dbg("Device disconnected"); - return -ENODEV; + retval = -ENODEV; + goto exit; } if (upriv->read_urb->status != -EINPROGRESS) diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 978e7eb2656..7fc46f26cf2 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -42,8 +42,7 @@ MODULE_FIRMWARE("3826.arm"); -/* - * gpios should be handled in board files and provided via platform data, +/* gpios should be handled in board files and provided via platform data, * but because it's currently impossible for p54spi to have a header file * in include/linux, let's use module paramaters for now */ @@ -191,8 +190,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev) const struct firmware *eeprom; int ret; - /* - * allow users to customize their eeprom. + /* allow users to customize their eeprom. */ ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev); @@ -285,8 +283,7 @@ static void p54spi_power_on(struct p54s_priv *priv) gpio_set_value(p54spi_gpio_power, 1); enable_irq(gpio_to_irq(p54spi_gpio_irq)); - /* - * need to wait a while before device can be accessed, the length + /* need to wait a while before device can be accessed, the length * is just a guess */ msleep(10); @@ -365,7 +362,8 @@ static int p54spi_rx(struct p54s_priv *priv) /* Firmware may insert up to 4 padding bytes after the lmac header, * but it does not amend the size of SPI data transfer. * Such packets has correct data size in header, thus referencing - * past the end of allocated skb. Reserve extra 4 bytes for this case */ + * past the end of allocated skb. Reserve extra 4 bytes for this case + */ skb = dev_alloc_skb(len + 4); if (!skb) { p54spi_sleep(priv); @@ -383,7 +381,8 @@ static int p54spi_rx(struct p54s_priv *priv) } p54spi_sleep(priv); /* Put additional bytes to compensate for the possible - * alignment-caused truncation */ + * alignment-caused truncation + */ skb_put(skb, 4); if (p54_rx(priv->hw, skb) == 0) @@ -713,27 +712,7 @@ static struct spi_driver p54spi_driver = { .remove = p54spi_remove, }; -static int __init p54spi_init(void) -{ - int ret; - - ret = spi_register_driver(&p54spi_driver); - if (ret < 0) { - printk(KERN_ERR "failed to register SPI driver: %d", ret); - goto out; - } - -out: - return ret; -} - -static void __exit p54spi_exit(void) -{ - spi_unregister_driver(&p54spi_driver); -} - -module_init(p54spi_init); -module_exit(p54spi_exit); +module_spi_driver(p54spi_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>"); diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index f7143733d7e..3d53a09da5a 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1767,33 +1767,45 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { .config = rt2400pci_config, }; -static const struct data_queue_desc rt2400pci_queue_rx = { - .entry_num = 24, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt2400pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2400pci_queue_tx = { - .entry_num = 24, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2400pci_queue_bcn = { - .entry_num = 1, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2400pci_queue_atim = { - .entry_num = 8, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2400pci_ops = { .name = KBUILD_MODNAME, @@ -1801,11 +1813,7 @@ static const struct rt2x00_ops rt2400pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = 0, - .rx = &rt2400pci_queue_rx, - .tx = &rt2400pci_queue_tx, - .bcn = &rt2400pci_queue_bcn, - .atim = &rt2400pci_queue_atim, + .queue_init = rt2400pci_queue_init, .lib = &rt2400pci_rt2x00_ops, .hw = &rt2400pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 77e45b223d1..0ac5c589ddc 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -2056,33 +2056,45 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { .config = rt2500pci_config, }; -static const struct data_queue_desc rt2500pci_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt2500pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2500pci_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2500pci_queue_bcn = { - .entry_num = 1, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2500pci_queue_atim = { - .entry_num = 8, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2500pci_ops = { .name = KBUILD_MODNAME, @@ -2090,11 +2102,7 @@ static const struct rt2x00_ops rt2500pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = 0, - .rx = &rt2500pci_queue_rx, - .tx = &rt2500pci_queue_tx, - .bcn = &rt2500pci_queue_bcn, - .atim = &rt2500pci_queue_atim, + .queue_init = rt2500pci_queue_init, .lib = &rt2500pci_rt2x00_ops, .hw = &rt2500pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index a7f7b365eff..85acc79f68b 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1867,33 +1867,45 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { .config = rt2500usb_config, }; -static const struct data_queue_desc rt2500usb_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; +static void rt2500usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt2500usb_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt2500usb_queue_bcn = { - .entry_num = 1, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb_bcn), -}; + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn); + break; -static const struct data_queue_desc rt2500usb_queue_atim = { - .entry_num = 8, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2500usb_ops = { .name = KBUILD_MODNAME, @@ -1901,11 +1913,7 @@ static const struct rt2x00_ops rt2500usb_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXD_DESC_SIZE, - .rx = &rt2500usb_queue_rx, - .tx = &rt2500usb_queue_tx, - .bcn = &rt2500usb_queue_bcn, - .atim = &rt2500usb_queue_atim, + .queue_init = rt2500usb_queue_init, .lib = &rt2500usb_rt2x00_ops, .hw = &rt2500usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index a7630d5ec89..d78c495a86a 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -100,7 +100,7 @@ #define CSR_REG_BASE 0x1000 #define CSR_REG_SIZE 0x0800 #define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0110 +#define EEPROM_SIZE 0x0200 #define BBP_BASE 0x0000 #define BBP_SIZE 0x00ff #define RF_BASE 0x0004 @@ -2625,11 +2625,13 @@ struct mac_iveiv_entry { /* * DMA descriptor defines. */ -#define TXWI_DESC_SIZE (4 * sizeof(__le32)) -#define RXWI_DESC_SIZE (4 * sizeof(__le32)) -#define TXWI_DESC_SIZE_5592 (5 * sizeof(__le32)) -#define RXWI_DESC_SIZE_5592 (6 * sizeof(__le32)) +#define TXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define TXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) + +#define RXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define RXWI_DESC_SIZE_6WORDS (6 * sizeof(__le32)) + /* * TX WI structure */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 72f32e5caa4..1f80ea5e29d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -840,7 +840,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, unsigned int beacon_base) { int i; - const int txwi_desc_size = rt2x00dev->ops->bcn->winfo_size; + const int txwi_desc_size = rt2x00dev->bcn->winfo_size; /* * For the Beacon base registers we only need to clear @@ -2392,7 +2392,7 @@ static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); - if (info->default_power1 > power_bound) + if (info->default_power2 > power_bound) rt2x00_set_field8(&rfcsr, RFCSR50_TX, power_bound); else rt2x00_set_field8(&rfcsr, RFCSR50_TX, info->default_power2); @@ -2678,30 +2678,53 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, tx_pin = 0; - /* Turn on unused PA or LNA when not using 1T or 1R */ - if (rt2x00dev->default_ant.tx_chain_num == 2) { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + /* Turn on tertiary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, + rf->channel > 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, + rf->channel <= 14); + /* fall-through */ + case 2: + /* Turn on secondary PAs */ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, rf->channel > 14); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, rf->channel <= 14); + /* fall-through */ + case 1: + /* Turn on primary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, + rf->channel > 14); + if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); + else + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, + rf->channel <= 14); + break; } - /* Turn on unused PA or LNA when not using 1T or 1R */ - if (rt2x00dev->default_ant.rx_chain_num == 2) { + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + /* Turn on tertiary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, 1); + /* fall-through */ + case 2: + /* Turn on secondary LNAs */ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); + /* fall-through */ + case 1: + /* Turn on primary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); + break; } - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); - else - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, - rf->channel <= 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14); rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); @@ -3960,379 +3983,577 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x35); } -static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) +static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev) { - int ant, div_mode; u16 eeprom; u8 value; - rt2800_init_bbp_early(rt2x00dev); + rt2800_bbp_read(rt2x00dev, 138, &value); + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + value |= 0x20; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + value &= ~0x02; + rt2800_bbp_write(rt2x00dev, 138, value); +} - rt2800_bbp_read(rt2x00dev, 105, &value); - rt2x00_set_field8(&value, BBP105_MLD, - rt2x00dev->default_ant.rx_chain_num == 2); - rt2800_bbp_write(rt2x00dev, 105, value); +static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x01); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 73, 0x12); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + } + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) + rt2800_bbp_write(rt2x00dev, 84, 0x19); + else + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || + rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090)) + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) +{ + u8 value; rt2800_bbp4_mac_if_ctrl(rt2x00dev); - rt2800_bbp_write(rt2x00dev, 20, 0x06); rt2800_bbp_write(rt2x00dev, 31, 0x08); - rt2800_bbp_write(rt2x00dev, 65, 0x2C); - rt2800_bbp_write(rt2x00dev, 68, 0xDD); - rt2800_bbp_write(rt2x00dev, 69, 0x1A); - rt2800_bbp_write(rt2x00dev, 70, 0x05); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 74, 0x0F); - rt2800_bbp_write(rt2x00dev, 75, 0x4F); + rt2800_bbp_write(rt2x00dev, 75, 0x46); rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x58); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 74, 0x0b); + rt2800_bbp_write(rt2x00dev, 79, 0x18); + rt2800_bbp_write(rt2x00dev, 80, 0x09); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x1c); + + rt2800_bbp_write(rt2x00dev, 106, 0x03); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); + + rt2800_bbp_write(rt2x00dev, 67, 0x24); + rt2800_bbp_write(rt2x00dev, 143, 0x04); + rt2800_bbp_write(rt2x00dev, 142, 0x99); + rt2800_bbp_write(rt2x00dev, 150, 0x30); + rt2800_bbp_write(rt2x00dev, 151, 0x2e); + rt2800_bbp_write(rt2x00dev, 152, 0x20); + rt2800_bbp_write(rt2x00dev, 153, 0x34); + rt2800_bbp_write(rt2x00dev, 154, 0x40); + rt2800_bbp_write(rt2x00dev, 155, 0x3b); + rt2800_bbp_write(rt2x00dev, 253, 0x04); + + rt2800_bbp_read(rt2x00dev, 47, &value); + rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); + rt2800_bbp_write(rt2x00dev, 47, value); + + /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ + rt2800_bbp_read(rt2x00dev, 3, &value); + rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); + rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); + rt2800_bbp_write(rt2x00dev, 3, value); +} + +static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 3, 0x00); + rt2800_bbp_write(rt2x00dev, 4, 0x50); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 47, 0x48); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + rt2800_bbp_write(rt2x00dev, 77, 0x59); - rt2800_bbp_write(rt2x00dev, 84, 0x9A); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + rt2800_bbp_write(rt2x00dev, 86, 0x38); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x02); - rt2800_bbp_write(rt2x00dev, 95, 0x9a); - rt2800_bbp_write(rt2x00dev, 98, 0x12); - rt2800_bbp_write(rt2x00dev, 103, 0xC0); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + rt2800_bbp_write(rt2x00dev, 104, 0x92); - /* FIXME BBP105 owerwrite */ - rt2800_bbp_write(rt2x00dev, 105, 0x3C); - rt2800_bbp_write(rt2x00dev, 106, 0x35); - rt2800_bbp_write(rt2x00dev, 128, 0x12); - rt2800_bbp_write(rt2x00dev, 134, 0xD0); - rt2800_bbp_write(rt2x00dev, 135, 0xF6); - rt2800_bbp_write(rt2x00dev, 137, 0x0F); - /* Initialize GLRT (Generalized Likehood Radio Test) */ - rt2800_init_bbp_5592_glrt(rt2x00dev); + rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 106, 0x05); + + rt2800_bbp_write(rt2x00dev, 120, 0x50); + + rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + /* Set ITxBF timeout to 0x9c40=1000msec */ + rt2800_bbp_write(rt2x00dev, 179, 0x02); + rt2800_bbp_write(rt2x00dev, 180, 0x00); + rt2800_bbp_write(rt2x00dev, 182, 0x40); + rt2800_bbp_write(rt2x00dev, 180, 0x01); + rt2800_bbp_write(rt2x00dev, 182, 0x9c); + rt2800_bbp_write(rt2x00dev, 179, 0x00); + /* Reprogram the inband interface to put right values in RXWI */ + rt2800_bbp_write(rt2x00dev, 142, 0x04); + rt2800_bbp_write(rt2x00dev, 143, 0x3b); + rt2800_bbp_write(rt2x00dev, 142, 0x06); + rt2800_bbp_write(rt2x00dev, 143, 0xa0); + rt2800_bbp_write(rt2x00dev, 142, 0x07); + rt2800_bbp_write(rt2x00dev, 143, 0xa1); + rt2800_bbp_write(rt2x00dev, 142, 0x08); + rt2800_bbp_write(rt2x00dev, 143, 0xa2); + + rt2800_bbp_write(rt2x00dev, 148, 0xc8); +} - rt2800_bbp4_mac_if_ctrl(rt2x00dev); +static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); - ant = (div_mode == 3) ? 1 : 0; - rt2800_bbp_read(rt2x00dev, 152, &value); - if (ant == 0) { - /* Main antenna */ - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); - } else { - /* Auxiliary antenna */ - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); - } - rt2800_bbp_write(rt2x00dev, 152, value); + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) { - rt2800_bbp_read(rt2x00dev, 254, &value); - rt2x00_set_field8(&value, BBP254_BIT7, 1); - rt2800_bbp_write(rt2x00dev, 254, value); - } + rt2800_bbp_write(rt2x00dev, 70, 0x0a); - rt2800_init_freq_calibration(rt2x00dev); + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); - rt2800_bbp_write(rt2x00dev, 84, 0x19); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E)) rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); } -static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) +static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) { - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; + rt2800_bbp_write(rt2x00dev, 31, 0x08); - if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || - rt2800_wait_bbp_ready(rt2x00dev))) - return -EACCES; + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); - if (rt2x00_rt(rt2x00dev, RT5592)) { - rt2800_init_bbp_5592(rt2x00dev); - return 0; - } + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); - if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 3, 0x00); - rt2800_bbp_write(rt2x00dev, 4, 0x50); - } + rt2800_bbp_write(rt2x00dev, 70, 0x0a); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp4_mac_if_ctrl(rt2x00dev); + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); - if (rt2800_is_305x_soc(rt2x00dev) || - rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 31, 0x08); + rt2800_bbp_write(rt2x00dev, 82, 0x62); - if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 47, 0x48); + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) +{ + int ant, div_mode; + u16 eeprom; + u8 value; + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 68, 0x0b); + rt2800_bbp_write(rt2x00dev, 68, 0x0b); - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { - rt2800_bbp_write(rt2x00dev, 69, 0x16); - rt2800_bbp_write(rt2x00dev, 73, 0x12); - } else if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 75, 0x46); - rt2800_bbp_write(rt2x00dev, 76, 0x28); + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); - if (rt2x00_rt(rt2x00dev, RT3290)) - rt2800_bbp_write(rt2x00dev, 77, 0x58); - else - rt2800_bbp_write(rt2x00dev, 77, 0x59); - } else { - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - } + rt2800_bbp_write(rt2x00dev, 77, 0x59); rt2800_bbp_write(rt2x00dev, 70, 0x0a); - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - } else if (rt2800_is_305x_soc(rt2x00dev)) { - rt2800_bbp_write(rt2x00dev, 78, 0x0e); - rt2800_bbp_write(rt2x00dev, 80, 0x08); - } else if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_bbp_write(rt2x00dev, 74, 0x0b); - rt2800_bbp_write(rt2x00dev, 79, 0x18); - rt2800_bbp_write(rt2x00dev, 80, 0x09); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - } else if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 78, 0x0e); - rt2800_bbp_write(rt2x00dev, 80, 0x08); - rt2800_bbp_write(rt2x00dev, 81, 0x37); - } else { - rt2800_bbp_write(rt2x00dev, 81, 0x37); - } + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); rt2800_bbp_write(rt2x00dev, 82, 0x62); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 83, 0x7a); - else - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) - rt2800_bbp_write(rt2x00dev, 84, 0x19); - else if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 84, 0x9a); - else - rt2800_bbp_write(rt2x00dev, 84, 0x99); + rt2800_bbp_write(rt2x00dev, 83, 0x7a); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 86, 0x38); - else - rt2800_bbp_write(rt2x00dev, 86, 0x00); + rt2800_bbp_write(rt2x00dev, 84, 0x9a); - if (rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + if (rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 88, 0x90); rt2800_bbp_write(rt2x00dev, 91, 0x04); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 92, 0x02); - else - rt2800_bbp_write(rt2x00dev, 92, 0x00); + rt2800_bbp_write(rt2x00dev, 92, 0x02); if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 95, 0x9a); rt2800_bbp_write(rt2x00dev, 98, 0x12); } - if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || - rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) || - rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) || - rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2800_is_305x_soc(rt2x00dev)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - else - rt2800_bbp_write(rt2x00dev, 103, 0x00); + rt2800_bbp_write(rt2x00dev, 103, 0xc0); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 104, 0x92); + rt2800_bbp_write(rt2x00dev, 104, 0x92); - if (rt2800_is_305x_soc(rt2x00dev)) - rt2800_bbp_write(rt2x00dev, 105, 0x01); - else if (rt2x00_rt(rt2x00dev, RT3290)) - rt2800_bbp_write(rt2x00dev, 105, 0x1c); - else if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 105, 0x34); - else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 105, 0x3c); - else - rt2800_bbp_write(rt2x00dev, 105, 0x05); + rt2800_bbp_write(rt2x00dev, 105, 0x3c); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390)) + if (rt2x00_rt(rt2x00dev, RT5390)) rt2800_bbp_write(rt2x00dev, 106, 0x03); - else if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 106, 0x05); else if (rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 106, 0x12); else - rt2800_bbp_write(rt2x00dev, 106, 0x35); - - if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 120, 0x50); + WARN_ON(1); - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 128, 0x12); + rt2800_bbp_write(rt2x00dev, 128, 0x12); if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 134, 0xd0); rt2800_bbp_write(rt2x00dev, 135, 0xf6); } - if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 137, 0x0f); + rt2800_disable_unused_dac_adc(rt2x00dev); - if (rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_read(rt2x00dev, 138, &value); + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) - value |= 0x20; - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) - value &= ~0x02; + /* check if this is a Bluetooth combo card */ + if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + u32 reg; - rt2800_bbp_write(rt2x00dev, 138, value); + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); + if (ant == 0) + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); + else if (ant == 1) + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); } - if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_bbp_write(rt2x00dev, 67, 0x24); - rt2800_bbp_write(rt2x00dev, 143, 0x04); - rt2800_bbp_write(rt2x00dev, 142, 0x99); - rt2800_bbp_write(rt2x00dev, 150, 0x30); - rt2800_bbp_write(rt2x00dev, 151, 0x2e); - rt2800_bbp_write(rt2x00dev, 152, 0x20); - rt2800_bbp_write(rt2x00dev, 153, 0x34); - rt2800_bbp_write(rt2x00dev, 154, 0x40); - rt2800_bbp_write(rt2x00dev, 155, 0x3b); - rt2800_bbp_write(rt2x00dev, 253, 0x04); - - rt2800_bbp_read(rt2x00dev, 47, &value); - rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); - rt2800_bbp_write(rt2x00dev, 47, value); - - /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ - rt2800_bbp_read(rt2x00dev, 3, &value); - rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); - rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); - rt2800_bbp_write(rt2x00dev, 3, value); + /* This chip has hardware antenna diversity*/ + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { + rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ + rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ + rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ } - if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 163, 0xbd); - /* Set ITxBF timeout to 0x9c40=1000msec */ - rt2800_bbp_write(rt2x00dev, 179, 0x02); - rt2800_bbp_write(rt2x00dev, 180, 0x00); - rt2800_bbp_write(rt2x00dev, 182, 0x40); - rt2800_bbp_write(rt2x00dev, 180, 0x01); - rt2800_bbp_write(rt2x00dev, 182, 0x9c); - rt2800_bbp_write(rt2x00dev, 179, 0x00); - /* Reprogram the inband interface to put right values in RXWI */ - rt2800_bbp_write(rt2x00dev, 142, 0x04); - rt2800_bbp_write(rt2x00dev, 143, 0x3b); - rt2800_bbp_write(rt2x00dev, 142, 0x06); - rt2800_bbp_write(rt2x00dev, 143, 0xa0); - rt2800_bbp_write(rt2x00dev, 142, 0x07); - rt2800_bbp_write(rt2x00dev, 143, 0xa1); - rt2800_bbp_write(rt2x00dev, 142, 0x08); - rt2800_bbp_write(rt2x00dev, 143, 0xa2); - - rt2800_bbp_write(rt2x00dev, 148, 0xc8); + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + else + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); + rt2800_bbp_write(rt2x00dev, 152, value); + + rt2800_init_freq_calibration(rt2x00dev); +} + +static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) +{ + int ant, div_mode; + u16 eeprom; + u8 value; + + rt2800_init_bbp_early(rt2x00dev); + + rt2800_bbp_read(rt2x00dev, 105, &value); + rt2x00_set_field8(&value, BBP105_MLD, + rt2x00dev->default_ant.rx_chain_num == 2); + rt2800_bbp_write(rt2x00dev, 105, value); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 20, 0x06); + rt2800_bbp_write(rt2x00dev, 31, 0x08); + rt2800_bbp_write(rt2x00dev, 65, 0x2C); + rt2800_bbp_write(rt2x00dev, 68, 0xDD); + rt2800_bbp_write(rt2x00dev, 69, 0x1A); + rt2800_bbp_write(rt2x00dev, 70, 0x05); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 74, 0x0F); + rt2800_bbp_write(rt2x00dev, 75, 0x4F); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + rt2800_bbp_write(rt2x00dev, 77, 0x59); + rt2800_bbp_write(rt2x00dev, 84, 0x9A); + rt2800_bbp_write(rt2x00dev, 86, 0x38); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 95, 0x9a); + rt2800_bbp_write(rt2x00dev, 98, 0x12); + rt2800_bbp_write(rt2x00dev, 103, 0xC0); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + /* FIXME BBP105 owerwrite */ + rt2800_bbp_write(rt2x00dev, 105, 0x3C); + rt2800_bbp_write(rt2x00dev, 106, 0x35); + rt2800_bbp_write(rt2x00dev, 128, 0x12); + rt2800_bbp_write(rt2x00dev, 134, 0xD0); + rt2800_bbp_write(rt2x00dev, 135, 0xF6); + rt2800_bbp_write(rt2x00dev, 137, 0x0F); + + /* Initialize GLRT (Generalized Likehood Radio Test) */ + rt2800_init_bbp_5592_glrt(rt2x00dev); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) { + /* Main antenna */ + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + } else { + /* Auxiliary antenna */ + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); } + rt2800_bbp_write(rt2x00dev, 152, value); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - int ant, div_mode; + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) { + rt2800_bbp_read(rt2x00dev, 254, &value); + rt2x00_set_field8(&value, BBP254_BIT7, 1); + rt2800_bbp_write(rt2x00dev, 254, value); + } - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - div_mode = rt2x00_get_field16(eeprom, - EEPROM_NIC_CONF1_ANT_DIVERSITY); - ant = (div_mode == 3) ? 1 : 0; + rt2800_init_freq_calibration(rt2x00dev); - /* check if this is a Bluetooth combo card */ - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { - u32 reg; - - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); - rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); - if (ant == 0) - rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); - else if (ant == 1) - rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); - } + rt2800_bbp_write(rt2x00dev, 84, 0x19); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); +} - /* This chip has hardware antenna diversity*/ - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { - rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ - rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ - rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ - } +static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; - rt2800_bbp_read(rt2x00dev, 152, &value); - if (ant == 0) - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); - else - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); - rt2800_bbp_write(rt2x00dev, 152, value); + if (rt2800_is_305x_soc(rt2x00dev)) + rt2800_init_bbp_305x_soc(rt2x00dev); - rt2800_init_freq_calibration(rt2x00dev); + switch (rt2x00dev->chip.rt) { + case RT2860: + case RT2872: + case RT2883: + rt2800_init_bbp_28xx(rt2x00dev); + break; + case RT3070: + case RT3071: + case RT3090: + rt2800_init_bbp_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_bbp_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_bbp_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_bbp_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_bbp_3572(rt2x00dev); + break; + case RT5390: + case RT5392: + rt2800_init_bbp_53xx(rt2x00dev); + break; + case RT5592: + rt2800_init_bbp_5592(rt2x00dev); + return; } for (i = 0; i < EEPROM_BBP_SIZE; i++) { @@ -4344,8 +4565,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, reg_id, value); } } - - return 0; } static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev) @@ -5196,9 +5415,11 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) } msleep(1); - if (unlikely(rt2800_init_bbp(rt2x00dev))) + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || + rt2800_wait_bbp_ready(rt2x00dev))) return -EIO; + rt2800_init_bbp(rt2x00dev); rt2800_init_rfcsr(rt2x00dev); if (rt2x00_is_usb(rt2x00dev) && @@ -6056,8 +6277,8 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2); for (i = 14; i < spec->num_channels; i++) { - info[i].default_power1 = default_power1[i]; - info[i].default_power2 = default_power2[i]; + info[i].default_power1 = default_power1[i - 14]; + info[i].default_power2 = default_power2[i - 14]; } } diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 6f4a861af33..00055627eb8 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -637,6 +637,7 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry, struct queue_entry_priv_mmio *entry_priv = entry->priv_data; __le32 *txd = entry_priv->desc; u32 word; + const unsigned int txwi_size = entry->queue->winfo_size; /* * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 @@ -659,14 +660,14 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry, !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W1_BURST, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_SD_LEN0, TXWI_DESC_SIZE); + rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); rt2x00_desc_write(txd, 1, word); word = 0; rt2x00_set_field32(&word, TXD_W2_SD_PTR1, - skbdesc->skb_dma + TXWI_DESC_SIZE); + skbdesc->skb_dma + txwi_size); rt2x00_desc_write(txd, 2, word); word = 0; @@ -1014,7 +1015,7 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) * Since we have only one producer and one consumer we don't * need to lock the kfifo. */ - for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { + for (i = 0; i < rt2x00dev->tx->limit; i++) { rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) @@ -1186,29 +1187,43 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .sta_remove = rt2800_sta_remove, }; -static const struct data_queue_desc rt2800pci_queue_rx = { - .entry_num = 128, - .data_size = AGGREGATION_SIZE, - .desc_size = RXD_DESC_SIZE, - .winfo_size = RXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt2800pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->winfo_size = RXWI_DESC_SIZE_4WORDS; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2800pci_queue_tx = { - .entry_num = 64, - .data_size = AGGREGATION_SIZE, - .desc_size = TXD_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 64; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = TXWI_DESC_SIZE_4WORDS; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2800pci_queue_bcn = { - .entry_num = 8, - .data_size = 0, /* No DMA required for beacons */ - .desc_size = TXD_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 8; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = TXWI_DESC_SIZE_4WORDS; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2800pci_ops = { .name = KBUILD_MODNAME, @@ -1217,10 +1232,7 @@ static const struct rt2x00_ops rt2800pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXWI_DESC_SIZE, - .rx = &rt2800pci_queue_rx, - .tx = &rt2800pci_queue_tx, - .bcn = &rt2800pci_queue_bcn, + .queue_init = rt2800pci_queue_init, .lib = &rt2800pci_rt2x00_ops, .drv = &rt2800pci_rt2800_ops, .hw = &rt2800pci_mac80211_ops, diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index ac854d75bd6..840833b26bf 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -327,7 +327,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) * this limit so reduce the number to prevent errors. */ rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT, - ((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE) + ((rt2x00dev->rx->limit * DATA_FRAME_SIZE) / 1024) - 3); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); @@ -849,85 +849,63 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .sta_remove = rt2800_sta_remove, }; -static const struct data_queue_desc rt2800usb_queue_rx = { - .entry_num = 128, - .data_size = AGGREGATION_SIZE, - .desc_size = RXINFO_DESC_SIZE, - .winfo_size = RXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; - -static const struct data_queue_desc rt2800usb_queue_tx = { - .entry_num = 16, - .data_size = AGGREGATION_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; - -static const struct data_queue_desc rt2800usb_queue_bcn = { - .entry_num = 8, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; +static void rt2800usb_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; -static const struct rt2x00_ops rt2800usb_ops = { - .name = KBUILD_MODNAME, - .drv_data_size = sizeof(struct rt2800_drv_data), - .max_ap_intf = 8, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, - .rx = &rt2800usb_queue_rx, - .tx = &rt2800usb_queue_tx, - .bcn = &rt2800usb_queue_bcn, - .lib = &rt2800usb_rt2x00_ops, - .drv = &rt2800usb_rt2800_ops, - .hw = &rt2800usb_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2800_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; + if (rt2x00_rt(rt2x00dev, RT5592)) { + txwi_size = TXWI_DESC_SIZE_5WORDS; + rxwi_size = RXWI_DESC_SIZE_6WORDS; + } else { + txwi_size = TXWI_DESC_SIZE_4WORDS; + rxwi_size = RXWI_DESC_SIZE_4WORDS; + } -static const struct data_queue_desc rt2800usb_queue_rx_5592 = { - .entry_num = 128, - .data_size = AGGREGATION_SIZE, - .desc_size = RXINFO_DESC_SIZE, - .winfo_size = RXWI_DESC_SIZE_5592, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXINFO_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt2800usb_queue_tx_5592 = { - .entry_num = 16, - .data_size = AGGREGATION_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE_5592, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 16; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt2800usb_queue_bcn_5592 = { - .entry_num = 8, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE_5592, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_BEACON: + queue->limit = 8; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} -static const struct rt2x00_ops rt2800usb_ops_5592 = { +static const struct rt2x00_ops rt2800usb_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), .max_ap_intf = 8, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592, - .rx = &rt2800usb_queue_rx_5592, - .tx = &rt2800usb_queue_tx_5592, - .bcn = &rt2800usb_queue_bcn_5592, + .queue_init = rt2800usb_queue_init, .lib = &rt2800usb_rt2x00_ops, .drv = &rt2800usb_rt2800_ops, .hw = &rt2800usb_mac80211_ops, @@ -1248,15 +1226,15 @@ static struct usb_device_id rt2800usb_device_table[] = { #endif #ifdef CONFIG_RT2800USB_RT55XX /* Arcadyan */ - { USB_DEVICE(0x043e, 0x7a32), .driver_info = 5592 }, + { USB_DEVICE(0x043e, 0x7a32) }, /* AVM GmbH */ - { USB_DEVICE(0x057c, 0x8501), .driver_info = 5592 }, + { USB_DEVICE(0x057c, 0x8501) }, /* D-Link DWA-160-B2 */ - { USB_DEVICE(0x2001, 0x3c1a), .driver_info = 5592 }, + { USB_DEVICE(0x2001, 0x3c1a) }, /* Proware */ - { USB_DEVICE(0x043e, 0x7a13), .driver_info = 5592 }, + { USB_DEVICE(0x043e, 0x7a13) }, /* Ralink */ - { USB_DEVICE(0x148f, 0x5572), .driver_info = 5592 }, + { USB_DEVICE(0x148f, 0x5572) }, #endif #ifdef CONFIG_RT2800USB_UNKNOWN /* @@ -1361,9 +1339,6 @@ MODULE_LICENSE("GPL"); static int rt2800usb_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { - if (id->driver_info == 5592) - return rt2x00usb_probe(usb_intf, &rt2800usb_ops_5592); - return rt2x00usb_probe(usb_intf, &rt2800usb_ops); } diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 7510723a8c3..ee3fc570b11 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -648,11 +648,7 @@ struct rt2x00_ops { const unsigned int eeprom_size; const unsigned int rf_size; const unsigned int tx_queues; - const unsigned int extra_tx_headroom; - const struct data_queue_desc *rx; - const struct data_queue_desc *tx; - const struct data_queue_desc *bcn; - const struct data_queue_desc *atim; + void (*queue_init)(struct data_queue *queue); const struct rt2x00lib_ops *lib; const void *drv; const struct ieee80211_ops *hw; @@ -1010,6 +1006,9 @@ struct rt2x00_dev { */ struct list_head bar_list; spinlock_t bar_list_lock; + + /* Extra TX headroom required for alignment purposes. */ + unsigned int extra_tx_headroom; }; struct rt2x00_bar_list_entry { diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 90dc1433698..b16521e6bf4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -334,7 +334,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, /* * Remove the extra tx headroom from the skb. */ - skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom); + skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); /* * Signal that the TX descriptor is no longer in the skb. @@ -1049,7 +1049,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->extra_tx_headroom = max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, - rt2x00dev->ops->extra_tx_headroom); + rt2x00dev->extra_tx_headroom); /* * Take TX headroom required for alignment into account. @@ -1077,7 +1077,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) */ int kfifo_size = roundup_pow_of_two(rt2x00dev->ops->tx_queues * - rt2x00dev->ops->tx->entry_num * + rt2x00dev->tx->limit * sizeof(u32)); status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, @@ -1256,6 +1256,17 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->wiphy->n_iface_combinations = 1; } +static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev) +{ + if (WARN_ON(!rt2x00dev->tx)) + return 0; + + if (rt2x00_is_usb(rt2x00dev)) + return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size; + + return rt2x00dev->tx[0].winfo_size; +} + /* * driver allocation handlers. */ @@ -1301,27 +1312,10 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) (rt2x00dev->ops->max_ap_intf - 1); /* - * Determine which operating modes are supported, all modes - * which require beaconing, depend on the availability of - * beacon entries. - */ - rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - if (rt2x00dev->ops->bcn->entry_num > 0) - rt2x00dev->hw->wiphy->interface_modes |= - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_WDS); - - rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - - /* * Initialize work. */ rt2x00dev->workqueue = - alloc_ordered_workqueue(wiphy_name(rt2x00dev->hw->wiphy), 0); + alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); if (!rt2x00dev->workqueue) { retval = -ENOMEM; goto exit; @@ -1347,6 +1341,26 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) if (retval) goto exit; + /* Cache TX headroom value */ + rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev); + + /* + * Determine which operating modes are supported, all modes + * which require beaconing, depend on the availability of + * beacon entries. + */ + rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + if (rt2x00dev->bcn->limit > 0) + rt2x00dev->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_WDS); + + rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + /* * Initialize ieee80211 structure. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index dc49e525ae5..76d95deb274 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -105,11 +105,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) goto exit_release_regions; } + pci_enable_msi(pci_dev); + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { rt2x00_probe_err("Failed to allocate hardware\n"); retval = -ENOMEM; - goto exit_release_regions; + goto exit_disable_msi; } pci_set_drvdata(pci_dev, hw); @@ -150,6 +152,9 @@ exit_free_reg: exit_free_device: ieee80211_free_hw(hw); +exit_disable_msi: + pci_disable_msi(pci_dev); + exit_release_regions: pci_release_regions(pci_dev); @@ -174,6 +179,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) rt2x00pci_free_reg(rt2x00dev); ieee80211_free_hw(hw); + pci_disable_msi(pci_dev); + /* * Free the PCI device data. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 2c12311467a..6c0a91ff963 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -542,8 +542,8 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry, /* * Add the requested extra tx headroom in front of the skb. */ - skb_push(entry->skb, rt2x00dev->ops->extra_tx_headroom); - memset(entry->skb->data, 0, rt2x00dev->ops->extra_tx_headroom); + skb_push(entry->skb, rt2x00dev->extra_tx_headroom); + memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom); /* * Call the driver's write_tx_data function, if it exists. @@ -596,7 +596,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct ieee80211_bar *bar = (void *) (entry->skb->data + - rt2x00dev->ops->extra_tx_headroom); + rt2x00dev->extra_tx_headroom); struct rt2x00_bar_list_entry *bar_entry; if (likely(!ieee80211_is_back_req(bar->frame_control))) @@ -1161,8 +1161,7 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev) } } -static int rt2x00queue_alloc_entries(struct data_queue *queue, - const struct data_queue_desc *qdesc) +static int rt2x00queue_alloc_entries(struct data_queue *queue) { struct queue_entry *entries; unsigned int entry_size; @@ -1170,16 +1169,10 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue, rt2x00queue_reset(queue); - queue->limit = qdesc->entry_num; - queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10); - queue->data_size = qdesc->data_size; - queue->desc_size = qdesc->desc_size; - queue->winfo_size = qdesc->winfo_size; - /* * Allocate all queue entries. */ - entry_size = sizeof(*entries) + qdesc->priv_size; + entry_size = sizeof(*entries) + queue->priv_size; entries = kcalloc(queue->limit, entry_size, GFP_KERNEL); if (!entries) return -ENOMEM; @@ -1195,7 +1188,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue, entries[i].entry_idx = i; entries[i].priv_data = QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit, - sizeof(*entries), qdesc->priv_size); + sizeof(*entries), queue->priv_size); } #undef QUEUE_ENTRY_PRIV_OFFSET @@ -1237,23 +1230,22 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) struct data_queue *queue; int status; - status = rt2x00queue_alloc_entries(rt2x00dev->rx, rt2x00dev->ops->rx); + status = rt2x00queue_alloc_entries(rt2x00dev->rx); if (status) goto exit; tx_queue_for_each(rt2x00dev, queue) { - status = rt2x00queue_alloc_entries(queue, rt2x00dev->ops->tx); + status = rt2x00queue_alloc_entries(queue); if (status) goto exit; } - status = rt2x00queue_alloc_entries(rt2x00dev->bcn, rt2x00dev->ops->bcn); + status = rt2x00queue_alloc_entries(rt2x00dev->bcn); if (status) goto exit; if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) { - status = rt2x00queue_alloc_entries(rt2x00dev->atim, - rt2x00dev->ops->atim); + status = rt2x00queue_alloc_entries(rt2x00dev->atim); if (status) goto exit; } @@ -1297,6 +1289,10 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, queue->aifs = 2; queue->cw_min = 5; queue->cw_max = 10; + + rt2x00dev->ops->queue_init(queue); + + queue->threshold = DIV_ROUND_UP(queue->limit, 10); } int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index 4a7b34e9261..ebe11722497 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -453,6 +453,7 @@ enum data_queue_flags { * @cw_max: The cw max value for outgoing frames (field ignored in RX queue). * @data_size: Maximum data size for the frames in this queue. * @desc_size: Hardware descriptor size for the data in this queue. + * @priv_size: Size of per-queue_entry private data. * @usb_endpoint: Device endpoint used for communication (USB only) * @usb_maxpacket: Max packet size for given endpoint (USB only) */ @@ -481,31 +482,13 @@ struct data_queue { unsigned short data_size; unsigned char desc_size; unsigned char winfo_size; + unsigned short priv_size; unsigned short usb_endpoint; unsigned short usb_maxpacket; }; /** - * struct data_queue_desc: Data queue description - * - * The information in this structure is used by drivers - * to inform rt2x00lib about the creation of the data queue. - * - * @entry_num: Maximum number of entries for a queue. - * @data_size: Maximum data size for the frames in this queue. - * @desc_size: Hardware descriptor size for the data in this queue. - * @priv_size: Size of per-queue_entry private data. - */ -struct data_queue_desc { - unsigned short entry_num; - unsigned short data_size; - unsigned char desc_size; - unsigned char winfo_size; - unsigned short priv_size; -}; - -/** * queue_end - Return pointer to the last queue (HELPER MACRO). * @__dev: Pointer to &struct rt2x00_dev * diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 0dc8180e251..54d3ddfc988 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2175,7 +2175,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) * that the TX_STA_FIFO stack has a size of 16. We stick to our * tx ring size for now. */ - for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { + for (i = 0; i < rt2x00dev->tx->limit; i++) { rt2x00mmio_register_read(rt2x00dev, STA_CSR4, ®); if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) break; @@ -2825,7 +2825,8 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); for (i = 14; i < spec->num_channels; i++) { info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + info[i].default_power1 = + TXPOWER_FROM_DEV(tx_power[i - 14]); } } @@ -3025,26 +3026,40 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { .config = rt61pci_config, }; -static const struct data_queue_desc rt61pci_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt61pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt61pci_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt61pci_queue_bcn = { - .entry_num = 4, - .data_size = 0, /* No DMA required for beacons */ - .desc_size = TXINFO_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 4; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt61pci_ops = { .name = KBUILD_MODNAME, @@ -3052,10 +3067,7 @@ static const struct rt2x00_ops rt61pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = 0, - .rx = &rt61pci_queue_rx, - .tx = &rt61pci_queue_tx, - .bcn = &rt61pci_queue_bcn, + .queue_init = rt61pci_queue_init, .lib = &rt61pci_rt2x00_ops, .hw = &rt61pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 377e09bb0b8..1d3880e09a1 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2167,7 +2167,8 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); for (i = 14; i < spec->num_channels; i++) { info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + info[i].default_power1 = + TXPOWER_FROM_DEV(tx_power[i - 14]); } } @@ -2359,26 +2360,40 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { .config = rt73usb_config, }; -static const struct data_queue_desc rt73usb_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; +static void rt73usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt73usb_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt73usb_queue_bcn = { - .entry_num = 4, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXINFO_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_BEACON: + queue->limit = 4; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt73usb_ops = { .name = KBUILD_MODNAME, @@ -2386,10 +2401,7 @@ static const struct rt2x00_ops rt73usb_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXD_DESC_SIZE, - .rx = &rt73usb_queue_rx, - .tx = &rt73usb_queue_tx, - .bcn = &rt73usb_queue_bcn, + .queue_init = rt73usb_queue_init, .lib = &rt73usb_rt2x00_ops, .hw = &rt73usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index af59dd5718e..9d558ac77b0 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -380,7 +380,7 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw) /* <2> work queue */ rtlpriv->works.hw = hw; - rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0); + rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name); INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq, (void *)rtl_watchdog_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq, @@ -1817,7 +1817,7 @@ static ssize_t rtl_store_debug_level(struct device *d, unsigned long val; int ret; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret) { printk(KERN_DEBUG "%s is not in hex or decimal form.\n", buf); } else { diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c index 953f1a0f853..2119313a737 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c @@ -104,7 +104,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, tx_agc[RF90_PATH_A] = 0x10101010; tx_agc[RF90_PATH_B] = 0x10101010; } else if (rtlpriv->dm.dynamic_txhighpower_lvl == - TXHIGHPWRLEVEL_LEVEL1) { + TXHIGHPWRLEVEL_LEVEL2) { tx_agc[RF90_PATH_A] = 0x00000000; tx_agc[RF90_PATH_B] = 0x00000000; } else{ diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 826f085c29d..2bd59852621 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -359,6 +359,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/ {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/ + {RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/ {RTL_USB_DEVICE(0x7392, 0x7822, rtl92cu_hal_cfg)}, /*Edimax -Edimax*/ {} }; diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index 19a76553260..47875ba09ff 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -842,7 +842,7 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( long val_y, ele_c = 0; u8 ofdm_index[2]; s8 cck_index = 0; - u8 ofdm_index_old[2]; + u8 ofdm_index_old[2] = {0, 0}; s8 cck_index_old = 0; u8 index; int i; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c index e4c4cdc3eb6..d9ee2efffe5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c @@ -251,7 +251,7 @@ static struct rtl_hal_cfg rtl8723ae_hal_cfg = { .bar_id = 2, .write_readback = true, .name = "rtl8723ae_pci", - .fw_name = "rtlwifi/rtl8723aefw.bin", + .fw_name = "rtlwifi/rtl8723fw.bin", .ops = &rtl8723ae_hal_ops, .mod_params = &rtl8723ae_mod_params, .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, @@ -353,8 +353,8 @@ MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8723E 802.11n PCI wireless"); -MODULE_FIRMWARE("rtlwifi/rtl8723aefw.bin"); -MODULE_FIRMWARE("rtlwifi/rtl8723aefw_B.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8723fw.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8723fw_B.bin"); module_param_named(swenc, rtl8723ae_mod_params.sw_crypto, bool, 0444); module_param_named(debug, rtl8723ae_mod_params.debug, int, 0444); diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index 4c67c2f9ea7..c7dc6feab2f 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl) memset(&t, 0, sizeof(t)); spi_message_init(&m); - /* - * Set WSPI_INIT_COMMAND + /* Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ cmd[2] = 0xff; @@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi) wl->if_ops = &wl1251_spi_ops; /* This is the only SPI value that we need to set here, the rest - * comes from the board-peripherals file */ + * comes from the board-peripherals file + */ spi->bits_per_word = 32; ret = spi_setup(spi); @@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = { .remove = wl1251_spi_remove, }; -static int __init wl1251_spi_init(void) -{ - int ret; - - ret = spi_register_driver(&wl1251_spi_driver); - if (ret < 0) { - wl1251_error("failed to register spi driver: %d", ret); - goto out; - } - -out: - return ret; -} - -static void __exit wl1251_spi_exit(void) -{ - spi_unregister_driver(&wl1251_spi_driver); - - wl1251_notice("unloaded"); -} - -module_init(wl1251_spi_init); -module_exit(wl1251_spi_exit); +module_spi_driver(wl1251_spi_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 9fa692d1102..7aa0eb848c5 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/ip.h> #include <linux/firmware.h> +#include <linux/etherdevice.h> #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" @@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_PHY_INIT] = { - .mem = { .start = 0x80926000, - .size = sizeof(struct wl18xx_mac_and_phy_params) }, + .mem = { .start = WL18XX_PHY_INIT_MEM_ADDR, + .size = WL18XX_PHY_INIT_MEM_SIZE }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, @@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl) u32 tmp; int ret; + BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) > + WL18XX_PHY_INIT_MEM_SIZE); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) goto out; @@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); + if (ret < 0) + goto out; + + /* + * Workaround for FDSP code RAM corruption (needed for PG2.1 + * and newer; for older chips it's a NOP). Change FDSP clock + * settings so that it's muxed to the ATGP clock instead of + * its own clock. + */ + + ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); + if (ret < 0) + goto out; + + /* disable FDSP clock */ + ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, + MEM_FDSP_CLK_120_DISABLE); + if (ret < 0) + goto out; + + /* set ATPG clock toward FDSP Code RAM rather than its own clock */ + ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, + MEM_FDSP_CODERAM_FUNC_CLK_SEL); + if (ret < 0) + goto out; + + /* re-enable FDSP clock */ + ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, + MEM_FDSP_CLK_120_ENABLE); out: return ret; @@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl) ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = (mac1 & 0xffffff); + if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) { + u8 mac[ETH_ALEN]; + + eth_random_addr(mac); + + wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2]; + wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5]; + wl1271_warning("MAC address from fuse not available, using random locally administered addresses."); + } + ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 6306e04cd25..05dd8bad274 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -38,6 +38,9 @@ #define WL18XX_REG_BOOT_PART_SIZE 0x00014578 #define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 +#define WL18XX_PHY_END_MEM_ADDR 0x8093CA44 +#define WL18XX_PHY_INIT_MEM_SIZE \ + (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR) #define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) #define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) @@ -217,4 +220,16 @@ static const char * const rdl_names[] = { [RDL_4_SP] = "1897 MIMO", }; +/* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */ +#define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40 + +/* command to disable FDSP clock */ +#define MEM_FDSP_CLK_120_DISABLE 0x80000000 + +/* command to set ATPG clock toward FDSP Code RAM rather than its own clock */ +#define MEM_FDSP_CODERAM_FUNC_CLK_SEL 0xC0000000 + +/* command to re-enable FDSP clock */ +#define MEM_FDSP_CLK_120_ENABLE 0x40000000 + #endif /* __REG_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/Makefile b/drivers/net/wireless/ti/wlcore/Makefile index b21398f6c3e..4f23931d7bd 100644 --- a/drivers/net/wireless/ti/wlcore/Makefile +++ b/drivers/net/wireless/ti/wlcore/Makefile @@ -1,5 +1,5 @@ wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ - boot.o init.o debugfs.o scan.o + boot.o init.o debugfs.o scan.o sysfs.o wlcore_spi-objs = spi.o wlcore_sdio-objs = sdio.o diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index c3e1f79c785..e17630c2a84 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -1056,7 +1056,7 @@ static ssize_t dev_mem_read(struct file *file, return -EINVAL; memset(&part, 0, sizeof(part)); - part.mem.start = file->f_pos; + part.mem.start = *ppos; part.mem.size = bytes; buf = kmalloc(bytes, GFP_KERNEL); @@ -1137,7 +1137,7 @@ static ssize_t dev_mem_write(struct file *file, const char __user *user_buf, return -EINVAL; memset(&part, 0, sizeof(part)); - part.mem.start = file->f_pos; + part.mem.start = *ppos; part.mem.size = bytes; buf = kmalloc(bytes, GFP_KERNEL); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 953111a502e..b8db55c868c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1,10 +1,9 @@ /* - * This file is part of wl1271 + * This file is part of wlcore * * Copyright (C) 2008-2010 Nokia Corporation - * - * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * Copyright (C) 2011-2013 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,34 +23,23 @@ #include <linux/module.h> #include <linux/firmware.h> -#include <linux/delay.h> -#include <linux/spi/spi.h> -#include <linux/crc32.h> #include <linux/etherdevice.h> #include <linux/vmalloc.h> -#include <linux/platform_device.h> -#include <linux/slab.h> #include <linux/wl12xx.h> -#include <linux/sched.h> #include <linux/interrupt.h> #include "wlcore.h" #include "debug.h" #include "wl12xx_80211.h" #include "io.h" -#include "event.h" #include "tx.h" -#include "rx.h" #include "ps.h" #include "init.h" #include "debugfs.h" -#include "cmd.h" -#include "boot.h" #include "testmode.h" #include "scan.h" #include "hw_ops.h" - -#define WL1271_BOOT_RETRIES 3 +#include "sysfs.h" #define WL1271_BOOT_RETRIES 3 @@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, static void wlcore_op_stop_locked(struct wl1271 *wl); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); -static int wl12xx_set_authorized(struct wl1271 *wl, - struct wl12xx_vif *wlvif) +static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; @@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) { - wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + + wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*wl->fw_status_2) + wl->fw_status_priv_len, GFP_KERNEL); if (!wl->fw_status_1) @@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl) (((u8 *) wl->fw_status_1) + WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); - wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); + wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); if (!wl->tx_res_if) { kfree(wl->fw_status_1); return -ENOMEM; @@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl, return 0; } -static void wl1271_configure_resume(struct wl1271 *wl, - struct wl12xx_vif *wlvif) +static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret = 0; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; @@ -2603,6 +2589,7 @@ unlock: cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); + cancel_delayed_work_sync(&wlvif->channel_switch_work); mutex_lock(&wl->mutex); } @@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - /* the default WEP key needs to be configured at least once */ - if (key_type == KEY_WEP) { - ret = wl12xx_cmd_set_default_wep_key(wl, - wlvif->default_key, - wlvif->sta.hlid); - if (ret < 0) - return ret; - } } return 0; @@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, } EXPORT_SYMBOL_GPL(wlcore_set_key); +static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + int key_idx) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + int ret; + + wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d", + key_idx); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EAGAIN; + goto out_unlock; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + + wlvif->default_key = key_idx; + + /* the default WEP key needs to be configured at least once */ + if (wlvif->encryption_type == KEY_WEP) { + ret = wl12xx_cmd_set_default_wep_key(wl, + key_idx, + wlvif->sta.hlid); + if (ret < 0) + goto out_sleep; + } + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out_unlock: + mutex_unlock(&wl->mutex); +} + void wlcore_regdomain_config(struct wl1271 *wl) { int ret; @@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, struct ieee80211_hdr *hdr; u32 min_rate; int ret; - int ieoffset = offsetof(struct ieee80211_mgmt, - u.beacon.variable); + int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); u16 tmpl_id; @@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } /* Handle new association with HT. Do this after join. */ - if (sta_exists && - (changed & BSS_CHANGED_HT)) { + if (sta_exists) { bool enabled = bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; @@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = { .ampdu_action = wl1271_op_ampdu_action, .tx_frames_pending = wl1271_tx_frames_pending, .set_bitrate_mask = wl12xx_set_bitrate_mask, + .set_default_unicast_key = wl1271_op_set_default_key_idx, .channel_switch = wl12xx_op_channel_switch, .flush = wlcore_op_flush, .remain_on_channel = wlcore_op_remain_on_channel, @@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band) return idx; } -static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wl1271 *wl = dev_get_drvdata(dev); - ssize_t len; - - len = PAGE_SIZE; - - mutex_lock(&wl->mutex); - len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", - wl->sg_enabled); - mutex_unlock(&wl->mutex); - - return len; - -} - -static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct wl1271 *wl = dev_get_drvdata(dev); - unsigned long res; - int ret; - - ret = kstrtoul(buf, 10, &res); - if (ret < 0) { - wl1271_warning("incorrect value written to bt_coex_mode"); - return count; - } - - mutex_lock(&wl->mutex); - - res = !!res; - - if (res == wl->sg_enabled) - goto out; - - wl->sg_enabled = res; - - if (unlikely(wl->state != WLCORE_STATE_ON)) - goto out; - - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - - wl1271_acx_sg_enable(wl, wl->sg_enabled); - wl1271_ps_elp_sleep(wl); - - out: - mutex_unlock(&wl->mutex); - return count; -} - -static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, - wl1271_sysfs_show_bt_coex_state, - wl1271_sysfs_store_bt_coex_state); - -static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wl1271 *wl = dev_get_drvdata(dev); - ssize_t len; - - len = PAGE_SIZE; - - mutex_lock(&wl->mutex); - if (wl->hw_pg_ver >= 0) - len = snprintf(buf, len, "%d\n", wl->hw_pg_ver); - else - len = snprintf(buf, len, "n/a\n"); - mutex_unlock(&wl->mutex); - - return len; -} - -static DEVICE_ATTR(hw_pg_ver, S_IRUGO, - wl1271_sysfs_show_hw_pg_ver, NULL); - -static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buffer, loff_t pos, size_t count) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct wl1271 *wl = dev_get_drvdata(dev); - ssize_t len; - int ret; - - ret = mutex_lock_interruptible(&wl->mutex); - if (ret < 0) - return -ERESTARTSYS; - - /* Let only one thread read the log at a time, blocking others */ - while (wl->fwlog_size == 0) { - DEFINE_WAIT(wait); - - prepare_to_wait_exclusive(&wl->fwlog_waitq, - &wait, - TASK_INTERRUPTIBLE); - - if (wl->fwlog_size != 0) { - finish_wait(&wl->fwlog_waitq, &wait); - break; - } - - mutex_unlock(&wl->mutex); - - schedule(); - finish_wait(&wl->fwlog_waitq, &wait); - - if (signal_pending(current)) - return -ERESTARTSYS; - - ret = mutex_lock_interruptible(&wl->mutex); - if (ret < 0) - return -ERESTARTSYS; - } - - /* Check if the fwlog is still valid */ - if (wl->fwlog_size < 0) { - mutex_unlock(&wl->mutex); - return 0; - } - - /* Seeking is not supported - old logs are not kept. Disregard pos. */ - len = min(count, (size_t)wl->fwlog_size); - wl->fwlog_size -= len; - memcpy(buffer, wl->fwlog, len); - - /* Make room for new messages */ - memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); - - mutex_unlock(&wl->mutex); - - return len; -} - -static struct bin_attribute fwlog_attr = { - .attr = {.name = "fwlog", .mode = S_IRUSR}, - .read = wl1271_sysfs_read_fwlog, -}; - static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) { int i; @@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) return 0; } -#define WL1271_DEFAULT_CHANNEL 0 - struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, u32 mbox_size) { @@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, goto err_hw; } - wl->channel = WL1271_DEFAULT_CHANNEL; + wl->channel = 0; wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->band = IEEE80211_BAND_2GHZ; @@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl) wake_up_interruptible_all(&wl->fwlog_waitq); mutex_unlock(&wl->mutex); - device_remove_bin_file(wl->dev, &fwlog_attr); - - device_remove_file(wl->dev, &dev_attr_hw_pg_ver); + wlcore_sysfs_free(wl); - device_remove_file(wl->dev, &dev_attr_bt_coex_state); kfree(wl->buffer_32); kfree(wl->mbox); free_page((unsigned long)wl->fwlog); @@ -6018,6 +5886,15 @@ int wlcore_free_hw(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wlcore_free_hw); +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support wlcore_wowlan_support = { + .flags = WIPHY_WOWLAN_ANY, + .n_patterns = WL1271_MAX_RX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = WL1271_RX_FILTER_MAX_PATTERN_SIZE, +}; +#endif + static void wlcore_nvs_cb(const struct firmware *fw, void *context) { struct wl1271 *wl = context; @@ -6071,14 +5948,8 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) if (!ret) { wl->irq_wake_enabled = true; device_init_wakeup(wl->dev, 1); - if (pdata->pwr_in_suspend) { - wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; - wl->hw->wiphy->wowlan.n_patterns = - WL1271_MAX_RX_FILTERS; - wl->hw->wiphy->wowlan.pattern_min_len = 1; - wl->hw->wiphy->wowlan.pattern_max_len = - WL1271_RX_FILTER_MAX_PATTERN_SIZE; - } + if (pdata->pwr_in_suspend) + wl->hw->wiphy->wowlan = &wlcore_wowlan_support; } #endif disable_irq(wl->irq); @@ -6101,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) if (ret) goto out_irq; - /* Create sysfs file to control bt coex state */ - ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); - if (ret < 0) { - wl1271_error("failed to create sysfs file bt_coex_state"); + ret = wlcore_sysfs_init(wl); + if (ret) goto out_unreg; - } - - /* Create sysfs file to get HW PG version */ - ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); - if (ret < 0) { - wl1271_error("failed to create sysfs file hw_pg_ver"); - goto out_bt_coex_state; - } - - /* Create sysfs file for the FW log */ - ret = device_create_bin_file(wl->dev, &fwlog_attr); - if (ret < 0) { - wl1271_error("failed to create sysfs file fwlog"); - goto out_hw_pg_ver; - } wl->initialized = true; goto out; -out_hw_pg_ver: - device_remove_file(wl->dev, &dev_attr_hw_pg_ver); - -out_bt_coex_state: - device_remove_file(wl->dev, &dev_attr_bt_coex_state); - out_unreg: wl1271_unregister_hw(wl); diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 9654577efd0..98066d40c2a 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -110,7 +110,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) DECLARE_COMPLETION_ONSTACK(compl); unsigned long flags; int ret; - u32 start_time = jiffies; + unsigned long start_time = jiffies; bool pending = false; /* diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index e2644783268..1b0cd98e35f 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -434,19 +434,7 @@ static struct spi_driver wl1271_spi_driver = { .remove = wl1271_remove, }; -static int __init wl1271_init(void) -{ - return spi_register_driver(&wl1271_spi_driver); -} - -static void __exit wl1271_exit(void) -{ - spi_unregister_driver(&wl1271_spi_driver); -} - -module_init(wl1271_init); -module_exit(wl1271_exit); - +module_spi_driver(wl1271_spi_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c new file mode 100644 index 00000000000..8e583497940 --- /dev/null +++ b/drivers/net/wireless/ti/wlcore/sysfs.c @@ -0,0 +1,216 @@ +/* + * This file is part of wlcore + * + * Copyright (C) 2013 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wlcore.h" +#include "debug.h" +#include "ps.h" +#include "sysfs.h" + +static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + ssize_t len; + + len = PAGE_SIZE; + + mutex_lock(&wl->mutex); + len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", + wl->sg_enabled); + mutex_unlock(&wl->mutex); + + return len; + +} + +static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + unsigned long res; + int ret; + + ret = kstrtoul(buf, 10, &res); + if (ret < 0) { + wl1271_warning("incorrect value written to bt_coex_mode"); + return count; + } + + mutex_lock(&wl->mutex); + + res = !!res; + + if (res == wl->sg_enabled) + goto out; + + wl->sg_enabled = res; + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl1271_acx_sg_enable(wl, wl->sg_enabled); + wl1271_ps_elp_sleep(wl); + + out: + mutex_unlock(&wl->mutex); + return count; +} + +static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, + wl1271_sysfs_show_bt_coex_state, + wl1271_sysfs_store_bt_coex_state); + +static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + ssize_t len; + + len = PAGE_SIZE; + + mutex_lock(&wl->mutex); + if (wl->hw_pg_ver >= 0) + len = snprintf(buf, len, "%d\n", wl->hw_pg_ver); + else + len = snprintf(buf, len, "n/a\n"); + mutex_unlock(&wl->mutex); + + return len; +} + +static DEVICE_ATTR(hw_pg_ver, S_IRUGO, + wl1271_sysfs_show_hw_pg_ver, NULL); + +static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct wl1271 *wl = dev_get_drvdata(dev); + ssize_t len; + int ret; + + ret = mutex_lock_interruptible(&wl->mutex); + if (ret < 0) + return -ERESTARTSYS; + + /* Let only one thread read the log at a time, blocking others */ + while (wl->fwlog_size == 0) { + DEFINE_WAIT(wait); + + prepare_to_wait_exclusive(&wl->fwlog_waitq, + &wait, + TASK_INTERRUPTIBLE); + + if (wl->fwlog_size != 0) { + finish_wait(&wl->fwlog_waitq, &wait); + break; + } + + mutex_unlock(&wl->mutex); + + schedule(); + finish_wait(&wl->fwlog_waitq, &wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + ret = mutex_lock_interruptible(&wl->mutex); + if (ret < 0) + return -ERESTARTSYS; + } + + /* Check if the fwlog is still valid */ + if (wl->fwlog_size < 0) { + mutex_unlock(&wl->mutex); + return 0; + } + + /* Seeking is not supported - old logs are not kept. Disregard pos. */ + len = min(count, (size_t)wl->fwlog_size); + wl->fwlog_size -= len; + memcpy(buffer, wl->fwlog, len); + + /* Make room for new messages */ + memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); + + mutex_unlock(&wl->mutex); + + return len; +} + +static struct bin_attribute fwlog_attr = { + .attr = {.name = "fwlog", .mode = S_IRUSR}, + .read = wl1271_sysfs_read_fwlog, +}; + +int wlcore_sysfs_init(struct wl1271 *wl) +{ + int ret; + + /* Create sysfs file to control bt coex state */ + ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); + if (ret < 0) { + wl1271_error("failed to create sysfs file bt_coex_state"); + goto out; + } + + /* Create sysfs file to get HW PG version */ + ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); + if (ret < 0) { + wl1271_error("failed to create sysfs file hw_pg_ver"); + goto out_bt_coex_state; + } + + /* Create sysfs file for the FW log */ + ret = device_create_bin_file(wl->dev, &fwlog_attr); + if (ret < 0) { + wl1271_error("failed to create sysfs file fwlog"); + goto out_hw_pg_ver; + } + + goto out; + +out_hw_pg_ver: + device_remove_file(wl->dev, &dev_attr_hw_pg_ver); + +out_bt_coex_state: + device_remove_file(wl->dev, &dev_attr_bt_coex_state); + +out: + return ret; +} + +void wlcore_sysfs_free(struct wl1271 *wl) +{ + device_remove_bin_file(wl->dev, &fwlog_attr); + + device_remove_file(wl->dev, &dev_attr_hw_pg_ver); + + device_remove_file(wl->dev, &dev_attr_bt_coex_state); +} diff --git a/drivers/net/wireless/ti/wlcore/sysfs.h b/drivers/net/wireless/ti/wlcore/sysfs.h new file mode 100644 index 00000000000..c1488921839 --- /dev/null +++ b/drivers/net/wireless/ti/wlcore/sysfs.h @@ -0,0 +1,28 @@ +/* + * This file is part of wlcore + * + * Copyright (C) 2013 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SYSFS_H__ +#define __SYSFS_H__ + +int wlcore_sysfs_init(struct wl1271 *wl); +void wlcore_sysfs_free(struct wl1271 *wl); + +#endif diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 004d02e71f0..7e93fe63a2c 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104); - if (unlikely(is_wep && wlvif->default_key != idx)) { + if (WARN_ON(is_wep && wlvif->default_key != idx)) { ret = wl1271_set_default_wep_key(wl, wlvif, idx); if (ret < 0) return ret; diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 9d7f1723dd8..8a4d77ee9c5 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -57,8 +57,12 @@ struct xenvif { u8 fe_dev_addr[6]; - /* Physical parameters of the comms window. */ - unsigned int irq; + /* When feature-split-event-channels = 0, tx_irq = rx_irq. */ + unsigned int tx_irq; + unsigned int rx_irq; + /* Only used when feature-split-event-channels = 1 */ + char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */ + char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */ /* List of frontends to notify after a batch of frames sent. */ struct list_head notify_list; @@ -113,13 +117,15 @@ struct xenvif *xenvif_alloc(struct device *parent, unsigned int handle); int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, - unsigned long rx_ring_ref, unsigned int evtchn); + unsigned long rx_ring_ref, unsigned int tx_evtchn, + unsigned int rx_evtchn); void xenvif_disconnect(struct xenvif *vif); void xenvif_get(struct xenvif *vif); void xenvif_put(struct xenvif *vif); int xenvif_xenbus_init(void); +void xenvif_xenbus_fini(void); int xenvif_schedulable(struct xenvif *vif); @@ -157,4 +163,6 @@ void xenvif_carrier_off(struct xenvif *vif); /* Returns number of ring slots required to send an skb to the frontend */ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb); +extern bool separate_tx_rx_irq; + #endif /* __XEN_NETBACK__COMMON_H__ */ diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index d9841416848..087d2db0389 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -60,21 +60,39 @@ static int xenvif_rx_schedulable(struct xenvif *vif) return xenvif_schedulable(vif) && !xen_netbk_rx_ring_full(vif); } -static irqreturn_t xenvif_interrupt(int irq, void *dev_id) +static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id) { struct xenvif *vif = dev_id; if (vif->netbk == NULL) - return IRQ_NONE; + return IRQ_HANDLED; xen_netbk_schedule_xenvif(vif); + return IRQ_HANDLED; +} + +static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id) +{ + struct xenvif *vif = dev_id; + + if (vif->netbk == NULL) + return IRQ_HANDLED; + if (xenvif_rx_schedulable(vif)) netif_wake_queue(vif->dev); return IRQ_HANDLED; } +static irqreturn_t xenvif_interrupt(int irq, void *dev_id) +{ + xenvif_tx_interrupt(irq, dev_id); + xenvif_rx_interrupt(irq, dev_id); + + return IRQ_HANDLED; +} + static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct xenvif *vif = netdev_priv(dev); @@ -125,13 +143,17 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev) static void xenvif_up(struct xenvif *vif) { xen_netbk_add_xenvif(vif); - enable_irq(vif->irq); + enable_irq(vif->tx_irq); + if (vif->tx_irq != vif->rx_irq) + enable_irq(vif->rx_irq); xen_netbk_check_rx_xenvif(vif); } static void xenvif_down(struct xenvif *vif) { - disable_irq(vif->irq); + disable_irq(vif->tx_irq); + if (vif->tx_irq != vif->rx_irq) + disable_irq(vif->rx_irq); del_timer_sync(&vif->credit_timeout); xen_netbk_deschedule_xenvif(vif); xen_netbk_remove_xenvif(vif); @@ -308,25 +330,52 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, } int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, - unsigned long rx_ring_ref, unsigned int evtchn) + unsigned long rx_ring_ref, unsigned int tx_evtchn, + unsigned int rx_evtchn) { int err = -ENOMEM; /* Already connected through? */ - if (vif->irq) + if (vif->tx_irq) return 0; + __module_get(THIS_MODULE); + err = xen_netbk_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref); if (err < 0) goto err; - err = bind_interdomain_evtchn_to_irqhandler( - vif->domid, evtchn, xenvif_interrupt, 0, - vif->dev->name, vif); - if (err < 0) - goto err_unmap; - vif->irq = err; - disable_irq(vif->irq); + if (tx_evtchn == rx_evtchn) { + /* feature-split-event-channels == 0 */ + err = bind_interdomain_evtchn_to_irqhandler( + vif->domid, tx_evtchn, xenvif_interrupt, 0, + vif->dev->name, vif); + if (err < 0) + goto err_unmap; + vif->tx_irq = vif->rx_irq = err; + disable_irq(vif->tx_irq); + } else { + /* feature-split-event-channels == 1 */ + snprintf(vif->tx_irq_name, sizeof(vif->tx_irq_name), + "%s-tx", vif->dev->name); + err = bind_interdomain_evtchn_to_irqhandler( + vif->domid, tx_evtchn, xenvif_tx_interrupt, 0, + vif->tx_irq_name, vif); + if (err < 0) + goto err_unmap; + vif->tx_irq = err; + disable_irq(vif->tx_irq); + + snprintf(vif->rx_irq_name, sizeof(vif->rx_irq_name), + "%s-rx", vif->dev->name); + err = bind_interdomain_evtchn_to_irqhandler( + vif->domid, rx_evtchn, xenvif_rx_interrupt, 0, + vif->rx_irq_name, vif); + if (err < 0) + goto err_tx_unbind; + vif->rx_irq = err; + disable_irq(vif->rx_irq); + } xenvif_get(vif); @@ -340,9 +389,13 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, rtnl_unlock(); return 0; +err_tx_unbind: + unbind_from_irqhandler(vif->tx_irq, vif); + vif->tx_irq = 0; err_unmap: xen_netbk_unmap_frontend_rings(vif); err: + module_put(THIS_MODULE); return err; } @@ -360,18 +413,37 @@ void xenvif_carrier_off(struct xenvif *vif) void xenvif_disconnect(struct xenvif *vif) { + /* Disconnect funtion might get called by generic framework + * even before vif connects, so we need to check if we really + * need to do a module_put. + */ + int need_module_put = 0; + if (netif_carrier_ok(vif->dev)) xenvif_carrier_off(vif); atomic_dec(&vif->refcnt); wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0); - if (vif->irq) - unbind_from_irqhandler(vif->irq, vif); + if (vif->tx_irq) { + if (vif->tx_irq == vif->rx_irq) + unbind_from_irqhandler(vif->tx_irq, vif); + else { + unbind_from_irqhandler(vif->tx_irq, vif); + unbind_from_irqhandler(vif->rx_irq, vif); + } + /* vif->irq is valid, we had a module_get in + * xenvif_connect. + */ + need_module_put = 1; + } unregister_netdev(vif->dev); xen_netbk_unmap_frontend_rings(vif); free_netdev(vif->dev); + + if (need_module_put) + module_put(THIS_MODULE); } diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 8c20935d72c..64828de25d9 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -47,6 +47,13 @@ #include <asm/xen/hypercall.h> #include <asm/xen/page.h> +/* Provide an option to disable split event channels at load time as + * event channels are limited resource. Split event channels are + * enabled by default. + */ +bool separate_tx_rx_irq = 1; +module_param(separate_tx_rx_irq, bool, 0644); + /* * This is the maximum slots a skb can have. If a guest sends a skb * which exceeds this limit it is considered malicious. @@ -783,7 +790,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk) } list_for_each_entry_safe(vif, tmp, ¬ify, notify_list) { - notify_remote_via_irq(vif->irq); + notify_remote_via_irq(vif->rx_irq); list_del_init(&vif->notify_list); xenvif_put(vif); } @@ -1763,7 +1770,7 @@ static void make_tx_response(struct xenvif *vif, vif->tx.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->tx, notify); if (notify) - notify_remote_via_irq(vif->irq); + notify_remote_via_irq(vif->tx_irq); } static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif, @@ -1883,9 +1890,8 @@ static int __init netback_init(void) return -ENODEV; if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) { - printk(KERN_INFO - "xen-netback: fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", - fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX); + pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", + fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX); fatal_skb_slots = XEN_NETBK_LEGACY_SLOTS_MAX; } @@ -1914,7 +1920,7 @@ static int __init netback_init(void) "netback/%u", group); if (IS_ERR(netbk->task)) { - printk(KERN_ALERT "kthread_create() fails at netback\n"); + pr_alert("kthread_create() fails at netback\n"); del_timer(&netbk->net_timer); rc = PTR_ERR(netbk->task); goto failed_init; @@ -1940,10 +1946,6 @@ static int __init netback_init(void) failed_init: while (--group >= 0) { struct xen_netbk *netbk = &xen_netbk[group]; - for (i = 0; i < MAX_PENDING_REQS; i++) { - if (netbk->mmap_pages[i]) - __free_page(netbk->mmap_pages[i]); - } del_timer(&netbk->net_timer); kthread_stop(netbk->task); } @@ -1954,5 +1956,25 @@ failed_init: module_init(netback_init); +static void __exit netback_fini(void) +{ + int i, j; + + xenvif_xenbus_fini(); + + for (i = 0; i < xen_netbk_group_nr; i++) { + struct xen_netbk *netbk = &xen_netbk[i]; + del_timer_sync(&netbk->net_timer); + kthread_stop(netbk->task); + for (j = 0; j < MAX_PENDING_REQS; j++) { + if (netbk->mmap_pages[j]) + __free_page(netbk->mmap_pages[j]); + } + } + + vfree(xen_netbk); +} +module_exit(netback_fini); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("xen-backend:vif"); diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 410018c4c52..1fe48fe364e 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -122,6 +122,16 @@ static int netback_probe(struct xenbus_device *dev, goto fail; } + /* + * Split event channels support, this is optional so it is not + * put inside the above loop. + */ + err = xenbus_printf(XBT_NIL, dev->nodename, + "feature-split-event-channels", + "%u", separate_tx_rx_irq); + if (err) + pr_debug("Error writing feature-split-event-channels\n"); + err = xenbus_switch_state(dev, XenbusStateInitWait); if (err) goto fail; @@ -135,7 +145,7 @@ abort_transaction: xenbus_transaction_end(xbt, 1); xenbus_dev_fatal(dev, err, "%s", message); fail: - pr_debug("failed"); + pr_debug("failed\n"); netback_remove(dev); return err; } @@ -218,15 +228,14 @@ static void frontend_changed(struct xenbus_device *dev, { struct backend_info *be = dev_get_drvdata(&dev->dev); - pr_debug("frontend state %s", xenbus_strstate(frontend_state)); + pr_debug("frontend state %s\n", xenbus_strstate(frontend_state)); be->frontend_state = frontend_state; switch (frontend_state) { case XenbusStateInitialising: if (dev->state == XenbusStateClosed) { - printk(KERN_INFO "%s: %s: prepare for reconnect\n", - __func__, dev->nodename); + pr_info("%s: prepare for reconnect\n", dev->nodename); xenbus_switch_state(dev, XenbusStateInitWait); } break; @@ -393,21 +402,36 @@ static int connect_rings(struct backend_info *be) struct xenvif *vif = be->vif; struct xenbus_device *dev = be->dev; unsigned long tx_ring_ref, rx_ring_ref; - unsigned int evtchn, rx_copy; + unsigned int tx_evtchn, rx_evtchn, rx_copy; int err; int val; err = xenbus_gather(XBT_NIL, dev->otherend, "tx-ring-ref", "%lu", &tx_ring_ref, - "rx-ring-ref", "%lu", &rx_ring_ref, - "event-channel", "%u", &evtchn, NULL); + "rx-ring-ref", "%lu", &rx_ring_ref, NULL); if (err) { xenbus_dev_fatal(dev, err, - "reading %s/ring-ref and event-channel", + "reading %s/ring-ref", dev->otherend); return err; } + /* Try split event channels first, then single event channel. */ + err = xenbus_gather(XBT_NIL, dev->otherend, + "event-channel-tx", "%u", &tx_evtchn, + "event-channel-rx", "%u", &rx_evtchn, NULL); + if (err < 0) { + err = xenbus_scanf(XBT_NIL, dev->otherend, + "event-channel", "%u", &tx_evtchn); + if (err < 0) { + xenbus_dev_fatal(dev, err, + "reading %s/event-channel(-tx/rx)", + dev->otherend); + return err; + } + rx_evtchn = tx_evtchn; + } + err = xenbus_scanf(XBT_NIL, dev->otherend, "request-rx-copy", "%u", &rx_copy); if (err == -ENOENT) { @@ -454,11 +478,13 @@ static int connect_rings(struct backend_info *be) vif->csum = !val; /* Map the shared frame, irq etc. */ - err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, evtchn); + err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, + tx_evtchn, rx_evtchn); if (err) { xenbus_dev_fatal(dev, err, - "mapping shared-frames %lu/%lu port %u", - tx_ring_ref, rx_ring_ref, evtchn); + "mapping shared-frames %lu/%lu port tx %u rx %u", + tx_ring_ref, rx_ring_ref, + tx_evtchn, rx_evtchn); return err; } return 0; @@ -485,3 +511,8 @@ int xenvif_xenbus_init(void) { return xenbus_register_backend(&netback_driver); } + +void xenvif_xenbus_fini(void) +{ + return xenbus_unregister_driver(&netback_driver); +} diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 1db10141506..ff7f111fffe 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -29,6 +29,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/netdevice.h> @@ -85,7 +87,15 @@ struct netfront_info { struct napi_struct napi; - unsigned int evtchn; + /* Split event channels support, tx_* == rx_* when using + * single event channel. + */ + unsigned int tx_evtchn, rx_evtchn; + unsigned int tx_irq, rx_irq; + /* Only used when split event channels support is enabled */ + char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */ + char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */ + struct xenbus_device *xbdev; spinlock_t tx_lock; @@ -330,7 +340,7 @@ no_skb: push: RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->rx, notify); if (notify) - notify_remote_via_irq(np->netdev->irq); + notify_remote_via_irq(np->rx_irq); } static int xennet_open(struct net_device *dev) @@ -377,9 +387,8 @@ static void xennet_tx_buf_gc(struct net_device *dev) skb = np->tx_skbs[id].skb; if (unlikely(gnttab_query_foreign_access( np->grant_tx_ref[id]) != 0)) { - printk(KERN_ALERT "xennet_tx_buf_gc: warning " - "-- grant still in use by backend " - "domain.\n"); + pr_alert("%s: warning -- grant still in use by backend domain\n", + __func__); BUG(); } gnttab_end_foreign_access_ref( @@ -623,7 +632,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify); if (notify) - notify_remote_via_irq(np->netdev->irq); + notify_remote_via_irq(np->tx_irq); u64_stats_update_begin(&stats->syncp); stats->tx_bytes += skb->len; @@ -796,14 +805,14 @@ static int xennet_set_skb_gso(struct sk_buff *skb, { if (!gso->u.gso.size) { if (net_ratelimit()) - printk(KERN_WARNING "GSO size must not be zero.\n"); + pr_warn("GSO size must not be zero\n"); return -EINVAL; } /* Currently only TCPv4 S.O. is supported. */ if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { if (net_ratelimit()) - printk(KERN_WARNING "Bad GSO type %d.\n", gso->u.gso.type); + pr_warn("Bad GSO type %d\n", gso->u.gso.type); return -EINVAL; } @@ -850,7 +859,6 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np, static int checksum_setup(struct net_device *dev, struct sk_buff *skb) { struct iphdr *iph; - unsigned char *th; int err = -EPROTO; int recalculate_partial_csum = 0; @@ -875,27 +883,27 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb) goto out; iph = (void *)skb->data; - th = skb->data + 4 * iph->ihl; - if (th >= skb_tail_pointer(skb)) - goto out; - skb->csum_start = th - skb->head; switch (iph->protocol) { case IPPROTO_TCP: - skb->csum_offset = offsetof(struct tcphdr, check); + if (!skb_partial_csum_set(skb, 4 * iph->ihl, + offsetof(struct tcphdr, check))) + goto out; if (recalculate_partial_csum) { - struct tcphdr *tcph = (struct tcphdr *)th; + struct tcphdr *tcph = tcp_hdr(skb); tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - iph->ihl*4, IPPROTO_TCP, 0); } break; case IPPROTO_UDP: - skb->csum_offset = offsetof(struct udphdr, check); + if (!skb_partial_csum_set(skb, 4 * iph->ihl, + offsetof(struct udphdr, check))) + goto out; if (recalculate_partial_csum) { - struct udphdr *udph = (struct udphdr *)th; + struct udphdr *udph = udp_hdr(skb); udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - iph->ihl*4, IPPROTO_UDP, 0); @@ -903,15 +911,11 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb) break; default: if (net_ratelimit()) - printk(KERN_ERR "Attempting to checksum a non-" - "TCP/UDP packet, dropping a protocol" - " %d packet", iph->protocol); + pr_err("Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n", + iph->protocol); goto out; } - if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb)) - goto out; - err = 0; out: @@ -1254,23 +1258,35 @@ static int xennet_set_features(struct net_device *dev, return 0; } -static irqreturn_t xennet_interrupt(int irq, void *dev_id) +static irqreturn_t xennet_tx_interrupt(int irq, void *dev_id) { - struct net_device *dev = dev_id; - struct netfront_info *np = netdev_priv(dev); + struct netfront_info *np = dev_id; + struct net_device *dev = np->netdev; unsigned long flags; spin_lock_irqsave(&np->tx_lock, flags); + xennet_tx_buf_gc(dev); + spin_unlock_irqrestore(&np->tx_lock, flags); - if (likely(netif_carrier_ok(dev))) { - xennet_tx_buf_gc(dev); - /* Under tx_lock: protects access to rx shared-ring indexes. */ - if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) + return IRQ_HANDLED; +} + +static irqreturn_t xennet_rx_interrupt(int irq, void *dev_id) +{ + struct netfront_info *np = dev_id; + struct net_device *dev = np->netdev; + + if (likely(netif_carrier_ok(dev) && + RING_HAS_UNCONSUMED_RESPONSES(&np->rx))) napi_schedule(&np->napi); - } - spin_unlock_irqrestore(&np->tx_lock, flags); + return IRQ_HANDLED; +} +static irqreturn_t xennet_interrupt(int irq, void *dev_id) +{ + xennet_tx_interrupt(irq, dev_id); + xennet_rx_interrupt(irq, dev_id); return IRQ_HANDLED; } @@ -1343,14 +1359,14 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) /* A grant for every tx ring slot */ if (gnttab_alloc_grant_references(TX_MAX_TARGET, &np->gref_tx_head) < 0) { - printk(KERN_ALERT "#### netfront can't alloc tx grant refs\n"); + pr_alert("can't alloc tx grant refs\n"); err = -ENOMEM; goto exit_free_stats; } /* A grant for every rx ring slot */ if (gnttab_alloc_grant_references(RX_MAX_TARGET, &np->gref_rx_head) < 0) { - printk(KERN_ALERT "#### netfront can't alloc rx grant refs\n"); + pr_alert("can't alloc rx grant refs\n"); err = -ENOMEM; goto exit_free_tx; } @@ -1414,16 +1430,14 @@ static int netfront_probe(struct xenbus_device *dev, err = register_netdev(info->netdev); if (err) { - printk(KERN_WARNING "%s: register_netdev err=%d\n", - __func__, err); + pr_warn("%s: register_netdev err=%d\n", __func__, err); goto fail; } err = xennet_sysfs_addif(info->netdev); if (err) { unregister_netdev(info->netdev); - printk(KERN_WARNING "%s: add sysfs failed err=%d\n", - __func__, err); + pr_warn("%s: add sysfs failed err=%d\n", __func__, err); goto fail; } @@ -1451,9 +1465,14 @@ static void xennet_disconnect_backend(struct netfront_info *info) spin_unlock_irq(&info->tx_lock); spin_unlock_bh(&info->rx_lock); - if (info->netdev->irq) - unbind_from_irqhandler(info->netdev->irq, info->netdev); - info->evtchn = info->netdev->irq = 0; + if (info->tx_irq && (info->tx_irq == info->rx_irq)) + unbind_from_irqhandler(info->tx_irq, info); + if (info->tx_irq && (info->tx_irq != info->rx_irq)) { + unbind_from_irqhandler(info->tx_irq, info); + unbind_from_irqhandler(info->rx_irq, info); + } + info->tx_evtchn = info->rx_evtchn = 0; + info->tx_irq = info->rx_irq = 0; /* End access and free the pages */ xennet_end_access(info->tx_ring_ref, info->tx.sring); @@ -1503,12 +1522,82 @@ static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[]) return 0; } +static int setup_netfront_single(struct netfront_info *info) +{ + int err; + + err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn); + if (err < 0) + goto fail; + + err = bind_evtchn_to_irqhandler(info->tx_evtchn, + xennet_interrupt, + 0, info->netdev->name, info); + if (err < 0) + goto bind_fail; + info->rx_evtchn = info->tx_evtchn; + info->rx_irq = info->tx_irq = err; + + return 0; + +bind_fail: + xenbus_free_evtchn(info->xbdev, info->tx_evtchn); + info->tx_evtchn = 0; +fail: + return err; +} + +static int setup_netfront_split(struct netfront_info *info) +{ + int err; + + err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn); + if (err < 0) + goto fail; + err = xenbus_alloc_evtchn(info->xbdev, &info->rx_evtchn); + if (err < 0) + goto alloc_rx_evtchn_fail; + + snprintf(info->tx_irq_name, sizeof(info->tx_irq_name), + "%s-tx", info->netdev->name); + err = bind_evtchn_to_irqhandler(info->tx_evtchn, + xennet_tx_interrupt, + 0, info->tx_irq_name, info); + if (err < 0) + goto bind_tx_fail; + info->tx_irq = err; + + snprintf(info->rx_irq_name, sizeof(info->rx_irq_name), + "%s-rx", info->netdev->name); + err = bind_evtchn_to_irqhandler(info->rx_evtchn, + xennet_rx_interrupt, + 0, info->rx_irq_name, info); + if (err < 0) + goto bind_rx_fail; + info->rx_irq = err; + + return 0; + +bind_rx_fail: + unbind_from_irqhandler(info->tx_irq, info); + info->tx_irq = 0; +bind_tx_fail: + xenbus_free_evtchn(info->xbdev, info->rx_evtchn); + info->rx_evtchn = 0; +alloc_rx_evtchn_fail: + xenbus_free_evtchn(info->xbdev, info->tx_evtchn); + info->tx_evtchn = 0; +fail: + return err; +} + static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) { struct xen_netif_tx_sring *txs; struct xen_netif_rx_sring *rxs; int err; struct net_device *netdev = info->netdev; + unsigned int feature_split_evtchn; info->tx_ring_ref = GRANT_INVALID_REF; info->rx_ring_ref = GRANT_INVALID_REF; @@ -1516,6 +1605,12 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) info->tx.sring = NULL; netdev->irq = 0; + err = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-split-event-channels", "%u", + &feature_split_evtchn); + if (err < 0) + feature_split_evtchn = 0; + err = xen_net_read_mac(dev, netdev->dev_addr); if (err) { xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); @@ -1532,40 +1627,50 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(txs)); - if (err < 0) { - free_page((unsigned long)txs); - goto fail; - } + if (err < 0) + goto grant_tx_ring_fail; info->tx_ring_ref = err; rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH); if (!rxs) { err = -ENOMEM; xenbus_dev_fatal(dev, err, "allocating rx ring page"); - goto fail; + goto alloc_rx_ring_fail; } SHARED_RING_INIT(rxs); FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(rxs)); - if (err < 0) { - free_page((unsigned long)rxs); - goto fail; - } + if (err < 0) + goto grant_rx_ring_fail; info->rx_ring_ref = err; - err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (feature_split_evtchn) + err = setup_netfront_split(info); + /* setup single event channel if + * a) feature-split-event-channels == 0 + * b) feature-split-event-channels == 1 but failed to setup + */ + if (!feature_split_evtchn || (feature_split_evtchn && err)) + err = setup_netfront_single(info); + if (err) - goto fail; + goto alloc_evtchn_fail; - err = bind_evtchn_to_irqhandler(info->evtchn, xennet_interrupt, - 0, netdev->name, netdev); - if (err < 0) - goto fail; - netdev->irq = err; return 0; - fail: + /* If we fail to setup netfront, it is safe to just revoke access to + * granted pages because backend is not accessing it at this point. + */ +alloc_evtchn_fail: + gnttab_end_foreign_access_ref(info->rx_ring_ref, 0); +grant_rx_ring_fail: + free_page((unsigned long)rxs); +alloc_rx_ring_fail: + gnttab_end_foreign_access_ref(info->tx_ring_ref, 0); +grant_tx_ring_fail: + free_page((unsigned long)txs); +fail: return err; } @@ -1601,11 +1706,27 @@ again: message = "writing rx ring-ref"; goto abort_transaction; } - err = xenbus_printf(xbt, dev->nodename, - "event-channel", "%u", info->evtchn); - if (err) { - message = "writing event-channel"; - goto abort_transaction; + + if (info->tx_evtchn == info->rx_evtchn) { + err = xenbus_printf(xbt, dev->nodename, + "event-channel", "%u", info->tx_evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + } else { + err = xenbus_printf(xbt, dev->nodename, + "event-channel-tx", "%u", info->tx_evtchn); + if (err) { + message = "writing event-channel-tx"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, + "event-channel-rx", "%u", info->rx_evtchn); + if (err) { + message = "writing event-channel-rx"; + goto abort_transaction; + } } err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u", @@ -1718,7 +1839,9 @@ static int xennet_connect(struct net_device *dev) * packets. */ netif_carrier_on(np->netdev); - notify_remote_via_irq(np->netdev->irq); + notify_remote_via_irq(np->tx_irq); + if (np->tx_irq != np->rx_irq) + notify_remote_via_irq(np->rx_irq); xennet_tx_buf_gc(dev); xennet_alloc_rx_buffers(dev); @@ -1991,7 +2114,7 @@ static int __init netif_init(void) if (xen_hvm_domain() && !xen_platform_pci_unplug) return -ENODEV; - printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); + pr_info("Initialising Xen virtual ethernet driver\n"); return xenbus_register_frontend(&netfront_driver); } |