summaryrefslogtreecommitdiffstats
path: root/sys/dev/pcf
diff options
context:
space:
mode:
authornsouch <nsouch@FreeBSD.org>1998-10-31 11:37:09 +0000
committernsouch <nsouch@FreeBSD.org>1998-10-31 11:37:09 +0000
commit2ae8dc409583e4ed96ca74b7f4817f015685e45f (patch)
tree6c1dd20e560277a32eb4ff159dc40b87e98b90cc /sys/dev/pcf
parent3b006890f16badfe89b25127eb04b14cfc373659 (diff)
downloadFreeBSD-src-2ae8dc409583e4ed96ca74b7f4817f015685e45f.zip
FreeBSD-src-2ae8dc409583e4ed96ca74b7f4817f015685e45f.tar.gz
pcf.c: timeout management added
ppc.c: nsc code improved. Actually, a complete rewrite.
Diffstat (limited to 'sys/dev/pcf')
-rw-r--r--sys/dev/pcf/pcf.c137
1 files changed, 90 insertions, 47 deletions
diff --git a/sys/dev/pcf/pcf.c b/sys/dev/pcf/pcf.c
index bee0e15..771dd30 100644
--- a/sys/dev/pcf/pcf.c
+++ b/sys/dev/pcf/pcf.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: pcf.c,v 1.1 1998/09/03 21:01:22 nsouch Exp $
+ * $Id: pcf.c,v 1.2 1998/10/22 05:58:40 bde Exp $
*
*/
#include <sys/param.h>
@@ -41,7 +41,7 @@
#include <dev/iicbus/iiconf.h>
#include "iicbus_if.h"
-#define TIMEOUT 99999 /* XXX */
+#define TIMEOUT 9999 /* XXX */
/* Status bits of S1 register (read only) */
#define nBB 0x01 /* busy when low set/reset by STOP/START*/
@@ -67,13 +67,15 @@
#define SLAVE_TRANSMITTER 0x1
#define SLAVE_RECEIVER 0x2
+#define PCF_DEFAULT_ADDR 0xaa
+
struct pcf_softc {
int pcf_base; /* isa port */
+ u_char pcf_addr; /* interface I2C address */
- int pcf_count;
- int pcf_own_address; /* own address */
int pcf_slave_mode; /* receiver or transmitter */
+ int pcf_started; /* 1 if start condition sent */
device_t iicbus; /* the corresponding iicbus */
};
@@ -103,8 +105,8 @@ static int pcf_probe(device_t);
static int pcf_attach(device_t);
static void pcf_print_child(device_t, device_t);
-static int pcf_repeated_start(device_t, u_char);
-static int pcf_start(device_t, u_char);
+static int pcf_repeated_start(device_t, u_char, int);
+static int pcf_start(device_t, u_char, int);
static int pcf_stop(device_t);
static int pcf_write(device_t, char *, int, int *);
static int pcf_read(device_t, char *, int, int *);
@@ -120,6 +122,7 @@ static device_method_t pcf_methods[] = {
DEVMETHOD(bus_print_child, pcf_print_child),
/* iicbus interface */
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
DEVMETHOD(iicbus_start, pcf_start),
DEVMETHOD(iicbus_stop, pcf_stop),
@@ -165,6 +168,7 @@ pcfprobe_isa(struct isa_device *dvp)
/* XXX add the pcf device to the root_bus until isa bus exists */
pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit, NULL);
+ device_probe_and_attach(pcfdev);
if (!pcfdev)
goto error;
@@ -188,15 +192,20 @@ static int
pcf_probe(device_t pcfdev)
{
struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev);
+ int unit = device_get_unit(pcfdev);
- /* XXX try do detect chipset */
+ /* retrieve base address from isa initialization
+ *
+ * XXX should use ivars with isabus
+ */
+ pcf->pcf_base = pcfdata[unit]->pcf_base;
- device_set_desc(pcfdev, "PCF8584 I2C bus controller");
+ /* reset the chip */
+ pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
- pcf->iicbus = iicbus_alloc_bus(pcfdev);
+ /* XXX try do detect chipset */
- if (!pcf->iicbus)
- return (EINVAL);
+ device_set_desc(pcfdev, "PCF8584 I2C bus controller");
return (0);
}
@@ -205,13 +214,11 @@ static int
pcf_attach(device_t pcfdev)
{
struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev);
- int unit = device_get_unit(pcfdev);
- /* retrieve base address from isa initialization
- *
- * XXX should use ivars with isabus
- */
- pcf->pcf_base = pcfdata[unit]->pcf_base;
+ pcf->iicbus = iicbus_alloc_bus(pcfdev);
+
+ if (!pcf->iicbus)
+ return (EINVAL);
/* probe and attach the iicbus */
device_probe_and_attach(pcf->iicbus);
@@ -222,8 +229,10 @@ pcf_attach(device_t pcfdev)
static void
pcf_print_child(device_t bus, device_t dev)
{
+ struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
+
printf(" on %s%d addr 0x%x", device_get_name(bus),
- device_get_unit(bus), iicbus_get_own_address(dev));
+ device_get_unit(bus), (int)pcf->pcf_addr);
return;
}
@@ -293,13 +302,39 @@ static int pcf_stop(device_t pcfdev)
{
struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
- /* set stop condition and enable IT */
- PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
+ /*
+ * Send STOP condition iff the START condition was previously sent.
+ * STOP is sent only once even if a iicbus_stop() is called after
+ * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
+ * before the last char is read.
+ */
+ if (pcf->pcf_started) {
+ /* set stop condition and enable IT */
+ PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
+
+ pcf->pcf_started = 0;
+ }
return (0);
}
-static int pcf_repeated_start(device_t pcfdev, u_char slave)
+
+static int pcf_noack(struct pcf_softc *pcf, int timeout)
+{
+ int noack;
+ int k = timeout/10;
+
+ do {
+ noack = PCF_GET_S1(pcf) & LRB;
+ if (!noack)
+ break;
+ DELAY(10); /* XXX wait 10 us */
+ } while (k--);
+
+ return (noack);
+}
+
+static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
{
struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
int error = 0;
@@ -315,8 +350,8 @@ static int pcf_repeated_start(device_t pcfdev, u_char slave)
if ((error = pcf_wait_byte(pcf)))
goto error;
- /* check ACK */
- if (PCF_GET_S1(pcf) & LRB) {
+ /* check for ack */
+ if (pcf_noack(pcf, timeout)) {
error = IIC_ENOACK;
goto error;
}
@@ -328,7 +363,7 @@ error:
return (error);
}
-static int pcf_start(device_t pcfdev, u_char slave)
+static int pcf_start(device_t pcfdev, u_char slave, int timeout)
{
struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
int error = 0;
@@ -343,12 +378,14 @@ static int pcf_start(device_t pcfdev, u_char slave)
/* START only */
PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
+ pcf->pcf_started = 1;
+
/* wait for address sent, polling */
if ((error = pcf_wait_byte(pcf)))
goto error;
- /* check ACK */
- if (PCF_GET_S1(pcf) & LRB) {
+ /* check for ACK */
+ if (pcf_noack(pcf, timeout)) {
error = IIC_ENOACK;
goto error;
}
@@ -468,19 +505,23 @@ error:
return;
}
-static int pcf_rst_card(device_t pcfdev, u_char speed)
+static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
{
struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
- u_char ownaddr;
+
+ if (oldaddr)
+ *oldaddr = pcf->pcf_addr;
/* retrieve own address from bus level */
- if ((ownaddr = iicbus_get_own_address(pcf->iicbus)) == 0)
- ownaddr = 0xaa;
+ if (!addr)
+ pcf->pcf_addr = PCF_DEFAULT_ADDR;
+ else
+ pcf->pcf_addr = addr;
PCF_SET_S1(pcf, PIN); /* initialize S1 */
/* own address S'O<>0 */
- PCF_SET_S0(pcf, ownaddr >> 1);
+ PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
/* select clock register */
PCF_SET_S1(pcf, PIN|ES1);
@@ -511,7 +552,7 @@ static int pcf_rst_card(device_t pcfdev, u_char speed)
}
static int
-pcf_write(device_t pcfdev, char *buf, int len, int *sent)
+pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
{
struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
int bytes, error = 0;
@@ -525,10 +566,12 @@ pcf_write(device_t pcfdev, char *buf, int len, int *sent)
PCF_SET_S0(pcf, *buf++);
+ /* wait for the byte to be send */
if ((error = pcf_wait_byte(pcf)))
goto error;
- if (PCF_GET_S1(pcf) & LRB) {
+ /* check if ack received */
+ if (pcf_noack(pcf, timeout)) {
error = IIC_ENOACK;
goto error;
}
@@ -549,7 +592,8 @@ error:
}
static int
-pcf_read(device_t pcfdev, char *buf, int len, int *read)
+pcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
+ int delay /* us */)
{
struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
int bytes, error = 0;
@@ -560,7 +604,7 @@ pcf_read(device_t pcfdev, char *buf, int len, int *read)
/* trig the bus to get the first data byte in S0 */
if (len) {
- if (len == 1)
+ if (len == 1 && last)
/* just one byte to read */
PCF_SET_S1(pcf, ES0); /* no ack */
@@ -570,26 +614,25 @@ pcf_read(device_t pcfdev, char *buf, int len, int *read)
bytes = 0;
while (len) {
+ /* XXX delay needed here */
+
+ /* wait for trigged byte */
if ((error = pcf_wait_byte(pcf))) {
pcf_stop(pcfdev);
goto error;
}
- if (len == 1) {
-
- /* ok, last data byte already in S0 */
+ if (len == 1 && last)
+ /* ok, last data byte already in S0, no I2C activity
+ * on next PCF_GET_S0() */
pcf_stop(pcfdev);
- *buf = PCF_GET_S0(pcf);
+ else if (len == 2 && last)
+ /* next trigged byte with no ack */
+ PCF_SET_S1(pcf, ES0);
- } else {
- if (len == 2)
- /* next trigged byte with no ack */
- PCF_SET_S1(pcf, ES0);
-
- /* read last data byte, trig for next data byte */
- *buf++ = PCF_GET_S0(pcf);
- }
+ /* receive byte, trig next byte */
+ *buf++ = PCF_GET_S0(pcf);
len --;
bytes ++;
OpenPOWER on IntegriCloud