Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port to pi4 #19

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 96 additions & 87 deletions gpio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,91 +102,6 @@ uint32_t GPIO::RequestInput(uint32_t inputs) {
return inputs;
}

// BCM2835-ARM-Peripherals.pdf, page 105 onwards.
double GPIO::StartClock(double requested_freq) {
// Figure out best clock source to get closest to the requested
// frequency with MASH=1. We check starting from the highest frequency to
// find lowest jitter opportunity first.

static const struct { int src; double frequency; } kClockSources[] = {
{ 5, 1000.0e6 }, // PLLC
{ 6, 500.0e6 }, // PLLD
{ 7, 216.0e6 }, // HDMI <- this can be problematic if monitor connected
{ 1, 19.2e6 }, // regular oscillator
};

int divI = -1;
int divF = -1;
int best_clock_source = -1;
double smallest_error_so_far = 1e9;
for (size_t i = 0; i < sizeof(kClockSources)/sizeof(kClockSources[0]); ++i) {
double division = kClockSources[i].frequency / requested_freq;
if (division < 2 || division > 4095) continue;
int test_divi = (int) division;
int test_divf = (division - test_divi) * 1024;
double freq = kClockSources[i].frequency / (test_divi + test_divf/1024.0);
double error = fabsl(requested_freq - freq);
if (error >= smallest_error_so_far) continue;
smallest_error_so_far = error;
best_clock_source = i;
divI = test_divi;
divF = test_divf;
}

if (divI < 0)
return -1.0; // Couldn't find any suitable clock.

assert(divI >= 2 && divI < 4096 && divF >= 0 && divF < 4096);

StopClock();

const uint32_t ctl = CLK_CMGP0_CTL;
const uint32_t div = CLK_CMGP0_DIV;
const uint32_t src = kClockSources[best_clock_source].src;
const uint32_t mash = 1; // Good approximation, low jitter.

clock_reg_[div] = CLK_PASSWD | CLK_DIV_DIVI(divI) | CLK_DIV_DIVF(divF);
usleep(10);

clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_MASH(mash) | CLK_CTL_SRC(src);
usleep(10);

clock_reg_[ctl] |= CLK_PASSWD | CLK_CTL_ENAB;

EnableClockOutput(true);

#if 0
// There have been reports of different clock source frequencies. This
// helps figuring out which source was picked.
fprintf(stderr, "Choose clock %d at %gHz / %.3f = %.3f\n",
kClockSources[best_clock_source].src,
kClockSources[best_clock_source].frequency,
divI + divF/1024.0,
kClockSources[best_clock_source].frequency / (divI + divF/1024.0));
#endif

return kClockSources[best_clock_source].frequency / (divI + divF/1024.0);
}

void GPIO::StopClock() {
const uint32_t ctl = CLK_CMGP0_CTL;
clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_KILL;

// Wait until clock confirms not to be busy anymore.
while (clock_reg_[ctl] & CLK_CTL_BUSY) {
usleep(10);
}
EnableClockOutput(false);
}

void GPIO::EnableClockOutput(bool on) {
if (on) {
ALT0_GPIO(4); // Pinmux GPIO4 into outputting clock.
} else {
INP_GPIO(4);
}
}

// We are not interested in the _exact_ model, just good enough to determine
// What to do.
enum RaspberryPiModel {
Expand Down Expand Up @@ -242,8 +157,7 @@ static RaspberryPiModel DetermineRaspberryModel() {

case 0x11: /* Pi 4 */
// A first test did not seem to work. Maybe the registers changed ?
fprintf(stderr, "Note: Frequency generation is known to not work on Pi4; "
"Use older Pis for now.\n");
fprintf(stderr, "Note: Frequency generation is experimental on Pi4.\n");
return PI_MODEL_4;

default: /* a bunch of versions representing Pi 3 */
Expand Down Expand Up @@ -301,3 +215,98 @@ bool GPIO::Init() {

return gpio_port_ != MAP_FAILED && clock_reg_ != MAP_FAILED;
}


// BCM2835-ARM-Peripherals.pdf, page 105 onwards.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the diff small, can you move this block back to around line 105 ?

double GPIO::StartClock(double requested_freq) {
// Figure out best clock source to get closest to the requested
// frequency with MASH=1. We check starting from the highest frequency to
// find lowest jitter opportunity first.
double f_pllc, f_plld, f_hdmi, f_regular;
switch (GetPiModel()) {
case PI_MODEL_1:
case PI_MODEL_2:
case PI_MODEL_3:
default: f_pllc = 1000.0e6; f_plld = 500.0e6; f_hdmi = 216.0e6; f_regular= 19.2e6; break;
case PI_MODEL_4: f_pllc = 317.0e6; f_plld = 317.0e6; f_hdmi = 216.0e6; f_regular= 54.0175e6; break;
}

static const struct { int src; double frequency; } kClockSources[] = {
{ 5, f_pllc }, // PLLC (note on pi4 317.0e6 is the closest I can get - but output on scope is 158.43 KHz)
{ 6, f_plld }, // PLLD (note on pi4 317.0e6 is the closest I can get - but output on scope is 183.37 KHz)
{ 7, f_hdmi }, // HDMI <- this can be problematic if monitor connected (could not find a valid value on pi4)
{ 1, f_regular }, // regular oscillator
};

int divI = -1;
int divF = -1;
int best_clock_source = -1;
double smallest_error_so_far = 1e9;
for (size_t i = 0; i < sizeof(kClockSources)/sizeof(kClockSources[0]); ++i) {
double division = kClockSources[i].frequency / requested_freq;
if (division < 2 || division > 4095) continue;
int test_divi = (int) division;
int test_divf = (division - test_divi) * 1024;
double freq = kClockSources[i].frequency / (test_divi + test_divf/1024.0);
double error = fabsl(requested_freq - freq);
if (error >= smallest_error_so_far) continue;
smallest_error_so_far = error;
best_clock_source = i;
divI = test_divi;
divF = test_divf;
}

if (divI < 0)
return -1.0; // Couldn't find any suitable clock.

assert(divI >= 2 && divI < 4096 && divF >= 0 && divF < 4096);

StopClock();

const uint32_t ctl = CLK_CMGP0_CTL;
const uint32_t div = CLK_CMGP0_DIV;
const uint32_t src = kClockSources[best_clock_source].src;
const uint32_t mash = 1; // Good approximation, low jitter.

clock_reg_[div] = CLK_PASSWD | CLK_DIV_DIVI(divI) | CLK_DIV_DIVF(divF);
usleep(10);

clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_MASH(mash) | CLK_CTL_SRC(src);
usleep(10);

clock_reg_[ctl] |= CLK_PASSWD | CLK_CTL_ENAB;

EnableClockOutput(true);

#if 0
// There have been reports of different clock source frequencies. This
// helps figuring out which source was picked.
fprintf(stderr, "Choose clock %d at %gHz / %.3f = %.3f\n",
kClockSources[best_clock_source].src,
kClockSources[best_clock_source].frequency,
divI + divF/1024.0,
kClockSources[best_clock_source].frequency / (divI + divF/1024.0));
#endif

return kClockSources[best_clock_source].frequency / (divI + divF/1024.0);
}

void GPIO::StopClock() {
const uint32_t ctl = CLK_CMGP0_CTL;
clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_KILL;

// Wait until clock confirms not to be busy anymore.
while (clock_reg_[ctl] & CLK_CTL_BUSY) {
usleep(10);
}
EnableClockOutput(false);
}

void GPIO::EnableClockOutput(bool on) {
if (on) {
ALT0_GPIO(4); // Pinmux GPIO4 into outputting clock.
} else {
INP_GPIO(4);
}
}