162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Parallel SCSI (SPI) transport specific attributes exported to sysfs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. 662306a36Sopenharmony_ci * Copyright (c) 2004, 2005 James Bottomley <James.Bottomley@SteelEye.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/ctype.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/workqueue.h> 1262306a36Sopenharmony_ci#include <linux/blkdev.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/sysfs.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/suspend.h> 1762306a36Sopenharmony_ci#include <scsi/scsi.h> 1862306a36Sopenharmony_ci#include "scsi_priv.h" 1962306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2062306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2162306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2262306a36Sopenharmony_ci#include <scsi/scsi_eh.h> 2362306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 2462306a36Sopenharmony_ci#include <scsi/scsi_transport.h> 2562306a36Sopenharmony_ci#include <scsi/scsi_transport_spi.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define SPI_NUM_ATTRS 14 /* increase this if you add attributes */ 2862306a36Sopenharmony_ci#define SPI_OTHER_ATTRS 1 /* Increase this if you add "always 2962306a36Sopenharmony_ci * on" attributes */ 3062306a36Sopenharmony_ci#define SPI_HOST_ATTRS 1 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define SPI_MAX_ECHO_BUFFER_SIZE 4096 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define DV_LOOPS 3 3562306a36Sopenharmony_ci#define DV_TIMEOUT (10*HZ) 3662306a36Sopenharmony_ci#define DV_RETRIES 3 /* should only need at most 3762306a36Sopenharmony_ci * two cc/ua clears */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Our blacklist flags */ 4062306a36Sopenharmony_cienum { 4162306a36Sopenharmony_ci SPI_BLIST_NOIUS = (__force blist_flags_t)0x1, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* blacklist table, modelled on scsi_devinfo.c */ 4562306a36Sopenharmony_cistatic struct { 4662306a36Sopenharmony_ci char *vendor; 4762306a36Sopenharmony_ci char *model; 4862306a36Sopenharmony_ci blist_flags_t flags; 4962306a36Sopenharmony_ci} spi_static_device_list[] __initdata = { 5062306a36Sopenharmony_ci {"HP", "Ultrium 3-SCSI", SPI_BLIST_NOIUS }, 5162306a36Sopenharmony_ci {"IBM", "ULTRIUM-TD3", SPI_BLIST_NOIUS }, 5262306a36Sopenharmony_ci {NULL, NULL, 0} 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Private data accessors (keep these out of the header file) */ 5662306a36Sopenharmony_ci#define spi_dv_in_progress(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_in_progress) 5762306a36Sopenharmony_ci#define spi_dv_mutex(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_mutex) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct spi_internal { 6062306a36Sopenharmony_ci struct scsi_transport_template t; 6162306a36Sopenharmony_ci struct spi_function_template *f; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define to_spi_internal(tmpl) container_of(tmpl, struct spi_internal, t) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const int ppr_to_ps[] = { 6762306a36Sopenharmony_ci /* The PPR values 0-6 are reserved, fill them in when 6862306a36Sopenharmony_ci * the committee defines them */ 6962306a36Sopenharmony_ci -1, /* 0x00 */ 7062306a36Sopenharmony_ci -1, /* 0x01 */ 7162306a36Sopenharmony_ci -1, /* 0x02 */ 7262306a36Sopenharmony_ci -1, /* 0x03 */ 7362306a36Sopenharmony_ci -1, /* 0x04 */ 7462306a36Sopenharmony_ci -1, /* 0x05 */ 7562306a36Sopenharmony_ci -1, /* 0x06 */ 7662306a36Sopenharmony_ci 3125, /* 0x07 */ 7762306a36Sopenharmony_ci 6250, /* 0x08 */ 7862306a36Sopenharmony_ci 12500, /* 0x09 */ 7962306a36Sopenharmony_ci 25000, /* 0x0a */ 8062306a36Sopenharmony_ci 30300, /* 0x0b */ 8162306a36Sopenharmony_ci 50000, /* 0x0c */ 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci/* The PPR values at which you calculate the period in ns by multiplying 8462306a36Sopenharmony_ci * by 4 */ 8562306a36Sopenharmony_ci#define SPI_STATIC_PPR 0x0c 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int sprint_frac(char *dest, int value, int denom) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int frac = value % denom; 9062306a36Sopenharmony_ci int result = sprintf(dest, "%d", value / denom); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (frac == 0) 9362306a36Sopenharmony_ci return result; 9462306a36Sopenharmony_ci dest[result++] = '.'; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci do { 9762306a36Sopenharmony_ci denom /= 10; 9862306a36Sopenharmony_ci sprintf(dest + result, "%d", frac / denom); 9962306a36Sopenharmony_ci result++; 10062306a36Sopenharmony_ci frac %= denom; 10162306a36Sopenharmony_ci } while (frac); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci dest[result++] = '\0'; 10462306a36Sopenharmony_ci return result; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int spi_execute(struct scsi_device *sdev, const void *cmd, 10862306a36Sopenharmony_ci enum req_op op, void *buffer, unsigned int bufflen, 10962306a36Sopenharmony_ci struct scsi_sense_hdr *sshdr) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int i, result; 11262306a36Sopenharmony_ci struct scsi_sense_hdr sshdr_tmp; 11362306a36Sopenharmony_ci blk_opf_t opf = op | REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | 11462306a36Sopenharmony_ci REQ_FAILFAST_DRIVER; 11562306a36Sopenharmony_ci const struct scsi_exec_args exec_args = { 11662306a36Sopenharmony_ci .req_flags = BLK_MQ_REQ_PM, 11762306a36Sopenharmony_ci .sshdr = sshdr ? : &sshdr_tmp, 11862306a36Sopenharmony_ci }; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci sshdr = exec_args.sshdr; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for(i = 0; i < DV_RETRIES; i++) { 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * The purpose of the RQF_PM flag below is to bypass the 12562306a36Sopenharmony_ci * SDEV_QUIESCE state. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci result = scsi_execute_cmd(sdev, cmd, opf, buffer, bufflen, 12862306a36Sopenharmony_ci DV_TIMEOUT, 1, &exec_args); 12962306a36Sopenharmony_ci if (result < 0 || !scsi_sense_valid(sshdr) || 13062306a36Sopenharmony_ci sshdr->sense_key != UNIT_ATTENTION) 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci return result; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic struct { 13762306a36Sopenharmony_ci enum spi_signal_type value; 13862306a36Sopenharmony_ci char *name; 13962306a36Sopenharmony_ci} signal_types[] = { 14062306a36Sopenharmony_ci { SPI_SIGNAL_UNKNOWN, "unknown" }, 14162306a36Sopenharmony_ci { SPI_SIGNAL_SE, "SE" }, 14262306a36Sopenharmony_ci { SPI_SIGNAL_LVD, "LVD" }, 14362306a36Sopenharmony_ci { SPI_SIGNAL_HVD, "HVD" }, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline const char *spi_signal_to_string(enum spi_signal_type type) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(signal_types); i++) { 15162306a36Sopenharmony_ci if (type == signal_types[i].value) 15262306a36Sopenharmony_ci return signal_types[i].name; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_cistatic inline enum spi_signal_type spi_signal_to_value(const char *name) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int i, len; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(signal_types); i++) { 16162306a36Sopenharmony_ci len = strlen(signal_types[i].name); 16262306a36Sopenharmony_ci if (strncmp(name, signal_types[i].name, len) == 0 && 16362306a36Sopenharmony_ci (name[len] == '\n' || name[len] == '\0')) 16462306a36Sopenharmony_ci return signal_types[i].value; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci return SPI_SIGNAL_UNKNOWN; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int spi_host_setup(struct transport_container *tc, struct device *dev, 17062306a36Sopenharmony_ci struct device *cdev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(dev); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci spi_signalling(shost) = SPI_SIGNAL_UNKNOWN; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int spi_host_configure(struct transport_container *tc, 18062306a36Sopenharmony_ci struct device *dev, 18162306a36Sopenharmony_ci struct device *cdev); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(spi_host_class, 18462306a36Sopenharmony_ci "spi_host", 18562306a36Sopenharmony_ci spi_host_setup, 18662306a36Sopenharmony_ci NULL, 18762306a36Sopenharmony_ci spi_host_configure); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int spi_host_match(struct attribute_container *cont, 19062306a36Sopenharmony_ci struct device *dev) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct Scsi_Host *shost; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!scsi_is_host_device(dev)) 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci shost = dev_to_shost(dev); 19862306a36Sopenharmony_ci if (!shost->transportt || shost->transportt->host_attrs.ac.class 19962306a36Sopenharmony_ci != &spi_host_class.class) 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return &shost->transportt->host_attrs.ac == cont; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int spi_target_configure(struct transport_container *tc, 20662306a36Sopenharmony_ci struct device *dev, 20762306a36Sopenharmony_ci struct device *cdev); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int spi_device_configure(struct transport_container *tc, 21062306a36Sopenharmony_ci struct device *dev, 21162306a36Sopenharmony_ci struct device *cdev) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 21462306a36Sopenharmony_ci struct scsi_target *starget = sdev->sdev_target; 21562306a36Sopenharmony_ci blist_flags_t bflags; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci bflags = scsi_get_device_flags_keyed(sdev, &sdev->inquiry[8], 21862306a36Sopenharmony_ci &sdev->inquiry[16], 21962306a36Sopenharmony_ci SCSI_DEVINFO_SPI); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Populate the target capability fields with the values 22262306a36Sopenharmony_ci * gleaned from the device inquiry */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci spi_support_sync(starget) = scsi_device_sync(sdev); 22562306a36Sopenharmony_ci spi_support_wide(starget) = scsi_device_wide(sdev); 22662306a36Sopenharmony_ci spi_support_dt(starget) = scsi_device_dt(sdev); 22762306a36Sopenharmony_ci spi_support_dt_only(starget) = scsi_device_dt_only(sdev); 22862306a36Sopenharmony_ci spi_support_ius(starget) = scsi_device_ius(sdev); 22962306a36Sopenharmony_ci if (bflags & SPI_BLIST_NOIUS) { 23062306a36Sopenharmony_ci dev_info(dev, "Information Units disabled by blacklist\n"); 23162306a36Sopenharmony_ci spi_support_ius(starget) = 0; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci spi_support_qas(starget) = scsi_device_qas(sdev); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int spi_setup_transport_attrs(struct transport_container *tc, 23962306a36Sopenharmony_ci struct device *dev, 24062306a36Sopenharmony_ci struct device *cdev) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct scsi_target *starget = to_scsi_target(dev); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci spi_period(starget) = -1; /* illegal value */ 24562306a36Sopenharmony_ci spi_min_period(starget) = 0; 24662306a36Sopenharmony_ci spi_offset(starget) = 0; /* async */ 24762306a36Sopenharmony_ci spi_max_offset(starget) = 255; 24862306a36Sopenharmony_ci spi_width(starget) = 0; /* narrow */ 24962306a36Sopenharmony_ci spi_max_width(starget) = 1; 25062306a36Sopenharmony_ci spi_iu(starget) = 0; /* no IU */ 25162306a36Sopenharmony_ci spi_max_iu(starget) = 1; 25262306a36Sopenharmony_ci spi_dt(starget) = 0; /* ST */ 25362306a36Sopenharmony_ci spi_qas(starget) = 0; 25462306a36Sopenharmony_ci spi_max_qas(starget) = 1; 25562306a36Sopenharmony_ci spi_wr_flow(starget) = 0; 25662306a36Sopenharmony_ci spi_rd_strm(starget) = 0; 25762306a36Sopenharmony_ci spi_rti(starget) = 0; 25862306a36Sopenharmony_ci spi_pcomp_en(starget) = 0; 25962306a36Sopenharmony_ci spi_hold_mcs(starget) = 0; 26062306a36Sopenharmony_ci spi_dv_pending(starget) = 0; 26162306a36Sopenharmony_ci spi_dv_in_progress(starget) = 0; 26262306a36Sopenharmony_ci spi_initial_dv(starget) = 0; 26362306a36Sopenharmony_ci mutex_init(&spi_dv_mutex(starget)); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci#define spi_transport_show_simple(field, format_string) \ 26962306a36Sopenharmony_ci \ 27062306a36Sopenharmony_cistatic ssize_t \ 27162306a36Sopenharmony_cishow_spi_transport_##field(struct device *dev, \ 27262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 27362306a36Sopenharmony_ci{ \ 27462306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); \ 27562306a36Sopenharmony_ci struct spi_transport_attrs *tp; \ 27662306a36Sopenharmony_ci \ 27762306a36Sopenharmony_ci tp = (struct spi_transport_attrs *)&starget->starget_data; \ 27862306a36Sopenharmony_ci return snprintf(buf, 20, format_string, tp->field); \ 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#define spi_transport_store_simple(field, format_string) \ 28262306a36Sopenharmony_ci \ 28362306a36Sopenharmony_cistatic ssize_t \ 28462306a36Sopenharmony_cistore_spi_transport_##field(struct device *dev, \ 28562306a36Sopenharmony_ci struct device_attribute *attr, \ 28662306a36Sopenharmony_ci const char *buf, size_t count) \ 28762306a36Sopenharmony_ci{ \ 28862306a36Sopenharmony_ci int val; \ 28962306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); \ 29062306a36Sopenharmony_ci struct spi_transport_attrs *tp; \ 29162306a36Sopenharmony_ci \ 29262306a36Sopenharmony_ci tp = (struct spi_transport_attrs *)&starget->starget_data; \ 29362306a36Sopenharmony_ci val = simple_strtoul(buf, NULL, 0); \ 29462306a36Sopenharmony_ci tp->field = val; \ 29562306a36Sopenharmony_ci return count; \ 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci#define spi_transport_show_function(field, format_string) \ 29962306a36Sopenharmony_ci \ 30062306a36Sopenharmony_cistatic ssize_t \ 30162306a36Sopenharmony_cishow_spi_transport_##field(struct device *dev, \ 30262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 30362306a36Sopenharmony_ci{ \ 30462306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); \ 30562306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ 30662306a36Sopenharmony_ci struct spi_transport_attrs *tp; \ 30762306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); \ 30862306a36Sopenharmony_ci tp = (struct spi_transport_attrs *)&starget->starget_data; \ 30962306a36Sopenharmony_ci if (i->f->get_##field) \ 31062306a36Sopenharmony_ci i->f->get_##field(starget); \ 31162306a36Sopenharmony_ci return snprintf(buf, 20, format_string, tp->field); \ 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci#define spi_transport_store_function(field, format_string) \ 31562306a36Sopenharmony_cistatic ssize_t \ 31662306a36Sopenharmony_cistore_spi_transport_##field(struct device *dev, \ 31762306a36Sopenharmony_ci struct device_attribute *attr, \ 31862306a36Sopenharmony_ci const char *buf, size_t count) \ 31962306a36Sopenharmony_ci{ \ 32062306a36Sopenharmony_ci int val; \ 32162306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); \ 32262306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ 32362306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); \ 32462306a36Sopenharmony_ci \ 32562306a36Sopenharmony_ci if (!i->f->set_##field) \ 32662306a36Sopenharmony_ci return -EINVAL; \ 32762306a36Sopenharmony_ci val = simple_strtoul(buf, NULL, 0); \ 32862306a36Sopenharmony_ci i->f->set_##field(starget, val); \ 32962306a36Sopenharmony_ci return count; \ 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci#define spi_transport_store_max(field, format_string) \ 33362306a36Sopenharmony_cistatic ssize_t \ 33462306a36Sopenharmony_cistore_spi_transport_##field(struct device *dev, \ 33562306a36Sopenharmony_ci struct device_attribute *attr, \ 33662306a36Sopenharmony_ci const char *buf, size_t count) \ 33762306a36Sopenharmony_ci{ \ 33862306a36Sopenharmony_ci int val; \ 33962306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); \ 34062306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ 34162306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); \ 34262306a36Sopenharmony_ci struct spi_transport_attrs *tp \ 34362306a36Sopenharmony_ci = (struct spi_transport_attrs *)&starget->starget_data; \ 34462306a36Sopenharmony_ci \ 34562306a36Sopenharmony_ci if (!i->f->set_##field) \ 34662306a36Sopenharmony_ci return -EINVAL; \ 34762306a36Sopenharmony_ci val = simple_strtoul(buf, NULL, 0); \ 34862306a36Sopenharmony_ci if (val > tp->max_##field) \ 34962306a36Sopenharmony_ci val = tp->max_##field; \ 35062306a36Sopenharmony_ci i->f->set_##field(starget, val); \ 35162306a36Sopenharmony_ci return count; \ 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci#define spi_transport_rd_attr(field, format_string) \ 35562306a36Sopenharmony_ci spi_transport_show_function(field, format_string) \ 35662306a36Sopenharmony_ci spi_transport_store_function(field, format_string) \ 35762306a36Sopenharmony_cistatic DEVICE_ATTR(field, S_IRUGO, \ 35862306a36Sopenharmony_ci show_spi_transport_##field, \ 35962306a36Sopenharmony_ci store_spi_transport_##field); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci#define spi_transport_simple_attr(field, format_string) \ 36262306a36Sopenharmony_ci spi_transport_show_simple(field, format_string) \ 36362306a36Sopenharmony_ci spi_transport_store_simple(field, format_string) \ 36462306a36Sopenharmony_cistatic DEVICE_ATTR(field, S_IRUGO, \ 36562306a36Sopenharmony_ci show_spi_transport_##field, \ 36662306a36Sopenharmony_ci store_spi_transport_##field); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci#define spi_transport_max_attr(field, format_string) \ 36962306a36Sopenharmony_ci spi_transport_show_function(field, format_string) \ 37062306a36Sopenharmony_ci spi_transport_store_max(field, format_string) \ 37162306a36Sopenharmony_ci spi_transport_simple_attr(max_##field, format_string) \ 37262306a36Sopenharmony_cistatic DEVICE_ATTR(field, S_IRUGO, \ 37362306a36Sopenharmony_ci show_spi_transport_##field, \ 37462306a36Sopenharmony_ci store_spi_transport_##field); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* The Parallel SCSI Tranport Attributes: */ 37762306a36Sopenharmony_cispi_transport_max_attr(offset, "%d\n"); 37862306a36Sopenharmony_cispi_transport_max_attr(width, "%d\n"); 37962306a36Sopenharmony_cispi_transport_max_attr(iu, "%d\n"); 38062306a36Sopenharmony_cispi_transport_rd_attr(dt, "%d\n"); 38162306a36Sopenharmony_cispi_transport_max_attr(qas, "%d\n"); 38262306a36Sopenharmony_cispi_transport_rd_attr(wr_flow, "%d\n"); 38362306a36Sopenharmony_cispi_transport_rd_attr(rd_strm, "%d\n"); 38462306a36Sopenharmony_cispi_transport_rd_attr(rti, "%d\n"); 38562306a36Sopenharmony_cispi_transport_rd_attr(pcomp_en, "%d\n"); 38662306a36Sopenharmony_cispi_transport_rd_attr(hold_mcs, "%d\n"); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* we only care about the first child device that's a real SCSI device 38962306a36Sopenharmony_ci * so we return 1 to terminate the iteration when we find it */ 39062306a36Sopenharmony_cistatic int child_iter(struct device *dev, void *data) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci if (!scsi_is_sdev_device(dev)) 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci spi_dv_device(to_scsi_device(dev)); 39662306a36Sopenharmony_ci return 1; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic ssize_t 40062306a36Sopenharmony_cistore_spi_revalidate(struct device *dev, struct device_attribute *attr, 40162306a36Sopenharmony_ci const char *buf, size_t count) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci device_for_each_child(&starget->dev, NULL, child_iter); 40662306a36Sopenharmony_ci return count; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_cistatic DEVICE_ATTR(revalidate, S_IWUSR, NULL, store_spi_revalidate); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* Translate the period into ns according to the current spec 41162306a36Sopenharmony_ci * for SDTR/PPR messages */ 41262306a36Sopenharmony_cistatic int period_to_str(char *buf, int period) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int len, picosec; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (period < 0 || period > 0xff) { 41762306a36Sopenharmony_ci picosec = -1; 41862306a36Sopenharmony_ci } else if (period <= SPI_STATIC_PPR) { 41962306a36Sopenharmony_ci picosec = ppr_to_ps[period]; 42062306a36Sopenharmony_ci } else { 42162306a36Sopenharmony_ci picosec = period * 4000; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (picosec == -1) { 42562306a36Sopenharmony_ci len = sprintf(buf, "reserved"); 42662306a36Sopenharmony_ci } else { 42762306a36Sopenharmony_ci len = sprint_frac(buf, picosec, 1000); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return len; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic ssize_t 43462306a36Sopenharmony_cishow_spi_transport_period_helper(char *buf, int period) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int len = period_to_str(buf, period); 43762306a36Sopenharmony_ci buf[len++] = '\n'; 43862306a36Sopenharmony_ci buf[len] = '\0'; 43962306a36Sopenharmony_ci return len; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic ssize_t 44362306a36Sopenharmony_cistore_spi_transport_period_helper(struct device *dev, const char *buf, 44462306a36Sopenharmony_ci size_t count, int *periodp) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci int j, picosec, period = -1; 44762306a36Sopenharmony_ci char *endp; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci picosec = simple_strtoul(buf, &endp, 10) * 1000; 45062306a36Sopenharmony_ci if (*endp == '.') { 45162306a36Sopenharmony_ci int mult = 100; 45262306a36Sopenharmony_ci do { 45362306a36Sopenharmony_ci endp++; 45462306a36Sopenharmony_ci if (!isdigit(*endp)) 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci picosec += (*endp - '0') * mult; 45762306a36Sopenharmony_ci mult /= 10; 45862306a36Sopenharmony_ci } while (mult > 0); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci for (j = 0; j <= SPI_STATIC_PPR; j++) { 46262306a36Sopenharmony_ci if (ppr_to_ps[j] < picosec) 46362306a36Sopenharmony_ci continue; 46462306a36Sopenharmony_ci period = j; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (period == -1) 46962306a36Sopenharmony_ci period = picosec / 4000; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (period > 0xff) 47262306a36Sopenharmony_ci period = 0xff; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci *periodp = period; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return count; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic ssize_t 48062306a36Sopenharmony_cishow_spi_transport_period(struct device *dev, 48162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(dev); 48462306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 48562306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); 48662306a36Sopenharmony_ci struct spi_transport_attrs *tp = 48762306a36Sopenharmony_ci (struct spi_transport_attrs *)&starget->starget_data; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (i->f->get_period) 49062306a36Sopenharmony_ci i->f->get_period(starget); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return show_spi_transport_period_helper(buf, tp->period); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic ssize_t 49662306a36Sopenharmony_cistore_spi_transport_period(struct device *cdev, struct device_attribute *attr, 49762306a36Sopenharmony_ci const char *buf, size_t count) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(cdev); 50062306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 50162306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); 50262306a36Sopenharmony_ci struct spi_transport_attrs *tp = 50362306a36Sopenharmony_ci (struct spi_transport_attrs *)&starget->starget_data; 50462306a36Sopenharmony_ci int period, retval; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (!i->f->set_period) 50762306a36Sopenharmony_ci return -EINVAL; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci retval = store_spi_transport_period_helper(cdev, buf, count, &period); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (period < tp->min_period) 51262306a36Sopenharmony_ci period = tp->min_period; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci i->f->set_period(starget, period); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return retval; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic DEVICE_ATTR(period, S_IRUGO, 52062306a36Sopenharmony_ci show_spi_transport_period, 52162306a36Sopenharmony_ci store_spi_transport_period); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic ssize_t 52462306a36Sopenharmony_cishow_spi_transport_min_period(struct device *cdev, 52562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(cdev); 52862306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 52962306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); 53062306a36Sopenharmony_ci struct spi_transport_attrs *tp = 53162306a36Sopenharmony_ci (struct spi_transport_attrs *)&starget->starget_data; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!i->f->set_period) 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return show_spi_transport_period_helper(buf, tp->min_period); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic ssize_t 54062306a36Sopenharmony_cistore_spi_transport_min_period(struct device *cdev, 54162306a36Sopenharmony_ci struct device_attribute *attr, 54262306a36Sopenharmony_ci const char *buf, size_t count) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(cdev); 54562306a36Sopenharmony_ci struct spi_transport_attrs *tp = 54662306a36Sopenharmony_ci (struct spi_transport_attrs *)&starget->starget_data; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return store_spi_transport_period_helper(cdev, buf, count, 54962306a36Sopenharmony_ci &tp->min_period); 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic DEVICE_ATTR(min_period, S_IRUGO, 55462306a36Sopenharmony_ci show_spi_transport_min_period, 55562306a36Sopenharmony_ci store_spi_transport_min_period); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic ssize_t show_spi_host_signalling(struct device *cdev, 55962306a36Sopenharmony_ci struct device_attribute *attr, 56062306a36Sopenharmony_ci char *buf) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct Scsi_Host *shost = transport_class_to_shost(cdev); 56362306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (i->f->get_signalling) 56662306a36Sopenharmony_ci i->f->get_signalling(shost); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return sprintf(buf, "%s\n", spi_signal_to_string(spi_signalling(shost))); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_cistatic ssize_t store_spi_host_signalling(struct device *dev, 57162306a36Sopenharmony_ci struct device_attribute *attr, 57262306a36Sopenharmony_ci const char *buf, size_t count) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct Scsi_Host *shost = transport_class_to_shost(dev); 57562306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(shost->transportt); 57662306a36Sopenharmony_ci enum spi_signal_type type = spi_signal_to_value(buf); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!i->f->set_signalling) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (type != SPI_SIGNAL_UNKNOWN) 58262306a36Sopenharmony_ci i->f->set_signalling(shost, type); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return count; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_cistatic DEVICE_ATTR(signalling, S_IRUGO, 58762306a36Sopenharmony_ci show_spi_host_signalling, 58862306a36Sopenharmony_ci store_spi_host_signalling); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic ssize_t show_spi_host_width(struct device *cdev, 59162306a36Sopenharmony_ci struct device_attribute *attr, 59262306a36Sopenharmony_ci char *buf) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct Scsi_Host *shost = transport_class_to_shost(cdev); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return sprintf(buf, "%s\n", shost->max_id == 16 ? "wide" : "narrow"); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_cistatic DEVICE_ATTR(host_width, S_IRUGO, 59962306a36Sopenharmony_ci show_spi_host_width, NULL); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic ssize_t show_spi_host_hba_id(struct device *cdev, 60262306a36Sopenharmony_ci struct device_attribute *attr, 60362306a36Sopenharmony_ci char *buf) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct Scsi_Host *shost = transport_class_to_shost(cdev); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return sprintf(buf, "%d\n", shost->this_id); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_cistatic DEVICE_ATTR(hba_id, S_IRUGO, 61062306a36Sopenharmony_ci show_spi_host_hba_id, NULL); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci#define DV_SET(x, y) \ 61362306a36Sopenharmony_ci if(i->f->set_##x) \ 61462306a36Sopenharmony_ci i->f->set_##x(sdev->sdev_target, y) 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cienum spi_compare_returns { 61762306a36Sopenharmony_ci SPI_COMPARE_SUCCESS, 61862306a36Sopenharmony_ci SPI_COMPARE_FAILURE, 61962306a36Sopenharmony_ci SPI_COMPARE_SKIP_TEST, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* This is for read/write Domain Validation: If the device supports 62462306a36Sopenharmony_ci * an echo buffer, we do read/write tests to it */ 62562306a36Sopenharmony_cistatic enum spi_compare_returns 62662306a36Sopenharmony_cispi_dv_device_echo_buffer(struct scsi_device *sdev, u8 *buffer, 62762306a36Sopenharmony_ci u8 *ptr, const int retries) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci int len = ptr - buffer; 63062306a36Sopenharmony_ci int j, k, r, result; 63162306a36Sopenharmony_ci unsigned int pattern = 0x0000ffff; 63262306a36Sopenharmony_ci struct scsi_sense_hdr sshdr; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci const char spi_write_buffer[] = { 63562306a36Sopenharmony_ci WRITE_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0 63662306a36Sopenharmony_ci }; 63762306a36Sopenharmony_ci const char spi_read_buffer[] = { 63862306a36Sopenharmony_ci READ_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0 63962306a36Sopenharmony_ci }; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* set up the pattern buffer. Doesn't matter if we spill 64262306a36Sopenharmony_ci * slightly beyond since that's where the read buffer is */ 64362306a36Sopenharmony_ci for (j = 0; j < len; ) { 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* fill the buffer with counting (test a) */ 64662306a36Sopenharmony_ci for ( ; j < min(len, 32); j++) 64762306a36Sopenharmony_ci buffer[j] = j; 64862306a36Sopenharmony_ci k = j; 64962306a36Sopenharmony_ci /* fill the buffer with alternating words of 0x0 and 65062306a36Sopenharmony_ci * 0xffff (test b) */ 65162306a36Sopenharmony_ci for ( ; j < min(len, k + 32); j += 2) { 65262306a36Sopenharmony_ci u16 *word = (u16 *)&buffer[j]; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci *word = (j & 0x02) ? 0x0000 : 0xffff; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci k = j; 65762306a36Sopenharmony_ci /* fill with crosstalk (alternating 0x5555 0xaaa) 65862306a36Sopenharmony_ci * (test c) */ 65962306a36Sopenharmony_ci for ( ; j < min(len, k + 32); j += 2) { 66062306a36Sopenharmony_ci u16 *word = (u16 *)&buffer[j]; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci *word = (j & 0x02) ? 0x5555 : 0xaaaa; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci k = j; 66562306a36Sopenharmony_ci /* fill with shifting bits (test d) */ 66662306a36Sopenharmony_ci for ( ; j < min(len, k + 32); j += 4) { 66762306a36Sopenharmony_ci u32 *word = (unsigned int *)&buffer[j]; 66862306a36Sopenharmony_ci u32 roll = (pattern & 0x80000000) ? 1 : 0; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci *word = pattern; 67162306a36Sopenharmony_ci pattern = (pattern << 1) | roll; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci /* don't bother with random data (test e) */ 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci for (r = 0; r < retries; r++) { 67762306a36Sopenharmony_ci result = spi_execute(sdev, spi_write_buffer, REQ_OP_DRV_OUT, 67862306a36Sopenharmony_ci buffer, len, &sshdr); 67962306a36Sopenharmony_ci if(result || !scsi_device_online(sdev)) { 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci scsi_device_set_state(sdev, SDEV_QUIESCE); 68262306a36Sopenharmony_ci if (scsi_sense_valid(&sshdr) 68362306a36Sopenharmony_ci && sshdr.sense_key == ILLEGAL_REQUEST 68462306a36Sopenharmony_ci /* INVALID FIELD IN CDB */ 68562306a36Sopenharmony_ci && sshdr.asc == 0x24 && sshdr.ascq == 0x00) 68662306a36Sopenharmony_ci /* This would mean that the drive lied 68762306a36Sopenharmony_ci * to us about supporting an echo 68862306a36Sopenharmony_ci * buffer (unfortunately some Western 68962306a36Sopenharmony_ci * Digital drives do precisely this) 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_ci return SPI_COMPARE_SKIP_TEST; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Write Buffer failure %x\n", result); 69562306a36Sopenharmony_ci return SPI_COMPARE_FAILURE; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci memset(ptr, 0, len); 69962306a36Sopenharmony_ci spi_execute(sdev, spi_read_buffer, REQ_OP_DRV_IN, 70062306a36Sopenharmony_ci ptr, len, NULL); 70162306a36Sopenharmony_ci scsi_device_set_state(sdev, SDEV_QUIESCE); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (memcmp(buffer, ptr, len) != 0) 70462306a36Sopenharmony_ci return SPI_COMPARE_FAILURE; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci return SPI_COMPARE_SUCCESS; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* This is for the simplest form of Domain Validation: a read test 71062306a36Sopenharmony_ci * on the inquiry data from the device */ 71162306a36Sopenharmony_cistatic enum spi_compare_returns 71262306a36Sopenharmony_cispi_dv_device_compare_inquiry(struct scsi_device *sdev, u8 *buffer, 71362306a36Sopenharmony_ci u8 *ptr, const int retries) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci int r, result; 71662306a36Sopenharmony_ci const int len = sdev->inquiry_len; 71762306a36Sopenharmony_ci const char spi_inquiry[] = { 71862306a36Sopenharmony_ci INQUIRY, 0, 0, 0, len, 0 71962306a36Sopenharmony_ci }; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci for (r = 0; r < retries; r++) { 72262306a36Sopenharmony_ci memset(ptr, 0, len); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci result = spi_execute(sdev, spi_inquiry, REQ_OP_DRV_IN, 72562306a36Sopenharmony_ci ptr, len, NULL); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if(result || !scsi_device_online(sdev)) { 72862306a36Sopenharmony_ci scsi_device_set_state(sdev, SDEV_QUIESCE); 72962306a36Sopenharmony_ci return SPI_COMPARE_FAILURE; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* If we don't have the inquiry data already, the 73362306a36Sopenharmony_ci * first read gets it */ 73462306a36Sopenharmony_ci if (ptr == buffer) { 73562306a36Sopenharmony_ci ptr += len; 73662306a36Sopenharmony_ci --r; 73762306a36Sopenharmony_ci continue; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (memcmp(buffer, ptr, len) != 0) 74162306a36Sopenharmony_ci /* failure */ 74262306a36Sopenharmony_ci return SPI_COMPARE_FAILURE; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci return SPI_COMPARE_SUCCESS; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic enum spi_compare_returns 74862306a36Sopenharmony_cispi_dv_retrain(struct scsi_device *sdev, u8 *buffer, u8 *ptr, 74962306a36Sopenharmony_ci enum spi_compare_returns 75062306a36Sopenharmony_ci (*compare_fn)(struct scsi_device *, u8 *, u8 *, int)) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(sdev->host->transportt); 75362306a36Sopenharmony_ci struct scsi_target *starget = sdev->sdev_target; 75462306a36Sopenharmony_ci int period = 0, prevperiod = 0; 75562306a36Sopenharmony_ci enum spi_compare_returns retval; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci for (;;) { 75962306a36Sopenharmony_ci int newperiod; 76062306a36Sopenharmony_ci retval = compare_fn(sdev, buffer, ptr, DV_LOOPS); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (retval == SPI_COMPARE_SUCCESS 76362306a36Sopenharmony_ci || retval == SPI_COMPARE_SKIP_TEST) 76462306a36Sopenharmony_ci break; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* OK, retrain, fallback */ 76762306a36Sopenharmony_ci if (i->f->get_iu) 76862306a36Sopenharmony_ci i->f->get_iu(starget); 76962306a36Sopenharmony_ci if (i->f->get_qas) 77062306a36Sopenharmony_ci i->f->get_qas(starget); 77162306a36Sopenharmony_ci if (i->f->get_period) 77262306a36Sopenharmony_ci i->f->get_period(sdev->sdev_target); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Here's the fallback sequence; first try turning off 77562306a36Sopenharmony_ci * IU, then QAS (if we can control them), then finally 77662306a36Sopenharmony_ci * fall down the periods */ 77762306a36Sopenharmony_ci if (i->f->set_iu && spi_iu(starget)) { 77862306a36Sopenharmony_ci starget_printk(KERN_ERR, starget, "Domain Validation Disabling Information Units\n"); 77962306a36Sopenharmony_ci DV_SET(iu, 0); 78062306a36Sopenharmony_ci } else if (i->f->set_qas && spi_qas(starget)) { 78162306a36Sopenharmony_ci starget_printk(KERN_ERR, starget, "Domain Validation Disabling Quick Arbitration and Selection\n"); 78262306a36Sopenharmony_ci DV_SET(qas, 0); 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci newperiod = spi_period(starget); 78562306a36Sopenharmony_ci period = newperiod > period ? newperiod : period; 78662306a36Sopenharmony_ci if (period < 0x0d) 78762306a36Sopenharmony_ci period++; 78862306a36Sopenharmony_ci else 78962306a36Sopenharmony_ci period += period >> 1; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (unlikely(period > 0xff || period == prevperiod)) { 79262306a36Sopenharmony_ci /* Total failure; set to async and return */ 79362306a36Sopenharmony_ci starget_printk(KERN_ERR, starget, "Domain Validation Failure, dropping back to Asynchronous\n"); 79462306a36Sopenharmony_ci DV_SET(offset, 0); 79562306a36Sopenharmony_ci return SPI_COMPARE_FAILURE; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci starget_printk(KERN_ERR, starget, "Domain Validation detected failure, dropping back\n"); 79862306a36Sopenharmony_ci DV_SET(period, period); 79962306a36Sopenharmony_ci prevperiod = period; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci return retval; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic int 80662306a36Sopenharmony_cispi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci int l, result; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* first off do a test unit ready. This can error out 81162306a36Sopenharmony_ci * because of reservations or some other reason. If it 81262306a36Sopenharmony_ci * fails, the device won't let us write to the echo buffer 81362306a36Sopenharmony_ci * so just return failure */ 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci static const char spi_test_unit_ready[] = { 81662306a36Sopenharmony_ci TEST_UNIT_READY, 0, 0, 0, 0, 0 81762306a36Sopenharmony_ci }; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci static const char spi_read_buffer_descriptor[] = { 82062306a36Sopenharmony_ci READ_BUFFER, 0x0b, 0, 0, 0, 0, 0, 0, 4, 0 82162306a36Sopenharmony_ci }; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* We send a set of three TURs to clear any outstanding 82562306a36Sopenharmony_ci * unit attention conditions if they exist (Otherwise the 82662306a36Sopenharmony_ci * buffer tests won't be happy). If the TUR still fails 82762306a36Sopenharmony_ci * (reservation conflict, device not ready, etc) just 82862306a36Sopenharmony_ci * skip the write tests */ 82962306a36Sopenharmony_ci for (l = 0; ; l++) { 83062306a36Sopenharmony_ci result = spi_execute(sdev, spi_test_unit_ready, REQ_OP_DRV_IN, 83162306a36Sopenharmony_ci NULL, 0, NULL); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if(result) { 83462306a36Sopenharmony_ci if(l >= 3) 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci } else { 83762306a36Sopenharmony_ci /* TUR succeeded */ 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci result = spi_execute(sdev, spi_read_buffer_descriptor, 84362306a36Sopenharmony_ci REQ_OP_DRV_IN, buffer, 4, NULL); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (result) 84662306a36Sopenharmony_ci /* Device has no echo buffer */ 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return buffer[3] + ((buffer[2] & 0x1f) << 8); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void 85362306a36Sopenharmony_cispi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(sdev->host->transportt); 85662306a36Sopenharmony_ci struct scsi_target *starget = sdev->sdev_target; 85762306a36Sopenharmony_ci struct Scsi_Host *shost = sdev->host; 85862306a36Sopenharmony_ci int len = sdev->inquiry_len; 85962306a36Sopenharmony_ci int min_period = spi_min_period(starget); 86062306a36Sopenharmony_ci int max_width = spi_max_width(starget); 86162306a36Sopenharmony_ci /* first set us up for narrow async */ 86262306a36Sopenharmony_ci DV_SET(offset, 0); 86362306a36Sopenharmony_ci DV_SET(width, 0); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS) 86662306a36Sopenharmony_ci != SPI_COMPARE_SUCCESS) { 86762306a36Sopenharmony_ci starget_printk(KERN_ERR, starget, "Domain Validation Initial Inquiry Failed\n"); 86862306a36Sopenharmony_ci /* FIXME: should probably offline the device here? */ 86962306a36Sopenharmony_ci return; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (!spi_support_wide(starget)) { 87362306a36Sopenharmony_ci spi_max_width(starget) = 0; 87462306a36Sopenharmony_ci max_width = 0; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* test width */ 87862306a36Sopenharmony_ci if (i->f->set_width && max_width) { 87962306a36Sopenharmony_ci i->f->set_width(starget, 1); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (spi_dv_device_compare_inquiry(sdev, buffer, 88262306a36Sopenharmony_ci buffer + len, 88362306a36Sopenharmony_ci DV_LOOPS) 88462306a36Sopenharmony_ci != SPI_COMPARE_SUCCESS) { 88562306a36Sopenharmony_ci starget_printk(KERN_ERR, starget, "Wide Transfers Fail\n"); 88662306a36Sopenharmony_ci i->f->set_width(starget, 0); 88762306a36Sopenharmony_ci /* Make sure we don't force wide back on by asking 88862306a36Sopenharmony_ci * for a transfer period that requires it */ 88962306a36Sopenharmony_ci max_width = 0; 89062306a36Sopenharmony_ci if (min_period < 10) 89162306a36Sopenharmony_ci min_period = 10; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (!i->f->set_period) 89662306a36Sopenharmony_ci return; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* device can't handle synchronous */ 89962306a36Sopenharmony_ci if (!spi_support_sync(starget) && !spi_support_dt(starget)) 90062306a36Sopenharmony_ci return; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* len == -1 is the signal that we need to ascertain the 90362306a36Sopenharmony_ci * presence of an echo buffer before trying to use it. len == 90462306a36Sopenharmony_ci * 0 means we don't have an echo buffer */ 90562306a36Sopenharmony_ci len = -1; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci retry: 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* now set up to the maximum */ 91062306a36Sopenharmony_ci DV_SET(offset, spi_max_offset(starget)); 91162306a36Sopenharmony_ci DV_SET(period, min_period); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* try QAS requests; this should be harmless to set if the 91462306a36Sopenharmony_ci * target supports it */ 91562306a36Sopenharmony_ci if (spi_support_qas(starget) && spi_max_qas(starget)) { 91662306a36Sopenharmony_ci DV_SET(qas, 1); 91762306a36Sopenharmony_ci } else { 91862306a36Sopenharmony_ci DV_SET(qas, 0); 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (spi_support_ius(starget) && spi_max_iu(starget) && 92262306a36Sopenharmony_ci min_period < 9) { 92362306a36Sopenharmony_ci /* This u320 (or u640). Set IU transfers */ 92462306a36Sopenharmony_ci DV_SET(iu, 1); 92562306a36Sopenharmony_ci /* Then set the optional parameters */ 92662306a36Sopenharmony_ci DV_SET(rd_strm, 1); 92762306a36Sopenharmony_ci DV_SET(wr_flow, 1); 92862306a36Sopenharmony_ci DV_SET(rti, 1); 92962306a36Sopenharmony_ci if (min_period == 8) 93062306a36Sopenharmony_ci DV_SET(pcomp_en, 1); 93162306a36Sopenharmony_ci } else { 93262306a36Sopenharmony_ci DV_SET(iu, 0); 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* now that we've done all this, actually check the bus 93662306a36Sopenharmony_ci * signal type (if known). Some devices are stupid on 93762306a36Sopenharmony_ci * a SE bus and still claim they can try LVD only settings */ 93862306a36Sopenharmony_ci if (i->f->get_signalling) 93962306a36Sopenharmony_ci i->f->get_signalling(shost); 94062306a36Sopenharmony_ci if (spi_signalling(shost) == SPI_SIGNAL_SE || 94162306a36Sopenharmony_ci spi_signalling(shost) == SPI_SIGNAL_HVD || 94262306a36Sopenharmony_ci !spi_support_dt(starget)) { 94362306a36Sopenharmony_ci DV_SET(dt, 0); 94462306a36Sopenharmony_ci } else { 94562306a36Sopenharmony_ci DV_SET(dt, 1); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci /* set width last because it will pull all the other 94862306a36Sopenharmony_ci * parameters down to required values */ 94962306a36Sopenharmony_ci DV_SET(width, max_width); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* Do the read only INQUIRY tests */ 95262306a36Sopenharmony_ci spi_dv_retrain(sdev, buffer, buffer + sdev->inquiry_len, 95362306a36Sopenharmony_ci spi_dv_device_compare_inquiry); 95462306a36Sopenharmony_ci /* See if we actually managed to negotiate and sustain DT */ 95562306a36Sopenharmony_ci if (i->f->get_dt) 95662306a36Sopenharmony_ci i->f->get_dt(starget); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* see if the device has an echo buffer. If it does we can do 95962306a36Sopenharmony_ci * the SPI pattern write tests. Because of some broken 96062306a36Sopenharmony_ci * devices, we *only* try this on a device that has actually 96162306a36Sopenharmony_ci * negotiated DT */ 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (len == -1 && spi_dt(starget)) 96462306a36Sopenharmony_ci len = spi_dv_device_get_echo_buffer(sdev, buffer); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (len <= 0) { 96762306a36Sopenharmony_ci starget_printk(KERN_INFO, starget, "Domain Validation skipping write tests\n"); 96862306a36Sopenharmony_ci return; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (len > SPI_MAX_ECHO_BUFFER_SIZE) { 97262306a36Sopenharmony_ci starget_printk(KERN_WARNING, starget, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE); 97362306a36Sopenharmony_ci len = SPI_MAX_ECHO_BUFFER_SIZE; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (spi_dv_retrain(sdev, buffer, buffer + len, 97762306a36Sopenharmony_ci spi_dv_device_echo_buffer) 97862306a36Sopenharmony_ci == SPI_COMPARE_SKIP_TEST) { 97962306a36Sopenharmony_ci /* OK, the stupid drive can't do a write echo buffer 98062306a36Sopenharmony_ci * test after all, fall back to the read tests */ 98162306a36Sopenharmony_ci len = 0; 98262306a36Sopenharmony_ci goto retry; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci/** spi_dv_device - Do Domain Validation on the device 98862306a36Sopenharmony_ci * @sdev: scsi device to validate 98962306a36Sopenharmony_ci * 99062306a36Sopenharmony_ci * Performs the domain validation on the given device in the 99162306a36Sopenharmony_ci * current execution thread. Since DV operations may sleep, 99262306a36Sopenharmony_ci * the current thread must have user context. Also no SCSI 99362306a36Sopenharmony_ci * related locks that would deadlock I/O issued by the DV may 99462306a36Sopenharmony_ci * be held. 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_civoid 99762306a36Sopenharmony_cispi_dv_device(struct scsi_device *sdev) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci struct scsi_target *starget = sdev->sdev_target; 100062306a36Sopenharmony_ci const int len = SPI_MAX_ECHO_BUFFER_SIZE*2; 100162306a36Sopenharmony_ci unsigned int sleep_flags; 100262306a36Sopenharmony_ci u8 *buffer; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* 100562306a36Sopenharmony_ci * Because this function and the power management code both call 100662306a36Sopenharmony_ci * scsi_device_quiesce(), it is not safe to perform domain validation 100762306a36Sopenharmony_ci * while suspend or resume is in progress. Hence the 100862306a36Sopenharmony_ci * lock/unlock_system_sleep() calls. 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ci sleep_flags = lock_system_sleep(); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (scsi_autopm_get_device(sdev)) 101362306a36Sopenharmony_ci goto unlock_system_sleep; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (unlikely(spi_dv_in_progress(starget))) 101662306a36Sopenharmony_ci goto put_autopm; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (unlikely(scsi_device_get(sdev))) 101962306a36Sopenharmony_ci goto put_autopm; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci spi_dv_in_progress(starget) = 1; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci buffer = kzalloc(len, GFP_KERNEL); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (unlikely(!buffer)) 102662306a36Sopenharmony_ci goto put_sdev; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* We need to verify that the actual device will quiesce; the 102962306a36Sopenharmony_ci * later target quiesce is just a nice to have */ 103062306a36Sopenharmony_ci if (unlikely(scsi_device_quiesce(sdev))) 103162306a36Sopenharmony_ci goto free_buffer; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci scsi_target_quiesce(starget); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci spi_dv_pending(starget) = 1; 103662306a36Sopenharmony_ci mutex_lock(&spi_dv_mutex(starget)); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci starget_printk(KERN_INFO, starget, "Beginning Domain Validation\n"); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci spi_dv_device_internal(sdev, buffer); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci starget_printk(KERN_INFO, starget, "Ending Domain Validation\n"); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci mutex_unlock(&spi_dv_mutex(starget)); 104562306a36Sopenharmony_ci spi_dv_pending(starget) = 0; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci scsi_target_resume(starget); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci spi_initial_dv(starget) = 1; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cifree_buffer: 105262306a36Sopenharmony_ci kfree(buffer); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ciput_sdev: 105562306a36Sopenharmony_ci spi_dv_in_progress(starget) = 0; 105662306a36Sopenharmony_ci scsi_device_put(sdev); 105762306a36Sopenharmony_ciput_autopm: 105862306a36Sopenharmony_ci scsi_autopm_put_device(sdev); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ciunlock_system_sleep: 106162306a36Sopenharmony_ci unlock_system_sleep(sleep_flags); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ciEXPORT_SYMBOL(spi_dv_device); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistruct work_queue_wrapper { 106662306a36Sopenharmony_ci struct work_struct work; 106762306a36Sopenharmony_ci struct scsi_device *sdev; 106862306a36Sopenharmony_ci}; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_cistatic void 107162306a36Sopenharmony_cispi_dv_device_work_wrapper(struct work_struct *work) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct work_queue_wrapper *wqw = 107462306a36Sopenharmony_ci container_of(work, struct work_queue_wrapper, work); 107562306a36Sopenharmony_ci struct scsi_device *sdev = wqw->sdev; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci kfree(wqw); 107862306a36Sopenharmony_ci spi_dv_device(sdev); 107962306a36Sopenharmony_ci spi_dv_pending(sdev->sdev_target) = 0; 108062306a36Sopenharmony_ci scsi_device_put(sdev); 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci/** 108562306a36Sopenharmony_ci * spi_schedule_dv_device - schedule domain validation to occur on the device 108662306a36Sopenharmony_ci * @sdev: The device to validate 108762306a36Sopenharmony_ci * 108862306a36Sopenharmony_ci * Identical to spi_dv_device() above, except that the DV will be 108962306a36Sopenharmony_ci * scheduled to occur in a workqueue later. All memory allocations 109062306a36Sopenharmony_ci * are atomic, so may be called from any context including those holding 109162306a36Sopenharmony_ci * SCSI locks. 109262306a36Sopenharmony_ci */ 109362306a36Sopenharmony_civoid 109462306a36Sopenharmony_cispi_schedule_dv_device(struct scsi_device *sdev) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct work_queue_wrapper *wqw = 109762306a36Sopenharmony_ci kmalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (unlikely(!wqw)) 110062306a36Sopenharmony_ci return; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (unlikely(spi_dv_pending(sdev->sdev_target))) { 110362306a36Sopenharmony_ci kfree(wqw); 110462306a36Sopenharmony_ci return; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci /* Set pending early (dv_device doesn't check it, only sets it) */ 110762306a36Sopenharmony_ci spi_dv_pending(sdev->sdev_target) = 1; 110862306a36Sopenharmony_ci if (unlikely(scsi_device_get(sdev))) { 110962306a36Sopenharmony_ci kfree(wqw); 111062306a36Sopenharmony_ci spi_dv_pending(sdev->sdev_target) = 0; 111162306a36Sopenharmony_ci return; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci INIT_WORK(&wqw->work, spi_dv_device_work_wrapper); 111562306a36Sopenharmony_ci wqw->sdev = sdev; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci schedule_work(&wqw->work); 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ciEXPORT_SYMBOL(spi_schedule_dv_device); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci/** 112262306a36Sopenharmony_ci * spi_display_xfer_agreement - Print the current target transfer agreement 112362306a36Sopenharmony_ci * @starget: The target for which to display the agreement 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * Each SPI port is required to maintain a transfer agreement for each 112662306a36Sopenharmony_ci * other port on the bus. This function prints a one-line summary of 112762306a36Sopenharmony_ci * the current agreement; more detailed information is available in sysfs. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_civoid spi_display_xfer_agreement(struct scsi_target *starget) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct spi_transport_attrs *tp; 113262306a36Sopenharmony_ci tp = (struct spi_transport_attrs *)&starget->starget_data; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (tp->offset > 0 && tp->period > 0) { 113562306a36Sopenharmony_ci unsigned int picosec, kb100; 113662306a36Sopenharmony_ci char *scsi = "FAST-?"; 113762306a36Sopenharmony_ci char tmp[8]; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (tp->period <= SPI_STATIC_PPR) { 114062306a36Sopenharmony_ci picosec = ppr_to_ps[tp->period]; 114162306a36Sopenharmony_ci switch (tp->period) { 114262306a36Sopenharmony_ci case 7: scsi = "FAST-320"; break; 114362306a36Sopenharmony_ci case 8: scsi = "FAST-160"; break; 114462306a36Sopenharmony_ci case 9: scsi = "FAST-80"; break; 114562306a36Sopenharmony_ci case 10: 114662306a36Sopenharmony_ci case 11: scsi = "FAST-40"; break; 114762306a36Sopenharmony_ci case 12: scsi = "FAST-20"; break; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } else { 115062306a36Sopenharmony_ci picosec = tp->period * 4000; 115162306a36Sopenharmony_ci if (tp->period < 25) 115262306a36Sopenharmony_ci scsi = "FAST-20"; 115362306a36Sopenharmony_ci else if (tp->period < 50) 115462306a36Sopenharmony_ci scsi = "FAST-10"; 115562306a36Sopenharmony_ci else 115662306a36Sopenharmony_ci scsi = "FAST-5"; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci kb100 = (10000000 + picosec / 2) / picosec; 116062306a36Sopenharmony_ci if (tp->width) 116162306a36Sopenharmony_ci kb100 *= 2; 116262306a36Sopenharmony_ci sprint_frac(tmp, picosec, 1000); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci dev_info(&starget->dev, 116562306a36Sopenharmony_ci "%s %sSCSI %d.%d MB/s %s%s%s%s%s%s%s%s (%s ns, offset %d)\n", 116662306a36Sopenharmony_ci scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10, 116762306a36Sopenharmony_ci tp->dt ? "DT" : "ST", 116862306a36Sopenharmony_ci tp->iu ? " IU" : "", 116962306a36Sopenharmony_ci tp->qas ? " QAS" : "", 117062306a36Sopenharmony_ci tp->rd_strm ? " RDSTRM" : "", 117162306a36Sopenharmony_ci tp->rti ? " RTI" : "", 117262306a36Sopenharmony_ci tp->wr_flow ? " WRFLOW" : "", 117362306a36Sopenharmony_ci tp->pcomp_en ? " PCOMP" : "", 117462306a36Sopenharmony_ci tp->hold_mcs ? " HMCS" : "", 117562306a36Sopenharmony_ci tmp, tp->offset); 117662306a36Sopenharmony_ci } else { 117762306a36Sopenharmony_ci dev_info(&starget->dev, "%sasynchronous\n", 117862306a36Sopenharmony_ci tp->width ? "wide " : ""); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ciEXPORT_SYMBOL(spi_display_xfer_agreement); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ciint spi_populate_width_msg(unsigned char *msg, int width) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci msg[0] = EXTENDED_MESSAGE; 118662306a36Sopenharmony_ci msg[1] = 2; 118762306a36Sopenharmony_ci msg[2] = EXTENDED_WDTR; 118862306a36Sopenharmony_ci msg[3] = width; 118962306a36Sopenharmony_ci return 4; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_populate_width_msg); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ciint spi_populate_sync_msg(unsigned char *msg, int period, int offset) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci msg[0] = EXTENDED_MESSAGE; 119662306a36Sopenharmony_ci msg[1] = 3; 119762306a36Sopenharmony_ci msg[2] = EXTENDED_SDTR; 119862306a36Sopenharmony_ci msg[3] = period; 119962306a36Sopenharmony_ci msg[4] = offset; 120062306a36Sopenharmony_ci return 5; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_populate_sync_msg); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ciint spi_populate_ppr_msg(unsigned char *msg, int period, int offset, 120562306a36Sopenharmony_ci int width, int options) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci msg[0] = EXTENDED_MESSAGE; 120862306a36Sopenharmony_ci msg[1] = 6; 120962306a36Sopenharmony_ci msg[2] = EXTENDED_PPR; 121062306a36Sopenharmony_ci msg[3] = period; 121162306a36Sopenharmony_ci msg[4] = 0; 121262306a36Sopenharmony_ci msg[5] = offset; 121362306a36Sopenharmony_ci msg[6] = width; 121462306a36Sopenharmony_ci msg[7] = options; 121562306a36Sopenharmony_ci return 8; 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_populate_ppr_msg); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci/** 122062306a36Sopenharmony_ci * spi_populate_tag_msg - place a tag message in a buffer 122162306a36Sopenharmony_ci * @msg: pointer to the area to place the tag 122262306a36Sopenharmony_ci * @cmd: pointer to the scsi command for the tag 122362306a36Sopenharmony_ci * 122462306a36Sopenharmony_ci * Notes: 122562306a36Sopenharmony_ci * designed to create the correct type of tag message for the 122662306a36Sopenharmony_ci * particular request. Returns the size of the tag message. 122762306a36Sopenharmony_ci * May return 0 if TCQ is disabled for this device. 122862306a36Sopenharmony_ci **/ 122962306a36Sopenharmony_ciint spi_populate_tag_msg(unsigned char *msg, struct scsi_cmnd *cmd) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci if (cmd->flags & SCMD_TAGGED) { 123262306a36Sopenharmony_ci *msg++ = SIMPLE_QUEUE_TAG; 123362306a36Sopenharmony_ci *msg++ = scsi_cmd_to_rq(cmd)->tag; 123462306a36Sopenharmony_ci return 2; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci return 0; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_populate_tag_msg); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci#ifdef CONFIG_SCSI_CONSTANTS 124262306a36Sopenharmony_cistatic const char * const one_byte_msgs[] = { 124362306a36Sopenharmony_ci/* 0x00 */ "Task Complete", NULL /* Extended Message */, "Save Pointers", 124462306a36Sopenharmony_ci/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error", 124562306a36Sopenharmony_ci/* 0x06 */ "Abort Task Set", "Message Reject", "Nop", "Message Parity Error", 124662306a36Sopenharmony_ci/* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag", 124762306a36Sopenharmony_ci/* 0x0c */ "Target Reset", "Abort Task", "Clear Task Set", 124862306a36Sopenharmony_ci/* 0x0f */ "Initiate Recovery", "Release Recovery", 124962306a36Sopenharmony_ci/* 0x11 */ "Terminate Process", "Continue Task", "Target Transfer Disable", 125062306a36Sopenharmony_ci/* 0x14 */ NULL, NULL, "Clear ACA", "LUN Reset" 125162306a36Sopenharmony_ci}; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic const char * const two_byte_msgs[] = { 125462306a36Sopenharmony_ci/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag", 125562306a36Sopenharmony_ci/* 0x23 */ "Ignore Wide Residue", "ACA" 125662306a36Sopenharmony_ci}; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic const char * const extended_msgs[] = { 125962306a36Sopenharmony_ci/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request", 126062306a36Sopenharmony_ci/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request", 126162306a36Sopenharmony_ci/* 0x04 */ "Parallel Protocol Request", "Modify Bidirectional Data Pointer" 126262306a36Sopenharmony_ci}; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic void print_nego(const unsigned char *msg, int per, int off, int width) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci if (per) { 126762306a36Sopenharmony_ci char buf[20]; 126862306a36Sopenharmony_ci period_to_str(buf, msg[per]); 126962306a36Sopenharmony_ci printk("period = %s ns ", buf); 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (off) 127362306a36Sopenharmony_ci printk("offset = %d ", msg[off]); 127462306a36Sopenharmony_ci if (width) 127562306a36Sopenharmony_ci printk("width = %d ", 8 << msg[width]); 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_cistatic void print_ptr(const unsigned char *msg, int msb, const char *desc) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci int ptr = (msg[msb] << 24) | (msg[msb+1] << 16) | (msg[msb+2] << 8) | 128162306a36Sopenharmony_ci msg[msb+3]; 128262306a36Sopenharmony_ci printk("%s = %d ", desc, ptr); 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ciint spi_print_msg(const unsigned char *msg) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci int len = 1, i; 128862306a36Sopenharmony_ci if (msg[0] == EXTENDED_MESSAGE) { 128962306a36Sopenharmony_ci len = 2 + msg[1]; 129062306a36Sopenharmony_ci if (len == 2) 129162306a36Sopenharmony_ci len += 256; 129262306a36Sopenharmony_ci if (msg[2] < ARRAY_SIZE(extended_msgs)) 129362306a36Sopenharmony_ci printk ("%s ", extended_msgs[msg[2]]); 129462306a36Sopenharmony_ci else 129562306a36Sopenharmony_ci printk ("Extended Message, reserved code (0x%02x) ", 129662306a36Sopenharmony_ci (int) msg[2]); 129762306a36Sopenharmony_ci switch (msg[2]) { 129862306a36Sopenharmony_ci case EXTENDED_MODIFY_DATA_POINTER: 129962306a36Sopenharmony_ci print_ptr(msg, 3, "pointer"); 130062306a36Sopenharmony_ci break; 130162306a36Sopenharmony_ci case EXTENDED_SDTR: 130262306a36Sopenharmony_ci print_nego(msg, 3, 4, 0); 130362306a36Sopenharmony_ci break; 130462306a36Sopenharmony_ci case EXTENDED_WDTR: 130562306a36Sopenharmony_ci print_nego(msg, 0, 0, 3); 130662306a36Sopenharmony_ci break; 130762306a36Sopenharmony_ci case EXTENDED_PPR: 130862306a36Sopenharmony_ci print_nego(msg, 3, 5, 6); 130962306a36Sopenharmony_ci break; 131062306a36Sopenharmony_ci case EXTENDED_MODIFY_BIDI_DATA_PTR: 131162306a36Sopenharmony_ci print_ptr(msg, 3, "out"); 131262306a36Sopenharmony_ci print_ptr(msg, 7, "in"); 131362306a36Sopenharmony_ci break; 131462306a36Sopenharmony_ci default: 131562306a36Sopenharmony_ci for (i = 2; i < len; ++i) 131662306a36Sopenharmony_ci printk("%02x ", msg[i]); 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci /* Identify */ 131962306a36Sopenharmony_ci } else if (msg[0] & 0x80) { 132062306a36Sopenharmony_ci printk("Identify disconnect %sallowed %s %d ", 132162306a36Sopenharmony_ci (msg[0] & 0x40) ? "" : "not ", 132262306a36Sopenharmony_ci (msg[0] & 0x20) ? "target routine" : "lun", 132362306a36Sopenharmony_ci msg[0] & 0x7); 132462306a36Sopenharmony_ci /* Normal One byte */ 132562306a36Sopenharmony_ci } else if (msg[0] < 0x1f) { 132662306a36Sopenharmony_ci if (msg[0] < ARRAY_SIZE(one_byte_msgs) && one_byte_msgs[msg[0]]) 132762306a36Sopenharmony_ci printk("%s ", one_byte_msgs[msg[0]]); 132862306a36Sopenharmony_ci else 132962306a36Sopenharmony_ci printk("reserved (%02x) ", msg[0]); 133062306a36Sopenharmony_ci } else if (msg[0] == 0x55) { 133162306a36Sopenharmony_ci printk("QAS Request "); 133262306a36Sopenharmony_ci /* Two byte */ 133362306a36Sopenharmony_ci } else if (msg[0] <= 0x2f) { 133462306a36Sopenharmony_ci if ((msg[0] - 0x20) < ARRAY_SIZE(two_byte_msgs)) 133562306a36Sopenharmony_ci printk("%s %02x ", two_byte_msgs[msg[0] - 0x20], 133662306a36Sopenharmony_ci msg[1]); 133762306a36Sopenharmony_ci else 133862306a36Sopenharmony_ci printk("reserved two byte (%02x %02x) ", 133962306a36Sopenharmony_ci msg[0], msg[1]); 134062306a36Sopenharmony_ci len = 2; 134162306a36Sopenharmony_ci } else 134262306a36Sopenharmony_ci printk("reserved "); 134362306a36Sopenharmony_ci return len; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ciEXPORT_SYMBOL(spi_print_msg); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci#else /* ifndef CONFIG_SCSI_CONSTANTS */ 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ciint spi_print_msg(const unsigned char *msg) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci int len = 1, i; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (msg[0] == EXTENDED_MESSAGE) { 135462306a36Sopenharmony_ci len = 2 + msg[1]; 135562306a36Sopenharmony_ci if (len == 2) 135662306a36Sopenharmony_ci len += 256; 135762306a36Sopenharmony_ci for (i = 0; i < len; ++i) 135862306a36Sopenharmony_ci printk("%02x ", msg[i]); 135962306a36Sopenharmony_ci /* Identify */ 136062306a36Sopenharmony_ci } else if (msg[0] & 0x80) { 136162306a36Sopenharmony_ci printk("%02x ", msg[0]); 136262306a36Sopenharmony_ci /* Normal One byte */ 136362306a36Sopenharmony_ci } else if ((msg[0] < 0x1f) || (msg[0] == 0x55)) { 136462306a36Sopenharmony_ci printk("%02x ", msg[0]); 136562306a36Sopenharmony_ci /* Two byte */ 136662306a36Sopenharmony_ci } else if (msg[0] <= 0x2f) { 136762306a36Sopenharmony_ci printk("%02x %02x", msg[0], msg[1]); 136862306a36Sopenharmony_ci len = 2; 136962306a36Sopenharmony_ci } else 137062306a36Sopenharmony_ci printk("%02x ", msg[0]); 137162306a36Sopenharmony_ci return len; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ciEXPORT_SYMBOL(spi_print_msg); 137462306a36Sopenharmony_ci#endif /* ! CONFIG_SCSI_CONSTANTS */ 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_cistatic int spi_device_match(struct attribute_container *cont, 137762306a36Sopenharmony_ci struct device *dev) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci struct scsi_device *sdev; 138062306a36Sopenharmony_ci struct Scsi_Host *shost; 138162306a36Sopenharmony_ci struct spi_internal *i; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (!scsi_is_sdev_device(dev)) 138462306a36Sopenharmony_ci return 0; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci sdev = to_scsi_device(dev); 138762306a36Sopenharmony_ci shost = sdev->host; 138862306a36Sopenharmony_ci if (!shost->transportt || shost->transportt->host_attrs.ac.class 138962306a36Sopenharmony_ci != &spi_host_class.class) 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci /* Note: this class has no device attributes, so it has 139262306a36Sopenharmony_ci * no per-HBA allocation and thus we don't need to distinguish 139362306a36Sopenharmony_ci * the attribute containers for the device */ 139462306a36Sopenharmony_ci i = to_spi_internal(shost->transportt); 139562306a36Sopenharmony_ci if (i->f->deny_binding && i->f->deny_binding(sdev->sdev_target)) 139662306a36Sopenharmony_ci return 0; 139762306a36Sopenharmony_ci return 1; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic int spi_target_match(struct attribute_container *cont, 140162306a36Sopenharmony_ci struct device *dev) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci struct Scsi_Host *shost; 140462306a36Sopenharmony_ci struct scsi_target *starget; 140562306a36Sopenharmony_ci struct spi_internal *i; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (!scsi_is_target_device(dev)) 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci shost = dev_to_shost(dev->parent); 141162306a36Sopenharmony_ci if (!shost->transportt || shost->transportt->host_attrs.ac.class 141262306a36Sopenharmony_ci != &spi_host_class.class) 141362306a36Sopenharmony_ci return 0; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci i = to_spi_internal(shost->transportt); 141662306a36Sopenharmony_ci starget = to_scsi_target(dev); 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (i->f->deny_binding && i->f->deny_binding(starget)) 141962306a36Sopenharmony_ci return 0; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci return &i->t.target_attrs.ac == cont; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(spi_transport_class, 142562306a36Sopenharmony_ci "spi_transport", 142662306a36Sopenharmony_ci spi_setup_transport_attrs, 142762306a36Sopenharmony_ci NULL, 142862306a36Sopenharmony_ci spi_target_configure); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic DECLARE_ANON_TRANSPORT_CLASS(spi_device_class, 143162306a36Sopenharmony_ci spi_device_match, 143262306a36Sopenharmony_ci spi_device_configure); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic struct attribute *host_attributes[] = { 143562306a36Sopenharmony_ci &dev_attr_signalling.attr, 143662306a36Sopenharmony_ci &dev_attr_host_width.attr, 143762306a36Sopenharmony_ci &dev_attr_hba_id.attr, 143862306a36Sopenharmony_ci NULL 143962306a36Sopenharmony_ci}; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic struct attribute_group host_attribute_group = { 144262306a36Sopenharmony_ci .attrs = host_attributes, 144362306a36Sopenharmony_ci}; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_cistatic int spi_host_configure(struct transport_container *tc, 144662306a36Sopenharmony_ci struct device *dev, 144762306a36Sopenharmony_ci struct device *cdev) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct kobject *kobj = &cdev->kobj; 145062306a36Sopenharmony_ci struct Scsi_Host *shost = transport_class_to_shost(cdev); 145162306a36Sopenharmony_ci struct spi_internal *si = to_spi_internal(shost->transportt); 145262306a36Sopenharmony_ci struct attribute *attr = &dev_attr_signalling.attr; 145362306a36Sopenharmony_ci int rc = 0; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (si->f->set_signalling) 145662306a36Sopenharmony_ci rc = sysfs_chmod_file(kobj, attr, attr->mode | S_IWUSR); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci return rc; 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci/* returns true if we should be showing the variable. Also 146262306a36Sopenharmony_ci * overloads the return by setting 1<<1 if the attribute should 146362306a36Sopenharmony_ci * be writeable */ 146462306a36Sopenharmony_ci#define TARGET_ATTRIBUTE_HELPER(name) \ 146562306a36Sopenharmony_ci (si->f->show_##name ? S_IRUGO : 0) | \ 146662306a36Sopenharmony_ci (si->f->set_##name ? S_IWUSR : 0) 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cistatic umode_t target_attribute_is_visible(struct kobject *kobj, 146962306a36Sopenharmony_ci struct attribute *attr, int i) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct device *cdev = container_of(kobj, struct device, kobj); 147262306a36Sopenharmony_ci struct scsi_target *starget = transport_class_to_starget(cdev); 147362306a36Sopenharmony_ci struct Scsi_Host *shost = transport_class_to_shost(cdev); 147462306a36Sopenharmony_ci struct spi_internal *si = to_spi_internal(shost->transportt); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (attr == &dev_attr_period.attr && 147762306a36Sopenharmony_ci spi_support_sync(starget)) 147862306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(period); 147962306a36Sopenharmony_ci else if (attr == &dev_attr_min_period.attr && 148062306a36Sopenharmony_ci spi_support_sync(starget)) 148162306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(period); 148262306a36Sopenharmony_ci else if (attr == &dev_attr_offset.attr && 148362306a36Sopenharmony_ci spi_support_sync(starget)) 148462306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(offset); 148562306a36Sopenharmony_ci else if (attr == &dev_attr_max_offset.attr && 148662306a36Sopenharmony_ci spi_support_sync(starget)) 148762306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(offset); 148862306a36Sopenharmony_ci else if (attr == &dev_attr_width.attr && 148962306a36Sopenharmony_ci spi_support_wide(starget)) 149062306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(width); 149162306a36Sopenharmony_ci else if (attr == &dev_attr_max_width.attr && 149262306a36Sopenharmony_ci spi_support_wide(starget)) 149362306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(width); 149462306a36Sopenharmony_ci else if (attr == &dev_attr_iu.attr && 149562306a36Sopenharmony_ci spi_support_ius(starget)) 149662306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(iu); 149762306a36Sopenharmony_ci else if (attr == &dev_attr_max_iu.attr && 149862306a36Sopenharmony_ci spi_support_ius(starget)) 149962306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(iu); 150062306a36Sopenharmony_ci else if (attr == &dev_attr_dt.attr && 150162306a36Sopenharmony_ci spi_support_dt(starget)) 150262306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(dt); 150362306a36Sopenharmony_ci else if (attr == &dev_attr_qas.attr && 150462306a36Sopenharmony_ci spi_support_qas(starget)) 150562306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(qas); 150662306a36Sopenharmony_ci else if (attr == &dev_attr_max_qas.attr && 150762306a36Sopenharmony_ci spi_support_qas(starget)) 150862306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(qas); 150962306a36Sopenharmony_ci else if (attr == &dev_attr_wr_flow.attr && 151062306a36Sopenharmony_ci spi_support_ius(starget)) 151162306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(wr_flow); 151262306a36Sopenharmony_ci else if (attr == &dev_attr_rd_strm.attr && 151362306a36Sopenharmony_ci spi_support_ius(starget)) 151462306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(rd_strm); 151562306a36Sopenharmony_ci else if (attr == &dev_attr_rti.attr && 151662306a36Sopenharmony_ci spi_support_ius(starget)) 151762306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(rti); 151862306a36Sopenharmony_ci else if (attr == &dev_attr_pcomp_en.attr && 151962306a36Sopenharmony_ci spi_support_ius(starget)) 152062306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(pcomp_en); 152162306a36Sopenharmony_ci else if (attr == &dev_attr_hold_mcs.attr && 152262306a36Sopenharmony_ci spi_support_ius(starget)) 152362306a36Sopenharmony_ci return TARGET_ATTRIBUTE_HELPER(hold_mcs); 152462306a36Sopenharmony_ci else if (attr == &dev_attr_revalidate.attr) 152562306a36Sopenharmony_ci return S_IWUSR; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci return 0; 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_cistatic struct attribute *target_attributes[] = { 153162306a36Sopenharmony_ci &dev_attr_period.attr, 153262306a36Sopenharmony_ci &dev_attr_min_period.attr, 153362306a36Sopenharmony_ci &dev_attr_offset.attr, 153462306a36Sopenharmony_ci &dev_attr_max_offset.attr, 153562306a36Sopenharmony_ci &dev_attr_width.attr, 153662306a36Sopenharmony_ci &dev_attr_max_width.attr, 153762306a36Sopenharmony_ci &dev_attr_iu.attr, 153862306a36Sopenharmony_ci &dev_attr_max_iu.attr, 153962306a36Sopenharmony_ci &dev_attr_dt.attr, 154062306a36Sopenharmony_ci &dev_attr_qas.attr, 154162306a36Sopenharmony_ci &dev_attr_max_qas.attr, 154262306a36Sopenharmony_ci &dev_attr_wr_flow.attr, 154362306a36Sopenharmony_ci &dev_attr_rd_strm.attr, 154462306a36Sopenharmony_ci &dev_attr_rti.attr, 154562306a36Sopenharmony_ci &dev_attr_pcomp_en.attr, 154662306a36Sopenharmony_ci &dev_attr_hold_mcs.attr, 154762306a36Sopenharmony_ci &dev_attr_revalidate.attr, 154862306a36Sopenharmony_ci NULL 154962306a36Sopenharmony_ci}; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cistatic struct attribute_group target_attribute_group = { 155262306a36Sopenharmony_ci .attrs = target_attributes, 155362306a36Sopenharmony_ci .is_visible = target_attribute_is_visible, 155462306a36Sopenharmony_ci}; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic int spi_target_configure(struct transport_container *tc, 155762306a36Sopenharmony_ci struct device *dev, 155862306a36Sopenharmony_ci struct device *cdev) 155962306a36Sopenharmony_ci{ 156062306a36Sopenharmony_ci struct kobject *kobj = &cdev->kobj; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci /* force an update based on parameters read from the device */ 156362306a36Sopenharmony_ci sysfs_update_group(kobj, &target_attribute_group); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci return 0; 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistruct scsi_transport_template * 156962306a36Sopenharmony_cispi_attach_transport(struct spi_function_template *ft) 157062306a36Sopenharmony_ci{ 157162306a36Sopenharmony_ci struct spi_internal *i = kzalloc(sizeof(struct spi_internal), 157262306a36Sopenharmony_ci GFP_KERNEL); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci if (unlikely(!i)) 157562306a36Sopenharmony_ci return NULL; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci i->t.target_attrs.ac.class = &spi_transport_class.class; 157862306a36Sopenharmony_ci i->t.target_attrs.ac.grp = &target_attribute_group; 157962306a36Sopenharmony_ci i->t.target_attrs.ac.match = spi_target_match; 158062306a36Sopenharmony_ci transport_container_register(&i->t.target_attrs); 158162306a36Sopenharmony_ci i->t.target_size = sizeof(struct spi_transport_attrs); 158262306a36Sopenharmony_ci i->t.host_attrs.ac.class = &spi_host_class.class; 158362306a36Sopenharmony_ci i->t.host_attrs.ac.grp = &host_attribute_group; 158462306a36Sopenharmony_ci i->t.host_attrs.ac.match = spi_host_match; 158562306a36Sopenharmony_ci transport_container_register(&i->t.host_attrs); 158662306a36Sopenharmony_ci i->t.host_size = sizeof(struct spi_host_attrs); 158762306a36Sopenharmony_ci i->f = ft; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci return &i->t; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ciEXPORT_SYMBOL(spi_attach_transport); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_civoid spi_release_transport(struct scsi_transport_template *t) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci struct spi_internal *i = to_spi_internal(t); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci transport_container_unregister(&i->t.target_attrs); 159862306a36Sopenharmony_ci transport_container_unregister(&i->t.host_attrs); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci kfree(i); 160162306a36Sopenharmony_ci} 160262306a36Sopenharmony_ciEXPORT_SYMBOL(spi_release_transport); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_cistatic __init int spi_transport_init(void) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci int error = scsi_dev_info_add_list(SCSI_DEVINFO_SPI, 160762306a36Sopenharmony_ci "SCSI Parallel Transport Class"); 160862306a36Sopenharmony_ci if (!error) { 160962306a36Sopenharmony_ci int i; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci for (i = 0; spi_static_device_list[i].vendor; i++) 161262306a36Sopenharmony_ci scsi_dev_info_list_add_keyed(1, /* compatible */ 161362306a36Sopenharmony_ci spi_static_device_list[i].vendor, 161462306a36Sopenharmony_ci spi_static_device_list[i].model, 161562306a36Sopenharmony_ci NULL, 161662306a36Sopenharmony_ci spi_static_device_list[i].flags, 161762306a36Sopenharmony_ci SCSI_DEVINFO_SPI); 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci error = transport_class_register(&spi_transport_class); 162162306a36Sopenharmony_ci if (error) 162262306a36Sopenharmony_ci return error; 162362306a36Sopenharmony_ci error = anon_transport_class_register(&spi_device_class); 162462306a36Sopenharmony_ci return transport_class_register(&spi_host_class); 162562306a36Sopenharmony_ci} 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_cistatic void __exit spi_transport_exit(void) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci transport_class_unregister(&spi_transport_class); 163062306a36Sopenharmony_ci anon_transport_class_unregister(&spi_device_class); 163162306a36Sopenharmony_ci transport_class_unregister(&spi_host_class); 163262306a36Sopenharmony_ci scsi_dev_info_remove_list(SCSI_DEVINFO_SPI); 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ciMODULE_AUTHOR("Martin Hicks"); 163662306a36Sopenharmony_ciMODULE_DESCRIPTION("SPI Transport Attributes"); 163762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cimodule_init(spi_transport_init); 164062306a36Sopenharmony_cimodule_exit(spi_transport_exit); 1641