/*
* IEEE 1394 for Linux
*
* 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>
#include <linux/moduleparam.h>
#include <linux/param.h>
#include <linux/spinlock.h>
#include "csr1212.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394.h"
#include "highlevel.h"
#include "ieee1394_core.h"
/* Module Parameters */
/* this module parameter can be used to disable mapping of the FCP registers */
static int fcp = 1;
module_param(fcp, int, 0444);
MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0).");
static struct csr1212_keyval *node_cap = NULL;
static void add_host(struct hpsb_host *host);
static void remove_host(struct hpsb_host *host);
static void host_reset(struct hpsb_host *host);
static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 fl);
static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
quadlet_t *data, u64 addr, size_t length, u16 flags);
static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
u64 addr, size_t length, u16 flags);
static int write_regs(struct hpsb_host *host, int nodeid, int destid,
quadlet_t *data, u64 addr, size_t length, u16 flags);
static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl);
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);
static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 fl);
static u64 allocate_addr_range(u64 size, u32 alignment, void *__host);
static void release_addr_range(u64 addr, void *__host);
static struct hpsb_highlevel csr_highlevel = {
.name = "standard registers",
.add_host = add_host,
.remove_host = remove_host,
.host_reset = host_reset,
};
static struct hpsb_address_ops map_ops = {
.read = read_maps,
};
static struct hpsb_address_ops fcp_ops = {
.write = write_fcp,
};
static struct hpsb_address_ops reg_ops = {
.read = read_regs,
.write = write_regs,
.lock = lock_regs,
.lock64 = lock64_regs,
};
static struct hpsb_address_ops config_rom_ops = {
.read = read_config_rom,
};
struct csr1212_bus_ops csr_bus_ops = {
.allocate_addr_range = allocate_addr_range,
.release_addr = release_addr_range,
};
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) ^ (be32_to_cpu(*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 = 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;
if (!host->is_root) {
/* clear cmstr bit */
host->csr.state &= ~0x100;
}
host->csr.topology_map[1] =
cpu_to_be32(be32_to_cpu(host->csr