#endif /* CONFIG_IWL5000_RUN_TIME_CALIB */
 
+
+
 static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
                                           size_t offset)
 {
        return &priv->eeprom[address];
 }
 
+/*
+ *  Calibration
+ */
+static int iwl5000_send_Xtal_calib(struct iwl_priv *priv)
+{
+       u16 *xtal_calib = (u16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_XTAL);
+
+       struct iwl5000_calibration cal_cmd = {
+               .op_code = IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD,
+               .data = {
+                       (u8)xtal_calib[0],
+                       (u8)xtal_calib[1],
+               }
+       };
+
+       return iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+                               sizeof(cal_cmd), &cal_cmd);
+}
+
+static int iwl5000_send_calib_results(struct iwl_priv *priv)
+{
+       int ret = 0;
+
+       if (priv->calib_results.lo_res)
+               ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+                                       priv->calib_results.lo_res_len,
+                                       priv->calib_results.lo_res);
+       if (ret)
+               goto err;
+
+
+       if (priv->calib_results.tx_iq_res)
+               ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+                               priv->calib_results.tx_iq_res_len,
+                               priv->calib_results.tx_iq_res);
+
+       if (ret)
+               goto err;
+
+       if (priv->calib_results.tx_iq_perd_res)
+               ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+                                     priv->calib_results.tx_iq_perd_res_len,
+                                     priv->calib_results.tx_iq_perd_res);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       IWL_ERROR("Error %d\n", ret);
+       return ret;
+}
+
+static int iwl5000_send_calib_cfg(struct iwl_priv *priv)
+{
+       struct iwl5000_calib_cfg_cmd calib_cfg_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = CALIBRATION_CFG_CMD,
+               .len = sizeof(struct iwl5000_calib_cfg_cmd),
+               .data = &calib_cfg_cmd,
+       };
+
+       memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
+       calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
+       calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL;
+       calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL;
+       calib_cfg_cmd.ucd_calib_cfg.flags = IWL_CALIB_INIT_CFG_ALL;
+
+       return iwl_send_cmd(priv, &cmd);
+}
+
+static void iwl5000_rx_calib_result(struct iwl_priv *priv,
+                            struct iwl_rx_mem_buffer *rxb)
+{
+       struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+       struct iwl5000_calib_hdr *hdr = (struct iwl5000_calib_hdr *)pkt->u.raw;
+       int len = le32_to_cpu(pkt->len) & FH_RSCSR_FRAME_SIZE_MSK;
+
+       iwl_free_calib_results(priv);
+
+       /* reduce the size of the length field itself */
+       len -= 4;
+
+       switch (hdr->op_code) {
+       case IWL5000_PHY_CALIBRATE_LO_CMD:
+               priv->calib_results.lo_res = kzalloc(len, GFP_ATOMIC);
+               priv->calib_results.lo_res_len = len;
+               memcpy(priv->calib_results.lo_res, pkt->u.raw, len);
+               break;
+       case IWL5000_PHY_CALIBRATE_TX_IQ_CMD:
+               priv->calib_results.tx_iq_res = kzalloc(len, GFP_ATOMIC);
+               priv->calib_results.tx_iq_res_len = len;
+               memcpy(priv->calib_results.tx_iq_res, pkt->u.raw, len);
+               break;
+       case IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD:
+               priv->calib_results.tx_iq_perd_res = kzalloc(len, GFP_ATOMIC);
+               priv->calib_results.tx_iq_perd_res_len = len;
+               memcpy(priv->calib_results.tx_iq_perd_res, pkt->u.raw, len);
+               break;
+       default:
+               IWL_ERROR("Unknown calibration notification %d\n",
+                         hdr->op_code);
+               return;
+       }
+}
+
+static void iwl5000_rx_calib_complete(struct iwl_priv *priv,
+                              struct iwl_rx_mem_buffer *rxb)
+{
+       IWL_DEBUG_INFO("Init. calibration is completed, restarting fw.\n");
+       queue_work(priv->workqueue, &priv->restart);
+}
+
 /*
  * ucode
  */
                goto restart;
        }
 
+       iwl5000_send_calib_cfg(priv);
        return;
 
 restart:
        iwl_release_nic_access(priv);
        spin_unlock_irqrestore(&priv->lock, flags);
 
+
        iwl5000_send_wimax_coex(priv);
 
+       iwl5000_send_Xtal_calib(priv);
+
+       if (priv->ucode_type == UCODE_RT)
+               iwl5000_send_calib_results(priv);
+
        return 0;
 }
 
 
 static void iwl5000_rx_handler_setup(struct iwl_priv *priv)
 {
+       /* init calibration handlers */
+       priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] =
+                                       iwl5000_rx_calib_result;
+       priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] =
+                                       iwl5000_rx_calib_complete;
 }
 
+
 static int iwl5000_hw_valid_rtc_data_addr(u32 addr)
 {
        return (addr >= RTC_DATA_LOWER_BOUND) &&
 
        IWL5000_PHY_CALIBRATE_AGC_TABLE_CMD     = 14,
        IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD   = 15,
        IWL5000_PHY_CALIBRATE_BASE_BAND_CMD     = 16,
+       IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD    = 17,
        IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18,
        IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19,
 };
 
+enum {
+       CALIBRATION_CFG_CMD = 0x65,
+       CALIBRATION_RES_NOTIFICATION = 0x66,
+       CALIBRATION_COMPLETE_NOTIFICATION = 0x67
+};
+
+struct iwl_cal_crystal_freq_cmd {
+       u8 cap_pin1;
+       u8 cap_pin2;
+} __attribute__ ((packed));
+
+struct iwl5000_calibration {
+       u8 op_code;
+       u8 first_group;
+       u8 num_groups;
+       u8 all_data_valid;
+       struct iwl_cal_crystal_freq_cmd data;
+} __attribute__ ((packed));
+
+#define IWL_CALIB_INIT_CFG_ALL __constant_cpu_to_le32(0xffffffff)
+
+struct iwl_calib_cfg_elmnt_s {
+       __le32 is_enable;
+       __le32 start;
+       __le32 send_res;
+       __le32 apply_res;
+       __le32 reserved;
+} __attribute__ ((packed));
+
+struct iwl_calib_cfg_status_s {
+       struct iwl_calib_cfg_elmnt_s once;
+       struct iwl_calib_cfg_elmnt_s perd;
+       __le32 flags;
+} __attribute__ ((packed));
+
+struct iwl5000_calib_cfg_cmd {
+       struct iwl_calib_cfg_status_s ucd_calib_cfg;
+       struct iwl_calib_cfg_status_s drv_calib_cfg;
+       __le32 reserved1;
+} __attribute__ ((packed));
+
+struct iwl5000_calib_hdr {
+       u8 op_code;
+       u8 first_group;
+       u8 groups_num;
+       u8 data_valid;
+} __attribute__ ((packed));
+
 struct iwl5000_calibration_chain_noise_reset_cmd {
        u8 op_code;     /* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
        u8 flags;       /* not used */
                struct iwl4965_notif_statistics stats;
                struct iwl4965_compressed_ba_resp compressed_ba;
                struct iwl4965_missed_beacon_notif missed_beacon;
+               struct iwl5000_calibration calib;
                __le32 status;
                u8 raw[0];
        } u;