iop_fw_load.c 5.8 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12
 * Firmware loader for ETRAX FS IO-Processor
 *
 * Copyright (C) 2004  Axis Communications AB
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/firmware.h>

13 14 15 16 17 18 19
#include <hwregs/reg_rdwr.h>
#include <hwregs/reg_map.h>
#include <hwregs/iop/iop_reg_space.h>
#include <hwregs/iop/iop_mpu_macros.h>
#include <hwregs/iop/iop_mpu_defs.h>
#include <hwregs/iop/iop_spu_defs.h>
#include <hwregs/iop/iop_sw_cpu_defs.h>
20 21 22

#define IOP_TIMEOUT 100

23 24 25
#error "This driver is broken with regard to its driver core usage."
#error "Please contact <greg@kroah.com> for details on how to fix it properly."

26
static struct device iop_spu_device[2] = {
27 28
	{ .init_name =     "iop-spu0", },
	{ .init_name =     "iop-spu1", },
29 30 31
};

static struct device iop_mpu_device = {
32
	.init_name =       "iop-mpu",
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
};

static int wait_mpu_idle(void)
{
	reg_iop_mpu_r_stat mpu_stat;
	unsigned int timeout = IOP_TIMEOUT;

	do {
		mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat);
	} while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0);
	if (timeout == 0) {
		printk(KERN_ERR "Timeout waiting for MPU to be idle\n");
		return -EBUSY;
	}
	return 0;
}

int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst)
{
	reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = {
		.wr_spu0_mem =    regk_iop_sw_cpu_no,
		.wr_spu1_mem =    regk_iop_sw_cpu_no,
		.size =           4,
		.cmd =            regk_iop_sw_cpu_reg_copy,
		.keep_owner =     regk_iop_sw_cpu_yes
	};
	reg_iop_spu_rw_ctrl spu_ctrl = {
		.en  =            regk_iop_spu_no,
		.fsm =            regk_iop_spu_no,
	};
	reg_iop_sw_cpu_r_mc_stat mc_stat;
        const struct firmware *fw_entry;
	u32 *data;
	unsigned int timeout;
	int retval, i;

	if (spu_inst > 1)
		return -ENODEV;

	/* get firmware */
	retval = request_firmware(&fw_entry,
				  fw_name,
				  &iop_spu_device[spu_inst]);
	if (retval != 0)
	{
		printk(KERN_ERR
		       "iop_load_spu: Failed to load firmware \"%s\"\n",
		       fw_name);
		return retval;
	}
	data = (u32 *) fw_entry->data;

	/* acquire ownership of memory controller */
	switch (spu_inst) {
	case 0:
		mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes;
		REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl);
		break;
	case 1:
		mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes;
		REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl);
		break;
	}
	timeout = IOP_TIMEOUT;
	do {
		REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl);
		mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat);
	} while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0);
	if (timeout == 0) {
		printk(KERN_ERR "Timeout waiting to acquire MC\n");
		retval = -EBUSY;
		goto out;
	}

	/* write to SPU memory */
	for (i = 0; i < (fw_entry->size/4); i++) {
		switch (spu_inst) {
		case 0:
			REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4));
			break;
		case 1:
			REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4));
			break;
		}
		REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data);
		data++;
	}

	/* release ownership of memory controller */
	(void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data);

 out:
	release_firmware(fw_entry);
	return retval;
}

int iop_fw_load_mpu(unsigned char *fw_name)
{
	const unsigned int start_addr = 0;
	reg_iop_mpu_rw_ctrl mpu_ctrl;
        const struct firmware *fw_entry;
	u32 *data;
	int retval, i;

	/* get firmware */
	retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
	if (retval != 0)
	{
		printk(KERN_ERR
		       "iop_load_spu: Failed to load firmware \"%s\"\n",
		       fw_name);
		return retval;
	}
	data = (u32 *) fw_entry->data;

	/* disable MPU */
	mpu_ctrl.en = regk_iop_mpu_no;
	REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
	/* put start address in R0 */
	REG_WR_VECT(iop_mpu, regi_iop_mpu, rw_r, 0, start_addr);
	/* write to memory by executing 'SWX i, 4, R0' for each word */
	if ((retval = wait_mpu_idle()) != 0)
		goto out;
	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_SWX_IIR_INSTR(0, 4, 0));
	for (i = 0; i < (fw_entry->size / 4); i++) {
		REG_WR_INT(iop_mpu, regi_iop_mpu, rw_immediate, *data);
		if ((retval = wait_mpu_idle()) != 0)
			goto out;
		data++;
	}

 out:
	release_firmware(fw_entry);
	return retval;
}

int iop_start_mpu(unsigned int start_addr)
{
	reg_iop_mpu_rw_ctrl mpu_ctrl = { .en = regk_iop_mpu_yes };
	int retval;

	/* disable MPU */
	if ((retval = wait_mpu_idle()) != 0)
		goto out;
	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_HALT());
	if ((retval = wait_mpu_idle()) != 0)
		goto out;
	/* set PC and wait for it to bite */
	if ((retval = wait_mpu_idle()) != 0)
		goto out;
	REG_WR_INT(iop_mpu, regi_iop_mpu, rw_instr, MPU_BA_I(start_addr));
	if ((retval = wait_mpu_idle()) != 0)
		goto out;
	/* make sure the MPU starts executing with interrupts disabled */
	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_DI());
	if ((retval = wait_mpu_idle()) != 0)
		goto out;
	/* enable MPU */
	REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
 out:
	return retval;
}

static int __init iop_fw_load_init(void)
{
198 199 200 201 202 203 204
#if 0
	/*
	 * static struct devices can not be added directly to sysfs by ignoring
	 * the driver model infrastructure.  To fix this properly, please use
	 * the platform_bus to register these devices to be able to properly
	 * use the firmware infrastructure.
	 */
205 206 207 208 209 210 211 212 213
	device_initialize(&iop_spu_device[0]);
	kobject_set_name(&iop_spu_device[0].kobj, "iop-spu0");
	kobject_add(&iop_spu_device[0].kobj);
	device_initialize(&iop_spu_device[1]);
	kobject_set_name(&iop_spu_device[1].kobj, "iop-spu1");
	kobject_add(&iop_spu_device[1].kobj);
	device_initialize(&iop_mpu_device);
	kobject_set_name(&iop_mpu_device.kobj, "iop-mpu");
	kobject_add(&iop_mpu_device.kobj);
214
#endif
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	return 0;
}

static void __exit iop_fw_load_exit(void)
{
}

module_init(iop_fw_load_init);
module_exit(iop_fw_load_exit);

MODULE_DESCRIPTION("ETRAX FS IO-Processor Firmware Loader");
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(iop_fw_load_spu);
EXPORT_SYMBOL(iop_fw_load_mpu);
EXPORT_SYMBOL(iop_start_mpu);