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

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, &reg_ops, CSR_REGISTER_BASE,
+        hpsb_register_addrspace(&csr_highlevel, &reg_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)