1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2018 Nexenta Systems, Inc.
  14  */
  15 
  16 /*
  17  * A few simple method to access controller when it's in the base mode.
  18  */
  19 #include <smartpqi.h>
  20 
  21 /* ---- legacy SIS interface commands ---- */
  22 #define SIS_CMD_GET_ADAPTER_PROPERTIES  0x19
  23 #define SIS_CMD_INIT_BASE_STRUCT_ADDRESS        0x1b
  24 #define SIS_CMD_GET_PQI_CAPABILITIES            0x3000
  25 
  26 /* ---- used with SIS_CMD_GET_ADAPTER_PROPERTIES command ---- */
  27 #define SIS_EXTENDED_PROPERTIES_SUPPORTED       0x800000
  28 #define SIS_SMARTARRAY_FEATURES_SUPPORTED       0x2
  29 #define SIS_PQI_MODE_SUPPORTED                  0x4
  30 #define SIS_REQUIRED_EXTENDED_PROPERTIES        \
  31         (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED)
  32 
  33 #pragma pack(1)
  34 /* used for passing command parameters/results when issuing SIS commands */
  35 typedef struct sis_sync_cmd_params {
  36         uint32_t        mailbox[6];     /* mailboxes 0-5 */
  37 } sis_sync_cmd_params_t;
  38 
  39 #define SIS_BASE_STRUCT_REVISION        9
  40 
  41 typedef struct sis_base_struct {
  42         uint32_t        sb_revision;
  43         uint32_t        sb_flags;
  44         uint32_t        sb_error_buffer_paddr_low;
  45         uint32_t        sb_error_buffer_paddr_high;
  46         uint32_t        sb_error_elements_len;
  47         uint32_t        sb_error_elements_num;
  48 } sis_base_struct_t;
  49 #pragma pack()
  50 
  51 /* ---- Forward declaration for support functions ---- */
  52 static boolean_t sis_send_sync_cmd(pqi_state_t s, uint32_t cmd,
  53     sis_sync_cmd_params_t *params);
  54 
  55 uint32_t
  56 sis_read_scratch(pqi_state_t s)
  57 {
  58         return (G32(s, sis_driver_scratch));
  59 }
  60 
  61 void
  62 sis_write_scratch(pqi_state_t s, int mode)
  63 {
  64         S32(s, sis_driver_scratch, mode);
  65 }
  66 
  67 boolean_t
  68 sis_reenable_mode(pqi_state_t s)
  69 {
  70         int             loop_count;
  71         uint32_t        doorbell;
  72 
  73         S32(s, sis_host_to_ctrl_doorbell, SIS_REENABLE_SIS_MODE);
  74 
  75         for (loop_count = 0; loop_count < 1000; loop_count++) {
  76                 doorbell = G32(s, sis_ctrl_to_host_doorbell);
  77                 if ((doorbell & SIS_REENABLE_SIS_MODE) == 0) {
  78                         return (B_TRUE);
  79                 }
  80                 drv_usecwait(1000);     /* ---- Wait 1ms ---- */
  81         }
  82         return (B_FALSE);
  83 }
  84 
  85 boolean_t
  86 sis_wait_for_ctrl_ready(pqi_state_t s)
  87 {
  88         int             loop_count;
  89         uint32_t        status;
  90 
  91         for (loop_count = 0; loop_count < 1000; loop_count++) {
  92                 status = G32(s, sis_firmware_status);
  93                 if (status & SIS_CTRL_KERNEL_PANIC)
  94                         return (B_FALSE);
  95                 if (status & SIS_CTRL_KERNEL_UP)
  96                         return (B_TRUE);
  97                 drv_usecwait(1000);      /* ---- Wait 1ms ---- */
  98         }
  99         return (B_FALSE);
 100 }
 101 
 102 /*
 103  * sis_get_ctrl_props -- Verify we're talking to controller that speaks PQI
 104  */
 105 boolean_t
 106 sis_get_ctrl_props(pqi_state_t s)
 107 {
 108         sis_sync_cmd_params_t   p;
 109         uint32_t                property;
 110         uint32_t                extended_property;
 111 
 112         (void) memset(&p, 0, sizeof (p));
 113         if (sis_send_sync_cmd(s, SIS_CMD_GET_ADAPTER_PROPERTIES, &p) == B_FALSE)
 114                 return (B_FALSE);
 115 
 116         property = p.mailbox[1];
 117         if (!(property & SIS_EXTENDED_PROPERTIES_SUPPORTED))
 118                 return (B_FALSE);
 119 
 120         extended_property = p.mailbox[4];
 121         if ((extended_property & SIS_REQUIRED_EXTENDED_PROPERTIES) !=
 122             SIS_REQUIRED_EXTENDED_PROPERTIES)
 123                 return (B_FALSE);
 124 
 125         return (B_TRUE);
 126 }
 127 
 128 boolean_t
 129 sis_get_pqi_capabilities(pqi_state_t s)
 130 {
 131         sis_sync_cmd_params_t   p;
 132 
 133         (void) memset(&p, 0, sizeof (p));
 134         if (sis_send_sync_cmd(s, SIS_CMD_GET_PQI_CAPABILITIES, &p) == B_FALSE)
 135                 return (B_FALSE);
 136 
 137         s->s_max_sg_entries = p.mailbox[1];
 138         s->s_max_xfer_size = p.mailbox[2];
 139         s->s_max_outstanding_requests = p.mailbox[3];
 140         s->s_config_table_offset = p.mailbox[4];
 141         s->s_config_table_len = p.mailbox[5];
 142         return (B_TRUE);
 143 }
 144 
 145 #define SIS_ROUNDUP(a, align, type) \
 146         (type)((((uintptr_t)(a) + align - 1) & ~align))
 147 
 148 boolean_t
 149 sis_init_base_struct_addr(pqi_state_t s)
 150 {
 151         sis_base_struct_t       *base;
 152         pqi_dma_overhead_t      *o;
 153         sis_sync_cmd_params_t   params;
 154         boolean_t               rc;
 155         void                    *dma_addr;
 156 
 157         o = pqi_alloc_single(s, sizeof (*base) + SIS_BASE_STRUCT_ALIGNMENT);
 158         if (o == NULL)
 159                 return (B_FALSE);
 160 
 161         base = PQIALIGN_TYPED(o->alloc_memory, SIS_BASE_STRUCT_ALIGNMENT,
 162             sis_base_struct_t *);
 163         base->sb_revision = SIS_BASE_STRUCT_REVISION;
 164         base->sb_error_buffer_paddr_low = (uint32_t)s->s_error_dma->dma_addr;
 165         base->sb_error_buffer_paddr_high =
 166             (uint32_t)(s->s_error_dma->dma_addr >> 32);
 167         base->sb_error_elements_len = PQI_ERROR_BUFFER_ELEMENT_LENGTH;
 168         base->sb_error_elements_num = s->s_max_outstanding_requests;
 169 
 170         dma_addr = PQIALIGN_TYPED(o->dma_addr, SIS_BASE_STRUCT_ALIGNMENT,
 171             void *);
 172         (void) memset(&params, 0, sizeof (params));
 173         params.mailbox[1] = (uint32_t)(intptr_t)dma_addr;
 174         params.mailbox[2] = (uint32_t)((uint64_t)((intptr_t)dma_addr) >> 32);
 175         params.mailbox[3] = sizeof (*base);
 176         (void) ddi_dma_sync(o->handle, 0, 0, DDI_DMA_SYNC_FORDEV);
 177         rc = sis_send_sync_cmd(s, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, &params);
 178 
 179         pqi_free_single(s, o);
 180 
 181         return (rc);
 182 }
 183 
 184 /*
 185  * []------------------------------------------------------------[]
 186  * | Support functions for the visable legacy fucntions         |
 187  * []------------------------------------------------------------[]
 188  */
 189 static boolean_t
 190 sis_send_sync_cmd(pqi_state_t s, uint32_t cmd,
 191     sis_sync_cmd_params_t *params)
 192 {
 193         uint32_t        i;
 194         uint32_t        doorbell;
 195         uint32_t        cmd_status;
 196 
 197         /* Write the command to mailbox 0. */
 198         S32(s, sis_mailbox[0], cmd);
 199 
 200         /*
 201          * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used
 202          * when sending a command to the controller).
 203          */
 204         for (i = 1; i <= 4; i++)
 205                 S32(s, sis_mailbox[i], params->mailbox[i]);
 206 
 207         /* Clear the command doorbell. */
 208         S32(s, sis_ctrl_to_host_doorbell_clear,
 209             SIS_CLEAR_CTRL_TO_HOST_DOORBELL);
 210 
 211         /* Disable doorbell interrupts by masking all interrupts. */
 212         S32(s, sis_interrupt_mask, ~0);
 213 
 214         /*
 215          * Force the completion of the interrupt mask register write before
 216          * submitting the command.
 217          */
 218         (void) G32(s, sis_interrupt_mask);
 219 
 220         /* Submit the command to the controller. */
 221         S32(s, sis_host_to_ctrl_doorbell, SIS_CMD_READY);
 222 
 223         /*
 224          * Poll for command completion.  Note that the call to msleep() is at
 225          * the top of the loop in order to give the controller time to start
 226          * processing the command before we start polling.
 227          */
 228         for (i = 0; i < 10000; i++) {
 229                 drv_usecwait(1000);
 230                 doorbell = G32(s, sis_ctrl_to_host_doorbell);
 231                 if (doorbell & SIS_CMD_COMPLETE)
 232                         break;
 233         }
 234         if (i == 10000)
 235                 return (B_FALSE);
 236 
 237         /* Read the command status from mailbox 0. */
 238         cmd_status = G32(s, sis_mailbox[0]);
 239         if (cmd_status != SIS_CMD_STATUS_SUCCESS)
 240                 return (B_FALSE);
 241 
 242         /*
 243          * The command completed successfully, so save the command status and
 244          * read the values returned in mailboxes 1-5.
 245          */
 246         params->mailbox[0] = cmd_status;
 247         for (i = 1; i < ARRAY_SIZE(params->mailbox); i++)
 248                 params->mailbox[i] = G32(s, sis_mailbox[i]);
 249 
 250         return (B_TRUE);
 251 }