2013-05-07 12:10:47 +02:00
|
|
|
/*
|
|
|
|
* \brief Driver for the i.MX53 i2c controller
|
|
|
|
* \author Stefan Kalkowski
|
|
|
|
* \date 2013-03-15
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2015-04-14 13:56:26 +02:00
|
|
|
* Copyright (C) 2013-2015 Genode Labs GmbH
|
2013-05-07 12:10:47 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _I2C_H_
|
|
|
|
#define _I2C_H_
|
|
|
|
|
|
|
|
/* Genode includes */
|
|
|
|
#include <util/mmio.h>
|
|
|
|
#include <timer_session/connection.h>
|
2015-04-14 13:56:26 +02:00
|
|
|
|
|
|
|
/* local includes */
|
|
|
|
#include "irq_handler.h"
|
2013-05-07 12:10:47 +02:00
|
|
|
|
|
|
|
namespace I2c
|
|
|
|
{
|
|
|
|
class I2c;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class I2c::I2c : Genode::Mmio
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
struct Address : public Register<0x0, 8>
|
|
|
|
{
|
|
|
|
struct Addr : Bitfield<1,7> {};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Freq_divider : public Register<0x4, 8> {};
|
|
|
|
|
|
|
|
struct Control : public Register<0x8, 8>
|
|
|
|
{
|
|
|
|
struct Repeat_start : Bitfield<2,1> {};
|
|
|
|
struct Tx_ack_enable : Bitfield<3,1> {};
|
|
|
|
struct Tx_rx_select : Bitfield<4,1> {};
|
|
|
|
struct Master_slave_select : Bitfield<5,1> {};
|
|
|
|
struct Irq_enable : Bitfield<6,1> {};
|
|
|
|
struct Enable : Bitfield<7,1> {};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Status : public Register<0xc, 8>
|
|
|
|
{
|
|
|
|
struct Rcv_ack : Bitfield<0,1> {};
|
|
|
|
struct Irq : Bitfield<1,1> {};
|
|
|
|
struct Slave_rw : Bitfield<2,1> {};
|
|
|
|
struct Arbitration_lost : Bitfield<4,1> {};
|
|
|
|
struct Busy : Bitfield<5,1> {};
|
|
|
|
struct Addressed_as_slave : Bitfield<6,1> {};
|
|
|
|
struct Data_transfer : Bitfield<7,1> {};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Data : public Register<0x10, 8> { };
|
|
|
|
|
|
|
|
|
|
|
|
class No_ack : Genode::Exception {};
|
|
|
|
|
|
|
|
|
2015-04-14 13:56:26 +02:00
|
|
|
Timer::Connection _timer;
|
|
|
|
Irq_handler &_irq_handler;
|
2013-05-07 12:10:47 +02:00
|
|
|
|
|
|
|
void _busy() { while (!read<Status::Busy>()); }
|
|
|
|
|
|
|
|
void _start()
|
|
|
|
{
|
|
|
|
/* clock enable */
|
|
|
|
|
|
|
|
write<Freq_divider>(0x2c);
|
|
|
|
write<Status>(0);
|
|
|
|
write<Control>(Control::Enable::bits(1));
|
|
|
|
|
|
|
|
_timer.msleep(1);
|
|
|
|
|
|
|
|
write<Control::Master_slave_select>(1);
|
|
|
|
|
|
|
|
_busy();
|
|
|
|
|
|
|
|
write<Control>(Control::Tx_rx_select::bits(1) |
|
|
|
|
Control::Tx_ack_enable::bits(1) |
|
|
|
|
Control::Irq_enable::bits(1) |
|
|
|
|
Control::Master_slave_select::bits(1) |
|
|
|
|
Control::Enable::bits(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void _stop()
|
|
|
|
{
|
|
|
|
write<Control>(0);
|
|
|
|
|
|
|
|
/* clock disable */
|
|
|
|
}
|
|
|
|
|
|
|
|
void _write(Genode::uint8_t value)
|
|
|
|
{
|
|
|
|
write<Data>(value);
|
|
|
|
|
2015-04-14 13:56:26 +02:00
|
|
|
do { _irq_handler.wait(); }
|
|
|
|
while (!read<Status::Irq>());
|
|
|
|
|
2013-05-07 12:10:47 +02:00
|
|
|
write<Status::Irq>(0);
|
|
|
|
if (read<Status::Rcv_ack>()) throw No_ack();
|
2015-04-14 13:56:26 +02:00
|
|
|
|
|
|
|
_irq_handler.ack();
|
2013-05-07 12:10:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2015-04-14 13:56:26 +02:00
|
|
|
I2c(Genode::addr_t const base, Irq_handler &irq_handler)
|
|
|
|
: Mmio(base), _irq_handler(irq_handler)
|
2013-05-07 12:10:47 +02:00
|
|
|
{
|
|
|
|
write<Control>(0);
|
|
|
|
write<Status>(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void send(Genode::uint8_t addr, const Genode::uint8_t *buf,
|
|
|
|
Genode::size_t num)
|
|
|
|
{
|
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
_start();
|
|
|
|
|
|
|
|
_write(addr << 1);
|
|
|
|
for (Genode::size_t i = 0; i < num; i++)
|
|
|
|
_write(buf[i]);
|
|
|
|
|
|
|
|
_stop();
|
|
|
|
return;
|
2015-04-14 13:56:26 +02:00
|
|
|
} catch(No_ack) { }
|
2013-05-07 12:10:47 +02:00
|
|
|
_stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void recv(Genode::uint8_t addr, Genode::uint8_t *buf,
|
|
|
|
Genode::size_t num)
|
|
|
|
{
|
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
_start();
|
|
|
|
_write(addr << 1 | 1);
|
|
|
|
write<Control::Tx_rx_select>(0);
|
|
|
|
if (num > 1)
|
|
|
|
write<Control::Tx_ack_enable>(0);
|
|
|
|
read<Data>(); /* dummy read */
|
|
|
|
|
|
|
|
for (Genode::size_t i = 0; i < num; i++) {
|
|
|
|
|
2015-04-14 13:56:26 +02:00
|
|
|
do { _irq_handler.wait(); }
|
|
|
|
while (!read<Status::Irq>());
|
|
|
|
|
2013-05-07 12:10:47 +02:00
|
|
|
write<Status::Irq>(0);
|
|
|
|
|
|
|
|
if (i == num-1) {
|
|
|
|
write<Control::Tx_rx_select>(0);
|
|
|
|
write<Control::Master_slave_select>(0);
|
|
|
|
while (read<Status::Busy>()) ;
|
|
|
|
} else if (i == num-2) {
|
|
|
|
write<Control::Tx_ack_enable>(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[i] = read<Data>();
|
|
|
|
}
|
|
|
|
|
|
|
|
_stop();
|
|
|
|
return;
|
2015-04-14 13:56:26 +02:00
|
|
|
} catch(No_ack) { }
|
2013-05-07 12:10:47 +02:00
|
|
|
_stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* _I2C_H_ */
|