aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-09-02 00:18:31 -0400
committerJames Bottomley <JBottomley@Parallels.com>2011-09-22 07:01:56 -0400
commitad4f4c1de80abdda5d55315289505598aa78e355 (patch)
tree268882b3dff78ee8fb9977a5f5aa209f7f171fb5 /drivers/scsi
parent13257cfbc57e9cf84fd9fe0cb7a909b3fb4f7482 (diff)
[SCSI] isci: initial sgpio write support
Basic support to initialize the gpio unit, accept an incomming SAS_GPIO_REG_TX_GP bitstream, and translate it to the ODx.n fields in the hardware registers. If register indexes outside the supported range are specified in the SMP frame we simply accept the write and return how many registers (SFF-8485) were written (libsas reports this as residue in the request). Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/isci/host.c69
-rw-r--r--drivers/scsi/isci/host.h15
-rw-r--r--drivers/scsi/isci/init.c3
3 files changed, 87 insertions, 0 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 6981b773a88d..f07f30fada1b 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -1263,6 +1263,10 @@ void isci_host_deinit(struct isci_host *ihost)
1263{ 1263{
1264 int i; 1264 int i;
1265 1265
1266 /* disable output data selects */
1267 for (i = 0; i < isci_gpio_count(ihost); i++)
1268 writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
1269
1266 isci_host_change_state(ihost, isci_stopping); 1270 isci_host_change_state(ihost, isci_stopping);
1267 for (i = 0; i < SCI_MAX_PORTS; i++) { 1271 for (i = 0; i < SCI_MAX_PORTS; i++) {
1268 struct isci_port *iport = &ihost->ports[i]; 1272 struct isci_port *iport = &ihost->ports[i];
@@ -1281,6 +1285,12 @@ void isci_host_deinit(struct isci_host *ihost)
1281 spin_unlock_irq(&ihost->scic_lock); 1285 spin_unlock_irq(&ihost->scic_lock);
1282 1286
1283 wait_for_stop(ihost); 1287 wait_for_stop(ihost);
1288
1289 /* disable sgpio: where the above wait should give time for the
1290 * enclosure to sample the gpios going inactive
1291 */
1292 writel(0, &ihost->scu_registers->peg0.sgpio.interface_control);
1293
1284 sci_controller_reset(ihost); 1294 sci_controller_reset(ihost);
1285 1295
1286 /* Cancel any/all outstanding port timers */ 1296 /* Cancel any/all outstanding port timers */
@@ -2365,6 +2375,12 @@ int isci_host_init(struct isci_host *ihost)
2365 for (i = 0; i < SCI_MAX_PHYS; i++) 2375 for (i = 0; i < SCI_MAX_PHYS; i++)
2366 isci_phy_init(&ihost->phys[i], ihost, i); 2376 isci_phy_init(&ihost->phys[i], ihost, i);
2367 2377
2378 /* enable sgpio */
2379 writel(1, &ihost->scu_registers->peg0.sgpio.interface_control);
2380 for (i = 0; i < isci_gpio_count(ihost); i++)
2381 writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
2382 writel(0, &ihost->scu_registers->peg0.sgpio.vendor_specific_code);
2383
2368 for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { 2384 for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
2369 struct isci_remote_device *idev = &ihost->devices[i]; 2385 struct isci_remote_device *idev = &ihost->devices[i];
2370 2386
@@ -2760,3 +2776,56 @@ enum sci_task_status sci_controller_start_task(struct isci_host *ihost,
2760 2776
2761 return status; 2777 return status;
2762} 2778}
2779
2780static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_count, u8 *write_data)
2781{
2782 int d;
2783
2784 /* no support for TX_GP_CFG */
2785 if (reg_index == 0)
2786 return -EINVAL;
2787
2788 for (d = 0; d < isci_gpio_count(ihost); d++) {
2789 u32 val = 0x444; /* all ODx.n clear */
2790 int i;
2791
2792 for (i = 0; i < 3; i++) {
2793 int bit = (i << 2) + 2;
2794
2795 bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i),
2796 write_data, reg_index,
2797 reg_count);
2798 if (bit < 0)
2799 break;
2800
2801 /* if od is set, clear the 'invert' bit */
2802 val &= ~(bit << ((i << 2) + 2));
2803 }
2804
2805 if (i < 3)
2806 break;
2807 writel(val, &ihost->scu_registers->peg0.sgpio.output_data_select[d]);
2808 }
2809
2810 /* unless reg_index is > 1, we should always be able to write at
2811 * least one register
2812 */
2813 return d > 0;
2814}
2815
2816int isci_gpio_write(struct sas_ha_struct *sas_ha, u8 reg_type, u8 reg_index,
2817 u8 reg_count, u8 *write_data)
2818{
2819 struct isci_host *ihost = sas_ha->lldd_ha;
2820 int written;
2821
2822 switch (reg_type) {
2823 case SAS_GPIO_REG_TX_GP:
2824 written = sci_write_gpio_tx_gp(ihost, reg_index, reg_count, write_data);
2825 break;
2826 default:
2827 written = -EINVAL;
2828 }
2829
2830 return written;
2831}
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 9f33831a2f04..646051afd3cb 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -440,6 +440,18 @@ static inline bool is_c0(struct pci_dev *pdev)
440 return false; 440 return false;
441} 441}
442 442
443/* set hw control for 'activity', even though active enclosures seem to drive
444 * the activity led on their own. Skip setting FSENG control on 'status' due
445 * to unexpected operation and 'error' due to not being a supported automatic
446 * FSENG output
447 */
448#define SGPIO_HW_CONTROL 0x00000443
449
450static inline int isci_gpio_count(struct isci_host *ihost)
451{
452 return ARRAY_SIZE(ihost->scu_registers->peg0.sgpio.output_data_select);
453}
454
443void sci_controller_post_request(struct isci_host *ihost, 455void sci_controller_post_request(struct isci_host *ihost,
444 u32 request); 456 u32 request);
445void sci_controller_release_frame(struct isci_host *ihost, 457void sci_controller_release_frame(struct isci_host *ihost,
@@ -542,4 +554,7 @@ void sci_port_configuration_agent_construct(
542enum sci_status sci_port_configuration_agent_initialize( 554enum sci_status sci_port_configuration_agent_initialize(
543 struct isci_host *ihost, 555 struct isci_host *ihost,
544 struct sci_port_configuration_agent *port_agent); 556 struct sci_port_configuration_agent *port_agent);
557
558int isci_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
559 u8 reg_count, u8 *write_data);
545#endif 560#endif
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index 29aa34efb0f5..43fe840fbe9c 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -192,6 +192,9 @@ static struct sas_domain_function_template isci_transport_ops = {
192 192
193 /* Phy management */ 193 /* Phy management */
194 .lldd_control_phy = isci_phy_control, 194 .lldd_control_phy = isci_phy_control,
195
196 /* GPIO support */
197 .lldd_write_gpio = isci_gpio_write,
195}; 198};
196 199
197 200