Author: bschmidt
Date: Sun Sep 19 12:47:41 2010
New Revision: 212854
URL: http://svn.freebsd.org/changeset/base/212854

Log:
  Rewrite parts of the calibration code which is run while bringing up
  the device:
  - unobscure some of the code by moving it into its own functions
  - get rid of some magic numbers
  - create similar structure as the reference driver has, this should
    make further syncs easier

Modified:
  head/sys/dev/iwn/if_iwn.c
  head/sys/dev/iwn/if_iwnvar.h

Modified: head/sys/dev/iwn/if_iwn.c
==============================================================================
--- head/sys/dev/iwn/if_iwn.c   Sun Sep 19 12:39:04 2010        (r212853)
+++ head/sys/dev/iwn/if_iwn.c   Sun Sep 19 12:47:41 2010        (r212854)
@@ -218,9 +218,13 @@ static void        iwn5000_ampdu_tx_start(struc
                    struct ieee80211_node *, uint8_t, uint16_t);
 static void    iwn5000_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t);
 #endif
-static int     iwn5000_send_calibration(struct iwn_softc *);
-static int     iwn5000_query_calibration(struct iwn_softc *);
-static void    iwn5000_rx_calib_results(struct iwn_softc *,
+static int     iwn5000_send_calib_results(struct iwn_softc *);
+static int     iwn5000_save_calib_result(struct iwn_softc *,
+                   struct iwn_phy_calib *, int, int);
+static void    iwn5000_free_calib_results(struct iwn_softc *);
+static int     iwn5000_chrystal_calib(struct iwn_softc *);
+static int     iwn5000_send_calib_query(struct iwn_softc *);
+static int     iwn5000_rx_calib_result(struct iwn_softc *,
                    struct iwn_rx_desc *, struct iwn_rx_data *);
 static int     iwn5000_send_wimax_coex(struct iwn_softc *);
 static int     iwn4965_post_alive(struct iwn_softc *);
@@ -705,6 +709,9 @@ iwn_hal_attach(struct iwn_softc *sc)
                sc->fwname = "iwn5000fw";
                sc->txchainmask = IWN_ANT_B;
                sc->rxchainmask = IWN_ANT_AB;
+               sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC |
+                   IWN_CALIB_BASE_BAND;
                break;
        case IWN_HW_REV_TYPE_5150:
                sc->sc_hal = &iwn5000_hal;
@@ -712,6 +719,8 @@ iwn_hal_attach(struct iwn_softc *sc)
                sc->fwname = "iwn5150fw";
                sc->txchainmask = IWN_ANT_A;
                sc->rxchainmask = IWN_ANT_AB;
+               sc->calib_init = IWN_CALIB_DC | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
                break;
        case IWN_HW_REV_TYPE_5300:
        case IWN_HW_REV_TYPE_5350:
@@ -720,6 +729,9 @@ iwn_hal_attach(struct iwn_softc *sc)
                sc->fwname = "iwn5000fw";
                sc->txchainmask = IWN_ANT_ABC;
                sc->rxchainmask = IWN_ANT_ABC;
+               sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC |
+                   IWN_CALIB_BASE_BAND;
                break;
        case IWN_HW_REV_TYPE_1000:
                sc->sc_hal = &iwn5000_hal;
@@ -727,6 +739,9 @@ iwn_hal_attach(struct iwn_softc *sc)
                sc->fwname = "iwn1000fw";
                sc->txchainmask = IWN_ANT_A;
                sc->rxchainmask = IWN_ANT_AB;
+               sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC |
+                   IWN_CALIB_BASE_BAND;
                break;
        case IWN_HW_REV_TYPE_6000:
                sc->sc_hal = &iwn5000_hal;
@@ -744,6 +759,8 @@ iwn_hal_attach(struct iwn_softc *sc)
                        sc->rxchainmask = IWN_ANT_ABC;
                        break;
                }
+               sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
                break;
        case IWN_HW_REV_TYPE_6050:
                sc->sc_hal = &iwn5000_hal;
@@ -751,6 +768,8 @@ iwn_hal_attach(struct iwn_softc *sc)
                sc->fwname = "iwn6050fw";
                sc->txchainmask = IWN_ANT_AB;
                sc->rxchainmask = IWN_ANT_AB;
+               sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_DC | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
                break;
        case IWN_HW_REV_TYPE_6005:
                sc->sc_hal = &iwn5000_hal;
@@ -758,6 +777,8 @@ iwn_hal_attach(struct iwn_softc *sc)
                sc->fwname = "iwn6005fw";
                sc->txchainmask = IWN_ANT_AB;
                sc->rxchainmask = IWN_ANT_AB;
+               sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO |
+                   IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND;
                break;
        default:
                device_printf(sc->sc_dev, "adapter type %d not supported\n",
@@ -842,6 +863,8 @@ iwn_cleanup(device_t dev)
                ieee80211_ifdetach(ic);
        }
 
+       iwn5000_free_calib_results(sc);
+
        /* Free DMA resources. */
        iwn_free_rx_ring(sc, &sc->rxq);
        if (sc->sc_hal != NULL)
@@ -1697,12 +1720,6 @@ iwn5000_read_eeprom(struct iwn_softc *sc
                sc->temp_off = temp - (volt / -5);
                DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n",
                    temp, volt, sc->temp_off);
-       } else {
-               /* Read crystal calibration. */
-               iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL,
-                   &sc->eeprom_crystal, sizeof (uint32_t));
-               DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n",
-               le32toh(sc->eeprom_crystal));
        }
 }
 
@@ -2550,7 +2567,7 @@ iwn_notif_intr(struct iwn_softc *sc)
                        break;
                }
                case IWN5000_CALIBRATION_RESULT:
-                       iwn5000_rx_calib_results(sc, desc, data);
+                       iwn5000_rx_calib_result(sc, desc, data);
                        break;
 
                case IWN5000_CALIBRATION_DONE:
@@ -5182,25 +5199,37 @@ iwn5000_ampdu_tx_stop(struct iwn_softc *
 
 /*
  * Send calibration results to the runtime firmware.  These results were
- * obtained on first boot from the initialization firmware.
+ * obtained on first boot from the initialization firmware, or by reading
+ * the EEPROM for crystal calibration.
  */
 static int
-iwn5000_send_calibration(struct iwn_softc *sc)
+iwn5000_send_calib_results(struct iwn_softc *sc)
 {
+       struct iwn_calib_info *calib_result;
        int idx, error;
 
-       for (idx = 0; idx < 5; idx++) {
-               if (sc->calibcmd[idx].buf == NULL)
-                       continue;       /* No results available. */
+       for (idx = 0; idx < IWN_CALIB_NUM; idx++) {
+               calib_result = &sc->calib_results[idx];
+
+               /* No support for this type of calibration. */
+               if ((sc->calib_init & (1 << idx)) == 0)
+                       continue;
+
+               /* No calibration result available. */
+               if (calib_result->buf == NULL)
+                       continue;
+
                DPRINTF(sc, IWN_DEBUG_CALIBRATE,
-                   "send calibration result idx=%d len=%d\n",
-                   idx, sc->calibcmd[idx].len);
-               error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf,
-                   sc->calibcmd[idx].len, 0);
+                   "%s: send calibration result idx=%d, len=%d\n",
+                   __func__, idx, calib_result->len);
+
+               error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, calib_result->buf,
+                   calib_result->len, 0);
                if (error != 0) {
                        device_printf(sc->sc_dev,
-                           "%s: could not send calibration result, error %d\n",
-                           __func__, error);
+                           "%s: could not send calibration result "
+                           "idx=%d, error=%d\n",
+                           __func__, idx, error);
                        return error;
                }
        }
@@ -5208,22 +5237,101 @@ iwn5000_send_calibration(struct iwn_soft
 }
 
 /*
- * Query calibration tables from the initialization firmware.  We do this
- * only once at first boot.  Called from a process context.
+ * Save calibration result at the given index.  The index determines
+ * in which order the results are sent to the runtime firmware.
  */
 static int
-iwn5000_query_calibration(struct iwn_softc *sc)
+iwn5000_save_calib_result(struct iwn_softc *sc, struct iwn_phy_calib *calib,
+    int len, int idx)
 {
+       struct iwn_calib_info *calib_result = &sc->calib_results[idx];
+
+       DPRINTF(sc, IWN_DEBUG_CALIBRATE,
+           "%s: saving calibration result code=%d, idx=%d, len=%d\n",
+           __func__, calib->code, idx, len);
+
+       if (calib_result->buf != NULL)
+               free(calib_result->buf, M_DEVBUF);
+
+       calib_result->buf = malloc(len, M_DEVBUF, M_NOWAIT);
+       if (calib_result->buf == NULL) {
+               device_printf(sc->sc_dev,
+                   "%s: not enough memory for calibration result "
+                   "code=%d, len=%d\n", __func__, calib->code, len);
+               return ENOMEM;
+       }
+
+       calib_result->len = len;
+       memcpy(calib_result->buf, calib, len);
+       return 0;
+}
+
+static void
+iwn5000_free_calib_results(struct iwn_softc *sc)
+{
+       struct iwn_calib_info *calib_result;
+       int idx;
+
+       for (idx = 0; idx < IWN_CALIB_NUM; idx++) {
+               calib_result = &sc->calib_results[idx];
+
+               if (calib_result->buf != NULL)
+                       free(calib_result->buf, M_DEVBUF);
+
+               calib_result->buf = NULL;
+               calib_result->len = 0;
+       }
+}
+
+/*
+ * Obtain the crystal calibration result from the EEPROM.
+ */
+static int
+iwn5000_chrystal_calib(struct iwn_softc *sc)
+{
+       struct iwn5000_phy_calib_crystal cmd;
+       uint32_t base, crystal;
+       uint16_t val;
+
+       /* Read crystal calibration. */
+       iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2);
+       base = le16toh(val);
+       iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, &crystal,
+           sizeof(uint32_t));
+       DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: crystal calibration=0x%08x\n",
+           __func__, le32toh(crystal));
+
+       memset(&cmd, 0, sizeof cmd);
+       cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
+       cmd.ngroups = 1;
+       cmd.isvalid = 1;
+       cmd.cap_pin[0] = le32toh(crystal) & 0xff;
+       cmd.cap_pin[1] = (le32toh(crystal) >> 16) & 0xff;
+
+       return iwn5000_save_calib_result(sc, (struct iwn_phy_calib *)&cmd,
+           sizeof cmd, IWN_CALIB_IDX_XTAL);
+}
+
+/*
+ * Query calibration results from the initialization firmware.  We do this
+ * only once at first boot.
+ */
+static int
+iwn5000_send_calib_query(struct iwn_softc *sc)
+{
+#define        CALIB_INIT_CFG  0xffffffff;
        struct iwn5000_calib_config cmd;
        int error;
 
        memset(&cmd, 0, sizeof cmd);
-       cmd.ucode.once.enable = 0xffffffff;
-       cmd.ucode.once.start  = 0xffffffff;
-       cmd.ucode.once.send   = 0xffffffff;
-       cmd.ucode.flags       = 0xffffffff;
-       DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
+       cmd.ucode.once.enable = CALIB_INIT_CFG;
+       cmd.ucode.once.start  = CALIB_INIT_CFG;
+       cmd.ucode.once.send   = CALIB_INIT_CFG;
+       cmd.ucode.flags       = CALIB_INIT_CFG;
+
+       DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: query calibration results\n",
            __func__);
+
        error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
        if (error != 0)
                return error;
@@ -5231,65 +5339,56 @@ iwn5000_query_calibration(struct iwn_sof
        /* Wait at most two seconds for calibration to complete. */
        if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE))
                error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 2 * hz);
+
        return error;
+#undef CALIB_INIT_CFG
 }
 
 /*
  * Process a CALIBRATION_RESULT notification sent by the initialization
- * firmware on response to a CMD_CALIB_CONFIG command (5000 only.)
+ * firmware on response to a CMD_CALIB_CONFIG command.
  */
-static void
-iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
+static int
+iwn5000_rx_calib_result(struct iwn_softc *sc, struct iwn_rx_desc *desc,
     struct iwn_rx_data *data)
 {
+#define        FRAME_SIZE_MASK         0x3fff
        struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1);
-       int len, idx = -1;
-
-       /* Runtime firmware should not send such a notification. */
-       if (sc->sc_flags & IWN_FLAG_CALIB_DONE)
-               return;
+       int len, idx;
 
        bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
-       len = (le32toh(desc->len) & 0x3fff) - 4;
+       len = (le32toh(desc->len) & FRAME_SIZE_MASK);
 
+       /* Remove length filed itself. */
+       len -= 4;
+
+       /*
+        * Determine the order in which the results will be send to the
+        * runtime firmware.
+        */
        switch (calib->code) {
        case IWN5000_PHY_CALIB_DC:
-               if (sc->hw_type == IWN_HW_REV_TYPE_5150 ||
-                   sc->hw_type == IWN_HW_REV_TYPE_6050)
-                       idx = 0;
+               idx = IWN_CALIB_IDX_DC;
                break;
        case IWN5000_PHY_CALIB_LO:
-               idx = 1;
+               idx = IWN_CALIB_IDX_LO;
                break;
        case IWN5000_PHY_CALIB_TX_IQ:
-               idx = 2;
+               idx = IWN_CALIB_IDX_TX_IQ;
                break;
        case IWN5000_PHY_CALIB_TX_IQ_PERIODIC:
-               if (sc->hw_type < IWN_HW_REV_TYPE_6000 &&
-                   sc->hw_type != IWN_HW_REV_TYPE_5150)
-                       idx = 3;
+               idx = IWN_CALIB_IDX_TX_IQ_PERIODIC;
                break;
        case IWN5000_PHY_CALIB_BASE_BAND:
-               idx = 4;
+               idx = IWN_CALIB_IDX_BASE_BAND;
                break;
-       }
-       if (idx == -1)  /* Ignore other results. */
-               return;
-
-       /* Save calibration result. */
-       if (sc->calibcmd[idx].buf != NULL)
-               free(sc->calibcmd[idx].buf, M_DEVBUF);
-       sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT);
-       if (sc->calibcmd[idx].buf == NULL) {
+       default:
                DPRINTF(sc, IWN_DEBUG_CALIBRATE,
-                   "not enough memory for calibration result %d\n",
-                   calib->code);
-               return;
+                  "%s: unknown calibration code=%d\n", __func__, calib->code);
+               return EINVAL;
        }
-       DPRINTF(sc, IWN_DEBUG_CALIBRATE,
-           "saving calibration result code=%d len=%d\n", calib->code, len);
-       sc->calibcmd[idx].len = len;
-       memcpy(sc->calibcmd[idx].buf, calib, len);
+       return iwn5000_save_calib_result(sc, calib, len, idx);
+#undef FRAME_SIZE_MASK
 }
 
 static int
@@ -5435,36 +5534,40 @@ iwn5000_post_alive(struct iwn_softc *sc)
                    __func__, error);
                return error;
        }
-       if (sc->hw_type != IWN_HW_REV_TYPE_5150) {
-               struct iwn5000_phy_calib_crystal cmd;
 
-               /* Perform crystal calibration. */
-               memset(&cmd, 0, sizeof cmd);
-               cmd.code = IWN5000_PHY_CALIB_CRYSTAL;
-               cmd.ngroups = 1;
-               cmd.isvalid = 1;
-               cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff;
-               cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff;
-               DPRINTF(sc, IWN_DEBUG_CALIBRATE,
-                   "sending crystal calibration %d, %d\n",
-                   cmd.cap_pin[0], cmd.cap_pin[1]);
-               error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0);
+       if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
+               /*
+                * Start calibration by setting and sending the chrystal
+                * calibration first, this must be done before we are able
+                * to query the other calibration results.
+                */
+               error = iwn5000_chrystal_calib(sc);
                if (error != 0) {
                        device_printf(sc->sc_dev,
-                           "%s: crystal calibration failed, error %d\n",
-                           __func__, error);
+                           "%s: could not set chrystal calibration, "
+                           "error=%d\n", __func__, error);
                        return error;
                }
-       }
-       if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) {
-               /* Query calibration from the initialization firmware. */
-               error = iwn5000_query_calibration(sc);
+               error = iwn5000_send_calib_results(sc);
                if (error != 0) {
                        device_printf(sc->sc_dev,
-                           "%s: could not query calibration, error %d\n",
+                           "%s: could not send chrystal calibration, "
+                           "error=%d\n", __func__, error);
+                       return error;
+               }
+
+               /*
+                * Query other calibration results from the initialization
+                * firmware.
+                */
+               error = iwn5000_send_calib_query(sc);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "%s: could not query calibration, error=%d\n",
                            __func__, error);
                        return error;
                }
+
                /*
                 * We have the calibration results now, reboot with the
                 * runtime firmware (call ourselves recursively!)
@@ -5472,8 +5575,11 @@ iwn5000_post_alive(struct iwn_softc *sc)
                iwn_hw_stop(sc);
                error = iwn_hw_init(sc);
        } else {
-               /* Send calibration results to runtime firmware. */
-               error = iwn5000_send_calibration(sc);
+               /*
+                * Send calibration results obtained from the initialization
+                * firmware to the runtime firmware.
+                */
+               error = iwn5000_send_calib_results(sc);
        }
        return error;
 }

Modified: head/sys/dev/iwn/if_iwnvar.h
==============================================================================
--- head/sys/dev/iwn/if_iwnvar.h        Sun Sep 19 12:39:04 2010        
(r212853)
+++ head/sys/dev/iwn/if_iwnvar.h        Sun Sep 19 12:47:41 2010        
(r212854)
@@ -263,9 +263,23 @@ struct iwn_softc {
 
        int                     calib_cnt;
        struct iwn_calib_state  calib;
+       u_int                   calib_init;
+#define        IWN_CALIB_XTAL                  (1 << IWN_CALIB_IDX_XTAL)
+#define        IWN_CALIB_DC                    (1 << IWN_CALIB_IDX_DC)
+#define        IWN_CALIB_LO                    (1 << IWN_CALIB_IDX_LO)
+#define        IWN_CALIB_TX_IQ                 (1 << IWN_CALIB_IDX_TX_IQ)
+#define        IWN_CALIB_TX_IQ_PERIODIC        (1 << 
IWN_CALIB_IDX_TX_IQ_PERIODIC)
+#define        IWN_CALIB_BASE_BAND             (1 << IWN_CALIB_IDX_BASE_BAND)
+#define        IWN_CALIB_NUM                   6
+       struct iwn_calib_info   calib_results[IWN_CALIB_NUM];
+#define        IWN_CALIB_IDX_XTAL              0
+#define        IWN_CALIB_IDX_DC                1
+#define        IWN_CALIB_IDX_LO                2
+#define        IWN_CALIB_IDX_TX_IQ             3
+#define        IWN_CALIB_IDX_TX_IQ_PERIODIC    4
+#define        IWN_CALIB_IDX_BASE_BAND         5
 
        struct iwn_fw_info      fw;
-       struct iwn_calib_info   calibcmd[5];
        uint32_t                errptr;
 
        struct iwn_rx_stat      last_rx_stat;
@@ -284,7 +298,6 @@ struct iwn_softc {
        uint16_t                rfcfg;
        uint8_t                 calib_ver;
        char                    eeprom_domain[4];
-       uint32_t                eeprom_crystal;
        int16_t                 eeprom_voltage;
        int8_t                  maxpwr2GHz;
        int8_t                  maxpwr5GHz;
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to