zynq: add gpio, i2c and vdma drivers

This commit is contained in:
Mark
2016-03-24 17:03:20 +01:00
committed by Norman Feske
parent 2e7bb650dc
commit 6418f34082
24 changed files with 1734 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
/*
* \brief Zynq Gpio session capability type
* \author Mark Albers
* \date 2015-04-02
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__GPIO_SESSION__CAPABILITY_H_
#define _INCLUDE__GPIO_SESSION__CAPABILITY_H_
#include <base/capability.h>
#include <gpio_session/zynq/gpio_session.h>
namespace Gpio { typedef Genode::Capability<Session> Session_capability; }
#endif /* _INCLUDE__GPIO_SESSION__CAPABILITY_H_ */

View File

@@ -0,0 +1,32 @@
/*
* \brief Client-side Zynq Gpio session interface
* \author Mark Albers
* \date 2015-04-02
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__GPIO_SESSION_H__CLIENT_H_
#define _INCLUDE__GPIO_SESSION_H__CLIENT_H_
#include <gpio_session/zynq/capability.h>
#include <base/rpc_client.h>
namespace Gpio {
struct Session_client : Genode::Rpc_client<Session>
{
explicit Session_client(Session_capability session)
: Genode::Rpc_client<Session>(session) { }
bool write(Genode::uint32_t data, bool isChannel2 = false) { return call<Rpc_write>(data, isChannel2); }
Genode::uint32_t read(bool isChannel2 = false) { return call<Rpc_read>(isChannel2); }
};
}
#endif /* _INCLUDE__GPIO_SESSION_H__CLIENT_H_ */

View File

@@ -0,0 +1,30 @@
/*
* \brief Connection to Zynq Gpio session
* \author Mark Albers
* \date 2015-04-02
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__GPIO_SESSION__CONNECTION_H_
#define _INCLUDE__GPIO_SESSION__CONNECTION_H_
#include <gpio_session/zynq/client.h>
#include <base/connection.h>
namespace Gpio {
struct Connection : Genode::Connection<Session>, Session_client
{
Connection(unsigned gpio_number)
: Genode::Connection<Session>(session("ram_quota=8K, gpio=%zd", gpio_number)),
Session_client(cap()) { }
};
}
#endif /* _INCLUDE__GPIO_SESSION__CONNECTION_H_ */

View File

@@ -0,0 +1,54 @@
/*
* \brief Zynq Gpio session interface
* \author Mark Albers
* \date 2015-04-02
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__GPIO_SESSION__GPIO_SESSION_H_
#define _INCLUDE__GPIO_SESSION__GPIO_SESSION_H_
#include <base/signal.h>
#include <session/session.h>
namespace Gpio {
struct Session : Genode::Session
{
static const char *service_name() { return "Gpio"; }
virtual ~Session() { }
/**
* Write
*
* \param data
*/
virtual bool write(Genode::uint32_t data, bool isChannel2) = 0;
/**
* Read
*
* \return data
*/
virtual Genode::uint32_t read(bool isChannel2) = 0;
/*******************
** RPC interface **
*******************/
GENODE_RPC(Rpc_write, bool, write, Genode::uint32_t, bool);
GENODE_RPC(Rpc_read, Genode::uint32_t, read, bool);
GENODE_RPC_INTERFACE(Rpc_write, Rpc_read);
};
}
#endif /* _INCLUDE__GPIO_SESSION__GPIO_SESSION_H_ */

View File

@@ -0,0 +1,25 @@
/*
* \brief I2C bus driver session capability type
* \author Mark Albers
* \date 2015-04-13
* \author Alexander Tarasikov <alexander.tarasikov@gmail.com>
* \date 2012-09-18
*/
/*
* Copyright (C) 2012 Ksys Labs LLC
* Copyright (C) 2012-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__I2C_SESSION__CAPABILITY_H_
#define _INCLUDE__I2C_SESSION__CAPABILITY_H_
#include <base/capability.h>
#include <i2c_session/zynq/i2c_session.h>
namespace I2C { typedef Genode::Capability<Session> Session_capability; }
#endif /* _INCLUDE__I2C_SESSION__CAPABILITY_H_ */

View File

@@ -0,0 +1,43 @@
/*
* \brief Client-side i2c interface
* \author Mark Albers
* \date 2015-04-13
* \author Alexander Tarasikov <alexander.tarasikov@gmail.com>
* \date 2012-09-18
*/
/*
* Copyright (C) 2012 Ksys Labs LLC
* Copyright (C) 2012-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__I2C_SESSION__CLIENT_H_
#define _INCLUDE__I2C_SESSION__CLIENT_H_
#include <i2c_session/zynq/capability.h>
#include <base/rpc_client.h>
namespace I2C {
struct Session_client : Genode::Rpc_client<Session>
{
explicit Session_client(Session_capability session)
: Genode::Rpc_client<Session>(session) { }
bool read_byte_16bit_reg(Genode::uint8_t adr, Genode::uint16_t reg, Genode::uint8_t *data)
{
return call<Rpc_read_byte_16bit_reg>(adr, reg, data);
}
bool write_16bit_reg(Genode::uint8_t adr, Genode::uint16_t reg,
Genode::uint8_t data)
{
return call<Rpc_write_16bit_reg>(adr, reg, data);
}
};
}
#endif /* _INCLUDE__I2C_SESSION__CLIENT_H_ */

View File

@@ -0,0 +1,34 @@
/*
* \brief Connection to i2c service
* \author Mark Albers
* \author Alexander Tarasikov <alexander.tarasikov@gmail.com>
* \date 2012-09-18
*/
/*
* Copyright (C) 2012 Ksys Labs LLC
* Copyright (C) 2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__I2C_SESSION__CONNECTION_H_
#define _INCLUDE__I2C_SESSION__CONNECTION_H_
#include <i2c_session/zynq/client.h>
#include <base/connection.h>
namespace I2C {
class Connection : public Genode::Connection<Session>,
public Session_client
{
public:
Connection(unsigned int bus_num) :
Genode::Connection<Session>(session("ram_quota=4K, bus=%zd", bus_num)),
Session_client(cap()) { }
};
}
#endif /* _INCLUDE__I2C_SESSION__CONNECTION_H_ */

View File

@@ -0,0 +1,69 @@
/*
* \brief I2C session interface
* \author Mark Albers
* \date 2015-04-13
* \author Alexander Tarasikov <alexander.tarasikov@gmail.com>
* \date 2012-09-18
*/
/*
* Copyright (C) 2012 Ksys Labs LLC
* Copyright (C) 2012-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__I2C_SESSION__I2C_SESSION_H_
#define _INCLUDE__I2C_SESSION__I2C_SESSION_H_
#include <base/signal.h>
#include <session/session.h>
namespace I2C {
struct Session : Genode::Session
{
static const char *service_name() { return "I2C"; }
virtual ~Session() { }
/**
* Read a single byte from a 16 bit register of the device
*
* \param adr the address of the device on the bus
* \param reg the register to read
* \param data the read value
*
*/
virtual bool read_byte_16bit_reg(Genode::uint8_t adr, Genode::uint16_t reg, Genode::uint8_t *data) = 0;
/**
* Write a single data byte to a 16 bit register
*
* \param adr the address of the device on the bus
* \param reg the register to write
* \param data the value to write
*
*/
virtual bool write_16bit_reg(Genode::uint8_t adr, Genode::uint16_t reg,
Genode::uint8_t data) = 0;
/*********************
** RPC declaration **
*********************/
GENODE_RPC(Rpc_read_byte_16bit_reg, bool, read_byte_16bit_reg,
Genode::uint8_t, Genode::uint16_t, Genode::uint8_t*);
GENODE_RPC(Rpc_write_16bit_reg, bool, write_16bit_reg,
Genode::uint8_t, Genode::uint16_t, Genode::uint8_t);
GENODE_RPC_INTERFACE(
Rpc_read_byte_16bit_reg,
Rpc_write_16bit_reg
);
};
}
#endif /* _INCLUDE__I2C_SESSION__I2C_SESSION_H_ */

View File

@@ -0,0 +1,22 @@
/*
* \brief Zynq VDMA session capability type
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VDMA_SESSION__CAPABILITY_H_
#define _INCLUDE__VDMA_SESSION__CAPABILITY_H_
#include <base/capability.h>
#include <vdma_session/zynq/vdma_session.h>
namespace Vdma { typedef Genode::Capability<Session> Session_capability; }
#endif /* _INCLUDE__VDMA_SESSION__CAPABILITY_H_ */

View File

@@ -0,0 +1,42 @@
/*
* \brief Client-side Zynq VDMA session interface
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VDMA_SESSION_H__CLIENT_H_
#define _INCLUDE__VDMA_SESSION_H__CLIENT_H_
#include <vdma_session/zynq/capability.h>
#include <base/rpc_client.h>
#define S2MM false
#define MM2S true
namespace Vdma {
struct Session_client : Genode::Rpc_client<Session>
{
explicit Session_client(Session_capability session)
: Genode::Rpc_client<Session>(session) { }
bool setConfig(Genode::uint32_t data, bool isMM2S) { return call<Rpc_setConfig>(data, isMM2S); }
bool setStride(Genode::uint16_t data, bool isMM2S) { return call<Rpc_setStride>(data, isMM2S); }
bool setWidth(Genode::uint16_t data, bool isMM2S) { return call<Rpc_setWidth>(data, isMM2S); }
bool setHeight(Genode::uint16_t data, bool isMM2S) { return call<Rpc_setHeight>(data, isMM2S); }
bool setAddr(Genode::uint32_t data, bool isMM2S) { return call<Rpc_setAddr>(data, isMM2S); }
};
}
#endif /* _INCLUDE__VDMA_SESSION_H__CLIENT_H_ */

View File

@@ -0,0 +1,30 @@
/*
* \brief Connection to Zynq VDMA session
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VDMA_SESSION__CONNECTION_H_
#define _INCLUDE__VDMA_SESSION__CONNECTION_H_
#include <vdma_session/zynq/client.h>
#include <base/connection.h>
namespace Vdma {
struct Connection : Genode::Connection<Session>, Session_client
{
Connection(unsigned vdma_number)
: Genode::Connection<Session>(session("ram_quota=8K, vdma=%zd", vdma_number)),
Session_client(cap()) { }
};
}
#endif /* _INCLUDE__VDMA_SESSION__CONNECTION_H_ */

View File

@@ -0,0 +1,54 @@
/*
* \brief Zynq VDMA session interface
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VDMA_SESSION__VDMA_SESSION_H_
#define _INCLUDE__VDMA_SESSION__VDMA_SESSION_H_
#include <base/signal.h>
#include <session/session.h>
namespace Vdma {
struct Session : Genode::Session
{
static const char *service_name() { return "Vdma"; }
virtual ~Session() { }
virtual bool setConfig(Genode::uint32_t data, bool isMM2S) = 0;
virtual bool setStride(Genode::uint16_t data, bool isMM2S) = 0;
virtual bool setWidth(Genode::uint16_t data, bool isMM2S) = 0;
virtual bool setHeight(Genode::uint16_t data, bool isMM2S) = 0;
virtual bool setAddr(Genode::uint32_t data, bool isMM2S) = 0;
/*******************
** RPC interface **
*******************/
GENODE_RPC(Rpc_setConfig, bool, setConfig, Genode::uint32_t, bool);
GENODE_RPC(Rpc_setStride, bool, setStride, Genode::uint16_t, bool);
GENODE_RPC(Rpc_setWidth, bool, setWidth, Genode::uint16_t, bool);
GENODE_RPC(Rpc_setHeight, bool, setHeight, Genode::uint16_t, bool);
GENODE_RPC(Rpc_setAddr, bool, setAddr, Genode::uint32_t, bool);
GENODE_RPC_INTERFACE(Rpc_setConfig, Rpc_setStride, Rpc_setWidth,
Rpc_setHeight, Rpc_setAddr);
};
}
#endif /* _INCLUDE__VDMA_SESSION__VDMA_SESSION_H_ */

View File

@@ -0,0 +1,76 @@
/*
* \brief Gpio Driver for Zynq
* \author Mark Albers
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DRIVER_H_
#define _DRIVER_H_
#include <io_mem_session/connection.h>
#include <timer_session/connection.h>
#include <util/mmio.h>
#include <vector>
#include <new>
#include <drivers/board_base.h>
#include "gpio.h"
namespace Gpio {
using namespace Genode;
class Driver;
}
class Gpio::Driver
{
private:
std::vector<Zynq_Gpio*> _gpio_bank;
Driver(std::vector<Genode::addr_t> addr)
{
for (unsigned i = 0; i < addr.size(); i++)
{
_gpio_bank.push_back(new Zynq_Gpio(addr[i], Genode::Board_base::GPIO_MMIO_SIZE));
}
}
~Driver()
{
for (std::vector<Zynq_Gpio*>::iterator it = _gpio_bank.begin() ; it != _gpio_bank.end(); ++it)
{
delete (*it);
}
_gpio_bank.clear();
}
public:
static Driver& factory(std::vector<Genode::addr_t> addr);
Genode::uint8_t read(unsigned gpio, bool isChannel2)
{
Zynq_Gpio *gpio_reg = _gpio_bank[gpio];
return gpio_reg->gpio_read(isChannel2);
}
bool write(unsigned gpio, Genode::uint8_t data, bool isChannel2)
{
Zynq_Gpio *gpio_reg = _gpio_bank[gpio];
return gpio_reg->gpio_write(data, isChannel2);
}
};
Gpio::Driver& Gpio::Driver::factory(std::vector<Genode::addr_t> addr)
{
static Gpio::Driver driver(addr);
return driver;
}
#endif /* _DRIVER_H_ */

View File

@@ -0,0 +1,98 @@
/*
* \brief Gpio Driver for Zynq
* \author Mark Albers
* \date 2015-04-02
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _GPIO_H_
#define _GPIO_H_
#include <os/attached_io_mem_dataspace.h>
#include <util/mmio.h>
namespace Gpio {
using namespace Genode;
class Zynq_Gpio;
}
struct Gpio::Zynq_Gpio : Attached_io_mem_dataspace, Mmio
{
Zynq_Gpio(Genode::addr_t const mmio_base, Genode::size_t const mmio_size) :
Genode::Attached_io_mem_dataspace(mmio_base, mmio_size),
Genode::Mmio((Genode::addr_t)local_addr<void>())
{ }
~Zynq_Gpio()
{ }
/*
* Registers
*/
struct GPIO_DATA : Register<0x00, 32> {};
struct GPIO_TRI : Register<0x04, 32> {};
struct GPIO2_DATA : Register<0x08, 32> {};
struct GPIO2_TRI : Register<0x0C, 32> {};
struct GIER : Register<0x011C, 32>
{
struct Global_Interrupt_Enable : Bitfield<31,1> {};
};
struct IP_IER : Register<0x0128, 32>
{
struct Channel_1_Interrupt_Enable : Bitfield<0,1> {};
struct Channel_2_Interrupt_Enable : Bitfield<1,1> {};
};
struct IP_ISR : Register<0x120, 32>
{
struct Channel_1_Interrupt_Status : Bitfield<0,1> {};
struct Channel_2_Interrupt_Status : Bitfield<1,1> {};
};
/*
* Functions
*/
Genode::uint8_t gpio_read(bool isChannel2)
{
if (isChannel2)
{
write<GPIO2_TRI>(0xffffffff);
return read<GPIO2_DATA>();
}
else
{
write<GPIO_TRI>(0xffffffff);
return read<GPIO_DATA>();
}
}
bool gpio_write(Genode::uint8_t data, bool isChannel2)
{
if (isChannel2)
{
write<GPIO2_TRI>(0);
write<GPIO2_DATA>(data);
}
else
{
write<GPIO_TRI>(0);
write<GPIO_DATA>(data);
}
return true;
}
};
#endif // _GPIO_H_

View File

@@ -0,0 +1,132 @@
/*
* \brief Gpio Driver for Zynq
* \author Mark Albers
* \date 2015-03-30
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <gpio_session/zynq/gpio_session.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <root/component.h>
#include <os/static_root.h>
#include <os/config.h>
#include <vector>
#include "driver.h"
namespace Gpio {
using namespace Genode;
class Session_component;
class Root;
};
class Gpio::Session_component : public Genode::Rpc_object<Gpio::Session>
{
private:
Driver &_driver;
unsigned _number;
public:
Session_component(Driver &driver, unsigned gpio_number) : _driver(driver), _number(gpio_number) {}
virtual Genode::uint32_t read(bool isChannel2)
{
return _driver.read(_number, isChannel2);
}
virtual bool write(Genode::uint32_t data, bool isChannel2)
{
return _driver.write(_number, data, isChannel2);
}
};
class Gpio::Root : public Genode::Root_component<Gpio::Session_component>
{
private:
Driver &_driver;
protected:
Session_component *_create_session(const char *args)
{
unsigned number = Genode::Arg_string::find_arg(args, "gpio").ulong_value(0);
Genode::size_t ram_quota = Genode::Arg_string::find_arg(args, "ram_quota").ulong_value(0);
if (ram_quota < sizeof(Session_component)) {
PWRN("Insufficient dontated ram_quota (%zd bytes), require %zd bytes",
ram_quota, sizeof(Session_component));
throw Genode::Root::Quota_exceeded();
}
return new (md_alloc()) Session_component(_driver, number);
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc, Driver &driver)
: Genode::Root_component<Gpio::Session_component>(session_ep, md_alloc),
_driver(driver) { }
};
int main(int, char **)
{
using namespace Gpio;
PINF("Zynq Gpio driver");
/*
* Read config
*/
std::vector<Genode::addr_t> addr;
try {
Genode::Xml_node gpio_node = Genode::config()->xml_node().sub_node("gpio");
for (unsigned i = 0; ;i++, gpio_node = gpio_node.next("gpio"))
{
addr.push_back(0);
gpio_node.attribute("addr").value(&addr[i]);
PINF("Gpio with mio address 0x%x added.", (unsigned int)addr[i]);
if (gpio_node.is_last("gpio")) break;
}
}
catch (Genode::Xml_node::Nonexistent_sub_node) {
PWRN("No Gpio config");
}
/*
* Create Driver
*/
Driver &driver = Driver::factory(addr);
addr.clear();
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
static Rpc_entrypoint ep(&cap, STACK_SIZE, "gpio_ep");
static Gpio::Root gpio_root(&ep, &sliced_heap, driver);
/*
* Announce service
*/
env()->parent()->announce(ep.manage(&gpio_root));
Genode::sleep_forever();
return 0;
}

View File

@@ -0,0 +1,12 @@
# \brief Gpio specific for zynq systems
# \author Mark Albers
# \date 2015-03-30
TARGET = zynq_gpio_drv
SRC_CC = main.cc
LIBS = base config libc libm stdcxx
INC_DIR += $(PRG_DIR)
vpath main.cc $(PRG_DIR)

View File

@@ -0,0 +1,100 @@
/*
* \brief I2C Driver for Zynq
* \author Mark Albers
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DRIVER_H_
#define _DRIVER_H_
#include <io_mem_session/connection.h>
#include <timer_session/connection.h>
#include <util/mmio.h>
#include <drivers/board_base.h>
#include "i2c.h"
namespace I2C {
using namespace Genode;
class Driver;
}
class I2C::Driver
{
private:
class I2C_bank
{
private:
Zynq_I2C _i2c;
public:
I2C_bank(Genode::addr_t base, Genode::size_t size) : _i2c(base, size) { }
Zynq_I2C* regs() { return &_i2c; }
};
static I2C_bank _i2c_bank[2];
Driver() {}
~Driver() {}
public:
static Driver& factory();
bool read_byte_16bit_reg(unsigned bus, Genode::uint8_t adr, Genode::uint16_t reg, Genode::uint8_t *data)
{
Zynq_I2C *i2c_reg = _i2c_bank[bus].regs();
Genode::uint8_t buf[2];
buf[0]=reg >> 8;
buf[1]=reg;
if (i2c_reg->i2c_write(adr, buf, 2) != 0)
{
PERR("Zynq i2c: read failed");
return false;
}
if (i2c_reg->i2c_read_byte(adr, data) != 0)
{
PERR("Zynq i2c: read failed");
return false;
}
return true;
}
bool write_16bit_reg(unsigned bus, Genode::uint8_t adr, Genode::uint16_t reg,
Genode::uint8_t data)
{
Zynq_I2C *i2c_reg = _i2c_bank[bus].regs();
Genode::uint8_t buf[3];
buf[0]=reg >> 8;
buf[1]=reg;
buf[2]=data;
if (i2c_reg->i2c_write(adr, buf, 3) != 0)
{
PERR("Zynq i2c: write failed");
return false;
}
return true;
}
};
I2C::Driver::I2C_bank I2C::Driver::_i2c_bank[2] = {
{Genode::Board_base::I2C0_MMIO_BASE, Genode::Board_base::I2C_MMIO_SIZE},
{Genode::Board_base::I2C1_MMIO_BASE, Genode::Board_base::I2C_MMIO_SIZE},
};
I2C::Driver& I2C::Driver::factory()
{
static I2C::Driver driver;
return driver;
}
#endif /* _DRIVER_H_ */

View File

@@ -0,0 +1,333 @@
/*
* \brief I2C Driver for Zynq
* \author Mark Albers
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* 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_
#include <os/attached_io_mem_dataspace.h>
#include <util/mmio.h>
/* Transfer direction */
#define SENDING 0
#define RECEIVING 1
/* Interrupt masks */
#define INTERRUPT_ARB_LOST_MASK 0x00000200
#define INTERRUPT_RX_UNF_MASK 0x00000080
#define INTERRUPT_TX_OVR_MASK 0x00000040
#define INTERRUPT_RX_OVR_MASK 0x00000020
#define INTERRUPT_SLV_RDY_MASK 0x00000010
#define INTERRUPT_TO_MASK 0x00000008
#define INTERRUPT_NACK_MASK 0x00000004
#define INTERRUPT_DATA_MASK 0x00000002
#define INTERRUPT_COMP_MASK 0x00000001
#define ALL_INTERRUPTS_MASK 0x000002FF
/* Maximal transfer size */
#define I2C_MAX_TRANSFER_SIZE 252
/* FIFO size */
#define I2C_FIFO_DEPTH 16
/* Number of bytes at data intr */
#define I2C_DATA_INTR_DEPTH 14
namespace I2C {
using namespace Genode;
class Zynq_I2C;
}
struct I2C::Zynq_I2C : Attached_io_mem_dataspace, Mmio
{
Zynq_I2C(Genode::addr_t const mmio_base, Genode::size_t const mmio_size) :
Genode::Attached_io_mem_dataspace(mmio_base, mmio_size),
Genode::Mmio((Genode::addr_t)local_addr<void>())
{
}
~Zynq_I2C()
{
}
/*
* Registers
*/
struct Control : Register<0x0, 16>
{
struct divisor_a : Bitfield<14,2> {};
struct divisor_b : Bitfield<8,6> {};
struct CLR_FIFO : Bitfield<6,1> {};
struct SLVMON : Bitfield<5,1> {};
struct HOLD : Bitfield<4,1> {};
struct ACK_EN : Bitfield<3,1> {};
struct NEA : Bitfield<2,1> {};
struct MS : Bitfield<1,1> {};
struct RW : Bitfield<0,1> {};
};
struct Status : Register<0x4, 16>
{
struct BA : Bitfield<8,1> {};
struct RXOVF : Bitfield<7,1> {};
struct TXDV : Bitfield<6,1> {};
struct RXDV : Bitfield<5,1> {};
struct RXRW : Bitfield<3,1> {};
};
struct I2C_address : Register<0x8, 16>
{
struct ADD : Bitfield<0,10> {};
};
struct I2C_data : Register<0xC, 16>
{
struct DATA : Bitfield<0,8> {};
};
struct Interrupt_status : Register<0x10, 16>
{
struct ARB_LOST : Bitfield<9,1> {};
struct RX_UNF : Bitfield<7,1> {};
struct TX_OVF : Bitfield<6,1> {};
struct RX_OVF : Bitfield<5,1> {};
struct SLV_RDY : Bitfield<4,1> {};
struct TO : Bitfield<3,1> {};
struct NACK : Bitfield<2,1> {};
struct DATA : Bitfield<1,1> {};
struct COMP : Bitfield<0,1> {};
};
struct Transfer_size : Register<0x14, 8>
{
struct SIZE : Bitfield<0,8> {};
};
struct Slave_mon_pause : Register<0x18, 8>
{
struct PAUSE : Bitfield<0,4> {};
};
struct Time_out : Register<0x1C, 8>
{
struct TO : Bitfield<0,8> {};
};
struct Interrupt_mask : Register<0x20, 16>
{
struct ARB_LOST : Bitfield<9,1> {};
struct RX_UNF : Bitfield<7,1> {};
struct TX_OVF : Bitfield<6,1> {};
struct RX_OVF : Bitfield<5,1> {};
struct SLV_RDY : Bitfield<4,1> {};
struct TO : Bitfield<3,1> {};
struct NACK : Bitfield<2,1> {};
struct DATA : Bitfield<1,1> {};
struct COMP : Bitfield<0,1> {};
};
struct Interrupt_enable : Register<0x24, 16>
{
struct ARB_LOST : Bitfield<9,1> {};
struct RX_UNF : Bitfield<7,1> {};
struct TX_OVF : Bitfield<6,1> {};
struct RX_OVF : Bitfield<5,1> {};
struct SLV_RDY : Bitfield<4,1> {};
struct TO : Bitfield<3,1> {};
struct NACK : Bitfield<2,1> {};
struct DATA : Bitfield<1,1> {};
struct COMP : Bitfield<0,1> {};
};
struct Interrupt_disable : Register<0x28, 16>
{
struct ARB_LOST : Bitfield<9,1> {};
struct RX_UNF : Bitfield<7,1> {};
struct TX_OVF : Bitfield<6,1> {};
struct RX_OVF : Bitfield<5,1> {};
struct SLV_RDY : Bitfield<4,1> {};
struct TO : Bitfield<3,1> {};
struct NACK : Bitfield<2,1> {};
struct DATA : Bitfield<1,1> {};
struct COMP : Bitfield<0,1> {};
};
Timer::Connection _timer;
int sendByteCount;
uint8_t *sendBufferPtr;
void init(int direction)
{
write<Control>( Control::divisor_a::bits(2) |
Control::divisor_b::bits(16) |
Control::ACK_EN::bits(1) |
Control::CLR_FIFO::bits(1) |
Control::NEA::bits(1) |
Control::MS::bits(1));
write<Control::RW>(direction);
}
void transmitFifoFill()
{
uint8_t availBytes;
int loopCnt;
int numBytesToSend;
/*
* Determine number of bytes to write to FIFO.
*/
availBytes = I2C_FIFO_DEPTH - read<Transfer_size::SIZE>();
numBytesToSend = sendByteCount > availBytes ? availBytes : sendByteCount;
/*
* Fill FIFO with amount determined above.
*/
for (loopCnt = 0; loopCnt < numBytesToSend; loopCnt++)
{
write<I2C_data::DATA>(*sendBufferPtr);
sendBufferPtr++;
sendByteCount--;
}
}
int i2c_write(uint8_t slaveAddr, uint8_t *msgPtr, int byteCount)
{
uint32_t intrs, intrStatusReg;
sendByteCount = byteCount;
sendBufferPtr = msgPtr;
/*
* Set HOLD bit if byteCount is bigger than FIFO.
*/
if (byteCount > I2C_FIFO_DEPTH) write<Control::HOLD>(1);
/*
* Init sending master.
*/
init(SENDING);
/*
* intrs keeps all the error-related interrupts.
*/
intrs = INTERRUPT_ARB_LOST_MASK | INTERRUPT_TX_OVR_MASK | INTERRUPT_NACK_MASK;
/*
* Clear the interrupt status register before use it to monitor.
*/
write<Interrupt_status>(read<Interrupt_status>());
/*
* Transmit first FIFO full of data.
*/
transmitFifoFill();
write<I2C_address::ADD>(slaveAddr);
intrStatusReg = read<Interrupt_status>();
/*
* Continue sending as long as there is more data and there are no errors.
*/
while ((sendByteCount > 0) && ((intrStatusReg & intrs) == 0))
{
/*
* Wait until transmit FIFO is empty.
*/
if (read<Status::TXDV>() != 0)
{
intrStatusReg = read<Interrupt_status>();
continue;
}
/*
* Send more data out through transmit FIFO.
*/
transmitFifoFill();
}
/*
* Check for completion of transfer.
*/
while ((intrStatusReg & INTERRUPT_COMP_MASK) != INTERRUPT_COMP_MASK)
{
intrStatusReg = read<Interrupt_status>();
/*
* If there is an error, tell the caller.
*/
if ((intrStatusReg & intrs) != 0)
{
return 1;
}
}
write<Control::HOLD>(0);
return 0;
}
int i2c_read_byte(uint8_t slaveAddr, uint8_t *msgPtr)
{
uint32_t intrs, intrStatusReg;
/*
* Init receiving master.
*/
init(RECEIVING);
/*
* Clear the interrupt status register before use it to monitor.
*/
write<Interrupt_status>(read<Interrupt_status>());
/*
* Set up the transfer size register so the slave knows how much
* to send to us.
*/
write<Transfer_size::SIZE>(1);
/*
* Set slave address.
*/
write<I2C_address::ADD>(slaveAddr);
/*
* intrs keeps all the error-related interrupts.
*/
intrs = INTERRUPT_ARB_LOST_MASK | INTERRUPT_RX_OVR_MASK |
INTERRUPT_RX_UNF_MASK | INTERRUPT_NACK_MASK;
/*
* Poll the interrupt status register to find the errors.
*/
intrStatusReg = read<Interrupt_status>();
while (read<Status::RXDV>() != 1)
{
if (intrStatusReg & intrs) return 1;
}
if (intrStatusReg & intrs) return 1;
*msgPtr = read<I2C_data::DATA>();
while (read<Interrupt_status::COMP>() != 1) {}
return 0;
}
};
#endif // _I2C_H_

View File

@@ -0,0 +1,102 @@
/*
* \brief I2C Driver for Zynq
* \author Mark Albers
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <i2c_session/i2c_session.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <root/component.h>
#include <os/static_root.h>
#include <os/config.h>
#include "driver.h"
namespace I2C {
using namespace Genode;
class Session_component;
class Root;
};
class I2C::Session_component : public Genode::Rpc_object<I2C::Session>
{
private:
Driver &_driver;
unsigned int _bus;
Genode::Signal_context_capability _sigh;
public:
Session_component(Driver &driver, unsigned int bus_num) : _driver(driver), _bus(bus_num) {}
virtual bool read_byte_16bit_reg(Genode::uint8_t adr, Genode::uint16_t reg, Genode::uint8_t *data)
{
return _driver.read_byte_16bit_reg(_bus, adr, reg, data);
}
virtual bool write_16bit_reg(Genode::uint8_t adr, Genode::uint16_t reg,
Genode::uint8_t data)
{
return _driver.write_16bit_reg(_bus, adr, reg, data);
}
};
class I2C::Root : public Genode::Root_component<I2C::Session_component>
{
private:
Driver &_driver;
protected:
Session_component *_create_session(const char *args)
{
unsigned int bus = Genode::Arg_string::find_arg(args, "bus").ulong_value(0);
return new (md_alloc()) Session_component(_driver, bus);
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc, Driver &driver)
: Genode::Root_component<I2C::Session_component>(session_ep, md_alloc),
_driver(driver) { }
};
int main(int, char **)
{
using namespace I2C;
PINF("Zynq I2C driver");
Driver &driver = Driver::factory();
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
static Rpc_entrypoint ep(&cap, STACK_SIZE, "i2c_ep");
static I2C::Root i2c_root(&ep, &sliced_heap, driver);
/*
* Announce service
*/
env()->parent()->announce(ep.manage(&i2c_root));
Genode::sleep_forever();
return 0;
}

View File

@@ -0,0 +1,12 @@
# \brief I2C specific for zynq systems
# \author Mark Albers
# \date 2015-03-12
TARGET = zynq_i2c_drv
SRC_CC = main.cc
LIBS = base config
INC_DIR += $(PRG_DIR)
vpath main.cc $(PRG_DIR)

View File

@@ -0,0 +1,105 @@
/*
* \brief VDMA Driver for Zynq
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DRIVER_H_
#define _DRIVER_H_
#include <io_mem_session/connection.h>
#include <timer_session/connection.h>
#include <util/mmio.h>
#include <vector>
#include <new>
#include <drivers/board_base.h>
#include "vdma.h"
namespace Vdma {
using namespace Genode;
class Driver;
}
class Vdma::Driver
{
private:
std::vector<Zynq_Vdma*> _vdma_bank;
Driver(std::vector<Genode::addr_t> addr)
{
for (unsigned i = 0; i < addr.size(); i++)
{
_vdma_bank.push_back(new Zynq_Vdma(addr[i], Board_base::VDMA_MMIO_SIZE));
}
}
~Driver()
{
for (std::vector<Zynq_Vdma*>::iterator it = _vdma_bank.begin() ; it != _vdma_bank.end(); ++it)
{
delete (*it);
}
_vdma_bank.clear();
}
public:
static Driver& factory(std::vector<Genode::addr_t> addr);
bool setConfig(unsigned vdma, Genode::uint32_t data, bool isMM2S)
{
Zynq_Vdma *vdma_reg = _vdma_bank[vdma];
if (isMM2S) vdma_reg->write<Zynq_Vdma::MM2S_VDMACR>(data);
else vdma_reg->write<Zynq_Vdma::S2MM_VDMACR>(data);
return true;
}
bool setStride(unsigned vdma, Genode::uint16_t data, bool isMM2S)
{
Zynq_Vdma *vdma_reg = _vdma_bank[vdma];
if (isMM2S) vdma_reg->write<Zynq_Vdma::MM2S_FRMDLY_STRIDE>(data);
else vdma_reg->write<Zynq_Vdma::S2MM_FRMDLY_STRIDE>(data);
return true;
}
bool setWidth(unsigned vdma, Genode::uint16_t data, bool isMM2S)
{
Zynq_Vdma *vdma_reg = _vdma_bank[vdma];
if (isMM2S) vdma_reg->write<Zynq_Vdma::MM2S_HSIZE>(data);
else vdma_reg->write<Zynq_Vdma::S2MM_HSIZE>(data);
return true;
}
bool setHeight(unsigned vdma, Genode::uint16_t data, bool isMM2S)
{
Zynq_Vdma *vdma_reg = _vdma_bank[vdma];
if (isMM2S) vdma_reg->write<Zynq_Vdma::MM2S_VSIZE>(data);
else vdma_reg->write<Zynq_Vdma::S2MM_VSIZE>(data);
return true;
}
bool setAddr(unsigned vdma, Genode::uint32_t data, bool isMM2S)
{
Zynq_Vdma *vdma_reg = _vdma_bank[vdma];
if (isMM2S) vdma_reg->write<Zynq_Vdma::MM2S_START_ADDRESS>(data);
else vdma_reg->write<Zynq_Vdma::S2MM_START_ADDRESS>(data);
return true;
}
};
Vdma::Driver& Vdma::Driver::factory(std::vector<Genode::addr_t> addr)
{
static Vdma::Driver driver(addr);
return driver;
}
#endif /* _DRIVER_H_ */

View File

@@ -0,0 +1,147 @@
/*
* \brief VDMA Driver for Zynq
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <vdma_session/vdma_session.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <root/component.h>
#include <os/static_root.h>
#include <os/config.h>
#include <vector>
#include "driver.h"
namespace Vdma {
using namespace Genode;
class Session_component;
class Root;
};
class Vdma::Session_component : public Genode::Rpc_object<Vdma::Session>
{
private:
Driver &_driver;
unsigned _number;
public:
Session_component(Driver &driver, unsigned vdma_number) : _driver(driver), _number(vdma_number) {}
virtual bool setConfig(Genode::uint32_t data, bool isMM2S)
{
return _driver.setConfig(_number, data, isMM2S);
}
virtual bool setStride(Genode::uint16_t data, bool isMM2S)
{
return _driver.setStride(_number, data, isMM2S);
}
virtual bool setWidth(Genode::uint16_t data, bool isMM2S)
{
return _driver.setWidth(_number, data, isMM2S);
}
virtual bool setHeight(Genode::uint16_t data, bool isMM2S)
{
return _driver.setHeight(_number, data, isMM2S);
}
virtual bool setAddr(Genode::uint32_t data, bool isMM2S)
{
return _driver.setAddr(_number, data, isMM2S);
}
};
class Vdma::Root : public Genode::Root_component<Vdma::Session_component>
{
private:
Driver &_driver;
protected:
Session_component *_create_session(const char *args)
{
unsigned number = Genode::Arg_string::find_arg(args, "vdma").ulong_value(0);
Genode::size_t ram_quota = Genode::Arg_string::find_arg(args, "ram_quota").ulong_value(0);
if (ram_quota < sizeof(Session_component)) {
PWRN("Insufficient dontated ram_quota (%zd bytes), require %zd bytes",
ram_quota, sizeof(Session_component));
throw Genode::Root::Quota_exceeded();
}
return new (md_alloc()) Session_component(_driver, number);
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc, Driver &driver)
: Genode::Root_component<Vdma::Session_component>(session_ep, md_alloc),
_driver(driver) { }
};
int main(int, char **)
{
using namespace Vdma;
PINF("Zynq VDMA driver");
/*
* Read config
*/
std::vector<Genode::addr_t> addr;
try {
Genode::Xml_node vdma_node = Genode::config()->xml_node().sub_node("vdma");
for (unsigned i = 0; ;i++, vdma_node = vdma_node.next("vdma"))
{
addr.push_back(0);
vdma_node.attribute("addr").value(&addr[i]);
PINF("VDMA with mio address 0x%x added.", (unsigned int)addr[i]);
if (vdma_node.is_last("vdma")) break;
}
}
catch (Genode::Xml_node::Nonexistent_sub_node) {
PWRN("No VDMA config");
}
/*
* Create Driver
*/
Driver &driver = Driver::factory(addr);
addr.clear();
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
static Rpc_entrypoint ep(&cap, STACK_SIZE, "vdma_ep");
static Vdma::Root vdma_root(&ep, &sliced_heap, driver);
/*
* Announce service
*/
env()->parent()->announce(ep.manage(&vdma_root));
Genode::sleep_forever();
return 0;
}

View File

@@ -0,0 +1,12 @@
# \brief VDMA specific for zynq systems
# \author Mark Albers
# \date 2015-04-13
TARGET = zynq_vdma_drv
SRC_CC = main.cc
LIBS = base config libc libm stdcxx
INC_DIR += $(PRG_DIR)
vpath main.cc $(PRG_DIR)

View File

@@ -0,0 +1,148 @@
/*
* \brief VDMA Driver for Zynq
* \author Mark Albers
* \date 2015-04-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _VDMA_H_
#define _VDMA_H_
#include <os/attached_io_mem_dataspace.h>
#include <util/mmio.h>
namespace Vdma {
using namespace Genode;
class Zynq_Vdma;
}
struct Vdma::Zynq_Vdma : Attached_io_mem_dataspace, Mmio
{
Zynq_Vdma(Genode::addr_t const mmio_base, Genode::size_t const mmio_size) :
Genode::Attached_io_mem_dataspace(mmio_base, mmio_size),
Genode::Mmio((Genode::addr_t)local_addr<void>())
{ }
~Zynq_Vdma()
{ }
/*
* Registers
*/
struct MM2S_VDMACR : Register<0x00, 32>
{
struct Repeat_En : Bitfield<15,1> {};
struct Err_IrqEn : Bitfield<14,1> {};
struct RdPntrNum : Bitfield<8,4> {};
struct GenlockSrc : Bitfield<7,1> {};
struct GenlockEn : Bitfield<3,1> {};
struct Reset : Bitfield<2,1> {};
struct Circular_Park : Bitfield<1,1> {};
struct RS : Bitfield<0,1> {};
};
struct MM2S_VDMASR : Register<0x04, 32>
{
struct Err_Irq : Bitfield<14,1> {};
struct SOFEarlyErr : Bitfield<7,1> {};
struct VDMADecErr : Bitfield<6,1> {};
struct VDMASlvErr : Bitfield<5,1> {};
struct VDMAIntErr : Bitfield<4,1> {};
struct Halted : Bitfield<0,1> {};
};
struct MM2S_REG_INDEX : Register<0x14, 32>
{
struct MM2S_Reg_Index : Bitfield<0,1> {};
};
struct PARK_PTR_REG : Register<0x28, 32>
{
struct WrFrmStore : Bitfield<24,5> {};
struct RdFrmStore : Bitfield<16,5> {};
struct WrFrmPtrRef : Bitfield<8,5> {};
struct RdFrmPtrRef : Bitfield<0,5> {};
};
struct VDMA_VERSION : Register<0x2c, 32>
{
struct Major_Version : Bitfield<28,4> {};
struct Minor_Version : Bitfield<20,8> {};
struct Revision : Bitfield<16,4> {};
struct Xilinx_Internal : Bitfield<0,16> {};
};
struct S2MM_VDMACR : Register<0x30, 32>
{
struct Repeat_En : Bitfield<15,1> {};
struct Err_IrqEn : Bitfield<14,1> {};
struct WrPntrNum : Bitfield<8,4> {};
struct GenlockSrc : Bitfield<7,1> {};
struct GenlockEn : Bitfield<3,1> {};
struct Reset : Bitfield<2,1> {};
};
struct S2MM_VDMASR : Register<0x34, 32>
{
struct EOLLateErr : Bitfield<15,1> {};
struct Err_Irq : Bitfield<14,1> {};
struct SOFLateErr : Bitfield<11,1> {};
struct EOLEarlyErr : Bitfield<8,1> {};
struct SOFEarlyErr : Bitfield<7,1> {};
struct VDMADecErr : Bitfield<6,1> {};
struct DMASlvErr : Bitfield<5,1> {};
struct DMAIntErr : Bitfield<4,1> {};
struct Halted : Bitfield<0,1> {};
};
struct S2MM_REG_INDEX : Register<0x44, 32>
{
struct S2MM_Reg_Index : Bitfield<0,1> {};
};
struct MM2S_VSIZE : Register<0x50, 32>
{
struct Vertical_Size : Bitfield<0,13> {};
};
struct MM2S_HSIZE : Register<0x54, 32>
{
struct Horizontal_Size : Bitfield<0,16> {};
};
struct MM2S_FRMDLY_STRIDE : Register<0x58, 32>
{
struct Frame_Delay : Bitfield<24,5> {};
struct Stride : Bitfield<0,16> {};
};
struct MM2S_START_ADDRESS : Register<0x5c, 32> {};
struct S2MM_VSIZE : Register<0xa0, 32>
{
struct Vertical_Size : Bitfield<0,13> {};
};
struct S2MM_HSIZE : Register<0xa4, 32>
{
struct Horizontal_Size : Bitfield<0,16> {};
};
struct S2MM_FRMDLY_STRIDE : Register<0xa8, 32>
{
struct Frame_Delay : Bitfield<24,5> {};
struct Stride : Bitfield<0,16> {};
};
struct S2MM_START_ADDRESS : Register<0xac, 32> {};
};
#endif // _VDMA_H_