octeon_ebt3000_cf.c revision 194173
1/* 2 * octeon_ebt3000_cf.c 3 * 4 */ 5 6#include <sys/cdefs.h> 7__FBSDID("$FreeBSD: projects/mips/sys/mips/octeon1/octeon_ebt3000_cf.c 194173 2009-06-14 06:49:13Z imp $"); 8 9#include <sys/param.h> 10#include <sys/bio.h> 11#include <sys/systm.h> 12#include <sys/sysctl.h> 13#include <sys/bus.h> 14#include <sys/kernel.h> 15#include <sys/module.h> 16#include <sys/rman.h> 17#include <sys/power.h> 18#include <sys/smp.h> 19#include <sys/time.h> 20#include <sys/timetc.h> 21#include <sys/malloc.h> 22 23#include <geom/geom.h> 24 25#include <machine/clock.h> 26#include <machine/locore.h> 27#include <machine/md_var.h> 28#include <machine/cpuregs.h> 29 30#include "octeon_ebt3000_cf.h" 31#include "driveid.h" 32#include <mips/octeon1/octeon_pcmap_regs.h> 33 34/* ATA Commands */ 35#define CMD_READ_SECTOR 0x20 36#define CMD_WRITE_SECTOR 0x30 37#define CMD_IDENTIFY 0xEC 38 39/* The ATA Task File */ 40#define TF_DATA 0x00 41#define TF_ERROR 0x01 42#define TF_PRECOMP 0x01 43#define TF_SECTOR_COUNT 0x02 44#define TF_SECTOR_NUMBER 0x03 45#define TF_CYL_LSB 0x04 46#define TF_CYL_MSB 0x05 47#define TF_DRV_HEAD 0x06 48#define TF_STATUS 0x07 49#define TF_COMMAND 0x07 50 51/* Status Register */ 52#define STATUS_BSY 0x80 /* Drive is busy */ 53#define STATUS_RDY 0x40 /* Drive is ready */ 54#define STATUS_DRQ 0x08 /* Data can be transferred */ 55 56/* Miscelaneous */ 57#define SECTOR_SIZE 512 58#define WAIT_DELAY 1000 59#define NR_TRIES 1000 60#define SWAP_SHORT(x) ((x << 8) | (x >> 8)) 61#define SWAP_LONG(x) (((x << 24) & 0xFF000000) | ((x << 8) & 0x00FF0000) | \ 62 ((x >> 8) & 0x0000FF00) | ((x << 24) & 0x000000FF) ) 63#define MODEL_STR_SIZE 40 64 65 66/* Globals */ 67int bus_width; 68void *base_addr; 69 70/* Device softc */ 71struct cf_priv { 72 73 device_t dev; 74 struct drive_param *drive_param; 75 76 struct bio_queue_head cf_bq; 77 struct g_geom *cf_geom; 78 struct g_provider *cf_provider; 79 80}; 81 82/* Device parameters */ 83struct drive_param{ 84 union { 85 char buf[SECTOR_SIZE]; 86 struct hd_driveid driveid; 87 } u; 88 89 char model[MODEL_STR_SIZE]; 90 uint32_t nr_sectors; 91 uint16_t sector_size; 92 uint16_t heads; 93 uint16_t tracks; 94 uint16_t sec_track; 95 96} drive_param; 97 98/* GEOM class implementation */ 99static g_access_t cf_access; 100static g_start_t cf_start; 101static g_ioctl_t cf_ioctl; 102 103struct g_class g_cf_class = { 104 .name = "CF", 105 .version = G_VERSION, 106 .start = cf_start, 107 .access = cf_access, 108 .ioctl = cf_ioctl, 109}; 110 111/* Device methods */ 112static int cf_probe(device_t); 113static void cf_identify(driver_t *, device_t); 114static int cf_attach(device_t); 115static int cf_attach_geom(void *, int); 116 117/* ATA methods */ 118static void cf_cmd_identify(void); 119static void cf_cmd_write(uint32_t, uint32_t, void *); 120static void cf_cmd_read(uint32_t, uint32_t, void *); 121static void cf_wait_busy(void); 122static void cf_send_cmd(uint32_t, uint8_t); 123static void cf_attach_geom_proxy(void *arg, int flag); 124 125/* Miscelenous */ 126static void cf_swap_ascii(unsigned char[], char[]); 127 128 129/* ------------------------------------------------------------------- * 130 * cf_access() * 131 * ------------------------------------------------------------------- */ 132static int cf_access (struct g_provider *pp, int r, int w, int e) 133{ 134 135 pp->sectorsize = drive_param.sector_size; 136 pp->stripesize = drive_param.heads * drive_param.sec_track * drive_param.sector_size; 137 pp->mediasize = pp->stripesize * drive_param.tracks; 138 139 return (0); 140} 141 142 143/* ------------------------------------------------------------------- * 144 * cf_start() * 145 * ------------------------------------------------------------------- */ 146static void cf_start (struct bio *bp) 147{ 148 /* 149 * Handle actual I/O requests. The request is passed down through 150 * the bio struct. 151 */ 152 153 if(bp->bio_cmd & BIO_GETATTR) { 154 if (g_handleattr_int(bp, "GEOM::fwsectors", drive_param.sec_track)) 155 return; 156 if (g_handleattr_int(bp, "GEOM::fwheads", drive_param.heads)) 157 return; 158 g_io_deliver(bp, ENOIOCTL); 159 return; 160 } 161 162 if ((bp->bio_cmd & (BIO_READ | BIO_WRITE))) { 163 164 if (bp->bio_cmd & BIO_READ) { 165 cf_cmd_read(bp->bio_length / drive_param.sector_size, 166 bp->bio_offset / drive_param.sector_size, bp->bio_data); 167 168 } else if (bp->bio_cmd & BIO_WRITE) { 169 cf_cmd_write(bp->bio_length / drive_param.sector_size, 170 bp->bio_offset/drive_param.sector_size, bp->bio_data); 171 } 172 173 bp->bio_resid = 0; 174 bp->bio_completed = bp->bio_length; 175 g_io_deliver(bp, 0); 176 } 177} 178 179 180static int cf_ioctl (struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td) 181{ 182 return (0); 183} 184 185 186/* ------------------------------------------------------------------- * 187 * cf_cmd_read() * 188 * ------------------------------------------------------------------- * 189 * 190 * Read nr_sectors from the device starting from start_sector. 191 */ 192static void cf_cmd_read (uint32_t nr_sectors, uint32_t start_sector, void *buf) 193{ 194 unsigned long lba; 195 uint32_t count; 196 uint16_t *ptr_16; 197 uint8_t *ptr_8; 198 199//#define OCTEON_VISUAL_CF_0 1 200#ifdef OCTEON_VISUAL_CF_0 201 octeon_led_write_char(0, 'R'); 202#endif 203 ptr_8 = (uint8_t*)buf; 204 ptr_16 = (uint16_t*)buf; 205 lba = start_sector; 206 207 208 while (nr_sectors--) { 209 210 cf_send_cmd(lba, CMD_READ_SECTOR); 211 212 if (bus_width == 8) { 213 volatile uint8_t *task_file = (volatile uint8_t*)base_addr; 214 volatile uint8_t dummy; 215 for (count = 0; count < SECTOR_SIZE; count++) { 216 *ptr_8++ = task_file[TF_DATA]; 217 if ((count & 0xf) == 0) dummy = task_file[TF_STATUS]; 218 } 219 } else { 220 volatile uint16_t *task_file = (volatile uint16_t*)base_addr; 221 volatile uint16_t dummy; 222 for (count = 0; count < SECTOR_SIZE; count+=2) { 223 uint16_t temp; 224 temp = task_file[TF_DATA]; 225 *ptr_16++ = SWAP_SHORT(temp); 226 if ((count & 0xf) == 0) dummy = task_file[TF_STATUS/2]; 227 } 228 } 229 230 lba ++; 231 } 232#ifdef OCTEON_VISUAL_CF_0 233 octeon_led_write_char(0, ' '); 234#endif 235} 236 237 238/* ------------------------------------------------------------------- * 239 * cf_cmd_write() * 240 * ------------------------------------------------------------------- * 241 * 242 * Write nr_sectors to the device starting from start_sector. 243 */ 244static void cf_cmd_write (uint32_t nr_sectors, uint32_t start_sector, void *buf) 245{ 246 uint32_t lba; 247 uint32_t count; 248 uint16_t *ptr_16; 249 uint8_t *ptr_8; 250 251//#define OCTEON_VISUAL_CF_1 1 252#ifdef OCTEON_VISUAL_CF_1 253 octeon_led_write_char(1, 'W'); 254#endif 255 lba = start_sector; 256 ptr_8 = (uint8_t*)buf; 257 ptr_16 = (uint16_t*)buf; 258 259 while (nr_sectors--) { 260 261 cf_send_cmd(lba, CMD_WRITE_SECTOR); 262 263 if (bus_width == 8) { 264 volatile uint8_t *task_file; 265 volatile uint8_t dummy; 266 267 task_file = (volatile uint8_t *) base_addr; 268 for (count = 0; count < SECTOR_SIZE; count++) { 269 task_file[TF_DATA] = *ptr_8++; 270 if ((count & 0xf) == 0) dummy = task_file[TF_STATUS]; 271 } 272 } else { 273 volatile uint16_t *task_file; 274 volatile uint16_t dummy; 275 276 task_file = (volatile uint16_t *) base_addr; 277 for (count = 0; count < SECTOR_SIZE; count+=2) { 278 uint16_t temp = *ptr_16++; 279 task_file[TF_DATA] = SWAP_SHORT(temp); 280 if ((count & 0xf) == 0) dummy = task_file[TF_STATUS/2]; 281 } 282 } 283 284 lba ++; 285 } 286#ifdef OCTEON_VISUAL_CF_1 287 octeon_led_write_char(1, ' '); 288#endif 289} 290 291 292/* ------------------------------------------------------------------- * 293 * cf_cmd_identify() * 294 * ------------------------------------------------------------------- * 295 * 296 * Read parameters and other information from the drive and store 297 * it in the drive_param structure 298 * 299 */ 300static void cf_cmd_identify (void) 301{ 302 int count; 303 uint8_t status; 304 305 if (bus_width == 8) { 306 volatile uint8_t *task_file; 307 308 task_file = (volatile uint8_t *) base_addr; 309 310 while ((status = task_file[TF_STATUS]) & STATUS_BSY) { 311 DELAY(WAIT_DELAY); 312 } 313 314 task_file[TF_SECTOR_COUNT] = 0; 315 task_file[TF_SECTOR_NUMBER] = 0; 316 task_file[TF_CYL_LSB] = 0; 317 task_file[TF_CYL_MSB] = 0; 318 task_file[TF_DRV_HEAD] = 0; 319 task_file[TF_COMMAND] = CMD_IDENTIFY; 320 321 cf_wait_busy(); 322 323 for (count = 0; count < SECTOR_SIZE; count++) 324 drive_param.u.buf[count] = task_file[TF_DATA]; 325 326 } else { 327 volatile uint16_t *task_file; 328 329 task_file = (volatile uint16_t *) base_addr; 330 331 while ((status = (task_file[TF_STATUS/2]>>8)) & STATUS_BSY) { 332 DELAY(WAIT_DELAY); 333 } 334 335 task_file[TF_SECTOR_COUNT/2] = 0; /* this includes TF_SECTOR_NUMBER */ 336 task_file[TF_CYL_LSB/2] = 0; /* this includes TF_CYL_MSB */ 337 task_file[TF_DRV_HEAD/2] = 0 | (CMD_IDENTIFY<<8); /* this includes TF_COMMAND */ 338 339 cf_wait_busy(); 340 341 for (count = 0; count < SECTOR_SIZE; count+=2) { 342 uint16_t temp; 343 temp = task_file[TF_DATA]; 344 345 /* endianess will be swapped below */ 346 drive_param.u.buf[count] = (temp & 0xff); 347 drive_param.u.buf[count+1] = (temp & 0xff00)>>8; 348 } 349 } 350 351 cf_swap_ascii(drive_param.u.driveid.model, drive_param.model); 352 353 drive_param.sector_size = 512; //= SWAP_SHORT (drive_param.u.driveid.sector_bytes); 354 drive_param.heads = SWAP_SHORT (drive_param.u.driveid.cur_heads); 355 drive_param.tracks = SWAP_SHORT (drive_param.u.driveid.cur_cyls); 356 drive_param.sec_track = SWAP_SHORT (drive_param.u.driveid.cur_sectors); 357 drive_param.nr_sectors = SWAP_LONG (drive_param.u.driveid.lba_capacity); 358 359} 360 361 362/* ------------------------------------------------------------------- * 363 * cf_send_cmd() * 364 * ------------------------------------------------------------------- * 365 * 366 * Send command to read/write one sector specified by lba. 367 * 368 */ 369static void cf_send_cmd (uint32_t lba, uint8_t cmd) 370{ 371 uint8_t status; 372 373 if (bus_width == 8) { 374 volatile uint8_t *task_file; 375 376 task_file = (volatile uint8_t *) base_addr; 377 378 while ( (status = task_file[TF_STATUS]) & STATUS_BSY) { 379 DELAY(WAIT_DELAY); 380 } 381 382 task_file[TF_SECTOR_COUNT] = 1; 383 task_file[TF_SECTOR_NUMBER] = (lba & 0xff); 384 task_file[TF_CYL_LSB] = ((lba >> 8) & 0xff); 385 task_file[TF_CYL_MSB] = ((lba >> 16) & 0xff); 386 task_file[TF_DRV_HEAD] = ((lba >> 24) & 0xff) | 0xe0; 387 task_file[TF_COMMAND] = cmd; 388 389 } else { 390 volatile uint16_t *task_file; 391 392 task_file = (volatile uint16_t *) base_addr; 393 394 while ( (status = (task_file[TF_STATUS/2]>>8)) & STATUS_BSY) { 395 DELAY(WAIT_DELAY); 396 } 397 398 task_file[TF_SECTOR_COUNT/2] = 1 | ((lba & 0xff) << 8); 399 task_file[TF_CYL_LSB/2] = ((lba >> 8) & 0xff) | (((lba >> 16) & 0xff) << 8); 400 task_file[TF_DRV_HEAD/2] = (((lba >> 24) & 0xff) | 0xe0) | (cmd << 8); 401 402 } 403 404 cf_wait_busy(); 405} 406 407/* ------------------------------------------------------------------- * 408 * cf_wait_busy() * 409 * ------------------------------------------------------------------- * 410 * 411 * Wait until the drive finishes a given command and data is 412 * ready to be transferred. This is done by repeatedly checking 413 * the BSY and DRQ bits of the status register. When the controller 414 * is ready for data transfer, it clears the BSY bit and sets the 415 * DRQ bit. 416 * 417 */ 418static void cf_wait_busy (void) 419{ 420 uint8_t status; 421 422//#define OCTEON_VISUAL_CF_2 1 423#ifdef OCTEON_VISUAL_CF_2 424 static int where0 = 0; 425 426 octeon_led_run_wheel(&where0, 2); 427#endif 428 429 if (bus_width == 8) { 430 volatile uint8_t *task_file; 431 task_file = (volatile uint8_t *)base_addr; 432 433 status = task_file[TF_STATUS]; 434 while ((status & STATUS_BSY) == STATUS_BSY || (status & STATUS_DRQ) != STATUS_DRQ ) { 435 DELAY(WAIT_DELAY); 436 status = task_file[TF_STATUS]; 437 } 438 } else { 439 volatile uint16_t *task_file; 440 task_file = (volatile uint16_t *)base_addr; 441 442 status = task_file[TF_STATUS/2]>>8; 443 while ((status & STATUS_BSY) == STATUS_BSY || (status & STATUS_DRQ) != STATUS_DRQ ) { 444 DELAY(WAIT_DELAY); 445 status = (uint8_t)(task_file[TF_STATUS/2]>>8); 446 } 447 } 448 449#ifdef OCTEON_VISUAL_CF_2 450 octeon_led_write_char(2, ' '); 451#endif 452} 453 454/* ------------------------------------------------------------------- * 455 * cf_swap_ascii() * 456 * ------------------------------------------------------------------- * 457 * 458 * The ascii string returned by the controller specifying 459 * the model of the drive is byte-swaped. This routine 460 * corrects the byte ordering. 461 * 462 */ 463static void cf_swap_ascii (unsigned char str1[], char str2[]) 464{ 465 int i; 466 467 for(i = 0; i < MODEL_STR_SIZE; i++) { 468 str2[i] = str1[i^1]; 469 } 470} 471 472 473/* ------------------------------------------------------------------- * 474 * cf_probe() * 475 * ------------------------------------------------------------------- */ 476 477static int cf_probe (device_t dev) 478{ 479 if (!octeon_board_real()) return 1; 480 481 if (device_get_unit(dev) != 0) { 482 panic("can't attach more devices\n"); 483 } 484 485 device_set_desc(dev, "Octeon Compact Flash Driver"); 486 487 cf_cmd_identify(); 488 489 return (0); 490} 491 492/* ------------------------------------------------------------------- * 493 * cf_identify() * 494 * ------------------------------------------------------------------- * 495 * 496 * Find the bootbus region for the CF to determine 497 * 16 or 8 bit and check to see if device is 498 * inserted. 499 * 500 */ 501static void cf_identify (driver_t *drv, device_t parent) 502{ 503 uint8_t status; 504 int bus_region; 505 int count = 0; 506 octeon_mio_boot_reg_cfgx_t cfg; 507 508 509 if (!octeon_board_real()) 510 return; 511 512 base_addr = (void *) OCTEON_PHYS2PTR(OCTEON_CF_COMMON_BASE_ADDR); 513 514 for (bus_region = 0; bus_region < 8; bus_region++) 515 { 516 cfg.word64 = oct_read64(OCTEON_MIO_BOOT_REG_CFGX(bus_region)); 517 if (cfg.bits.base == OCTEON_CF_COMMON_BASE_ADDR >> 16) 518 { 519 bus_width = (cfg.bits.width) ? 16: 8; 520 printf("Compact flash found in bootbus region %d (%d bit).\n", bus_region, bus_width); 521 break; 522 } 523 } 524 525 if (bus_width == 8) { 526 volatile uint8_t *task_file; 527 task_file = (volatile uint8_t *) base_addr; 528 /* Check if CF is inserted */ 529 while ( (status = task_file[TF_STATUS]) & STATUS_BSY){ 530 if ((count++) == NR_TRIES ) { 531 printf("Compact Flash not present\n"); 532 return; 533 } 534 DELAY(WAIT_DELAY); 535 } 536 } else { 537 volatile uint16_t *task_file; 538 task_file = (volatile uint16_t *) base_addr; 539 /* Check if CF is inserted */ 540 while ( (status = (task_file[TF_STATUS/2]>>8)) & STATUS_BSY){ 541 if ((count++) == NR_TRIES ) { 542 printf("Compact Flash not present\n"); 543 return; 544 } 545 DELAY(WAIT_DELAY); 546 } 547 } 548 549 BUS_ADD_CHILD(parent, 0, "cf", 0); 550} 551 552 553/* ------------------------------------------------------------------- * 554 * cf_attach_geom() * 555 * ------------------------------------------------------------------- */ 556 557static int cf_attach_geom (void *arg, int flag) 558{ 559 struct cf_priv *cf_priv; 560 561 cf_priv = (struct cf_priv *) arg; 562 cf_priv->cf_geom = g_new_geomf(&g_cf_class, "cf%d", device_get_unit(cf_priv->dev)); 563 cf_priv->cf_provider = g_new_providerf(cf_priv->cf_geom, cf_priv->cf_geom->name); 564 cf_priv->cf_geom->softc = cf_priv; 565 g_error_provider(cf_priv->cf_provider, 0); 566 567 return (0); 568} 569 570/* ------------------------------------------------------------------- * 571 * cf_attach_geom() * 572 * ------------------------------------------------------------------- */ 573static void cf_attach_geom_proxy (void *arg, int flag) 574{ 575 cf_attach_geom(arg, flag); 576} 577 578 579 580/* ------------------------------------------------------------------- * 581 * cf_attach() * 582 * ------------------------------------------------------------------- */ 583 584static int cf_attach (device_t dev) 585{ 586 struct cf_priv *cf_priv; 587 588 if (!octeon_board_real()) return 1; 589 590 cf_priv = device_get_softc(dev); 591 cf_priv->dev = dev; 592 cf_priv->drive_param = &drive_param; 593 594 g_post_event(cf_attach_geom_proxy, cf_priv, M_WAITOK, NULL); 595 bioq_init(&cf_priv->cf_bq); 596 597 return 0; 598} 599 600 601static device_method_t cf_methods[] = { 602 /* Device interface */ 603 DEVMETHOD(device_probe, cf_probe), 604 DEVMETHOD(device_identify, cf_identify), 605 DEVMETHOD(device_attach, cf_attach), 606 DEVMETHOD(device_detach, bus_generic_detach), 607 DEVMETHOD(device_shutdown, bus_generic_shutdown), 608 609 { 0, 0 } 610}; 611 612static driver_t cf_driver = { 613 "cf", 614 cf_methods, 615 sizeof(struct cf_priv) 616}; 617 618static devclass_t cf_devclass; 619 620DRIVER_MODULE(cf, nexus, cf_driver, cf_devclass, 0, 0); 621 622