Commit ccde55eb authored by Matthew Wilcox's avatar Matthew Wilcox Committed by James Bottomley

[PATCH] Display SPI transfer agreement in common code

[ This is an updated version of
  http://marc.theaimsgroup.com/?l=linux-scsi&m=110918302718178&w=2
  Relative to that patch, it prints the period in a nicer way; exactly
  the same way as the strings in the original version of this file. ]

Introduce a generic SPI function to print the negotiated transfer
agreement.  It's based on the implementation in sym53c8xx_2/sym_misc.c.
I have changes in my tree to make 53c700, ncr53c8xx and sym2 use it.
Other drivers need to be converted to use the SPI transport layer first.

In order to calculate the speed I needed to be able to convert from
period factor to period in picoseconds.  That required changing
(show|store)_spi_transport_period to work in picoseconds rather than
a string.
Signed-off-by: default avatarMatthew Wilcox <matthew@wil.cx>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent a18e1b13
......@@ -18,15 +18,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <scsi/scsi.h>
#include "scsi_priv.h"
#include <scsi/scsi_device.h>
......@@ -63,27 +59,47 @@ struct spi_internal {
#define to_spi_internal(tmpl) container_of(tmpl, struct spi_internal, t)
static const char *const ppr_to_ns[] = {
static const int ppr_to_ps[] = {
/* The PPR values 0-6 are reserved, fill them in when
* the committee defines them */
NULL, /* 0x00 */
NULL, /* 0x01 */
NULL, /* 0x02 */
NULL, /* 0x03 */
NULL, /* 0x04 */
NULL, /* 0x05 */
NULL, /* 0x06 */
"3.125", /* 0x07 */
"6.25", /* 0x08 */
"12.5", /* 0x09 */
"25", /* 0x0a */
"30.3", /* 0x0b */
"50", /* 0x0c */
-1, /* 0x00 */
-1, /* 0x01 */
-1, /* 0x02 */
-1, /* 0x03 */
-1, /* 0x04 */
-1, /* 0x05 */
-1, /* 0x06 */
3125, /* 0x07 */
6250, /* 0x08 */
12500, /* 0x09 */
25000, /* 0x0a */
30300, /* 0x0b */
50000, /* 0x0c */
};
/* The PPR values at which you calculate the period in ns by multiplying
* by 4 */
#define SPI_STATIC_PPR 0x0c
static int sprint_frac(char *dest, int value, int denom)
{
int frac = value % denom;
int result = sprintf(dest, "%d", value / denom);
if (frac == 0)
return result;
dest[result++] = '.';
do {
denom /= 10;
sprintf(dest + result, "%d", frac / denom);
result++;
frac %= denom;
} while (frac);
dest[result++] = '\0';
return result;
}
static struct {
enum spi_signal_type value;
char *name;
......@@ -261,7 +277,7 @@ static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf)
struct scsi_target *starget = transport_class_to_starget(cdev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct spi_transport_attrs *tp;
const char *str;
int len, picosec;
struct spi_internal *i = to_spi_internal(shost->transportt);
tp = (struct spi_transport_attrs *)&starget->starget_data;
......@@ -269,22 +285,20 @@ static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf)
if (i->f->get_period)
i->f->get_period(starget);
switch(tp->period) {
case 0x07 ... SPI_STATIC_PPR:
str = ppr_to_ns[tp->period];
if(!str)
str = "reserved";
break;
case (SPI_STATIC_PPR+1) ... 0xff:
return sprintf(buf, "%d\n", tp->period * 4);
default:
str = "unknown";
if (tp->period < 0 || tp->period > 0xff) {
picosec = -1;
} else if (tp->period <= SPI_STATIC_PPR) {
picosec = ppr_to_ps[tp->period];
} else {
picosec = tp->period * 4000;
}
return sprintf(buf, "%s\n", str);
if (picosec == -1)
return sprintf(buf, "reserved");
len = sprint_frac(buf, picosec, 1000);
buf[len++] = '\n';
buf[len] = '\0';
return len;
}
static ssize_t
......@@ -294,34 +308,30 @@ store_spi_transport_period(struct class_device *cdev, const char *buf,
struct scsi_target *starget = transport_class_to_starget(cdev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct spi_internal *i = to_spi_internal(shost->transportt);
int j, period = -1;
for (j = 0; j < SPI_STATIC_PPR; j++) {
int len;
if(ppr_to_ns[j] == NULL)
continue;
len = strlen(ppr_to_ns[j]);
if(strncmp(ppr_to_ns[j], buf, len) != 0)
continue;
int j, picosec, period = -1;
char *endp;
picosec = simple_strtoul(buf, &endp, 10) * 1000;
if (*endp == '.') {
int mult = 100;
do {
endp++;
if (!isdigit(*endp))
break;
picosec += (*endp - '0') * mult;
mult /= 10;
} while (mult > 0);
}
if(buf[len] != '\n')
for (j = 0; j <= SPI_STATIC_PPR; j++) {
if (ppr_to_ps[j] < picosec)
continue;
period = j;
break;
}
if (period == -1) {
int val = simple_strtoul(buf, NULL, 0);
/* Should probably check limits here, but this
* gets reasonably close to OK for most things */
period = val/4;
}
if (period == -1)
period = picosec / 4000;
if (period > 0xff)
period = 0xff;
......@@ -330,7 +340,7 @@ store_spi_transport_period(struct class_device *cdev, const char *buf,
return count;
}
static CLASS_DEVICE_ATTR(period, S_IRUGO | S_IWUSR,
show_spi_transport_period,
store_spi_transport_period);
......@@ -795,6 +805,61 @@ spi_schedule_dv_device(struct scsi_device *sdev)
}
EXPORT_SYMBOL(spi_schedule_dv_device);
/**
* spi_display_xfer_agreement - Print the current target transfer agreement
* @starget: The target for which to display the agreement
*
* Each SPI port is required to maintain a transfer agreement for each
* other port on the bus. This function prints a one-line summary of
* the current agreement; more detailed information is available in sysfs.
*/
void spi_display_xfer_agreement(struct scsi_target *starget)
{
struct spi_transport_attrs *tp;
tp = (struct spi_transport_attrs *)&starget->starget_data;
if (tp->offset > 0 && tp->period > 0) {
unsigned int picosec, kb100;
char *scsi = "FAST-?";
char tmp[8];
if (tp->period <= SPI_STATIC_PPR) {
picosec = ppr_to_ps[tp->period];
switch (tp->period) {
case 7: scsi = "FAST-320"; break;
case 8: scsi = "FAST-160"; break;
case 9: scsi = "FAST-80"; break;
case 10:
case 11: scsi = "FAST-40"; break;
case 12: scsi = "FAST-20"; break;
}
} else {
picosec = tp->period * 4000;
if (tp->period < 25)
scsi = "FAST-20";
else if (tp->period < 50)
scsi = "FAST-10";
else
scsi = "FAST-5";
}
kb100 = (10000000 + picosec / 2) / picosec;
if (tp->width)
kb100 *= 2;
sprint_frac(tmp, picosec, 1000);
dev_info(&starget->dev,
"%s %sSCSI %d.%d MB/s %s%s%s (%s ns, offset %d)\n",
scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
tp->dt ? "DT" : "ST", tp->iu ? " IU" : "",
tp->qas ? " QAS" : "", tmp, tp->offset);
} else {
dev_info(&starget->dev, "%sasynchronous.\n",
tp->width ? "wide " : "");
}
}
EXPORT_SYMBOL(spi_display_xfer_agreement);
#define SETUP_ATTRIBUTE(field) \
i->private_attrs[count] = class_device_attr_##field; \
if (!i->f->set_##field) { \
......
......@@ -130,5 +130,6 @@ struct scsi_transport_template *spi_attach_transport(struct spi_function_templat
void spi_release_transport(struct scsi_transport_template *);
void spi_schedule_dv_device(struct scsi_device *);
void spi_dv_device(struct scsi_device *);
void spi_display_xfer_agreement(struct scsi_target *);
#endif /* SCSI_TRANSPORT_SPI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment