]> git.karo-electronics.de Git - linux-beck.git/commitdiff
mvsas: Add SGPIO support to Marvell 94xx
authorWilfried Weissmann <Wilfried.Weissmann@gmx.at>
Sun, 27 Dec 2015 19:21:19 +0000 (20:21 +0100)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 6 Jan 2016 20:18:49 +0000 (15:18 -0500)
Add SGPIO support to Marvell 94xx.

Signed-off-by: Wilfried Weissmann <Wilfried.Weissmann@gmx.at>
Reviewed-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/mvsas/mv_94xx.c
drivers/scsi/mvsas/mv_94xx.h
drivers/scsi/mvsas/mv_init.c
drivers/scsi/mvsas/mv_sas.c
drivers/scsi/mvsas/mv_sas.h

index 9270d15ff1a4977817bd24215f4f8ccbaf3a7c3c..f6fc4a70592489c5bb01589304414f9be523961b 100644 (file)
@@ -330,6 +330,51 @@ static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
        mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
 }
 
+static void mvs_94xx_sgpio_init(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs_ex - 0x10200;
+       u32 tmp;
+
+       tmp = mr32(MVS_HST_CHIP_CONFIG);
+       tmp |= 0x100;
+       mw32(MVS_HST_CHIP_CONFIG, tmp);
+
+       mw32(MVS_SGPIO_CTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+               MVS_SGPIO_CTRL_SDOUT_AUTO << MVS_SGPIO_CTRL_SDOUT_SHIFT);
+
+       mw32(MVS_SGPIO_CFG1 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+               8 << MVS_SGPIO_CFG1_LOWA_SHIFT |
+               8 << MVS_SGPIO_CFG1_HIA_SHIFT |
+               4 << MVS_SGPIO_CFG1_LOWB_SHIFT |
+               4 << MVS_SGPIO_CFG1_HIB_SHIFT |
+               2 << MVS_SGPIO_CFG1_MAXACTON_SHIFT |
+               1 << MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT
+       );
+
+       mw32(MVS_SGPIO_CFG2 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+               (300000 / 100) << MVS_SGPIO_CFG2_CLK_SHIFT | /* 100kHz clock */
+               66 << MVS_SGPIO_CFG2_BLINK_SHIFT /* (66 * 0,121 Hz?)*/
+       );
+
+       mw32(MVS_SGPIO_CFG0 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+               MVS_SGPIO_CFG0_ENABLE |
+               MVS_SGPIO_CFG0_BLINKA |
+               MVS_SGPIO_CFG0_BLINKB |
+               /* 3*4 data bits / PDU */
+               (12 - 1) << MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT
+       );
+
+       mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+               DEFAULT_SGPIO_BITS);
+
+       mw32(MVS_SGPIO_DSRC + MVS_SGPIO_HOST_OFFSET * mvi->id,
+               ((mvi->id * 4) + 3) << (8 * 3) |
+               ((mvi->id * 4) + 2) << (8 * 2) |
+               ((mvi->id * 4) + 1) << (8 * 1) |
+               ((mvi->id * 4) + 0) << (8 * 0));
+
+}
+
 static int mvs_94xx_init(struct mvs_info *mvi)
 {
        void __iomem *regs = mvi->regs;
@@ -533,6 +578,8 @@ static int mvs_94xx_init(struct mvs_info *mvi)
        /* Enable SRS interrupt */
        mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
 
+       mvs_94xx_sgpio_init(mvi);
+
        return 0;
 }
 
@@ -1005,6 +1052,92 @@ static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
 
 }
 
+static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
+                       u8 reg_type, u8 reg_index,
+                       u8 reg_count, u8 *write_data)
+{
+       int i;
+
+       switch (reg_type) {
+
+       case SAS_GPIO_REG_TX_GP:
+               if (reg_index == 0)
+                       return -EINVAL;
+
+               if (reg_count > 1)
+                       return -EINVAL;
+
+               if (reg_count == 0)
+                       return 0;
+
+               /* maximum supported bits = hosts * 4 drives * 3 bits */
+               for (i = 0; i < mvs_prv->n_host * 4 * 3; i++) {
+
+                       /* select host */
+                       struct mvs_info *mvi = mvs_prv->mvi[i/(4*3)];
+
+                       void __iomem *regs = mvi->regs_ex - 0x10200;
+
+                       int drive = (i/3) & (4-1); /* drive number on host */
+                       u32 block = mr32(MVS_SGPIO_DCTRL +
+                               MVS_SGPIO_HOST_OFFSET * mvi->id);
+
+
+                       /*
+                       * if bit is set then create a mask with the first
+                       * bit of the drive set in the mask ...
+                       */
+                       u32 bit = (write_data[i/8] & (1 << (i&(8-1)))) ?
+                               1<<(24-drive*8) : 0;
+
+                       /*
+                       * ... and then shift it to the right position based
+                       * on the led type (activity/id/fail)
+                       */
+                       switch (i%3) {
+                       case 0: /* activity */
+                               block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
+                                       << (24-drive*8));
+                                       /* hardwire activity bit to SOF */
+                               block |= LED_BLINKA_SOF << (
+                                       MVS_SGPIO_DCTRL_ACT_SHIFT +
+                                       (24-drive*8));
+                               break;
+                       case 1: /* id */
+                               block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
+                                       << (24-drive*8));
+                               block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
+                               break;
+                       case 2: /* fail */
+                               block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
+                                       << (24-drive*8));
+                               block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
+                               break;
+                       }
+
+                       mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+                               block);
+
+               }
+
+               return reg_count;
+
+       case SAS_GPIO_REG_TX:
+               if (reg_index + reg_count > mvs_prv->n_host)
+                       return -EINVAL;
+
+               for (i = 0; i < reg_count; i++) {
+                       struct mvs_info *mvi = mvs_prv->mvi[i+reg_index];
+                       void __iomem *regs = mvi->regs_ex - 0x10200;
+
+                       mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+                               be32_to_cpu(((u32 *) write_data)[i]));
+               }
+               return reg_count;
+       }
+       return -ENOSYS;
+}
+
 const struct mvs_dispatch mvs_94xx_dispatch = {
        "mv94xx",
        mvs_94xx_init,
@@ -1057,5 +1190,6 @@ const struct mvs_dispatch mvs_94xx_dispatch = {
        mvs_94xx_fix_dma,
        mvs_94xx_tune_interrupt,
        mvs_94xx_non_spec_ncq_error,
+       mvs_94xx_gpio_write,
 };
 
index 14e197497b4696971abfc81c492575f5a35e2db5..578960803a003316a0906c47e8219000840e147d 100644 (file)
@@ -38,6 +38,10 @@ enum VANIR_REVISION_ID {
        VANIR_C2_REV            = 0xC2,
 };
 
+enum host_registers {
+       MVS_HST_CHIP_CONFIG     = 0x10104,      /* chip configuration */
+};
+
 enum hw_registers {
        MVS_GBL_CTL             = 0x04,  /* global control */
        MVS_GBL_INT_STAT        = 0x00,  /* global irq status */
@@ -239,6 +243,73 @@ struct mvs_prd {
        __le32                  im_len;
 } __attribute__ ((packed));
 
+enum sgpio_registers {
+       MVS_SGPIO_HOST_OFFSET   = 0x100,        /* offset between hosts */
+
+       MVS_SGPIO_CFG0  = 0xc200,
+       MVS_SGPIO_CFG0_ENABLE   = (1 << 0),     /* enable pins */
+       MVS_SGPIO_CFG0_BLINKB   = (1 << 1),     /* blink generators */
+       MVS_SGPIO_CFG0_BLINKA   = (1 << 2),
+       MVS_SGPIO_CFG0_INVSCLK  = (1 << 3),     /* invert signal? */
+       MVS_SGPIO_CFG0_INVSLOAD = (1 << 4),
+       MVS_SGPIO_CFG0_INVSDOUT = (1 << 5),
+       MVS_SGPIO_CFG0_SLOAD_FALLEDGE = (1 << 6),       /* rise/fall edge? */
+       MVS_SGPIO_CFG0_SDOUT_FALLEDGE = (1 << 7),
+       MVS_SGPIO_CFG0_SDIN_RISEEDGE = (1 << 8),
+       MVS_SGPIO_CFG0_MAN_BITLEN_SHIFT = 18,   /* bits/frame manual mode */
+       MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT = 24,   /* bits/frame auto mode */
+
+       MVS_SGPIO_CFG1  = 0xc204,       /* blink timing register */
+       MVS_SGPIO_CFG1_LOWA_SHIFT       = 0,    /* A off time */
+       MVS_SGPIO_CFG1_HIA_SHIFT        = 4,    /* A on time */
+       MVS_SGPIO_CFG1_LOWB_SHIFT       = 8,    /* B off time */
+       MVS_SGPIO_CFG1_HIB_SHIFT        = 12,   /* B on time */
+       MVS_SGPIO_CFG1_MAXACTON_SHIFT   = 16,   /* max activity on time */
+
+               /* force activity off time */
+       MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT        = 20,
+               /* stretch activity on time */
+       MVS_SGPIO_CFG1_STRCHACTON_SHIFT = 24,
+               /* stretch activiity off time */
+       MVS_SGPIO_CFG1_STRCHACTOFF_SHIFT        = 28,
+
+
+       MVS_SGPIO_CFG2  = 0xc208,       /* clock speed register */
+       MVS_SGPIO_CFG2_CLK_SHIFT        = 0,
+       MVS_SGPIO_CFG2_BLINK_SHIFT      = 20,
+
+       MVS_SGPIO_CTRL  = 0xc20c,       /* SDOUT/SDIN mode control */
+       MVS_SGPIO_CTRL_SDOUT_AUTO       = 2,
+       MVS_SGPIO_CTRL_SDOUT_SHIFT      = 2,
+
+       MVS_SGPIO_DSRC  = 0xc220,       /* map ODn bits to drives */
+
+       MVS_SGPIO_DCTRL = 0xc238,
+       MVS_SGPIO_DCTRL_ERR_SHIFT       = 0,
+       MVS_SGPIO_DCTRL_LOC_SHIFT       = 3,
+       MVS_SGPIO_DCTRL_ACT_SHIFT       = 5,
+};
+
+enum sgpio_led_status {
+       LED_OFF = 0,
+       LED_ON  = 1,
+       LED_BLINKA      = 2,
+       LED_BLINKA_INV  = 3,
+       LED_BLINKA_SOF  = 4,
+       LED_BLINKA_EOF  = 5,
+       LED_BLINKB      = 6,
+       LED_BLINKB_INV  = 7,
+};
+
+#define DEFAULT_SGPIO_BITS ((LED_BLINKA_SOF << \
+                               MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 3) | \
+                       (LED_BLINKA_SOF << \
+                               MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 2) | \
+                       (LED_BLINKA_SOF << \
+                               MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 1) | \
+                       (LED_BLINKA_SOF << \
+                               MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 0))
+
 /*
  * these registers are accessed through port vendor
  * specific address/data registers
index 90fdf0e859e31fd9e72352d2ed19a889061e6ed4..6110ef3bfa92e7ab3a83fd36ab2733e50698cb74 100644 (file)
@@ -84,6 +84,8 @@ static struct sas_domain_function_template mvs_transport_ops = {
        .lldd_port_formed       = mvs_port_formed,
        .lldd_port_deformed     = mvs_port_deformed,
 
+       .lldd_write_gpio        = mvs_gpio_write,
+
 };
 
 static void mvs_phy_init(struct mvs_info *mvi, int phy_id)
index e712fe74595560daebfaf9073988e6a0704f10ed..83cd3ea2df41e50ea1291b565d15d2bd5ad43662 100644 (file)
@@ -2105,3 +2105,16 @@ int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
        return 0;
 }
 
+int mvs_gpio_write(struct sas_ha_struct *sha, u8 reg_type, u8 reg_index,
+                       u8 reg_count, u8 *write_data)
+{
+       struct mvs_prv_info *mvs_prv = sha->lldd_ha;
+       struct mvs_info *mvi = mvs_prv->mvi[0];
+
+       if (MVS_CHIP_DISP->gpio_write) {
+               return MVS_CHIP_DISP->gpio_write(mvs_prv, reg_type,
+                       reg_index, reg_count, write_data);
+       }
+
+       return -ENOSYS;
+}
index dc409c04747ae668c629ffe468965302fcc853ac..f9afd4cdd4c4e9784e2bf83736ca5c48ed675f7e 100644 (file)
@@ -103,6 +103,7 @@ enum dev_reset {
 };
 
 struct mvs_info;
+struct mvs_prv_info;
 
 struct mvs_dispatch {
        char *name;
@@ -172,6 +173,8 @@ struct mvs_dispatch {
                                int buf_len, int from, void *prd);
        void (*tune_interrupt)(struct mvs_info *mvi, u32 time);
        void (*non_spec_ncq_error)(struct mvs_info *mvi);
+       int (*gpio_write)(struct mvs_prv_info *mvs_prv, u8 reg_type,
+                       u8 reg_index, u8 reg_count, u8 *write_data);
 
 };
 
@@ -476,5 +479,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
 void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
 int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
 struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi, u8 reg_set);
+int mvs_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
+                       u8 reg_count, u8 *write_data);
 #endif