--- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -28,6 +28,11 @@ #include "ath9k.h" +#ifdef CONFIG_ATH79 +#include +#include +#endif + struct ath9k_eeprom_ctx { struct completion complete; struct ath_hw *ah; @@ -242,6 +247,81 @@ static unsigned int ath9k_reg_rmw(void * return val; } +#ifdef CONFIG_ATH79 +#define QCA955X_DDR_CTL_CONFIG 0x108 +#define QCA955X_DDR_CTL_CONFIG_ACT_WMAC BIT(23) + +static int ar913x_wmac_reset(void) +{ + ath79_device_reset_set(AR913X_RESET_AMBA2WMAC); + mdelay(10); + + ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC); + mdelay(10); + + return 0; +} + +static int ar933x_wmac_reset(void) +{ + int retries = 20; + + ath79_device_reset_set(AR933X_RESET_WMAC); + ath79_device_reset_clear(AR933X_RESET_WMAC); + + while (1) { + u32 bootstrap; + + bootstrap = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP); + if ((bootstrap & AR933X_BOOTSTRAP_EEPBUSY) == 0) + return 0; + + if (retries-- == 0) + break; + + udelay(10000); + } + + pr_err("ar933x: WMAC reset timed out"); + return -ETIMEDOUT; +} + +static int qca955x_wmac_reset(void) +{ + int i; + + /* Try to wait for WMAC DDR activity to stop */ + for (i = 0; i < 10; i++) { + if (!(__raw_readl(ath79_ddr_base + QCA955X_DDR_CTL_CONFIG) & + QCA955X_DDR_CTL_CONFIG_ACT_WMAC)) + break; + + udelay(10); + } + + ath79_device_reset_set(QCA955X_RESET_RTC); + udelay(10); + ath79_device_reset_clear(QCA955X_RESET_RTC); + udelay(10); + + return 0; +} + + +static int ar9330_get_soc_revision(void) +{ + if (ath79_soc_rev == 1) + return ath79_soc_rev; + + return 0; +} + +static int ath79_get_soc_revision(void) +{ + return ath79_soc_rev; +} +#endif + /**************************/ /* Initialization */ /**************************/ @@ -626,6 +706,7 @@ static int ath9k_of_init(struct ath_soft struct ath_common *common = ath9k_hw_common(ah); enum ath_bus_type bus_type = common->bus_ops->ath_bus_type; char eeprom_name[100]; + u32 mask; int ret; if (!of_device_is_available(np)) @@ -633,6 +714,43 @@ static int ath9k_of_init(struct ath_soft ath_dbg(common, CONFIG, "parsing configuration from OF node\n"); + if (!of_property_read_u32(np, "qca,gpio-mask", &mask)) + ah->caps.gpio_mask = mask; + + if (of_property_read_bool(np, "qca,tx-gain-buffalo")) + ah->config.tx_gain_buffalo = true; + +#ifdef CONFIG_ATH79 + if (ah->hw_version.devid == AR5416_AR9100_DEVID) { + ah->external_reset = ar913x_wmac_reset; + } else if (ah->hw_version.devid == AR9300_DEVID_AR9330) { + ah->get_mac_revision = ar9330_get_soc_revision; + u32 t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP); + ah->is_clk_25mhz = !(t & AR933X_BOOTSTRAP_REF_CLK_40); + ah->external_reset = ar933x_wmac_reset; + } else if (ah->hw_version.devid == AR9300_DEVID_AR9340) { + ah->get_mac_revision = ath79_get_soc_revision; + u32 t = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); + ah->is_clk_25mhz = !(t & AR934X_BOOTSTRAP_REF_CLK_40); + } else if (ah->hw_version.devid == AR9300_DEVID_AR953X) { + ah->get_mac_revision = ath79_get_soc_revision; + /* + * QCA953x only supports 25MHz refclk. + * Some vendors have an invalid bootstrap option + * set, which would break the WMAC here. + */ + ah->is_clk_25mhz = true; + } else if (ah->hw_version.devid == AR9300_DEVID_QCA955X) { + u32 t = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP); + ah->is_clk_25mhz = !(t & QCA955X_BOOTSTRAP_REF_CLK_40); + ah->external_reset = qca955x_wmac_reset; + } else if (ah->hw_version.devid == AR9300_DEVID_QCA956X) { + ah->get_mac_revision = ath79_get_soc_revision; + u32 t = ath79_reset_rr(QCA956X_RESET_REG_BOOTSTRAP); + ah->is_clk_25mhz = !(t & QCA956X_BOOTSTRAP_REF_CLK_40); + } +#endif + if (of_property_read_bool(np, "qca,no-eeprom")) { /* ath9k-eeprom--.bin */ scnprintf(eeprom_name, sizeof(eeprom_name), @@ -651,6 +769,17 @@ static int ath9k_of_init(struct ath_soft if (ret == -EPROBE_DEFER) return ret; + np = of_get_child_by_name(np, "led"); + if (np && of_device_is_available(np)) { + u32 led_pin; + + if (!of_property_read_u32(np, "led-sources", &led_pin)) + ah->led_pin = led_pin; + + ah->config.led_active_high = !of_property_read_bool(np, "led-active-low"); + of_node_put(np); + } + return 0; }