From ab5c4f71d8c7add173a2d32e5beefdaaf1b7cbbc Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 10 Dec 2012 15:30:28 +0100 Subject: ath9k: allow to load EEPROM content via firmware API The calibration data for devices w/o a separate EEPROM chip can be specified via the 'eeprom_data' field of 'ath9k_platform_data'. The 'eeprom_data' is usually filled from board specific setup functions. It is easy if the EEPROM data is mapped to the memory, but it can be complicated if it is stored elsewhere. The patch adds support for loading of the EEPROM data via the firmware API to avoid this limitation. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom.c | 19 ++++++++++- drivers/net/wireless/ath/ath9k/hw.h | 3 ++ drivers/net/wireless/ath/ath9k/init.c | 60 ++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index eb9ac5ea6180..971d770722cf 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -113,12 +113,29 @@ void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, } } +static bool ath9k_hw_nvram_read_blob(struct ath_hw *ah, u32 off, + u16 *data) +{ + u16 *blob_data; + + if (off * sizeof(u16) > ah->eeprom_blob->size) + return false; + + blob_data = (u16 *)ah->eeprom_blob->data; + *data = blob_data[off]; + return true; +} + bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) { struct ath_common *common = ath9k_hw_common(ah); bool ret; - ret = common->bus_ops->eeprom_read(common, off, data); + if (ah->eeprom_blob) + ret = ath9k_hw_nvram_read_blob(ah, off, data); + else + ret = common->bus_ops->eeprom_read(common, off, data); + if (!ret) ath_dbg(common, EEPROM, "unable to read eeprom region at offset %u\n", off); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index eff67592db18..7f1a8e91c908 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "mac.h" #include "ani.h" @@ -921,6 +922,8 @@ struct ath_hw { bool is_clk_25mhz; int (*get_mac_revision)(void); int (*external_reset)(void); + + const struct firmware *eeprom_blob; }; struct ath_bus_ops { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 80cae53a33e5..27703a5e48d6 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -23,6 +23,11 @@ #include "ath9k.h" +struct ath9k_eeprom_ctx { + struct completion complete; + struct ath_hw *ah; +}; + static char *dev_info = "ath9k"; MODULE_AUTHOR("Atheros Communications"); @@ -506,6 +511,51 @@ static void ath9k_init_misc(struct ath_softc *sc) sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT; } +static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob, + void *ctx) +{ + struct ath9k_eeprom_ctx *ec = ctx; + + if (eeprom_blob) + ec->ah->eeprom_blob = eeprom_blob; + + complete(&ec->complete); +} + +static int ath9k_eeprom_request(struct ath_softc *sc, const char *name) +{ + struct ath9k_eeprom_ctx ec; + struct ath_hw *ah = ah = sc->sc_ah; + int err; + + /* try to load the EEPROM content asynchronously */ + init_completion(&ec.complete); + ec.ah = sc->sc_ah; + + err = request_firmware_nowait(THIS_MODULE, 1, name, sc->dev, GFP_KERNEL, + &ec, ath9k_eeprom_request_cb); + if (err < 0) { + ath_err(ath9k_hw_common(ah), + "EEPROM request failed\n"); + return err; + } + + wait_for_completion(&ec.complete); + + if (!ah->eeprom_blob) { + ath_err(ath9k_hw_common(ah), + "Unable to load EEPROM file %s\n", name); + return -EINVAL; + } + + return 0; +} + +static void ath9k_eeprom_release(struct ath_softc *sc) +{ + release_firmware(sc->sc_ah->eeprom_blob); +} + static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { @@ -583,6 +633,12 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ + if (pdata->eeprom_name) { + ret = ath9k_eeprom_request(sc, pdata->eeprom_name); + if (ret) + goto err_eeprom; + } + /* Initializes the hardware for all supported chipsets */ ret = ath9k_hw_init(ah); if (ret) @@ -619,7 +675,8 @@ err_btcoex: err_queues: ath9k_hw_deinit(ah); err_hw: - + ath9k_eeprom_release(sc); +err_eeprom: kfree(ah); sc->sc_ah = NULL; @@ -882,6 +939,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); + ath9k_eeprom_release(sc); kfree(sc->sc_ah); sc->sc_ah = NULL; } -- cgit v1.2.3