Skip to content

Commit

Permalink
firewire: core: add CSR SPLIT_TIMEOUT support
Browse files Browse the repository at this point in the history
Implement the SPLIT_TIMEOUT registers.  Besides being required by the
spec, this is desirable for some IIDC devices and necessary for many
audio devices to be able to increase the timeout from userspace.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
  • Loading branch information
Clemens Ladisch committed Jun 10, 2010
1 parent 446eba0 commit 8e4b50f
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 11 deletions.
4 changes: 4 additions & 0 deletions drivers/firewire/core-card.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,10 @@ void fw_card_initialize(struct fw_card *card,
card->device = device;
card->current_tlabel = 0;
card->tlabel_mask = 0;
card->split_timeout_hi = 0;
card->split_timeout_lo = 800 << 19;
card->split_timeout_cycles = 800;
card->split_timeout_jiffies = DIV_ROUND_UP(HZ, 10);
card->color = 0;
card->broadcast_channel = BROADCAST_CHANNEL_INITIAL;

Expand Down
76 changes: 65 additions & 11 deletions drivers/firewire/core-transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
setup_timer(&t->split_timeout_timer,
split_transaction_timeout_callback, (unsigned long)t);
/* FIXME: start this timer later, relative to t->timestamp */
mod_timer(&t->split_timeout_timer, jiffies + DIV_ROUND_UP(HZ, 10));
mod_timer(&t->split_timeout_timer,
jiffies + card->split_timeout_jiffies);
t->callback = callback;
t->callback_data = callback_data;

Expand Down Expand Up @@ -673,11 +674,28 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header,
}
EXPORT_SYMBOL(fw_fill_response);

static struct fw_request *allocate_request(struct fw_packet *p)
static u32 compute_split_timeout_timestamp(struct fw_card *card,
u32 request_timestamp)
{
unsigned int cycles;
u32 timestamp;

cycles = card->split_timeout_cycles;
cycles += request_timestamp & 0x1fff;

timestamp = request_timestamp & ~0x1fff;
timestamp += (cycles / 8000) << 13;
timestamp |= cycles % 8000;

return timestamp;
}

static struct fw_request *allocate_request(struct fw_card *card,
struct fw_packet *p)
{
struct fw_request *request;
u32 *data, length;
int request_tcode, t;
int request_tcode;

request_tcode = HEADER_GET_TCODE(p->header[0]);
switch (request_tcode) {
Expand Down Expand Up @@ -712,14 +730,9 @@ static struct fw_request *allocate_request(struct fw_packet *p)
if (request == NULL)
return NULL;

t = (p->timestamp & 0x1fff) + 4000;
if (t >= 8000)
t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000;
else
t = (p->timestamp & ~0x1fff) + t;

request->response.speed = p->speed;
request->response.timestamp = t;
request->response.timestamp =
compute_split_timeout_timestamp(card, p->timestamp);
request->response.generation = p->generation;
request->response.ack = 0;
request->response.callback = free_response_callback;
Expand Down Expand Up @@ -845,7 +858,7 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE)
return;

request = allocate_request(p);
request = allocate_request(card, p);
if (request == NULL) {
/* FIXME: send statically allocated busy packet. */
return;
Expand Down Expand Up @@ -993,6 +1006,19 @@ static u32 read_state_register(struct fw_card *card)
return 0;
}

static void update_split_timeout(struct fw_card *card)
{
unsigned int cycles;

cycles = card->split_timeout_hi * 8000 + (card->split_timeout_lo >> 19);

cycles = max(cycles, 800u); /* minimum as per the spec */
cycles = min(cycles, 3u * 8000u); /* maximum OHCI timeout */

card->split_timeout_cycles = cycles;
card->split_timeout_jiffies = DIV_ROUND_UP(cycles * HZ, 8000);
}

static void handle_registers(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source, int generation,
int speed, unsigned long long offset,
Expand All @@ -1001,6 +1027,7 @@ static void handle_registers(struct fw_card *card, struct fw_request *request,
int reg = offset & ~CSR_REGISTER_BASE;
__be32 *data = payload;
int rcode = RCODE_COMPLETE;
unsigned long flags;

switch (reg) {
case CSR_STATE_CLEAR:
Expand Down Expand Up @@ -1039,6 +1066,33 @@ static void handle_registers(struct fw_card *card, struct fw_request *request,
rcode = RCODE_TYPE_ERROR;
break;

case CSR_SPLIT_TIMEOUT_HI:
if (tcode == TCODE_READ_QUADLET_REQUEST) {
*data = cpu_to_be32(card->split_timeout_hi);
} else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
spin_lock_irqsave(&card->lock, flags);
card->split_timeout_hi = be32_to_cpu(*data) & 7;
update_split_timeout(card);
spin_unlock_irqrestore(&card->lock, flags);
} else {
rcode = RCODE_TYPE_ERROR;
}
break;

case CSR_SPLIT_TIMEOUT_LO:
if (tcode == TCODE_READ_QUADLET_REQUEST) {
*data = cpu_to_be32(card->split_timeout_lo);
} else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
spin_lock_irqsave(&card->lock, flags);
card->split_timeout_lo =
be32_to_cpu(*data) & 0xfff80000;
update_split_timeout(card);
spin_unlock_irqrestore(&card->lock, flags);
} else {
rcode = RCODE_TYPE_ERROR;
}
break;

case CSR_CYCLE_TIME:
if (TCODE_IS_READ_REQUEST(tcode) && length == 4)
*data = cpu_to_be32(card->driver->
Expand Down
5 changes: 5 additions & 0 deletions include/linux/firewire.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ struct fw_card {
struct list_head transaction_list;
unsigned long reset_jiffies;

u32 split_timeout_hi;
u32 split_timeout_lo;
unsigned int split_timeout_cycles;
unsigned int split_timeout_jiffies;

unsigned long long guid;
unsigned max_receive;
int link_speed;
Expand Down

0 comments on commit 8e4b50f

Please sign in to comment.