atmel_cs.c 18.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/*** -*- linux-c -*- **********************************************************

     Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.

        Copyright 2000-2001 ATMEL Corporation.
        Copyright 2003 Simon Kelley.

8 9 10 11
    This code was developed from version 2.1.1 of the Atmel drivers,
    released by Atmel corp. under the GPL in December 2002. It also
    includes code from the Linux aironet drivers (C) Benjamin Reed,
    and the Linux PCMCIA package, (C) David Hinds.
L
Linus Torvalds 已提交
12

13
    For all queries about this code, please contact the current author,
L
Linus Torvalds 已提交
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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
    Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Atmel wireless lan drivers; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

******************************************************************************/

#ifdef __IN_PCMCIA_PACKAGE__
#include <pcmcia/k_compat.h>
#endif
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/moduleparam.h>
#include <linux/device.h>

#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <pcmcia/ciscode.h>

#include <asm/io.h>
#include <asm/system.h>
#include <linux/wireless.h>

#include "atmel.h"

/*
   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
   you do not define PCMCIA_DEBUG at all, all the debug code will be
   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
   be present but disabled -- but it can then be enabled for specific
   modules at load time with a 'pc_debug=#' option to insmod.
*/
65

L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
module_param(pc_debug, int, 0);
static char *version = "$Revision: 1.2 $";
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
#else
#define DEBUG(n, args...)
#endif

/*====================================================================*/

MODULE_AUTHOR("Simon Kelley");
MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards");

/*====================================================================*/

/*
   The event() function is this driver's Card Services event handler.
   It will be called by Card Services when an appropriate card status
   event is received.  The config() and release() entry points are
   used to configure or release a socket, in response to card
   insertion and ejection events.  They are invoked from the atmel_cs
90
   event handler.
L
Linus Torvalds 已提交
91 92
*/

93
static int atmel_config(struct pcmcia_device *link);
94
static void atmel_release(struct pcmcia_device *link);
L
Linus Torvalds 已提交
95 96 97 98 99 100 101

/*
   The attach() and detach() entry points are used to create and destroy
   "instances" of the driver, where each instance represents everything
   needed to manage one actual PCMCIA card.
*/

102
static void atmel_detach(struct pcmcia_device *p_dev);
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110 111 112 113

/*
   You'll also need to prototype all the functions that will actually
   be used to talk to your device.  See 'pcmem_cs' for a good example
   of a fully self-sufficient driver; the other drivers rely more or
   less on other parts of the kernel.
*/

/*
   A linked list of "instances" of the  atmelnet device.  Each actual
   PCMCIA card corresponds to one device instance, and is described
114
   by one struct pcmcia_device structure (defined in ds.h).
L
Linus Torvalds 已提交
115 116

   You may not want to use a linked list for this -- for example, the
117
   memory card driver uses an array of struct pcmcia_device pointers, where minor
L
Linus Torvalds 已提交
118 119 120 121 122 123 124 125 126
   device numbers are used to derive the corresponding array index.
*/

/*
   A driver needs to provide a dev_node_t structure for each device
   on a card.  In some cases, there is only one device per card (for
   example, ethernet cards, modems).  In other cases, there may be
   many actual or logical devices (SCSI adapters, memory cards with
   multiple partitions).  The dev_node_t structures need to be kept
127
   in a linked list starting at the 'dev' field of a struct pcmcia_device
L
Linus Torvalds 已提交
128 129 130 131 132 133 134 135
   structure.  We allocate them in the card's private data structure,
   because they generally shouldn't be allocated dynamically.

   In this case, we also provide a flag to indicate if a device is
   "stopped" due to a power management event, or card ejection.  The
   device IO routines can use a flag like this to throttle IO to a
   card that is not ready to accept it.
*/
136

L
Linus Torvalds 已提交
137 138 139 140 141 142
typedef struct local_info_t {
	dev_node_t	node;
	struct net_device *eth_dev;
} local_info_t;

/*======================================================================
143

L
Linus Torvalds 已提交
144 145 146
  atmel_attach() creates an "instance" of the driver, allocating
  local data structures for one device.  The device is registered
  with Card Services.
147

L
Linus Torvalds 已提交
148 149 150
  The dev_link structure is initialized, but we don't actually
  configure the card at this point -- we wait until we receive a
  card insertion event.
151

L
Linus Torvalds 已提交
152 153
  ======================================================================*/

154
static int atmel_probe(struct pcmcia_device *p_dev)
L
Linus Torvalds 已提交
155 156
{
	local_info_t *local;
157

L
Linus Torvalds 已提交
158 159 160
	DEBUG(0, "atmel_attach()\n");

	/* Interrupt setup */
A
Alan Cox 已提交
161
	p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
162 163
	p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
	p_dev->irq.Handler = NULL;
164

L
Linus Torvalds 已提交
165 166 167 168 169 170 171
	/*
	  General socket configuration defaults can go here.  In this
	  client, we assume very little, and rely on the CIS for almost
	  everything.  In most clients, many details (i.e., number, sizes,
	  and attributes of IO windows) are fixed by the nature of the
	  device, and can be hard-wired here.
	*/
172 173
	p_dev->conf.Attributes = 0;
	p_dev->conf.IntType = INT_MEMORY_AND_IO;
174

L
Linus Torvalds 已提交
175
	/* Allocate space for private device-specific data */
176
	local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
L
Linus Torvalds 已提交
177 178
	if (!local) {
		printk(KERN_ERR "atmel_cs: no memory for new device\n");
179
		return -ENOMEM;
L
Linus Torvalds 已提交
180
	}
181
	p_dev->priv = local;
182

183
	return atmel_config(p_dev);
L
Linus Torvalds 已提交
184 185 186
} /* atmel_attach */

/*======================================================================
187

L
Linus Torvalds 已提交
188 189 190 191
  This deletes a driver "instance".  The device is de-registered
  with Card Services.  If it has been released, all local data
  structures are freed.  Otherwise, the structures will be freed
  when the device is released.
192

L
Linus Torvalds 已提交
193 194
  ======================================================================*/

195
static void atmel_detach(struct pcmcia_device *link)
L
Linus Torvalds 已提交
196 197 198
{
	DEBUG(0, "atmel_detach(0x%p)\n", link);

199
	atmel_release(link);
200

201
	kfree(link->priv);
L
Linus Torvalds 已提交
202 203 204
}

/*======================================================================
205

L
Linus Torvalds 已提交
206 207 208
  atmel_config() is scheduled to run after a CARD_INSERTION event
  is received, to configure the PCMCIA socket, and to make the
  device available to the system.
209

L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217
  ======================================================================*/

#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)

/* Call-back function to interrogate PCMCIA-specific information
   about the current existance of the card */
static int card_present(void *arg)
218
{
219
	struct pcmcia_device *link = (struct pcmcia_device *)arg;
220 221

	if (pcmcia_dev_present(link))
L
Linus Torvalds 已提交
222
		return 1;
223

L
Linus Torvalds 已提交
224 225 226
	return 0;
}

227
static int atmel_config(struct pcmcia_device *link)
L
Linus Torvalds 已提交
228 229 230 231 232 233
{
	tuple_t tuple;
	cisparse_t parse;
	local_info_t *dev;
	int last_fn, last_ret;
	u_char buf[64];
234 235
	struct pcmcia_device_id *did;

L
Linus Torvalds 已提交
236
	dev = link->priv;
237
	did = handle_to_dev(link).driver_data;
L
Linus Torvalds 已提交
238 239

	DEBUG(0, "atmel_config(0x%p)\n", link);
240

L
Linus Torvalds 已提交
241 242 243 244
	tuple.Attributes = 0;
	tuple.TupleData = buf;
	tuple.TupleDataMax = sizeof(buf);
	tuple.TupleOffset = 0;
245

L
Linus Torvalds 已提交
246 247 248 249
	/*
	  In this loop, we scan the CIS for configuration table entries,
	  each of which describes a valid card configuration, including
	  voltage, IO window, memory window, and interrupt settings.
250

L
Linus Torvalds 已提交
251 252 253 254 255 256 257 258
	  We make no assumptions about the card to be configured: we use
	  just the information available in the CIS.  In an ideal world,
	  this would work for any PCMCIA card, but it requires a complete
	  and accurate CIS.  In practice, a driver usually "knows" most of
	  these things without consulting the CIS, and most client drivers
	  will only use the CIS to fill in implementation-defined details.
	*/
	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
259
	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
L
Linus Torvalds 已提交
260 261 262
	while (1) {
		cistpl_cftable_entry_t dflt = { 0 };
		cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
263 264
		if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
				pcmcia_parse_tuple(link, &tuple, &parse) != 0)
L
Linus Torvalds 已提交
265
			goto next_entry;
266

L
Linus Torvalds 已提交
267 268 269
		if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
		if (cfg->index == 0) goto next_entry;
		link->conf.ConfigIndex = cfg->index;
270

L
Linus Torvalds 已提交
271 272 273 274 275
		/* Does this card need audio output? */
		if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
			link->conf.Attributes |= CONF_ENABLE_SPKR;
			link->conf.Status = CCSR_AUDIO_ENA;
		}
276

L
Linus Torvalds 已提交
277 278 279
		/* Use power settings for Vcc and Vpp if present */
		/*  Note that the CIS values need to be rescaled */
		if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
280
			link->conf.Vpp =
L
Linus Torvalds 已提交
281 282
				cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
		else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
283
			link->conf.Vpp =
L
Linus Torvalds 已提交
284
				dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
285

L
Linus Torvalds 已提交
286 287 288
		/* Do we need to allocate an interrupt? */
		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
			link->conf.Attributes |= CONF_ENABLE_IRQ;
289

L
Linus Torvalds 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
		/* IO window settings */
		link->io.NumPorts1 = link->io.NumPorts2 = 0;
		if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
			cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
			link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
			if (!(io->flags & CISTPL_IO_8BIT))
				link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
			if (!(io->flags & CISTPL_IO_16BIT))
				link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
			link->io.BasePort1 = io->win[0].base;
			link->io.NumPorts1 = io->win[0].len;
			if (io->nwin > 1) {
				link->io.Attributes2 = link->io.Attributes1;
				link->io.BasePort2 = io->win[1].base;
				link->io.NumPorts2 = io->win[1].len;
			}
		}
307

L
Linus Torvalds 已提交
308
		/* This reserves IO space but doesn't actually enable it */
309
		if (pcmcia_request_io(link, &link->io) != 0)
L
Linus Torvalds 已提交
310 311 312 313
			goto next_entry;

		/* If we got this far, we're cool! */
		break;
314

L
Linus Torvalds 已提交
315
	next_entry:
316
		CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
L
Linus Torvalds 已提交
317
	}
318

L
Linus Torvalds 已提交
319 320 321 322 323 324
	/*
	  Allocate an interrupt line.  Note that this does not assign a
	  handler to the interrupt, unless the 'Handler' member of the
	  irq structure is initialized.
	*/
	if (link->conf.Attributes & CONF_ENABLE_IRQ)
325
		CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
326

L
Linus Torvalds 已提交
327 328 329 330 331
	/*
	  This actually configures the PCMCIA socket -- setting up
	  the I/O windows and the interrupt mapping, and putting the
	  card and host interface into "Memory and IO" mode.
	*/
332
	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
333

L
Linus Torvalds 已提交
334
	if (link->irq.AssignedIRQ == 0) {
335
		printk(KERN_ALERT
L
Linus Torvalds 已提交
336 337 338
		       "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config.");
		goto cs_failed;
	}
339 340

	((local_info_t*)link->priv)->eth_dev =
L
Linus Torvalds 已提交
341 342
		init_atmel_card(link->irq.AssignedIRQ,
				link->io.BasePort1,
343
				did ? did->driver_info : ATMEL_FW_TYPE_NONE,
344
				&handle_to_dev(link),
345
				card_present,
L
Linus Torvalds 已提交
346
				link);
347
	if (!((local_info_t*)link->priv)->eth_dev)
348
			goto cs_failed;
349 350


L
Linus Torvalds 已提交
351 352
	/*
	  At this point, the dev_node_t structure(s) need to be
353
	  initialized and arranged in a linked list at link->dev_node.
L
Linus Torvalds 已提交
354 355 356
	*/
	strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name );
	dev->node.major = dev->node.minor = 0;
357
	link->dev_node = &dev->node;
358

359
	return 0;
360

L
Linus Torvalds 已提交
361
 cs_failed:
362
	cs_error(link, last_fn, last_ret);
L
Linus Torvalds 已提交
363
	atmel_release(link);
364
	return -ENODEV;
L
Linus Torvalds 已提交
365 366 367
}

/*======================================================================
368

L
Linus Torvalds 已提交
369 370 371
  After a card is removed, atmel_release() will unregister the
  device, and release the PCMCIA configuration.  If the device is
  still open, this will be postponed until it is closed.
372

L
Linus Torvalds 已提交
373 374
  ======================================================================*/

375
static void atmel_release(struct pcmcia_device *link)
L
Linus Torvalds 已提交
376 377
{
	struct net_device *dev = ((local_info_t*)link->priv)->eth_dev;
378

L
Linus Torvalds 已提交
379
	DEBUG(0, "atmel_release(0x%p)\n", link);
380 381

	if (dev)
382
		stop_atmel_card(dev);
383 384
	((local_info_t*)link->priv)->eth_dev = NULL;

385
	pcmcia_disable_device(link);
L
Linus Torvalds 已提交
386 387
}

388
static int atmel_suspend(struct pcmcia_device *link)
389 390 391
{
	local_info_t *local = link->priv;

392
	netif_device_detach(local->eth_dev);
393 394 395 396

	return 0;
}

397
static int atmel_resume(struct pcmcia_device *link)
398 399 400
{
	local_info_t *local = link->priv;

401 402
	atmel_open(local->eth_dev);
	netif_device_attach(local->eth_dev);
403 404 405 406

	return 0;
}

L
Linus Torvalds 已提交
407
/*====================================================================*/
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
/* We use the driver_info field to store the correct firmware type for a card. */

#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
			PCMCIA_DEV_ID_MATCH_CARD_ID, \
	.manf_id = (manf), \
	.card_id = (card), \
        .driver_info = (kernel_ulong_t)(info), }

#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2, \
	.prod_id = { (v1), (v2), NULL, NULL }, \
	.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
        .driver_info = (kernel_ulong_t)(info), }

424
static struct pcmcia_device_id atmel_ids[] = {
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
	PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM),
	PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM),
	PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E),
	PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502),
	PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E),
	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502),
	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D),
	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E),
	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958),
	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504),
	PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958),
	PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502),
	PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E),
	PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502),
	PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958),
	PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502),
	PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D),
	PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502),
	PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D),
	PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D),
	PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E),
446 447
	PCMCIA_DEVICE_NULL
};
448

449 450
MODULE_DEVICE_TABLE(pcmcia, atmel_ids);

L
Linus Torvalds 已提交
451
static struct pcmcia_driver atmel_driver = {
452 453 454
	.owner		= THIS_MODULE,
	.drv		= {
		.name	= "atmel_cs",
L
Linus Torvalds 已提交
455
        },
456
	.probe          = atmel_probe,
457
	.remove		= atmel_detach,
458
	.id_table	= atmel_ids,
459 460
	.suspend	= atmel_suspend,
	.resume		= atmel_resume,
L
Linus Torvalds 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
};

static int atmel_cs_init(void)
{
        return pcmcia_register_driver(&atmel_driver);
}

static void atmel_cs_cleanup(void)
{
        pcmcia_unregister_driver(&atmel_driver);
}

/*
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    In addition:

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.
    3. The name of the author may not be used to endorse or promote
       products derived from this software without specific prior written
       permission.

    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
509
    POSSIBILITY OF SUCH DAMAGE.
L
Linus Torvalds 已提交
510 511 512 513
*/

module_init(atmel_cs_init);
module_exit(atmel_cs_cleanup);