11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* cycx_drv.c	Cyclom 2X Support Module.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*		This module is a library of common hardware specific
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*		functions used by the Cyclades Cyclom 2X sync card.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Copyright:	(c) 1998-2003 Arnaldo Carvalho de Melo
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Based on sdladrv.c by Gene Kozin <genek@compuserve.com>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*		This program is free software; you can redistribute it and/or
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*		modify it under the terms of the GNU General Public License
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*		as published by the Free Software Foundation; either version
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*		2 of the License, or (at your option) any later version.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* ============================================================================
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/11/11	acme		set_current_state(TASK_INTERRUPTIBLE), code
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				cleanup
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/11/08	acme		init_cyc2x deleted, doing nothing
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/11/06	acme		back to read[bw], write[bw] and memcpy_to and
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				fromio to use dpmbase ioremaped
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/10/26	acme		use isa_read[bw], isa_write[bw] & isa_memcpy_to
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				& fromio
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/10/23	acme		cleanup to only supports cyclom2x: all the other
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				boards are no longer manufactured by cyclades,
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				if someone wants to support them... be my guest!
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/05/28    acme		cycx_intack & cycx_intde gone for good
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/05/18	acme		lots of unlogged work, submitting to Linus...
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/01/03	acme		more judicious use of data types
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/01/03	acme		judicious use of data types :>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				cycx_inten trying to reset pending interrupts
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				from cyclom 2x - I think this isn't the way to
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				go, but for now...
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* 1999/01/02	acme		cycx_intack ok, I think there's nothing to do
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				to ack an int in cycx_drv.c, only handle it in
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				cyx_isr (or in the other protocols: cyp_isr,
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				cyf_isr, when they get implemented.
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Dec 31, 1998	acme		cycx_data_boot & cycx_code_boot fixed, crossing
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				fingers to see x25_configure in cycx_x25.c
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				work... :)
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Dec 26, 1998	acme		load implementation fixed, seems to work! :)
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				cycx_2x_dpmbase_options with all the possible
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				DPM addresses (20).
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				cycx_intr implemented (test this!)
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*				general code cleanup
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Dec  8, 1998	Ivan Passos	Cyclom-2X firmware load implementation.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds* Aug  8, 1998	acme		Initial version.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
52d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>		/* __init */
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>	/* printk(), and other useful stuff */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/stddef.h>	/* offsetof(), etc. */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>	/* return codes */
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cycx_drv.h>	/* API definitions */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cycx_cfm.h>	/* CYCX firmware module definitions */
60a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan#include <linux/delay.h>	/* udelay, msleep_interruptible */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>		/* read[wl], write[wl], ioremap, iounmap */
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	MOD_VERSION	0
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	MOD_RELEASE	6
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Arnaldo Carvalho de Melo");
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Cyclom 2x Sync Card Driver");
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Hardware-specific functions */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len);
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cycx_bootcfg(struct cycx_hw *hw);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int reset_cyc2x(void __iomem *addr);
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int detect_cyc2x(void __iomem *addr);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Miscellaneous functions */
78dcfc5d787a24b9a20be3e18e3dbbab5280d35e38stephen hemmingerstatic int get_option_index(const long *optlist, long optval);
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 checksum(u8 *buf, u32 len);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define wait_cyc(addr) cycx_exec(addr + CMD_OFFSET)
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Global Data */
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* private data */
86dcfc5d787a24b9a20be3e18e3dbbab5280d35e38stephen hemmingerstatic const char fullname[] = "Cyclom 2X Support Module";
87d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perchesstatic const char copyright[] =
88d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	"(c) 1998-2003 Arnaldo Carvalho de Melo <acme@conectiva.com.br>";
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Hardware configuration options.
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * These are arrays of configuration options used by verification routines.
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The first element of each array is its size (i.e. number of options).
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
94dcfc5d787a24b9a20be3e18e3dbbab5280d35e38stephen hemmingerstatic const long cyc2x_dpmbase_options[] = {
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	20,
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0xA0000, 0xA4000, 0xA8000, 0xAC000, 0xB0000, 0xB4000, 0xB8000,
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0xBC000, 0xC0000, 0xC4000, 0xC8000, 0xCC000, 0xD0000, 0xD4000,
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0xD8000, 0xDC000, 0xE0000, 0xE4000, 0xE8000, 0xEC000
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101dcfc5d787a24b9a20be3e18e3dbbab5280d35e38stephen hemmingerstatic const long cycx_2x_irq_options[]  = { 7, 3, 5, 9, 10, 11, 12, 15 };
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Kernel Loadable Module Entry Points */
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Module 'insert' entry point.
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o print announcement
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o initialize static data
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Return:	0	Ok
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		< 0	error.
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Context:	process */
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1127665a08928f241247afe8c76865cdbe4ef5489bfAdrian Bunkstatic int __init cycx_drv_init(void)
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
114d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("%s v%u.%u %s\n",
115d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		fullname, MOD_VERSION, MOD_RELEASE, copyright);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Module 'remove' entry point.
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o release all remaining system resources */
1227665a08928f241247afe8c76865cdbe4ef5489bfAdrian Bunkstatic void cycx_drv_cleanup(void)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Kernel APIs */
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Set up adapter.
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o detect adapter type
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o verify hardware configuration options
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o check for hardware conflicts
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o set up adapter shared memory
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o test adapter memory
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o load firmware
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Return:	0	ok.
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		< 0	error */
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cycx_setup);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cycx_setup(struct cycx_hw *hw, void *cfm, u32 len, unsigned long dpmbase)
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Verify IRQ configuration options */
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!get_option_index(cycx_2x_irq_options, hw->irq)) {
143d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("IRQ %d is invalid!\n", hw->irq);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Setup adapter dual-port memory window and test memory */
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dpmbase) {
149d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("you must specify the dpm address!\n");
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 		return -EINVAL;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (!get_option_index(cyc2x_dpmbase_options, dpmbase)) {
152d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("memory address 0x%lX is invalid!\n", dpmbase);
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hw->dpmbase = ioremap(dpmbase, CYCX_WINDOWSIZE);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hw->dpmsize = CYCX_WINDOWSIZE;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!detect_cyc2x(hw->dpmbase)) {
160d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("adapter Cyclom 2X not found at address 0x%lX!\n",
161d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		       dpmbase);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
165d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("found Cyclom 2X card at address 0x%lX\n", dpmbase);
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Load firmware. If loader fails then shut down adapter */
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = load_cyc2x(hw, cfm, len);
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cycx_down(hw);         /* shutdown adapter */
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cycx_down);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cycx_down(struct cycx_hw *hw)
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iounmap(hw->dpmbase);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Enable interrupt generation.  */
1847665a08928f241247afe8c76865cdbe4ef5489bfAdrian Bunkstatic void cycx_inten(struct cycx_hw *hw)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0, hw->dpmbase);
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Generate an interrupt to adapter's CPU. */
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cycx_intr);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid cycx_intr(struct cycx_hw *hw)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(0, hw->dpmbase + GEN_CYCX_INTR);
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Execute Adapter Command.
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o Set exec flag.
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o Busy-wait until flag is reset. */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cycx_exec);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cycx_exec(void __iomem *addr)
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 i = 0;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* wait till addr content is zeroed */
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (readw(addr)) {
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		udelay(1000);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (++i > 50)
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read absolute adapter memory.
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Transfer data from adapter's memory to data buffer. */
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cycx_peek);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cycx_peek(struct cycx_hw *hw, u32 addr, void *buf, u32 len)
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len == 1)
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*(u8*)buf = readb(hw->dpmbase + addr);
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(buf, hw->dpmbase + addr, len);
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Write Absolute Adapter Memory.
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Transfer data from data buffer to adapter's memory. */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cycx_poke);
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cycx_poke(struct cycx_hw *hw, u32 addr, void *buf, u32 len)
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len == 1)
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writeb(*(u8*)buf, hw->dpmbase + addr);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_toio(hw->dpmbase + addr, buf, len);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Hardware-Specific Functions */
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load Aux Routines */
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Reset board hardware.
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   return 1 if memory exists at addr and 0 if not. */
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int memory_exists(void __iomem *addr)
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int tries = 0;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (; tries < 3 ; tries++) {
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writew(TEST_PATTERN, addr + 0x10);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (readw(addr + 0x10) == TEST_PATTERN)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (readw(addr + 0x10) == TEST_PATTERN)
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return 1;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
257a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan		msleep_interruptible(1 * 1000);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load reset code. */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void reset_load(void __iomem *addr, u8 *buffer, u32 cnt)
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *pt_code = addr + RESET_OFFSET;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 i; /*, j; */
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0 ; i < cnt ; i++) {
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*		for (j = 0 ; j < 50 ; j++); Delay - FIXME busy waiting... */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writeb(*buffer++, pt_code++);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load buffer using boot interface.
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o copy data from buffer to Cyclom-X memory
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o wait for reset code to copy it to right portion of memory */
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int buffer_load(void __iomem *addr, u8 *buffer, u32 cnt)
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_toio(addr + DATA_OFFSET, buffer, cnt);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(GEN_BOOT_DAT, addr + CMD_OFFSET);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return wait_cyc(addr);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Set up entry point and kick start Cyclom-X CPU. */
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cycx_start(void __iomem *addr)
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* put in 0x30 offset the jump instruction to the code entry point */
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0xea, addr + 0x30);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0x00, addr + 0x31);
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0xc4, addr + 0x32);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0x00, addr + 0x33);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0x00, addr + 0x34);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* cmd to start executing code */
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(GEN_START, addr + CMD_OFFSET);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load and boot reset code. */
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cycx_reset_boot(void __iomem *addr, u8 *code, u32 len)
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *pt_start = addr + START_OFFSET;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0xea, pt_start++); /* jmp to f000:3f00 */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0x00, pt_start++);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0xfc, pt_start++);
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0x00, pt_start++);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0xf0, pt_start);
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	reset_load(addr, code, len);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* 80186 was in hold, go */
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0, addr + START_CPU);
314a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan	msleep_interruptible(1 * 1000);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load data.bin file through boot (reset) interface. */
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cycx_data_boot(void __iomem *addr, u8 *code, u32 len)
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *pt_boot_cmd = addr + CMD_OFFSET;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 i;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
323efad798b9f01300565f65058b153250cc49d58f2Paulius Zaleckas	/* boot buffer length */
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(GEN_DEFPAR, pt_boot_cmd);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_cyc(addr) < 0)
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(0, pt_boot_cmd + sizeof(u16));
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(0x4000, pt_boot_cmd + 2 * sizeof(u16));
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(GEN_SET_SEG, pt_boot_cmd);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_cyc(addr) < 0)
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer_load(addr, code + i,
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				min_t(u32, CFM_LOAD_BUFSZ, (len - i))) < 0) {
340d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches			pr_err("Error !!\n");
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load code.bin file through boot (reset) interface. */
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cycx_code_boot(void __iomem *addr, u8 *code, u32 len)
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *pt_boot_cmd = addr + CMD_OFFSET;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 i;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
354efad798b9f01300565f65058b153250cc49d58f2Paulius Zaleckas	/* boot buffer length */
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(GEN_DEFPAR, pt_boot_cmd);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_cyc(addr) < 0)
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(0x0000, pt_boot_cmd + sizeof(u16));
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(0xc400, pt_boot_cmd + 2 * sizeof(u16));
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(GEN_SET_SEG, pt_boot_cmd);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_cyc(addr) < 0)
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buffer_load(addr, code + i,
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				min_t(u32, CFM_LOAD_BUFSZ, (len - i)))) {
371d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches			pr_err("Error !!\n");
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Load adapter from the memory image of the CYCX firmware module.
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o verify firmware integrity and compatibility
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o start adapter up */
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len)
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cycx_fw_header *img_hdr;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 *reset_image,
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   *data_image,
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   *code_image;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *pt_cycld = hw->dpmbase + 0x400;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 cksum;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Announce */
392d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("firmware signature=\"%s\"\n", cfm->signature);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Verify firmware signature */
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (strcmp(cfm->signature, CFM_SIGNATURE)) {
396d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("load_cyc2x: not Cyclom-2X firmware!\n");
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
400d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("firmware version=%u\n", cfm->version);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Verify firmware module format version */
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cfm->version != CFM_VERSION) {
404d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("%s: firmware format %u rejected! Expecting %u.\n",
405d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		       __func__, cfm->version, CFM_VERSION);
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Verify firmware module length and checksum */
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cksum = checksum((u8*)&cfm->info, sizeof(struct cycx_fw_info) +
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  cfm->info.codesize);
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	FIXME cfm->info.codesize is off by 2
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (((len - sizeof(struct cycx_firmware) - 1) != cfm->info.codesize) ||
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cksum != cfm->checksum) {
417d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("%s: firmware corrupted!\n", __func__);
418d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err(" cdsize = 0x%x (expected 0x%lx)\n",
419d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		       len - (int)sizeof(struct cycx_firmware) - 1,
420d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		       cfm->info.codesize);
421d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err(" chksum = 0x%x (expected 0x%x)\n",
422d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		       cksum, cfm->checksum);
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If everything is ok, set reset, data and code pointers */
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	img_hdr = (struct cycx_fw_header *)&cfm->image;
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef FIRMWARE_DEBUG
429d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("%s: image sizes\n", __func__);
430d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info(" reset=%lu\n", img_hdr->reset_size);
431d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("  data=%lu\n", img_hdr->data_size);
432d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("  code=%lu\n", img_hdr->code_size);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	reset_image = ((u8 *)img_hdr) + sizeof(struct cycx_fw_header);
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data_image = reset_image + img_hdr->reset_size;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	code_image = data_image + img_hdr->data_size;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*---- Start load ----*/
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Announce */
440d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("loading firmware %s (ID=%u)...\n",
441d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		cfm->descr[0] ? cfm->descr : "unknown firmware",
442d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		cfm->info.codeid);
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0 ; i < 5 ; i++) {
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Reset Cyclom hardware */
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!reset_cyc2x(hw->dpmbase)) {
447d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches			pr_err("dpm problem or board not found\n");
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Load reset.bin */
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size);
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* reset is waiting for boot */
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writew(GEN_POWER_ON, pt_cycld);
455a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan		msleep_interruptible(1 * 1000);
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (j = 0 ; j < 3 ; j++)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!readw(pt_cycld))
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto reset_loaded;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
461a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan				msleep_interruptible(1 * 1000);
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
464d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_err("reset not started\n");
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsreset_loaded:
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Load data.bin */
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cycx_data_boot(hw->dpmbase, data_image, img_hdr->data_size)) {
470d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("cannot load data file\n");
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Load code.bin */
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cycx_code_boot(hw->dpmbase, code_image, img_hdr->code_size)) {
476d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches		pr_err("cannot load code file\n");
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Prepare boot-time configuration data */
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cycx_bootcfg(hw);
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* kick-off CPU */
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cycx_start(hw->dpmbase);
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Arthur Ganzert's tip: wait a while after the firmware loading...
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   seg abr 26 17:17:12 EST 1999 - acme */
488a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan	msleep_interruptible(7 * 1000);
489d6810e1375c9b3b28ed1caccb6fb706945903f49Joe Perches	pr_info("firmware loaded!\n");
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* enable interrupts */
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cycx_inten(hw);
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Prepare boot-time firmware configuration data.
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * o initialize configuration data area
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   From async.doc - V_3.4.0 - 07/18/1994
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - As of now, only static buffers are available to the user.
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     So, the bit VD_RXDIRC must be set in 'valid'. That means that user
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     wants to use the static transmission and reception buffers. */
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cycx_bootcfg(struct cycx_hw *hw)
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* use fixed buffers */
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Detect Cyclom 2x adapter.
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Following tests are used to detect Cyclom 2x adapter:
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *       to be completed based on the tests done below
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Return 1 if detected o.k. or 0 if failed.
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Note:	This test is destructive! Adapter will be left in shutdown
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		state after the test. */
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int detect_cyc2x(void __iomem *addr)
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	reset_cyc2x(addr);
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return memory_exists(addr);
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Miscellaneous */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Get option's index into the options list.
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Return option's index (1 .. N) or zero if option is invalid. */
525dcfc5d787a24b9a20be3e18e3dbbab5280d35e38stephen hemmingerstatic int get_option_index(const long *optlist, long optval)
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i = 1;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (; i <= optlist[0]; ++i)
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (optlist[i] == optval)
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return i;
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Reset adapter's CPU. */
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int reset_cyc2x(void __iomem *addr)
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0, addr + RST_ENABLE);
540a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan	msleep_interruptible(2 * 1000);
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(0, addr + RST_DISABLE);
542a51d74409d856e472bad753aecf1f2715718c242Nishanth Aravamudan	msleep_interruptible(2 * 1000);
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return memory_exists(addr);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Calculate 16-bit CRC using CCITT polynomial. */
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 checksum(u8 *buf, u32 len)
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 crc = 0;
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 mask, flag;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (; len; --len, ++buf)
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (mask = 0x80; mask; mask >>= 1) {
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			flag = (crc & 0x8000);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			crc <<= 1;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			crc |= ((*buf & mask) ? 1 : 0);
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (flag)
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				crc ^= 0x1021;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return crc;
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(cycx_drv_init);
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cycx_drv_cleanup);
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* End */
570