Commit 74d07703 authored by Andrey Filippov's avatar Andrey Filippov

merged with master (ahci-related)

parents 9f769f4c 363177f0
......@@ -111,6 +111,36 @@ static int elphel_check_load(struct device *dev)
return ret;
}
/** Calculate the difference between two time stamps and return it in microseconds */
static unsigned long time_diff_usec(sec_usec_t *start_time, sec_usec_t *end_time)
{
unsigned long time_us;
const unsigned long scale = 1000000;
if (start_time->sec <= end_time->sec) {
time_us = (end_time->sec - start_time->sec) * scale;
} else {
// time counter has rolled over
time_us = (ULONG_MAX - start_time->sec + end_time->sec) * scale;
}
if (start_time->usec <= end_time->usec)
time_us += end_time->usec - start_time->usec;
else
time_us += scale - start_time->usec + end_time->usec;
return time_us;
}
/** Add new recording speed sample to the list of samples */
static void add_sample(unsigned int sample, struct rec_stat *stat)
{
stat->samples[stat->samples_ptr] = sample;
stat->samples_ptr++;
if (stat->samples_ptr >= SPEED_SAMPLES_NUM) {
stat->samples_ptr = 0;
}
}
static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
{
unsigned long irq_flags;
......@@ -154,9 +184,22 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
/** Command queue processing tasklet */
void process_queue(unsigned long data)
{
int i;
size_t total_sz = 0;
unsigned long irq_flags;
unsigned long time_usec;
sec_usec_t end_time = {0};
struct elphel_ahci_priv *dpriv = (struct elphel_ahci_priv *)data;
// calculate the speed this frame has been written with
get_fpga_rtc(&end_time);
time_usec = time_diff_usec(&dpriv->stat.start_time, &end_time);
if (time_usec != 0) {
for (i = 0; i < dpriv->sg_elems; i++)
total_sz += dpriv->sgl[i].iov_len;
add_sample(total_sz / time_usec, &dpriv->stat);
}
if (process_cmd(dpriv) == 0) {
finish_cmd(dpriv);
if (move_head(dpriv) != -1) {
......@@ -919,6 +962,8 @@ static int process_cmd(struct elphel_ahci_priv *dpriv)
size_t max_sz = (MAX_LBA_COUNT + 1) * PHY_BLOCK_SIZE;
size_t rem_sz = get_size_from(dpriv->data_chunks[dpriv->head_ptr], dpriv->curr_data_chunk, dpriv->curr_data_offset, EXCLUDE_REM);
get_fpga_rtc(&dpriv->stat.start_time);
if (dpriv->flags & PROC_CMD)
dpriv->lba_ptr.lba_write += dpriv->lba_ptr.wr_count;
dpriv->flags |= PROC_CMD;
......@@ -1389,17 +1434,45 @@ static ssize_t lba_current_write(struct device *dev, struct device_attribute *at
return buff_sz;
}
/** Return the current disk write speed in MB/s */
static ssize_t wr_speed_read(struct device *dev, struct device_attribute *attr, char *buff)
{
int i;
unsigned int val, median;
unsigned int *l, *r;
unsigned int samples[SPEED_SAMPLES_NUM];
struct elphel_ahci_priv *dpriv = dev_get_dpriv(dev);
// sort recording speed samples and get median
memcpy(samples, dpriv->stat.samples, SPEED_SAMPLES_NUM * sizeof(dpriv->stat.samples[0]));
l = samples;
for (i = 1; i < SPEED_SAMPLES_NUM; i++) {
val = samples[i];
r = &samples[i- 1];
while (r >= l && *r > val) {
*(r + 1) = *r;
r--;
}
*(r + 1) = val;
}
median = samples[SPEED_SAMPLES_NUM / 2];
return snprintf(buff, 32, "Write speed: %u MB/s\n", median);
}
static DEVICE_ATTR(load_module, S_IWUSR | S_IWGRP, NULL, set_load_flag);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_WRITE, S_IWUSR | S_IWGRP, NULL, rawdev_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_START, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, lba_start_read, lba_start_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_END, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, lba_end_read, lba_end_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_CURR, S_IRUSR | S_IRGRP | S_IWUSR | S_IRGRP, lba_current_read, lba_current_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_START, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP, lba_start_read, lba_start_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_END, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP, lba_end_read, lba_end_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_CURR, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP, lba_current_read, lba_current_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_WRSPEED, S_IRUSR | S_IRGRP | S_IROTH, wr_speed_read, NULL);
static struct attribute *root_dev_attrs[] = {
&dev_attr_load_module.attr,
&dev_attr_SYSFS_AHCI_FNAME_WRITE.attr,
&dev_attr_SYSFS_AHCI_FNAME_START.attr,
&dev_attr_SYSFS_AHCI_FNAME_END.attr,
&dev_attr_SYSFS_AHCI_FNAME_CURR.attr,
&dev_attr_SYSFS_AHCI_FNAME_WRSPEED.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
......
......@@ -20,7 +20,9 @@
*/
#include <uapi/elphel/ahci_cmd.h>
#include <uapi/elphel/c313a.h>
#include "../elphel/circbuf.h"
#include "../elphel/x393_fpga_functions.h"
#ifndef _AHCI_ELPHEL_EXT
#define _AHCI_ELPHEL_EXT
......@@ -55,6 +57,15 @@
#define JPEG_SIZE_LEN 2 ///< The size in bytes of JPEG marker length field
#define INCLUDE_REM 1 ///< Include REM buffer to total size calculation
#define EXCLUDE_REM 0 ///< Exclude REM buffer from total size calculation
#define SPEED_SAMPLES_NUM 5 ///< Maximum number of samples for disk recording speed measurement
/** This structure is for collecting some recording statistics */
struct rec_stat {
unsigned int samples_ptr; ///< pointer to next sample in rec_stat::samples
unsigned int samples[SPEED_SAMPLES_NUM]; ///< calculated recording speed samples, the value of recording speed
///< presented via sysfs is a median of this array
sec_usec_t start_time; ///< time when current command has been issued
};
/** This structure holds raw device buffer pointers */
struct drv_pointers {
......@@ -111,6 +122,7 @@ struct elphel_ahci_priv {
///< because this flag is accessed from interrupt context
struct tasklet_struct bh; ///< command processing tasklet
struct device *dev; ///< pointer to parent device structure
struct rec_stat stat; ///< recording statistics
};
#endif /* _AHCI_ELPHEL_EXT */
......@@ -180,6 +180,7 @@ sec_usec_t * get_fpga_rtc(sec_usec_t * ts) ///< Pointer to a sec/usec structure
spin_unlock_bh(&fpga_time_lock);
return ts;
}
EXPORT_SYMBOL_GPL(get_fpga_rtc);
/** Set FPGA RTC to specified time */
int set_fpga_rtc (sec_usec_t ts) ///< timestamp providing seconds and microseconds
......
......@@ -38,6 +38,8 @@
#define SYSFS_AHCI_FNAME_END lba_end
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_CURR lba_current
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_WRSPEED wr_speed
/** This file is used to send commands to AHCI driver from user space applications (camogm as for now). */
#define SYSFS_AHCI_WRITE SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_WRITE)
/** This file is used to control starting LBA of a disk buffer (R/W). */
......@@ -47,6 +49,8 @@
/** This file is used to control current LBA of a disk buffer (R/W). Use this file to set a pointer inside
* [lba_start..lba_end] area where next write operation will begin. */
#define SYSFS_AHCI_LBA_CURRENT SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_CURR)
/** This file shows avarage write speed */
#define SYSFS_AHCI_WR_SPEED SYSFS_AHCI_ENTRY_NAME_TO_STR(SYSFS_AHCI_FNAME_WRSPEED)
struct frame_data {
unsigned int sensor_port;
......
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