| 1 | From ee4581f2f024c601a5e247ec6acab3e7df538f88 Mon Sep 17 00:00:00 2001 |
| 2 | From: Gabor Juhos <juhosg@openwrt.org> |
| 3 | Date: Sun, 9 Dec 2012 17:31:54 +0100 |
| 4 | Subject: [PATCH 4/4] ath9k: allow to load EEPROM content via firmware API |
| 5 | |
| 6 | The calibration data for devices w/o a separate |
| 7 | EEPROM chip can be specified via the 'eeprom_data' |
| 8 | field of 'ath9k_platform_data'. The 'eeprom_data' |
| 9 | is usually filled from board specific setup |
| 10 | functions. It is easy if the EEPROM data is mapped |
| 11 | to the memory, but it can be complicated if it is |
| 12 | stored elsewhere. |
| 13 | |
| 14 | The patch adds support for loading of the EEPROM |
| 15 | data via the firmware API to avoid this limitation. |
| 16 | |
| 17 | Signed-off-by: Gabor Juhos <juhosg@openwrt.org> |
| 18 | --- |
| 19 | drivers/net/wireless/ath/ath9k/eeprom.c | 19 +++++++++- |
| 20 | drivers/net/wireless/ath/ath9k/hw.h | 3 ++ |
| 21 | drivers/net/wireless/ath/ath9k/init.c | 60 ++++++++++++++++++++++++++++++- |
| 22 | include/linux/ath9k_platform.h | 2 ++ |
| 23 | 4 files changed, 82 insertions(+), 2 deletions(-) |
| 24 | |
| 25 | --- a/drivers/net/wireless/ath/ath9k/eeprom.c |
| 26 | +++ b/drivers/net/wireless/ath/ath9k/eeprom.c |
| 27 | @@ -113,12 +113,29 @@ void ath9k_hw_usb_gen_fill_eeprom(struct |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | +static bool ath9k_hw_nvram_read_blob(struct ath_hw *ah, u32 off, |
| 32 | + u16 *data) |
| 33 | +{ |
| 34 | + u16 *blob_data; |
| 35 | + |
| 36 | + if (off * sizeof(u16) > ah->eeprom_blob->size) |
| 37 | + return false; |
| 38 | + |
| 39 | + blob_data = (u16 *)ah->eeprom_blob->data; |
| 40 | + *data = blob_data[off]; |
| 41 | + return true; |
| 42 | +} |
| 43 | + |
| 44 | bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) |
| 45 | { |
| 46 | struct ath_common *common = ath9k_hw_common(ah); |
| 47 | bool ret; |
| 48 | |
| 49 | - ret = common->bus_ops->eeprom_read(common, off, data); |
| 50 | + if (ah->eeprom_blob) |
| 51 | + ret = ath9k_hw_nvram_read_blob(ah, off, data); |
| 52 | + else |
| 53 | + ret = common->bus_ops->eeprom_read(common, off, data); |
| 54 | + |
| 55 | if (!ret) |
| 56 | ath_dbg(common, EEPROM, |
| 57 | "unable to read eeprom region at offset %u\n", off); |
| 58 | --- a/drivers/net/wireless/ath/ath9k/hw.h |
| 59 | +++ b/drivers/net/wireless/ath/ath9k/hw.h |
| 60 | @@ -20,6 +20,7 @@ |
| 61 | #include <linux/if_ether.h> |
| 62 | #include <linux/delay.h> |
| 63 | #include <linux/io.h> |
| 64 | +#include <linux/firmware.h> |
| 65 | |
| 66 | #include "mac.h" |
| 67 | #include "ani.h" |
| 68 | @@ -920,6 +921,8 @@ struct ath_hw { |
| 69 | bool is_clk_25mhz; |
| 70 | int (*get_mac_revision)(void); |
| 71 | int (*external_reset)(void); |
| 72 | + |
| 73 | + const struct firmware *eeprom_blob; |
| 74 | }; |
| 75 | |
| 76 | struct ath_bus_ops { |
| 77 | --- a/drivers/net/wireless/ath/ath9k/init.c |
| 78 | +++ b/drivers/net/wireless/ath/ath9k/init.c |
| 79 | @@ -25,6 +25,11 @@ |
| 80 | |
| 81 | #include "ath9k.h" |
| 82 | |
| 83 | +struct ath9k_eeprom_ctx { |
| 84 | + struct completion complete; |
| 85 | + struct ath_hw *ah; |
| 86 | +}; |
| 87 | + |
| 88 | static char *dev_info = "ath9k"; |
| 89 | |
| 90 | MODULE_AUTHOR("Atheros Communications"); |
| 91 | @@ -508,6 +513,51 @@ static void ath9k_init_misc(struct ath_s |
| 92 | sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT; |
| 93 | } |
| 94 | |
| 95 | +static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob, |
| 96 | + void *ctx) |
| 97 | +{ |
| 98 | + struct ath9k_eeprom_ctx *ec = ctx; |
| 99 | + |
| 100 | + if (eeprom_blob) |
| 101 | + ec->ah->eeprom_blob = eeprom_blob; |
| 102 | + |
| 103 | + complete(&ec->complete); |
| 104 | +} |
| 105 | + |
| 106 | +static int ath9k_eeprom_request(struct ath_softc *sc, const char *name) |
| 107 | +{ |
| 108 | + struct ath9k_eeprom_ctx ec; |
| 109 | + struct ath_hw *ah = ah = sc->sc_ah; |
| 110 | + int err; |
| 111 | + |
| 112 | + /* try to load the EEPROM content asynchronously */ |
| 113 | + init_completion(&ec.complete); |
| 114 | + ec.ah = sc->sc_ah; |
| 115 | + |
| 116 | + err = request_firmware_nowait(THIS_MODULE, 1, name, sc->dev, GFP_KERNEL, |
| 117 | + &ec, ath9k_eeprom_request_cb); |
| 118 | + if (err < 0) { |
| 119 | + ath_err(ath9k_hw_common(ah), |
| 120 | + "EEPROM request failed\n"); |
| 121 | + return err; |
| 122 | + } |
| 123 | + |
| 124 | + wait_for_completion(&ec.complete); |
| 125 | + |
| 126 | + if (!ah->eeprom_blob) { |
| 127 | + ath_err(ath9k_hw_common(ah), |
| 128 | + "Unable to load EEPROM file %s\n", name); |
| 129 | + return -EINVAL; |
| 130 | + } |
| 131 | + |
| 132 | + return 0; |
| 133 | +} |
| 134 | + |
| 135 | +static void ath9k_eeprom_release(struct ath_softc *sc) |
| 136 | +{ |
| 137 | + release_firmware(sc->sc_ah->eeprom_blob); |
| 138 | +} |
| 139 | + |
| 140 | static int ath9k_init_softc(u16 devid, struct ath_softc *sc, |
| 141 | const struct ath_bus_ops *bus_ops) |
| 142 | { |
| 143 | @@ -585,6 +635,12 @@ static int ath9k_init_softc(u16 devid, s |
| 144 | ath_read_cachesize(common, &csz); |
| 145 | common->cachelsz = csz << 2; /* convert to bytes */ |
| 146 | |
| 147 | + if (pdata && pdata->eeprom_name) { |
| 148 | + ret = ath9k_eeprom_request(sc, pdata->eeprom_name); |
| 149 | + if (ret) |
| 150 | + goto err_eeprom; |
| 151 | + } |
| 152 | + |
| 153 | /* Initializes the hardware for all supported chipsets */ |
| 154 | ret = ath9k_hw_init(ah); |
| 155 | if (ret) |
| 156 | @@ -621,7 +677,8 @@ err_btcoex: |
| 157 | err_queues: |
| 158 | ath9k_hw_deinit(ah); |
| 159 | err_hw: |
| 160 | - |
| 161 | + ath9k_eeprom_release(sc); |
| 162 | +err_eeprom: |
| 163 | kfree(ah); |
| 164 | sc->sc_ah = NULL; |
| 165 | |
| 166 | @@ -884,6 +941,7 @@ static void ath9k_deinit_softc(struct at |
| 167 | if (sc->dfs_detector != NULL) |
| 168 | sc->dfs_detector->exit(sc->dfs_detector); |
| 169 | |
| 170 | + ath9k_eeprom_release(sc); |
| 171 | kfree(sc->sc_ah); |
| 172 | sc->sc_ah = NULL; |
| 173 | } |
| 174 | --- a/include/linux/ath9k_platform.h |
| 175 | +++ b/include/linux/ath9k_platform.h |
| 176 | @@ -22,6 +22,8 @@ |
| 177 | #define ATH9K_PLAT_EEP_MAX_WORDS 2048 |
| 178 | |
| 179 | struct ath9k_platform_data { |
| 180 | + const char *eeprom_name; |
| 181 | + |
| 182 | u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; |
| 183 | u8 *macaddr; |
| 184 | |
| 185 | |