bool spi_start_clock(sdioh_info_t *sd, uint16 div) { spih_info_t *si = (spih_info_t *)sd->controller; osl_t *osh = si->osh; spih_regs_t *regs = si->regs; uint32 t, espr, disp; uint32 disp_xtal_freq; bool ext_clock = FALSE; char disp_string[5]; if (div > 2048) { sd_err(("%s: divisor %d too large; using max of 2048\n", __FUNCTION__, div)); div = 2048; } else if (div & (div - 1)) { while ((div + 1) & div) div |= div >> 1; div++; } if (si->rev >= 5) { uint32 clk_tick; if (!sdspi_switch_clock(sd, TRUE)) { sd_err(("%s: error switching to external clock\n", __FUNCTION__)); } clk_tick = SPIPCI_RREG(osh, ®s->spih_clk_count); if (clk_tick == SPIPCI_RREG(osh, ®s->spih_clk_count)) { if (!sdspi_switch_clock(sd, FALSE)) { sd_err(("%s: error switching to external clock\n", __FUNCTION__)); } } else { ext_clock = TRUE; } } if (div == 0) { ext_clock = FALSE; div = 2; if (!sdspi_switch_clock(sd, FALSE)) { sd_err(("%s: error switching to external clock\n", __FUNCTION__)); } sd_err(("%s: Ok to hot-swap oscillators.\n", __FUNCTION__)); } if (ext_clock == TRUE) { uint32 xtal_freq; OSL_DELAY(1000); xtal_freq = SPIPCI_RREG(osh, ®s->spih_xtal_freq) * 10000; sd_info(("%s: Oscillator is %dHz\n", __FUNCTION__, xtal_freq)); disp_xtal_freq = xtal_freq / 10000; if ((disp_xtal_freq % 100) > 50) { disp_xtal_freq += 100; } disp_xtal_freq = (disp_xtal_freq / 100) * 100; } else { sd_err(("%s: no external oscillator installed, using PCI clock.\n", __FUNCTION__)); disp_xtal_freq = 3333; } sprintf(disp_string, "%04d", disp_xtal_freq / div); disp = (disp_string[0] - '0') << 12; disp |= (disp_string[1] - '0') << 8; disp |= (disp_string[2] - '0') << 4; disp |= (disp_string[3] - '0'); switch (div) { case 1: espr = 0x0; break; case 2: espr = 0x1; break; case 4: espr = 0x2; break; case 8: espr = 0x5; break; case 16: espr = 0x3; break; case 32: espr = 0x4; break; case 64: espr = 0x6; break; case 128: espr = 0x7; break; case 256: espr = 0x8; break; case 512: espr = 0x9; break; case 1024: espr = 0xa; break; case 2048: espr = 0xb; break; default: espr = 0x0; ASSERT(0); break; } t = SPIPCI_RREG(osh, ®s->spih_ctrl); t &= ~3; t |= espr & 3; SPIPCI_WREG(osh, ®s->spih_ctrl, t); t = SPIPCI_RREG(osh, ®s->spih_ext); t &= ~3; t |= (espr >> 2) & 3; SPIPCI_WREG(osh, ®s->spih_ext, t); SPIPCI_WREG(osh, ®s->spih_hex_disp, disp); if (si->rev < 8) { OSL_DELAY(5000); } sd_info(("%s: SPI_CTRL=0x%08x SPI_EXT=0x%08x\n", __FUNCTION__, SPIPCI_RREG(osh, ®s->spih_ctrl), SPIPCI_RREG(osh, ®s->spih_ext))); return TRUE; }
/* Configure PCI-SPI Host Controller's SPI Clock rate as a divisor into the * base clock rate. The base clock is either the PCI Clock (33MHz) or the * external clock oscillator at U17 on the PciSpiHost. */ bool spi_start_clock(sdioh_info_t *sd, uint16 div) { spih_info_t *si = (spih_info_t *)sd->controller; osl_t *osh = si->osh; spih_regs_t *regs = si->regs; uint32 t, espr, disp; uint32 disp_xtal_freq; bool ext_clock = FALSE; char disp_string[5]; if (div > 2048) { sd_err(("%s: divisor %d too large; using max of 2048\n", __FUNCTION__, div)); div = 2048; } else if (div & (div - 1)) { /* Not a power of 2? */ /* Round up to a power of 2 */ while ((div + 1) & div) div |= div >> 1; div++; } /* For FPGA Rev >= 5, the use of an external clock oscillator is supported. * If the oscillator is populated, use it to provide the SPI base clock, * otherwise, default to the PCI clock as the SPI base clock. */ if (si->rev >= 5) { uint32 clk_tick; /* Enable the External Clock Oscillator as PLL clock source. */ if (!sdspi_switch_clock(sd, TRUE)) { sd_err(("%s: error switching to external clock\n", __FUNCTION__)); } /* Check to make sure the external clock is running. If not, then it * is not populated on the card, so we will default to the PCI clock. */ clk_tick = SPIPCI_RREG(osh, ®s->spih_clk_count); if (clk_tick == SPIPCI_RREG(osh, ®s->spih_clk_count)) { /* Switch back to the PCI clock as the clock source. */ if (!sdspi_switch_clock(sd, FALSE)) { sd_err(("%s: error switching to external clock\n", __FUNCTION__)); } } else { ext_clock = TRUE; } } /* Hack to allow hot-swapping oscillators: * 1. Force PCI clock as clock source, using sd_divisor of 0. * 2. Swap oscillator * 3. Set desired sd_divisor (will switch to external oscillator as clock source. */ if (div == 0) { ext_clock = FALSE; div = 2; /* Select PCI clock as the clock source. */ if (!sdspi_switch_clock(sd, FALSE)) { sd_err(("%s: error switching to external clock\n", __FUNCTION__)); } sd_err(("%s: Ok to hot-swap oscillators.\n", __FUNCTION__)); } /* If using the external oscillator, read the clock frequency from the controller * The value read is in units of 10000Hz, and it's not a nice round number because * it is calculated by the FPGA. So to make up for that, we round it off. */ if (ext_clock == TRUE) { uint32 xtal_freq; OSL_DELAY(1000); xtal_freq = SPIPCI_RREG(osh, ®s->spih_xtal_freq) * 10000; sd_info(("%s: Oscillator is %dHz\n", __FUNCTION__, xtal_freq)); disp_xtal_freq = xtal_freq / 10000; /* Round it off to a nice number. */ if ((disp_xtal_freq % 100) > 50) { disp_xtal_freq += 100; } disp_xtal_freq = (disp_xtal_freq / 100) * 100; } else { sd_err(("%s: no external oscillator installed, using PCI clock.\n", __FUNCTION__)); disp_xtal_freq = 3333; } /* Convert the SPI Clock frequency to BCD format. */ sprintf(disp_string, "%04d", disp_xtal_freq / div); disp = (disp_string[0] - '0') << 12; disp |= (disp_string[1] - '0') << 8; disp |= (disp_string[2] - '0') << 4; disp |= (disp_string[3] - '0'); /* Select the correct ESPR register value based on the divisor. */ switch (div) { case 1: espr = 0x0; break; case 2: espr = 0x1; break; case 4: espr = 0x2; break; case 8: espr = 0x5; break; case 16: espr = 0x3; break; case 32: espr = 0x4; break; case 64: espr = 0x6; break; case 128: espr = 0x7; break; case 256: espr = 0x8; break; case 512: espr = 0x9; break; case 1024: espr = 0xa; break; case 2048: espr = 0xb; break; default: espr = 0x0; ASSERT(0); break; } t = SPIPCI_RREG(osh, ®s->spih_ctrl); t &= ~3; t |= espr & 3; SPIPCI_WREG(osh, ®s->spih_ctrl, t); t = SPIPCI_RREG(osh, ®s->spih_ext); t &= ~3; t |= (espr >> 2) & 3; SPIPCI_WREG(osh, ®s->spih_ext, t); SPIPCI_WREG(osh, ®s->spih_hex_disp, disp); /* For Rev 8, writing to the PLL_CTRL register resets * the PLL, and it can re-acquire in 200uS. For * Rev 7 and older, we use a software delay to allow * the PLL to re-acquire, which takes more than 2mS. */ if (si->rev < 8) { /* Wait for clock to settle. */ OSL_DELAY(5000); } sd_info(("%s: SPI_CTRL=0x%08x SPI_EXT=0x%08x\n", __FUNCTION__, SPIPCI_RREG(osh, ®s->spih_ctrl), SPIPCI_RREG(osh, ®s->spih_ext))); return TRUE; }