1/*
2 * Firmware loader for ETRAX FS IO-Processor
3 *
4 * Copyright (C) 2004  Axis Communications AB
5 */
6
7#include <linux/module.h>
8#include <linux/kernel.h>
9#include <linux/init.h>
10#include <linux/device.h>
11#include <linux/firmware.h>
12
13#include <hwregs/reg_rdwr.h>
14#include <hwregs/reg_map.h>
15#include <hwregs/iop/iop_reg_space.h>
16#include <hwregs/iop/iop_mpu_macros.h>
17#include <hwregs/iop/iop_mpu_defs.h>
18#include <hwregs/iop/iop_spu_defs.h>
19#include <hwregs/iop/iop_sw_cpu_defs.h>
20
21#define IOP_TIMEOUT 100
22
23#error "This driver is broken with regard to its driver core usage."
24#error "Please contact <greg@kroah.com> for details on how to fix it properly."
25
26static struct device iop_spu_device[2] = {
27	{ .init_name =     "iop-spu0", },
28	{ .init_name =     "iop-spu1", },
29};
30
31static struct device iop_mpu_device = {
32	.init_name =       "iop-mpu",
33};
34
35static int wait_mpu_idle(void)
36{
37	reg_iop_mpu_r_stat mpu_stat;
38	unsigned int timeout = IOP_TIMEOUT;
39
40	do {
41		mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat);
42	} while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0);
43	if (timeout == 0) {
44		printk(KERN_ERR "Timeout waiting for MPU to be idle\n");
45		return -EBUSY;
46	}
47	return 0;
48}
49
50int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst)
51{
52	reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = {
53		.wr_spu0_mem =    regk_iop_sw_cpu_no,
54		.wr_spu1_mem =    regk_iop_sw_cpu_no,
55		.size =           4,
56		.cmd =            regk_iop_sw_cpu_reg_copy,
57		.keep_owner =     regk_iop_sw_cpu_yes
58	};
59	reg_iop_spu_rw_ctrl spu_ctrl = {
60		.en  =            regk_iop_spu_no,
61		.fsm =            regk_iop_spu_no,
62	};
63	reg_iop_sw_cpu_r_mc_stat mc_stat;
64        const struct firmware *fw_entry;
65	u32 *data;
66	unsigned int timeout;
67	int retval, i;
68
69	if (spu_inst > 1)
70		return -ENODEV;
71
72	/* get firmware */
73	retval = request_firmware(&fw_entry,
74				  fw_name,
75				  &iop_spu_device[spu_inst]);
76	if (retval != 0)
77	{
78		printk(KERN_ERR
79		       "iop_load_spu: Failed to load firmware \"%s\"\n",
80		       fw_name);
81		return retval;
82	}
83	data = (u32 *) fw_entry->data;
84
85	/* acquire ownership of memory controller */
86	switch (spu_inst) {
87	case 0:
88		mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes;
89		REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl);
90		break;
91	case 1:
92		mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes;
93		REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl);
94		break;
95	}
96	timeout = IOP_TIMEOUT;
97	do {
98		REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl);
99		mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat);
100	} while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0);
101	if (timeout == 0) {
102		printk(KERN_ERR "Timeout waiting to acquire MC\n");
103		retval = -EBUSY;
104		goto out;
105	}
106
107	/* write to SPU memory */
108	for (i = 0; i < (fw_entry->size/4); i++) {
109		switch (spu_inst) {
110		case 0:
111			REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4));
112			break;
113		case 1:
114			REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4));
115			break;
116		}
117		REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data);
118		data++;
119	}
120
121	/* release ownership of memory controller */
122	(void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data);
123
124 out:
125	release_firmware(fw_entry);
126	return retval;
127}
128
129int iop_fw_load_mpu(unsigned char *fw_name)
130{
131	const unsigned int start_addr = 0;
132	reg_iop_mpu_rw_ctrl mpu_ctrl;
133        const struct firmware *fw_entry;
134	u32 *data;
135	int retval, i;
136
137	/* get firmware */
138	retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
139	if (retval != 0)
140	{
141		printk(KERN_ERR
142		       "iop_load_spu: Failed to load firmware \"%s\"\n",
143		       fw_name);
144		return retval;
145	}
146	data = (u32 *) fw_entry->data;
147
148	/* disable MPU */
149	mpu_ctrl.en = regk_iop_mpu_no;
150	REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
151	/* put start address in R0 */
152	REG_WR_VECT(iop_mpu, regi_iop_mpu, rw_r, 0, start_addr);
153	/* write to memory by executing 'SWX i, 4, R0' for each word */
154	if ((retval = wait_mpu_idle()) != 0)
155		goto out;
156	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_SWX_IIR_INSTR(0, 4, 0));
157	for (i = 0; i < (fw_entry->size / 4); i++) {
158		REG_WR_INT(iop_mpu, regi_iop_mpu, rw_immediate, *data);
159		if ((retval = wait_mpu_idle()) != 0)
160			goto out;
161		data++;
162	}
163
164 out:
165	release_firmware(fw_entry);
166	return retval;
167}
168
169int iop_start_mpu(unsigned int start_addr)
170{
171	reg_iop_mpu_rw_ctrl mpu_ctrl = { .en = regk_iop_mpu_yes };
172	int retval;
173
174	/* disable MPU */
175	if ((retval = wait_mpu_idle()) != 0)
176		goto out;
177	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_HALT());
178	if ((retval = wait_mpu_idle()) != 0)
179		goto out;
180	/* set PC and wait for it to bite */
181	if ((retval = wait_mpu_idle()) != 0)
182		goto out;
183	REG_WR_INT(iop_mpu, regi_iop_mpu, rw_instr, MPU_BA_I(start_addr));
184	if ((retval = wait_mpu_idle()) != 0)
185		goto out;
186	/* make sure the MPU starts executing with interrupts disabled */
187	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_DI());
188	if ((retval = wait_mpu_idle()) != 0)
189		goto out;
190	/* enable MPU */
191	REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
192 out:
193	return retval;
194}
195
196static int __init iop_fw_load_init(void)
197{
198#if 0
199	/*
200	 * static struct devices can not be added directly to sysfs by ignoring
201	 * the driver model infrastructure.  To fix this properly, please use
202	 * the platform_bus to register these devices to be able to properly
203	 * use the firmware infrastructure.
204	 */
205	device_initialize(&iop_spu_device[0]);
206	kobject_set_name(&iop_spu_device[0].kobj, "iop-spu0");
207	kobject_add(&iop_spu_device[0].kobj);
208	device_initialize(&iop_spu_device[1]);
209	kobject_set_name(&iop_spu_device[1].kobj, "iop-spu1");
210	kobject_add(&iop_spu_device[1].kobj);
211	device_initialize(&iop_mpu_device);
212	kobject_set_name(&iop_mpu_device.kobj, "iop-mpu");
213	kobject_add(&iop_mpu_device.kobj);
214#endif
215	return 0;
216}
217
218static void __exit iop_fw_load_exit(void)
219{
220}
221
222module_init(iop_fw_load_init);
223module_exit(iop_fw_load_exit);
224
225MODULE_DESCRIPTION("ETRAX FS IO-Processor Firmware Loader");
226MODULE_LICENSE("GPL");
227
228EXPORT_SYMBOL(iop_fw_load_spu);
229EXPORT_SYMBOL(iop_fw_load_mpu);
230EXPORT_SYMBOL(iop_start_mpu);
231