sdhci: Add set_clock callback and a quirk for nonstandard clocks
[safe/jmp/linux-2.6] / drivers / scsi / aic94xx / aic94xx_seq.c
index d9b6da5..8f98e33 100644 (file)
 #define PAUSE_TRIES 1000
 
 static const struct firmware *sequencer_fw;
-static const char *sequencer_version;
 static u16 cseq_vecs[CSEQ_NUM_VECS], lseq_vecs[LSEQ_NUM_VECS], mode2_task,
        cseq_idle_loop, lseq_idle_loop;
-static u8 *cseq_code, *lseq_code;
+static const u8 *cseq_code, *lseq_code;
 static u32 cseq_code_size, lseq_code_size;
 
 static u16 first_scb_site_no = 0xFFFF;
@@ -61,7 +60,7 @@ static u16 last_scb_site_no;
  *
  * Return 0 on success, negative on failure.
  */
-int asd_pause_cseq(struct asd_ha_struct *asd_ha)
+static int asd_pause_cseq(struct asd_ha_struct *asd_ha)
 {
        int     count = PAUSE_TRIES;
        u32     arp2ctl;
@@ -88,7 +87,7 @@ int asd_pause_cseq(struct asd_ha_struct *asd_ha)
  *
  * Return 0 on success, negative on error.
  */
-int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
+static int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
 {
        u32     arp2ctl;
        int     count = PAUSE_TRIES;
@@ -116,7 +115,7 @@ int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
  *
  * Return 0 on success, negative on error.
  */
-static inline int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
+static int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
 {
        u32    arp2ctl;
        int    count = PAUSE_TRIES;
@@ -144,7 +143,7 @@ static inline int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
  *
  * Return 0 on success, negative on failure.
  */
-int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
+static int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
 {
        int lseq;
        int err = 0;
@@ -165,7 +164,7 @@ int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
  *
  * Return 0 on success, negative on error.
  */
-static inline int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
+static int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
 {
        u32 arp2ctl;
        int count = PAUSE_TRIES;
@@ -187,27 +186,6 @@ static inline int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
 }
 
 
-/**
- * asd_unpause_lseq - unpause the link sequencer(s)
- * @asd_ha: pointer to host adapter structure
- * @lseq_mask: mask of link sequencers of interest
- *
- * Return 0 on success, negative on failure.
- */
-int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
-{
-       int lseq;
-       int err = 0;
-
-       for_each_sequencer(lseq_mask, lseq_mask, lseq) {
-               err = asd_seq_unpause_lseq(asd_ha, lseq);
-               if (err)
-                       return err;
-       }
-
-       return err;
-}
-
 /* ---------- Downloading CSEQ/LSEQ microcode ---------- */
 
 static int asd_verify_cseq(struct asd_ha_struct *asd_ha, const u8 *_prog,
@@ -764,7 +742,7 @@ static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha,  int lseq)
        asd_write_reg_word(asd_ha, LmSEQ_FIRST_INV_SCB_SITE(lseq),
                           (u16)last_scb_site_no+1);
        asd_write_reg_word(asd_ha, LmSEQ_INTEN_SAVE(lseq),
-                           (u16) LmM0INTEN_MASK & 0xFFFF0000 >> 16);
+                           (u16) ((LmM0INTEN_MASK & 0xFFFF0000) >> 16));
        asd_write_reg_word(asd_ha, LmSEQ_INTEN_SAVE(lseq) + 2,
                            (u16) LmM0INTEN_MASK & 0xFFFF);
        asd_write_reg_byte(asd_ha, LmSEQ_LINK_RST_FRM_LEN(lseq), 0);
@@ -810,6 +788,8 @@ static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha,  int lseq)
        /* No delay for the first NOTIFY to be sent to the attached target. */
        asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_DOWN_COUNT(lseq),
                           ASD_NOTIFY_DOWN_COUNT);
+       asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_INITIAL_COUNT(lseq),
+                          ASD_NOTIFY_DOWN_COUNT);
 
        /* LSEQ Mode dependent, mode 0 and 1, page 1 setup. */
        for (i = 0; i < 2; i++) {
@@ -907,6 +887,16 @@ static void asd_init_scb_sites(struct asd_ha_struct *asd_ha)
                for (i = 0; i < ASD_SCB_SIZE; i += 4)
                        asd_scbsite_write_dword(asd_ha, site_no, i, 0);
 
+               /* Initialize SCB Site Opcode field to invalid. */
+               asd_scbsite_write_byte(asd_ha, site_no,
+                                      offsetof(struct scb_header, opcode),
+                                      0xFF);
+
+               /* Initialize SCB Site Flags field to mean a response
+                * frame has been received.  This means inadvertent
+                * frames received to be dropped. */
+               asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01);
+
                /* Workaround needed by SEQ to fix a SATA issue is to exclude
                 * certain SCB sites from the free list. */
                if (!SCB_SITE_VALID(site_no))
@@ -922,16 +912,6 @@ static void asd_init_scb_sites(struct asd_ha_struct *asd_ha)
                /* Q_NEXT field of the last SCB is invalidated. */
                asd_scbsite_write_word(asd_ha, site_no, 0, first_scb_site_no);
 
-               /* Initialize SCB Site Opcode field to invalid. */
-               asd_scbsite_write_byte(asd_ha, site_no,
-                                      offsetof(struct scb_header, opcode),
-                                      0xFF);
-
-               /* Initialize SCB Site Flags field to mean a response
-                * frame has been received.  This means inadvertent
-                * frames received to be dropped. */
-               asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01);
-
                first_scb_site_no = site_no;
                max_scbs++;
        }
@@ -1173,6 +1153,16 @@ static void asd_init_ddb_0(struct asd_ha_struct *asd_ha)
        set_bit(0, asd_ha->hw_prof.ddb_bitmap);
 }
 
+static void asd_seq_init_ddb_sites(struct asd_ha_struct *asd_ha)
+{
+       unsigned int i;
+       unsigned int ddb_site;
+
+       for (ddb_site = 0 ; ddb_site < ASD_MAX_DDBS; ddb_site++)
+               for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
+                       asd_ddbsite_write_dword(asd_ha, ddb_site, i, 0);
+}
+
 /**
  * asd_seq_setup_seqs -- setup and initialize central and link sequencers
  * @asd_ha: pointer to host adapter structure
@@ -1182,6 +1172,9 @@ static void asd_seq_setup_seqs(struct asd_ha_struct *asd_ha)
        int             lseq;
        u8              lseq_mask;
 
+       /* Initialize DDB sites */
+       asd_seq_init_ddb_sites(asd_ha);
+
        /* Initialize SCB sites. Done first to compute some values which
         * the rest of the init code depends on. */
        asd_init_scb_sites(asd_ha);
@@ -1232,10 +1225,18 @@ static int asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq)
        return asd_seq_unpause_lseq(asd_ha, lseq);
 }
 
+int asd_release_firmware(void)
+{
+       if (sequencer_fw)
+               release_firmware(sequencer_fw);
+       return 0;
+}
+
 static int asd_request_firmware(struct asd_ha_struct *asd_ha)
 {
        int err, i;
-       struct sequencer_file_header header, *hdr_ptr;
+       struct sequencer_file_header header;
+       const struct sequencer_file_header *hdr_ptr;
        u32 csum = 0;
        u16 *ptr_cseq_vecs, *ptr_lseq_vecs;
 
@@ -1249,12 +1250,11 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha)
        if (err)
                return err;
 
-       hdr_ptr = (struct sequencer_file_header *)sequencer_fw->data;
+       hdr_ptr = (const struct sequencer_file_header *)sequencer_fw->data;
 
        header.csum = le32_to_cpu(hdr_ptr->csum);
        header.major = le32_to_cpu(hdr_ptr->major);
        header.minor = le32_to_cpu(hdr_ptr->minor);
-       sequencer_version = hdr_ptr->version;
        header.cseq_table_offset = le32_to_cpu(hdr_ptr->cseq_table_offset);
        header.cseq_table_size = le32_to_cpu(hdr_ptr->cseq_table_size);
        header.lseq_table_offset = le32_to_cpu(hdr_ptr->lseq_table_offset);
@@ -1281,6 +1281,16 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha)
                return -EINVAL;
        }
 
+       asd_printk("Found sequencer Firmware version %d.%d (%s)\n",
+                  header.major, header.minor, hdr_ptr->version);
+
+       if (header.major != SAS_RAZOR_SEQUENCER_FW_MAJOR) {
+               asd_printk("Firmware Major Version Mismatch;"
+                          "driver requires version %d.X",
+                          SAS_RAZOR_SEQUENCER_FW_MAJOR);
+               return -EINVAL;
+       }
+
        ptr_cseq_vecs = (u16 *)&sequencer_fw->data[header.cseq_table_offset];
        ptr_lseq_vecs = (u16 *)&sequencer_fw->data[header.lseq_table_offset];
        mode2_task = header.mode2_task;
@@ -1313,7 +1323,6 @@ int asd_init_seqs(struct asd_ha_struct *asd_ha)
                return err;
        }
 
-       asd_printk("using sequencer %s\n", sequencer_version);
        err = asd_seq_download_seqs(asd_ha);
        if (err) {
                asd_printk("couldn't download sequencers for %s\n",
@@ -1369,14 +1378,15 @@ int asd_start_seqs(struct asd_ha_struct *asd_ha)
  * port_map_by_links is also used as the conn_mask byte in the
  * initiator/target port DDB.
  */
-void asd_update_port_links(struct asd_sas_phy *sas_phy)
+void asd_update_port_links(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
 {
-       struct asd_ha_struct *asd_ha = sas_phy->ha->lldd_ha;
-       const u8 phy_mask = (u8) sas_phy->port->phy_mask;
+       const u8 phy_mask = (u8) phy->asd_port->phy_mask;
        u8  phy_is_up;
        u8  mask;
        int i, err;
+       unsigned long flags;
 
+       spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
        for_each_phy(phy_mask, mask, i)
                asd_ddbsite_write_byte(asd_ha, 0,
                                       offsetof(struct asd_ddb_seq_shared,
@@ -1396,6 +1406,7 @@ void asd_update_port_links(struct asd_sas_phy *sas_phy)
                        break;
                }
        }
+       spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
 
        if (err)
                asd_printk("couldn't update DDB 0:error:%d\n", err);