diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2012-01-04 04:32:49 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-01-16 02:43:04 -0500 |
commit | 9fee607f0b29adabd72265a847b8e421dff10d66 (patch) | |
tree | 7442c82b8e363a7aa1e87955e9619cf8e949cf3a /drivers/scsi/isci | |
parent | 594e566ae5985e0cc3185ac21509a86e90aad577 (diff) |
[SCSI] isci: oem parameter format v1.3 (cable select)
v1.3 allows the attenuation of the attached cables to be specified to
the driver in terms of 'short', 'medium', and 'long' (see probe_roms.h).
These settings (per phy) are retrieved from the platform oem-parameters
(BIOS rom) or via a module parameter override.
Reviewed-by: Jiangbi Liu <jiangbi.liu@intel.com>
Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r-- | drivers/scsi/isci/host.c | 43 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 18 | ||||
-rw-r--r-- | drivers/scsi/isci/init.c | 16 | ||||
-rw-r--r-- | drivers/scsi/isci/isci.h | 1 | ||||
-rw-r--r-- | drivers/scsi/isci/probe_roms.h | 38 |
5 files changed, 111 insertions, 5 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index ed1441c89577..9a52b984620f 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c | |||
@@ -1666,6 +1666,9 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost | |||
1666 | /* Default to no SSC operation. */ | 1666 | /* Default to no SSC operation. */ |
1667 | ihost->oem_parameters.controller.do_enable_ssc = false; | 1667 | ihost->oem_parameters.controller.do_enable_ssc = false; |
1668 | 1668 | ||
1669 | /* Default to short cables on all phys. */ | ||
1670 | ihost->oem_parameters.controller.cable_selection_mask = 0; | ||
1671 | |||
1669 | /* Initialize all of the port parameter information to narrow ports. */ | 1672 | /* Initialize all of the port parameter information to narrow ports. */ |
1670 | for (index = 0; index < SCI_MAX_PORTS; index++) { | 1673 | for (index = 0; index < SCI_MAX_PORTS; index++) { |
1671 | ihost->oem_parameters.ports[index].phy_mask = 0; | 1674 | ihost->oem_parameters.ports[index].phy_mask = 0; |
@@ -1953,12 +1956,46 @@ void sci_controller_power_control_queue_remove(struct isci_host *ihost, | |||
1953 | 1956 | ||
1954 | static int is_long_cable(int phy, unsigned char selection_byte) | 1957 | static int is_long_cable(int phy, unsigned char selection_byte) |
1955 | { | 1958 | { |
1956 | return 0; | 1959 | return !!(selection_byte & (1 << phy)); |
1957 | } | 1960 | } |
1958 | 1961 | ||
1959 | static int is_medium_cable(int phy, unsigned char selection_byte) | 1962 | static int is_medium_cable(int phy, unsigned char selection_byte) |
1960 | { | 1963 | { |
1961 | return 0; | 1964 | return !!(selection_byte & (1 << (phy + 4))); |
1965 | } | ||
1966 | |||
1967 | static enum cable_selections decode_selection_byte( | ||
1968 | int phy, | ||
1969 | unsigned char selection_byte) | ||
1970 | { | ||
1971 | return ((selection_byte & (1 << phy)) ? 1 : 0) | ||
1972 | + (selection_byte & (1 << (phy + 4)) ? 2 : 0); | ||
1973 | } | ||
1974 | |||
1975 | static unsigned char *to_cable_select(struct isci_host *ihost) | ||
1976 | { | ||
1977 | if (is_cable_select_overridden()) | ||
1978 | return ((unsigned char *)&cable_selection_override) | ||
1979 | + ihost->id; | ||
1980 | else | ||
1981 | return &ihost->oem_parameters.controller.cable_selection_mask; | ||
1982 | } | ||
1983 | |||
1984 | enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy) | ||
1985 | { | ||
1986 | return decode_selection_byte(phy, *to_cable_select(ihost)); | ||
1987 | } | ||
1988 | |||
1989 | char *lookup_cable_names(enum cable_selections selection) | ||
1990 | { | ||
1991 | static char *cable_names[] = { | ||
1992 | [short_cable] = "short", | ||
1993 | [long_cable] = "long", | ||
1994 | [medium_cable] = "medium", | ||
1995 | [undefined_cable] = "<undefined, assumed long>" /* bit 0==1 */ | ||
1996 | }; | ||
1997 | return (selection <= undefined_cable) ? cable_names[selection] | ||
1998 | : cable_names[undefined_cable]; | ||
1962 | } | 1999 | } |
1963 | 2000 | ||
1964 | #define AFE_REGISTER_WRITE_DELAY 10 | 2001 | #define AFE_REGISTER_WRITE_DELAY 10 |
@@ -1967,10 +2004,10 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) | |||
1967 | { | 2004 | { |
1968 | struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; | 2005 | struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; |
1969 | const struct sci_oem_params *oem = &ihost->oem_parameters; | 2006 | const struct sci_oem_params *oem = &ihost->oem_parameters; |
1970 | unsigned char cable_selection_mask = 0; | ||
1971 | struct pci_dev *pdev = ihost->pdev; | 2007 | struct pci_dev *pdev = ihost->pdev; |
1972 | u32 afe_status; | 2008 | u32 afe_status; |
1973 | u32 phy_id; | 2009 | u32 phy_id; |
2010 | unsigned char cable_selection_mask = *to_cable_select(ihost); | ||
1974 | 2011 | ||
1975 | /* Clear DFX Status registers */ | 2012 | /* Clear DFX Status registers */ |
1976 | writel(0x0081000f, &afe->afe_dfx_master_control0); | 2013 | writel(0x0081000f, &afe->afe_dfx_master_control0); |
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 4573075a6b97..5477f0fa8233 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h | |||
@@ -447,6 +447,24 @@ static inline bool is_c1(struct pci_dev *pdev) | |||
447 | return false; | 447 | return false; |
448 | } | 448 | } |
449 | 449 | ||
450 | enum cable_selections { | ||
451 | short_cable = 0, | ||
452 | long_cable = 1, | ||
453 | medium_cable = 2, | ||
454 | undefined_cable = 3 | ||
455 | }; | ||
456 | |||
457 | #define CABLE_OVERRIDE_DISABLED (0x10000) | ||
458 | |||
459 | static inline int is_cable_select_overridden(void) | ||
460 | { | ||
461 | return cable_selection_override < CABLE_OVERRIDE_DISABLED; | ||
462 | } | ||
463 | |||
464 | enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy); | ||
465 | void validate_cable_selections(struct isci_host *ihost); | ||
466 | char *lookup_cable_names(enum cable_selections); | ||
467 | |||
450 | /* set hw control for 'activity', even though active enclosures seem to drive | 468 | /* set hw control for 'activity', even though active enclosures seem to drive |
451 | * the activity led on their own. Skip setting FSENG control on 'status' due | 469 | * the activity led on their own. Skip setting FSENG control on 'status' due |
452 | * to unexpected operation and 'error' due to not being a supported automatic | 470 | * to unexpected operation and 'error' due to not being a supported automatic |
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 8a34fd92e42e..1047108ba4de 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c | |||
@@ -122,6 +122,14 @@ unsigned char max_concurr_spinup; | |||
122 | module_param(max_concurr_spinup, byte, 0); | 122 | module_param(max_concurr_spinup, byte, 0); |
123 | MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup"); | 123 | MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup"); |
124 | 124 | ||
125 | uint cable_selection_override = CABLE_OVERRIDE_DISABLED; | ||
126 | module_param(cable_selection_override, uint, 0); | ||
127 | |||
128 | MODULE_PARM_DESC(cable_selection_override, | ||
129 | "This field indicates length of the SAS/SATA cable between " | ||
130 | "host and device. If any bits > 15 are set (default) " | ||
131 | "indicates \"use platform defaults\""); | ||
132 | |||
125 | static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf) | 133 | static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf) |
126 | { | 134 | { |
127 | struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev); | 135 | struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev); |
@@ -412,6 +420,14 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) | |||
412 | return NULL; | 420 | return NULL; |
413 | isci_host->shost = shost; | 421 | isci_host->shost = shost; |
414 | 422 | ||
423 | dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: " | ||
424 | "{%s, %s, %s, %s}\n", | ||
425 | (is_cable_select_overridden() ? "* " : ""), isci_host->id, | ||
426 | lookup_cable_names(decode_cable_selection(isci_host, 3)), | ||
427 | lookup_cable_names(decode_cable_selection(isci_host, 2)), | ||
428 | lookup_cable_names(decode_cable_selection(isci_host, 1)), | ||
429 | lookup_cable_names(decode_cable_selection(isci_host, 0))); | ||
430 | |||
415 | err = isci_host_init(isci_host); | 431 | err = isci_host_init(isci_host); |
416 | if (err) | 432 | if (err) |
417 | goto err_shost; | 433 | goto err_shost; |
diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h index 8efeb6b08321..234ab46fce33 100644 --- a/drivers/scsi/isci/isci.h +++ b/drivers/scsi/isci/isci.h | |||
@@ -480,6 +480,7 @@ extern u16 ssp_inactive_to; | |||
480 | extern u16 stp_inactive_to; | 480 | extern u16 stp_inactive_to; |
481 | extern unsigned char phy_gen; | 481 | extern unsigned char phy_gen; |
482 | extern unsigned char max_concurr_spinup; | 482 | extern unsigned char max_concurr_spinup; |
483 | extern uint cable_selection_override; | ||
483 | 484 | ||
484 | irqreturn_t isci_msix_isr(int vec, void *data); | 485 | irqreturn_t isci_msix_isr(int vec, void *data); |
485 | irqreturn_t isci_intx_isr(int vec, void *data); | 486 | irqreturn_t isci_intx_isr(int vec, void *data); |
diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index 42dd05414f3b..bb0e9d4d97c9 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h | |||
@@ -193,7 +193,8 @@ struct isci_oem_hdr { | |||
193 | 193 | ||
194 | #define ISCI_ROM_VER_1_0 0x10 | 194 | #define ISCI_ROM_VER_1_0 0x10 |
195 | #define ISCI_ROM_VER_1_1 0x11 | 195 | #define ISCI_ROM_VER_1_1 0x11 |
196 | #define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_1 | 196 | #define ISCI_ROM_VER_1_3 0x13 |
197 | #define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_3 | ||
197 | 198 | ||
198 | /* Allowed PORT configuration modes APC Automatic PORT configuration mode is | 199 | /* Allowed PORT configuration modes APC Automatic PORT configuration mode is |
199 | * defined by the OEM configuration parameters providing no PHY_MASK parameters | 200 | * defined by the OEM configuration parameters providing no PHY_MASK parameters |
@@ -270,7 +271,40 @@ struct sci_oem_params { | |||
270 | }; | 271 | }; |
271 | uint8_t do_enable_ssc; | 272 | uint8_t do_enable_ssc; |
272 | }; | 273 | }; |
273 | uint8_t reserved; | 274 | /* |
275 | * This field indicates length of the SAS/SATA cable between | ||
276 | * host and device. | ||
277 | * This field is used make relationship between analog | ||
278 | * parameters of the phy in the silicon and length of the cable. | ||
279 | * Supported cable attenuation levels: | ||
280 | * "short"- up to 3m, "medium"-3m to 6m, and "long"- more than | ||
281 | * 6m. | ||
282 | * | ||
283 | * This is bit mask field: | ||
284 | * | ||
285 | * BIT: (MSB) 7 6 5 4 | ||
286 | * ASSIGNMENT: <phy3><phy2><phy1><phy0> - Medium cable | ||
287 | * length assignment | ||
288 | * BIT: 3 2 1 0 (LSB) | ||
289 | * ASSIGNMENT: <phy3><phy2><phy1><phy0> - Long cable length | ||
290 | * assignment | ||
291 | * | ||
292 | * BITS 7-4 are set when the cable length is assigned to medium | ||
293 | * BITS 3-0 are set when the cable length is assigned to long | ||
294 | * | ||
295 | * The BIT positions are clear when the cable length is | ||
296 | * assigned to short. | ||
297 | * | ||
298 | * Setting the bits for both long and medium cable length is | ||
299 | * undefined. | ||
300 | * | ||
301 | * A value of 0x84 would assign | ||
302 | * phy3 - medium | ||
303 | * phy2 - long | ||
304 | * phy1 - short | ||
305 | * phy0 - short | ||
306 | */ | ||
307 | uint8_t cable_selection_mask; | ||
274 | } controller; | 308 | } controller; |
275 | 309 | ||
276 | struct { | 310 | struct { |