patch-2.3.40 linux/drivers/ieee1394/csr.c
Next file: linux/drivers/ieee1394/csr.h
Previous file: linux/drivers/ieee1394/aic5800.h
Back to the patch index
Back to the overall index
- Lines: 436
- Date:
Thu Jan 13 16:49:22 2000
- Orig file:
v2.3.39/linux/drivers/ieee1394/csr.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.39/linux/drivers/ieee1394/csr.c linux/drivers/ieee1394/csr.c
@@ -0,0 +1,435 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * CSR implementation, iso/bus manager implementation.
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ */
+
+#include <linux/string.h>
+
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "highlevel.h"
+
+
+static u16 csr_crc16(unsigned *data, int length)
+{
+ int check=0, i;
+ int shift, sum, next=0;
+
+ for (i = length; i; i--) {
+ for (next = check, shift = 28; shift >= 0; shift -= 4 ) {
+ sum = ((next >> 12) ^ (*data >> shift)) & 0xf;
+ next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
+ }
+ check = next & 0xffff;
+ data++;
+ }
+
+ return check;
+}
+
+static void host_reset(struct hpsb_host *host)
+{
+ host->csr.state &= 0x300;
+
+ host->csr.bus_manager_id = 0x3f;
+ host->csr.bandwidth_available = 4915;
+ host->csr.channels_available_hi = ~0;
+ host->csr.channels_available_lo = ~0;
+
+ host->csr.node_ids = host->node_id << 16;
+
+ if (!host->is_root) {
+ /* clear cmstr bit */
+ host->csr.state &= ~0x100;
+ }
+
+ host->csr.topology_map[1]++;
+ host->csr.topology_map[2] = host->node_count << 16 | host->selfid_count;
+ host->csr.topology_map[0] = (host->selfid_count + 2) << 16
+ | csr_crc16(host->csr.topology_map + 1, host->selfid_count + 2);
+
+ /* FIXME - generate speed map */
+ host->csr.speed_map[0] = 0x3f1 << 16 | csr_crc16(host->csr.speed_map+1,
+ 0x3f1);
+}
+
+
+static void add_host(struct hpsb_host *host)
+{
+ host->csr.lock = SPIN_LOCK_UNLOCKED;
+
+ host->csr.rom_size = host->template->get_rom(host, &host->csr.rom);
+
+ host->csr.state = 0;
+ host->csr.node_ids = 0;
+ host->csr.split_timeout_hi = 0;
+ host->csr.split_timeout_lo = 800 << 19;
+ host->csr.cycle_time = 0;
+ 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_lo = ~0;
+}
+
+
+/* Read topology / speed maps and configuration ROM */
+static int read_maps(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
+ unsigned int length)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ const char *src;
+
+ if (csraddr < CSR_TOPOLOGY_MAP) {
+ if (csraddr + length > CSR_CONFIG_ROM + host->csr.rom_size) {
+ return RCODE_ADDRESS_ERROR;
+ }
+ src = ((char *)host->csr.rom) + csraddr - CSR_CONFIG_ROM;
+ } else if (csraddr < CSR_SPEED_MAP) {
+ src = ((char *)host->csr.topology_map) + csraddr
+ - CSR_TOPOLOGY_MAP;
+ } else {
+ src = ((char *)host->csr.speed_map) + csraddr - CSR_SPEED_MAP;
+ }
+
+ memcpy(buffer, src, length);
+ return RCODE_COMPLETE;
+}
+
+/* Read FCP register space */
+static int read_fcp(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
+ unsigned int length)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ const char *src;
+
+ if (csraddr + length > CSR_FCP_END) {
+ return RCODE_ADDRESS_ERROR;
+ }
+ src = ((char *)host->csr.fcp_data) + csraddr - CSR_FCP_COMMAND;
+
+ memcpy(buffer, src, length);
+ return RCODE_COMPLETE;
+}
+
+/* Write FCP register space */
+static int write_fcp(struct hpsb_host *host, quadlet_t *data, u64 addr,
+ unsigned int length)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ char *dest;
+
+ if (csraddr + length > CSR_FCP_END) {
+ return RCODE_ADDRESS_ERROR;
+ }
+ dest = ((char *)host->csr.fcp_data) + csraddr - CSR_FCP_COMMAND;
+
+ memcpy(dest, data, length);
+ return RCODE_COMPLETE;
+}
+
+
+#define out if (--length == 0) break
+
+static int read_regs(struct hpsb_host *host, quadlet_t *buf, u64 addr,
+ unsigned int length)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ int oldcycle;
+
+ if ((csraddr | length) & 0x3) {
+ return RCODE_TYPE_ERROR;
+ }
+
+ length /= 4;
+
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ *(buf++) = host->csr.state;
+ out;
+ case CSR_STATE_SET:
+ *(buf++) = host->csr.state;
+ out;
+ case CSR_NODE_IDS:
+ *(buf++) = host->csr.node_ids;
+ out;
+
+ case CSR_RESET_START:
+ return RCODE_TYPE_ERROR;
+
+ /* address gap - handled by default below */
+
+ case CSR_SPLIT_TIMEOUT_HI:
+ *(buf++) = host->csr.split_timeout_hi;
+ out;
+ case CSR_SPLIT_TIMEOUT_LO:
+ *(buf++) = host->csr.split_timeout_lo;
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_CYCLE_TIME:
+ oldcycle = host->csr.cycle_time;
+ host->csr.cycle_time =
+ host->template->devctl(host, GET_CYCLE_COUNTER, 0);
+
+ if (oldcycle > host->csr.cycle_time) {
+ /* cycle time wrapped around */
+ host->csr.bus_time += 1 << 7;
+ }
+ *(buf++) = host->csr.cycle_time;
+ out;
+ case CSR_BUS_TIME:
+ oldcycle = host->csr.cycle_time;
+ host->csr.cycle_time =
+ host->template->devctl(host, GET_CYCLE_COUNTER, 0);
+
+ if (oldcycle > host->csr.cycle_time) {
+ /* cycle time wrapped around */
+ host->csr.bus_time += (1 << 7);
+ }
+ *(buf++) = host->csr.bus_time | (host->csr.cycle_time >> 25);
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUSY_TIMEOUT:
+ /* not yet implemented */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUS_MANAGER_ID:
+ *(buf++) = host->csr.bus_manager_id;
+ out;
+ case CSR_BANDWIDTH_AVAILABLE:
+ *(buf++) = host->csr.bandwidth_available;
+ out;
+ case CSR_CHANNELS_AVAILABLE_HI:
+ *(buf++) = host->csr.channels_available_hi;
+ out;
+ case CSR_CHANNELS_AVAILABLE_LO:
+ *(buf++) = host->csr.channels_available_lo;
+ out;
+
+ /* address gap to end - fall through to default */
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ return RCODE_COMPLETE;
+}
+
+static int write_regs(struct hpsb_host *host, quadlet_t *data, u64 addr,
+ unsigned int length)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+
+ if ((csraddr | length) & 0x3) {
+ return RCODE_TYPE_ERROR;
+ }
+
+ length /= 4;
+
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ /* FIXME FIXME FIXME */
+ printk("doh, someone wants to mess with state clear\n");
+ out;
+ case CSR_STATE_SET:
+ printk("doh, someone wants to mess with state set\n");
+ out;
+
+ case CSR_NODE_IDS:
+ host->csr.node_ids &= NODE_MASK << 16;
+ host->csr.node_ids |= *(data++) & (BUS_MASK << 16);
+ host->node_id = host->csr.node_ids >> 16;
+ host->template->devctl(host, SET_BUS_ID, host->node_id >> 6);
+ out;
+
+ case CSR_RESET_START:
+ /* FIXME - perform command reset */
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_SPLIT_TIMEOUT_HI:
+ host->csr.split_timeout_hi = *(data++) & 0x00000007;
+ out;
+ case CSR_SPLIT_TIMEOUT_LO:
+ host->csr.split_timeout_lo = *(data++) & 0xfff80000;
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_CYCLE_TIME:
+ /* should only be set by cycle start packet, automatically */
+ host->csr.cycle_time = *data;
+ host->template->devctl(host, SET_CYCLE_COUNTER, *(data++));
+ out;
+ case CSR_BUS_TIME:
+ host->csr.bus_time = *(data++) & 0xffffff80;
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUSY_TIMEOUT:
+ /* not yet implemented */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUS_MANAGER_ID:
+ case CSR_BANDWIDTH_AVAILABLE:
+ case CSR_CHANNELS_AVAILABLE_HI:
+ case CSR_CHANNELS_AVAILABLE_LO:
+ /* these are not writable, only lockable */
+ return RCODE_TYPE_ERROR;
+
+ /* address gap to end - fall through */
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ return RCODE_COMPLETE;
+}
+
+#undef out
+
+
+/* helper function for lock_regs */
+inline static void compare_swap(quadlet_t *old, quadlet_t data, quadlet_t arg)
+{
+ if (*old == arg) {
+ *old = data;
+ }
+}
+
+static int lock_regs(struct hpsb_host *host, quadlet_t *store, u64 addr,
+ quadlet_t data, quadlet_t arg, int extcode)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ unsigned long flags;
+
+ if (csraddr & 0x3) {
+ return RCODE_TYPE_ERROR;
+ }
+
+ if ((csraddr >= CSR_BUS_MANAGER_ID)
+ && (csraddr <= CSR_CHANNELS_AVAILABLE_LO)) {
+ if (extcode == EXTCODE_COMPARE_SWAP) {
+ spin_lock_irqsave(&host->csr.lock, flags);
+
+ switch (csraddr) {
+ case CSR_BUS_MANAGER_ID:
+ *store = host->csr.bus_manager_id;
+ compare_swap(&host->csr.bus_manager_id,
+ data, arg);
+ break;
+
+ case CSR_BANDWIDTH_AVAILABLE:
+ *store = host->csr.bandwidth_available;
+ compare_swap(&host->csr.bandwidth_available,
+ data, arg);
+ break;
+
+ case CSR_CHANNELS_AVAILABLE_HI:
+ *store = host->csr.channels_available_hi;
+ compare_swap(&host->csr.channels_available_hi,
+ data, arg);
+ break;
+
+ case CSR_CHANNELS_AVAILABLE_LO:
+ *store = host->csr.channels_available_lo;
+ compare_swap(&host->csr.channels_available_lo,
+ data, arg);
+ break;
+ }
+
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ return RCODE_COMPLETE;
+ } else {
+ return RCODE_TYPE_ERROR;
+ }
+ }
+
+ /* no locking for anything else yet */
+ 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:
+ return RCODE_TYPE_ERROR;
+
+ case CSR_BUSY_TIMEOUT:
+ /* not yet implemented - fall through */
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+}
+
+
+struct hpsb_highlevel_ops csr_ops = {
+ add_host,
+ NULL,
+ host_reset,
+ NULL
+};
+
+
+struct hpsb_address_ops map_ops = {
+ read_maps,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct hpsb_address_ops fcp_ops = {
+ read_fcp,
+ write_fcp,
+ NULL,
+ NULL
+};
+
+struct hpsb_address_ops reg_ops = {
+ read_regs,
+ write_regs,
+ lock_regs,
+ NULL
+};
+
+
+void init_csr(void)
+{
+ struct hpsb_highlevel *hl;
+
+ hl = hpsb_register_highlevel("standard registers", &csr_ops);
+ if (hl == NULL) {
+ HPSB_ERR("out of memory during ieee1394 initialization");
+ return;
+ }
+
+ hpsb_register_addrspace(hl, ®_ops, CSR_REGISTER_BASE,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM);
+ hpsb_register_addrspace(hl, &map_ops,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM_END);
+ hpsb_register_addrspace(hl, &fcp_ops,
+ CSR_REGISTER_BASE + CSR_FCP_COMMAND,
+ CSR_REGISTER_BASE + CSR_FCP_END);
+ hpsb_register_addrspace(hl, &map_ops,
+ CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP,
+ CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END);
+ hpsb_register_addrspace(hl, &map_ops,
+ CSR_REGISTER_BASE + CSR_SPEED_MAP,
+ CSR_REGISTER_BASE + CSR_SPEED_MAP_END);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)