* Separated I2C from DDC a bit more.
* i2c_bus now contains a i2c_timing structure, so that you don't need both to talk to the I2C bus. * Therefore, there is now a void ddc2_init_timing() function to get the the timing DDC needs. * Cleanup in radeon's monitor_detection.c, and updated it to work with the DDC/I2C changes. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22265 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,21 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2003, Thomas Kurschel
|
* Copyright (c) 2003, Thomas Kurschel
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
|
||||||
Part of DDC driver
|
|
||||||
|
|
||||||
Main DDC communication
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DDC_H
|
#ifndef _DDC_H
|
||||||
#define _DDC_H
|
#define _DDC_H
|
||||||
|
|
||||||
|
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "edid.h"
|
#include "edid.h"
|
||||||
|
|
||||||
|
|
||||||
|
void ddc2_init_timing(i2c_bus *bus);
|
||||||
|
|
||||||
// read EDID and VDIF from monitor via ddc2
|
// read EDID and VDIF from monitor via ddc2
|
||||||
// (currently, *vdif and *vdif_len is always set to null)
|
// (currently, *vdif and *vdif_len is always set to null)
|
||||||
status_t ddc2_read_edid1( const i2c_bus *bus, edid1_info *edid,
|
status_t ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
||||||
void **vdif, size_t *vdif_len );
|
void **vdif, size_t *vdifLength);
|
||||||
|
|
||||||
#endif
|
#endif /* _DDC_H */
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2003, Thomas Kurschel
|
* Copyright 2003, Thomas Kurschel. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
Part of DDC driver
|
|
||||||
|
|
||||||
I2C protocoll
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _I2C_H
|
#ifndef _I2C_H
|
||||||
#define _I2C_H
|
#define _I2C_H
|
||||||
|
|
||||||
|
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
// timing for i2c bus
|
// timing for i2c bus
|
||||||
typedef struct i2c_timing {
|
typedef struct i2c_timing {
|
||||||
// general timing as defined by standard
|
// general timing as defined by standard
|
||||||
@@ -39,31 +36,29 @@ typedef struct i2c_timing {
|
|||||||
|
|
||||||
|
|
||||||
// set signals on bus
|
// set signals on bus
|
||||||
typedef status_t (*i2c_set_signals)( void *cookie, int scl, int sda );
|
typedef status_t (*i2c_set_signals)(void *cookie, int clock, int data);
|
||||||
// read signals from bus
|
// read signals from bus
|
||||||
typedef status_t (*i2c_get_signals)( void *cookie, int *scl, int *sda );
|
typedef status_t (*i2c_get_signals)(void *cookie, int *clock, int *data);
|
||||||
|
|
||||||
|
|
||||||
// i2c bus definition
|
// i2c bus definition
|
||||||
typedef struct i2c_bus {
|
typedef struct i2c_bus {
|
||||||
void *cookie; // user-defined cookie
|
void *cookie; // user-defined cookie
|
||||||
|
i2c_timing timing;
|
||||||
i2c_set_signals set_signals; // callback to set signals
|
i2c_set_signals set_signals; // callback to set signals
|
||||||
i2c_get_signals get_signals; // callback to detect signals
|
i2c_get_signals get_signals; // callback to detect signals
|
||||||
} i2c_bus;
|
} i2c_bus;
|
||||||
|
|
||||||
|
|
||||||
// send and receive data via i2c bus
|
// send and receive data via i2c bus
|
||||||
status_t i2c_send_receive( const i2c_bus *bus, const i2c_timing *timing,
|
status_t i2c_send_receive(const i2c_bus *bus, int slave_address,
|
||||||
int slave_address,
|
const uint8 *writeBuffer, size_t writeLength, uint8 *readBuffer,
|
||||||
const uint8 *write_buffer, size_t write_len,
|
size_t readLength);
|
||||||
uint8 *read_buffer, size_t read_len );
|
|
||||||
|
|
||||||
|
|
||||||
// fill <timing> with standard 100kHz bus timing
|
// fill <timing> with standard 100kHz bus timing
|
||||||
void i2c_get100k_timing( i2c_timing *timing );
|
void i2c_get100k_timing(i2c_timing *timing);
|
||||||
|
|
||||||
// fill <timing> with standard 400kHz bus timing
|
// fill <timing> with standard 400kHz bus timing
|
||||||
// (as timing resolution is 1 microsecond, we cannot reach full speed!)
|
// (as timing resolution is 1 microsecond, we cannot reach full speed!)
|
||||||
void i2c_get400k_timing( i2c_timing *timing );
|
void i2c_get400k_timing(i2c_timing *timing);
|
||||||
|
|
||||||
#endif
|
#endif /* _I2C_H */
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Part of DDC driver
|
DDC communication
|
||||||
Main DDC communication
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -56,36 +54,26 @@ verify_checksum(const uint8 *data, size_t len)
|
|||||||
|
|
||||||
//! Read ddc2 data from monitor
|
//! Read ddc2 data from monitor
|
||||||
static status_t
|
static status_t
|
||||||
ddc2_read(const i2c_bus *bus, int start, uint8 *buffer, size_t len)
|
ddc2_read(const i2c_bus *bus, int start, uint8 *buffer, size_t length)
|
||||||
{
|
{
|
||||||
uint8 write_buffer[2];
|
status_t status = B_OK;
|
||||||
i2c_timing timing;
|
uint8 writeBuffer[2];
|
||||||
int i;
|
int i;
|
||||||
status_t res = B_OK;
|
|
||||||
|
|
||||||
write_buffer[0] = start & 0xff;
|
writeBuffer[0] = start & 0xff;
|
||||||
write_buffer[1] = (start >> 8) & 0xff;
|
writeBuffer[1] = (start >> 8) & 0xff;
|
||||||
|
|
||||||
i2c_get100k_timing(&timing);
|
|
||||||
|
|
||||||
timing.start_timeout = 550;
|
|
||||||
timing.byte_timeout = 2200;
|
|
||||||
timing.bit_timeout = 40;
|
|
||||||
timing.ack_start_timeout = 40;
|
|
||||||
timing.ack_timeout = 40;
|
|
||||||
|
|
||||||
for (i = 0; i < READ_RETRIES; ++i) {
|
for (i = 0; i < READ_RETRIES; ++i) {
|
||||||
res = i2c_send_receive(bus, &timing,
|
status = i2c_send_receive(bus, 0xa0, writeBuffer,
|
||||||
0xa0, write_buffer, start < 0x100 ? 1 : 2,
|
start < 0x100 ? 1 : 2, buffer, length);
|
||||||
buffer, len);
|
|
||||||
// don't verify checksum - it's often broken
|
// don't verify checksum - it's often broken
|
||||||
if (res == B_OK /*&& verify_checksum( buffer, len ) == B_OK*/)
|
if (status == B_OK /*&& verify_checksum( buffer, len ) == B_OK*/)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
res = B_ERROR;
|
status = B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -133,30 +121,42 @@ ddc2_read_vdif(const i2c_bus *bus, int start,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ddc2_init_timing(i2c_bus *bus)
|
||||||
|
{
|
||||||
|
i2c_get100k_timing(&bus->timing);
|
||||||
|
|
||||||
|
// VESA standard
|
||||||
|
bus->timing.start_timeout = 550;
|
||||||
|
bus->timing.byte_timeout = 2200;
|
||||||
|
bus->timing.bit_timeout = 40;
|
||||||
|
bus->timing.ack_start_timeout = 40;
|
||||||
|
bus->timing.ack_timeout = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//! Read EDID and VDIF from monitor via ddc2
|
//! Read EDID and VDIF from monitor via ddc2
|
||||||
status_t
|
status_t
|
||||||
ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
|
||||||
void **vdif, size_t *vdif_len)
|
void **vdif, size_t *vdifLength)
|
||||||
{
|
{
|
||||||
status_t res;
|
|
||||||
edid1_raw raw;
|
edid1_raw raw;
|
||||||
|
status_t status = ddc2_read(bus, 0, (uint8 *)&raw, sizeof(raw));
|
||||||
res = ddc2_read(bus, 0, (uint8 *)&raw, sizeof(raw));
|
if (status != B_OK)
|
||||||
if (res != B_OK)
|
return status;
|
||||||
return res;
|
|
||||||
|
|
||||||
edid_decode(edid, &raw);
|
edid_decode(edid, &raw);
|
||||||
|
|
||||||
*vdif = NULL;
|
*vdif = NULL;
|
||||||
*vdif_len = 0;
|
*vdifLength = 0;
|
||||||
|
|
||||||
// skip vdif as long as it's not tested
|
// skip vdif as long as it's not tested
|
||||||
#if 0
|
#if 0
|
||||||
res = ddc2_read_vdif(bus, sizeof(raw) * (edid->num_sections + 1),
|
status = ddc2_read_vdif(bus, sizeof(raw) * (edid->num_sections + 1),
|
||||||
vdif, vdif_len);
|
vdif, vdif_len);
|
||||||
if (res != B_OK)
|
if (status != B_OK)
|
||||||
return res;
|
return status;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,398 +1,30 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2007, Axel Dörfler, [email protected]. All Rights Reserved.
|
||||||
* Copyright 2003, Thomas Kurschel. All Rights Reserved.
|
* Copyright 2003, Thomas Kurschel. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Part of DDC driver
|
I2C protocol
|
||||||
I2C protocoll
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ddc_int.h"
|
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
/*!
|
//#define TRACE_I2C
|
||||||
There's no spin in user space, but we need it to wait a couple
|
#ifdef TRACE_I2C
|
||||||
of microseconds only
|
extern "C" void _sPrintf(const char *format, ...);
|
||||||
(in this case, snooze has much too much overhead)
|
# define TRACE(x...) _sPrintf("I2C: " x)
|
||||||
*/
|
#else
|
||||||
void
|
# define TRACE(x...) ;
|
||||||
spin(bigtime_t delay)
|
#endif
|
||||||
{
|
|
||||||
bigtime_t start_time = system_time();
|
|
||||||
|
|
||||||
while (system_time() - start_time < delay)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Wait until slave releases clock signal ("clock stretching")
|
|
||||||
static status_t
|
|
||||||
wait_for_clk(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
bigtime_t timeout)
|
|
||||||
{
|
|
||||||
bigtime_t start_time;
|
|
||||||
|
|
||||||
// wait for clock signal to raise
|
|
||||||
spin(timing->r);
|
|
||||||
|
|
||||||
start_time = system_time();
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int clk, data;
|
|
||||||
|
|
||||||
bus->get_signals(bus->cookie, &clk, &data);
|
|
||||||
if (clk != 0)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
if (system_time() - start_time > timeout)
|
|
||||||
return B_TIMEOUT;
|
|
||||||
|
|
||||||
spin(timing->r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send start or repeated start condition
|
|
||||||
static status_t
|
|
||||||
send_start_condition(const i2c_bus *bus, const i2c_timing *timing)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
|
|
||||||
res = wait_for_clk(bus, timing, timing->start_timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout sending start condition");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->su_sta);
|
|
||||||
bus->set_signals(bus->cookie, 1, 0);
|
|
||||||
spin(timing->hd_sta);
|
|
||||||
bus->set_signals(bus->cookie, 0, 0);
|
|
||||||
spin(timing->f);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send stop condition
|
|
||||||
static status_t
|
|
||||||
send_stop_condition(const i2c_bus *bus, const i2c_timing *timing)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, 0);
|
|
||||||
spin(timing->r);
|
|
||||||
bus->set_signals(bus->cookie, 1, 0);
|
|
||||||
|
|
||||||
// a slave may wait for us, so let elapse the acknowledge timeout
|
|
||||||
// to make the slave release bus control
|
|
||||||
res = wait_for_clk(bus, timing, timing->ack_timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout sending stop condition");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->su_sto);
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
spin(timing->buf);
|
|
||||||
|
|
||||||
SHOW_FLOW0(3, "");
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send one bit
|
|
||||||
static status_t
|
|
||||||
send_bit(const i2c_bus *bus, const i2c_timing *timing, bool bit, int timeout)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
//SHOW_FLOW( 3, "%d", bit & 1 );
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, bit & 1);
|
|
||||||
spin(timing->su_dat);
|
|
||||||
bus->set_signals(bus->cookie, 1, bit & 1);
|
|
||||||
|
|
||||||
res = wait_for_clk(bus, timing, timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout when sending next bit");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->high);
|
|
||||||
bus->set_signals(bus->cookie, 0, bit & 1);
|
|
||||||
spin(timing->f + timing->low);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send acknowledge and wait for reply
|
|
||||||
static status_t
|
|
||||||
send_acknowledge(const i2c_bus *bus, const i2c_timing *timing)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
bigtime_t start_time;
|
|
||||||
|
|
||||||
// release data so slave can modify it
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
spin(timing->su_dat);
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
|
|
||||||
res = wait_for_clk(bus, timing, timing->ack_start_timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout when sending acknowledge");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// data and clock is high, now wait for slave to pull data low
|
|
||||||
// (according to spec, this can happen any time once clock is high)
|
|
||||||
start_time = system_time();
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int clk, data;
|
|
||||||
|
|
||||||
bus->get_signals(bus->cookie, &clk, &data);
|
|
||||||
|
|
||||||
if (data == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (system_time() - start_time > timing->ack_timeout) {
|
|
||||||
SHOW_FLOW0(3, "Slave didn't acknowledge byte");
|
|
||||||
return B_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin(timing->r);
|
|
||||||
}
|
|
||||||
|
|
||||||
SHOW_FLOW0(4, "Success!");
|
|
||||||
|
|
||||||
// make sure we've waited at least t_high
|
|
||||||
spin(timing->high);
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
spin(timing->f + timing->low);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send byte and wait for acknowledge if <ackowledge> is true
|
|
||||||
static status_t
|
|
||||||
send_byte(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
uint8 byte, bool acknowledge)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
SHOW_FLOW( 3, "%x ", byte );
|
|
||||||
|
|
||||||
for (i = 7; i >= 0; --i) {
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_bit(bus, timing, byte >> i,
|
|
||||||
i == 7 ? timing->byte_timeout : timing->bit_timeout);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acknowledge)
|
|
||||||
return send_acknowledge(bus, timing);
|
|
||||||
else
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send slave address, obeying 10-bit addresses and general call addresses
|
|
||||||
static status_t
|
|
||||||
send_slave_address( const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
int slave_address, bool is_write )
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_byte(bus, timing, (slave_address & 0xfe) | !is_write, true);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
// there are the following special cases if the first byte looks like:
|
|
||||||
// - 0000 0000 - general call address (second byte with address follows)
|
|
||||||
// - 0000 0001 - start byte
|
|
||||||
// - 0000 001x - CBus address
|
|
||||||
// - 0000 010x - address reserved for different bus format
|
|
||||||
// - 0000 011x |
|
|
||||||
// - 0000 1xxx |-> reserved
|
|
||||||
// - 1111 1xxx |
|
|
||||||
// - 1111 0xxx - 10 bit address (second byte contains remaining 8 bits)
|
|
||||||
|
|
||||||
// the lsb is 0 for write and 1 for read (except for general call address)
|
|
||||||
if ((slave_address & 0xff) != 0 && (slave_address & 0xf8) != 0xf0)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
return send_byte(bus, timing, slave_address >> 8, true);
|
|
||||||
// send second byte if required
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Receive one bit
|
|
||||||
static status_t
|
|
||||||
receive_bit(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
bool *bit, int timeout)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
int clk, data;
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 1, 1);
|
|
||||||
// release clock
|
|
||||||
|
|
||||||
// wait for slave to raise clock
|
|
||||||
res = wait_for_clk(bus, timing, timeout);
|
|
||||||
if (res != B_OK) {
|
|
||||||
SHOW_FLOW0(3, "Timeout waiting for bit sent by slave");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bus->get_signals(bus->cookie, &clk, &data);
|
|
||||||
// sample data
|
|
||||||
|
|
||||||
spin(timing->high);
|
|
||||||
// leave clock high for minimal time
|
|
||||||
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
// pull clock low so slave waits for us before next bit
|
|
||||||
|
|
||||||
spin(timing->f + timing->low);
|
|
||||||
// let it settle and leave it low for minimal time
|
|
||||||
// to make sure slave has finished bit transmission too
|
|
||||||
|
|
||||||
*bit = data;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! receive byte
|
|
||||||
Send positive acknowledge afterwards if <acknowledge> is true,
|
|
||||||
else send negative one
|
|
||||||
*/
|
|
||||||
static status_t
|
|
||||||
receive_byte(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
uint8 *res_byte, bool acknowledge)
|
|
||||||
{
|
|
||||||
uint8 byte = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// pull clock low to let slave wait for us
|
|
||||||
bus->set_signals(bus->cookie, 0, 1);
|
|
||||||
|
|
||||||
for (i = 7; i >= 0; --i) {
|
|
||||||
status_t res;
|
|
||||||
bool bit;
|
|
||||||
|
|
||||||
res = receive_bit(bus, timing, &bit,
|
|
||||||
i == 7 ? timing->byte_timeout : timing->bit_timeout);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
byte = (byte << 1) | bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
//SHOW_FLOW( 3, "%x ", byte );
|
|
||||||
|
|
||||||
*res_byte = byte;
|
|
||||||
|
|
||||||
return send_bit(bus, timing, acknowledge ? 0 : 1, timing->bit_timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Send multiple bytes
|
|
||||||
static status_t
|
|
||||||
send_bytes(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
const uint8 *write_buffer, ssize_t write_len)
|
|
||||||
{
|
|
||||||
SHOW_FLOW( 3, "len=%ld", write_len );
|
|
||||||
|
|
||||||
for (; write_len > 0; --write_len, ++write_buffer) {
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_byte(bus, timing, *write_buffer, true);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Receive multiple bytes
|
|
||||||
static status_t
|
|
||||||
receive_bytes(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
uint8 *read_buffer, ssize_t read_len)
|
|
||||||
{
|
|
||||||
SHOW_FLOW(3, "len=%ld", read_len);
|
|
||||||
|
|
||||||
for (; read_len > 0; --read_len, ++read_buffer) {
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = receive_byte(bus, timing, read_buffer, read_len > 1);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Combined i2c send+receive format
|
|
||||||
status_t
|
|
||||||
i2c_send_receive(const i2c_bus *bus, const i2c_timing *timing,
|
|
||||||
int slave_address, const uint8 *write_buffer, size_t write_len,
|
|
||||||
uint8 *read_buffer, size_t read_len)
|
|
||||||
{
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
res = send_start_condition(bus, timing);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = send_slave_address(bus, timing, slave_address, true);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = send_bytes(bus, timing, write_buffer, write_len);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = send_start_condition(bus, timing);
|
|
||||||
if (res != B_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = send_slave_address(bus, timing, slave_address, false);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = receive_bytes(bus, timing, read_buffer, read_len);
|
|
||||||
if (res != B_OK)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
res = send_stop_condition(bus, timing);
|
|
||||||
return res;
|
|
||||||
|
|
||||||
err:
|
|
||||||
SHOW_FLOW0(3, "Cancelling transmission");
|
|
||||||
send_stop_condition(bus, timing);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! Timining for 100kHz bus (fractional parts are rounded up)
|
//! Timining for 100kHz bus (fractional parts are rounded up)
|
||||||
i2c_timing i2c_timing_100k = {
|
const static i2c_timing kTiming100k = {
|
||||||
buf : 5,
|
buf : 5,
|
||||||
hd_sta : 4,
|
hd_sta : 4,
|
||||||
low : 5,
|
low : 5,
|
||||||
@@ -414,7 +46,7 @@ i2c_timing i2c_timing_100k = {
|
|||||||
|
|
||||||
// timing for 400 kHz bus
|
// timing for 400 kHz bus
|
||||||
// (argh! heavy up-rounding here)
|
// (argh! heavy up-rounding here)
|
||||||
i2c_timing i2c_timing_400k = {
|
const static i2c_timing kTiming400k = {
|
||||||
buf : 2,
|
buf : 2,
|
||||||
hd_sta : 1,
|
hd_sta : 1,
|
||||||
low : 2,
|
low : 2,
|
||||||
@@ -426,7 +58,7 @@ i2c_timing i2c_timing_400k = {
|
|||||||
f : 1,
|
f : 1,
|
||||||
su_sto : 1,
|
su_sto : 1,
|
||||||
|
|
||||||
// see i2c_timing_100k
|
// see kTiming100k
|
||||||
start_timeout : 2,
|
start_timeout : 2,
|
||||||
byte_timeout : 2,
|
byte_timeout : 2,
|
||||||
bit_timeout : 2,
|
bit_timeout : 2,
|
||||||
@@ -435,15 +67,373 @@ i2c_timing i2c_timing_400k = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
There's no spin in user space, but we need it to wait a couple
|
||||||
|
of microseconds only
|
||||||
|
(in this case, snooze has much too much overhead)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
spin(bigtime_t delay)
|
||||||
|
{
|
||||||
|
bigtime_t startTime = system_time();
|
||||||
|
|
||||||
|
while (system_time() - startTime < delay)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Wait until slave releases clock signal ("clock stretching")
|
||||||
|
static status_t
|
||||||
|
wait_for_clk(const i2c_bus *bus, bigtime_t timeout)
|
||||||
|
{
|
||||||
|
bigtime_t startTime;
|
||||||
|
|
||||||
|
// wait for clock signal to raise
|
||||||
|
spin(bus->timing.r);
|
||||||
|
|
||||||
|
startTime = system_time();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int clk, data;
|
||||||
|
|
||||||
|
bus->get_signals(bus->cookie, &clk, &data);
|
||||||
|
if (clk != 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
if (system_time() - startTime > timeout)
|
||||||
|
return B_TIMEOUT;
|
||||||
|
|
||||||
|
spin(bus->timing.r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send start or repeated start condition
|
||||||
|
static status_t
|
||||||
|
send_start_condition(const i2c_bus *bus)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
|
||||||
|
status = wait_for_clk(bus, bus->timing.start_timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_start_condition(): Timeout sending start condition\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.su_sta);
|
||||||
|
bus->set_signals(bus->cookie, 1, 0);
|
||||||
|
spin(bus->timing.hd_sta);
|
||||||
|
bus->set_signals(bus->cookie, 0, 0);
|
||||||
|
spin(bus->timing.f);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send stop condition
|
||||||
|
static status_t
|
||||||
|
send_stop_condition(const i2c_bus *bus)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, 0);
|
||||||
|
spin(bus->timing.r);
|
||||||
|
bus->set_signals(bus->cookie, 1, 0);
|
||||||
|
|
||||||
|
// a slave may wait for us, so let elapse the acknowledge timeout
|
||||||
|
// to make the slave release bus control
|
||||||
|
status = wait_for_clk(bus, bus->timing.ack_timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_stop_condition(): Timeout sending stop condition\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.su_sto);
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
spin(bus->timing.buf);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send one bit
|
||||||
|
static status_t
|
||||||
|
send_bit(const i2c_bus *bus, bool bit, int timeout)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
//TRACE("send_bit(bit = %d)\n", bit & 1);
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, bit & 1);
|
||||||
|
spin(bus->timing.su_dat);
|
||||||
|
bus->set_signals(bus->cookie, 1, bit & 1);
|
||||||
|
|
||||||
|
status = wait_for_clk(bus, timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_bit(): Timeout when sending next bit\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.high);
|
||||||
|
bus->set_signals(bus->cookie, 0, bit & 1);
|
||||||
|
spin(bus->timing.f + bus->timing.low);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send acknowledge and wait for reply
|
||||||
|
static status_t
|
||||||
|
send_acknowledge(const i2c_bus *bus)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
bigtime_t startTime;
|
||||||
|
|
||||||
|
// release data so slave can modify it
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
spin(bus->timing.su_dat);
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
|
||||||
|
status = wait_for_clk(bus, bus->timing.ack_start_timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("send_acknowledge(): Timeout when sending acknowledge\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data and clock is high, now wait for slave to pull data low
|
||||||
|
// (according to spec, this can happen any time once clock is high)
|
||||||
|
startTime = system_time();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int clk, data;
|
||||||
|
|
||||||
|
bus->get_signals(bus->cookie, &clk, &data);
|
||||||
|
|
||||||
|
if (data == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (system_time() - startTime > bus->timing.ack_timeout) {
|
||||||
|
TRACE("send_acknowledge(): Slave didn't acknowledge byte\n");
|
||||||
|
return B_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin(bus->timing.r);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("send_acknowledge(): Success!\n");
|
||||||
|
|
||||||
|
// make sure we've waited at least t_high
|
||||||
|
spin(bus->timing.high);
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
spin(bus->timing.f + bus->timing.low);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send byte and wait for acknowledge if <ackowledge> is true
|
||||||
|
static status_t
|
||||||
|
send_byte(const i2c_bus *bus, uint8 byte, bool acknowledge)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//TRACE("send_byte(byte = %x)\n", byte);
|
||||||
|
|
||||||
|
for (i = 7; i >= 0; --i) {
|
||||||
|
status_t status = send_bit(bus, byte >> i,
|
||||||
|
i == 7 ? bus->timing.byte_timeout : bus->timing.bit_timeout);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acknowledge)
|
||||||
|
return send_acknowledge(bus);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send slave address, obeying 10-bit addresses and general call addresses
|
||||||
|
static status_t
|
||||||
|
send_slave_address(const i2c_bus *bus, int slaveAddress, bool isWrite)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
status = send_byte(bus, (slaveAddress & 0xfe) | !isWrite, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// there are the following special cases if the first byte looks like:
|
||||||
|
// - 0000 0000 - general call address (second byte with address follows)
|
||||||
|
// - 0000 0001 - start byte
|
||||||
|
// - 0000 001x - CBus address
|
||||||
|
// - 0000 010x - address reserved for different bus format
|
||||||
|
// - 0000 011x |
|
||||||
|
// - 0000 1xxx |-> reserved
|
||||||
|
// - 1111 1xxx |
|
||||||
|
// - 1111 0xxx - 10 bit address (second byte contains remaining 8 bits)
|
||||||
|
|
||||||
|
// the lsb is 0 for write and 1 for read (except for general call address)
|
||||||
|
if ((slaveAddress & 0xff) != 0 && (slaveAddress & 0xf8) != 0xf0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
return send_byte(bus, slaveAddress >> 8, true);
|
||||||
|
// send second byte if required
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Receive one bit
|
||||||
|
static status_t
|
||||||
|
receive_bit(const i2c_bus *bus, bool *bit, int timeout)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
int clk, data;
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 1, 1);
|
||||||
|
// release clock
|
||||||
|
|
||||||
|
// wait for slave to raise clock
|
||||||
|
status = wait_for_clk(bus, timeout);
|
||||||
|
if (status != B_OK) {
|
||||||
|
TRACE("receive_bit(): Timeout waiting for bit sent by slave\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus->get_signals(bus->cookie, &clk, &data);
|
||||||
|
// sample data
|
||||||
|
|
||||||
|
spin(bus->timing.high);
|
||||||
|
// leave clock high for minimal time
|
||||||
|
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
// pull clock low so slave waits for us before next bit
|
||||||
|
|
||||||
|
spin(bus->timing.f + bus->timing.low);
|
||||||
|
// let it settle and leave it low for minimal time
|
||||||
|
// to make sure slave has finished bit transmission too
|
||||||
|
|
||||||
|
*bit = data;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Send positive acknowledge afterwards if <acknowledge> is true,
|
||||||
|
else send negative one
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
receive_byte(const i2c_bus *bus, uint8 *resultByte, bool acknowledge)
|
||||||
|
{
|
||||||
|
uint8 byte = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// pull clock low to let slave wait for us
|
||||||
|
bus->set_signals(bus->cookie, 0, 1);
|
||||||
|
|
||||||
|
for (i = 7; i >= 0; i--) {
|
||||||
|
bool bit;
|
||||||
|
|
||||||
|
status_t status = receive_bit(bus, &bit,
|
||||||
|
i == 7 ? bus->timing.byte_timeout : bus->timing.bit_timeout);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
byte = (byte << 1) | bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SHOW_FLOW(3, "%x ", byte);
|
||||||
|
|
||||||
|
*resultByte = byte;
|
||||||
|
|
||||||
|
return send_bit(bus, acknowledge ? 0 : 1, bus->timing.bit_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Send multiple bytes
|
||||||
|
static status_t
|
||||||
|
send_bytes(const i2c_bus *bus, const uint8 *writeBuffer, ssize_t writeLength)
|
||||||
|
{
|
||||||
|
TRACE("send_bytes(length = %ld)\n", writeLength);
|
||||||
|
|
||||||
|
for (; writeLength > 0; --writeLength, ++writeBuffer) {
|
||||||
|
status_t status = send_byte(bus, *writeBuffer, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Receive multiple bytes
|
||||||
|
static status_t
|
||||||
|
receive_bytes(const i2c_bus *bus, uint8 *readBuffer, ssize_t readLength)
|
||||||
|
{
|
||||||
|
TRACE("receive_bytes(length = %ld)\n", writeLength);
|
||||||
|
|
||||||
|
for (; readLength > 0; --readLength, ++readBuffer) {
|
||||||
|
status_t status = receive_byte(bus, readBuffer, readLength > 1);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - exported functions
|
||||||
|
|
||||||
|
|
||||||
|
//! Combined i2c send+receive format
|
||||||
|
status_t
|
||||||
|
i2c_send_receive(const i2c_bus *bus, int slaveAddress, const uint8 *writeBuffer,
|
||||||
|
size_t writeLength, uint8 *readBuffer, size_t readLength)
|
||||||
|
{
|
||||||
|
status_t status = send_start_condition(bus);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = send_slave_address(bus, slaveAddress, true);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
status = send_bytes(bus, writeBuffer, writeLength);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
status = send_start_condition(bus);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status = send_slave_address(bus, slaveAddress, false);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
status = receive_bytes(bus, readBuffer, readLength);
|
||||||
|
if (status != B_OK)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return send_stop_condition(bus);
|
||||||
|
|
||||||
|
err:
|
||||||
|
TRACE("i2c_send_receive(): Cancelling transmission\n");
|
||||||
|
send_stop_condition(bus);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
i2c_get100k_timing(i2c_timing *timing)
|
i2c_get100k_timing(i2c_timing *timing)
|
||||||
{
|
{
|
||||||
*timing = i2c_timing_100k;
|
*timing = kTiming100k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
i2c_get400k_timing(i2c_timing *timing)
|
i2c_get400k_timing(i2c_timing *timing)
|
||||||
{
|
{
|
||||||
*timing = i2c_timing_400k;
|
*timing = kTiming400k;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user