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