diff options
Diffstat (limited to 'os/sa1110/l3gpio.c')
| -rw-r--r-- | os/sa1110/l3gpio.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/os/sa1110/l3gpio.c b/os/sa1110/l3gpio.c new file mode 100644 index 00000000..8f162fe6 --- /dev/null +++ b/os/sa1110/l3gpio.c @@ -0,0 +1,246 @@ +/* + * L3 emulation using GPIO pins + * + * from the Linux sa1100-uda1341.c, + * Copyright (c) 2000 Nicolas Pitre <nico@cam.org> + * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * Modified by Vita Nuova 2001 + * + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * GPIO based L3 bus support. + * + * This provides control of Philips L3 type devices. + * GPIO lines are used for clock, data and mode pins. + * + * Note: The L3 pins are shared with I2C devices. This should not present + * any problems as long as an I2C start sequence is not generated. This is + * defined as a 1->0 transition on the data lines when the clock is high. + * It is critical this code only allow data transitions when the clock + * is low. This is always legal in L3. + * + * The IIC interface requires the clock and data pin to be LOW when idle. We + * must make sure we leave them in this state. + * + * It appears the read data is generated on the falling edge of the clock + * and should be held stable during the clock high time. + */ + +/* + * L3 setup and hold times (expressed in us) + */ +enum { + L3DataSetupTime = 1, /* 190 ns */ + L3DataHoldTime = 1, /* 30 ns */ + L3ModeSetupTime = 1, /* 190 ns */ + L3ModeHoldTime = 1, /* 190 ns */ + L3ClockHighTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ + L3ClockLowTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */ + L3HaltTime = 1, /* 190 ns */ +}; + +/* + * Grab control of the IIC/L3 shared pins + */ +static void +L3acquirepins(void) +{ + GpioReg *g = GPIOREG; + int s; + + s = splhi(); + g->gpsr = (L3Mode | L3Clock | L3Data); + g->gpdr |= (L3Mode | L3Clock | L3Data); + splx(s); +// microdelay(2); +} + +/* + * Release control of the IIC/L3 shared pins + */ +static void +L3releasepins(void) +{ + GpioReg *g = GPIOREG; + int s; + + s = splhi(); + g->gpdr &= ~(L3Mode | L3Clock | L3Data); + splx(s); +} + +/* + * Initialize the interface + */ +void +L3init(void) +{ + GpioReg *g = GPIOREG; + int s; + + s = splhi(); + g->gafr &= ~(L3Data | L3Clock | L3Mode); + splx(s); + L3releasepins(); +} + +/* + * Send a byte. The mode line is set or pulsed based on the mode sequence + * count. The mode line is high on entry and exit. The mod line is pulsed + * before the second data byte and before ech byte thereafter. + */ +static void +L3sendbyte(int data, int mode) +{ + int i; + GpioReg *g = GPIOREG; + + switch(mode) { + case 0: /* Address mode */ + g->gpcr = L3Mode; + break; + case 1: /* First data byte */ + break; + default: /* Subsequent bytes */ + g->gpcr = L3Mode; + microdelay(L3HaltTime); + g->gpsr = L3Mode; + break; + } + + microdelay(L3ModeSetupTime); + + for (i = 0; i < 8; i++){ + microdelay(2); + /* + * Send a bit. The clock is high on entry and on exit. Data is sent only + * when the clock is low (I2C compatibility). + */ + g->gpcr = L3Clock; + + if (data & (1<<i)) + g->gpsr = L3Data; + else + g->gpcr = L3Data; + + /* Assumes L3DataSetupTime < L3ClockLowTime */ + microdelay(L3ClockLowTime); + + g->gpsr = L3Clock; + microdelay(L3ClockHighTime); + } + + if (mode == 0) /* Address mode */ + g->gpsr = L3Mode; + + microdelay(L3ModeHoldTime); + +} + +/* + * Get a byte. The mode line is set or pulsed based on the mode sequence + * count. The mode line is high on entry and exit. The mod line is pulsed + * before the second data byte and before each byte thereafter. This + * function is never valid with mode == 0 (address cycle) as the address + * is always sent on the bus, not read. + */ +static int +L3getbyte(int mode) +{ + int data = 0; + int i; + GpioReg *g = GPIOREG; + + switch(mode) { + case 0: /* Address mode - never valid */ + break; + case 1: /* First data byte */ + break; + default: /* Subsequent bytes */ + g->gpcr = L3Mode; + microdelay(L3HaltTime); + g->gpsr = L3Mode; + break; + } + + microdelay(L3ModeSetupTime); + + for (i = 0; i < 8; i++){ + /* + * Get a bit. The clock is high on entry and on exit. Data is read after + * the clock low time has expired. + */ + g->gpcr = L3Clock; + microdelay(L3ClockLowTime); + + if(g->gplr & L3Data) + data |= 1<<i; + + g->gpsr = L3Clock; + microdelay(L3ClockHighTime); + } + + microdelay(L3ModeHoldTime); + + return data; +} + +/* + * Write data to a device on the L3 bus. The address is passed as well as + * the data and length. The length written is returned. The register space + * is encoded in the address (low two bits are set and device address is + * in the upper 6 bits). + */ +int +L3write(int addr, void *data, int len) +{ + int mode = 0; + int bytes = len; + uchar *b; + + L3acquirepins(); + L3sendbyte(addr, mode++); + for(b = data; --len >= 0;) + L3sendbyte(*b++, mode++); + L3releasepins(); + + return bytes; +} + +/* + * Read data from a device on the L3 bus. The address is passed as well as + * the data and length. The length read is returned. The register space + * is encoded in the address (low two bits are set and device address is + * in the upper 6 bits). + */ +int +L3read(int addr, void *data, int len) +{ + int mode = 0; + int bytes = len; + uchar *b; + int s; + + L3acquirepins(); + L3sendbyte(addr, mode++); + s = splhi(); + GPIOREG->gpdr &= ~(L3Data); + splx(s); + for(b = data; --len >= 0;) + *b++ = L3getbyte(mode++); + L3releasepins(); + + return bytes; +} |
