patch-2.4.21 linux-2.4.21/drivers/ieee1394/csr.c
Next file: linux-2.4.21/drivers/ieee1394/csr.h
Previous file: linux-2.4.21/drivers/ieee1394/cmp.c
Back to the patch index
Back to the overall index
- Lines: 484
- Date:
2003-06-13 07:51:34.000000000 -0700
- Orig file:
linux-2.4.20/drivers/ieee1394/csr.c
- Orig date:
2002-11-28 15:53:13.000000000 -0800
diff -urN linux-2.4.20/drivers/ieee1394/csr.c linux-2.4.21/drivers/ieee1394/csr.c
@@ -4,20 +4,33 @@
* CSR implementation, iso/bus manager implementation.
*
* Copyright (C) 1999 Andreas E. Bombe
+ * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
*
* This code is licensed under the GPL. See the file COPYING in the root
* directory of the kernel sources for details.
+ *
+ *
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * configuration ROM manipulation
+ *
*/
#include <linux/string.h>
+#include <linux/module.h> /* needed for MODULE_PARM */
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394.h"
#include "highlevel.h"
+/* Module Parameters */
+/* this module parameter can be used to disable mapping of the FCP registers */
+MODULE_PARM(fcp,"i");
+MODULE_PARM_DESC(fcp, "FCP-registers");
+static int fcp = 1;
-/* FIXME: this one won't work on little endian with big endian data */
static u16 csr_crc16(unsigned *data, int length)
{
int check=0, i;
@@ -25,7 +38,7 @@
for (i = length; i; i--) {
for (next = check, shift = 28; shift >= 0; shift -= 4 ) {
- sum = ((next >> 12) ^ (*data >> shift)) & 0xf;
+ sum = ((next >> 12) ^ (be32_to_cpu(*data) >> shift)) & 0xf;
next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
}
check = next & 0xffff;
@@ -41,8 +54,15 @@
host->csr.bus_manager_id = 0x3f;
host->csr.bandwidth_available = 4915;
- host->csr.channels_available_hi = ~0;
+ host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */
host->csr.channels_available_lo = ~0;
+ host->csr.broadcast_channel = 0x80000000 | 31;
+
+ if (host->is_irm) {
+ if (host->driver->hw_csr_reg) {
+ host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
+ }
+ }
host->csr.node_ids = host->node_id << 16;
@@ -60,6 +80,8 @@
| csr_crc16(host->csr.topology_map + 1,
host->selfid_count + 2));
+ host->csr.speed_map[1] =
+ cpu_to_be32(be32_to_cpu(host->csr.speed_map[1]) + 1);
host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16
| csr_crc16(host->csr.speed_map+1,
0x3f1));
@@ -71,7 +93,7 @@
host->csr.lock = SPIN_LOCK_UNLOCKED;
host->csr.rom_size = host->driver->get_rom(host, &host->csr.rom);
-
+ host->csr.rom_version = 0;
host->csr.state = 0;
host->csr.node_ids = 0;
host->csr.split_timeout_hi = 0;
@@ -80,18 +102,68 @@
host->csr.bus_time = 0;
host->csr.bus_manager_id = 0x3f;
host->csr.bandwidth_available = 4915;
- host->csr.channels_available_hi = ~0;
+ host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */
host->csr.channels_available_lo = ~0;
+ host->csr.broadcast_channel = 0x80000000 | 31;
+
+ if (host->is_irm) {
+ if (host->driver->hw_csr_reg) {
+ host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
+ }
+ }
+}
+
+int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
+ size_t size, unsigned char rom_version)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+ if (rom_version != host->csr.rom_version)
+ ret = -1;
+ else if (size > (CSR_CONFIG_ROM_SIZE << 2))
+ ret = -2;
+ else {
+ memcpy(host->csr.rom,new_rom,size);
+ host->csr.rom_size=size;
+ host->csr.rom_version++;
+ ret=0;
+ }
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ return ret;
+}
+
+int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer,
+ size_t buffersize, size_t *rom_size, unsigned char *rom_version)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+ *rom_version=host->csr.rom_version;
+ *rom_size=host->csr.rom_size;
+ if (buffersize < host->csr.rom_size)
+ ret = -1;
+ else {
+ memcpy(buffer,host->csr.rom,host->csr.rom_size);
+ ret=0;
+ }
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ return ret;
}
/* Read topology / speed maps and configuration ROM */
static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, unsigned int length)
+ u64 addr, unsigned int length, u16 fl)
{
+ unsigned long flags;
int csraddr = addr - CSR_REGISTER_BASE;
const char *src;
+ spin_lock_irqsave(&host->csr.lock, flags);
+
if (csraddr < CSR_TOPOLOGY_MAP) {
if (csraddr + length > CSR_CONFIG_ROM + host->csr.rom_size) {
return RCODE_ADDRESS_ERROR;
@@ -105,6 +177,7 @@
}
memcpy(buffer, src, length);
+ spin_unlock_irqrestore(&host->csr.lock, flags);
return RCODE_COMPLETE;
}
@@ -112,7 +185,7 @@
#define out if (--length == 0) break
static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
- u64 addr, unsigned int length)
+ u64 addr, unsigned int length, u16 flags)
{
int csraddr = addr - CSR_REGISTER_BASE;
int oldcycle;
@@ -213,6 +286,10 @@
*(buf++) = cpu_to_be32(ret);
out;
+ case CSR_BROADCAST_CHANNEL:
+ *(buf++) = cpu_to_be32(host->csr.broadcast_channel);
+ out;
+
/* address gap to end - fall through to default */
default:
return RCODE_ADDRESS_ERROR;
@@ -222,7 +299,7 @@
}
static int write_regs(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, unsigned int length)
+ quadlet_t *data, u64 addr, unsigned int length, u16 flags)
{
int csraddr = addr - CSR_REGISTER_BASE;
@@ -290,6 +367,12 @@
/* these are not writable, only lockable */
return RCODE_TYPE_ERROR;
+ case CSR_BROADCAST_CHANNEL:
+ /* only the valid bit can be written */
+ host->csr.broadcast_channel = (host->csr.broadcast_channel & ~0x40000000)
+ | (be32_to_cpu(*data) & 0x40000000);
+ out;
+
/* address gap to end - fall through */
default:
return RCODE_ADDRESS_ERROR;
@@ -302,7 +385,7 @@
static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int extcode)
+ u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl)
{
int csraddr = addr - CSR_REGISTER_BASE;
unsigned long flags;
@@ -318,6 +401,18 @@
data = be32_to_cpu(data);
arg = be32_to_cpu(arg);
+ /* Is somebody releasing the broadcast_channel on us? */
+ if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x1)) {
+ /* Note: this is may not be the right way to handle
+ * the problem, so we should look into the proper way
+ * eventually. */
+ HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release "
+ "broadcast channel 31. Ignoring.",
+ NODE_BUS_ARGS(nodeid));
+
+ data &= ~0x1; /* keep broadcast channel allocated */
+ }
+
if (host->driver->hw_csr_reg) {
quadlet_t old;
@@ -334,23 +429,84 @@
switch (csraddr) {
case CSR_BUS_MANAGER_ID:
regptr = &host->csr.bus_manager_id;
+ *store = cpu_to_be32(*regptr);
+ if (*regptr == arg)
+ *regptr = data;
break;
case CSR_BANDWIDTH_AVAILABLE:
+ {
+ quadlet_t bandwidth;
+ quadlet_t old;
+ quadlet_t new;
+
regptr = &host->csr.bandwidth_available;
+ old = *regptr;
+
+ /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */
+ if (arg > 0x1fff) {
+ *store = cpu_to_be32(old); /* change nothing */
+ break;
+ }
+ data &= 0x1fff;
+ if (arg >= data) {
+ /* allocate bandwidth */
+ bandwidth = arg - data;
+ if (old >= bandwidth) {
+ new = old - bandwidth;
+ *store = cpu_to_be32(arg);
+ *regptr = new;
+ } else {
+ *store = cpu_to_be32(old);
+ }
+ } else {
+ /* deallocate bandwidth */
+ bandwidth = data - arg;
+ if (old + bandwidth < 0x2000) {
+ new = old + bandwidth;
+ *store = cpu_to_be32(arg);
+ *regptr = new;
+ } else {
+ *store = cpu_to_be32(old);
+ }
+ }
break;
+ }
case CSR_CHANNELS_AVAILABLE_HI:
+ {
+ /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */
+ quadlet_t affected_channels = arg ^ data;
+
regptr = &host->csr.channels_available_hi;
+
+ if ((arg & affected_channels) == (*regptr & affected_channels)) {
+ *regptr ^= affected_channels;
+ *store = cpu_to_be32(arg);
+ } else {
+ *store = cpu_to_be32(*regptr);
+ }
+
break;
+ }
case CSR_CHANNELS_AVAILABLE_LO:
+ {
+ /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */
+ quadlet_t affected_channels = arg ^ data;
+
regptr = &host->csr.channels_available_lo;
+
+ if ((arg & affected_channels) == (*regptr & affected_channels)) {
+ *regptr ^= affected_channels;
+ *store = cpu_to_be32(arg);
+ } else {
+ *store = cpu_to_be32(*regptr);
+ }
break;
}
+ }
- *store = cpu_to_be32(*regptr);
- if (*regptr == arg) *regptr = data;
spin_unlock_irqrestore(&host->csr.lock, flags);
return RCODE_COMPLETE;
@@ -365,10 +521,7 @@
case CSR_SPLIT_TIMEOUT_LO:
case CSR_CYCLE_TIME:
case CSR_BUS_TIME:
- case CSR_BUS_MANAGER_ID:
- case CSR_BANDWIDTH_AVAILABLE:
- case CSR_CHANNELS_AVAILABLE_HI:
- case CSR_CHANNELS_AVAILABLE_LO:
+ case CSR_BROADCAST_CHANNEL:
return RCODE_TYPE_ERROR;
case CSR_BUSY_TIMEOUT:
@@ -378,8 +531,99 @@
}
}
+static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store,
+ u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ unsigned long flags;
+
+ data = be64_to_cpu(data);
+ arg = be64_to_cpu(arg);
+
+ if (csraddr & 0x3)
+ return RCODE_TYPE_ERROR;
+
+ if (csraddr != CSR_CHANNELS_AVAILABLE
+ || extcode != EXTCODE_COMPARE_SWAP)
+ goto unsupported_lock64req;
+
+ /* Is somebody releasing the broadcast_channel on us? */
+ if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x100000000ULL)) {
+ /* Note: this is may not be the right way to handle
+ * the problem, so we should look into the proper way
+ * eventually. */
+ HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release "
+ "broadcast channel 31. Ignoring.",
+ NODE_BUS_ARGS(nodeid));
+
+ data &= ~0x100000000ULL; /* keep broadcast channel allocated */
+ }
+
+ if (host->driver->hw_csr_reg) {
+ quadlet_t data_hi, data_lo;
+ quadlet_t arg_hi, arg_lo;
+ quadlet_t old_hi, old_lo;
+
+ data_hi = data >> 32;
+ data_lo = data & 0xFFFFFFFF;
+ arg_hi = arg >> 32;
+ arg_lo = arg & 0xFFFFFFFF;
+
+ old_hi = host->driver->hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
+ data_hi, arg_hi);
+
+ old_lo = host->driver->hw_csr_reg(host, ((csraddr + 4) - CSR_BUS_MANAGER_ID) >> 2,
+ data_lo, arg_lo);
+
+ *store = cpu_to_be64(((octlet_t)old_hi << 32) | old_lo);
+ } else {
+ octlet_t old;
+ octlet_t affected_channels = arg ^ data;
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+
+ old = ((octlet_t)host->csr.channels_available_hi << 32) | host->csr.channels_available_lo;
+
+ if ((arg & affected_channels) == (old & affected_channels)) {
+ host->csr.channels_available_hi ^= (affected_channels >> 32);
+ host->csr.channels_available_lo ^= (affected_channels & 0xffffffff);
+ *store = cpu_to_be64(arg);
+ } else {
+ *store = cpu_to_be64(old);
+ }
+
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ }
+
+ /* Is somebody erroneously releasing the broadcast_channel on us? */
+ if (host->csr.channels_available_hi & 0x1)
+ host->csr.channels_available_hi &= ~0x1;
+
+ return RCODE_COMPLETE;
+
+ unsupported_lock64req:
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ case CSR_STATE_SET:
+ case CSR_RESET_START:
+ case CSR_NODE_IDS:
+ case CSR_SPLIT_TIMEOUT_HI:
+ case CSR_SPLIT_TIMEOUT_LO:
+ case CSR_CYCLE_TIME:
+ case CSR_BUS_TIME:
+ case CSR_BUS_MANAGER_ID:
+ case CSR_BROADCAST_CHANNEL:
+ case CSR_BUSY_TIMEOUT:
+ case CSR_BANDWIDTH_AVAILABLE:
+ return RCODE_TYPE_ERROR;
+
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+}
+
static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
- quadlet_t *data, u64 addr, unsigned int length)
+ quadlet_t *data, u64 addr, unsigned int length, u16 flags)
{
int csraddr = addr - CSR_REGISTER_BASE;
@@ -401,7 +645,8 @@
}
-static struct hpsb_highlevel_ops csr_ops = {
+static struct hpsb_highlevel csr_highlevel = {
+ .name = "standard registers",
.add_host = add_host,
.host_reset = host_reset,
};
@@ -419,35 +664,32 @@
.read = read_regs,
.write = write_regs,
.lock = lock_regs,
+ .lock64 = lock64_regs,
};
-static struct hpsb_highlevel *hl;
-
void init_csr(void)
{
- hl = hpsb_register_highlevel("standard registers", &csr_ops);
- if (hl == NULL) {
- HPSB_ERR("out of memory during ieee1394 initialization");
- return;
- }
+ hpsb_register_highlevel(&csr_highlevel);
- hpsb_register_addrspace(hl, ®_ops, CSR_REGISTER_BASE,
+ hpsb_register_addrspace(&csr_highlevel, ®_ops, CSR_REGISTER_BASE,
CSR_REGISTER_BASE + CSR_CONFIG_ROM);
- hpsb_register_addrspace(hl, &map_ops,
+ hpsb_register_addrspace(&csr_highlevel, &map_ops,
CSR_REGISTER_BASE + CSR_CONFIG_ROM,
CSR_REGISTER_BASE + CSR_CONFIG_ROM_END);
- hpsb_register_addrspace(hl, &fcp_ops,
+ if (fcp) {
+ hpsb_register_addrspace(&csr_highlevel, &fcp_ops,
CSR_REGISTER_BASE + CSR_FCP_COMMAND,
CSR_REGISTER_BASE + CSR_FCP_END);
- hpsb_register_addrspace(hl, &map_ops,
+ }
+ hpsb_register_addrspace(&csr_highlevel, &map_ops,
CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP,
CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END);
- hpsb_register_addrspace(hl, &map_ops,
+ hpsb_register_addrspace(&csr_highlevel, &map_ops,
CSR_REGISTER_BASE + CSR_SPEED_MAP,
CSR_REGISTER_BASE + CSR_SPEED_MAP_END);
}
void cleanup_csr(void)
{
- hpsb_unregister_highlevel(hl);
+ hpsb_unregister_highlevel(&csr_highlevel);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)