`
`1 /*======================================================================
`2
`3 A simple MTD for Intel Series 2+ Flash devices
`4
`5 iflash2+_mtd.c 1.66 2000/07/24 20:41:31
`6
`7 The contents of this file are subject to the Mozilla Public
`8 License Version 1.1 (the "License"); you may not use this file
`9 except in compliance with the License. You may obtain a copy of
`10 the License at http://www.mozilla.org/MPL/
`11
`12 Software distributed under the License is distributed on an "AS
`13 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
`14 implied. See the License for the specific language governing
`15 rights and limitations under the License.
`16
`17 The initial developer of the original code is David A. Hinds
`18 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
`19 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
`20
`21 Alternatively, the contents of this file may be used under the
`22 terms of the GNU Public License version 2 (the "GPL"), in which
`23 case the provisions of the GPL are applicable instead of the
`24 above. If you wish to allow the use of your version of this file
`25 only under the terms of the GPL and not to allow others to use
`26 your version of this file under the MPL, indicate your decision
`27 by deleting the provisions above and replace them with the notice
`28 and other provisions required by the GPL. If you do not delete
`29 the provisions above, a recipient may use your version of this
`30 file under either the MPL or the GPL.
`31
`32 For efficiency and simplicity, this driver is very block oriented.
`33 Reads and writes must not span erase block boundaries. Erases
`34 are limited to one erase block per request. This makes it much
`35 easier to manage multiple asynchronous erases efficiently.
`36
`37 ======================================================================*/
`38
`39 #include <pcmcia/config.h>
`40 #include <pcmcia/k_compat.h>
`41
`42 #ifdef __LINUX__
`43 #include <linux/kernel.h>
`44 #include <linux/module.h>
`45 #include <linux/init.h>
`46 #include <linux/sched.h>
`47 #include <linux/ptrace.h>
`48 #include <linux/malloc.h>
`49 #include <linux/string.h>
`50 #include <linux/timer.h>
`51 #include <linux/timex.h>
`52 #include <linux/major.h>
`53 #include <linux/fs.h>
`
`-1-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0001
`
`
`
`iflash2+_mtd.c
`
`54 #include <linux/delay.h>
`55 #include <asm/io.h>
`56 #include <asm/system.h>
`57 #include <asm/segment.h>
`58 #endif
`59
`60 #include <stdarg.h>
`61
`62 #include <pcmcia/version.h>
`63 #include <pcmcia/cs_types.h>
`64 #include <pcmcia/cs.h>
`65 #include <pcmcia/bulkmem.h>
`66 #include <pcmcia/cistpl.h>
`67 #include <pcmcia/ds.h>
`68 #include <pcmcia/mem_op.h>
`69 #include "iflash.h"
`70
`71 #ifdef PCMCIA_DEBUG
`72 static int pc_debug = PCMCIA_DEBUG;
`73 MODULE_PARM(pc_debug, "i");
`74 #define DEBUG(n, args...) do { if (pc_debug>(n)) printk(KERN_INFO args); } while (0)
`75 static char *version =
`76 "iflash2+_mtd.c 1.66 2000/07/24 20:41:31 (David Hinds)";
`77 #else
`78 #define DEBUG(n, args...) do { } while (0)
`79 #endif
`80
`81 /*====================================================================*/
`82
`83 /* Parameters that can be set with 'insmod' */
`84
`85 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
`86
`87 INT_MODULE_PARM(word_width, 1);
`/* 1 = 16-bit */
`88 INT_MODULE_PARM(mem_speed, 0);
`/* in ns */
`89 INT_MODULE_PARM(vpp_timeout_period, 1000);
`/* in ms */
`90 INT_MODULE_PARM(vpp_settle, 100);
`/* in ms */
`91 INT_MODULE_PARM(write_timeout, 100);
`/* in ms */
`92 INT_MODULE_PARM(erase_timeout, 100);
`/* in ms */
`93 INT_MODULE_PARM(erase_limit, 10000);
`/* in ms */
`94 INT_MODULE_PARM(retry_limit, 8);
`/* write retries */
`95 INT_MODULE_PARM(max_tries, 4096);
`/* status polling */
`96 INT_MODULE_PARM(do_sleep, 1);
`/* spin vs sleep? */
`97
`98 /*====================================================================*/
`99
`100 static void flash_config(dev_link_t *link);
`101 static void flash_release(u_long arg);
`102 static int flash_event(event_t event, int priority,
`event_callback_args_t *args);
`103
`104
`105 static dev_link_t *flash_attach(void);
`106 static void flash_detach(dev_link_t *);
`
`-2-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0002
`
`
`
`32
`
`iflash2+_mtd.c
`
`107
`108 #define MAX_CELLS
`109
`110 /* A flash region is composed of one or more "cells", where we allow
`111 simultaneous erases if they are in different cells */
`112 typedef struct flash_region_t {
`region;
`113
`region_info_t
`cell_size;
`114
`u_int
`struct flash_cell_t {
`115
`state;
`116
`u_int
`erase_time;
`117
`k_time_t
`erase_addr;
`118
`u_int
`erase_retries;
`119
`u_int
`} cell[MAX_CELLS];
`120
`121 } flash_region_t;
`122
`123 typedef struct flash_dev_t {
`link;
`124
`dev_link_t
`Base;
`125
`caddr_t
`Size;
`126
`u_int
`window_handle_t ESRwin;
`127
`ESRbase;
`128
`caddr_t
`vpp_usage;
`129
`int
`vpp_start;
`130
`k_time_t
`vpp_timeout;
`131
`struct timer_list
`*flash[2*CISTPL_MAX_DEVICES];
`132
`flash_region_t
`133 } flash_dev_t;
`134
`0x01
`135 #define FLASH_PENDING
`0x02
`136 #define FLASH_ERASING
`137 #define FLASH_ERASE_SUSPEND 0x04
`138
`139 static dev_info_t dev_info = "iflash2+_mtd";
`140
`141 static dev_link_t *dev_list = NULL;
`142
`143 /*====================================================================*/
`144
`145 static void cs_error(client_handle_t handle, int func, int ret)
`146 {
`147
`148
`149 }
`150
`151 #ifdef BENCHMARK
`152 static inline k_time_t uticks(void)
`153 {
`154
`155
`156
`157
`158
`159
`
`k_time_t count;
`outb_p(0x00, 0x43);
`count = inb_p(0x40);
`count |= inb(0x40) << 8;
`count = ((LATCH-1) - count) * 10000;
`count = (count + LATCH/2) / LATCH;
`
`error_info_t err = { func, ret };
`CardServices(ReportError, handle, &err);
`
`-3-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0003
`
`
`
`count += jiffies * 10000;
`return count;
`
`iflash2+_mtd.c
`
`if (do_sleep)
`wsleeptimeout(queue, 1);
`else
`udelay(50);
`
`160
`161
`162 }
`163 #endif
`164
`165 /*======================================================================
`166
`167 Low level routines for programming the flash card.
`168
`169 ======================================================================*/
`170
`171 static void sleep_or_spin(wait_queue_head_t *queue)
`172 {
`173
`174
`175
`176
`177 }
`178
`179 static void log_esr(char *s, volatile u_short *esr)
`180 {
`181
`182
`183
`184
`185
`186
`187
`188
`189
`190 }
`191
`192 static void abort_cmd(volatile u_short *esr)
`193 {
`194
`195
`196
`197
`198
`199
`200
`201
`202
`203
`204 }
`205
`206 static int set_rdy_mode(volatile u_short *esr, u_short mode)
`207 {
`208
`209
`210
`211
`212
`
`writew(IF_ABORT, esr);
`writew(IF_READ_ESR, esr);
`for (i = 0; i < max_tries; i++)
`if ((readw(esr+4) & GSR_SLEEP) == GSR_SLEEP) break;
`if (i == max_tries)
`printk(KERN_NOTICE "iflash2+_mtd: abort cmd failed!\n");
`writew(IF_READ_ARRAY, esr);
`writew(IF_CLEAR_CSR, esr);
`
`u_short CSR, GSR, BSR;
`
`writew(IF_READ_CSR, esr);
`CSR = readw(esr);
`writew(IF_READ_ESR, esr);
`BSR = readw(esr+2);
`GSR = readw(esr+4);
`printk("%sCSR = 0x%04x, BSR = 0x%04x, GSR = 0x%04x\n",
`(s ? s : KERN_NOTICE), CSR, BSR, GSR);
`
`u_short i;
`
`u_short i, j;
`
`DEBUG(3, "iflash2+_mtd: set_rdy_mode(%04x)\n", mode);
`for (j = 0; j < retry_limit; j++) {
`writew(IF_READ_ESR, esr);
`
`-4-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0004
`
`
`
`iflash2+_mtd.c
`
`for (i = 0; i < max_tries; i++)
`213
`if (!(readw(esr+4) & GSR_QUEUE_FULL)) break;
`214
`if (i == max_tries)
`215
`goto failed;
`216
`writew(IF_RDY_MODE, esr);
`217
`writew(mode, esr);
`218
`writew(IF_READ_ESR, esr);
`219
`for (i = 0; i < max_tries; i++)
`220
`if ((readw(esr+4) & GSR_WR_READY) == GSR_WR_READY) break;
`221
`if (i == max_tries)
`222
`goto failed;
`223
`if (!(readw(esr+4) & GSR_OP_ERR))
`224
`return CS_SUCCESS;
`225
`writew(IF_READ_ARRAY, esr);
`226
`writew(IF_CLEAR_CSR, esr);
`227
`}
`228
`229 failed:
`printk(KERN_NOTICE "iflash2+_mtd: set_rdy_mode failed!\n");
`230
`log_esr(NULL, esr);
`231
`return CS_GENERAL_FAILURE;
`232
`233 }
`234
`235 static int check_write(wait_queue_head_t *queue, volatile u_short *esr)
`236 {
`237
`238
`239
`240
`241
`242
`243
`244
`245
`246
`247
`248
`249
`250
`251
`252 }
`253
`254 static int page_setup(wait_queue_head_t *queue, volatile u_short *esr,
`volatile u_short *address, u_short count)
`255
`256 {
`257
`258
`259
`260
`261
`262
`263
`264
`265
`
`u_long end = jiffies + write_timeout;
`writew(IF_READ_ESR, esr);
`while (((readw(esr+2) & BSR_READY) != BSR_READY) &&
`(jiffies < end))
`sleep_or_spin(queue);
`if ((readw(esr+2) & BSR_READY) != BSR_READY) {
`printk(KERN_NOTICE "iflash2+_mtd: check_write: timed out!\n");
`log_esr(NULL, esr);
`return CS_GENERAL_FAILURE;
`}
`if (readw(esr+2) & BSR_FAILED) {
`log_esr(KERN_DEBUG "write error: ", esr);
`return CS_WRITE_FAILURE;
`} else
`return CS_SUCCESS;
`
`u_long end = jiffies + write_timeout;
`u_short nw;
`writew(IF_READ_ESR, esr);
`while (((readw(esr+4) & GSR_PAGE_AVAIL) != GSR_PAGE_AVAIL) &&
`(jiffies < end))
`sleep_or_spin(queue);
`if ((readw(esr+4) & GSR_PAGE_AVAIL) != GSR_PAGE_AVAIL) {
`printk(KERN_NOTICE "iflash2+_mtd: page_setup timed out\n");
`log_esr(NULL, esr);
`
`-5-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0005
`
`
`
`iflash2+_mtd.c
`
`return CS_GENERAL_FAILURE;
`}
`
`nw = count >> 1;
`if (nw == 0) {
`/* Special case of single byte write */
`writeb(LOW(IF_SINGLE_LOAD), address);
`} else {
`writew(IF_SEQ_LOAD, address);
`/* Tricky: split count into low bytes and high bytes */
`nw = (count-nw-1) | ((nw-1) << 8);
`if ((u_long)address & 2) {
`writew(0, address);
`writew(nw, address);
`} else {
`writew(nw, address);
`writew(0, address);
`
`}
`}
`return CS_SUCCESS;
`
`u_short nw;
`u_short i;
`
`266
`267
`268
`269
`270
`271
`272
`273
`274
`275
`276
`277
`278
`279
`280
`281
`282
`283
`284
`285
`286 }
`287
`288 static int page_write(volatile u_short *esr, volatile u_short *address,
`u_short count)
`289
`290 {
`291
`292
`293
`294
`295
`296
`297
`298
`299
`300
`301
`302
`303
`304
`305
`306
`307
`308
`309
`310
`311
`312
`313
`314
`315
`316
`317
`318
`
`writew(IF_READ_ESR, esr);
`for (i = 0; i < max_tries; i++)
`if (!(readw(esr+2) & BSR_QUEUE_FULL)) break;
`if (i == max_tries) {
`printk(KERN_NOTICE "iflash2+_mtd: page_write timed out\n");
`log_esr(NULL, esr);
`return CS_GENERAL_FAILURE;
`}
`nw = count >> 1;
`if (nw == 0) {
`writeb(LOW(IF_PAGE_WRITE), address);
`writeb(0, address);
`writeb(0, address);
`} else {
`writew(IF_PAGE_WRITE, address);
`/* Tricky: split count into low bytes and high bytes */
`nw = (count-nw-1) | ((nw-1) << 8);
`if ((u_long)address & 2) {
`writew(0, address);
`writew(nw, address);
`} else {
`writew(nw, address);
`writew(0, address);
`
`}
`}
`
`-6-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0006
`
`
`
`return CS_SUCCESS;
`
`iflash2+_mtd.c
`
`u_short CSR;
`writew(IF_READ_CSR, address);
`CSR = readw(address);
`if ((CSR & CSR_WR_READY) != CSR_WR_READY) {
`return CS_BUSY;
`} else if (CSR & (CSR_ERA_ERR | CSR_VPP_LOW | CSR_WR_ERR)) {
`log_esr(KERN_NOTICE "erase error: ", address);
`return CS_WRITE_FAILURE;
`} else
`return CS_SUCCESS;
`
`writew(IF_BLOCK_ERASE, address);
`writew(IF_CONFIRM, address);
`
`319
`320 }
`321
`322 static void block_erase(volatile u_short *address)
`323 {
`324
`325
`326 }
`327
`328 static int check_erase(volatile u_short *address)
`329 {
`330
`331
`332
`333
`334
`335
`336
`337
`338
`339
`340 }
`341
`342 static int suspend_erase(volatile u_short *esr)
`343 {
`344
`345
`346
`347
`348
`349
`350
`351
`352
`353
`354
`355
`356
`357
`358 }
`359
`360 static void resume_erase(volatile u_short *esr)
`361 {
`362
`363
`364
`365
`366
`367 }
`368
`369 static void reset_block(volatile u_short *esr)
`370 {
`371
`
`u_short i;
`
`DEBUG(1, "iflash2+_mtd: suspending erase...\n");
`writew(IF_ERASE_SUSPEND, esr);
`writew(IF_READ_ESR, esr);
`for (i = 0; i < max_tries; i++)
`if ((readw(esr+4) & GSR_WR_READY) == GSR_WR_READY) break;
`if (i == max_tries) {
`printk(KERN_NOTICE "iflash2+_mtd: suspend_erase timed out\n");
`log_esr(NULL, esr);
`return CS_GENERAL_FAILURE;
`}
`writew(IF_READ_ARRAY, esr);
`return CS_SUCCESS;
`
`DEBUG(1, "iflash2+_mtd: resuming erase...\n");
`writew(IF_READ_ESR, esr);
`/* Only give resume signal if the erase is really suspended */
`if (readw(esr+4) & GSR_OP_SUSPEND)
`writew(IF_CONFIRM, esr);
`
`writew(IF_READ_ARRAY, esr);
`
`-7-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0007
`
`
`
`writew(IF_CLEAR_CSR, esr);
`
`iflash2+_mtd.c
`
`mtd_mod_win_t mod;
`mod.Attributes = WIN_MEMORY_TYPE_AM;
`mod.AccessSpeed = 250;
`mod.CardOffset = 0x4000;
`MTDHelperEntry(MTDModifyWindow, win, &mod);
`writeb((set) ? WP_BLKEN : 0, base+CISREG_WP);
`
`372
`373 }
`374
`375 /*====================================================================*/
`376
`377 static void set_global_lock(window_handle_t win,
`volatile caddr_t base, int set)
`378
`379 {
`380
`381
`382
`383
`384
`385
`386 }
`387
`388 /*======================================================================
`389
`390 Vpp management functions. The vpp_setup() function checks to
`391 see if Vpp is available for the specified device. If not, it
`392 turns on Vpp. The vpp_shutdown() function is scheduled to turn
`393 Vpp off after an interval of inactivity.
`394
`395 vpp_setup() assumes that it will be called at the top of a
`396 request handler, and that it can use the MTD_REQ_TIMEOUT flag
`397 to tell if it has already been called for this particular
`398 request, so that it can count Vpp users.
`399
`400 A handler should call vpp_shutdown() once for each request that
`401 does a vpp_setup().
`402
`403 ======================================================================*/
`404
`405 static int vpp_setup(dev_link_t *link, mtd_request_t *req)
`406 {
`407
`408
`409
`410
`411
`412
`413
`414
`415
`416
`417
`418
`419
`420
`421
`422
`423
`424
`
`/* First time for this request? */
`if (!(req->Function & MTD_REQ_TIMEOUT)) {
`/* If no other users, kill shutdown timer and apply power */
`if (++dev->vpp_usage == 1) {
`if (!del_timer(&dev->vpp_timeout)) {
`DEBUG(1, "iflash2+_mtd: raising Vpp...\n");
`dev->vpp_start = jiffies;
`vpp_req.Vpp1 = vpp_req.Vpp2 = 120;
`MTDHelperEntry(MTDSetVpp, link->handle, &vpp_req);
`}
`
`flash_dev_t *dev = (flash_dev_t *)link->priv;
`mtd_vpp_req_t vpp_req;
`
`}
`}
`/* Wait for Vpp to settle if it was just applied */
`if (jiffies < dev->vpp_start + vpp_settle) {
`req->Status = MTD_WAITTIMER;
`
`-8-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0008
`
`
`
`iflash2+_mtd.c
`
`req->Timeout = vpp_settle * 1000 / HZ;
`return 1;
`}
`return 0;
`
`dev_link_t *link = (dev_link_t *)arg;
`flash_dev_t *dev;
`mtd_vpp_req_t req;
`
`DEBUG(1, "iflash2+_mtd: lowering Vpp...\n");
`dev = (flash_dev_t *)link->priv;
`dev->vpp_timeout.expires = 0;
`req.Vpp1 = req.Vpp2 = 0;
`MTDHelperEntry(MTDSetVpp, link->handle, &req);
`
`flash_dev_t *dev;
`dev = (flash_dev_t *)link->priv;
`dev->vpp_usage--;
`if (dev->vpp_usage == 0) {
`dev->vpp_timeout.expires = jiffies + vpp_timeout_period;
`add_timer(&dev->vpp_timeout);
`}
`
`425
`426
`427
`428
`429 }
`430
`431 static void vpp_off(u_long arg)
`432 {
`433
`434
`435
`436
`437
`438
`439
`440
`441
`442 }
`443
`444 static void vpp_shutdown(dev_link_t *link)
`445 {
`446
`447
`448
`449
`450
`451
`452
`453 }
`454
`455 /*======================================================================
`456
`457 flash_attach() creates an "instance" of the driver, allocating
`458 local data structures for one device. The device is registered
`459 with Card Services.
`460
`461 ======================================================================*/
`462
`463 static dev_link_t *flash_attach(void)
`464 {
`465
`466
`467
`468
`469
`470
`471
`472
`473
`474
`475
`476
`477
`
`client_reg_t client_reg;
`dev_link_t *link;
`flash_dev_t *dev;
`int ret;
`
`DEBUG(0, "iflash2+_mtd: flash_attach()\n");
`
`/* Create new memory card device */
`dev = kmalloc(sizeof(*dev), GFP_KERNEL);
`if (!dev) return NULL;
`memset(dev, 0, sizeof(*dev));
`link = &dev->link; link->priv = dev;
`
`-9-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0009
`
`
`
`link->release.function = &flash_release;
`link->release.data = (u_long)link;
`init_waitqueue_head(&link->pending);
`
`dev->vpp_timeout.function = vpp_off;
`dev->vpp_timeout.data = (u_long)link;
`
`iflash2+_mtd.c
`
`/* Register with Card Services */
`link->next = dev_list;
`dev_list = link;
`client_reg.dev_info = &dev_info;
`client_reg.Attributes = INFO_MTD_CLIENT | INFO_CARD_SHARE;
`client_reg.EventMask =
`CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
`CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
`CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME |
`CS_EVENT_READY_CHANGE;
`client_reg.event_handler = &flash_event;
`client_reg.Version = 0x0210;
`client_reg.event_callback_args.client_data = link;
`ret = CardServices(RegisterClient, &link->handle, &client_reg);
`if (ret != 0) {
`cs_error(link->handle, RegisterClient, ret);
`flash_detach(link);
`return NULL;
`}
`
`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
`return link;
`505
`506 } /* flash_attach */
`507
`508 /*======================================================================
`509
`510 This deletes a driver "instance". The device is de-registered
`511 with Card Services. If it has been released, all local data
`512 structures are freed. Otherwise, the structures will be freed
`513 when the device is released.
`514
`515 ======================================================================*/
`516
`517 static void flash_detach(dev_link_t *link)
`518 {
`519
`520
`521
`522
`523
`524
`525
`526
`527
`528
`529
`530
`
`dev_link_t **linkp;
`int ret;
`
`DEBUG(0, "iflash2+_mtd: flash_detach(0x%p)\n", link);
`
`/* Locate device structure */
`for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
`if (*linkp == link) break;
`if (*linkp == NULL)
`return;
`
`del_timer(&link->release);
`
`-10-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0010
`
`
`
`if (link->state & DEV_CONFIG)
`flash_release((u_long)link);
`
`iflash2+_mtd.c
`
`if (link->handle) {
`ret = CardServices(DeregisterClient, link->handle);
`if (ret != CS_SUCCESS)
`cs_error(link->handle, DeregisterClient, ret);
`
`}
`
`/* Unlink device structure, free bits */
`*linkp = link->next;
`kfree(link->priv);
`
`531
`532
`533
`534
`535
`536
`537
`538
`539
`540
`541
`542
`543
`544 } /* flash_detach */
`545
`546 /*======================================================================
`547
`548 flash_config() is scheduled to run after a CARD_INSERTION event
`549 is received, to bind the MTD to appropriate memory regions.
`550
`551 ======================================================================*/
`552
`553 static void printk_size(u_int sz)
`554 {
`555
`556
`557
`558
`559
`560
`561 }
`562
`563 static void flash_config(dev_link_t *link)
`564 {
`565
`566
`567
`568
`569
`570
`571
`572
`573
`574
`575
`576
`577
`578
`579
`580
`581
`582
`583
`
`/* Allocate a small memory window */
`if (word_width)
`req.Attributes = WIN_DATA_WIDTH_16;
`else
`req.Attributes = WIN_DATA_WIDTH_8;
`req.Base = req.Size = 0;
`req.AccessSpeed = mem_speed;
`link->win = (window_handle_t)handle;
`ret = MTDHelperEntry(MTDRequestWindow, &link->win, &req);
`if (ret != 0) {
`
`if (sz & 0x3ff)
`printk("%u bytes", sz);
`else if (sz & 0x0fffff)
`printk("%u kb", sz >> 10);
`else
`printk("%u mb", sz >> 20);
`
`client_handle_t handle = link->handle;
`flash_dev_t *dev = link->priv;
`win_req_t req;
`mtd_reg_t reg;
`region_info_t region;
`int i, attr, ret;
`
`DEBUG(0, "iflash2+_mtd: flash_config(0x%p)\n", link);
`
`-11-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0011
`
`
`
`iflash2+_mtd.c
`
`584
`585
`586
`587
`588
`589
`590
`591
`592
`593
`594
`595
`596
`597
`598
`599
`600
`601
`602
`603
`604
`605
`606
`607
`608
`609
`610
`611
`612
`613
`614
`615
`616
`617
`618
`619
`620
`621
`622
`623
`624
`625
`626
`627
`628
`629
`630
`631
`632
`633
`634
`635
`636
`
`cs_error(handle, RequestWindow, ret);
`link->state &= ~DEV_CONFIG_PENDING;
`flash_release((u_long)link);
`return;
`}
`dev->Base = ioremap(req.Base, req.Size);
`dev->Size = req.Size;
`
`/* Allocate a memory window for ESR accesses*/
`req.Base = 0;
`dev->ESRwin = (window_handle_t)handle;
`ret = MTDHelperEntry(MTDRequestWindow, &dev->ESRwin, &req);
`if (ret != 0) {
`cs_error(handle, RequestWindow, ret);
`link->state &= ~DEV_CONFIG_PENDING;
`flash_release((u_long)link);
`return;
`}
`dev->ESRbase = ioremap(req.Base, req.Size);
`
`link->state |= DEV_CONFIG;
`
`/* Grab info for all the memory regions we can access */
`i = 0;
`for (attr = 0; attr < 2; attr++) {
`region.Attributes = attr ? REGION_TYPE_AM : REGION_TYPE_CM;
`ret = CardServices(GetFirstRegion, handle, ®ion);
`while (ret == CS_SUCCESS) {
`reg.Attributes = region.Attributes;
`reg.Offset = region.CardOffset;
`dev->flash[i] = kmalloc(sizeof(struct flash_region_t),
`GFP_KERNEL);
`if (!dev->flash[i]) break;
`reg.MediaID = (u_long)dev->flash[i];
`ret = CardServices(RegisterMTD, handle, ®);
`if (ret != 0) {
`kfree(dev->flash[i]);
`break;
`}
`printk(KERN_INFO "iflash2+_mtd: %s at 0x%x, ",
`attr ? "attr" : "common", region.CardOffset);
`printk_size(region.RegionSize);
`printk(", ");
`printk_size(region.BlockSize);
`printk(" blocks, %u ns\n", region.AccessSpeed);
`memset(dev->flash[i], 0, sizeof(struct flash_region_t));
`dev->flash[i]->region = region;
`/* Distinguish between 4MB..20MB cards and 40MB cards */
`if (region.RegionSize > 0x1400000)
`dev->flash[i]->cell_size = 0x800000; /* 8MB components */
`else
`dev->flash[i]->cell_size = 0x400000; /* 4MB components */
`i++;
`
`-12-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0012
`
`
`
`ret = CardServices(GetNextRegion, handle, ®ion);
`
`iflash2+_mtd.c
`
`}
`}
`dev->flash[i] = NULL;
`
`dev_link_t *link = (dev_link_t *)arg;
`flash_dev_t *dev = link->priv;
`int i;
`
`DEBUG(0, "iflash2+_mtd: flash_release(0x%p)\n", link);
`
`link->state &= ~DEV_CONFIG;
`if (link->win) {
`iounmap(dev->Base);
`i = MTDHelperEntry(MTDReleaseWindow, link->win);
`if (i != CS_SUCCESS)
`cs_error(link->handle, ReleaseWindow, i);
`
`}
`if (dev->ESRwin) {
`iounmap(dev->ESRbase);
`i = MTDHelperEntry(MTDReleaseWindow, dev->ESRwin);
`if (i != CS_SUCCESS)
`cs_error(link->handle, ReleaseWindow, i);
`
`637
`638
`639
`640
`641
`642 } /* flash_config */
`643
`644 /*======================================================================
`645
`646 After a card is removed, flash_release() will release the memory
`647 window allocated for this socket.
`648
`649 ======================================================================*/
`650
`651 static void flash_release(u_long arg)
`652 {
`653
`654
`655
`656
`657
`658
`659
`660
`661
`662
`663
`664
`665
`666
`667
`668
`669
`670
`671
`672
`673
`674
`675
`676
`677
`678
`679
`680
`681 } /* flash_release */
`682
`683 /*======================================================================
`684
`685 The read request handler. This handler supports suspending
`686 current erase requests. Reading from a block that is currently
`687 erasing is undefined.
`688
`689 ======================================================================*/
`
`}
`if (dev->vpp_usage == 0)
`del_timer(&dev->vpp_timeout);
`vpp_off((u_long)link);
`for (i = 0; (i < 2*CISTPL_MAX_DEVICES) && dev->flash[i]; i++)
`kfree(dev->flash[i]);
`
`if (link->state & DEV_STALE_LINK)
`flash_detach(link);
`
`-13-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0013
`
`
`
`iflash2+_mtd.c
`
`DEBUG(2, "iflash2+_mtd: flash_read(0x%p, 0x%lx, 0x%p, 0x%x, "
`"0x%x)\n", link, req->MediaID, buf, req->SrcCardOffset,
`req->TransferLength);
`
`flash = (flash_region_t *)(req->MediaID);
`region = &flash->region;
`if ((req->SrcCardOffset / region->BlockSize) !=
`((req->SrcCardOffset+req->TransferLength-1) / region->BlockSize))
`return CS_BAD_SIZE;
`if (region->Attributes & REGION_TYPE_AM)
`mod.Attributes = WIN_MEMORY_TYPE_AM;
`else
`mod.Attributes = WIN_MEMORY_TYPE_CM;
`mod.AccessSpeed = region->AccessSpeed;
`
`690
`691 static int flash_read(dev_link_t *link, char *buf, mtd_request_t *req)
`692 {
`flash_dev_t *dev = (flash_dev_t *)link->priv;
`693
`flash_region_t *flash;
`694
`region_info_t *region;
`695
`mtd_mod_win_t mod;
`696
`u_int from, length, nb, cell;
`697
`int ret;
`698
`699 #ifdef BENCHMARK
`k_time_t time;
`700
`701 #endif
`702
`703
`704
`705
`706
`707
`708
`709
`710
`711
`712
`713
`714
`715
`716
`717
`718
`719
`720
`721
`722
`723
`724
`725
`726
`727
`728
`729
`730
`731
`732
`733
`734
`735
`736
`737
`738
`739
`ret = CS_SUCCESS;
`740
`741 #ifdef BENCHMARK
`time = uticks();
`742
`
`/* Suspend an in-progress block erase */
`cell = (req->SrcCardOffset - region->CardOffset) / flash->cell_size;
`if (flash->cell[cell].state & FLASH_ERASING) {
`if ((flash->cell[cell].erase_addr / region->BlockSize) ==
`(req->SrcCardOffset / region->BlockSize)) {
`DEBUG(1, "iflash2+_mtd: delaying read...\n");
`req->Status = MTD_WAITREQ;
`return CS_BUSY;
`
`}
`link->state |= DEV_BUSY;
`mod.CardOffset = flash->cell[cell].erase_addr;
`ret = MTDHelperEntry(MTDModifyWindow, dev->ESRwin, &mod);
`if (ret != CS_SUCCESS) goto done;
`ret = suspend_erase((u_short *)dev->ESRbase);
`if (ret != CS_SUCCESS) goto done;
`flash->cell[cell].state |= FLASH_ERASE_SUSPEND;
`} else
`link->state |= DEV_BUSY;
`
`mod.CardOffset = req->SrcCardOffset & ~(dev->Size-1);
`from = req->SrcCardOffset & (dev->Size-1);
`
`-14-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0014
`
`
`
`iflash2+_mtd.c
`
`if (req->Function & MTD_REQ_KERNEL)
`copy_from_pc(buf, &dev->Base[from], nb);
`
`else
`
`copy_pc_to_user(buf, &dev->Base[from], nb);
`
`buf += nb;
`from = 0;
`mod.CardOffset += dev->Size;
`}
`
`ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod);
`if (ret != CS_SUCCESS) goto done;
`nb = (from+length > dev->Size) ? dev->Size-from : length;
`
`743 #endif
`for (length = req->TransferLength; length > 0; length -= nb) {
`744
`745
`746
`747
`748
`749
`750
`751
`752
`753
`754
`755
`756
`757
`758
`759
`760 #ifdef BENCHMARK
`time = uticks() - time;
`761
`if (time < 10000000)
`762
`DEBUG(3, "iflash2+_mtd: read complete, time = %ld,"
`763
`" avg = %ld ns/word, rate = %ld kb/sec\n", time,
`764
`time*2000/req->TransferLength,
`765
`req->TransferLength*977/time);
`766
`767 #endif
`768
`769 done:
`if (flash->cell[cell].state & FLASH_ERASE_SUSPEND) {
`770
`resume_erase((u_short *)dev->ESRbase);
`771
`flash->cell[cell].state &= ~FLASH_ERASE_SUSPEND;
`772
`}
`773
`link->state &= ~DEV_BUSY;
`774
`return ret;
`775
`776 } /* flash_read */
`777
`778 /*======================================================================
`779
`780 basic_write() handles a write that fits completely within a
`781 memory window that has already been set up. It does a series
`782 of pipelined page buffer writes.
`783
`784 ======================================================================*/
`785
`786 static int basic_write(wait_queue_head_t *queue, char *esr, char *dest,
`char *buf, u_int nb, u_int is_krnl)
`787
`788 {
`789
`790
`791
`792
`793
`794
`795
`
`u_short npb;
`int ret;
`
`/* Enable interrupts on write complete */
`ret = set_rdy_mode((u_short *)esr, IF_RDY_LEVEL);
`if (ret != CS_SUCCESS) return ret;
`
`-15-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0015
`
`
`
`iflash2+_mtd.c
`
`}
`ret = page_write((u_short *)esr, (u_short *)dest, 1);
`if (ret != CS_SUCCESS) return ret;
`dest++; buf++; nb--;
`}
`
`for (; nb > 0; nb -= npb) {
`/* npb = # of bytes to write to page buffer */
`npb = (nb > 512) ? 512 : nb;
`/* sleep until page buffer is free */
`ret = page_setup(queue, (u_short *)esr, (u_short *)dest, npb);
`if (ret != CS_SUCCESS) return ret;
`
`if (is_krnl)
`copy_to_pc(dest, buf, npb);
`
`else
`
`copy_user_to_pc(dest, buf, npb);
`
`ret = page_write((u_short *)esr, (u_short *)dest, npb);
`if (ret != CS_SUCCESS) return ret;
`check_write(queue, (u_short *)esr);
`
`/* Fix for mis-aligned writes */
`if ((u_long)dest & 1) {
`DEBUG(2, "iflash2+_mtd: odd address fixup at 0x%p\n", dest);
`ret = page_setup(queue, (u_short *)esr, (u_short *)dest, 1);
`if (ret != CS_SUCCESS) return ret;
`if (is_krnl)
`writeb(*buf, dest);
`else {
`char c;
`get_user(c, buf);
`writeb(c, dest);
`
`796
`797
`798
`799
`800
`801
`802
`803
`804
`805
`806
`807
`808
`809
`810
`811
`812
`813
`814
`815
`816
`817
`818
`819
`820
`821
`822
`823
`824
`825
`826
`827
`828
`829
`830
`831
`832
`833
`834
`835 }
`836
`837 /*======================================================================
`838
`839 The write request handler. The Series 2+ cards support automatic
`840 erase suspend for writes.
`841
`842 ======================================================================*/
`843
`844 static int flash_write(dev_link_t *link, char *buf, mtd_request_t *req)
`845 {
`846
`847
`848
`
`dest += npb;
`buf += npb;
`}
`
`/* sleep until block is ready */
`return check_write(queue, (u_short *)esr);
`
`flash_dev_t *dev = (flash_dev_t *)link->priv;
`mtd_mod_win_t mod;
`mtd_rdy_req_t rdy;
`
`-16-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0016
`
`
`
`iflash2+_mtd.c
`
`DEBUG(2, "iflash2+_mtd: flash_write(0x%p, 0x%lx, "
`"0x%p, 0x%x, 0x%x)\n", link, req->MediaID, buf,
`req->DestCardOffset, req->TransferLength);
`
`/* Check card write protect status */
`ret = CardServices(GetStatus, link->handle, &status);
`if (ret != CS_SUCCESS) {
`cs_error(link->handle, GetStatus, ret);
`return CS_GENERAL_FAILURE;
`}
`if (status.CardState & CS_EVENT_WRITE_PROTECT)
`return CS_WRITE_PROTECTED;
`
`flash = (flash_region_t *)(req->MediaID);
`region = &flash->region;
`if ((req->DestCardOffset / region->BlockSize) !=
`((req->DestCardOffset+req->TransferLength-1) / region->BlockSize))
`return CS_BAD_SIZE;
`
`/* Is this cell being erased or written? */
`cell = (req->DestCardOffset - region->CardOffset) / flash->cell_size;
`if (flash->cell[cell].state & FLASH_ERASING) {
`DEBUG(1, "iflash2+_mtd: delaying write...\n");
`req->Status = MTD_WAITREQ;
`return CS_BUSY;
`}
`link->state |= DEV_BUSY;
`
`flash_region_t *flash;
`849
`region_info_t *region;
`850
`u_int from, length, nb, retry, cell;
`851
`cs_status_t status;
`852
`int ret;
`853
`854 #ifdef BENCHMARK
`k_time_t time;
`855
`856 #endif
`857
`858
`859
`860
`861
`862
`863
`864
`865
`866
`867
`868
`869
`870
`871
`872
`873
`874
`875
`876
`877
`878
`879
`880
`881
`882
`883
`884
`885
`886
`887
`888
`889
`890
`891
`892
`893
`894
`895
`896
`897
`898
`899
`900
`901
`
`if (vpp_setup(link, req) != 0)
`return CS_BUSY;
`
`if (region->Attributes & REGION_TYPE_AM)
`mod.Attributes = WIN_MEMORY_TYPE_AM;
`else
`mod.Attributes = WIN_MEMORY_TYPE_CM;
`mod.AccessSpeed = region->AccessSpeed;
`
`/* Set up window for ESR accesses */
`mod.CardOffset = req->DestCardOffset & ~(region->BlockSize-1);
`ret = MTDHelperEntry(MTDModifyWindow, dev->ESRwin, &mod);
`if (ret != CS_SUCCESS) goto done;
`
`rdy.Mask = CS_EVENT_READY_CHANGE;
`MTDHelperEntry(MTDRDYMask, link->handle, &rdy);
`
`-17-
`
`APPLE INC.
`EXHIBIT 1036 - PAGE 0017
`
`
`
`iflash2+_mtd.c
`
`for (length = req->TransferLength ; length > 0; length -= nb) {
`ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod);
`if (ret != CS_SUCCESS) goto done;
`
`nb = (from+length > dev->Size) ? dev->Size-from : length;
`
`}
`if (retry == retry_limit) {
`printk(KERN_NOTICE "iflash2+_mtd: write at 0x%06x failed:"
`" too many retries!\n", mod.CardOffset);
`goto done;
`
`}
`
`buf += nb;
`from = 0;
`mod.CardOffset += dev->Size;
`}
`
`for (retry = 0; retry < retry_limit; retry++) {
`ret = basic_write(&link->pending, dev->ESRbase,
`dev->Base+from, buf, nb,
`(req->Function & MTD_REQ_KERNEL));
`if (ret == CS_SUCCESS)
`break;
`abort_cmd((u_short *)dev->ESRbase);
`
`902
`903 #ifdef BENCHMARK
`time = uticks();
`904
`905 #endif
`mod.CardOffset = req->DestCardOffset & ~(dev->Size-1);
`906
`from = req->DestCardOffset & (dev->Size-1);
`907
`908
`909
`910
`911
`912
`913
`914
`915
`916
`917
`918
`919
`920
`921
`922
`923
`924
`925
`926
`927
`928
`929
`930
`931
`93