--- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -20,7 +20,15 @@ #include #include #include +#include #include "ath9k.h" +#include + +#ifdef CONFIG_OF +#include +#include +#include +#endif static const struct platform_device_id ath9k_platform_id_table[] = { { @@ -69,6 +77,192 @@ static const struct ath_bus_ops ath_ahb_ .eeprom_read = ath_ahb_eeprom_read, }; +#ifdef CONFIG_OF + +#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; +} + +enum { + AR913X_WMAC = 0, + AR933X_WMAC, + AR934X_WMAC, + QCA953X_WMAC, + QCA955X_WMAC, + QCA956X_WMAC, +}; + +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; +} + +static const struct of_ath_ahb_data { + u16 dev_id; + u32 bootstrap_reg; + u32 bootstrap_ref; + + int (*soc_revision)(void); + int (*wmac_reset)(void); +} of_ath_ahb_data[] = { + [AR913X_WMAC] = { + .dev_id = AR5416_AR9100_DEVID, + .wmac_reset = ar913x_wmac_reset, + + }, + [AR933X_WMAC] = { + .dev_id = AR9300_DEVID_AR9330, + .bootstrap_reg = AR933X_RESET_REG_BOOTSTRAP, + .bootstrap_ref = AR933X_BOOTSTRAP_REF_CLK_40, + .soc_revision = ar9330_get_soc_revision, + .wmac_reset = ar933x_wmac_reset, + }, + [AR934X_WMAC] = { + .dev_id = AR9300_DEVID_AR9340, + .bootstrap_reg = AR934X_RESET_REG_BOOTSTRAP, + .bootstrap_ref = AR934X_BOOTSTRAP_REF_CLK_40, + .soc_revision = ath79_get_soc_revision, + }, + [QCA953X_WMAC] = { + .dev_id = AR9300_DEVID_AR953X, + .bootstrap_reg = QCA953X_RESET_REG_BOOTSTRAP, + .bootstrap_ref = QCA953X_BOOTSTRAP_REF_CLK_40, + .soc_revision = ath79_get_soc_revision, + }, + [QCA955X_WMAC] = { + .dev_id = AR9300_DEVID_QCA955X, + .bootstrap_reg = QCA955X_RESET_REG_BOOTSTRAP, + .bootstrap_ref = QCA955X_BOOTSTRAP_REF_CLK_40, + .wmac_reset = qca955x_wmac_reset, + }, + [QCA956X_WMAC] = { + .dev_id = AR9300_DEVID_QCA956X, + .bootstrap_reg = QCA956X_RESET_REG_BOOTSTRAP, + .bootstrap_ref = QCA956X_BOOTSTRAP_REF_CLK_40, + .soc_revision = ath79_get_soc_revision, + }, +}; + +const struct of_device_id of_ath_ahb_match[] = { + { .compatible = "qca,ar9130-wmac", .data = &of_ath_ahb_data[AR913X_WMAC] }, + { .compatible = "qca,ar9330-wmac", .data = &of_ath_ahb_data[AR933X_WMAC] }, + { .compatible = "qca,ar9340-wmac", .data = &of_ath_ahb_data[AR934X_WMAC] }, + { .compatible = "qca,qca9530-wmac", .data = &of_ath_ahb_data[QCA953X_WMAC] }, + { .compatible = "qca,qca9550-wmac", .data = &of_ath_ahb_data[QCA955X_WMAC] }, + { .compatible = "qca,qca9560-wmac", .data = &of_ath_ahb_data[QCA956X_WMAC] }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_ath_ahb_match); + +static int of_ath_ahb_probe(struct platform_device *pdev) +{ + struct ath9k_platform_data *pdata; + const struct of_device_id *match; + const struct of_ath_ahb_data *data; + u8 led_pin; + + match = of_match_device(of_ath_ahb_match, &pdev->dev); + data = (const struct of_ath_ahb_data *)match->data; + + pdata = dev_get_platdata(&pdev->dev); + + if (!of_property_read_u8(pdev->dev.of_node, "qca,led-pin", &led_pin)) + pdata->led_pin = led_pin; + else + pdata->led_pin = -1; + + if (of_property_read_bool(pdev->dev.of_node, "qca,tx-gain-buffalo")) + pdata->tx_gain_buffalo = true; + + if (data->wmac_reset) { + data->wmac_reset(); + pdata->external_reset = data->wmac_reset; + } + + if (data->dev_id == AR9300_DEVID_AR953X) { + /* + * QCA953x only supports 25MHz refclk. + * Some vendors have an invalid bootstrap option + * set, which would break the WMAC here. + */ + pdata->is_clk_25mhz = true; + } else if (data->bootstrap_reg && data->bootstrap_ref) { + u32 t = ath79_reset_rr(data->bootstrap_reg); + if (t & data->bootstrap_ref) + pdata->is_clk_25mhz = false; + else + pdata->is_clk_25mhz = true; + } + + pdata->get_mac_revision = data->soc_revision; + + return data->dev_id; +} +#endif + static int ath_ahb_probe(struct platform_device *pdev) { void __iomem *mem; @@ -80,6 +318,17 @@ static int ath_ahb_probe(struct platform int ret = 0; struct ath_hw *ah; char hw_name[64]; + u16 dev_id; + + if (id) + dev_id = id->driver_data; + +#ifdef CONFIG_OF + if (pdev->dev.of_node) + pdev->dev.platform_data = devm_kzalloc(&pdev->dev, + sizeof(struct ath9k_platform_data), + GFP_KERNEL); +#endif if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data specified\n"); @@ -118,13 +367,16 @@ static int ath_ahb_probe(struct platform sc->mem = mem; sc->irq = irq; +#ifdef CONFIG_OF + dev_id = of_ath_ahb_probe(pdev); +#endif ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); goto err_free_hw; } - ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops); + ret = ath9k_init_device(dev_id, sc, &ath_ahb_bus_ops); if (ret) { dev_err(&pdev->dev, "failed to initialize device\n"); goto err_irq; @@ -155,6 +407,9 @@ static int ath_ahb_remove(struct platfor free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); } +#ifdef CONFIG_OF + pdev->dev.platform_data = NULL; +#endif return 0; } @@ -164,6 +419,9 @@ static struct platform_driver ath_ahb_dr .remove = ath_ahb_remove, .driver = { .name = "ath9k", +#ifdef CONFIG_OF + .of_match_table = of_ath_ahb_match, +#endif }, .id_table = ath9k_platform_id_table, }; --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "common.h" #include "debug.h" @@ -1012,6 +1013,9 @@ struct ath_softc { struct ath_hw *sc_ah; void __iomem *mem; int irq; +#ifdef CONFIG_OF + struct reset_control *reset; +#endif spinlock_t sc_serial_rw; spinlock_t sc_pm_lock; spinlock_t sc_pcu_lock;